summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-fs-f2fs1
-rw-r--r--Documentation/device-mapper/thin-provisioning.txt8
-rw-r--r--Documentation/devicetree/bindings/display/msm/sde.txt12
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-cam-diag.txt164
-rw-r--r--Documentation/devicetree/bindings/platform/msm/ssm.txt13
-rw-r--r--Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt6
-rw-r--r--Documentation/filesystems/f2fs.txt16
-rw-r--r--Documentation/networking/netdev-FAQ.txt9
-rw-r--r--Makefile6
-rw-r--r--arch/alpha/include/asm/xchg.h30
-rw-r--r--arch/arc/Kconfig1
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi132
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-auto-mizar.dts1
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-camera.dtsi135
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-sde.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-cdp.dtsi7
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-gpu.dtsi65
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi22
-rw-r--r--arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-ivi-la.dts10
-rw-r--r--arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-telematics.dts1
-rw-r--r--arch/arm/boot/dts/socfpga.dtsi2
-rw-r--r--arch/arm/include/asm/vdso.h2
-rw-r--r--arch/arm/kernel/vdso.c12
-rw-r--r--arch/arm/mach-omap1/clock.c6
-rw-r--r--arch/arm/mach-omap2/pm.c4
-rw-r--r--arch/arm/mach-omap2/timer.c19
-rw-r--r--arch/arm/plat-omap/dmtimer.c7
-rw-r--r--arch/arm64/configs/msm-auto-gvm-perf_defconfig3
-rw-r--r--arch/arm64/configs/msm-auto-gvm_defconfig3
-rw-r--r--arch/arm64/configs/msm-auto-perf_defconfig3
-rw-r--r--arch/arm64/configs/msm-auto_defconfig3
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig2
-rw-r--r--arch/arm64/configs/msmcortex_defconfig2
-rw-r--r--arch/arm64/configs/ranchu64_defconfig71
-rw-r--r--arch/arm64/include/asm/atomic_lse.h14
-rw-r--r--arch/arm64/include/asm/spinlock.h4
-rw-r--r--arch/arm64/kernel/hibernate.c82
-rw-r--r--arch/arm64/mm/mmu.c22
-rw-r--r--arch/m68k/coldfire/device.c12
-rw-r--r--arch/mips/cavium-octeon/octeon-irq.c10
-rw-r--r--arch/mips/include/asm/mach-ath79/ar71xx_regs.h2
-rw-r--r--arch/mips/kernel/process.c4
-rw-r--r--arch/mips/kernel/ptrace.c24
-rw-r--r--arch/mips/kernel/ptrace32.c6
-rw-r--r--arch/mips/kvm/mips.c2
-rw-r--r--arch/mips/txx9/rbtx4939/setup.c4
-rw-r--r--arch/powerpc/include/asm/irq_work.h1
-rw-r--r--arch/powerpc/kernel/cpu_setup_power.S4
-rw-r--r--arch/powerpc/kvm/book3s_hv.c12
-rw-r--r--arch/powerpc/mm/numa.c78
-rw-r--r--arch/powerpc/net/bpf_jit_comp.c3
-rw-r--r--arch/powerpc/perf/core-book3s.c25
-rw-r--r--arch/powerpc/sysdev/mpic.c2
-rw-r--r--arch/s390/include/asm/nospec-insn.h13
-rw-r--r--arch/s390/kernel/asm-offsets.c1
-rw-r--r--arch/s390/kernel/mcount.S14
-rw-r--r--arch/sh/kernel/entry-common.S2
-rw-r--r--arch/sh/kernel/sh_ksyms_32.c3
-rw-r--r--arch/sh/lib/ashlsi3.S35
-rw-r--r--arch/sh/lib/ashrsi3.S33
-rw-r--r--arch/sh/lib/lshrsi3.S34
-rw-r--r--arch/sparc/include/asm/atomic_64.h6
-rw-r--r--arch/sparc/kernel/ds.c2
-rw-r--r--arch/x86/configs/x86_64_cuttlefish_defconfig13
-rw-r--r--arch/x86/configs/x86_64_ranchu_defconfig70
-rw-r--r--arch/x86/crypto/chacha20_glue.c2
-rw-r--r--arch/x86/crypto/crc32c-intel_glue.c7
-rw-r--r--arch/x86/include/asm/cmpxchg_32.h2
-rw-r--r--arch/x86/include/asm/cmpxchg_64.h2
-rw-r--r--arch/x86/include/asm/cpufeature.h39
-rw-r--r--arch/x86/include/asm/fpu/internal.h6
-rw-r--r--arch/x86/include/asm/fpu/xstate.h2
-rw-r--r--arch/x86/include/asm/kvm_emulate.h6
-rw-r--r--arch/x86/include/asm/smp.h9
-rw-r--r--arch/x86/include/asm/xor_32.h2
-rw-r--r--arch/x86/kernel/apic/apic.c2
-rw-r--r--arch/x86/kernel/cpu/amd.c4
-rw-r--r--arch/x86/kernel/cpu/common.c4
-rw-r--r--arch/x86/kernel/cpu/intel.c3
-rw-r--r--arch/x86/kernel/cpu/intel_cacheinfo.c6
-rw-r--r--arch/x86/kernel/cpu/mtrr/generic.c2
-rw-r--r--arch/x86/kernel/cpu/mtrr/main.c2
-rw-r--r--arch/x86/kernel/cpu/perf_event_amd.c4
-rw-r--r--arch/x86/kernel/cpu/perf_event_amd_uncore.c11
-rw-r--r--arch/x86/kernel/devicetree.c21
-rw-r--r--arch/x86/kernel/fpu/core.c24
-rw-r--r--arch/x86/kernel/fpu/init.c169
-rw-r--r--arch/x86/kernel/fpu/xstate.c3
-rw-r--r--arch/x86/kernel/hw_breakpoint.c6
-rw-r--r--arch/x86/kernel/smpboot.c3
-rw-r--r--arch/x86/kernel/traps.c1
-rw-r--r--arch/x86/kernel/vm86_32.c4
-rw-r--r--arch/x86/kvm/emulate.c72
-rw-r--r--arch/x86/kvm/lapic.c10
-rw-r--r--arch/x86/kvm/vmx.c43
-rw-r--r--arch/x86/kvm/x86.c58
-rw-r--r--arch/x86/kvm/x86.h4
-rw-r--r--arch/x86/mm/pgtable.c9
-rw-r--r--arch/x86/mm/setup_nx.c4
-rw-r--r--arch/x86/power/hibernate_32.c2
-rw-r--r--arch/x86/power/hibernate_64.c2
-rw-r--r--drivers/acpi/acpi_pad.c3
-rw-r--r--drivers/acpi/acpica/evevent.c9
-rw-r--r--drivers/acpi/acpica/nseval.c8
-rw-r--r--drivers/acpi/processor_perflib.c2
-rw-r--r--drivers/ata/libata-core.c6
-rw-r--r--drivers/base/dd.c20
-rw-r--r--drivers/block/paride/pcd.c2
-rw-r--r--drivers/bluetooth/btusb.c6
-rw-r--r--drivers/cdrom/cdrom.c3
-rw-r--r--drivers/cdrom/gdrom.c3
-rw-r--r--drivers/char/adsprpc.c22
-rw-r--r--drivers/char/adsprpc_compat.c109
-rw-r--r--drivers/char/adsprpc_shared.h18
-rw-r--r--drivers/char/diag/diag_ipc_logging.h3
-rw-r--r--drivers/char/diag/diag_memorydevice.c16
-rw-r--r--drivers/char/diag/diag_memorydevice.h1
-rw-r--r--drivers/char/diag/diagfwd.c52
-rw-r--r--drivers/char/diag/diagfwd_cntl.c240
-rw-r--r--drivers/char/diag/diagfwd_peripheral.c32
-rw-r--r--drivers/char/hw_random/stm32-rng.c9
-rw-r--r--drivers/char/hw_random/via-rng.c5
-rw-r--r--drivers/char/ipmi/ipmi_powernv.c5
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c4
-rw-r--r--drivers/char/tpm/tpm-chip.c13
-rw-r--r--drivers/char/tpm/tpm-interface.c7
-rw-r--r--drivers/char/tpm/tpm.h1
-rw-r--r--drivers/clk/clk.c3
-rw-r--r--drivers/clk/rockchip/clk-mmc-phase.c23
-rw-r--r--drivers/clk/samsung/clk-exynos3250.c4
-rw-r--r--drivers/clk/samsung/clk-exynos5250.c8
-rw-r--r--drivers/clk/samsung/clk-exynos5260.c2
-rw-r--r--drivers/clk/samsung/clk-exynos5433.c12
-rw-r--r--drivers/clk/samsung/clk-s3c2410.c16
-rw-r--r--drivers/clocksource/fsl_ftm_timer.c2
-rw-r--r--drivers/cpufreq/cppc_cpufreq.c23
-rw-r--r--drivers/crypto/padlock-aes.c2
-rw-r--r--drivers/crypto/padlock-sha.c2
-rw-r--r--drivers/crypto/sunxi-ss/sun4i-ss-core.c1
-rw-r--r--drivers/crypto/vmx/aes.c2
-rw-r--r--drivers/crypto/vmx/aes_cbc.c2
-rw-r--r--drivers/crypto/vmx/aes_ctr.c2
-rw-r--r--drivers/crypto/vmx/ghash.c2
-rw-r--r--drivers/devfreq/arm-memlat-mon.c3
-rw-r--r--drivers/devfreq/bimc-bwmon.c3
-rw-r--r--drivers/dma/pl330.c6
-rw-r--r--drivers/dma/sh/rcar-dmac.c2
-rw-r--r--drivers/dma/sh/usb-dmac.c4
-rw-r--r--drivers/firewire/ohci.c8
-rw-r--r--drivers/firmware/dmi_scan.c22
-rw-r--r--drivers/gpu/drm/drm_fops.c1
-rw-r--r--drivers/gpu/drm/exynos/regs-fimc.h2
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c8
-rw-r--r--drivers/gpu/drm/msm/dba_bridge.c7
-rw-r--r--drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c21
-rw-r--r--drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h10
-rw-r--r--drivers/gpu/drm/msm/dsi-staging/dsi_display.c57
-rw-r--r--drivers/gpu/drm/msm/dsi-staging/dsi_display.h3
-rw-r--r--drivers/gpu/drm/msm/sde/sde_color_processing.c52
-rw-r--r--drivers/gpu/drm/msm/sde/sde_connector.h12
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_catalog.c32
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_catalog.h14
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c43
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h9
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_dspp.c5
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_dspp.h27
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.c15
-rw-r--r--drivers/gpu/drm/msm/sde/sde_rm.c75
-rw-r--r--drivers/gpu/drm/msm/sde/sde_splash.c50
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_gem.c7
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_ioctl.c17
-rw-r--r--drivers/hid/hid-roccat-kovaplus.c2
-rw-r--r--drivers/hwmon/nct6775.c10
-rw-r--r--drivers/hwmon/pmbus/adm1275.c4
-rw-r--r--drivers/hwmon/pmbus/max8688.c2
-rw-r--r--drivers/hwtracing/stm/core.c7
-rw-r--r--drivers/i2c/busses/i2c-msm-v2.c8
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c8
-rw-r--r--drivers/i2c/busses/i2c-rcar.c202
-rw-r--r--drivers/ide/ide-cd.c2
-rw-r--r--drivers/iio/buffer/kfifo_buf.c7
-rw-r--r--drivers/iio/imu/inv_mpu/Kconfig63
-rw-r--r--drivers/iio/imu/inv_mpu/Makefile61
-rw-r--r--drivers/iio/imu/inv_mpu/README117
-rw-r--r--drivers/iio/imu/inv_mpu/iam20680/inv_mpu_core_20680.c1072
-rw-r--r--drivers/iio/imu/inv_mpu/iam20680/inv_mpu_iio_reg_20680.h236
-rw-r--r--drivers/iio/imu/inv_mpu/iam20680/inv_mpu_init_20680.c258
-rw-r--r--drivers/iio/imu/inv_mpu/iam20680/inv_mpu_parsing_20680.c421
-rw-r--r--drivers/iio/imu/inv_mpu/iam20680/inv_mpu_selftest_20680.c752
-rw-r--r--drivers/iio/imu/inv_mpu/iam20680/inv_mpu_setup_20680.c466
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_common.c988
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_dts.c343
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_dts.h25
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_i2c.c556
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_iio.h1138
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_ring.c643
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_spi.c410
-rw-r--r--drivers/iio/imu/inv_mpu/inv_mpu_timestamp.c280
-rw-r--r--drivers/iio/imu/inv_mpu/inv_test/Kconfig13
-rw-r--r--drivers/iio/imu/inv_mpu/inv_test/Makefile6
-rw-r--r--drivers/iio/imu/inv_mpu/inv_test/inv_counters.c159
-rw-r--r--drivers/iio/imu/inv_mpu/inv_test/inv_counters.h76
-rw-r--r--drivers/infiniband/core/ucma.c2
-rw-r--r--drivers/infiniband/hw/mlx5/qp.c5
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c3
-rw-r--r--drivers/input/mouse/elan_i2c_core.c1
-rw-r--r--drivers/input/mouse/elan_i2c_smbus.c22
-rw-r--r--drivers/input/touchscreen/goodix.c1
-rw-r--r--drivers/iommu/arm-smmu.c11
-rw-r--r--drivers/iommu/intel_irq_remapping.c2
-rw-r--r--drivers/irqchip/irq-gic-v3.c2
-rw-r--r--drivers/isdn/hardware/eicon/diva.c22
-rw-r--r--drivers/isdn/hardware/eicon/diva.h5
-rw-r--r--drivers/isdn/hardware/eicon/divasmain.c18
-rw-r--r--drivers/md/bcache/alloc.c4
-rw-r--r--drivers/md/bcache/bcache.h2
-rw-r--r--drivers/md/bcache/btree.c9
-rw-r--r--drivers/md/bcache/request.c2
-rw-r--r--drivers/md/bcache/super.c23
-rw-r--r--drivers/md/bcache/sysfs.c11
-rw-r--r--drivers/md/bcache/writeback.c27
-rw-r--r--drivers/md/raid1.c11
-rw-r--r--drivers/md/raid10.c6
-rw-r--r--drivers/md/raid5.c7
-rw-r--r--drivers/media/i2c/adv7481.c128
-rw-r--r--drivers/media/i2c/adv7481_reg.h7
-rw-r--r--drivers/media/pci/cx23885/cx23885-cards.c4
-rw-r--r--drivers/media/pci/cx23885/cx23885-core.c10
-rw-r--r--drivers/media/pci/cx25821/cx25821-core.c7
-rw-r--r--drivers/media/platform/msm/ais/Makefile1
-rw-r--r--drivers/media/platform/msm/ais/common/Makefile2
-rw-r--r--drivers/media/platform/msm/ais/common/cam_hw_ops.c5
-rw-r--r--drivers/media/platform/msm/ais/common/cam_hw_ops.h13
-rw-r--r--drivers/media/platform/msm/ais/common/cam_soc_api.c10
-rw-r--r--drivers/media/platform/msm/ais/common/msm_camera_diag_util.c364
-rw-r--r--drivers/media/platform/msm/ais/common/msm_camera_diag_util.h47
-rw-r--r--drivers/media/platform/msm/ais/common/msm_camera_io_util.c5
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_buf_mgr.c48
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_buf_mgr.h15
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp47.c3
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c43
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h1
-rw-r--r--drivers/media/platform/msm/ais/isp/msm_isp_util.c48
-rw-r--r--drivers/media/platform/msm/ais/ispif/msm_ispif.c17
-rw-r--r--drivers/media/platform/msm/ais/msm.c10
-rw-r--r--drivers/media/platform/msm/ais/msm_ais_diag/Makefile4
-rw-r--r--drivers/media/platform/msm/ais/msm_ais_diag/msm_diag_cam.c267
-rw-r--r--drivers/media/platform/msm/ais/msm_ais_diag/msm_diag_cam.h57
-rw-r--r--drivers/media/platform/msm/ais/msm_ais_mgr/Makefile1
-rw-r--r--drivers/media/platform/msm/ais/msm_ais_mgr/msm_ais_mgr.c57
-rw-r--r--drivers/media/platform/msm/ais/sensor/csid/msm_csid.c15
-rw-r--r--drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c17
-rw-r--r--drivers/media/platform/msm/ais/sensor/msm_sensor.c13
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c2
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c6
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_common.c10
-rw-r--r--drivers/media/platform/s3c-camif/camif-capture.c7
-rw-r--r--drivers/media/usb/em28xx/em28xx.h2
-rw-r--r--drivers/message/fusion/mptctl.c2
-rw-r--r--drivers/misc/qseecom.c9
-rw-r--r--drivers/misc/vmw_balloon.c23
-rw-r--r--drivers/mmc/card/block.c1
-rw-r--r--drivers/mmc/host/sdhci-iproc.c30
-rw-r--r--drivers/mmc/host/sdhci-msm.c43
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.c3
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c2
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.c3
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_main.c18
-rw-r--r--drivers/net/ethernet/freescale/gianfar.c7
-rw-r--r--drivers/net/ethernet/intel/e1000e/ich8lan.c2
-rw-r--r--drivers/net/ethernet/intel/e1000e/mac.c2
-rw-r--r--drivers/net/ethernet/intel/e1000e/netdev.c4
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/qp.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cmd.c2
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_cxt.c2
-rw-r--r--drivers/net/ethernet/sun/sunvnet.c2
-rw-r--r--drivers/net/irda/w83977af_ir.c4
-rw-r--r--drivers/net/phy/bcm-cygnus.c6
-rw-r--r--drivers/net/phy/bcm-phy-lib.h7
-rw-r--r--drivers/net/phy/bcm7xxx.c4
-rw-r--r--drivers/net/phy/dp83640.c18
-rw-r--r--drivers/net/team/team.c3
-rw-r--r--drivers/net/usb/cdc_mbim.c2
-rw-r--r--drivers/net/usb/qmi_wwan.c5
-rw-r--r--drivers/net/usb/r8152.c2
-rw-r--r--drivers/net/usb/smsc75xx.c7
-rw-r--r--drivers/net/virtio_net.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c10
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c2
-rw-r--r--drivers/net/wireless/cnss/cnss_pci.c12
-rw-r--r--drivers/net/wireless/cnss2/main.c16
-rw-r--r--drivers/net/wireless/cnss2/pci.c22
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c3
-rw-r--r--drivers/net/xen-netfront.c46
-rw-r--r--drivers/ntb/ntb_transport.c3
-rw-r--r--drivers/nvme/host/pci.c5
-rw-r--r--drivers/parisc/lba_pci.c20
-rw-r--r--drivers/pci/pci-driver.c17
-rw-r--r--drivers/pci/quirks.c5
-rw-r--r--drivers/platform/goldfish/goldfish_pipe_v2.c18
-rw-r--r--drivers/platform/msm/Kconfig10
-rw-r--r--drivers/platform/msm/Makefile1
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_dp.c20
-rw-r--r--drivers/platform/msm/ssm.c516
-rw-r--r--drivers/platform/msm/ssm.h90
-rw-r--r--drivers/power/power_supply_sysfs.c1
-rw-r--r--drivers/power/supply/qcom/battery.c387
-rw-r--r--drivers/power/supply/qcom/qpnp-smb2.c7
-rw-r--r--drivers/power/supply/qcom/smb-lib.c9
-rw-r--r--drivers/power/supply/qcom/smb-lib.h2
-rw-r--r--drivers/regulator/cpr3-hmss-regulator.c24
-rw-r--r--drivers/regulator/cpr3-mmss-regulator.c24
-rw-r--r--drivers/regulator/of_regulator.c1
-rw-r--r--drivers/rtc/hctosys.c5
-rw-r--r--drivers/rtc/rtc-snvs.c15
-rw-r--r--drivers/rtc/rtc-tx4939.c6
-rw-r--r--drivers/s390/cio/device_fsm.c7
-rw-r--r--drivers/s390/cio/io_sch.h1
-rw-r--r--drivers/scsi/aacraid/commsup.c4
-rw-r--r--drivers/scsi/aacraid/linit.c5
-rw-r--r--drivers/scsi/arm/fas216.c2
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_io.c1
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c5
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c5
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c2
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_scsih.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c6
-rw-r--r--drivers/scsi/qla4xxx/ql4_def.h2
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c46
-rw-r--r--drivers/scsi/scsi_transport_srp.c22
-rw-r--r--drivers/scsi/sd.c83
-rw-r--r--drivers/scsi/sr.c21
-rw-r--r--drivers/scsi/storvsc_drv.c2
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_hipd.c2
-rw-r--r--drivers/scsi/ufs/ufshcd.c2
-rw-r--r--drivers/soc/qcom/hab/Kconfig4
-rw-r--r--drivers/soc/qcom/hab/Makefile21
-rw-r--r--drivers/soc/qcom/hab/ghs_comm.c141
-rw-r--r--drivers/soc/qcom/hab/hab.c552
-rw-r--r--drivers/soc/qcom/hab/hab.h135
-rw-r--r--drivers/soc/qcom/hab/hab_ghs.c214
-rw-r--r--drivers/soc/qcom/hab/hab_ghs.h30
-rw-r--r--drivers/soc/qcom/hab/hab_mem_linux.c57
-rw-r--r--drivers/soc/qcom/hab/hab_mimex.c125
-rw-r--r--drivers/soc/qcom/hab/hab_msg.c115
-rw-r--r--drivers/soc/qcom/hab/hab_open.c207
-rw-r--r--drivers/soc/qcom/hab/hab_parser.c29
-rw-r--r--drivers/soc/qcom/hab/hab_pchan.c29
-rw-r--r--drivers/soc/qcom/hab/hab_qvm.c59
-rw-r--r--drivers/soc/qcom/hab/hab_qvm.h3
-rw-r--r--drivers/soc/qcom/hab/hab_vchan.c165
-rw-r--r--drivers/soc/qcom/hab/khab.c8
-rw-r--r--drivers/soc/qcom/hab/khab_test.c11
-rw-r--r--drivers/soc/qcom/hab/qvm_comm.c13
-rw-r--r--drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c48
-rw-r--r--drivers/soc/qcom/msm_smem.c18
-rw-r--r--drivers/soc/qcom/qdsp6v2/apr.c31
-rw-r--r--drivers/soc/qcom/scm_qcpe.c3
-rw-r--r--drivers/soc/qcom/subsystem_restart.c14
-rw-r--r--drivers/soc/qcom/watchdog_v2.c5
-rw-r--r--drivers/staging/android/ion/ion_system_heap.c6
-rw-r--r--drivers/staging/rtl8192u/r8192U_core.c2
-rw-r--r--drivers/tty/serial/arc_uart.c5
-rw-r--r--drivers/tty/serial/fsl_lpuart.c4
-rw-r--r--drivers/tty/serial/imx.c6
-rw-r--r--drivers/tty/serial/mxs-auart.c4
-rw-r--r--drivers/tty/serial/samsung.c11
-rw-r--r--drivers/tty/serial/xilinx_uartps.c2
-rw-r--r--drivers/uio/msm_sharedmem/msm_sharedmem.c32
-rw-r--r--drivers/usb/dwc2/core.h2
-rw-r--r--drivers/usb/dwc2/gadget.c12
-rw-r--r--drivers/usb/dwc3/core.h2
-rw-r--r--drivers/usb/gadget/composite.c40
-rw-r--r--drivers/usb/gadget/configfs.c4
-rw-r--r--drivers/usb/gadget/function/f_cdev.c3
-rw-r--r--drivers/usb/gadget/function/f_fs.c6
-rw-r--r--drivers/usb/gadget/function/f_uac2.c2
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c4
-rw-r--r--drivers/usb/gadget/udc/goku_udc.h2
-rw-r--r--drivers/usb/host/ohci-hcd.c3
-rw-r--r--drivers/usb/host/xhci-mem.c2
-rw-r--r--drivers/usb/misc/ks_bridge.c4
-rw-r--r--drivers/usb/musb/musb_core.c5
-rw-r--r--drivers/usb/serial/cp210x.c9
-rw-r--r--drivers/video/fbdev/sbuslib.c4
-rw-r--r--drivers/video/msm/ba/msm_ba.c1
-rw-r--r--drivers/watchdog/f71808e_wdt.c3
-rw-r--r--drivers/watchdog/sp5100_tco.h2
-rw-r--r--drivers/xen/events/events_base.c4
-rw-r--r--drivers/xen/grant-table.c4
-rw-r--r--drivers/xen/swiotlb-xen.c2
-rw-r--r--drivers/xen/xen-acpi-processor.c6
-rw-r--r--drivers/xen/xenbus/xenbus_probe.c5
-rw-r--r--drivers/zorro/zorro.c12
-rw-r--r--fs/affs/namei.c10
-rw-r--r--fs/aio.c7
-rw-r--r--fs/btrfs/ctree.c12
-rw-r--r--fs/btrfs/disk-io.c4
-rw-r--r--fs/btrfs/extent-tree.c1
-rw-r--r--fs/btrfs/file.c9
-rw-r--r--fs/btrfs/inode.c16
-rw-r--r--fs/btrfs/raid56.c18
-rw-r--r--fs/btrfs/send.c3
-rw-r--r--fs/btrfs/tests/qgroup-tests.c2
-rw-r--r--fs/btrfs/tree-log.c12
-rw-r--r--fs/btrfs/volumes.c9
-rw-r--r--fs/cifs/cifssmb.c4
-rw-r--r--fs/dcache.c22
-rw-r--r--fs/ecryptfs/inode.c3
-rw-r--r--fs/ext2/namei.c6
-rw-r--r--fs/ext4/namei.c6
-rw-r--r--fs/f2fs/f2fs.h13
-rw-r--r--fs/f2fs/file.c2
-rw-r--r--fs/f2fs/namei.c12
-rw-r--r--fs/f2fs/segment.c257
-rw-r--r--fs/f2fs/super.c4
-rw-r--r--fs/f2fs/sysfs.c3
-rw-r--r--fs/fscache/page.c13
-rw-r--r--fs/gfs2/file.c5
-rw-r--r--fs/gfs2/quota.h2
-rw-r--r--fs/jffs2/dir.c12
-rw-r--r--fs/jffs2/fs.c1
-rw-r--r--fs/jfs/namei.c12
-rw-r--r--fs/nfs/nfs4proc.c12
-rw-r--r--fs/nfs/nfs4state.c5
-rw-r--r--fs/nfs/nfs4sysctl.c2
-rw-r--r--fs/nilfs2/namei.c6
-rw-r--r--fs/ocfs2/acl.c6
-rw-r--r--fs/ocfs2/dlm/dlmdomain.c14
-rw-r--r--fs/ocfs2/dlm/dlmdomain.h25
-rw-r--r--fs/ocfs2/dlm/dlmrecovery.c9
-rw-r--r--fs/ocfs2/journal.c23
-rw-r--r--fs/ocfs2/super.c5
-rw-r--r--fs/ocfs2/xattr.c2
-rw-r--r--fs/proc/base.c29
-rw-r--r--fs/proc/proc_sysctl.c3
-rw-r--r--fs/reiserfs/namei.c12
-rw-r--r--fs/udf/namei.c6
-rw-r--r--fs/udf/super.c5
-rw-r--r--fs/ufs/namei.c6
-rw-r--r--fs/xfs/libxfs/xfs_alloc.c94
-rw-r--r--fs/xfs/libxfs/xfs_attr.c6
-rw-r--r--fs/xfs/xfs_discard.c14
-rw-r--r--fs/xfs/xfs_log.c7
-rw-r--r--fs/xfs/xfs_mount.h1
-rw-r--r--fs/xfs/xfs_trace.h9
-rw-r--r--include/asm-generic/pgtable.h15
-rw-r--r--include/linux/dcache.h1
-rw-r--r--include/linux/diagchar.h2
-rw-r--r--include/linux/fs.h4
-rw-r--r--include/linux/habmm.h2
-rw-r--r--include/linux/iio/imu/mpu.h124
-rw-r--r--include/linux/power_supply.h1
-rw-r--r--include/linux/suspend.h2
-rw-r--r--include/linux/tcp.h2
-rw-r--r--include/linux/usb/composite.h3
-rw-r--r--include/net/cnss.h10
-rw-r--r--include/net/ip.h11
-rw-r--r--include/net/ip_fib.h1
-rw-r--r--include/net/llc_conn.h2
-rw-r--r--include/net/mac80211.h2
-rw-r--r--include/net/regulatory.h2
-rw-r--r--include/net/route.h3
-rw-r--r--include/sound/apr_audio-v2.h5
-rw-r--r--include/sound/q6adm-v2.h2
-rw-r--r--include/sound/q6asm-v2.h6
-rw-r--r--include/trace/events/timer.h20
-rw-r--r--include/uapi/drm/msm_drm_pp.h118
-rw-r--r--include/uapi/drm/virtgpu_drm.h1
-rw-r--r--include/uapi/linux/hab_ioctl.h1
-rw-r--r--include/uapi/linux/if_ether.h1
-rw-r--r--include/uapi/linux/nl80211.h2
-rw-r--r--include/uapi/media/ais/msm_ais_isp.h44
-rw-r--r--include/uapi/media/ais/msm_ais_ispif.h8
-rw-r--r--include/uapi/media/ais/msm_ais_mgr.h73
-rw-r--r--include/uapi/media/ais/msm_ais_sensor.h10
-rw-r--r--init/Kconfig13
-rw-r--r--ipc/shm.c19
-rw-r--r--kernel/audit.c2
-rw-r--r--kernel/debug/kdb/kdb_main.c27
-rw-r--r--kernel/events/core.c24
-rw-r--r--kernel/locking/qspinlock.c8
-rw-r--r--kernel/power/hibernate.c3
-rw-r--r--kernel/power/power.h3
-rw-r--r--kernel/relay.c2
-rw-r--r--kernel/sched/fair.c2
-rw-r--r--kernel/sched/rt.c2
-rw-r--r--kernel/signal.c4
-rw-r--r--kernel/sys.c3
-rw-r--r--kernel/trace/trace_events_trigger.c5
-rw-r--r--kernel/workqueue.c2
-rw-r--r--lib/test_bpf.c31
-rw-r--r--mm/kasan/kasan.c2
-rw-r--r--mm/kmemleak.c12
-rw-r--r--mm/ksm.c28
-rw-r--r--mm/mempolicy.c36
-rw-r--r--mm/mmap.c32
-rw-r--r--mm/swapfile.c4
-rw-r--r--mm/vmscan.c22
-rw-r--r--net/batman-adv/distributed-arp-table.c2
-rw-r--r--net/batman-adv/fragmentation.c3
-rw-r--r--net/batman-adv/gateway_client.c3
-rw-r--r--net/batman-adv/multicast.c4
-rw-r--r--net/batman-adv/soft-interface.c8
-rw-r--r--net/bridge/netfilter/ebtables.c33
-rw-r--r--net/core/rtnetlink.c8
-rw-r--r--net/core/skbuff.c9
-rw-r--r--net/dccp/proto.c2
-rw-r--r--net/ipv4/fib_semantics.c2
-rw-r--r--net/ipv4/ip_sockglue.c2
-rw-r--r--net/ipv4/ip_vti.c1
-rw-r--r--net/ipv4/route.c26
-rw-r--r--net/ipv4/tcp_illinois.c2
-rw-r--r--net/ipv4/tcp_input.c10
-rw-r--r--net/ipv4/xfrm4_policy.c1
-rw-r--r--net/ipv6/ip6mr.c3
-rw-r--r--net/ipv6/sit.c7
-rw-r--r--net/key/af_key.c45
-rw-r--r--net/llc/llc_c_ac.c15
-rw-r--r--net/llc/llc_conn.c32
-rw-r--r--net/netlabel/netlabel_unlabeled.c10
-rw-r--r--net/nfc/llcp_commands.c4
-rw-r--r--net/nfc/netlink.c3
-rw-r--r--net/packet/af_packet.c6
-rw-r--r--net/rds/ib.c3
-rw-r--r--net/wireless/nl80211.c3
-rw-r--r--scripts/kconfig/confdata.c2
-rw-r--r--scripts/kconfig/expr.c2
-rw-r--r--scripts/kconfig/menu.c1
-rw-r--r--scripts/kconfig/zconf.y33
-rw-r--r--security/integrity/ima/Kconfig1
-rw-r--r--security/integrity/ima/ima_appraise.c8
-rw-r--r--security/integrity/ima/ima_crypto.c2
-rw-r--r--security/integrity/ima/ima_main.c13
-rw-r--r--sound/core/rawmidi.c2
-rw-r--r--sound/core/timer.c4
-rw-r--r--sound/core/vmaster.c5
-rw-r--r--sound/pci/hda/Kconfig1
-rw-r--r--sound/pci/hda/patch_realtek.c5
-rw-r--r--sound/soc/au1x/ac97c.c6
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c11
-rw-r--r--sound/soc/intel/common/sst-firmware.c2
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c31
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c47
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c87
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h1
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c249
-rw-r--r--sound/soc/msm/qdsp6v2/msm-qti-pp-config.c55
-rw-r--r--sound/soc/msm/qdsp6v2/q6adm.c47
-rw-r--r--sound/soc/msm/qdsp6v2/q6afe.c6
-rw-r--r--sound/soc/msm/qdsp6v2/q6asm.c175
-rw-r--r--sound/soc/msm/qdsp6v2/q6voice.c8
-rw-r--r--sound/soc/msm/qdsp6v2/rtac.c4
-rw-r--r--sound/soc/msm/sdm660-internal.c2
-rw-r--r--sound/soc/samsung/i2s.c13
-rw-r--r--sound/soc/soc-topology.c3
-rw-r--r--tools/lib/bpf/libbpf.c26
-rw-r--r--tools/lib/traceevent/event-parse.c17
-rw-r--r--tools/lib/traceevent/parse-filter.c10
-rw-r--r--tools/perf/tests/vmlinux-kallsyms.c2
-rw-r--r--tools/perf/util/evsel.c8
-rw-r--r--tools/perf/util/hist.c4
-rw-r--r--tools/perf/util/hist.h1
-rw-r--r--tools/testing/selftests/Makefile1
-rw-r--r--tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc46
-rw-r--r--tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc97
-rw-r--r--tools/testing/selftests/ftrace/test.d/kprobe/probepoint.tc43
-rw-r--r--tools/testing/selftests/memfd/config1
-rw-r--r--tools/testing/selftests/net/psock_fanout.c3
-rw-r--r--tools/testing/selftests/powerpc/mm/subpage_prot.c14
-rw-r--r--tools/thermal/tmon/sysfs.c12
-rw-r--r--tools/thermal/tmon/tmon.c1
574 files changed, 16700 insertions, 3063 deletions
diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs
index b8d0a30f1644..f82da9bbb1fd 100644
--- a/Documentation/ABI/testing/sysfs-fs-f2fs
+++ b/Documentation/ABI/testing/sysfs-fs-f2fs
@@ -101,6 +101,7 @@ Date: February 2015
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
Description:
Controls the trimming rate in batch mode.
+ <deprecated>
What: /sys/fs/f2fs/<disk>/cp_interval
Date: October 2015
diff --git a/Documentation/device-mapper/thin-provisioning.txt b/Documentation/device-mapper/thin-provisioning.txt
index 1699a55b7b70..ef639960b272 100644
--- a/Documentation/device-mapper/thin-provisioning.txt
+++ b/Documentation/device-mapper/thin-provisioning.txt
@@ -112,9 +112,11 @@ $low_water_mark is expressed in blocks of size $data_block_size. If
free space on the data device drops below this level then a dm event
will be triggered which a userspace daemon should catch allowing it to
extend the pool device. Only one such event will be sent.
-Resuming a device with a new table itself triggers an event so the
-userspace daemon can use this to detect a situation where a new table
-already exceeds the threshold.
+
+No special event is triggered if a just resumed device's free space is below
+the low water mark. However, resuming a device always triggers an
+event; a userspace daemon should verify that free space exceeds the low
+water mark when handling this event.
A low water mark for the metadata device is maintained in the kernel and
will trigger a dm event if free space on the metadata device drops below
diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt
index 1583da81c090..9472af5de4e7 100644
--- a/Documentation/devicetree/bindings/display/msm/sde.txt
+++ b/Documentation/devicetree/bindings/display/msm/sde.txt
@@ -247,7 +247,19 @@ Optional properties:
applied in scenarios where panel interface can
be more tolerant to memory latency such as
command mode panels.
+- qcom,sde-mixer-display-pref: A string array indicating the preferred display type
+ for the mixer block. Possible values:
+ "primary" - preferred for primary display
+ "secondary" - preferred for secondary display
+ "tertiary" - preferred for tertiary display
+ "none" - no preference for display
+- qcom,sde-ctl-display-pref: A string array indicating the preferred display type
+ for the ctl block. Possible values:
+ "primary" - preferred for primary display
+ "secondary" - preferred for secondary display
+ "tertiary" - preferred for tertiary display
+ "none" - no preference for display
Bus Scaling Subnodes:
- qcom,sde-reg-bus: Property to provide Bus scaling for register access for
mdss blocks.
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-diag.txt b/Documentation/devicetree/bindings/media/video/msm-cam-diag.txt
new file mode 100644
index 000000000000..6dfe8ffac828
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-diag.txt
@@ -0,0 +1,164 @@
+* Qualcomm Technologies, Inc. MSM Camera diag
+
+[Root level node]
+==================
+Required properties:
+- compatible: Must be "qcom,diag-cam".
+
+[Subnode]
+==========
+Required properties:
+- mmagic-supply: should contain mmagic regulator used for mmagic clocks.
+- gdscr-supply: should contain gdsr regulator used for cci clocks.
+- vfe0-vdd-supply: phandle to vfe0 regulator.
+- qcom,cam-vreg-name: name of the voltage regulators required for the device.
+- clocks: List of clock handles. The parent clocks of the input clocks to the
+ devices in this power domain are set to oscclk before power gating
+ and restored back after powering on a domain. This is required for
+ all domains which are powered on and off and not required for unused
+ domains.
+- clock-names: name of the clock used by the driver.
+- qcom,clock-rates: clock rate in Hz.
+- qcom,clock-control: The valid fields are "NO_SET_RATE", "INIT_RATE" and
+ "SET_RATE". "NO_SET_RATE" the corresponding clock is enabled without setting
+ the rate assuming some other driver has already set it to appropriate rate.
+ "INIT_RATE" clock rate is not queried assuming some other driver has set
+ the clock rate and ispif will set the the clock to this rate.
+ "SET_RATE" clock is enabled and the rate is set to the value specified
+ in the property qcom,clock-rates.
+
+Example:
+
+ qcom,diag-cam {
+ cell-index = <0>;
+ compatible = "qcom,diag-cam";
+ status = "ok";
+ mmagic-supply = <&gdsc_mmagic_camss>;
+ gdscr-supply = <&gdsc_camss_top>;
+ vfe0-vdd-supply = <&gdsc_vfe0>;
+ vfe1-vdd-supply = <&gdsc_vfe1>;
+ qcom,cam-vreg-name = "mmagic", "gdscr",
+ "vfe0-vdd", "vfe1-vdd";
+ 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_csi1phytimer_clk_src>,
+ <&clock_mmss clk_camss_csi1phytimer_clk>,
+ <&clock_mmss clk_csi2phytimer_clk_src>,
+ <&clock_mmss clk_camss_csi2phytimer_clk>,
+ <&clock_mmss clk_csi0_clk_src>,
+ <&clock_mmss clk_camss_csi0_clk>,
+ <&clock_mmss clk_camss_csi0phy_clk>,
+ <&clock_mmss clk_camss_csi0_ahb_clk>,
+ <&clock_mmss clk_camss_csi0rdi_clk>,
+ <&clock_mmss clk_camss_csi0pix_clk>,
+ <&clock_mmss clk_csi1_clk_src>,
+ <&clock_mmss clk_camss_csi1_clk>,
+ <&clock_mmss clk_camss_csi1phy_clk>,
+ <&clock_mmss clk_camss_csi1_ahb_clk>,
+ <&clock_mmss clk_camss_csi1rdi_clk>,
+ <&clock_mmss clk_camss_csi1pix_clk>,
+ <&clock_mmss clk_csi2_clk_src>,
+ <&clock_mmss clk_camss_csi2_clk>,
+ <&clock_mmss clk_camss_csi2phy_clk>,
+ <&clock_mmss clk_camss_csi2_ahb_clk>,
+ <&clock_mmss clk_camss_csi2rdi_clk>,
+ <&clock_mmss clk_camss_csi2pix_clk>,
+ <&clock_mmss clk_csi3_clk_src>,
+ <&clock_mmss clk_camss_csi3_clk>,
+ <&clock_mmss clk_camss_csi3phy_clk>,
+ <&clock_mmss clk_camss_csi3_ahb_clk>,
+ <&clock_mmss clk_camss_csi3rdi_clk>,
+ <&clock_mmss clk_camss_csi3pix_clk>,
+ <&clock_mmss clk_vfe0_clk_src>,
+ <&clock_mmss clk_camss_vfe0_clk>,
+ <&clock_mmss clk_camss_csi_vfe0_clk>,
+ <&clock_mmss clk_vfe1_clk_src>,
+ <&clock_mmss clk_camss_vfe1_clk>,
+ <&clock_mmss clk_camss_csi_vfe1_clk>,
+ <&clock_mmss clk_mmagic_camss_axi_clk>,
+ <&clock_mmss clk_camss_vfe_ahb_clk>,
+ <&clock_mmss clk_camss_vfe0_ahb_clk>,
+ <&clock_mmss clk_camss_vfe_axi_clk>,
+ <&clock_mmss clk_camss_vfe0_stream_clk>,
+ <&clock_mmss clk_smmu_vfe_axi_clk>,
+ <&clock_mmss clk_camss_vfe1_ahb_clk>,
+ <&clock_mmss clk_camss_vfe1_stream_clk>;
+ clock-names =
+ "clk_mmss_mmagic_ahb_clk",
+ "clk_camss_top_ahb_clk",
+ "clk_camss_ispif_ahb_clk",
+ "clk_csi0phytimer_clk_src",
+ "clk_camss_csi0phytimer_clk",
+ "clk_camss_ahb_clk",
+ "clk_csi1phytimer_clk_src",
+ "clk_camss_csi1phytimer_clk",
+ "clk_csi2phytimer_clk_src",
+ "clk_camss_csi2phytimer_clk",
+ "clk_csi0_clk_src",
+ "clk_camss_csi0_clk",
+ "clk_camss_csi0phy_clk",
+ "clk_camss_csi0_ahb_clk",
+ "clk_camss_csi0rdi_clk",
+ "clk_camss_csi0pix_clk",
+ "clk_csi1_clk_src",
+ "clk_camss_csi1_clk",
+ "clk_camss_csi1phy_clk",
+ "clk_camss_csi1_ahb_clk",
+ "clk_camss_csi1rdi_clk",
+ "clk_camss_csi1pix_clk",
+ "clk_csi2_clk_src",
+ "clk_camss_csi2_clk",
+ "clk_camss_csi2phy_clk",
+ "clk_camss_csi2_ahb_clk",
+ "clk_camss_csi2rdi_clk",
+ "clk_camss_csi2pix_clk",
+ "clk_csi3_clk_src",
+ "clk_camss_csi3_clk",
+ "clk_camss_csi3phy_clk",
+ "clk_camss_csi3_ahb_clk",
+ "clk_camss_csi3rdi_clk",
+ "clk_camss_csi3pix_clk",
+ "clk_vfe0_clk_src",
+ "clk_camss_vfe0_clk",
+ "clk_camss_csi_vfe0_clk",
+ "clk_vfe1_clk_src",
+ "clk_camss_vfe1_clk",
+ "clk_camss_csi_vfe1_clk",
+ "clk_mmagic_camss_axi_clk",
+ "clk_camss_vfe_ahb_clk",
+ "clk_camss_vfe0_ahb_clk",
+ "clk_camss_vfe_axi_clk",
+ "clk_camss_vfe0_stream_clk",
+ "clk_smmu_vfe_axi_clk",
+ "clk_camss_vfe1_ahb_clk",
+ "clk_camss_vfe1_stream_clk";
+ qcom,clock-rates = <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
+ 0 0 0 0 0 0 0 0 0 0
+ >;
+ qcom,clock-cntl-support;
+ qcom,clock-control = "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","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE";
+ };
+
diff --git a/Documentation/devicetree/bindings/platform/msm/ssm.txt b/Documentation/devicetree/bindings/platform/msm/ssm.txt
deleted file mode 100644
index 7df3efd66577..000000000000
--- a/Documentation/devicetree/bindings/platform/msm/ssm.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-* Qualcomm Technologies, Inc. Secure Service Module driver
-
-This module enables framework to which a client can register itself
-specifying different attributes and defining various permission levels
-associated with different combination of attribute values and mode of the system.
-
-Required properties:
-- compatible: Must be "qcom,ssm"
-
-Example:
- qcom,ssm {
- compatible = "qcom,ssm";
- }; \ No newline at end of file
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
index f01eae10bf4f..30a26a8965bc 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
@@ -183,6 +183,12 @@ Charger specific properties:
Value type: bool
Definition: Boolean flag which when present enables sw compensation for jeita
+- qcom,fcc-stepping-enable
+ Usage: optional
+ Value type: bool
+ Definition: Boolean flag which when present enables stepwise change in FCC.
+ The default stepping rate is 100mA/sec.
+
=============================================
Second Level Nodes - SMB2 Charger Peripherals
=============================================
diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt
index 1f52baea2f69..ecccb51c7279 100644
--- a/Documentation/filesystems/f2fs.txt
+++ b/Documentation/filesystems/f2fs.txt
@@ -180,13 +180,15 @@ whint_mode=%s Control which write hints are passed down to block
passes down hints with its policy.
alloc_mode=%s Adjust block allocation policy, which supports "reuse"
and "default".
-fsync_mode=%s Control the policy of fsync. Currently supports "posix"
- and "strict". In "posix" mode, which is default, fsync
- will follow POSIX semantics and does a light operation
- to improve the filesystem performance. In "strict" mode,
- fsync will be heavy and behaves in line with xfs, ext4
- and btrfs, where xfstest generic/342 will pass, but the
- performance will regress.
+fsync_mode=%s Control the policy of fsync. Currently supports "posix",
+ "strict", and "nobarrier". In "posix" mode, which is
+ default, fsync will follow POSIX semantics and does a
+ light operation to improve the filesystem performance.
+ In "strict" mode, fsync will be heavy and behaves in line
+ with xfs, ext4 and btrfs, where xfstest generic/342 will
+ pass, but the performance will regress. "nobarrier" is
+ based on "posix", but doesn't issue flush command for
+ non-atomic files likewise "nobarrier" mount option.
test_dummy_encryption Enable dummy encryption, which provides a fake fscrypt
context. The fake fscrypt context is used by xfstests.
diff --git a/Documentation/networking/netdev-FAQ.txt b/Documentation/networking/netdev-FAQ.txt
index 0fe1c6e0dbcd..bfc6b3e68cc4 100644
--- a/Documentation/networking/netdev-FAQ.txt
+++ b/Documentation/networking/netdev-FAQ.txt
@@ -168,6 +168,15 @@ A: No. See above answer. In short, if you think it really belongs in
dash marker line as described in Documentation/SubmittingPatches to
temporarily embed that information into the patch that you send.
+Q: Are all networking bug fixes backported to all stable releases?
+
+A: Due to capacity, Dave could only take care of the backports for the last
+ 2 stable releases. For earlier stable releases, each stable branch maintainer
+ is supposed to take care of them. If you find any patch is missing from an
+ earlier stable branch, please notify stable@vger.kernel.org with either a
+ commit ID or a formal patch backported, and CC Dave and other relevant
+ networking developers.
+
Q: Someone said that the comment style and coding convention is different
for the networking content. Is this true?
diff --git a/Makefile b/Makefile
index ea532bf51579..e3c0272ae099 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 4
-SUBLEVEL = 133
+SUBLEVEL = 138
EXTRAVERSION =
NAME = Blurry Fish Butt
@@ -404,7 +404,9 @@ KBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
-Wno-format-security \
-std=gnu89 $(call cc-option,-fno-PIE)
-
+ifeq ($(TARGET_BOARD_TYPE),auto)
+KBUILD_CFLAGS += -DCONFIG_PLATFORM_AUTO
+endif
KBUILD_AFLAGS_KERNEL :=
KBUILD_CFLAGS_KERNEL :=
KBUILD_AFLAGS := -D__ASSEMBLY__ $(call cc-option,-fno-PIE)
diff --git a/arch/alpha/include/asm/xchg.h b/arch/alpha/include/asm/xchg.h
index 0ca9724597c1..7081e52291d0 100644
--- a/arch/alpha/include/asm/xchg.h
+++ b/arch/alpha/include/asm/xchg.h
@@ -11,6 +11,10 @@
* Atomic exchange.
* Since it can be used to implement critical sections
* it must clobber "memory" (also for interrupts in UP).
+ *
+ * The leading and the trailing memory barriers guarantee that these
+ * operations are fully ordered.
+ *
*/
static inline unsigned long
@@ -18,6 +22,7 @@ ____xchg(_u8, volatile char *m, unsigned long val)
{
unsigned long ret, tmp, addr64;
+ smp_mb();
__asm__ __volatile__(
" andnot %4,7,%3\n"
" insbl %1,%4,%1\n"
@@ -42,6 +47,7 @@ ____xchg(_u16, volatile short *m, unsigned long val)
{
unsigned long ret, tmp, addr64;
+ smp_mb();
__asm__ __volatile__(
" andnot %4,7,%3\n"
" inswl %1,%4,%1\n"
@@ -66,6 +72,7 @@ ____xchg(_u32, volatile int *m, unsigned long val)
{
unsigned long dummy;
+ smp_mb();
__asm__ __volatile__(
"1: ldl_l %0,%4\n"
" bis $31,%3,%1\n"
@@ -86,6 +93,7 @@ ____xchg(_u64, volatile long *m, unsigned long val)
{
unsigned long dummy;
+ smp_mb();
__asm__ __volatile__(
"1: ldq_l %0,%4\n"
" bis $31,%3,%1\n"
@@ -127,10 +135,12 @@ ____xchg(, volatile void *ptr, unsigned long x, int size)
* store NEW in MEM. Return the initial value in MEM. Success is
* indicated by comparing RETURN with OLD.
*
- * The memory barrier should be placed in SMP only when we actually
- * make the change. If we don't change anything (so if the returned
- * prev is equal to old) then we aren't acquiring anything new and
- * we don't need any memory barrier as far I can tell.
+ * The leading and the trailing memory barriers guarantee that these
+ * operations are fully ordered.
+ *
+ * The trailing memory barrier is placed in SMP unconditionally, in
+ * order to guarantee that dependency ordering is preserved when a
+ * dependency is headed by an unsuccessful operation.
*/
static inline unsigned long
@@ -138,6 +148,7 @@ ____cmpxchg(_u8, volatile char *m, unsigned char old, unsigned char new)
{
unsigned long prev, tmp, cmp, addr64;
+ smp_mb();
__asm__ __volatile__(
" andnot %5,7,%4\n"
" insbl %1,%5,%1\n"
@@ -149,8 +160,8 @@ ____cmpxchg(_u8, volatile char *m, unsigned char old, unsigned char new)
" or %1,%2,%2\n"
" stq_c %2,0(%4)\n"
" beq %2,3f\n"
- __ASM__MB
"2:\n"
+ __ASM__MB
".subsection 2\n"
"3: br 1b\n"
".previous"
@@ -165,6 +176,7 @@ ____cmpxchg(_u16, volatile short *m, unsigned short old, unsigned short new)
{
unsigned long prev, tmp, cmp, addr64;
+ smp_mb();
__asm__ __volatile__(
" andnot %5,7,%4\n"
" inswl %1,%5,%1\n"
@@ -176,8 +188,8 @@ ____cmpxchg(_u16, volatile short *m, unsigned short old, unsigned short new)
" or %1,%2,%2\n"
" stq_c %2,0(%4)\n"
" beq %2,3f\n"
- __ASM__MB
"2:\n"
+ __ASM__MB
".subsection 2\n"
"3: br 1b\n"
".previous"
@@ -192,6 +204,7 @@ ____cmpxchg(_u32, volatile int *m, int old, int new)
{
unsigned long prev, cmp;
+ smp_mb();
__asm__ __volatile__(
"1: ldl_l %0,%5\n"
" cmpeq %0,%3,%1\n"
@@ -199,8 +212,8 @@ ____cmpxchg(_u32, volatile int *m, int old, int new)
" mov %4,%1\n"
" stl_c %1,%2\n"
" beq %1,3f\n"
- __ASM__MB
"2:\n"
+ __ASM__MB
".subsection 2\n"
"3: br 1b\n"
".previous"
@@ -215,6 +228,7 @@ ____cmpxchg(_u64, volatile long *m, unsigned long old, unsigned long new)
{
unsigned long prev, cmp;
+ smp_mb();
__asm__ __volatile__(
"1: ldq_l %0,%5\n"
" cmpeq %0,%3,%1\n"
@@ -222,8 +236,8 @@ ____cmpxchg(_u64, volatile long *m, unsigned long old, unsigned long new)
" mov %4,%1\n"
" stq_c %1,%2\n"
" beq %1,3f\n"
- __ASM__MB
"2:\n"
+ __ASM__MB
".subsection 2\n"
"3: br 1b\n"
".previous"
diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 2d785f5a3041..c4ee25e88a7b 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -479,7 +479,6 @@ config ARC_CURR_IN_REG
config ARC_EMUL_UNALIGNED
bool "Emulate unaligned memory access (userspace only)"
- default N
select SYSCTL_ARCH_UNALIGN_NO_WARN
select SYSCTL_ARCH_UNALIGN_ALLOW
depends on ISA_ARCOMPACT
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi
new file mode 100644
index 000000000000..2bb0f6b5b116
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi
@@ -0,0 +1,132 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+ dsi_hx8399c_truly_vid: qcom,mdss_dsi_hx8399_truly_fhd_video {
+ qcom,mdss-dsi-panel-name =
+ "hx8399c video mode dsi truly panel";
+ qcom,mdss-dsi-panel-type = "dsi_video_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 = <2160>;
+ qcom,mdss-dsi-h-front-porch = <42>;
+ qcom,mdss-dsi-h-back-porch = <42>;
+ qcom,mdss-dsi-h-pulse-width = <10>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <15>;
+ qcom,mdss-dsi-v-front-porch = <10>;
+ qcom,mdss-dsi-v-pulse-width = <3>;
+ 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-pan-physical-width-dimension = <65>;
+ qcom,mdss-pan-physical-height-dimension = <129>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-on-command = [
+ 39 01 00 00 00 00 04
+ b9 ff 83 99
+ 39 01 00 00 00 00 02
+ d2 88
+ 39 01 00 00 00 00 0c
+ b1 02 04 72 92 01
+ 32 aa 11 11 52 57
+ 39 01 00 00 00 00 10
+ b2 00 80 80 cc 05 07 5a
+ 11 10 10 00 1e 70 03 d4
+ 39 01 00 00 00 00 2d
+ b4 00 ff 59 59 01 ab 00
+ 00 09 00 03 05 00 28 03
+ 0b 0d 21 03 02 00 0c a3
+ 80 59 59 02 ab 00 00 09
+ 00 03 05 00 28 03 0b 0d
+ 02 00 0c a3 01
+ 39 01 00 00 05 00 22
+ d3 00 0c 03 03 00 00 10
+ 10 00 00 03 00 03 00 08
+ 78 08 78 00 00 00 00 00
+ 24 02 05 05 03 00 00 00
+ 05 40
+ 39 01 00 00 05 00 21
+ d5 20 20 19 19 18 18 02
+ 03 00 01 24 24 18 18 18
+ 18 24 24 00 00 00 00 00
+ 00 00 00 2f 2f 30 30 31
+ 31
+ 39 01 00 00 05 00 21
+ d6 24 24 18 18 19 19 01
+ 00 03 02 24 24 18 18 18
+ 18 20 20 40 40 40 40 40
+ 40 40 40 2f 2f 30 30 31
+ 31
+ 39 01 00 00 00 00 02
+ bd 00
+ 39 01 00 00 00 00 11
+ d8 aa aa aa aa aa aa aa
+ aa aa ba aa aa aa ba aa
+ aa
+ 39 01 00 00 00 00 02
+ bd 01
+ 39 01 00 00 00 00 11
+ d8 00 00 00 00 00 00 00
+ 00 82 ea aa aa 82 ea aa
+ aa
+ 39 01 00 00 00 00 02
+ bd 02
+ 39 01 00 00 00 00 09
+ d8 ff ff c0 3f ff ff c0
+ 3f
+ 39 01 00 00 00 00 02
+ bd 00
+ 39 01 00 00 05 00 37
+ e0 01 21 31 2d 66 6f 7b
+ 75 7a 81 86 89 8c 90 95
+ 97 9a a1 a2 aa 9e ad b0
+ 5b 57 63 7a 01 21 31 2d
+ 66 6f 7b 75 7a 81 86 89
+ 8c 90 95 97 9a a1 a2 aa
+ 9e ad b0 5b 57 63 7a
+ 39 01 00 00 00 00 03
+ b6 7e 7e
+ 39 01 00 00 00 00 02
+ cc 08
+ 05 01 00 00 96 00 02 11 00
+ 05 01 00 00 32 00 02 29 00];
+ qcom,mdss-dsi-off-command = [
+ 05 01 00 00 32 00 02 28 00
+ 05 01 00 00 96 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_event";
+ qcom,mdss-dsi-lane-map = "lane_map_0123";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-tx-eot-append;
+ 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-t-clk-post = <0x0e>;
+ qcom,mdss-dsi-t-clk-pre = <0x31>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-lp11-init;
+ qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ };
+};
+
diff --git a/arch/arm/boot/dts/qcom/msm8996-auto-mizar.dts b/arch/arm/boot/dts/qcom/msm8996-auto-mizar.dts
index 8c750dab8707..006f1a28bf55 100644
--- a/arch/arm/boot/dts/qcom/msm8996-auto-mizar.dts
+++ b/arch/arm/boot/dts/qcom/msm8996-auto-mizar.dts
@@ -336,7 +336,6 @@
};
&pcie2 {
- qcom,boot-option = <0x0>;
perst-gpio = <&tlmm 90 0>;
wake-gpio = <&tlmm 54 0>;
};
diff --git a/arch/arm/boot/dts/qcom/msm8996-camera.dtsi b/arch/arm/boot/dts/qcom/msm8996-camera.dtsi
index f3838785b38c..4f632cff79be 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-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -787,6 +787,139 @@
status = "disabled";
};
};
+
+ qcom,diag-cam {
+ cell-index = <0>;
+ compatible = "qcom,diag-cam";
+ status = "ok";
+ mmagic-supply = <&gdsc_mmagic_camss>;
+ gdscr-supply = <&gdsc_camss_top>;
+ vfe0-vdd-supply = <&gdsc_vfe0>;
+ vfe1-vdd-supply = <&gdsc_vfe1>;
+ qcom,cam-vreg-name = "mmagic", "gdscr",
+ "vfe0-vdd", "vfe1-vdd";
+ 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_csi1phytimer_clk_src>,
+ <&clock_mmss clk_camss_csi1phytimer_clk>,
+ <&clock_mmss clk_csi2phytimer_clk_src>,
+ <&clock_mmss clk_camss_csi2phytimer_clk>,
+ <&clock_mmss clk_csi0_clk_src>,
+ <&clock_mmss clk_camss_csi0_clk>,
+ <&clock_mmss clk_camss_csi0phy_clk>,
+ <&clock_mmss clk_camss_csi0_ahb_clk>,
+ <&clock_mmss clk_camss_csi0rdi_clk>,
+ <&clock_mmss clk_camss_csi0pix_clk>,
+ <&clock_mmss clk_csi1_clk_src>,
+ <&clock_mmss clk_camss_csi1_clk>,
+ <&clock_mmss clk_camss_csi1phy_clk>,
+ <&clock_mmss clk_camss_csi1_ahb_clk>,
+ <&clock_mmss clk_camss_csi1rdi_clk>,
+ <&clock_mmss clk_camss_csi1pix_clk>,
+ <&clock_mmss clk_csi2_clk_src>,
+ <&clock_mmss clk_camss_csi2_clk>,
+ <&clock_mmss clk_camss_csi2phy_clk>,
+ <&clock_mmss clk_camss_csi2_ahb_clk>,
+ <&clock_mmss clk_camss_csi2rdi_clk>,
+ <&clock_mmss clk_camss_csi2pix_clk>,
+ <&clock_mmss clk_csi3_clk_src>,
+ <&clock_mmss clk_camss_csi3_clk>,
+ <&clock_mmss clk_camss_csi3phy_clk>,
+ <&clock_mmss clk_camss_csi3_ahb_clk>,
+ <&clock_mmss clk_camss_csi3rdi_clk>,
+ <&clock_mmss clk_camss_csi3pix_clk>,
+ <&clock_mmss clk_vfe0_clk_src>,
+ <&clock_mmss clk_camss_vfe0_clk>,
+ <&clock_mmss clk_camss_csi_vfe0_clk>,
+ <&clock_mmss clk_vfe1_clk_src>,
+ <&clock_mmss clk_camss_vfe1_clk>,
+ <&clock_mmss clk_camss_csi_vfe1_clk>,
+ <&clock_mmss clk_mmagic_camss_axi_clk>,
+ <&clock_mmss clk_camss_vfe_ahb_clk>,
+ <&clock_mmss clk_camss_vfe0_ahb_clk>,
+ <&clock_mmss clk_camss_vfe_axi_clk>,
+ <&clock_mmss clk_camss_vfe0_stream_clk>,
+ <&clock_mmss clk_smmu_vfe_axi_clk>,
+ <&clock_mmss clk_camss_vfe1_ahb_clk>,
+ <&clock_mmss clk_camss_vfe1_stream_clk>;
+ clock-names =
+ "clk_mmss_mmagic_ahb_clk",
+ "clk_camss_top_ahb_clk",
+ "clk_camss_ispif_ahb_clk",
+ "clk_csi0phytimer_clk_src",
+ "clk_camss_csi0phytimer_clk",
+ "clk_camss_ahb_clk",
+ "clk_csi1phytimer_clk_src",
+ "clk_camss_csi1phytimer_clk",
+ "clk_csi2phytimer_clk_src",
+ "clk_camss_csi2phytimer_clk",
+ "clk_csi0_clk_src",
+ "clk_camss_csi0_clk",
+ "clk_camss_csi0phy_clk",
+ "clk_camss_csi0_ahb_clk",
+ "clk_camss_csi0rdi_clk",
+ "clk_camss_csi0pix_clk",
+ "clk_csi1_clk_src",
+ "clk_camss_csi1_clk",
+ "clk_camss_csi1phy_clk",
+ "clk_camss_csi1_ahb_clk",
+ "clk_camss_csi1rdi_clk",
+ "clk_camss_csi1pix_clk",
+ "clk_csi2_clk_src",
+ "clk_camss_csi2_clk",
+ "clk_camss_csi2phy_clk",
+ "clk_camss_csi2_ahb_clk",
+ "clk_camss_csi2rdi_clk",
+ "clk_camss_csi2pix_clk",
+ "clk_csi3_clk_src",
+ "clk_camss_csi3_clk",
+ "clk_camss_csi3phy_clk",
+ "clk_camss_csi3_ahb_clk",
+ "clk_camss_csi3rdi_clk",
+ "clk_camss_csi3pix_clk",
+ "clk_vfe0_clk_src",
+ "clk_camss_vfe0_clk",
+ "clk_camss_csi_vfe0_clk",
+ "clk_vfe1_clk_src",
+ "clk_camss_vfe1_clk",
+ "clk_camss_csi_vfe1_clk",
+ "clk_mmagic_camss_axi_clk",
+ "clk_camss_vfe_ahb_clk",
+ "clk_camss_vfe0_ahb_clk",
+ "clk_camss_vfe_axi_clk",
+ "clk_camss_vfe0_stream_clk",
+ "clk_smmu_vfe_axi_clk",
+ "clk_camss_vfe1_ahb_clk",
+ "clk_camss_vfe1_stream_clk";
+ qcom,clock-rates = <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
+ 0 0 0 0 0 0 0 0 0 0
+ >;
+ qcom,clock-cntl-support;
+ qcom,clock-control = "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","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE",
+ "NO_SET_RATE","NO_SET_RATE","NO_SET_RATE";
+ };
};
&i2c_freq_100Khz {
diff --git a/arch/arm/boot/dts/qcom/msm8996-sde.dtsi b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi
index 661f0ebfd8fa..ea7c77e4b0a2 100644
--- a/arch/arm/boot/dts/qcom/msm8996-sde.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi
@@ -59,8 +59,12 @@
qcom,sde-off = <0x1000>;
qcom,sde-ctl-off = <0x2000 0x2200 0x2400
0x2600 0x2800>;
+ qcom,sde-ctl-display-pref = "primary", "secondary", "none",
+ "none", "none";
qcom,sde-mixer-off = <0x45000 0x46000 0x47000
0x48000 0x49000 0x4a000>;
+ qcom,sde-mixer-display-pref = "primary", "secondary", "none",
+ "none", "none";
qcom,sde-dspp-off = <0x55000 0x57000>;
qcom,sde-dspp-ad-off = <0x24000 0x22800>;
qcom,sde-dspp-ad-version = <0x00030000>;
diff --git a/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi b/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi
index 7db93928a369..70fc57e2594f 100644
--- a/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi
@@ -191,6 +191,13 @@
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
};
+&dsi_hx8399c_truly_vid {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
&mdss_dp_ctrl {
pinctrl-names = "mdss_dp_active", "mdss_dp_sleep";
pinctrl-0 = <&mdss_dp_aux_active &mdss_dp_usbplug_cc_active>;
diff --git a/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi b/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi
index 5a571c2db634..953e3a20da0b 100644
--- a/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -605,6 +605,69 @@
qcom,bus-max = <0>;
};
};
+
+ qcom,gpu-pwrlevels-6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,speed-bin = <122>;
+
+ qcom,initial-pwrlevel = <3>;
+
+ /* NOM */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <585000000>;
+ qcom,bus-freq = <12>;
+ qcom,bus-min = <11>;
+ qcom,bus-max = <12>;
+ };
+
+ /* SVS_L1 */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <465000000>;
+ qcom,bus-freq = <9>;
+ qcom,bus-min = <8>;
+ qcom,bus-max = <11>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <370000000>;
+ qcom,bus-freq = <8>;
+ qcom,bus-min = <6>;
+ qcom,bus-max = <9>;
+ };
+
+ /* Low SVS */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <266000000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <6>;
+ };
+
+ /* Min SVS */
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <160000000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <5>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
+ qcom,gpu-freq = <19200000>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
index 2cf4a1378778..b2f4a8ce47d3 100644
--- a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
@@ -26,6 +26,7 @@
#include "dsi-panel-truly-1080p-video.dtsi"
#include "dsi-panel-rm67195-amoled-fhd-cmd.dtsi"
#include "dsi-panel-lgd-incell-sw49106-fhd-video.dtsi"
+#include "dsi-panel-hx8399c-fhd-plus-video.dtsi"
&soc {
dsi_panel_pwr_supply: dsi_panel_pwr_supply {
@@ -337,3 +338,24 @@
qcom,mdss-dsi-t-clk-post = <0x0d>;
qcom,mdss-dsi-t-clk-pre = <0x30>;
};
+
+&dsi_hx8399c_truly_vid {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1c 08 09 05 03 04 a0];
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-status-read-length = <4>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ 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";
+};
diff --git a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-ivi-la.dts b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-ivi-la.dts
index 92f52a6de6e8..304a2ad4268f 100644
--- a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-ivi-la.dts
+++ b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-ivi-la.dts
@@ -154,6 +154,16 @@
/* Up to 800 Mbps */
<45 512 207108 14432000>;
};
+
+ subsys_notif_virt: qcom,subsys_notif_virt@2d000000 {
+ compatible = "qcom,subsys-notif-virt";
+ reg = <0x2d000000 0x18>;
+ reg-names = "vdev_base";
+ wlan {
+ subsys-name = "AR6320";
+ offset = <16>;
+ };
+ };
};
&reserved_memory {
diff --git a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-telematics.dts b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-telematics.dts
index d063a883a9e1..3902731dc5bd 100644
--- a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-telematics.dts
+++ b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996-telematics.dts
@@ -132,6 +132,7 @@
pinctrl-names = "bootstrap_active", "bootstrap_sleep";
pinctrl-0 = <&cnss_bootstrap_active>;
pinctrl-1 = <&cnss_bootstrap_sleep>;
+ qcom,wlan-ramdump-dynamic = <0x200000>;
qcom,msm-bus,name = "msm-cnss";
qcom,msm-bus,num-cases = <4>;
diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi
index 39c470e291f9..69381deeb703 100644
--- a/arch/arm/boot/dts/socfpga.dtsi
+++ b/arch/arm/boot/dts/socfpga.dtsi
@@ -738,7 +738,7 @@
timer@fffec600 {
compatible = "arm,cortex-a9-twd-timer";
reg = <0xfffec600 0x100>;
- interrupts = <1 13 0xf04>;
+ interrupts = <1 13 0xf01>;
clocks = <&mpu_periph_clk>;
};
diff --git a/arch/arm/include/asm/vdso.h b/arch/arm/include/asm/vdso.h
index d0295f1dd1a3..ff65b6d96c7e 100644
--- a/arch/arm/include/asm/vdso.h
+++ b/arch/arm/include/asm/vdso.h
@@ -11,8 +11,6 @@ struct mm_struct;
void arm_install_vdso(struct mm_struct *mm, unsigned long addr);
-extern char vdso_start, vdso_end;
-
extern unsigned int vdso_total_pages;
#else /* CONFIG_VDSO */
diff --git a/arch/arm/kernel/vdso.c b/arch/arm/kernel/vdso.c
index bbbffe946122..9d500067a25a 100644
--- a/arch/arm/kernel/vdso.c
+++ b/arch/arm/kernel/vdso.c
@@ -39,6 +39,8 @@
static struct page **vdso_text_pagelist;
+extern char vdso_start[], vdso_end[];
+
/* Total number of pages needed for the data and text portions of the VDSO. */
unsigned int vdso_total_pages __ro_after_init;
@@ -179,13 +181,13 @@ static int __init vdso_init(void)
unsigned int text_pages;
int i;
- if (memcmp(&vdso_start, "\177ELF", 4)) {
+ if (memcmp(vdso_start, "\177ELF", 4)) {
pr_err("VDSO is not a valid ELF object!\n");
return -ENOEXEC;
}
- text_pages = (&vdso_end - &vdso_start) >> PAGE_SHIFT;
- pr_debug("vdso: %i text pages at base %p\n", text_pages, &vdso_start);
+ text_pages = (vdso_end - vdso_start) >> PAGE_SHIFT;
+ pr_debug("vdso: %i text pages at base %pK\n", text_pages, vdso_start);
/* Allocate the VDSO text pagelist */
vdso_text_pagelist = kcalloc(text_pages, sizeof(struct page *),
@@ -200,7 +202,7 @@ static int __init vdso_init(void)
for (i = 0; i < text_pages; i++) {
struct page *page;
- page = virt_to_page(&vdso_start + i * PAGE_SIZE);
+ page = virt_to_page(vdso_start + i * PAGE_SIZE);
vdso_text_pagelist[i] = page;
}
@@ -211,7 +213,7 @@ static int __init vdso_init(void)
cntvct_ok = cntvct_functional();
- patch_vdso(&vdso_start);
+ patch_vdso(vdso_start);
return 0;
}
diff --git a/arch/arm/mach-omap1/clock.c b/arch/arm/mach-omap1/clock.c
index 4f5fd4a084c0..034b89499bd7 100644
--- a/arch/arm/mach-omap1/clock.c
+++ b/arch/arm/mach-omap1/clock.c
@@ -1031,17 +1031,17 @@ static int clk_debugfs_register_one(struct clk *c)
return -ENOMEM;
c->dent = d;
- d = debugfs_create_u8("usecount", S_IRUGO, c->dent, (u8 *)&c->usecount);
+ d = debugfs_create_u8("usecount", S_IRUGO, c->dent, &c->usecount);
if (!d) {
err = -ENOMEM;
goto err_out;
}
- d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate);
+ d = debugfs_create_ulong("rate", S_IRUGO, c->dent, &c->rate);
if (!d) {
err = -ENOMEM;
goto err_out;
}
- d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags);
+ d = debugfs_create_x8("flags", S_IRUGO, c->dent, &c->flags);
if (!d) {
err = -ENOMEM;
goto err_out;
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
index 58920bc8807b..3d876bde8c85 100644
--- a/arch/arm/mach-omap2/pm.c
+++ b/arch/arm/mach-omap2/pm.c
@@ -231,7 +231,7 @@ static void omap_pm_end(void)
cpu_idle_poll_ctrl(false);
}
-static void omap_pm_finish(void)
+static void omap_pm_wake(void)
{
if (cpu_is_omap34xx())
omap_prcm_irq_complete();
@@ -241,7 +241,7 @@ static const struct platform_suspend_ops omap_pm_ops = {
.begin = omap_pm_begin,
.end = omap_pm_end,
.enter = omap_pm_enter,
- .finish = omap_pm_finish,
+ .wake = omap_pm_wake,
.valid = suspend_valid_only_mem,
};
diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
index 83fc403aec3c..1f774ec4ab27 100644
--- a/arch/arm/mach-omap2/timer.c
+++ b/arch/arm/mach-omap2/timer.c
@@ -136,12 +136,6 @@ static struct clock_event_device clockevent_gpt = {
.tick_resume = omap2_gp_timer_shutdown,
};
-static struct property device_disabled = {
- .name = "status",
- .length = sizeof("disabled"),
- .value = "disabled",
-};
-
static const struct of_device_id omap_timer_match[] __initconst = {
{ .compatible = "ti,omap2420-timer", },
{ .compatible = "ti,omap3430-timer", },
@@ -183,8 +177,17 @@ static struct device_node * __init omap_get_timer_dt(const struct of_device_id *
of_get_property(np, "ti,timer-secure", NULL)))
continue;
- if (!of_device_is_compatible(np, "ti,omap-counter32k"))
- of_add_property(np, &device_disabled);
+ if (!of_device_is_compatible(np, "ti,omap-counter32k")) {
+ struct property *prop;
+
+ prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+ if (!prop)
+ return NULL;
+ prop->name = "status";
+ prop->value = "disabled";
+ prop->length = strlen(prop->value);
+ of_add_property(np, prop);
+ }
return np;
}
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
index 8ca94d379bc3..6f75c32dc3bf 100644
--- a/arch/arm/plat-omap/dmtimer.c
+++ b/arch/arm/plat-omap/dmtimer.c
@@ -854,11 +854,8 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
timer->irq = irq->start;
timer->pdev = pdev;
- /* Skip pm_runtime_enable for OMAP1 */
- if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) {
- pm_runtime_enable(dev);
- pm_runtime_irq_safe(dev);
- }
+ pm_runtime_enable(dev);
+ pm_runtime_irq_safe(dev);
if (!timer->reserved) {
ret = pm_runtime_get_sync(dev);
diff --git a/arch/arm64/configs/msm-auto-gvm-perf_defconfig b/arch/arm64/configs/msm-auto-gvm-perf_defconfig
index 63f492ee9786..cb83980e12c9 100644
--- a/arch/arm64/configs/msm-auto-gvm-perf_defconfig
+++ b/arch/arm64/configs/msm-auto-gvm-perf_defconfig
@@ -235,8 +235,11 @@ CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_GPIO=m
CONFIG_SERIO_AMBAKMI=y
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_MSM_HS=y
CONFIG_DIAG_CHAR=y
+# CONFIG_DEVPORT is not set
CONFIG_MSM_SMD_PKT=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_MSM_V2=y
diff --git a/arch/arm64/configs/msm-auto-gvm_defconfig b/arch/arm64/configs/msm-auto-gvm_defconfig
index 7e6d0f152306..f4934ba7bab2 100644
--- a/arch/arm64/configs/msm-auto-gvm_defconfig
+++ b/arch/arm64/configs/msm-auto-gvm_defconfig
@@ -224,10 +224,13 @@ CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_GPIO=m
CONFIG_SERIO_AMBAKMI=y
CONFIG_VT_HW_CONSOLE_BINDING=y
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_AMBA_PL011=y
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
CONFIG_SERIAL_MSM_HS=y
CONFIG_DIAG_CHAR=y
+# CONFIG_DEVPORT is not set
CONFIG_MSM_SMD_PKT=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_MSM_V2=y
diff --git a/arch/arm64/configs/msm-auto-perf_defconfig b/arch/arm64/configs/msm-auto-perf_defconfig
index 77f186f793b2..acf8acd6375c 100644
--- a/arch/arm64/configs/msm-auto-perf_defconfig
+++ b/arch/arm64/configs/msm-auto-perf_defconfig
@@ -316,11 +316,14 @@ CONFIG_INPUT_GPIO=y
# CONFIG_SERIO_SERPORT is not set
# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_MSM_HS=y
CONFIG_SERIAL_MSM_SMD=y
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM_LEGACY=y
+# CONFIG_DEVPORT is not set
CONFIG_MSM_SMD_PKT=y
CONFIG_MSM_ADSPRPC=y
CONFIG_MSM_RDBG=m
diff --git a/arch/arm64/configs/msm-auto_defconfig b/arch/arm64/configs/msm-auto_defconfig
index 926992c19853..15b059a621ac 100644
--- a/arch/arm64/configs/msm-auto_defconfig
+++ b/arch/arm64/configs/msm-auto_defconfig
@@ -317,6 +317,8 @@ CONFIG_INPUT_GPIO=y
# CONFIG_SERIO_SERPORT is not set
# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_MSM=y
CONFIG_SERIAL_MSM_CONSOLE=y
CONFIG_SERIAL_MSM_HS=y
@@ -324,6 +326,7 @@ CONFIG_SERIAL_MSM_SMD=y
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM_LEGACY=y
+# CONFIG_DEVPORT is not set
CONFIG_MSM_SMD_PKT=y
CONFIG_MSM_ADSPRPC=y
CONFIG_MSM_RDBG=m
diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig
index 031869a26722..9be3b7160ebe 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -614,6 +614,8 @@ CONFIG_EXT4_FS_SECURITY=y
CONFIG_EXT4_ENCRYPTION=y
CONFIG_EXT4_FS_ENCRYPTION=y
CONFIG_EXT4_FS_ICE_ENCRYPTION=y
+CONFIG_F2FS_FS=y
+CONFIG_F2FS_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V2=y
diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig
index bd5435729d88..14fe16629069 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -637,6 +637,8 @@ CONFIG_EXT4_FS_SECURITY=y
CONFIG_EXT4_ENCRYPTION=y
CONFIG_EXT4_FS_ENCRYPTION=y
CONFIG_EXT4_FS_ICE_ENCRYPTION=y
+CONFIG_F2FS_FS=y
+CONFIG_F2FS_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V2=y
diff --git a/arch/arm64/configs/ranchu64_defconfig b/arch/arm64/configs/ranchu64_defconfig
index fc55008d8c4c..7f847fc40d14 100644
--- a/arch/arm64/configs/ranchu64_defconfig
+++ b/arch/arm64/configs/ranchu64_defconfig
@@ -1,6 +1,7 @@
# CONFIG_LOCALVERSION_AUTO is not set
# CONFIG_SWAP is not set
CONFIG_POSIX_MQUEUE=y
+# CONFIG_USELIB is not set
CONFIG_AUDIT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -17,14 +18,19 @@ CONFIG_CGROUP_DEBUG=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_RT_GROUP_SCHED=y
+CONFIG_NAMESPACES=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_CC_STACKPROTECTOR_STRONG=y
CONFIG_ARCH_MMAP_RND_BITS=24
CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
# CONFIG_BLK_DEV_BSG is not set
# CONFIG_IOSCHED_DEADLINE is not set
CONFIG_ARCH_VEXPRESS=y
@@ -36,6 +42,8 @@ CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
CONFIG_SETEND_EMULATION=y
+CONFIG_ARM64_SW_TTBR0_PAN=y
+CONFIG_RANDOMIZE_BASE=y
CONFIG_CMDLINE="console=ttyAMA0"
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_COMPAT=y
@@ -50,15 +58,16 @@ 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_NET_IPVTI=y
CONFIG_INET_ESP=y
# 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
@@ -66,6 +75,7 @@ CONFIG_INET6_AH=y
CONFIG_INET6_ESP=y
CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_VTI=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=y
@@ -124,6 +134,10 @@ 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_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_NF_MANGLE=y
CONFIG_IP_NF_TARGET_ECN=y
CONFIG_IP_NF_TARGET_TTL=y
@@ -141,6 +155,7 @@ 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_RPFILTER=y
CONFIG_IP6_NF_MATCH_RT=y
CONFIG_IP6_NF_TARGET_HL=y
CONFIG_IP6_NF_FILTER=y
@@ -154,12 +169,14 @@ 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_MAC80211=y
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_UID_SYS_STATS=y
+CONFIG_MEMORY_STATE_TIME=y
CONFIG_SCSI=y
# CONFIG_SCSI_PROC_FS is not set
CONFIG_BLK_DEV_SD=y
@@ -180,13 +197,22 @@ CONFIG_PPP_DEFLATE=y
CONFIG_PPP_MPPE=y
CONFIG_PPPOLAC=y
CONFIG_PPPOPNS=y
-# CONFIG_WLAN is not set
+CONFIG_USB_USBNET=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_KEYRESET=y
CONFIG_KEYBOARD_GOLDFISH_EVENTS=y
+CONFIG_KEYBOARD_GOLDFISH_ROTARY=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
@@ -199,7 +225,8 @@ CONFIG_INPUT_GPIO=y
CONFIG_SERIAL_AMBA_PL011=y
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
CONFIG_VIRTIO_CONSOLE=y
-# CONFIG_HW_RANDOM is not set
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_VIRTIO=y
CONFIG_BATTERY_GOLDFISH=y
# CONFIG_HWMON is not set
CONFIG_MEDIA_SUPPORT=y
@@ -228,8 +255,10 @@ 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
@@ -244,14 +273,17 @@ 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
@@ -265,7 +297,18 @@ CONFIG_HID_WACOM=y
CONFIG_HID_WIIMOTE=y
CONFIG_HID_ZEROPLUS=y
CONFIG_HID_ZYDACRON=y
-# CONFIG_USB_SUPPORT is not set
+CONFIG_USB_HIDDEV=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_F_MTP=y
+CONFIG_USB_CONFIGFS_F_PTP=y
+CONFIG_USB_CONFIGFS_F_ACC=y
+CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y
+CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_MIDI=y
CONFIG_RTC_CLASS=y
CONFIG_VIRTIO_MMIO=y
CONFIG_STAGING=y
@@ -286,27 +329,31 @@ CONFIG_EXT2_FS=y
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_QFMT_V2=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_SDCARD_FS=y
+CONFIG_PSTORE=y
+CONFIG_PSTORE_CONSOLE=y
+CONFIG_PSTORE_RAM=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_ENABLE_DEFAULT_TRACERS=y
CONFIG_ATOMIC64_SELFTEST=y
-CONFIG_DEBUG_RODATA=y
+CONFIG_KEYS=y
+CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
CONFIG_SECURITY=y
CONFIG_SECURITY_NETWORK=y
+CONFIG_HARDENED_USERCOPY=y
CONFIG_SECURITY_SELINUX=y
+CONFIG_CRYPTO_SHA512=y
diff --git a/arch/arm64/include/asm/atomic_lse.h b/arch/arm64/include/asm/atomic_lse.h
index 39c1d340fec5..a000e47d5016 100644
--- a/arch/arm64/include/asm/atomic_lse.h
+++ b/arch/arm64/include/asm/atomic_lse.h
@@ -114,7 +114,7 @@ static inline void atomic_and(int i, atomic_t *v)
/* LSE atomics */
" mvn %w[i], %w[i]\n"
" stclr %w[i], %[v]")
- : [i] "+r" (w0), [v] "+Q" (v->counter)
+ : [i] "+&r" (w0), [v] "+Q" (v->counter)
: "r" (x1)
: __LL_SC_CLOBBERS);
}
@@ -131,7 +131,7 @@ static inline void atomic_sub(int i, atomic_t *v)
/* LSE atomics */
" neg %w[i], %w[i]\n"
" stadd %w[i], %[v]")
- : [i] "+r" (w0), [v] "+Q" (v->counter)
+ : [i] "+&r" (w0), [v] "+Q" (v->counter)
: "r" (x1)
: __LL_SC_CLOBBERS);
}
@@ -151,7 +151,7 @@ static inline int atomic_sub_return##name(int i, atomic_t *v) \
" neg %w[i], %w[i]\n" \
" ldadd" #mb " %w[i], w30, %[v]\n" \
" add %w[i], %w[i], w30") \
- : [i] "+r" (w0), [v] "+Q" (v->counter) \
+ : [i] "+&r" (w0), [v] "+Q" (v->counter) \
: "r" (x1) \
: __LL_SC_CLOBBERS , ##cl); \
\
@@ -255,7 +255,7 @@ static inline void atomic64_and(long i, atomic64_t *v)
/* LSE atomics */
" mvn %[i], %[i]\n"
" stclr %[i], %[v]")
- : [i] "+r" (x0), [v] "+Q" (v->counter)
+ : [i] "+&r" (x0), [v] "+Q" (v->counter)
: "r" (x1)
: __LL_SC_CLOBBERS);
}
@@ -272,7 +272,7 @@ static inline void atomic64_sub(long i, atomic64_t *v)
/* LSE atomics */
" neg %[i], %[i]\n"
" stadd %[i], %[v]")
- : [i] "+r" (x0), [v] "+Q" (v->counter)
+ : [i] "+&r" (x0), [v] "+Q" (v->counter)
: "r" (x1)
: __LL_SC_CLOBBERS);
}
@@ -292,7 +292,7 @@ static inline long atomic64_sub_return##name(long i, atomic64_t *v) \
" neg %[i], %[i]\n" \
" ldadd" #mb " %[i], x30, %[v]\n" \
" add %[i], %[i], x30") \
- : [i] "+r" (x0), [v] "+Q" (v->counter) \
+ : [i] "+&r" (x0), [v] "+Q" (v->counter) \
: "r" (x1) \
: __LL_SC_CLOBBERS, ##cl); \
\
@@ -412,7 +412,7 @@ static inline long __cmpxchg_double##name(unsigned long old1, \
" eor %[old1], %[old1], %[oldval1]\n" \
" eor %[old2], %[old2], %[oldval2]\n" \
" orr %[old1], %[old1], %[old2]") \
- : [old1] "+r" (x0), [old2] "+r" (x1), \
+ : [old1] "+&r" (x0), [old2] "+&r" (x1), \
[v] "+Q" (*(unsigned long *)ptr) \
: [new1] "r" (x2), [new2] "r" (x3), [ptr] "r" (x4), \
[oldval1] "r" (oldval1), [oldval2] "r" (oldval2) \
diff --git a/arch/arm64/include/asm/spinlock.h b/arch/arm64/include/asm/spinlock.h
index 19afa01911dd..da7a921d88d5 100644
--- a/arch/arm64/include/asm/spinlock.h
+++ b/arch/arm64/include/asm/spinlock.h
@@ -116,8 +116,8 @@ static inline int arch_spin_trylock(arch_spinlock_t *lock)
" cbnz %w1, 1f\n"
" add %w1, %w0, %3\n"
" casa %w0, %w1, %2\n"
- " and %w1, %w1, #0xffff\n"
- " eor %w1, %w1, %w0, lsr #16\n"
+ " sub %w1, %w1, %3\n"
+ " eor %w1, %w1, %w0\n"
"1:")
: "=&r" (lockval), "=&r" (tmp), "+Q" (*lock)
: "I" (1 << TICKET_SHIFT)
diff --git a/arch/arm64/kernel/hibernate.c b/arch/arm64/kernel/hibernate.c
index f8df75d740f4..6dd18140ebb8 100644
--- a/arch/arm64/kernel/hibernate.c
+++ b/arch/arm64/kernel/hibernate.c
@@ -34,6 +34,7 @@
#include <asm/pgtable-hwdef.h>
#include <asm/sections.h>
#include <asm/suspend.h>
+#include <asm/sysreg.h>
#include <asm/virt.h>
/*
@@ -216,12 +217,22 @@ static int create_safe_exec_page(void *src_start, size_t length,
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)));
+ /*
+ * Load our new page tables. A strict BBM approach requires that we
+ * ensure that TLBs are free of any entries that may overlap with the
+ * global mappings we are about to install.
+ *
+ * For a real hibernate/resume cycle TTBR0 currently points to a zero
+ * page, but TLBs may contain stale ASID-tagged entries (e.g. for EFI
+ * runtime services), while for a userspace-driven test_resume cycle it
+ * points to userspace page tables (and we must point it at a zero page
+ * ourselves). Elsewhere we only (un)install the idmap with preemption
+ * disabled, so T0SZ should be as required regardless.
+ */
+ cpu_set_reserved_ttbr0();
+ local_flush_tlb_all();
+ write_sysreg(virt_to_phys(pgd), ttbr0_el1);
+ isb();
*phys_dst_addr = virt_to_phys((void *)dst);
@@ -388,6 +399,38 @@ int swsusp_arch_resume(void)
void *, phys_addr_t, phys_addr_t);
/*
+ * 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);
+
+ /*
+ * 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);
+ if (!zero_page) {
+ pr_err("Failed to allocate zero page.");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /*
* Locate the exit code in the bottom-but-one page, so that *NULL
* still has disastrous affects.
*/
@@ -413,27 +456,6 @@ int swsusp_arch_resume(void)
__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.
*
@@ -447,12 +469,6 @@ int swsusp_arch_resume(void)
__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));
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index b30bdffa3066..24b0ed38ab7d 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -656,10 +656,16 @@ static phys_addr_t pgd_pgtable_alloc(void)
*/
void hotplug_paging(phys_addr_t start, phys_addr_t size)
{
-
struct page *pg;
- phys_addr_t pgd_phys = pgd_pgtable_alloc();
- pgd_t *pgd = pgd_set_fixmap(pgd_phys);
+ phys_addr_t pgd_phys;
+ pgd_t *pgd;
+ int cpu;
+
+ for_each_possible_cpu(cpu)
+ if (current->cpu != cpu)
+ sched_isolate_cpu(cpu);
+ pgd_phys = pgd_pgtable_alloc();
+ pgd = pgd_set_fixmap(pgd_phys);
memcpy(pgd, swapper_pg_dir, PAGE_SIZE);
@@ -675,6 +681,9 @@ void hotplug_paging(phys_addr_t start, phys_addr_t size)
pg = phys_to_page(pgd_phys);
pgtable_page_dtor(pg);
__free_pages(pg, 0);
+ for_each_possible_cpu(cpu)
+ if (current->cpu != cpu)
+ sched_unisolate_cpu_unlocked(cpu);
}
#ifdef CONFIG_MEMORY_HOTREMOVE
@@ -1028,7 +1037,11 @@ void remove_pagetable(unsigned long start, unsigned long end, bool direct)
unsigned long addr;
pgd_t *pgd;
pud_t *pud;
+ int cpu;
+ for_each_possible_cpu(cpu)
+ if (current->cpu != cpu)
+ sched_isolate_cpu(cpu);
for (addr = start; addr < end; addr = next) {
next = pgd_addr_end(addr, end);
@@ -1049,6 +1062,9 @@ void remove_pagetable(unsigned long start, unsigned long end, bool direct)
}
flush_tlb_all();
+ for_each_possible_cpu(cpu)
+ if (current->cpu != cpu)
+ sched_unisolate_cpu_unlocked(cpu);
}
diff --git a/arch/m68k/coldfire/device.c b/arch/m68k/coldfire/device.c
index 71ea4c02795d..8a2dc0af4cad 100644
--- a/arch/m68k/coldfire/device.c
+++ b/arch/m68k/coldfire/device.c
@@ -135,7 +135,11 @@ static struct platform_device mcf_fec0 = {
.id = 0,
.num_resources = ARRAY_SIZE(mcf_fec0_resources),
.resource = mcf_fec0_resources,
- .dev.platform_data = FEC_PDATA,
+ .dev = {
+ .dma_mask = &mcf_fec0.dev.coherent_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = FEC_PDATA,
+ }
};
#ifdef MCFFEC_BASE1
@@ -167,7 +171,11 @@ static struct platform_device mcf_fec1 = {
.id = 1,
.num_resources = ARRAY_SIZE(mcf_fec1_resources),
.resource = mcf_fec1_resources,
- .dev.platform_data = FEC_PDATA,
+ .dev = {
+ .dma_mask = &mcf_fec1.dev.coherent_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = FEC_PDATA,
+ }
};
#endif /* MCFFEC_BASE1 */
#endif /* CONFIG_FEC */
diff --git a/arch/mips/cavium-octeon/octeon-irq.c b/arch/mips/cavium-octeon/octeon-irq.c
index 10d0b2140375..63d35076722d 100644
--- a/arch/mips/cavium-octeon/octeon-irq.c
+++ b/arch/mips/cavium-octeon/octeon-irq.c
@@ -2240,7 +2240,7 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node,
parent_irq = irq_of_parse_and_map(ciu_node, 0);
if (!parent_irq) {
- pr_err("ERROR: Couldn't acquire parent_irq for %s\n.",
+ pr_err("ERROR: Couldn't acquire parent_irq for %s\n",
ciu_node->name);
return -EINVAL;
}
@@ -2252,7 +2252,7 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node,
addr = of_get_address(ciu_node, 0, NULL, NULL);
if (!addr) {
- pr_err("ERROR: Couldn't acquire reg(0) %s\n.", ciu_node->name);
+ pr_err("ERROR: Couldn't acquire reg(0) %s\n", ciu_node->name);
return -EINVAL;
}
host_data->raw_reg = (u64)phys_to_virt(
@@ -2260,7 +2260,7 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node,
addr = of_get_address(ciu_node, 1, NULL, NULL);
if (!addr) {
- pr_err("ERROR: Couldn't acquire reg(1) %s\n.", ciu_node->name);
+ pr_err("ERROR: Couldn't acquire reg(1) %s\n", ciu_node->name);
return -EINVAL;
}
host_data->en_reg = (u64)phys_to_virt(
@@ -2268,7 +2268,7 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node,
r = of_property_read_u32(ciu_node, "cavium,max-bits", &val);
if (r) {
- pr_err("ERROR: Couldn't read cavium,max-bits from %s\n.",
+ pr_err("ERROR: Couldn't read cavium,max-bits from %s\n",
ciu_node->name);
return r;
}
@@ -2278,7 +2278,7 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node,
&octeon_irq_domain_cib_ops,
host_data);
if (!cib_domain) {
- pr_err("ERROR: Couldn't irq_domain_add_linear()\n.");
+ pr_err("ERROR: Couldn't irq_domain_add_linear()\n");
return -ENOMEM;
}
diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h
index aa3800c82332..d99ca862dae3 100644
--- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h
+++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h
@@ -167,7 +167,7 @@
#define AR71XX_AHB_DIV_MASK 0x7
#define AR724X_PLL_REG_CPU_CONFIG 0x00
-#define AR724X_PLL_REG_PCIE_CONFIG 0x18
+#define AR724X_PLL_REG_PCIE_CONFIG 0x10
#define AR724X_PLL_FB_SHIFT 0
#define AR724X_PLL_FB_MASK 0x3ff
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index fe61ce7ad9b1..054a22c0873b 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -684,6 +684,10 @@ int mips_set_process_fp_mode(struct task_struct *task, unsigned int value)
if (value & ~known_bits)
return -EOPNOTSUPP;
+ /* Setting FRE without FR is not supported. */
+ if ((value & (PR_FP_MODE_FR | PR_FP_MODE_FRE)) == PR_FP_MODE_FRE)
+ return -EOPNOTSUPP;
+
/* Avoid inadvertently triggering emulation */
if ((value & PR_FP_MODE_FR) && raw_cpu_has_fpu &&
!(raw_current_cpu_data.fpu_id & MIPS_FPIR_F64))
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index 0f0030e7f6d9..0b70d62e2231 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -482,7 +482,7 @@ static int fpr_get_msa(struct task_struct *target,
/*
* Copy the floating-point context to the supplied NT_PRFPREG buffer.
* Choose the appropriate helper for general registers, and then copy
- * the FCSR register separately.
+ * the FCSR and FIR registers separately.
*/
static int fpr_get(struct task_struct *target,
const struct user_regset *regset,
@@ -490,6 +490,7 @@ static int fpr_get(struct task_struct *target,
void *kbuf, void __user *ubuf)
{
const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t);
+ const int fir_pos = fcr31_pos + sizeof(u32);
int err;
if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t))
@@ -502,6 +503,12 @@ static int fpr_get(struct task_struct *target,
err = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&target->thread.fpu.fcr31,
fcr31_pos, fcr31_pos + sizeof(u32));
+ if (err)
+ return err;
+
+ err = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &boot_cpu_data.fpu_id,
+ fir_pos, fir_pos + sizeof(u32));
return err;
}
@@ -550,7 +557,8 @@ static int fpr_set_msa(struct task_struct *target,
/*
* Copy the supplied NT_PRFPREG buffer to the floating-point context.
* Choose the appropriate helper for general registers, and then copy
- * the FCSR register separately.
+ * the FCSR register separately. Ignore the incoming FIR register
+ * contents though, as the register is read-only.
*
* We optimize for the case where `count % sizeof(elf_fpreg_t) == 0',
* which is supposed to have been guaranteed by the kernel before
@@ -564,6 +572,7 @@ static int fpr_set(struct task_struct *target,
const void *kbuf, const void __user *ubuf)
{
const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t);
+ const int fir_pos = fcr31_pos + sizeof(u32);
u32 fcr31;
int err;
@@ -591,6 +600,11 @@ static int fpr_set(struct task_struct *target,
ptrace_setfcr31(target, fcr31);
}
+ if (count > 0)
+ err = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
+ fir_pos,
+ fir_pos + sizeof(u32));
+
return err;
}
@@ -815,7 +829,7 @@ long arch_ptrace(struct task_struct *child, long request,
fregs = get_fpu_regs(child);
#ifdef CONFIG_32BIT
- if (test_thread_flag(TIF_32BIT_FPREGS)) {
+ if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) {
/*
* The odd registers are actually the high
* order bits of the values stored in the even
@@ -826,7 +840,7 @@ long arch_ptrace(struct task_struct *child, long request,
break;
}
#endif
- tmp = get_fpr32(&fregs[addr - FPR_BASE], 0);
+ tmp = get_fpr64(&fregs[addr - FPR_BASE], 0);
break;
case PC:
tmp = regs->cp0_epc;
@@ -904,7 +918,7 @@ long arch_ptrace(struct task_struct *child, long request,
init_fp_ctx(child);
#ifdef CONFIG_32BIT
- if (test_thread_flag(TIF_32BIT_FPREGS)) {
+ if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) {
/*
* The odd registers are actually the high
* order bits of the values stored in the even
diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c
index 283b5a1967d1..d95117e71f69 100644
--- a/arch/mips/kernel/ptrace32.c
+++ b/arch/mips/kernel/ptrace32.c
@@ -97,7 +97,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
break;
}
fregs = get_fpu_regs(child);
- if (test_thread_flag(TIF_32BIT_FPREGS)) {
+ if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) {
/*
* The odd registers are actually the high
* order bits of the values stored in the even
@@ -107,7 +107,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
addr & 1);
break;
}
- tmp = get_fpr32(&fregs[addr - FPR_BASE], 0);
+ tmp = get_fpr64(&fregs[addr - FPR_BASE], 0);
break;
case PC:
tmp = regs->cp0_epc;
@@ -203,7 +203,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
sizeof(child->thread.fpu));
child->thread.fpu.fcr31 = 0;
}
- if (test_thread_flag(TIF_32BIT_FPREGS)) {
+ if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) {
/*
* The odd registers are actually the high
* order bits of the values stored in the even
diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c
index a017b23ee4aa..8a95c3d76a9a 100644
--- a/arch/mips/kvm/mips.c
+++ b/arch/mips/kvm/mips.c
@@ -40,7 +40,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
{ "cache", VCPU_STAT(cache_exits), KVM_STAT_VCPU },
{ "signal", VCPU_STAT(signal_exits), KVM_STAT_VCPU },
{ "interrupt", VCPU_STAT(int_exits), KVM_STAT_VCPU },
- { "cop_unsuable", VCPU_STAT(cop_unusable_exits), KVM_STAT_VCPU },
+ { "cop_unusable", VCPU_STAT(cop_unusable_exits), KVM_STAT_VCPU },
{ "tlbmod", VCPU_STAT(tlbmod_exits), KVM_STAT_VCPU },
{ "tlbmiss_ld", VCPU_STAT(tlbmiss_ld_exits), KVM_STAT_VCPU },
{ "tlbmiss_st", VCPU_STAT(tlbmiss_st_exits), KVM_STAT_VCPU },
diff --git a/arch/mips/txx9/rbtx4939/setup.c b/arch/mips/txx9/rbtx4939/setup.c
index 37030409745c..586ca7ea3e7c 100644
--- a/arch/mips/txx9/rbtx4939/setup.c
+++ b/arch/mips/txx9/rbtx4939/setup.c
@@ -186,7 +186,7 @@ static void __init rbtx4939_update_ioc_pen(void)
#define RBTX4939_MAX_7SEGLEDS 8
-#if IS_ENABLED(CONFIG_LEDS_CLASS)
+#if IS_BUILTIN(CONFIG_LEDS_CLASS)
static u8 led_val[RBTX4939_MAX_7SEGLEDS];
struct rbtx4939_led_data {
struct led_classdev cdev;
@@ -261,7 +261,7 @@ static inline void rbtx4939_led_setup(void)
static void __rbtx4939_7segled_putc(unsigned int pos, unsigned char val)
{
-#if IS_ENABLED(CONFIG_LEDS_CLASS)
+#if IS_BUILTIN(CONFIG_LEDS_CLASS)
unsigned long flags;
local_irq_save(flags);
/* bit7: reserved for LED class */
diff --git a/arch/powerpc/include/asm/irq_work.h b/arch/powerpc/include/asm/irq_work.h
index 744fd54de374..1bcc84903930 100644
--- a/arch/powerpc/include/asm/irq_work.h
+++ b/arch/powerpc/include/asm/irq_work.h
@@ -5,5 +5,6 @@ static inline bool arch_irq_work_has_interrupt(void)
{
return true;
}
+extern void arch_irq_work_raise(void);
#endif /* _ASM_POWERPC_IRQ_WORK_H */
diff --git a/arch/powerpc/kernel/cpu_setup_power.S b/arch/powerpc/kernel/cpu_setup_power.S
index 9c9b7411b28b..55eb3b752ca0 100644
--- a/arch/powerpc/kernel/cpu_setup_power.S
+++ b/arch/powerpc/kernel/cpu_setup_power.S
@@ -27,6 +27,7 @@ _GLOBAL(__setup_cpu_power7)
beqlr
li r0,0
mtspr SPRN_LPID,r0
+ mtspr SPRN_PCR,r0
mfspr r3,SPRN_LPCR
bl __init_LPCR
bl __init_tlb_power7
@@ -40,6 +41,7 @@ _GLOBAL(__restore_cpu_power7)
beqlr
li r0,0
mtspr SPRN_LPID,r0
+ mtspr SPRN_PCR,r0
mfspr r3,SPRN_LPCR
bl __init_LPCR
bl __init_tlb_power7
@@ -55,6 +57,7 @@ _GLOBAL(__setup_cpu_power8)
beqlr
li r0,0
mtspr SPRN_LPID,r0
+ mtspr SPRN_PCR,r0
mfspr r3,SPRN_LPCR
ori r3, r3, LPCR_PECEDH
bl __init_LPCR
@@ -74,6 +77,7 @@ _GLOBAL(__restore_cpu_power8)
beqlr
li r0,0
mtspr SPRN_LPID,r0
+ mtspr SPRN_PCR,r0
mfspr r3,SPRN_LPCR
ori r3, r3, LPCR_PECEDH
bl __init_LPCR
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index 428563b195c3..767ac1572c02 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -3002,15 +3002,17 @@ static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu)
goto up_out;
psize = vma_kernel_pagesize(vma);
- porder = __ilog2(psize);
up_read(&current->mm->mmap_sem);
/* We can handle 4k, 64k or 16M pages in the VRMA */
- err = -EINVAL;
- if (!(psize == 0x1000 || psize == 0x10000 ||
- psize == 0x1000000))
- goto out_srcu;
+ if (psize >= 0x1000000)
+ psize = 0x1000000;
+ else if (psize >= 0x10000)
+ psize = 0x10000;
+ else
+ psize = 0x1000;
+ porder = __ilog2(psize);
/* Update VRMASD field in the LPCR */
senc = slb_pgsize_encoding(psize);
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
index 669a15e7fa76..3c4faa4c2742 100644
--- a/arch/powerpc/mm/numa.c
+++ b/arch/powerpc/mm/numa.c
@@ -551,7 +551,7 @@ static int numa_setup_cpu(unsigned long lcpu)
nid = of_node_to_nid_single(cpu);
out_present:
- if (nid < 0 || !node_online(nid))
+ if (nid < 0 || !node_possible(nid))
nid = first_online_node;
map_cpu_to_node(lcpu, nid);
@@ -951,6 +951,32 @@ static void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn)
NODE_DATA(nid)->node_spanned_pages = spanned_pages;
}
+static void __init find_possible_nodes(void)
+{
+ struct device_node *rtas;
+ u32 numnodes, i;
+
+ if (min_common_depth <= 0)
+ return;
+
+ rtas = of_find_node_by_path("/rtas");
+ if (!rtas)
+ return;
+
+ if (of_property_read_u32_index(rtas,
+ "ibm,max-associativity-domains",
+ min_common_depth, &numnodes))
+ goto out;
+
+ for (i = 0; i < numnodes; i++) {
+ if (!node_possible(i))
+ node_set(i, node_possible_map);
+ }
+
+out:
+ of_node_put(rtas);
+}
+
void __init initmem_init(void)
{
int nid, cpu;
@@ -966,12 +992,15 @@ void __init initmem_init(void)
memblock_dump_all();
/*
- * Reduce the possible NUMA nodes to the online NUMA nodes,
- * since we do not support node hotplug. This ensures that we
- * lower the maximum NUMA node ID to what is actually present.
+ * Modify the set of possible NUMA nodes to reflect information
+ * available about the set of online nodes, and the set of nodes
+ * that we expect to make use of for this platform's affinity
+ * calculations.
*/
nodes_and(node_possible_map, node_possible_map, node_online_map);
+ find_possible_nodes();
+
for_each_online_node(nid) {
unsigned long start_pfn, end_pfn;
@@ -1304,6 +1333,40 @@ static long vphn_get_associativity(unsigned long cpu,
return rc;
}
+static inline int find_and_online_cpu_nid(int cpu)
+{
+ __be32 associativity[VPHN_ASSOC_BUFSIZE] = {0};
+ int new_nid;
+
+ /* Use associativity from first thread for all siblings */
+ vphn_get_associativity(cpu, associativity);
+ new_nid = associativity_to_nid(associativity);
+ if (new_nid < 0 || !node_possible(new_nid))
+ new_nid = first_online_node;
+
+ if (NODE_DATA(new_nid) == NULL) {
+#ifdef CONFIG_MEMORY_HOTPLUG
+ /*
+ * Need to ensure that NODE_DATA is initialized for a node from
+ * available memory (see memblock_alloc_try_nid). If unable to
+ * init the node, then default to nearest node that has memory
+ * installed.
+ */
+ if (try_online_node(new_nid))
+ new_nid = first_online_node;
+#else
+ /*
+ * Default to using the nearest node that has memory installed.
+ * Otherwise, it would be necessary to patch the kernel MM code
+ * to deal with more memoryless-node error conditions.
+ */
+ new_nid = first_online_node;
+#endif
+ }
+
+ return new_nid;
+}
+
/*
* Update the CPU maps and sysfs entries for a single CPU when its NUMA
* characteristics change. This function doesn't perform any locking and is
@@ -1369,7 +1432,6 @@ int arch_update_cpu_topology(void)
{
unsigned int cpu, sibling, changed = 0;
struct topology_update_data *updates, *ud;
- __be32 associativity[VPHN_ASSOC_BUFSIZE] = {0};
cpumask_t updated_cpus;
struct device *dev;
int weight, new_nid, i = 0;
@@ -1404,11 +1466,7 @@ int arch_update_cpu_topology(void)
continue;
}
- /* Use associativity from first thread for all siblings */
- vphn_get_associativity(cpu, associativity);
- new_nid = associativity_to_nid(associativity);
- if (new_nid < 0 || !node_online(new_nid))
- new_nid = first_online_node;
+ new_nid = find_and_online_cpu_nid(cpu);
if (new_nid == numa_cpu_lookup_table[cpu]) {
cpumask_andnot(&cpu_associativity_changes_mask,
diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index 2d66a8446198..345e255c06a2 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -329,6 +329,9 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image,
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4);
PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, len));
break;
+ case BPF_LDX | BPF_W | BPF_ABS: /* A = *((u32 *)(seccomp_data + K)); */
+ PPC_LWZ_OFFS(r_A, r_skb, K);
+ break;
case BPF_LDX | BPF_W | BPF_LEN: /* X = skb->len; */
PPC_LWZ_OFFS(r_X, r_skb, offsetof(struct sk_buff, len));
break;
diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
index 4eba7c00ea1f..30e2e8efbe6b 100644
--- a/arch/powerpc/perf/core-book3s.c
+++ b/arch/powerpc/perf/core-book3s.c
@@ -448,6 +448,16 @@ static void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw)
/* invalid entry */
continue;
+ /*
+ * BHRB rolling buffer could very much contain the kernel
+ * addresses at this point. Check the privileges before
+ * exporting it to userspace (avoid exposure of regions
+ * where we could have speculative execution)
+ */
+ if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN) &&
+ is_kernel_addr(addr))
+ continue;
+
/* Branches are read most recent first (ie. mfbhrb 0 is
* the most recent branch).
* There are two types of valid entries:
@@ -1188,6 +1198,7 @@ static void power_pmu_disable(struct pmu *pmu)
*/
write_mmcr0(cpuhw, val);
mb();
+ isync();
/*
* Disable instruction sampling if it was enabled
@@ -1196,12 +1207,26 @@ static void power_pmu_disable(struct pmu *pmu)
mtspr(SPRN_MMCRA,
cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE);
mb();
+ isync();
}
cpuhw->disabled = 1;
cpuhw->n_added = 0;
ebb_switch_out(mmcr0);
+
+#ifdef CONFIG_PPC64
+ /*
+ * These are readable by userspace, may contain kernel
+ * addresses and are not switched by context switch, so clear
+ * them now to avoid leaking anything to userspace in general
+ * including to another process.
+ */
+ if (ppmu->flags & PPMU_ARCH_207S) {
+ mtspr(SPRN_SDAR, 0);
+ mtspr(SPRN_SIAR, 0);
+ }
+#endif
}
local_irq_restore(flags);
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index 2a0452e364ba..d11f931cac69 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -626,7 +626,7 @@ static inline u32 mpic_physmask(u32 cpumask)
int i;
u32 mask = 0;
- for (i = 0; i < min(32, NR_CPUS); ++i, cpumask >>= 1)
+ for (i = 0; i < min(32, NR_CPUS) && cpu_possible(i); ++i, cpumask >>= 1)
mask |= (cpumask & 1) << get_hard_smp_processor_id(i);
return mask;
}
diff --git a/arch/s390/include/asm/nospec-insn.h b/arch/s390/include/asm/nospec-insn.h
index 087fc9b972c5..9a56e738d645 100644
--- a/arch/s390/include/asm/nospec-insn.h
+++ b/arch/s390/include/asm/nospec-insn.h
@@ -2,10 +2,15 @@
#ifndef _ASM_S390_NOSPEC_ASM_H
#define _ASM_S390_NOSPEC_ASM_H
+#include <asm/alternative-asm.h>
+#include <asm/asm-offsets.h>
+
#ifdef __ASSEMBLY__
#ifdef CONFIG_EXPOLINE
+_LC_BR_R1 = __LC_BR_R1
+
/*
* The expoline macros are used to create thunks in the same format
* as gcc generates them. The 'comdat' section flag makes sure that
@@ -101,13 +106,21 @@
.endm
.macro __THUNK_EX_BR reg,ruse
+ # Be very careful when adding instructions to this macro!
+ # The ALTERNATIVE replacement code has a .+10 which targets
+ # the "br \reg" after the code has been patched.
#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES
exrl 0,555f
j .
#else
+ .ifc \reg,%r1
+ ALTERNATIVE "ex %r0,_LC_BR_R1", ".insn ril,0xc60000000000,0,.+10", 35
+ j .
+ .else
larl \ruse,555f
ex 0,0(\ruse)
j .
+ .endif
#endif
555: br \reg
.endm
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
index dc6c9c604543..39572281e213 100644
--- a/arch/s390/kernel/asm-offsets.c
+++ b/arch/s390/kernel/asm-offsets.c
@@ -170,6 +170,7 @@ int main(void)
OFFSET(__LC_MACHINE_FLAGS, _lowcore, machine_flags);
OFFSET(__LC_GMAP, _lowcore, gmap);
OFFSET(__LC_PASTE, _lowcore, paste);
+ OFFSET(__LC_BR_R1, _lowcore, br_r1_trampoline);
/* software defined ABI-relevant lowcore locations 0xe00 - 0xe20 */
OFFSET(__LC_DUMP_REIPL, _lowcore, ipib);
/* hardware defined lowcore locations 0x1000 - 0x18ff */
diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
index e499370fbccb..6c1c7d399bf9 100644
--- a/arch/s390/kernel/mcount.S
+++ b/arch/s390/kernel/mcount.S
@@ -8,12 +8,16 @@
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#include <asm/ftrace.h>
+#include <asm/nospec-insn.h>
#include <asm/ptrace.h>
+ GEN_BR_THUNK %r1
+ GEN_BR_THUNK %r14
+
.section .kprobes.text, "ax"
ENTRY(ftrace_stub)
- br %r14
+ BR_EX %r14
#define STACK_FRAME_SIZE (STACK_FRAME_OVERHEAD + __PT_SIZE)
#define STACK_PTREGS (STACK_FRAME_OVERHEAD)
@@ -21,7 +25,7 @@ ENTRY(ftrace_stub)
#define STACK_PTREGS_PSW (STACK_PTREGS + __PT_PSW)
ENTRY(_mcount)
- br %r14
+ BR_EX %r14
ENTRY(ftrace_caller)
.globl ftrace_regs_caller
@@ -49,7 +53,7 @@ ENTRY(ftrace_caller)
#endif
lgr %r3,%r14
la %r5,STACK_PTREGS(%r15)
- basr %r14,%r1
+ BASR_EX %r14,%r1
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
# The j instruction gets runtime patched to a nop instruction.
# See ftrace_enable_ftrace_graph_caller.
@@ -64,7 +68,7 @@ ftrace_graph_caller_end:
#endif
lg %r1,(STACK_PTREGS_PSW+8)(%r15)
lmg %r2,%r15,(STACK_PTREGS_GPRS+2*8)(%r15)
- br %r1
+ BR_EX %r1
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
@@ -77,6 +81,6 @@ ENTRY(return_to_handler)
aghi %r15,STACK_FRAME_OVERHEAD
lgr %r14,%r2
lmg %r2,%r5,32(%r15)
- br %r14
+ BR_EX %r14
#endif
diff --git a/arch/sh/kernel/entry-common.S b/arch/sh/kernel/entry-common.S
index 13047a4facd2..5a9017ba26ab 100644
--- a/arch/sh/kernel/entry-common.S
+++ b/arch/sh/kernel/entry-common.S
@@ -255,7 +255,7 @@ debug_trap:
mov.l @r8, r8
jsr @r8
nop
- bra __restore_all
+ bra ret_from_exception
nop
CFI_ENDPROC
diff --git a/arch/sh/kernel/sh_ksyms_32.c b/arch/sh/kernel/sh_ksyms_32.c
index d77f2f6c7ff0..0b30b9dfc87f 100644
--- a/arch/sh/kernel/sh_ksyms_32.c
+++ b/arch/sh/kernel/sh_ksyms_32.c
@@ -34,6 +34,9 @@ DECLARE_EXPORT(__sdivsi3);
DECLARE_EXPORT(__lshrsi3);
DECLARE_EXPORT(__ashrsi3);
DECLARE_EXPORT(__ashlsi3);
+DECLARE_EXPORT(__lshrsi3_r0);
+DECLARE_EXPORT(__ashrsi3_r0);
+DECLARE_EXPORT(__ashlsi3_r0);
DECLARE_EXPORT(__ashiftrt_r4_6);
DECLARE_EXPORT(__ashiftrt_r4_7);
DECLARE_EXPORT(__ashiftrt_r4_8);
diff --git a/arch/sh/lib/ashlsi3.S b/arch/sh/lib/ashlsi3.S
index bd47e9b403a5..70a6434945ab 100644
--- a/arch/sh/lib/ashlsi3.S
+++ b/arch/sh/lib/ashlsi3.S
@@ -54,21 +54,38 @@ Boston, MA 02110-1301, USA. */
!
! (none)
!
+! __ashlsi3_r0
+!
+! Entry:
+!
+! r4: Value to shift
+! r0: Shifts
+!
+! Exit:
+!
+! r0: Result
+!
+! Destroys:
+!
+! (none)
+
+
.global __ashlsi3
+ .global __ashlsi3_r0
.align 2
__ashlsi3:
- mov #31,r0
- and r0,r5
+ mov r5,r0
+ .align 2
+__ashlsi3_r0:
+ and #31,r0
+ mov.l r4,@-r15
+ mov r0,r4
mova ashlsi3_table,r0
- mov.b @(r0,r5),r5
-#ifdef __sh1__
- add r5,r0
+ mov.b @(r0,r4),r4
+ add r4,r0
jmp @r0
-#else
- braf r5
-#endif
- mov r4,r0
+ mov.l @r15+,r0
.align 2
ashlsi3_table:
diff --git a/arch/sh/lib/ashrsi3.S b/arch/sh/lib/ashrsi3.S
index 6f3cf46b77c2..602599d80209 100644
--- a/arch/sh/lib/ashrsi3.S
+++ b/arch/sh/lib/ashrsi3.S
@@ -54,22 +54,37 @@ Boston, MA 02110-1301, USA. */
!
! (none)
!
+! __ashrsi3_r0
+!
+! Entry:
+!
+! r4: Value to shift
+! r0: Shifts
+!
+! Exit:
+!
+! r0: Result
+!
+! Destroys:
+!
+! (none)
.global __ashrsi3
+ .global __ashrsi3_r0
.align 2
__ashrsi3:
- mov #31,r0
- and r0,r5
+ mov r5,r0
+ .align 2
+__ashrsi3_r0:
+ and #31,r0
+ mov.l r4,@-r15
+ mov r0,r4
mova ashrsi3_table,r0
- mov.b @(r0,r5),r5
-#ifdef __sh1__
- add r5,r0
+ mov.b @(r0,r4),r4
+ add r4,r0
jmp @r0
-#else
- braf r5
-#endif
- mov r4,r0
+ mov.l @r15+,r0
.align 2
ashrsi3_table:
diff --git a/arch/sh/lib/lshrsi3.S b/arch/sh/lib/lshrsi3.S
index 1e7aaa557130..f2a6959f526d 100644
--- a/arch/sh/lib/lshrsi3.S
+++ b/arch/sh/lib/lshrsi3.S
@@ -54,21 +54,37 @@ Boston, MA 02110-1301, USA. */
!
! (none)
!
+! __lshrsi3_r0
+!
+! Entry:
+!
+! r0: Value to shift
+! r5: Shifts
+!
+! Exit:
+!
+! r0: Result
+!
+! Destroys:
+!
+! (none)
+!
.global __lshrsi3
+ .global __lshrsi3_r0
.align 2
__lshrsi3:
- mov #31,r0
- and r0,r5
+ mov r5,r0
+ .align 2
+__lshrsi3_r0:
+ and #31,r0
+ mov.l r4,@-r15
+ mov r0,r4
mova lshrsi3_table,r0
- mov.b @(r0,r5),r5
-#ifdef __sh1__
- add r5,r0
+ mov.b @(r0,r4),r4
+ add r4,r0
jmp @r0
-#else
- braf r5
-#endif
- mov r4,r0
+ mov.l @r15+,r0
.align 2
lshrsi3_table:
diff --git a/arch/sparc/include/asm/atomic_64.h b/arch/sparc/include/asm/atomic_64.h
index f2fbf9e16faf..29070c9a70f9 100644
--- a/arch/sparc/include/asm/atomic_64.h
+++ b/arch/sparc/include/asm/atomic_64.h
@@ -74,7 +74,11 @@ ATOMIC_OP(xor)
#define atomic64_add_negative(i, v) (atomic64_add_return(i, v) < 0)
#define atomic_cmpxchg(v, o, n) (cmpxchg(&((v)->counter), (o), (n)))
-#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
+
+static inline int atomic_xchg(atomic_t *v, int new)
+{
+ return xchg(&v->counter, new);
+}
static inline int __atomic_add_unless(atomic_t *v, int a, int u)
{
diff --git a/arch/sparc/kernel/ds.c b/arch/sparc/kernel/ds.c
index f87a55d77094..9b3f2e212b37 100644
--- a/arch/sparc/kernel/ds.c
+++ b/arch/sparc/kernel/ds.c
@@ -908,7 +908,7 @@ static int register_services(struct ds_info *dp)
pbuf.req.handle = cp->handle;
pbuf.req.major = 1;
pbuf.req.minor = 0;
- strcpy(pbuf.req.svc_id, cp->service_id);
+ strcpy(pbuf.id_buf, cp->service_id);
err = __ds_send(lp, &pbuf, msg_len);
if (err > 0)
diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig
index 9f0107157b8f..71026930c04c 100644
--- a/arch/x86/configs/x86_64_cuttlefish_defconfig
+++ b/arch/x86/configs/x86_64_cuttlefish_defconfig
@@ -11,6 +11,7 @@ CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_SCHED=y
@@ -56,6 +57,7 @@ CONFIG_RANDOMIZE_BASE=y
CONFIG_PHYSICAL_ALIGN=0x1000000
CONFIG_CMDLINE_BOOL=y
CONFIG_CMDLINE="console=ttyS0 reboot=p nopti"
+CONFIG_PM_AUTOSLEEP=y
CONFIG_PM_WAKELOCKS=y
CONFIG_PM_WAKELOCKS_LIMIT=0
# CONFIG_PM_WAKELOCKS_GC is not set
@@ -88,8 +90,8 @@ CONFIG_IP_MROUTE=y
CONFIG_IP_PIMSM_V1=y
CONFIG_IP_PIMSM_V2=y
CONFIG_SYN_COOKIES=y
+CONFIG_NET_IPVTI=y
CONFIG_INET_ESP=y
-# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
# CONFIG_INET_XFRM_MODE_BEET is not set
# CONFIG_INET_LRO is not set
CONFIG_INET_DIAG_DESTROY=y
@@ -105,6 +107,7 @@ CONFIG_INET6_AH=y
CONFIG_INET6_ESP=y
CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_VTI=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_NETLABEL=y
CONFIG_NETFILTER=y
@@ -298,7 +301,6 @@ CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_HIDRAW=y
CONFIG_UHID=y
-# CONFIG_HID_GENERIC is not set
CONFIG_HID_A4TECH=y
CONFIG_HID_ACRUX=y
CONFIG_HID_ACRUX_FF=y
@@ -362,6 +364,8 @@ CONFIG_USB_GADGET=y
CONFIG_USB_DUMMY_HCD=y
CONFIG_USB_CONFIGFS=y
CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_F_MTP=y
+CONFIG_USB_CONFIGFS_F_PTP=y
CONFIG_USB_CONFIGFS_F_ACC=y
CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y
CONFIG_USB_CONFIGFS_UEVENT=y
@@ -390,6 +394,9 @@ CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_POSIX_ACL=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_EXT4_ENCRYPTION=y
+CONFIG_F2FS_FS=y
+CONFIG_F2FS_FS_SECURITY=y
+CONFIG_F2FS_FS_ENCRYPTION=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
# CONFIG_PRINT_QUOTA_WARNING is not set
@@ -423,7 +430,6 @@ CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_DEBUG_STACKOVERFLOW=y
CONFIG_LOCKUP_DETECTOR=y
CONFIG_PANIC_TIMEOUT=5
-# CONFIG_SCHED_DEBUG is not set
CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
CONFIG_RCU_CPU_STALL_TIMEOUT=60
@@ -440,3 +446,4 @@ CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set
CONFIG_CRYPTO_ECHAINIV=y
+CONFIG_CRYPTO_SHA512=y
diff --git a/arch/x86/configs/x86_64_ranchu_defconfig b/arch/x86/configs/x86_64_ranchu_defconfig
index e8ed8eef62ec..5cf2450842ab 100644
--- a/arch/x86/configs/x86_64_ranchu_defconfig
+++ b/arch/x86/configs/x86_64_ranchu_defconfig
@@ -1,5 +1,6 @@
# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_POSIX_MQUEUE=y
+# CONFIG_USELIB is not set
CONFIG_AUDIT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -8,20 +9,29 @@ 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_CGROUPS=y
CONFIG_CGROUP_DEBUG=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_SCHED=y
CONFIG_RT_GROUP_SCHED=y
+CONFIG_NAMESPACES=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_SYSCTL_SYSCALL=y
CONFIG_KALLSYMS_ALL=y
+CONFIG_BPF_SYSCALL=y
CONFIG_EMBEDDED=y
# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
CONFIG_ARCH_MMAP_RND_BITS=32
CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_OSF_PARTITION=y
CONFIG_AMIGA_PARTITION=y
@@ -34,6 +44,9 @@ CONFIG_SGI_PARTITION=y
CONFIG_SUN_PARTITION=y
CONFIG_KARMA_PARTITION=y
CONFIG_SMP=y
+CONFIG_GOLDFISH=y
+CONFIG_HYPERVISOR_GUEST=y
+CONFIG_PARAVIRT=y
CONFIG_MCORE2=y
CONFIG_MAXSMP=y
CONFIG_PREEMPT=y
@@ -47,6 +60,9 @@ CONFIG_EFI=y
CONFIG_EFI_STUB=y
CONFIG_HZ_100=y
CONFIG_PHYSICAL_START=0x100000
+CONFIG_RANDOMIZE_BASE=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="nopti"
CONFIG_PM_AUTOSLEEP=y
CONFIG_PM_WAKELOCKS=y
CONFIG_PM_WAKELOCKS_LIMIT=0
@@ -69,6 +85,7 @@ CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_XFRM_USER=y
+CONFIG_XFRM_STATISTICS=y
CONFIG_NET_KEY=y
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
@@ -84,6 +101,8 @@ CONFIG_IP_MROUTE=y
CONFIG_IP_PIMSM_V1=y
CONFIG_IP_PIMSM_V2=y
CONFIG_SYN_COOKIES=y
+CONFIG_NET_IPVTI=y
+CONFIG_INET_AH=y
CONFIG_INET_ESP=y
# CONFIG_INET_XFRM_MODE_BEET is not set
# CONFIG_INET_LRO is not set
@@ -95,6 +114,7 @@ CONFIG_INET6_AH=y
CONFIG_INET6_ESP=y
CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_VTI=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_NETLABEL=y
CONFIG_NETFILTER=y
@@ -153,6 +173,10 @@ CONFIG_IP_NF_MATCH_ECN=y
CONFIG_IP_NF_MATCH_TTL=y
CONFIG_IP_NF_FILTER=y
CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_NF_MANGLE=y
CONFIG_IP_NF_RAW=y
CONFIG_IP_NF_SECURITY=y
@@ -161,6 +185,7 @@ 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_RPFILTER=y
CONFIG_IP6_NF_FILTER=y
CONFIG_IP6_NF_TARGET_REJECT=y
CONFIG_IP6_NF_MANGLE=y
@@ -177,10 +202,13 @@ CONFIG_MAC80211_LEDS=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DMA_CMA=y
CONFIG_CONNECTOR=y
+CONFIG_OF=y
+CONFIG_OF_UNITTEST=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_BLK_DEV_RAM_SIZE=16384
CONFIG_VIRTIO_BLK=y
+CONFIG_UID_SYS_STATS=y
CONFIG_BLK_DEV_SD=y
CONFIG_BLK_DEV_SR=y
CONFIG_BLK_DEV_SR_VENDOR=y
@@ -210,6 +238,7 @@ CONFIG_DM_VERITY_FEC=y
CONFIG_NETDEVICES=y
CONFIG_NETCONSOLE=y
CONFIG_TUN=y
+CONFIG_VETH=y
CONFIG_VIRTIO_NET=y
CONFIG_BNX2=y
CONFIG_TIGON3=y
@@ -231,6 +260,7 @@ CONFIG_PPP_MPPE=y
CONFIG_PPPOLAC=y
CONFIG_PPPOPNS=y
CONFIG_USB_USBNET=y
+CONFIG_MAC80211_HWSIM=y
CONFIG_INPUT_POLLDEV=y
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_INPUT_EVDEV=y
@@ -263,7 +293,10 @@ CONFIG_SERIAL_NONSTANDARD=y
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_VIRTIO_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_VIRTIO=y
CONFIG_NVRAM=y
+# CONFIG_DEVPORT is not set
CONFIG_I2C_I801=y
CONFIG_BATTERY_GOLDFISH=y
CONFIG_WATCHDOG=y
@@ -280,6 +313,18 @@ CONFIG_BACKLIGHT_LCD_SUPPORT=y
# CONFIG_LCD_CLASS_DEVICE is not set
CONFIG_SOUND=y
CONFIG_SND=y
+CONFIG_SND_HDA_INTEL=y
+CONFIG_SND_HDA_CODEC_REALTEK=y
+CONFIG_SND_HDA_CODEC_ANALOG=y
+CONFIG_SND_HDA_CODEC_SIGMATEL=y
+CONFIG_SND_HDA_CODEC_VIA=y
+CONFIG_SND_HDA_CODEC_HDMI=y
+CONFIG_SND_HDA_CODEC_CIRRUS=y
+CONFIG_SND_HDA_CODEC_CONEXANT=y
+CONFIG_SND_HDA_CODEC_CA0110=y
+CONFIG_SND_HDA_CODEC_CA0132=y
+CONFIG_SND_HDA_CODEC_CMEDIA=y
+CONFIG_SND_HDA_CODEC_SI3054=y
CONFIG_HIDRAW=y
CONFIG_UHID=y
CONFIG_HID_A4TECH=y
@@ -349,6 +394,15 @@ CONFIG_USB_UHCI_HCD=y
CONFIG_USB_PRINTER=y
CONFIG_USB_STORAGE=y
CONFIG_USB_OTG_WAKELOCK=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_F_MTP=y
+CONFIG_USB_CONFIGFS_F_PTP=y
+CONFIG_USB_CONFIGFS_F_ACC=y
+CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y
+CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_MIDI=y
CONFIG_EDAC=y
CONFIG_RTC_CLASS=y
# CONFIG_RTC_HCTOSYS is not set
@@ -362,8 +416,6 @@ 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
@@ -373,6 +425,7 @@ CONFIG_EXT4_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
# CONFIG_PRINT_QUOTA_WARNING is not set
+CONFIG_QFMT_V2=y
CONFIG_FUSE_FS=y
CONFIG_ISO9660_FS=y
CONFIG_JOLIET=y
@@ -383,8 +436,10 @@ CONFIG_PROC_KCORE=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_HUGETLBFS=y
+CONFIG_SDCARD_FS=y
CONFIG_PSTORE=y
CONFIG_PSTORE_CONSOLE=y
+CONFIG_PSTORE_PMSG=y
CONFIG_PSTORE_RAM=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_DEFAULT="utf8"
@@ -399,16 +454,24 @@ CONFIG_DEBUG_INFO=y
# CONFIG_UNUSED_SYMBOLS is not set
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_DETECT_HUNG_TASK=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_DEBUG_SET_MODULE_RONX=y
CONFIG_KEYS=y
+CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
CONFIG_SECURITY=y
CONFIG_SECURITY_NETWORK=y
+CONFIG_HARDENED_USERCOPY=y
CONFIG_SECURITY_SELINUX=y
+CONFIG_CRYPTO_USER=y
+CONFIG_CRYPTO_ECHAINIV=y
+CONFIG_CRYPTO_SHA512=y
+CONFIG_CRYPTO_AES_X86_64=y
CONFIG_CRYPTO_TWOFISH=y
CONFIG_ASYMMETRIC_KEY_TYPE=y
CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
@@ -417,3 +480,4 @@ CONFIG_PKCS7_MESSAGE_PARSER=y
CONFIG_PKCS7_TEST_KEY=y
# CONFIG_VIRTUALIZATION is not set
CONFIG_CRC_T10DIF=y
+CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder,vndbinder"
diff --git a/arch/x86/crypto/chacha20_glue.c b/arch/x86/crypto/chacha20_glue.c
index 722bacea040e..8baaff5af0b5 100644
--- a/arch/x86/crypto/chacha20_glue.c
+++ b/arch/x86/crypto/chacha20_glue.c
@@ -125,7 +125,7 @@ static struct crypto_alg alg = {
static int __init chacha20_simd_mod_init(void)
{
- if (!cpu_has_ssse3)
+ if (!boot_cpu_has(X86_FEATURE_SSSE3))
return -ENODEV;
#ifdef CONFIG_AS_AVX2
diff --git a/arch/x86/crypto/crc32c-intel_glue.c b/arch/x86/crypto/crc32c-intel_glue.c
index 81a595d75cf5..15f5c7675d42 100644
--- a/arch/x86/crypto/crc32c-intel_glue.c
+++ b/arch/x86/crypto/crc32c-intel_glue.c
@@ -58,16 +58,11 @@
asmlinkage unsigned int crc_pcl(const u8 *buffer, int len,
unsigned int crc_init);
static int crc32c_pcl_breakeven = CRC32C_PCL_BREAKEVEN_EAGERFPU;
-#if defined(X86_FEATURE_EAGER_FPU)
#define set_pcl_breakeven_point() \
do { \
if (!use_eager_fpu()) \
crc32c_pcl_breakeven = CRC32C_PCL_BREAKEVEN_NOEAGERFPU; \
} while (0)
-#else
-#define set_pcl_breakeven_point() \
- (crc32c_pcl_breakeven = CRC32C_PCL_BREAKEVEN_NOEAGERFPU)
-#endif
#endif /* CONFIG_X86_64 */
static u32 crc32c_intel_le_hw_byte(u32 crc, unsigned char const *data, size_t length)
@@ -257,7 +252,7 @@ static int __init crc32c_intel_mod_init(void)
if (!x86_match_cpu(crc32c_cpu_id))
return -ENODEV;
#ifdef CONFIG_X86_64
- if (cpu_has_pclmulqdq) {
+ if (boot_cpu_has(X86_FEATURE_PCLMULQDQ)) {
alg.update = crc32c_pcl_intel_update;
alg.finup = crc32c_pcl_intel_finup;
alg.digest = crc32c_pcl_intel_digest;
diff --git a/arch/x86/include/asm/cmpxchg_32.h b/arch/x86/include/asm/cmpxchg_32.h
index f7e142926481..e4959d023af8 100644
--- a/arch/x86/include/asm/cmpxchg_32.h
+++ b/arch/x86/include/asm/cmpxchg_32.h
@@ -109,6 +109,6 @@ static inline u64 __cmpxchg64_local(volatile u64 *ptr, u64 old, u64 new)
#endif
-#define system_has_cmpxchg_double() cpu_has_cx8
+#define system_has_cmpxchg_double() boot_cpu_has(X86_FEATURE_CX8)
#endif /* _ASM_X86_CMPXCHG_32_H */
diff --git a/arch/x86/include/asm/cmpxchg_64.h b/arch/x86/include/asm/cmpxchg_64.h
index 1af94697aae5..caa23a34c963 100644
--- a/arch/x86/include/asm/cmpxchg_64.h
+++ b/arch/x86/include/asm/cmpxchg_64.h
@@ -18,6 +18,6 @@ static inline void set_64bit(volatile u64 *ptr, u64 val)
cmpxchg_local((ptr), (o), (n)); \
})
-#define system_has_cmpxchg_double() cpu_has_cx16
+#define system_has_cmpxchg_double() boot_cpu_has(X86_FEATURE_CX16)
#endif /* _ASM_X86_CMPXCHG_64_H */
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h
index 641f0f2c2982..232621c5e859 100644
--- a/arch/x86/include/asm/cpufeature.h
+++ b/arch/x86/include/asm/cpufeature.h
@@ -104,7 +104,7 @@
#define X86_FEATURE_EXTD_APICID ( 3*32+26) /* has extended APICID (8 bits) */
#define X86_FEATURE_AMD_DCM ( 3*32+27) /* multi-node processor */
#define X86_FEATURE_APERFMPERF ( 3*32+28) /* APERFMPERF */
-#define X86_FEATURE_EAGER_FPU ( 3*32+29) /* "eagerfpu" Non lazy FPU restore */
+/* free, was #define X86_FEATURE_EAGER_FPU ( 3*32+29) * "eagerfpu" Non lazy FPU restore */
#define X86_FEATURE_NONSTOP_TSC_S3 ( 3*32+30) /* TSC doesn't stop in S3 state */
/* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */
@@ -368,58 +368,29 @@ extern const char * const x86_bug_flags[NBUGINTS*32];
#define setup_force_cpu_bug(bit) setup_force_cpu_cap(bit)
#define cpu_has_fpu boot_cpu_has(X86_FEATURE_FPU)
-#define cpu_has_de boot_cpu_has(X86_FEATURE_DE)
#define cpu_has_pse boot_cpu_has(X86_FEATURE_PSE)
#define cpu_has_tsc boot_cpu_has(X86_FEATURE_TSC)
#define cpu_has_pge boot_cpu_has(X86_FEATURE_PGE)
#define cpu_has_apic boot_cpu_has(X86_FEATURE_APIC)
-#define cpu_has_sep boot_cpu_has(X86_FEATURE_SEP)
-#define cpu_has_mtrr boot_cpu_has(X86_FEATURE_MTRR)
-#define cpu_has_mmx boot_cpu_has(X86_FEATURE_MMX)
#define cpu_has_fxsr boot_cpu_has(X86_FEATURE_FXSR)
#define cpu_has_xmm boot_cpu_has(X86_FEATURE_XMM)
#define cpu_has_xmm2 boot_cpu_has(X86_FEATURE_XMM2)
-#define cpu_has_xmm3 boot_cpu_has(X86_FEATURE_XMM3)
-#define cpu_has_ssse3 boot_cpu_has(X86_FEATURE_SSSE3)
#define cpu_has_aes boot_cpu_has(X86_FEATURE_AES)
#define cpu_has_avx boot_cpu_has(X86_FEATURE_AVX)
#define cpu_has_avx2 boot_cpu_has(X86_FEATURE_AVX2)
-#define cpu_has_ht boot_cpu_has(X86_FEATURE_HT)
-#define cpu_has_nx boot_cpu_has(X86_FEATURE_NX)
-#define cpu_has_xstore boot_cpu_has(X86_FEATURE_XSTORE)
-#define cpu_has_xstore_enabled boot_cpu_has(X86_FEATURE_XSTORE_EN)
-#define cpu_has_xcrypt boot_cpu_has(X86_FEATURE_XCRYPT)
-#define cpu_has_xcrypt_enabled boot_cpu_has(X86_FEATURE_XCRYPT_EN)
-#define cpu_has_ace2 boot_cpu_has(X86_FEATURE_ACE2)
-#define cpu_has_ace2_enabled boot_cpu_has(X86_FEATURE_ACE2_EN)
-#define cpu_has_phe boot_cpu_has(X86_FEATURE_PHE)
-#define cpu_has_phe_enabled boot_cpu_has(X86_FEATURE_PHE_EN)
-#define cpu_has_pmm boot_cpu_has(X86_FEATURE_PMM)
-#define cpu_has_pmm_enabled boot_cpu_has(X86_FEATURE_PMM_EN)
-#define cpu_has_ds boot_cpu_has(X86_FEATURE_DS)
-#define cpu_has_pebs boot_cpu_has(X86_FEATURE_PEBS)
#define cpu_has_clflush boot_cpu_has(X86_FEATURE_CLFLUSH)
-#define cpu_has_bts boot_cpu_has(X86_FEATURE_BTS)
#define cpu_has_gbpages boot_cpu_has(X86_FEATURE_GBPAGES)
#define cpu_has_arch_perfmon boot_cpu_has(X86_FEATURE_ARCH_PERFMON)
#define cpu_has_pat boot_cpu_has(X86_FEATURE_PAT)
-#define cpu_has_xmm4_1 boot_cpu_has(X86_FEATURE_XMM4_1)
-#define cpu_has_xmm4_2 boot_cpu_has(X86_FEATURE_XMM4_2)
#define cpu_has_x2apic boot_cpu_has(X86_FEATURE_X2APIC)
#define cpu_has_xsave boot_cpu_has(X86_FEATURE_XSAVE)
-#define cpu_has_xsaveopt boot_cpu_has(X86_FEATURE_XSAVEOPT)
#define cpu_has_xsaves boot_cpu_has(X86_FEATURE_XSAVES)
#define cpu_has_osxsave boot_cpu_has(X86_FEATURE_OSXSAVE)
#define cpu_has_hypervisor boot_cpu_has(X86_FEATURE_HYPERVISOR)
-#define cpu_has_pclmulqdq boot_cpu_has(X86_FEATURE_PCLMULQDQ)
-#define cpu_has_perfctr_core boot_cpu_has(X86_FEATURE_PERFCTR_CORE)
-#define cpu_has_perfctr_nb boot_cpu_has(X86_FEATURE_PERFCTR_NB)
-#define cpu_has_perfctr_l2 boot_cpu_has(X86_FEATURE_PERFCTR_L2)
-#define cpu_has_cx8 boot_cpu_has(X86_FEATURE_CX8)
-#define cpu_has_cx16 boot_cpu_has(X86_FEATURE_CX16)
-#define cpu_has_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU)
-#define cpu_has_topoext boot_cpu_has(X86_FEATURE_TOPOEXT)
-#define cpu_has_bpext boot_cpu_has(X86_FEATURE_BPEXT)
+/*
+ * Do not add any more of those clumsy macros - use static_cpu_has_safe() for
+ * fast paths and boot_cpu_has() otherwise!
+ */
#if __GNUC__ >= 4
extern void warn_pre_alternatives(void);
diff --git a/arch/x86/include/asm/fpu/internal.h b/arch/x86/include/asm/fpu/internal.h
index 3c3550c3a4a3..146d838e6ee7 100644
--- a/arch/x86/include/asm/fpu/internal.h
+++ b/arch/x86/include/asm/fpu/internal.h
@@ -42,6 +42,7 @@ extern void fpu__init_cpu_xstate(void);
extern void fpu__init_system(struct cpuinfo_x86 *c);
extern void fpu__init_check_bugs(void);
extern void fpu__resume_cpu(void);
+extern u64 fpu__get_supported_xfeatures_mask(void);
/*
* Debugging facility:
@@ -57,7 +58,7 @@ extern void fpu__resume_cpu(void);
*/
static __always_inline __pure bool use_eager_fpu(void)
{
- return static_cpu_has_safe(X86_FEATURE_EAGER_FPU);
+ return true;
}
static __always_inline __pure bool use_xsaveopt(void)
@@ -595,7 +596,8 @@ switch_fpu_prepare(struct fpu *old_fpu, struct fpu *new_fpu, int cpu)
* If the task has used the math, pre-load the FPU on xsave processors
* or if the past 5 consecutive context-switches used math.
*/
- fpu.preload = new_fpu->fpstate_active &&
+ fpu.preload = static_cpu_has(X86_FEATURE_FPU) &&
+ new_fpu->fpstate_active &&
(use_eager_fpu() || new_fpu->counter > 5);
if (old_fpu->fpregs_active) {
diff --git a/arch/x86/include/asm/fpu/xstate.h b/arch/x86/include/asm/fpu/xstate.h
index 3a6c89b70307..f23cd8c80b1c 100644
--- a/arch/x86/include/asm/fpu/xstate.h
+++ b/arch/x86/include/asm/fpu/xstate.h
@@ -22,7 +22,7 @@
#define XFEATURE_MASK_LAZY (XFEATURE_MASK_FP | \
XFEATURE_MASK_SSE | \
XFEATURE_MASK_YMM | \
- XFEATURE_MASK_OPMASK | \
+ XFEATURE_MASK_OPMASK | \
XFEATURE_MASK_ZMM_Hi256 | \
XFEATURE_MASK_Hi16_ZMM)
diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h
index fc3c7e49c8e4..ae357d0afc91 100644
--- a/arch/x86/include/asm/kvm_emulate.h
+++ b/arch/x86/include/asm/kvm_emulate.h
@@ -105,11 +105,12 @@ struct x86_emulate_ops {
* @addr: [IN ] Linear address from which to read.
* @val: [OUT] Value read from memory, zero-extended to 'u_long'.
* @bytes: [IN ] Number of bytes to read from memory.
+ * @system:[IN ] Whether the access is forced to be at CPL0.
*/
int (*read_std)(struct x86_emulate_ctxt *ctxt,
unsigned long addr, void *val,
unsigned int bytes,
- struct x86_exception *fault);
+ struct x86_exception *fault, bool system);
/*
* read_phys: Read bytes of standard (non-emulated/special) memory.
@@ -127,10 +128,11 @@ struct x86_emulate_ops {
* @addr: [IN ] Linear address to which to write.
* @val: [OUT] Value write to memory, zero-extended to 'u_long'.
* @bytes: [IN ] Number of bytes to write to memory.
+ * @system:[IN ] Whether the access is forced to be at CPL0.
*/
int (*write_std)(struct x86_emulate_ctxt *ctxt,
unsigned long addr, void *val, unsigned int bytes,
- struct x86_exception *fault);
+ struct x86_exception *fault, bool system);
/*
* fetch: Read bytes of standard (non-emulated/special) memory.
* Used for instruction fetch.
diff --git a/arch/x86/include/asm/smp.h b/arch/x86/include/asm/smp.h
index 222a6a3ca2b5..a438c5598a90 100644
--- a/arch/x86/include/asm/smp.h
+++ b/arch/x86/include/asm/smp.h
@@ -21,15 +21,6 @@
extern int smp_num_siblings;
extern unsigned int num_processors;
-static inline bool cpu_has_ht_siblings(void)
-{
- bool has_siblings = false;
-#ifdef CONFIG_SMP
- has_siblings = cpu_has_ht && smp_num_siblings > 1;
-#endif
- return has_siblings;
-}
-
DECLARE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_sibling_map);
DECLARE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_core_map);
/* cpus sharing the last level cache: */
diff --git a/arch/x86/include/asm/xor_32.h b/arch/x86/include/asm/xor_32.h
index 5a08bc8bff33..c54beb44c4c1 100644
--- a/arch/x86/include/asm/xor_32.h
+++ b/arch/x86/include/asm/xor_32.h
@@ -553,7 +553,7 @@ do { \
if (cpu_has_xmm) { \
xor_speed(&xor_block_pIII_sse); \
xor_speed(&xor_block_sse_pf64); \
- } else if (cpu_has_mmx) { \
+ } else if (boot_cpu_has(X86_FEATURE_MMX)) { \
xor_speed(&xor_block_pII_mmx); \
xor_speed(&xor_block_p5_mmx); \
} else { \
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index a3e1f8497f8c..deddc9b93299 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -1368,7 +1368,7 @@ void setup_local_APIC(void)
* TODO: set up through-local-APIC from through-I/O-APIC? --macro
*/
value = apic_read(APIC_LVT0) & APIC_LVT_MASKED;
- if (!cpu && (pic_mode || !value)) {
+ if (!cpu && (pic_mode || !value || skip_ioapic_setup)) {
value = APIC_DM_EXTINT;
apic_printk(APIC_VERBOSE, "enabled ExtINT on CPU#%d\n", cpu);
} else {
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index 4bf9e77f3e05..f4fb8f5b0be4 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -304,7 +304,7 @@ static void amd_get_topology(struct cpuinfo_x86 *c)
int cpu = smp_processor_id();
/* get information required for multi-node processors */
- if (cpu_has_topoext) {
+ if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
u32 eax, ebx, ecx, edx;
cpuid(0x8000001e, &eax, &ebx, &ecx, &edx);
@@ -954,7 +954,7 @@ static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum)
void set_dr_addr_mask(unsigned long mask, int dr)
{
- if (!cpu_has_bpext)
+ if (!boot_cpu_has(X86_FEATURE_BPEXT))
return;
switch (dr) {
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 8eabbafff213..0498ad3702f5 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -1539,7 +1539,9 @@ void cpu_init(void)
printk(KERN_INFO "Initializing CPU#%d\n", cpu);
- if (cpu_feature_enabled(X86_FEATURE_VME) || cpu_has_tsc || cpu_has_de)
+ if (cpu_feature_enabled(X86_FEATURE_VME) ||
+ cpu_has_tsc ||
+ boot_cpu_has(X86_FEATURE_DE))
cr4_clear_bits(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE);
load_current_idt();
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index 209ac1e7d1f0..565648bc1a0a 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -445,7 +445,8 @@ static void init_intel(struct cpuinfo_x86 *c)
if (cpu_has_xmm2)
set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);
- if (cpu_has_ds) {
+
+ if (boot_cpu_has(X86_FEATURE_DS)) {
unsigned int l1;
rdmsr(MSR_IA32_MISC_ENABLE, l1, l2);
if (!(l1 & (1<<11)))
diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c
index b4ca91cf55b0..3fa72317ad78 100644
--- a/arch/x86/kernel/cpu/intel_cacheinfo.c
+++ b/arch/x86/kernel/cpu/intel_cacheinfo.c
@@ -591,7 +591,7 @@ cpuid4_cache_lookup_regs(int index, struct _cpuid4_info_regs *this_leaf)
unsigned edx;
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
- if (cpu_has_topoext)
+ if (boot_cpu_has(X86_FEATURE_TOPOEXT))
cpuid_count(0x8000001d, index, &eax.full,
&ebx.full, &ecx.full, &edx);
else
@@ -637,7 +637,7 @@ static int find_num_cache_leaves(struct cpuinfo_x86 *c)
void init_amd_cacheinfo(struct cpuinfo_x86 *c)
{
- if (cpu_has_topoext) {
+ if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
num_cache_leaves = find_num_cache_leaves(c);
} else if (c->extended_cpuid_level >= 0x80000006) {
if (cpuid_edx(0x80000006) & 0xf000)
@@ -809,7 +809,7 @@ static int __cache_amd_cpumap_setup(unsigned int cpu, int index,
struct cacheinfo *this_leaf;
int i, sibling;
- if (cpu_has_topoext) {
+ if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
unsigned int apicid, nshared, first, last;
this_leaf = this_cpu_ci->info_list + index;
diff --git a/arch/x86/kernel/cpu/mtrr/generic.c b/arch/x86/kernel/cpu/mtrr/generic.c
index b5624fafa44a..136ae86f4f5f 100644
--- a/arch/x86/kernel/cpu/mtrr/generic.c
+++ b/arch/x86/kernel/cpu/mtrr/generic.c
@@ -349,7 +349,7 @@ static void get_fixed_ranges(mtrr_type *frs)
void mtrr_save_fixed_ranges(void *info)
{
- if (cpu_has_mtrr)
+ if (boot_cpu_has(X86_FEATURE_MTRR))
get_fixed_ranges(mtrr_state.fixed_ranges);
}
diff --git a/arch/x86/kernel/cpu/mtrr/main.c b/arch/x86/kernel/cpu/mtrr/main.c
index fa77ac8291f0..f924f41af89a 100644
--- a/arch/x86/kernel/cpu/mtrr/main.c
+++ b/arch/x86/kernel/cpu/mtrr/main.c
@@ -682,7 +682,7 @@ void __init mtrr_bp_init(void)
phys_addr = 32;
- if (cpu_has_mtrr) {
+ if (boot_cpu_has(X86_FEATURE_MTRR)) {
mtrr_if = &generic_mtrr_ops;
size_or_mask = SIZE_OR_MASK_BITS(36);
size_and_mask = 0x00f00000;
diff --git a/arch/x86/kernel/cpu/perf_event_amd.c b/arch/x86/kernel/cpu/perf_event_amd.c
index 1cee5d2d7ece..3ea177cb7366 100644
--- a/arch/x86/kernel/cpu/perf_event_amd.c
+++ b/arch/x86/kernel/cpu/perf_event_amd.c
@@ -160,7 +160,7 @@ static inline int amd_pmu_addr_offset(int index, bool eventsel)
if (offset)
return offset;
- if (!cpu_has_perfctr_core)
+ if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE))
offset = index;
else
offset = index << 1;
@@ -652,7 +652,7 @@ static __initconst const struct x86_pmu amd_pmu = {
static int __init amd_core_pmu_init(void)
{
- if (!cpu_has_perfctr_core)
+ if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE))
return 0;
switch (boot_cpu_data.x86) {
diff --git a/arch/x86/kernel/cpu/perf_event_amd_uncore.c b/arch/x86/kernel/cpu/perf_event_amd_uncore.c
index cc6cedb8f25d..49742746a6c9 100644
--- a/arch/x86/kernel/cpu/perf_event_amd_uncore.c
+++ b/arch/x86/kernel/cpu/perf_event_amd_uncore.c
@@ -523,10 +523,10 @@ static int __init amd_uncore_init(void)
if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
goto fail_nodev;
- if (!cpu_has_topoext)
+ if (!boot_cpu_has(X86_FEATURE_TOPOEXT))
goto fail_nodev;
- if (cpu_has_perfctr_nb) {
+ if (boot_cpu_has(X86_FEATURE_PERFCTR_NB)) {
amd_uncore_nb = alloc_percpu(struct amd_uncore *);
if (!amd_uncore_nb) {
ret = -ENOMEM;
@@ -540,7 +540,7 @@ static int __init amd_uncore_init(void)
ret = 0;
}
- if (cpu_has_perfctr_l2) {
+ if (boot_cpu_has(X86_FEATURE_PERFCTR_L2)) {
amd_uncore_l2 = alloc_percpu(struct amd_uncore *);
if (!amd_uncore_l2) {
ret = -ENOMEM;
@@ -583,10 +583,11 @@ fail_online:
/* amd_uncore_nb/l2 should have been freed by cleanup_cpu_online */
amd_uncore_nb = amd_uncore_l2 = NULL;
- if (cpu_has_perfctr_l2)
+
+ if (boot_cpu_has(X86_FEATURE_PERFCTR_L2))
perf_pmu_unregister(&amd_l2_pmu);
fail_l2:
- if (cpu_has_perfctr_nb)
+ if (boot_cpu_has(X86_FEATURE_PERFCTR_NB))
perf_pmu_unregister(&amd_nb_pmu);
if (amd_uncore_l2)
free_percpu(amd_uncore_l2);
diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c
index 1f4acd68b98b..74b8dcd1bbdc 100644
--- a/arch/x86/kernel/devicetree.c
+++ b/arch/x86/kernel/devicetree.c
@@ -11,6 +11,7 @@
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/of_irq.h>
+#include <linux/libfdt.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/of_pci.h>
@@ -199,19 +200,22 @@ static struct of_ioapic_type of_ioapic_type[] =
static int dt_irqdomain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *arg)
{
- struct of_phandle_args *irq_data = (void *)arg;
+ struct irq_fwspec *fwspec = (struct irq_fwspec *)arg;
struct of_ioapic_type *it;
struct irq_alloc_info tmp;
+ int type_index;
- if (WARN_ON(irq_data->args_count < 2))
+ if (WARN_ON(fwspec->param_count < 2))
return -EINVAL;
- if (irq_data->args[1] >= ARRAY_SIZE(of_ioapic_type))
+
+ type_index = fwspec->param[1];
+ if (type_index >= ARRAY_SIZE(of_ioapic_type))
return -EINVAL;
- it = &of_ioapic_type[irq_data->args[1]];
+ it = &of_ioapic_type[type_index];
ioapic_set_alloc_attr(&tmp, NUMA_NO_NODE, it->trigger, it->polarity);
tmp.ioapic_id = mpc_ioapic_id(mp_irqdomain_ioapic_idx(domain));
- tmp.ioapic_pin = irq_data->args[0];
+ tmp.ioapic_pin = fwspec->param[0];
return mp_irqdomain_alloc(domain, virq, nr_irqs, &tmp);
}
@@ -276,14 +280,15 @@ static void __init x86_flattree_get_config(void)
map_len = max(PAGE_SIZE - (initial_dtb & ~PAGE_MASK), (u64)128);
- initial_boot_params = dt = early_memremap(initial_dtb, map_len);
- size = of_get_flat_dt_size();
+ dt = early_memremap(initial_dtb, map_len);
+ size = fdt_totalsize(dt);
if (map_len < size) {
early_memunmap(dt, map_len);
- initial_boot_params = dt = early_memremap(initial_dtb, size);
+ dt = early_memremap(initial_dtb, size);
map_len = size;
}
+ early_init_dt_verify(dt);
unflatten_and_copy_device_tree();
early_memunmap(dt, map_len);
}
diff --git a/arch/x86/kernel/fpu/core.c b/arch/x86/kernel/fpu/core.c
index d25097c3fc1d..6aa0b519c851 100644
--- a/arch/x86/kernel/fpu/core.c
+++ b/arch/x86/kernel/fpu/core.c
@@ -114,6 +114,10 @@ void __kernel_fpu_begin(void)
kernel_fpu_disable();
if (fpu->fpregs_active) {
+ /*
+ * Ignore return value -- we don't care if reg state
+ * is clobbered.
+ */
copy_fpregs_to_fpstate(fpu);
} else {
this_cpu_write(fpu_fpregs_owner_ctx, NULL);
@@ -189,8 +193,12 @@ void fpu__save(struct fpu *fpu)
preempt_disable();
if (fpu->fpregs_active) {
- if (!copy_fpregs_to_fpstate(fpu))
- fpregs_deactivate(fpu);
+ if (!copy_fpregs_to_fpstate(fpu)) {
+ if (use_eager_fpu())
+ copy_kernel_to_fpregs(&fpu->state);
+ else
+ fpregs_deactivate(fpu);
+ }
}
preempt_enable();
}
@@ -259,7 +267,11 @@ static void fpu_copy(struct fpu *dst_fpu, struct fpu *src_fpu)
preempt_disable();
if (!copy_fpregs_to_fpstate(dst_fpu)) {
memcpy(&src_fpu->state, &dst_fpu->state, xstate_size);
- fpregs_deactivate(src_fpu);
+
+ if (use_eager_fpu())
+ copy_kernel_to_fpregs(&src_fpu->state);
+ else
+ fpregs_deactivate(src_fpu);
}
preempt_enable();
}
@@ -409,8 +421,10 @@ static inline void copy_init_fpstate_to_fpregs(void)
{
if (use_xsave())
copy_kernel_to_xregs(&init_fpstate.xsave, -1);
- else
+ else if (static_cpu_has(X86_FEATURE_FXSR))
copy_kernel_to_fxregs(&init_fpstate.fxsave);
+ else
+ copy_kernel_to_fregs(&init_fpstate.fsave);
}
/*
@@ -423,7 +437,7 @@ void fpu__clear(struct fpu *fpu)
{
WARN_ON_FPU(fpu != &current->thread.fpu); /* Almost certainly an anomaly */
- if (!use_eager_fpu()) {
+ if (!use_eager_fpu() || !static_cpu_has(X86_FEATURE_FPU)) {
/* FPU state will be reallocated lazily at the first use. */
fpu__drop(fpu);
} else {
diff --git a/arch/x86/kernel/fpu/init.c b/arch/x86/kernel/fpu/init.c
index 1011c05b1bd5..954517285fa2 100644
--- a/arch/x86/kernel/fpu/init.c
+++ b/arch/x86/kernel/fpu/init.c
@@ -3,8 +3,11 @@
*/
#include <asm/fpu/internal.h>
#include <asm/tlbflush.h>
+#include <asm/setup.h>
+#include <asm/cmdline.h>
#include <linux/sched.h>
+#include <linux/init.h>
/*
* Initialize the TS bit in CR0 according to the style of context-switches
@@ -12,10 +15,7 @@
*/
static void fpu__init_cpu_ctx_switch(void)
{
- if (!cpu_has_eager_fpu)
- stts();
- else
- clts();
+ clts();
}
/*
@@ -75,13 +75,15 @@ static void fpu__init_system_early_generic(struct cpuinfo_x86 *c)
cr0 &= ~(X86_CR0_TS | X86_CR0_EM);
write_cr0(cr0);
- asm volatile("fninit ; fnstsw %0 ; fnstcw %1"
- : "+m" (fsw), "+m" (fcw));
+ if (!test_bit(X86_FEATURE_FPU, (unsigned long *)cpu_caps_cleared)) {
+ asm volatile("fninit ; fnstsw %0 ; fnstcw %1"
+ : "+m" (fsw), "+m" (fcw));
- if (fsw == 0 && (fcw & 0x103f) == 0x003f)
- set_cpu_cap(c, X86_FEATURE_FPU);
- else
- clear_cpu_cap(c, X86_FEATURE_FPU);
+ if (fsw == 0 && (fcw & 0x103f) == 0x003f)
+ set_cpu_cap(c, X86_FEATURE_FPU);
+ else
+ clear_cpu_cap(c, X86_FEATURE_FPU);
+ }
#ifndef CONFIG_MATH_EMULATION
if (!cpu_has_fpu) {
@@ -130,7 +132,7 @@ static void __init fpu__init_system_generic(void)
* Set up the legacy init FPU context. (xstate init might overwrite this
* with a more modern format, if the CPU supports it.)
*/
- fpstate_init_fxstate(&init_fpstate.fxsave);
+ fpstate_init(&init_fpstate);
fpu__init_system_mxcsr();
}
@@ -230,53 +232,16 @@ static void __init fpu__init_system_xstate_size_legacy(void)
}
/*
- * FPU context switching strategies:
- *
- * Against popular belief, we don't do lazy FPU saves, due to the
- * task migration complications it brings on SMP - we only do
- * lazy FPU restores.
- *
- * 'lazy' is the traditional strategy, which is based on setting
- * CR0::TS to 1 during context-switch (instead of doing a full
- * restore of the FPU state), which causes the first FPU instruction
- * after the context switch (whenever it is executed) to fault - at
- * which point we lazily restore the FPU state into FPU registers.
- *
- * Tasks are of course under no obligation to execute FPU instructions,
- * so it can easily happen that another context-switch occurs without
- * a single FPU instruction being executed. If we eventually switch
- * back to the original task (that still owns the FPU) then we have
- * not only saved the restores along the way, but we also have the
- * FPU ready to be used for the original task.
- *
- * 'eager' switching is used on modern CPUs, there we switch the FPU
- * state during every context switch, regardless of whether the task
- * has used FPU instructions in that time slice or not. This is done
- * because modern FPU context saving instructions are able to optimize
- * state saving and restoration in hardware: they can detect both
- * unused and untouched FPU state and optimize accordingly.
- *
- * [ Note that even in 'lazy' mode we might optimize context switches
- * to use 'eager' restores, if we detect that a task is using the FPU
- * frequently. See the fpu->counter logic in fpu/internal.h for that. ]
+ * Find supported xfeatures based on cpu features and command-line input.
+ * This must be called after fpu__init_parse_early_param() is called and
+ * xfeatures_mask is enumerated.
*/
-static enum { AUTO, ENABLE, DISABLE } eagerfpu = AUTO;
-
-static int __init eager_fpu_setup(char *s)
+u64 __init fpu__get_supported_xfeatures_mask(void)
{
- if (!strcmp(s, "on"))
- eagerfpu = ENABLE;
- else if (!strcmp(s, "off"))
- eagerfpu = DISABLE;
- else if (!strcmp(s, "auto"))
- eagerfpu = AUTO;
- return 1;
+ return XCNTXT_MASK;
}
-__setup("eagerfpu=", eager_fpu_setup);
-/*
- * Pick the FPU context switching strategy:
- */
+/* Legacy code to initialize eager fpu mode. */
static void __init fpu__init_system_ctx_switch(void)
{
static bool on_boot_cpu = 1;
@@ -286,25 +251,31 @@ static void __init fpu__init_system_ctx_switch(void)
WARN_ON_FPU(current->thread.fpu.fpstate_active);
current_thread_info()->status = 0;
+}
- /* Auto enable eagerfpu for xsaveopt */
- if (cpu_has_xsaveopt && eagerfpu != DISABLE)
- eagerfpu = ENABLE;
-
- if (xfeatures_mask & XFEATURE_MASK_EAGER) {
- if (eagerfpu == DISABLE) {
- pr_err("x86/fpu: eagerfpu switching disabled, disabling the following xstate features: 0x%llx.\n",
- xfeatures_mask & XFEATURE_MASK_EAGER);
- xfeatures_mask &= ~XFEATURE_MASK_EAGER;
- } else {
- eagerfpu = ENABLE;
- }
+/*
+ * We parse fpu parameters early because fpu__init_system() is executed
+ * before parse_early_param().
+ */
+static void __init fpu__init_parse_early_param(void)
+{
+ if (cmdline_find_option_bool(boot_command_line, "no387"))
+ setup_clear_cpu_cap(X86_FEATURE_FPU);
+
+ if (cmdline_find_option_bool(boot_command_line, "nofxsr")) {
+ setup_clear_cpu_cap(X86_FEATURE_FXSR);
+ setup_clear_cpu_cap(X86_FEATURE_FXSR_OPT);
+ setup_clear_cpu_cap(X86_FEATURE_XMM);
}
- if (eagerfpu == ENABLE)
- setup_force_cpu_cap(X86_FEATURE_EAGER_FPU);
+ if (cmdline_find_option_bool(boot_command_line, "noxsave"))
+ fpu__xstate_clear_all_cpu_caps();
+
+ if (cmdline_find_option_bool(boot_command_line, "noxsaveopt"))
+ setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
- printk(KERN_INFO "x86/fpu: Using '%s' FPU context switches.\n", eagerfpu == ENABLE ? "eager" : "lazy");
+ if (cmdline_find_option_bool(boot_command_line, "noxsaves"))
+ setup_clear_cpu_cap(X86_FEATURE_XSAVES);
}
/*
@@ -313,6 +284,7 @@ static void __init fpu__init_system_ctx_switch(void)
*/
void __init fpu__init_system(struct cpuinfo_x86 *c)
{
+ fpu__init_parse_early_param();
fpu__init_system_early_generic(c);
/*
@@ -336,62 +308,3 @@ void __init fpu__init_system(struct cpuinfo_x86 *c)
fpu__init_system_ctx_switch();
}
-
-/*
- * Boot parameter to turn off FPU support and fall back to math-emu:
- */
-static int __init no_387(char *s)
-{
- setup_clear_cpu_cap(X86_FEATURE_FPU);
- return 1;
-}
-__setup("no387", no_387);
-
-/*
- * Disable all xstate CPU features:
- */
-static int __init x86_noxsave_setup(char *s)
-{
- if (strlen(s))
- return 0;
-
- fpu__xstate_clear_all_cpu_caps();
-
- return 1;
-}
-__setup("noxsave", x86_noxsave_setup);
-
-/*
- * Disable the XSAVEOPT instruction specifically:
- */
-static int __init x86_noxsaveopt_setup(char *s)
-{
- setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
-
- return 1;
-}
-__setup("noxsaveopt", x86_noxsaveopt_setup);
-
-/*
- * Disable the XSAVES instruction:
- */
-static int __init x86_noxsaves_setup(char *s)
-{
- setup_clear_cpu_cap(X86_FEATURE_XSAVES);
-
- return 1;
-}
-__setup("noxsaves", x86_noxsaves_setup);
-
-/*
- * Disable FX save/restore and SSE support:
- */
-static int __init x86_nofxsr_setup(char *s)
-{
- setup_clear_cpu_cap(X86_FEATURE_FXSR);
- setup_clear_cpu_cap(X86_FEATURE_FXSR_OPT);
- setup_clear_cpu_cap(X86_FEATURE_XMM);
-
- return 1;
-}
-__setup("nofxsr", x86_nofxsr_setup);
diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c
index 70fc312221fc..3fa200ecca62 100644
--- a/arch/x86/kernel/fpu/xstate.c
+++ b/arch/x86/kernel/fpu/xstate.c
@@ -632,8 +632,7 @@ void __init fpu__init_system_xstate(void)
BUG();
}
- /* Support only the state known to the OS: */
- xfeatures_mask = xfeatures_mask & XCNTXT_MASK;
+ xfeatures_mask &= fpu__get_supported_xfeatures_mask();
/* Enable xstate instructions to be able to continue with initialization: */
fpu__init_cpu_xstate();
diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c
index 50a3fad5b89f..2bcfb5f2bc44 100644
--- a/arch/x86/kernel/hw_breakpoint.c
+++ b/arch/x86/kernel/hw_breakpoint.c
@@ -300,6 +300,10 @@ static int arch_build_bp_info(struct perf_event *bp)
return -EINVAL;
if (bp->attr.bp_addr & (bp->attr.bp_len - 1))
return -EINVAL;
+
+ if (!boot_cpu_has(X86_FEATURE_BPEXT))
+ return -EOPNOTSUPP;
+
/*
* It's impossible to use a range breakpoint to fake out
* user vs kernel detection because bp_len - 1 can't
@@ -307,8 +311,6 @@ static int arch_build_bp_info(struct perf_event *bp)
* breakpoints, then we'll have to check for kprobe-blacklisted
* addresses anywhere in the range.
*/
- if (!cpu_has_bpext)
- return -EOPNOTSUPP;
info->mask = bp->attr.bp_len - 1;
info->len = X86_BREAKPOINT_LEN_1;
}
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 00c7878043ef..1f7aefc7b0b4 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -295,7 +295,7 @@ do { \
static bool match_smt(struct cpuinfo_x86 *c, struct cpuinfo_x86 *o)
{
- if (cpu_has_topoext) {
+ if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
int cpu1 = c->cpu_index, cpu2 = o->cpu_index;
if (c->phys_proc_id == o->phys_proc_id &&
@@ -1344,6 +1344,7 @@ static void remove_siblinginfo(int cpu)
cpumask_clear(topology_core_cpumask(cpu));
c->phys_proc_id = 0;
c->cpu_core_id = 0;
+ c->booted_cores = 0;
cpumask_clear_cpu(cpu, cpu_sibling_setup_mask);
}
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 1fbd2631be60..8c73bf1492b8 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -751,7 +751,6 @@ dotraplinkage void
do_device_not_available(struct pt_regs *regs, long error_code)
{
RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
- BUG_ON(use_eager_fpu());
#ifdef CONFIG_MATH_EMULATION
if (read_cr0() & X86_CR0_EM) {
diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c
index af57736a0309..d6d64a519559 100644
--- a/arch/x86/kernel/vm86_32.c
+++ b/arch/x86/kernel/vm86_32.c
@@ -357,8 +357,10 @@ static long do_sys_vm86(struct vm86plus_struct __user *user_vm86, bool plus)
tss = &per_cpu(cpu_tss, get_cpu());
/* make room for real-mode segments */
tsk->thread.sp0 += 16;
- if (cpu_has_sep)
+
+ if (static_cpu_has_safe(X86_FEATURE_SEP))
tsk->thread.sysenter_cs = 0;
+
load_sp0(tss, &tsk->thread);
put_cpu();
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 8864fec63a20..f1507626ed36 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -790,6 +790,19 @@ static inline int jmp_rel(struct x86_emulate_ctxt *ctxt, int rel)
return assign_eip_near(ctxt, ctxt->_eip + rel);
}
+static int linear_read_system(struct x86_emulate_ctxt *ctxt, ulong linear,
+ void *data, unsigned size)
+{
+ return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception, true);
+}
+
+static int linear_write_system(struct x86_emulate_ctxt *ctxt,
+ ulong linear, void *data,
+ unsigned int size)
+{
+ return ctxt->ops->write_std(ctxt, linear, data, size, &ctxt->exception, true);
+}
+
static int segmented_read_std(struct x86_emulate_ctxt *ctxt,
struct segmented_address addr,
void *data,
@@ -801,7 +814,7 @@ static int segmented_read_std(struct x86_emulate_ctxt *ctxt,
rc = linearize(ctxt, addr, size, false, &linear);
if (rc != X86EMUL_CONTINUE)
return rc;
- return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception);
+ return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception, false);
}
static int segmented_write_std(struct x86_emulate_ctxt *ctxt,
@@ -815,7 +828,7 @@ static int segmented_write_std(struct x86_emulate_ctxt *ctxt,
rc = linearize(ctxt, addr, size, true, &linear);
if (rc != X86EMUL_CONTINUE)
return rc;
- return ctxt->ops->write_std(ctxt, linear, data, size, &ctxt->exception);
+ return ctxt->ops->write_std(ctxt, linear, data, size, &ctxt->exception, false);
}
/*
@@ -1488,8 +1501,7 @@ static int read_interrupt_descriptor(struct x86_emulate_ctxt *ctxt,
return emulate_gp(ctxt, index << 3 | 0x2);
addr = dt.address + index * 8;
- return ctxt->ops->read_std(ctxt, addr, desc, sizeof *desc,
- &ctxt->exception);
+ return linear_read_system(ctxt, addr, desc, sizeof *desc);
}
static void get_descriptor_table_ptr(struct x86_emulate_ctxt *ctxt,
@@ -1552,8 +1564,7 @@ static int read_segment_descriptor(struct x86_emulate_ctxt *ctxt,
if (rc != X86EMUL_CONTINUE)
return rc;
- return ctxt->ops->read_std(ctxt, *desc_addr_p, desc, sizeof(*desc),
- &ctxt->exception);
+ return linear_read_system(ctxt, *desc_addr_p, desc, sizeof(*desc));
}
/* allowed just for 8 bytes segments */
@@ -1567,8 +1578,7 @@ static int write_segment_descriptor(struct x86_emulate_ctxt *ctxt,
if (rc != X86EMUL_CONTINUE)
return rc;
- return ctxt->ops->write_std(ctxt, addr, desc, sizeof *desc,
- &ctxt->exception);
+ return linear_write_system(ctxt, addr, desc, sizeof *desc);
}
static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
@@ -1729,8 +1739,7 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
return ret;
}
} else if (ctxt->mode == X86EMUL_MODE_PROT64) {
- ret = ctxt->ops->read_std(ctxt, desc_addr+8, &base3,
- sizeof(base3), &ctxt->exception);
+ ret = linear_read_system(ctxt, desc_addr+8, &base3, sizeof(base3));
if (ret != X86EMUL_CONTINUE)
return ret;
if (is_noncanonical_address(get_desc_base(&seg_desc) |
@@ -2043,11 +2052,11 @@ static int __emulate_int_real(struct x86_emulate_ctxt *ctxt, int irq)
eip_addr = dt.address + (irq << 2);
cs_addr = dt.address + (irq << 2) + 2;
- rc = ops->read_std(ctxt, cs_addr, &cs, 2, &ctxt->exception);
+ rc = linear_read_system(ctxt, cs_addr, &cs, 2);
if (rc != X86EMUL_CONTINUE)
return rc;
- rc = ops->read_std(ctxt, eip_addr, &eip, 2, &ctxt->exception);
+ rc = linear_read_system(ctxt, eip_addr, &eip, 2);
if (rc != X86EMUL_CONTINUE)
return rc;
@@ -2891,12 +2900,12 @@ static bool emulator_io_port_access_allowed(struct x86_emulate_ctxt *ctxt,
#ifdef CONFIG_X86_64
base |= ((u64)base3) << 32;
#endif
- r = ops->read_std(ctxt, base + 102, &io_bitmap_ptr, 2, NULL);
+ r = ops->read_std(ctxt, base + 102, &io_bitmap_ptr, 2, NULL, true);
if (r != X86EMUL_CONTINUE)
return false;
if (io_bitmap_ptr + port/8 > desc_limit_scaled(&tr_seg))
return false;
- r = ops->read_std(ctxt, base + io_bitmap_ptr + port/8, &perm, 2, NULL);
+ r = ops->read_std(ctxt, base + io_bitmap_ptr + port/8, &perm, 2, NULL, true);
if (r != X86EMUL_CONTINUE)
return false;
if ((perm >> bit_idx) & mask)
@@ -3025,35 +3034,30 @@ static int task_switch_16(struct x86_emulate_ctxt *ctxt,
u16 tss_selector, u16 old_tss_sel,
ulong old_tss_base, struct desc_struct *new_desc)
{
- const struct x86_emulate_ops *ops = ctxt->ops;
struct tss_segment_16 tss_seg;
int ret;
u32 new_tss_base = get_desc_base(new_desc);
- ret = ops->read_std(ctxt, old_tss_base, &tss_seg, sizeof tss_seg,
- &ctxt->exception);
+ ret = linear_read_system(ctxt, old_tss_base, &tss_seg, sizeof tss_seg);
if (ret != X86EMUL_CONTINUE)
return ret;
save_state_to_tss16(ctxt, &tss_seg);
- ret = ops->write_std(ctxt, old_tss_base, &tss_seg, sizeof tss_seg,
- &ctxt->exception);
+ ret = linear_write_system(ctxt, old_tss_base, &tss_seg, sizeof tss_seg);
if (ret != X86EMUL_CONTINUE)
return ret;
- ret = ops->read_std(ctxt, new_tss_base, &tss_seg, sizeof tss_seg,
- &ctxt->exception);
+ ret = linear_read_system(ctxt, new_tss_base, &tss_seg, sizeof tss_seg);
if (ret != X86EMUL_CONTINUE)
return ret;
if (old_tss_sel != 0xffff) {
tss_seg.prev_task_link = old_tss_sel;
- ret = ops->write_std(ctxt, new_tss_base,
- &tss_seg.prev_task_link,
- sizeof tss_seg.prev_task_link,
- &ctxt->exception);
+ ret = linear_write_system(ctxt, new_tss_base,
+ &tss_seg.prev_task_link,
+ sizeof tss_seg.prev_task_link);
if (ret != X86EMUL_CONTINUE)
return ret;
}
@@ -3169,38 +3173,34 @@ static int task_switch_32(struct x86_emulate_ctxt *ctxt,
u16 tss_selector, u16 old_tss_sel,
ulong old_tss_base, struct desc_struct *new_desc)
{
- const struct x86_emulate_ops *ops = ctxt->ops;
struct tss_segment_32 tss_seg;
int ret;
u32 new_tss_base = get_desc_base(new_desc);
u32 eip_offset = offsetof(struct tss_segment_32, eip);
u32 ldt_sel_offset = offsetof(struct tss_segment_32, ldt_selector);
- ret = ops->read_std(ctxt, old_tss_base, &tss_seg, sizeof tss_seg,
- &ctxt->exception);
+ ret = linear_read_system(ctxt, old_tss_base, &tss_seg, sizeof tss_seg);
if (ret != X86EMUL_CONTINUE)
return ret;
save_state_to_tss32(ctxt, &tss_seg);
/* Only GP registers and segment selectors are saved */
- ret = ops->write_std(ctxt, old_tss_base + eip_offset, &tss_seg.eip,
- ldt_sel_offset - eip_offset, &ctxt->exception);
+ ret = linear_write_system(ctxt, old_tss_base + eip_offset, &tss_seg.eip,
+ ldt_sel_offset - eip_offset);
if (ret != X86EMUL_CONTINUE)
return ret;
- ret = ops->read_std(ctxt, new_tss_base, &tss_seg, sizeof tss_seg,
- &ctxt->exception);
+ ret = linear_read_system(ctxt, new_tss_base, &tss_seg, sizeof tss_seg);
if (ret != X86EMUL_CONTINUE)
return ret;
if (old_tss_sel != 0xffff) {
tss_seg.prev_task_link = old_tss_sel;
- ret = ops->write_std(ctxt, new_tss_base,
- &tss_seg.prev_task_link,
- sizeof tss_seg.prev_task_link,
- &ctxt->exception);
+ ret = linear_write_system(ctxt, new_tss_base,
+ &tss_seg.prev_task_link,
+ sizeof tss_seg.prev_task_link);
if (ret != X86EMUL_CONTINUE)
return ret;
}
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 1c96f09367ae..a1afd80a68aa 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -288,8 +288,16 @@ void kvm_apic_set_version(struct kvm_vcpu *vcpu)
if (!kvm_vcpu_has_lapic(vcpu))
return;
+ /*
+ * KVM emulates 82093AA datasheet (with in-kernel IOAPIC implementation)
+ * which doesn't have EOI register; Some buggy OSes (e.g. Windows with
+ * Hyper-V role) disable EOI broadcast in lapic not checking for IOAPIC
+ * version first and level-triggered interrupts never get EOIed in
+ * IOAPIC.
+ */
feat = kvm_find_cpuid_entry(apic->vcpu, 0x1, 0);
- if (feat && (feat->ecx & (1 << (X86_FEATURE_X2APIC & 31))))
+ if (feat && (feat->ecx & (1 << (X86_FEATURE_X2APIC & 31))) &&
+ !ioapic_in_kernel(vcpu->kvm))
v |= APIC_LVR_DIRECTED_EOI;
apic_set_reg(apic, APIC_LVR, v);
}
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 528b4352fa99..63c44a9bf6bb 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -2319,6 +2319,8 @@ static void vmx_queue_exception(struct kvm_vcpu *vcpu, unsigned nr,
return;
}
+ WARN_ON_ONCE(vmx->emulation_required);
+
if (kvm_exception_is_soft(nr)) {
vmcs_write32(VM_ENTRY_INSTRUCTION_LEN,
vmx->vcpu.arch.event_exit_inst_len);
@@ -6037,12 +6039,12 @@ static int handle_invalid_guest_state(struct kvm_vcpu *vcpu)
goto out;
}
- if (err != EMULATE_DONE) {
- vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
- vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
- vcpu->run->internal.ndata = 0;
- return 0;
- }
+ if (err != EMULATE_DONE)
+ goto emulation_error;
+
+ if (vmx->emulation_required && !vmx->rmode.vm86_active &&
+ vcpu->arch.exception.pending)
+ goto emulation_error;
if (vcpu->arch.halt_request) {
vcpu->arch.halt_request = 0;
@@ -6058,6 +6060,12 @@ static int handle_invalid_guest_state(struct kvm_vcpu *vcpu)
out:
return ret;
+
+emulation_error:
+ vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
+ vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
+ vcpu->run->internal.ndata = 0;
+ return 0;
}
static int __grow_ple_window(int val)
@@ -6684,8 +6692,7 @@ static int nested_vmx_check_vmptr(struct kvm_vcpu *vcpu, int exit_reason,
vmcs_read32(VMX_INSTRUCTION_INFO), false, &gva))
return 1;
- if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &vmptr,
- sizeof(vmptr), &e)) {
+ if (kvm_read_guest_virt(vcpu, gva, &vmptr, sizeof(vmptr), &e)) {
kvm_inject_page_fault(vcpu, &e);
return 1;
}
@@ -7203,8 +7210,8 @@ static int handle_vmread(struct kvm_vcpu *vcpu)
vmx_instruction_info, true, &gva))
return 1;
/* _system ok, as nested_vmx_check_permission verified cpl=0 */
- kvm_write_guest_virt_system(&vcpu->arch.emulate_ctxt, gva,
- &field_value, (is_long_mode(vcpu) ? 8 : 4), NULL);
+ kvm_write_guest_virt_system(vcpu, gva, &field_value,
+ (is_long_mode(vcpu) ? 8 : 4), NULL);
}
nested_vmx_succeed(vcpu);
@@ -7239,8 +7246,8 @@ static int handle_vmwrite(struct kvm_vcpu *vcpu)
if (get_vmx_mem_address(vcpu, exit_qualification,
vmx_instruction_info, false, &gva))
return 1;
- if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva,
- &field_value, (is_64_bit_mode(vcpu) ? 8 : 4), &e)) {
+ if (kvm_read_guest_virt(vcpu, gva, &field_value,
+ (is_64_bit_mode(vcpu) ? 8 : 4), &e)) {
kvm_inject_page_fault(vcpu, &e);
return 1;
}
@@ -7330,9 +7337,9 @@ static int handle_vmptrst(struct kvm_vcpu *vcpu)
vmx_instruction_info, true, &vmcs_gva))
return 1;
/* ok to use *_system, as nested_vmx_check_permission verified cpl=0 */
- if (kvm_write_guest_virt_system(&vcpu->arch.emulate_ctxt, vmcs_gva,
- (void *)&to_vmx(vcpu)->nested.current_vmptr,
- sizeof(u64), &e)) {
+ if (kvm_write_guest_virt_system(vcpu, vmcs_gva,
+ (void *)&to_vmx(vcpu)->nested.current_vmptr,
+ sizeof(u64), &e)) {
kvm_inject_page_fault(vcpu, &e);
return 1;
}
@@ -7386,8 +7393,7 @@ static int handle_invept(struct kvm_vcpu *vcpu)
if (get_vmx_mem_address(vcpu, vmcs_readl(EXIT_QUALIFICATION),
vmx_instruction_info, false, &gva))
return 1;
- if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &operand,
- sizeof(operand), &e)) {
+ if (kvm_read_guest_virt(vcpu, gva, &operand, sizeof(operand), &e)) {
kvm_inject_page_fault(vcpu, &e);
return 1;
}
@@ -7446,8 +7452,7 @@ static int handle_invvpid(struct kvm_vcpu *vcpu)
if (get_vmx_mem_address(vcpu, vmcs_readl(EXIT_QUALIFICATION),
vmx_instruction_info, false, &gva))
return 1;
- if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &vpid,
- sizeof(u32), &e)) {
+ if (kvm_read_guest_virt(vcpu, gva, &vpid, sizeof(u32), &e)) {
kvm_inject_page_fault(vcpu, &e);
return 1;
}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index f37f0c72b22a..53d43d22a84b 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3973,13 +3973,14 @@ long kvm_arch_vm_ioctl(struct file *filp,
mutex_unlock(&kvm->lock);
break;
case KVM_XEN_HVM_CONFIG: {
+ struct kvm_xen_hvm_config xhc;
r = -EFAULT;
- if (copy_from_user(&kvm->arch.xen_hvm_config, argp,
- sizeof(struct kvm_xen_hvm_config)))
+ if (copy_from_user(&xhc, argp, sizeof(xhc)))
goto out;
r = -EINVAL;
- if (kvm->arch.xen_hvm_config.flags)
+ if (xhc.flags)
goto out;
+ memcpy(&kvm->arch.xen_hvm_config, &xhc, sizeof(xhc));
r = 0;
break;
}
@@ -4244,11 +4245,10 @@ static int kvm_fetch_guest_virt(struct x86_emulate_ctxt *ctxt,
return X86EMUL_CONTINUE;
}
-int kvm_read_guest_virt(struct x86_emulate_ctxt *ctxt,
+int kvm_read_guest_virt(struct kvm_vcpu *vcpu,
gva_t addr, void *val, unsigned int bytes,
struct x86_exception *exception)
{
- struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt);
u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, access,
@@ -4256,12 +4256,17 @@ int kvm_read_guest_virt(struct x86_emulate_ctxt *ctxt,
}
EXPORT_SYMBOL_GPL(kvm_read_guest_virt);
-static int kvm_read_guest_virt_system(struct x86_emulate_ctxt *ctxt,
- gva_t addr, void *val, unsigned int bytes,
- struct x86_exception *exception)
+static int emulator_read_std(struct x86_emulate_ctxt *ctxt,
+ gva_t addr, void *val, unsigned int bytes,
+ struct x86_exception *exception, bool system)
{
struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt);
- return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, 0, exception);
+ u32 access = 0;
+
+ if (!system && kvm_x86_ops->get_cpl(vcpu) == 3)
+ access |= PFERR_USER_MASK;
+
+ return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, access, exception);
}
static int kvm_read_guest_phys_system(struct x86_emulate_ctxt *ctxt,
@@ -4273,18 +4278,16 @@ static int kvm_read_guest_phys_system(struct x86_emulate_ctxt *ctxt,
return r < 0 ? X86EMUL_IO_NEEDED : X86EMUL_CONTINUE;
}
-int kvm_write_guest_virt_system(struct x86_emulate_ctxt *ctxt,
- gva_t addr, void *val,
- unsigned int bytes,
- struct x86_exception *exception)
+static int kvm_write_guest_virt_helper(gva_t addr, void *val, unsigned int bytes,
+ struct kvm_vcpu *vcpu, u32 access,
+ struct x86_exception *exception)
{
- struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt);
void *data = val;
int r = X86EMUL_CONTINUE;
while (bytes) {
gpa_t gpa = vcpu->arch.walk_mmu->gva_to_gpa(vcpu, addr,
- PFERR_WRITE_MASK,
+ access,
exception);
unsigned offset = addr & (PAGE_SIZE-1);
unsigned towrite = min(bytes, (unsigned)PAGE_SIZE - offset);
@@ -4305,6 +4308,27 @@ int kvm_write_guest_virt_system(struct x86_emulate_ctxt *ctxt,
out:
return r;
}
+
+static int emulator_write_std(struct x86_emulate_ctxt *ctxt, gva_t addr, void *val,
+ unsigned int bytes, struct x86_exception *exception,
+ bool system)
+{
+ struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt);
+ u32 access = PFERR_WRITE_MASK;
+
+ if (!system && kvm_x86_ops->get_cpl(vcpu) == 3)
+ access |= PFERR_USER_MASK;
+
+ return kvm_write_guest_virt_helper(addr, val, bytes, vcpu,
+ access, exception);
+}
+
+int kvm_write_guest_virt_system(struct kvm_vcpu *vcpu, gva_t addr, void *val,
+ unsigned int bytes, struct x86_exception *exception)
+{
+ return kvm_write_guest_virt_helper(addr, val, bytes, vcpu,
+ PFERR_WRITE_MASK, exception);
+}
EXPORT_SYMBOL_GPL(kvm_write_guest_virt_system);
static int vcpu_mmio_gva_to_gpa(struct kvm_vcpu *vcpu, unsigned long gva,
@@ -5024,8 +5048,8 @@ static void emulator_set_hflags(struct x86_emulate_ctxt *ctxt, unsigned emul_fla
static const struct x86_emulate_ops emulate_ops = {
.read_gpr = emulator_read_gpr,
.write_gpr = emulator_write_gpr,
- .read_std = kvm_read_guest_virt_system,
- .write_std = kvm_write_guest_virt_system,
+ .read_std = emulator_read_std,
+ .write_std = emulator_write_std,
.read_phys = kvm_read_guest_phys_system,
.fetch = kvm_fetch_guest_virt,
.read_emulated = emulator_read_emulated,
diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
index f2afa5fe48a6..53a750a10598 100644
--- a/arch/x86/kvm/x86.h
+++ b/arch/x86/kvm/x86.h
@@ -164,11 +164,11 @@ int kvm_inject_realmode_interrupt(struct kvm_vcpu *vcpu, int irq, int inc_eip);
void kvm_write_tsc(struct kvm_vcpu *vcpu, struct msr_data *msr);
-int kvm_read_guest_virt(struct x86_emulate_ctxt *ctxt,
+int kvm_read_guest_virt(struct kvm_vcpu *vcpu,
gva_t addr, void *val, unsigned int bytes,
struct x86_exception *exception);
-int kvm_write_guest_virt_system(struct x86_emulate_ctxt *ctxt,
+int kvm_write_guest_virt_system(struct kvm_vcpu *vcpu,
gva_t addr, void *val, unsigned int bytes,
struct x86_exception *exception);
diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c
index c013326a0d7a..08e94b6139ab 100644
--- a/arch/x86/mm/pgtable.c
+++ b/arch/x86/mm/pgtable.c
@@ -1,5 +1,6 @@
#include <linux/mm.h>
#include <linux/gfp.h>
+#include <linux/hugetlb.h>
#include <asm/pgalloc.h>
#include <asm/pgtable.h>
#include <asm/tlb.h>
@@ -600,6 +601,10 @@ int pud_set_huge(pud_t *pud, phys_addr_t addr, pgprot_t prot)
(mtrr != MTRR_TYPE_WRBACK))
return 0;
+ /* Bail out if we are we on a populated non-leaf entry: */
+ if (pud_present(*pud) && !pud_huge(*pud))
+ return 0;
+
prot = pgprot_4k_2_large(prot);
set_pte((pte_t *)pud, pfn_pte(
@@ -628,6 +633,10 @@ int pmd_set_huge(pmd_t *pmd, phys_addr_t addr, pgprot_t prot)
return 0;
}
+ /* Bail out if we are we on a populated non-leaf entry: */
+ if (pmd_present(*pmd) && !pmd_huge(*pmd))
+ return 0;
+
prot = pgprot_4k_2_large(prot);
set_pte((pte_t *)pmd, pfn_pte(
diff --git a/arch/x86/mm/setup_nx.c b/arch/x86/mm/setup_nx.c
index 90555bf60aa4..92e2eacb3321 100644
--- a/arch/x86/mm/setup_nx.c
+++ b/arch/x86/mm/setup_nx.c
@@ -31,7 +31,7 @@ early_param("noexec", noexec_setup);
void x86_configure_nx(void)
{
- if (cpu_has_nx && !disable_nx)
+ if (boot_cpu_has(X86_FEATURE_NX) && !disable_nx)
__supported_pte_mask |= _PAGE_NX;
else
__supported_pte_mask &= ~_PAGE_NX;
@@ -39,7 +39,7 @@ void x86_configure_nx(void)
void __init x86_report_nx(void)
{
- if (!cpu_has_nx) {
+ if (!boot_cpu_has(X86_FEATURE_NX)) {
printk(KERN_NOTICE "Notice: NX (Execute Disable) protection "
"missing in CPU!\n");
} else {
diff --git a/arch/x86/power/hibernate_32.c b/arch/x86/power/hibernate_32.c
index 291226b952a9..77ac4e4deb16 100644
--- a/arch/x86/power/hibernate_32.c
+++ b/arch/x86/power/hibernate_32.c
@@ -142,7 +142,7 @@ static inline void resume_init_first_level_page_table(pgd_t *pg_dir)
#endif
}
-int swsusp_arch_resume(void)
+asmlinkage int swsusp_arch_resume(void)
{
int error;
diff --git a/arch/x86/power/hibernate_64.c b/arch/x86/power/hibernate_64.c
index 009947d419a6..0e0c773edffc 100644
--- a/arch/x86/power/hibernate_64.c
+++ b/arch/x86/power/hibernate_64.c
@@ -78,7 +78,7 @@ static int set_up_temporary_mappings(void)
return 0;
}
-int swsusp_arch_resume(void)
+asmlinkage int swsusp_arch_resume(void)
{
int error;
diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c
index 8ea8211b2d58..f8bb0e4d035a 100644
--- a/drivers/acpi/acpi_pad.c
+++ b/drivers/acpi/acpi_pad.c
@@ -108,6 +108,7 @@ static void round_robin_cpu(unsigned int tsk_index)
cpumask_andnot(tmp, cpu_online_mask, pad_busy_cpus);
if (cpumask_empty(tmp)) {
mutex_unlock(&round_robin_lock);
+ free_cpumask_var(tmp);
return;
}
for_each_cpu(cpu, tmp) {
@@ -125,6 +126,8 @@ static void round_robin_cpu(unsigned int tsk_index)
mutex_unlock(&round_robin_lock);
set_cpus_allowed_ptr(current, cpumask_of(preferred_cpu));
+
+ free_cpumask_var(tmp);
}
static void exit_round_robin(unsigned int tsk_index)
diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c
index bf6873f95e72..0b5eedb60d04 100644
--- a/drivers/acpi/acpica/evevent.c
+++ b/drivers/acpi/acpica/evevent.c
@@ -204,6 +204,7 @@ u32 acpi_ev_fixed_event_detect(void)
u32 fixed_status;
u32 fixed_enable;
u32 i;
+ acpi_status status;
ACPI_FUNCTION_NAME(ev_fixed_event_detect);
@@ -211,8 +212,12 @@ u32 acpi_ev_fixed_event_detect(void)
* Read the fixed feature status and enable registers, as all the cases
* depend on their values. Ignore errors here.
*/
- (void)acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, &fixed_status);
- (void)acpi_hw_register_read(ACPI_REGISTER_PM1_ENABLE, &fixed_enable);
+ status = acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, &fixed_status);
+ status |=
+ acpi_hw_register_read(ACPI_REGISTER_PM1_ENABLE, &fixed_enable);
+ if (ACPI_FAILURE(status)) {
+ return (int_status);
+ }
ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS,
"Fixed Event Block: Enable %08X Status %08X\n",
diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c
index 7eba578d36f3..10262cae8a19 100644
--- a/drivers/acpi/acpica/nseval.c
+++ b/drivers/acpi/acpica/nseval.c
@@ -308,6 +308,14 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info)
/* Map AE_CTRL_RETURN_VALUE to AE_OK, we are done with it */
status = AE_OK;
+ } else if (ACPI_FAILURE(status)) {
+
+ /* If return_object exists, delete it */
+
+ if (info->return_object) {
+ acpi_ut_remove_reference(info->return_object);
+ info->return_object = NULL;
+ }
}
ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c
index bb01dea39fdc..9825780a1cd2 100644
--- a/drivers/acpi/processor_perflib.c
+++ b/drivers/acpi/processor_perflib.c
@@ -161,7 +161,7 @@ int acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag)
{
int ret;
- if (ignore_ppc) {
+ if (ignore_ppc || !pr->performance) {
/*
* Only when it is notification event, the _OST object
* will be evaluated. Otherwise it is skipped.
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 60d6db82ce5a..f9b86a1d922d 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4187,6 +4187,10 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
/* https://bugzilla.kernel.org/show_bug.cgi?id=15573 */
{ "C300-CTFDDAC128MAG", "0001", ATA_HORKAGE_NONCQ, },
+ /* Some Sandisk SSDs lock up hard with NCQ enabled. Reported on
+ SD7SN6S256G and SD8SN8U256G */
+ { "SanDisk SD[78]SN*G", NULL, ATA_HORKAGE_NONCQ, },
+
/* devices which puke on READ_NATIVE_MAX */
{ "HDS724040KLSA80", "KFAOA20N", ATA_HORKAGE_BROKEN_HPA, },
{ "WDC WD3200JD-00KLB0", "WD-WCAMR1130137", ATA_HORKAGE_BROKEN_HPA },
@@ -4247,6 +4251,8 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
{ "SanDisk SD7UB3Q*G1001", NULL, ATA_HORKAGE_NOLPM, },
/* devices that don't properly handle queued TRIM commands */
+ { "Micron_M500IT_*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "Micron_M500_*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "Crucial_CT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 0dd6379ac215..7e00d10d5a94 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -25,6 +25,7 @@
#include <linux/async.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/devinfo.h>
+#include <linux/platform_device.h>
#include "base.h"
#include "power/power.h"
@@ -642,6 +643,20 @@ void device_initial_probe(struct device *dev)
{
__device_attach(dev, true);
}
+#ifdef CONFIG_PLATFORM_AUTO
+static inline int lock_parent(struct device *dev)
+{
+ if (!dev->parent || dev->bus == &platform_bus_type)
+ return 0;
+
+ return 1;
+}
+#else
+static inline int lock_parent(struct device *dev)
+{
+ return dev->parent ? 1 : 0;
+}
+#endif
static int __driver_attach(struct device *dev, void *data)
{
@@ -659,14 +674,13 @@ static int __driver_attach(struct device *dev, void *data)
if (!driver_match_device(drv, dev))
return 0;
-
- if (dev->parent) /* Needed for USB */
+ if (lock_parent(dev))
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev);
device_unlock(dev);
- if (dev->parent)
+ if (lock_parent(dev))
device_unlock(dev->parent);
return 0;
diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
index 93362362aa55..8474a1b0740f 100644
--- a/drivers/block/paride/pcd.c
+++ b/drivers/block/paride/pcd.c
@@ -230,6 +230,8 @@ static int pcd_block_open(struct block_device *bdev, fmode_t mode)
struct pcd_unit *cd = bdev->bd_disk->private_data;
int ret;
+ check_disk_change(bdev);
+
mutex_lock(&pcd_mutex);
ret = cdrom_open(&cd->info, bdev, mode);
mutex_unlock(&pcd_mutex);
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 54cef3dc0beb..91676535a1a3 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -336,6 +336,9 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x13d3, 0x3459), .driver_info = BTUSB_REALTEK },
{ USB_DEVICE(0x13d3, 0x3494), .driver_info = BTUSB_REALTEK },
+ /* Additional Realtek 8723BU Bluetooth devices */
+ { USB_DEVICE(0x7392, 0xa611), .driver_info = BTUSB_REALTEK },
+
/* Additional Realtek 8821AE Bluetooth devices */
{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
{ USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK },
@@ -343,6 +346,9 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x13d3, 0x3461), .driver_info = BTUSB_REALTEK },
{ USB_DEVICE(0x13d3, 0x3462), .driver_info = BTUSB_REALTEK },
+ /* Additional Realtek 8822BE Bluetooth devices */
+ { USB_DEVICE(0x0b05, 0x185c), .driver_info = BTUSB_REALTEK },
+
/* Silicon Wave based devices */
{ USB_DEVICE(0x0c10, 0x0000), .driver_info = BTUSB_SWAVE },
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index b5f245d2875c..0151039bff05 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -1154,9 +1154,6 @@ int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev,
cd_dbg(CD_OPEN, "entering cdrom_open\n");
- /* open is event synchronization point, check events first */
- check_disk_change(bdev);
-
/* if this was a O_NONBLOCK open and we should honor the flags,
* do a quick open without drive/disc integrity checks. */
cdi->use_count++;
diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c
index 584bc3126403..e2808fefbb78 100644
--- a/drivers/cdrom/gdrom.c
+++ b/drivers/cdrom/gdrom.c
@@ -497,6 +497,9 @@ static struct cdrom_device_ops gdrom_ops = {
static int gdrom_bdops_open(struct block_device *bdev, fmode_t mode)
{
int ret;
+
+ check_disk_change(bdev);
+
mutex_lock(&gdrom_mutex);
ret = cdrom_open(gd.cd_info, bdev, mode);
mutex_unlock(&gdrom_mutex);
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 18d98292a187..c0787608af56 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -2774,6 +2774,28 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int ioctl_num,
if (err)
goto bail;
break;
+ case FASTRPC_IOCTL_MMAP_64:
+ K_COPY_FROM_USER(err, 0, &p.mmap, param,
+ sizeof(p.mmap));
+ if (err)
+ goto bail;
+ VERIFY(err, 0 == (err = fastrpc_internal_mmap(fl, &p.mmap)));
+ if (err)
+ goto bail;
+ K_COPY_TO_USER(err, 0, param, &p.mmap, sizeof(p.mmap));
+ if (err)
+ goto bail;
+ break;
+ case FASTRPC_IOCTL_MUNMAP_64:
+ K_COPY_FROM_USER(err, 0, &p.munmap, param,
+ sizeof(p.munmap));
+ if (err)
+ goto bail;
+ VERIFY(err, 0 == (err = fastrpc_internal_munmap(fl,
+ &p.munmap)));
+ if (err)
+ goto bail;
+ break;
case FASTRPC_IOCTL_SETMODE:
switch ((uint32_t)ioctl_param) {
case FASTRPC_MODE_PARALLEL:
diff --git a/drivers/char/adsprpc_compat.c b/drivers/char/adsprpc_compat.c
index fc6450336061..e1e061748f22 100644
--- a/drivers/char/adsprpc_compat.c
+++ b/drivers/char/adsprpc_compat.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -36,6 +36,11 @@
_IOWR('R', 9, struct compat_fastrpc_ioctl_perf)
#define COMPAT_FASTRPC_IOCTL_INIT_ATTRS \
_IOWR('R', 10, struct compat_fastrpc_ioctl_init_attrs)
+#define COMPAT_FASTRPC_IOCTL_MMAP_64 \
+ _IOWR('R', 14, struct compat_fastrpc_ioctl_mmap_64)
+#define COMPAT_FASTRPC_IOCTL_MUNMAP_64 \
+ _IOWR('R', 15, struct compat_fastrpc_ioctl_munmap_64)
+
struct compat_remote_buf {
compat_uptr_t pv; /* buffer pointer */
@@ -72,11 +77,24 @@ struct compat_fastrpc_ioctl_mmap {
compat_uptr_t vaddrout; /* dsps virtual address */
};
+struct compat_fastrpc_ioctl_mmap_64 {
+ compat_int_t fd; /* ion fd */
+ compat_uint_t flags; /* flags for dsp to map with */
+ compat_u64 vaddrin; /* optional virtual address */
+ compat_size_t size; /* size */
+ compat_u64 vaddrout; /* dsps virtual address */
+};
+
struct compat_fastrpc_ioctl_munmap {
compat_uptr_t vaddrout; /* address to unmap */
compat_size_t size; /* size */
};
+struct compat_fastrpc_ioctl_munmap_64 {
+ compat_u64 vaddrout; /* address to unmap */
+ compat_size_t size; /* size */
+};
+
struct compat_fastrpc_ioctl_init {
compat_uint_t flags; /* one of FASTRPC_INIT_* macros */
compat_uptr_t file; /* pointer to elf file */
@@ -209,6 +227,28 @@ static int compat_get_fastrpc_ioctl_mmap(
return err;
}
+static int compat_get_fastrpc_ioctl_mmap_64(
+ struct compat_fastrpc_ioctl_mmap_64 __user *map32,
+ struct fastrpc_ioctl_mmap __user *map)
+{
+ compat_uint_t u;
+ compat_int_t i;
+ compat_size_t s;
+ compat_u64 p;
+ int err;
+
+ err = get_user(i, &map32->fd);
+ err |= put_user(i, &map->fd);
+ err |= get_user(u, &map32->flags);
+ err |= put_user(u, &map->flags);
+ err |= get_user(p, &map32->vaddrin);
+ err |= put_user(p, &map->vaddrin);
+ err |= get_user(s, &map32->size);
+ err |= put_user(s, &map->size);
+
+ return err;
+}
+
static int compat_put_fastrpc_ioctl_mmap(
struct compat_fastrpc_ioctl_mmap __user *map32,
struct fastrpc_ioctl_mmap __user *map)
@@ -222,6 +262,19 @@ static int compat_put_fastrpc_ioctl_mmap(
return err;
}
+static int compat_put_fastrpc_ioctl_mmap_64(
+ struct compat_fastrpc_ioctl_mmap_64 __user *map32,
+ struct fastrpc_ioctl_mmap __user *map)
+{
+ compat_u64 p;
+ int err;
+
+ err = get_user(p, &map->vaddrout);
+ err |= put_user(p, &map32->vaddrout);
+
+ return err;
+}
+
static int compat_get_fastrpc_ioctl_munmap(
struct compat_fastrpc_ioctl_munmap __user *unmap32,
struct fastrpc_ioctl_munmap __user *unmap)
@@ -238,6 +291,22 @@ static int compat_get_fastrpc_ioctl_munmap(
return err;
}
+static int compat_get_fastrpc_ioctl_munmap_64(
+ struct compat_fastrpc_ioctl_munmap_64 __user *unmap32,
+ struct fastrpc_ioctl_munmap __user *unmap)
+{
+ compat_u64 p;
+ compat_size_t s;
+ int err;
+
+ err = get_user(p, &unmap32->vaddrout);
+ err |= put_user(p, &unmap->vaddrout);
+ err |= get_user(s, &unmap32->size);
+ err |= put_user(s, &unmap->size);
+
+ return err;
+}
+
static int compat_get_fastrpc_ioctl_perf(
struct compat_fastrpc_ioctl_perf __user *perf32,
struct fastrpc_ioctl_perf __user *perf)
@@ -343,6 +412,27 @@ long compat_fastrpc_device_ioctl(struct file *filp, unsigned int cmd,
VERIFY(err, 0 == compat_put_fastrpc_ioctl_mmap(map32, map));
return err;
}
+ case COMPAT_FASTRPC_IOCTL_MMAP_64:
+ {
+ struct compat_fastrpc_ioctl_mmap_64 __user *map32;
+ struct fastrpc_ioctl_mmap __user *map;
+ long ret;
+
+ map32 = compat_ptr(arg);
+ VERIFY(err, NULL != (map = compat_alloc_user_space(
+ sizeof(*map))));
+ if (err)
+ return -EFAULT;
+ VERIFY(err, 0 == compat_get_fastrpc_ioctl_mmap_64(map32, map));
+ if (err)
+ return err;
+ ret = filp->f_op->unlocked_ioctl(filp, FASTRPC_IOCTL_MMAP_64,
+ (unsigned long)map);
+ if (ret)
+ return ret;
+ VERIFY(err, 0 == compat_put_fastrpc_ioctl_mmap_64(map32, map));
+ return err;
+ }
case COMPAT_FASTRPC_IOCTL_MUNMAP:
{
struct compat_fastrpc_ioctl_munmap __user *unmap32;
@@ -360,6 +450,23 @@ long compat_fastrpc_device_ioctl(struct file *filp, unsigned int cmd,
return filp->f_op->unlocked_ioctl(filp, FASTRPC_IOCTL_MUNMAP,
(unsigned long)unmap);
}
+ case COMPAT_FASTRPC_IOCTL_MUNMAP_64:
+ {
+ struct compat_fastrpc_ioctl_munmap_64 __user *unmap32;
+ struct fastrpc_ioctl_munmap __user *unmap;
+
+ unmap32 = compat_ptr(arg);
+ VERIFY(err, NULL != (unmap = compat_alloc_user_space(
+ sizeof(*unmap))));
+ if (err)
+ return -EFAULT;
+ VERIFY(err, 0 == compat_get_fastrpc_ioctl_munmap_64(unmap32,
+ unmap));
+ if (err)
+ return err;
+ return filp->f_op->unlocked_ioctl(filp, FASTRPC_IOCTL_MUNMAP_64,
+ (unsigned long)unmap);
+ }
case COMPAT_FASTRPC_IOCTL_INIT:
/* fall through */
case COMPAT_FASTRPC_IOCTL_INIT_ATTRS:
diff --git a/drivers/char/adsprpc_shared.h b/drivers/char/adsprpc_shared.h
index be8d1a536d6c..a88c668440c7 100644
--- a/drivers/char/adsprpc_shared.h
+++ b/drivers/char/adsprpc_shared.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,6 +19,8 @@
#define FASTRPC_IOCTL_INVOKE _IOWR('R', 1, struct fastrpc_ioctl_invoke)
#define FASTRPC_IOCTL_MMAP _IOWR('R', 2, struct fastrpc_ioctl_mmap)
#define FASTRPC_IOCTL_MUNMAP _IOWR('R', 3, struct fastrpc_ioctl_munmap)
+#define FASTRPC_IOCTL_MMAP_64 _IOWR('R', 14, struct fastrpc_ioctl_mmap_64)
+#define FASTRPC_IOCTL_MUNMAP_64 _IOWR('R', 15, struct fastrpc_ioctl_munmap_64)
#define FASTRPC_IOCTL_INVOKE_FD _IOWR('R', 4, struct fastrpc_ioctl_invoke_fd)
#define FASTRPC_IOCTL_SETMODE _IOWR('R', 5, uint32_t)
#define FASTRPC_IOCTL_INIT _IOWR('R', 6, struct fastrpc_ioctl_init)
@@ -171,6 +173,11 @@ struct fastrpc_ioctl_munmap {
size_t size; /* size */
};
+struct fastrpc_ioctl_munmap_64 {
+ uint64_t vaddrout; /* address to unmap */
+ size_t size; /* size */
+};
+
struct fastrpc_ioctl_mmap {
int fd; /* ion fd */
uint32_t flags; /* flags for dsp to map with */
@@ -179,6 +186,15 @@ struct fastrpc_ioctl_mmap {
uintptr_t vaddrout; /* dsps virtual address */
};
+
+struct fastrpc_ioctl_mmap_64 {
+ int fd; /* ion fd */
+ uint32_t flags; /* flags for dsp to map with */
+ uint64_t vaddrin; /* optional virtual address */
+ size_t size; /* size */
+ uint64_t vaddrout; /* dsps virtual address */
+};
+
struct fastrpc_ioctl_perf { /* kernel performance data */
uintptr_t data;
uint32_t numkeys;
diff --git a/drivers/char/diag/diag_ipc_logging.h b/drivers/char/diag/diag_ipc_logging.h
index b9958a433c46..4b8dd1b12c1c 100644
--- a/drivers/char/diag/diag_ipc_logging.h
+++ b/drivers/char/diag/diag_ipc_logging.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -24,6 +24,7 @@
#define DIAG_DEBUG_MASKS 0x0010
#define DIAG_DEBUG_POWER 0x0020
#define DIAG_DEBUG_BRIDGE 0x0040
+#define DIAG_DEBUG_CONTROL 0x0080
#define DIAG_DEBUG
diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c
index 001a1b367dc6..aa45c2e7ec7b 100644
--- a/drivers/char/diag/diag_memorydevice.c
+++ b/drivers/char/diag/diag_memorydevice.c
@@ -37,6 +37,7 @@ struct diag_md_info diag_md[NUM_DIAG_MD_DEV] = {
.ctx = 0,
.mempool = POOL_TYPE_MUX_APPS,
.num_tbl_entries = 0,
+ .md_info_inited = 0,
.tbl = NULL,
.ops = NULL,
},
@@ -46,6 +47,7 @@ struct diag_md_info diag_md[NUM_DIAG_MD_DEV] = {
.ctx = 0,
.mempool = POOL_TYPE_MDM_MUX,
.num_tbl_entries = 0,
+ .md_info_inited = 0,
.tbl = NULL,
.ops = NULL,
},
@@ -54,6 +56,7 @@ struct diag_md_info diag_md[NUM_DIAG_MD_DEV] = {
.ctx = 0,
.mempool = POOL_TYPE_MDM2_MUX,
.num_tbl_entries = 0,
+ .md_info_inited = 0,
.tbl = NULL,
.ops = NULL,
},
@@ -62,6 +65,7 @@ struct diag_md_info diag_md[NUM_DIAG_MD_DEV] = {
.ctx = 0,
.mempool = POOL_TYPE_QSC_MUX,
.num_tbl_entries = 0,
+ .md_info_inited = 0,
.tbl = NULL,
.ops = NULL,
}
@@ -85,6 +89,8 @@ void diag_md_open_all()
for (i = 0; i < NUM_DIAG_MD_DEV; i++) {
ch = &diag_md[i];
+ if (!ch->md_info_inited)
+ continue;
if (ch->ops && ch->ops->open)
ch->ops->open(ch->ctx, DIAG_MEMORY_DEVICE_MODE);
}
@@ -101,6 +107,8 @@ void diag_md_close_all()
for (i = 0; i < NUM_DIAG_MD_DEV; i++) {
ch = &diag_md[i];
+ if (!ch->md_info_inited)
+ continue;
if (ch->ops && ch->ops->close)
ch->ops->close(ch->ctx, DIAG_MEMORY_DEVICE_MODE);
@@ -159,7 +167,7 @@ int diag_md_write(int id, unsigned char *buf, int len, int ctx)
mutex_unlock(&driver->md_session_lock);
ch = &diag_md[id];
- if (!ch)
+ if (!ch || !ch->md_info_inited)
return -EINVAL;
spin_lock_irqsave(&ch->lock, flags);
@@ -236,6 +244,8 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size,
for (i = 0; i < NUM_DIAG_MD_DEV && !err; i++) {
ch = &diag_md[i];
+ if (!ch->md_info_inited)
+ continue;
for (j = 0; j < ch->num_tbl_entries && !err; j++) {
entry = &ch->tbl[j];
if (entry->len <= 0 || entry->buf == NULL)
@@ -358,6 +368,8 @@ int diag_md_close_peripheral(int id, uint8_t peripheral)
return -EINVAL;
ch = &diag_md[id];
+ if (!ch || !ch->md_info_inited)
+ return -EINVAL;
spin_lock_irqsave(&ch->lock, flags);
for (i = 0; i < ch->num_tbl_entries && !found; i++) {
@@ -405,6 +417,7 @@ int diag_md_init(void)
ch->tbl[j].ctx = 0;
}
spin_lock_init(&(ch->lock));
+ ch->md_info_inited = 1;
}
return 0;
@@ -433,6 +446,7 @@ int diag_md_mdm_init(void)
ch->tbl[j].ctx = 0;
}
spin_lock_init(&(ch->lock));
+ ch->md_info_inited = 1;
}
return 0;
diff --git a/drivers/char/diag/diag_memorydevice.h b/drivers/char/diag/diag_memorydevice.h
index 9b4aa392233d..4d65dedfdb58 100644
--- a/drivers/char/diag/diag_memorydevice.h
+++ b/drivers/char/diag/diag_memorydevice.h
@@ -38,6 +38,7 @@ struct diag_md_info {
int ctx;
int mempool;
int num_tbl_entries;
+ int md_info_inited;
spinlock_t lock;
struct diag_buf_tbl_t *tbl;
struct diag_mux_ops *ops;
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index c7b46304dc84..ff024c2200c0 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -48,6 +48,7 @@
#define STM_RSP_SUPPORTED_INDEX 7
#define STM_RSP_STATUS_INDEX 8
#define STM_RSP_NUM_BYTES 9
+#define RETRY_MAX_COUNT 1000
struct diag_md_hdlc_reset_work {
int pid;
@@ -272,28 +273,22 @@ static void pack_rsp_and_send(unsigned char *buf, int len,
diag_md_session_get_peripheral(APPS_DATA);
if (info && info->peripheral_mask) {
- if (info->peripheral_mask == DIAG_CON_ALL ||
- (info->peripheral_mask & (1 << APPS_DATA)) ||
- (info->peripheral_mask & (1 << PERIPHERAL_MODEM))) {
- rsp_ctxt = SET_BUF_CTXT(APPS_DATA, TYPE_CMD, 1);
- } else {
- for (i = 0; i <= NUM_PERIPHERALS; i++) {
- if (info->peripheral_mask & (1 << i))
- break;
- }
- rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, 1);
+ for (i = 0; i < NUM_MD_SESSIONS; i++) {
+ if (info->peripheral_mask & (1 << i))
+ break;
}
+ rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, TYPE_CMD);
} else
rsp_ctxt = driver->rsp_buf_ctxt;
mutex_unlock(&driver->md_session_lock);
/*
* Keep trying till we get the buffer back. It should probably
- * take one or two iterations. When this loops till UINT_MAX, it
+ * take one or two iterations. When this loops till RETRY_MAX_COUNT, it
* means we did not get a write complete for the previous
* response.
*/
- while (retry_count < UINT_MAX) {
+ while (retry_count < RETRY_MAX_COUNT) {
if (!driver->rsp_buf_busy)
break;
/*
@@ -366,27 +361,21 @@ static void encode_rsp_and_send(unsigned char *buf, int len,
diag_md_session_get_peripheral(APPS_DATA);
if (info && info->peripheral_mask) {
- if (info->peripheral_mask == DIAG_CON_ALL ||
- (info->peripheral_mask & (1 << APPS_DATA)) ||
- (info->peripheral_mask & (1 << PERIPHERAL_MODEM))) {
- rsp_ctxt = SET_BUF_CTXT(APPS_DATA, TYPE_CMD, 1);
- } else {
- for (i = 0; i <= NUM_PERIPHERALS; i++) {
- if (info->peripheral_mask & (1 << i))
- break;
- }
- rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, 1);
+ for (i = 0; i < NUM_MD_SESSIONS; i++) {
+ if (info->peripheral_mask & (1 << i))
+ break;
}
+ rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, TYPE_CMD);
} else
rsp_ctxt = driver->rsp_buf_ctxt;
mutex_unlock(&driver->md_session_lock);
/*
* Keep trying till we get the buffer back. It should probably
- * take one or two iterations. When this loops till UINT_MAX, it
+ * take one or two iterations. When this loops till RETRY_MAX_COUNT, it
* means we did not get a write complete for the previous
* response.
*/
- while (retry_count < UINT_MAX) {
+ while (retry_count < RETRY_MAX_COUNT) {
if (!driver->rsp_buf_busy)
break;
/*
@@ -1754,11 +1743,18 @@ static int diagfwd_mux_write_done(unsigned char *buf, int len, int buf_ctxt,
}
break;
case TYPE_CMD:
- if (peripheral >= 0 && peripheral < NUM_PERIPHERALS) {
+ if (peripheral >= 0 && peripheral < NUM_PERIPHERALS &&
+ num != TYPE_CMD) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "Marking buffer as free after write done p: %d, t: %d, buf_num: %d\n",
+ peripheral, type, num);
diagfwd_write_done(peripheral, type, num);
- }
- if (peripheral == APPS_DATA ||
- ctxt == DIAG_MEMORY_DEVICE_MODE) {
+ } else if (peripheral == APPS_DATA ||
+ (peripheral >= 0 && peripheral < NUM_PERIPHERALS &&
+ num == TYPE_CMD)) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "Marking APPS response buffer free after write done for p: %d, t: %d, buf_num: %d\n",
+ peripheral, type, num);
spin_lock_irqsave(&driver->rsp_buf_busy_lock, flags);
driver->rsp_buf_busy = 0;
driver->encoded_rsp_len = 0;
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 10038e629e6c..d8ec09f90ec4 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -47,8 +47,11 @@ static void diag_mask_update_work_fn(struct work_struct *work)
void diag_cntl_channel_open(struct diagfwd_info *p_info)
{
- if (!p_info)
+ if (!p_info) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid fwd_info structure\n");
return;
+ }
driver->mask_update |= PERIPHERAL_MASK(p_info->peripheral);
queue_work(driver->cntl_wq, &driver->mask_update_work);
diag_notify_md_client(p_info->peripheral, DIAG_STATUS_OPEN);
@@ -58,12 +61,18 @@ void diag_cntl_channel_close(struct diagfwd_info *p_info)
{
uint8_t peripheral;
- if (!p_info)
+ if (!p_info) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid fwd_info structure\n");
return;
+ }
peripheral = p_info->peripheral;
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
driver->feature[peripheral].sent_feature_mask = 0;
driver->feature[peripheral].rcvd_feature_mask = 0;
@@ -88,8 +97,11 @@ static void diag_stm_update_work_fn(struct work_struct *work)
driver->stm_peripheral = 0;
mutex_unlock(&driver->cntl_lock);
- if (peripheral_mask == 0)
+ if (peripheral_mask == 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Empty Peripheral mask\n");
return;
+ }
for (i = 0; i < NUM_PERIPHERALS; i++) {
if (!driver->feature[i].stm_support)
@@ -112,11 +124,18 @@ void diag_notify_md_client(uint8_t peripheral, int data)
struct pid *pid_struct;
struct task_struct *result;
- if (peripheral > NUM_PERIPHERALS)
+ if (peripheral > NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
- if (driver->logging_mode != DIAG_MEMORY_DEVICE_MODE)
+ if (driver->logging_mode != DIAG_MEMORY_DEVICE_MODE) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid logging_mode (%d)\n",
+ driver->logging_mode);
return;
+ }
mutex_lock(&driver->md_session_lock);
memset(&info, 0, sizeof(struct siginfo));
@@ -172,8 +191,12 @@ static void process_pd_status(uint8_t *buf, uint32_t len,
uint32_t pd;
int status = DIAG_STATUS_CLOSED;
- if (!buf || peripheral >= NUM_PERIPHERALS || len < sizeof(*pd_msg))
+ if (!buf || peripheral >= NUM_PERIPHERALS || len < sizeof(*pd_msg)) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d, pd_msg_len = %d\n",
+ !buf, peripheral, len, (int)sizeof(*pd_msg));
return;
+ }
pd_msg = (struct diag_ctrl_msg_pd_status *)buf;
pd = pd_msg->pd_id;
@@ -183,8 +206,11 @@ static void process_pd_status(uint8_t *buf, uint32_t len,
static void enable_stm_feature(uint8_t peripheral)
{
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
mutex_lock(&driver->cntl_lock);
driver->feature[peripheral].stm_support = ENABLE_STM;
@@ -196,8 +222,11 @@ static void enable_stm_feature(uint8_t peripheral)
static void enable_socket_feature(uint8_t peripheral)
{
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
if (driver->supports_sockets)
driver->feature[peripheral].sockets_enabled = 1;
@@ -207,8 +236,11 @@ static void enable_socket_feature(uint8_t peripheral)
static void process_hdlc_encoding_feature(uint8_t peripheral)
{
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
if (driver->supports_apps_hdlc_encoding) {
driver->feature[peripheral].encode_hdlc =
@@ -221,8 +253,11 @@ static void process_hdlc_encoding_feature(uint8_t peripheral)
static void process_upd_header_untagging_feature(uint8_t peripheral)
{
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
if (driver->supports_apps_header_untagging) {
driver->feature[peripheral].untag_header =
@@ -248,8 +283,16 @@ static void process_command_deregistration(uint8_t *buf, uint32_t len,
* Perform Basic sanity. The len field is the size of the data payload.
* This doesn't include the header size.
*/
- if (!buf || peripheral >= NUM_PERIPHERALS || len == 0)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len == 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d\n",
+ !buf, peripheral, len);
return;
+ }
+
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag:peripheral(%d) command deregistration packet processing started\n",
+ peripheral);
dereg = (struct diag_ctrl_cmd_dereg *)ptr;
ptr += header_len;
@@ -257,8 +300,8 @@ static void process_command_deregistration(uint8_t *buf, uint32_t len,
read_len += header_len - (2 * sizeof(uint32_t));
if (dereg->count_entries == 0) {
- pr_debug("diag: In %s, received reg tbl with no entries\n",
- __func__);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: received reg tbl with no entries\n");
return;
}
@@ -277,6 +320,9 @@ static void process_command_deregistration(uint8_t *buf, uint32_t len,
pr_err("diag: In %s, reading less than available, read_len: %d, len: %d count: %d\n",
__func__, read_len, len, dereg->count_entries);
}
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag:peripheral(%d) command deregistration packet processing complete\n",
+ peripheral);
}
static void process_command_registration(uint8_t *buf, uint32_t len,
uint8_t peripheral)
@@ -293,8 +339,15 @@ static void process_command_registration(uint8_t *buf, uint32_t len,
* Perform Basic sanity. The len field is the size of the data payload.
* This doesn't include the header size.
*/
- if (!buf || peripheral >= NUM_PERIPHERALS || len == 0)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len == 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d\n",
+ !buf, peripheral, len);
return;
+ }
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: peripheral(%d) command registration packet processing started\n",
+ peripheral);
reg = (struct diag_ctrl_cmd_reg *)ptr;
ptr += header_len;
@@ -302,7 +355,8 @@ static void process_command_registration(uint8_t *buf, uint32_t len,
read_len += header_len - (2 * sizeof(uint32_t));
if (reg->count_entries == 0) {
- pr_debug("diag: In %s, received reg tbl with no entries\n",
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: In %s, received reg tbl with no entries\n",
__func__);
return;
}
@@ -322,6 +376,9 @@ static void process_command_registration(uint8_t *buf, uint32_t len,
pr_err("diag: In %s, reading less than available, read_len: %d, len: %d count: %d\n",
__func__, read_len, len, reg->count_entries);
}
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: peripheral(%d) command registration packet processing complete\n",
+ peripheral);
}
static void diag_close_transport_work_fn(struct work_struct *work)
@@ -343,8 +400,11 @@ static void diag_close_transport_work_fn(struct work_struct *work)
static void process_socket_feature(uint8_t peripheral)
{
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
mutex_lock(&driver->cntl_lock);
driver->close_transport |= PERIPHERAL_MASK(peripheral);
@@ -375,15 +435,20 @@ static void process_incoming_feature_mask(uint8_t *buf, uint32_t len,
uint32_t feature_mask = 0;
uint8_t *ptr = buf;
- if (!buf || peripheral >= NUM_PERIPHERALS || len == 0)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len == 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d\n",
+ !buf, peripheral, len);
return;
+ }
header = (struct diag_ctrl_feature_mask *)ptr;
ptr += header_len;
feature_mask_len = header->feature_mask_len;
if (feature_mask_len == 0) {
- pr_debug("diag: In %s, received invalid feature mask from peripheral %d\n",
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: In %s, received invalid feature mask from peripheral %d\n",
__func__, peripheral);
return;
}
@@ -396,6 +461,8 @@ static void process_incoming_feature_mask(uint8_t *buf, uint32_t len,
diag_cmd_remove_reg_by_proc(peripheral);
driver->feature[peripheral].rcvd_feature_mask = 1;
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: Received feature mask for peripheral %d\n", peripheral);
for (i = 0; i < feature_mask_len && read_len < len; i++) {
feature_mask = *(uint8_t *)ptr;
@@ -425,6 +492,10 @@ static void process_incoming_feature_mask(uint8_t *buf, uint32_t len,
process_socket_feature(peripheral);
process_log_on_demand_feature(peripheral);
+
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: Peripheral(%d) feature mask is processed\n",
+ peripheral);
}
static void process_last_event_report(uint8_t *buf, uint32_t len,
@@ -436,14 +507,23 @@ static void process_last_event_report(uint8_t *buf, uint32_t len,
uint32_t pkt_len = sizeof(uint32_t) + sizeof(uint16_t);
uint16_t event_size = 0;
- if (!buf || peripheral >= NUM_PERIPHERALS || len != pkt_len)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len != pkt_len) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d, pkt_len = %d\n",
+ !buf, peripheral, len, pkt_len);
return;
+ }
+
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag:started processing last event report for peripheral (%d)\n",
+ peripheral);
mutex_lock(&event_mask.lock);
header = (struct diag_ctrl_last_event_report *)ptr;
event_size = ((header->event_last_id / 8) + 1);
if (event_size >= driver->event_mask_size) {
- pr_debug("diag: In %s, receiving event mask size more that Apps can handle\n",
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: In %s, receiving event mask size more that Apps can handle\n",
__func__);
temp = krealloc(driver->event_mask->ptr, event_size,
GFP_KERNEL);
@@ -461,6 +541,9 @@ static void process_last_event_report(uint8_t *buf, uint32_t len,
driver->last_event_id = header->event_last_id;
err:
mutex_unlock(&event_mask.lock);
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: last event report processed for peripheral (%d)\n",
+ peripheral);
}
static void process_log_range_report(uint8_t *buf, uint32_t len,
@@ -474,8 +557,15 @@ static void process_log_range_report(uint8_t *buf, uint32_t len,
struct diag_ctrl_log_range *log_range = NULL;
struct diag_log_mask_t *mask_ptr = NULL;
- if (!buf || peripheral >= NUM_PERIPHERALS || len < 0)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len < 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d\n",
+ !buf, peripheral, len);
return;
+ }
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag:started processing log range report for peripheral(%d)\n",
+ peripheral);
header = (struct diag_ctrl_log_range_report *)ptr;
ptr += header_len;
@@ -501,6 +591,9 @@ static void process_log_range_report(uint8_t *buf, uint32_t len,
mask_ptr->range = LOG_ITEMS_TO_SIZE(log_range->num_items);
mutex_unlock(&(mask_ptr->lock));
}
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: log range report processed for peripheral (%d)\n",
+ peripheral);
}
static int update_msg_mask_tbl_entry(struct diag_msg_mask_t *mask,
@@ -508,8 +601,12 @@ static int update_msg_mask_tbl_entry(struct diag_msg_mask_t *mask,
{
uint32_t temp_range;
- if (!mask || !range)
+ if (!mask || !range) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid %s\n",
+ (!mask ? "mask" : (!range ? "range" : " ")));
return -EIO;
+ }
if (range->ssid_last < range->ssid_first) {
pr_err("diag: In %s, invalid ssid range, first: %d, last: %d\n",
__func__, range->ssid_first, range->ssid_last);
@@ -541,8 +638,16 @@ static void process_ssid_range_report(uint8_t *buf, uint32_t len,
uint8_t *temp = NULL;
uint32_t min_len = header_len - sizeof(struct diag_ctrl_pkt_header_t);
- if (!buf || peripheral >= NUM_PERIPHERALS || len < min_len)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len < min_len) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d, min_len = %d\n",
+ !buf, peripheral, len, min_len);
return;
+ }
+
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: started processing ssid range for peripheral (%d)\n",
+ peripheral);
header = (struct diag_ctrl_ssid_range_report *)ptr;
ptr += header_len;
@@ -594,6 +699,9 @@ static void process_ssid_range_report(uint8_t *buf, uint32_t len,
driver->msg_mask_tbl_count += 1;
}
mutex_unlock(&driver->msg_mask_lock);
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: processed ssid range for peripheral(%d)\n",
+ peripheral);
}
static void diag_build_time_mask_update(uint8_t *buf,
@@ -610,8 +718,12 @@ static void diag_build_time_mask_update(uint8_t *buf,
uint32_t *dest_ptr = NULL;
struct diag_msg_mask_t *build_mask = NULL;
- if (!range || !buf)
+ if (!range || !buf) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid %s\n",
+ (!range ? "range" : (!buf ? "buf" : " ")));
return;
+ }
if (range->ssid_last < range->ssid_first) {
pr_err("diag: In %s, invalid ssid range, first: %d, last: %d\n",
@@ -673,8 +785,16 @@ static void process_build_mask_report(uint8_t *buf, uint32_t len,
struct diag_ctrl_build_mask_report *header = NULL;
struct diag_ssid_range_t *range = NULL;
- if (!buf || peripheral >= NUM_PERIPHERALS || len < header_len)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len < header_len) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d, header_len = %d\n",
+ !buf, peripheral, len, header_len);
return;
+ }
+
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: started processing build mask for peripheral(%d)\n",
+ peripheral);
header = (struct diag_ctrl_build_mask_report *)ptr;
ptr += header_len;
@@ -690,6 +810,8 @@ static void process_build_mask_report(uint8_t *buf, uint32_t len,
ptr += num_items * sizeof(uint32_t);
read_len += num_items * sizeof(uint32_t);
}
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: processing build mask complete (%d)\n", peripheral);
}
void diag_cntl_process_read_data(struct diagfwd_info *p_info, void *buf,
@@ -700,8 +822,10 @@ void diag_cntl_process_read_data(struct diagfwd_info *p_info, void *buf,
uint8_t *ptr = buf;
struct diag_ctrl_pkt_header_t *ctrl_pkt = NULL;
- if (!buf || len <= 0 || !p_info)
+ if (!buf || len <= 0 || !p_info) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag: Invalid parameters\n");
return;
+ }
if (reg_dirty & PERIPHERAL_MASK(p_info->peripheral)) {
pr_err_ratelimited("diag: dropping command registration from peripheral %d\n",
@@ -711,6 +835,9 @@ void diag_cntl_process_read_data(struct diagfwd_info *p_info, void *buf,
while (read_len + header_len < len) {
ctrl_pkt = (struct diag_ctrl_pkt_header_t *)ptr;
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag:peripheral: %d: pkt_id: %d\n",
+ p_info->peripheral, ctrl_pkt->pkt_id);
switch (ctrl_pkt->pkt_id) {
case DIAG_CTRL_MSG_REG:
process_command_registration(ptr, ctrl_pkt->len,
@@ -745,13 +872,15 @@ void diag_cntl_process_read_data(struct diagfwd_info *p_info, void *buf,
p_info->peripheral);
break;
default:
- pr_debug("diag: Control packet %d not supported\n",
- ctrl_pkt->pkt_id);
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: Control packet %d not supported\n",
+ ctrl_pkt->pkt_id);
}
ptr += header_len + ctrl_pkt->len;
read_len += header_len + ctrl_pkt->len;
}
-
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: control packet processing complete\n");
return;
}
@@ -969,15 +1098,16 @@ void diag_real_time_work_fn(struct work_struct *work)
for (i = 0; i < DIAG_NUM_PROC; i++) {
temp_real_time = diag_compute_real_time(i);
if (temp_real_time == driver->real_time_mode[i]) {
- pr_debug("diag: did not update real time mode on proc %d, already in the req mode %d",
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: did not update real time mode on proc %d, already in the req mode %d\n",
i, temp_real_time);
continue;
}
if (i == DIAG_LOCAL_PROC) {
if (!send_update) {
- pr_debug("diag: In %s, cannot send real time mode pkt since one of the periperhal is in buffering mode\n",
- __func__);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: cannot send real time mode pkt since one of the periperhal is in buffering mode\n");
break;
}
for (j = 0; j < NUM_PERIPHERALS; j++)
@@ -1011,7 +1141,8 @@ void diag_real_time_work_fn(struct work_struct *work)
temp_real_time = MODE_NONREALTIME;
}
if (temp_real_time == driver->real_time_mode[i]) {
- pr_debug("diag: did not update real time mode on proc %d, already in the req mode %d",
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: did not update real time mode on proc %d, already in the req mode %d\n",
i, temp_real_time);
continue;
}
@@ -1046,8 +1177,8 @@ static int __diag_send_real_time_update(uint8_t peripheral, int real_time,
if (!driver->diagfwd_cntl[peripheral] ||
!driver->diagfwd_cntl[peripheral]->ch_open) {
- pr_debug("diag: In %s, control channel is not open, p: %d\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: control channel is not open, p: %d\n", peripheral);
return err;
}
@@ -1194,8 +1325,9 @@ int diag_send_peripheral_buffering_mode(struct diag_buffering_mode_t *params)
}
if (!driver->feature[peripheral].peripheral_buffering) {
- pr_debug("diag: In %s, peripheral %d doesn't support buffering\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: peripheral %d doesn't support buffering\n",
+ peripheral);
driver->buffering_flag[params->peripheral] = 0;
return -EIO;
}
@@ -1260,8 +1392,9 @@ int diag_send_stm_state(uint8_t peripheral, uint8_t stm_control_data)
if (!driver->diagfwd_cntl[peripheral] ||
!driver->diagfwd_cntl[peripheral]->ch_open) {
- pr_debug("diag: In %s, control channel is not open, p: %d\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: control channel is not open, p: %d\n",
+ peripheral);
return -ENODEV;
}
@@ -1290,15 +1423,17 @@ int diag_send_peripheral_drain_immediate(uint8_t pd,
struct diag_ctrl_drain_immediate_v2 ctrl_pkt_v2;
if (!driver->feature[peripheral].peripheral_buffering) {
- pr_debug("diag: In %s, peripheral %d doesn't support buffering\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: peripheral %d doesn't support buffering\n",
+ peripheral);
return -EINVAL;
}
if (!driver->diagfwd_cntl[peripheral] ||
!driver->diagfwd_cntl[peripheral]->ch_open) {
- pr_debug("diag: In %s, control channel is not open, p: %d\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: control channel is not open, p: %d\n",
+ peripheral);
return -ENODEV;
}
@@ -1355,8 +1490,9 @@ int diag_send_buffering_tx_mode_pkt(uint8_t peripheral,
}
if (!driver->feature[peripheral].peripheral_buffering) {
- pr_debug("diag: In %s, peripheral %d doesn't support buffering\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: peripheral %d doesn't support buffering\n",
+ peripheral);
return -EINVAL;
}
@@ -1434,15 +1570,17 @@ int diag_send_buffering_wm_values(uint8_t peripheral,
}
if (!driver->feature[peripheral].peripheral_buffering) {
- pr_debug("diag: In %s, peripheral %d doesn't support buffering\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: peripheral %d doesn't support buffering\n",
+ peripheral);
return -EINVAL;
}
if (!driver->diagfwd_cntl[peripheral] ||
!driver->diagfwd_cntl[peripheral]->ch_open) {
- pr_debug("diag: In %s, control channel is not open, p: %d\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: control channel is not open, p: %d\n",
+ peripheral);
return -ENODEV;
}
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index 6b74c0056d1b..bfdce051d405 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -728,6 +728,7 @@ static void diagfwd_cntl_read_done(struct diagfwd_info *fwd_info,
unsigned char *buf, int len)
{
if (!fwd_info) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag: Invalid fwd_info\n");
diag_ws_release();
return;
}
@@ -748,8 +749,12 @@ static void diagfwd_cntl_read_done(struct diagfwd_info *fwd_info,
*/
diag_ws_on_copy_fail(DIAG_WS_MUX);
/* Reset the buffer in_busy value after processing the data */
- if (fwd_info->buf_1)
+ if (fwd_info->buf_1) {
atomic_set(&fwd_info->buf_1->in_busy, 0);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "Buffer 1 for core PD is marked free, p: %d, t: %d\n",
+ fwd_info->peripheral, fwd_info->type);
+ }
diagfwd_queue_read(fwd_info);
diagfwd_queue_read(&peripheral_info[TYPE_DATA][fwd_info->peripheral]);
@@ -774,8 +779,12 @@ static void diagfwd_dci_read_done(struct diagfwd_info *fwd_info,
diag_dci_process_peripheral_data(fwd_info, (void *)buf, len);
/* Reset the buffer in_busy value after processing the data */
- if (fwd_info->buf_1)
+ if (fwd_info->buf_1) {
atomic_set(&fwd_info->buf_1->in_busy, 0);
+ DIAG_LOG(DIAG_DEBUG_DCI,
+ "Buffer 1 for core PD is marked free, p: %d, t: %d\n",
+ fwd_info->peripheral, fwd_info->type);
+ }
diagfwd_queue_read(fwd_info);
}
@@ -1106,8 +1115,11 @@ void *diagfwd_request_write_buf(struct diagfwd_info *fwd_info)
int index;
unsigned long flags;
+ if (!fwd_info)
+ return NULL;
spin_lock_irqsave(&fwd_info->write_buf_lock, flags);
- for (index = 0 ; index < NUM_WRITE_BUFFERS; index++) {
+ for (index = 0; (index < NUM_WRITE_BUFFERS) && fwd_info->buf_ptr[index];
+ index++) {
if (!atomic_read(&(fwd_info->buf_ptr[index]->in_busy))) {
atomic_set(&(fwd_info->buf_ptr[index]->in_busy), 1);
buf = fwd_info->buf_ptr[index]->data;
@@ -1529,7 +1541,8 @@ int diagfwd_write_buffer_done(struct diagfwd_info *fwd_info, const void *ptr)
if (!fwd_info || !ptr)
return found;
spin_lock_irqsave(&fwd_info->write_buf_lock, flags);
- for (index = 0; index < NUM_WRITE_BUFFERS; index++) {
+ for (index = 0; (index < NUM_WRITE_BUFFERS) && fwd_info->buf_ptr[index];
+ index++) {
if (fwd_info->buf_ptr[index]->data == ptr) {
atomic_set(&fwd_info->buf_ptr[index]->in_busy, 0);
found = 1;
@@ -1548,13 +1561,15 @@ void diagfwd_channel_read(struct diagfwd_info *fwd_info)
struct diagfwd_buf_t *temp_buf = NULL;
if (!fwd_info) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag: Invalid fwd_info\n");
diag_ws_release();
return;
}
if (!fwd_info->inited || !atomic_read(&fwd_info->opened)) {
- pr_debug("diag: In %s, p: %d, t: %d, inited: %d, opened: %d ch_open: %d\n",
- __func__, fwd_info->peripheral, fwd_info->type,
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: p: %d, t: %d, inited: %d, opened: %d, ch_open: %d\n",
+ fwd_info->peripheral, fwd_info->type,
fwd_info->inited, atomic_read(&fwd_info->opened),
fwd_info->ch_open);
diag_ws_release();
@@ -1590,8 +1605,9 @@ void diagfwd_channel_read(struct diagfwd_info *fwd_info)
atomic_set(&temp_buf->in_busy, 1);
}
} else {
- pr_debug("diag: In %s, both buffers are empty for p: %d, t: %d\n",
- __func__, fwd_info->peripheral, fwd_info->type);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: both buffers are busy for p: %d, t: %d\n",
+ fwd_info->peripheral, fwd_info->type);
}
if (!read_buf) {
diff --git a/drivers/char/hw_random/stm32-rng.c b/drivers/char/hw_random/stm32-rng.c
index 92a810648bd0..530aacca3eb8 100644
--- a/drivers/char/hw_random/stm32-rng.c
+++ b/drivers/char/hw_random/stm32-rng.c
@@ -21,6 +21,7 @@
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
+#include <linux/reset.h>
#include <linux/slab.h>
#define RNG_CR 0x00
@@ -46,6 +47,7 @@ struct stm32_rng_private {
struct hwrng rng;
void __iomem *base;
struct clk *clk;
+ struct reset_control *rst;
};
static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
@@ -140,6 +142,13 @@ static int stm32_rng_probe(struct platform_device *ofdev)
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
+ priv->rst = devm_reset_control_get(&ofdev->dev, NULL);
+ if (!IS_ERR(priv->rst)) {
+ reset_control_assert(priv->rst);
+ udelay(2);
+ reset_control_deassert(priv->rst);
+ }
+
dev_set_drvdata(dev, priv);
priv->rng.name = dev_driver_string(dev),
diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c
index 0c98a9d51a24..44ce80606944 100644
--- a/drivers/char/hw_random/via-rng.c
+++ b/drivers/char/hw_random/via-rng.c
@@ -140,7 +140,7 @@ static int via_rng_init(struct hwrng *rng)
* RNG configuration like it used to be the case in this
* register */
if ((c->x86 == 6) && (c->x86_model >= 0x0f)) {
- if (!cpu_has_xstore_enabled) {
+ if (!boot_cpu_has(X86_FEATURE_XSTORE_EN)) {
pr_err(PFX "can't enable hardware RNG "
"if XSTORE is not enabled\n");
return -ENODEV;
@@ -200,8 +200,9 @@ static int __init mod_init(void)
{
int err;
- if (!cpu_has_xstore)
+ if (!boot_cpu_has(X86_FEATURE_XSTORE))
return -ENODEV;
+
pr_info("VIA RNG detected\n");
err = hwrng_register(&via_rng);
if (err) {
diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c
index 6e658aa114f1..a70518a4fcec 100644
--- a/drivers/char/ipmi/ipmi_powernv.c
+++ b/drivers/char/ipmi/ipmi_powernv.c
@@ -251,8 +251,9 @@ static int ipmi_powernv_probe(struct platform_device *pdev)
ipmi->irq = opal_event_request(prop);
}
- if (request_irq(ipmi->irq, ipmi_opal_event, IRQ_TYPE_LEVEL_HIGH,
- "opal-ipmi", ipmi)) {
+ rc = request_irq(ipmi->irq, ipmi_opal_event, IRQ_TYPE_LEVEL_HIGH,
+ "opal-ipmi", ipmi);
+ if (rc) {
dev_warn(dev, "Unable to request irq\n");
goto err_dispose;
}
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index 83c206f0fc98..d6d166fe49a3 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -757,7 +757,7 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
ssif_info->ssif_state = SSIF_NORMAL;
ipmi_ssif_unlock_cond(ssif_info, flags);
pr_warn(PFX "Error getting flags: %d %d, %x\n",
- result, len, data[2]);
+ result, len, (len >= 3) ? data[2] : 0);
} else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| data[1] != IPMI_GET_MSG_FLAGS_CMD) {
/*
@@ -779,7 +779,7 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
if ((result < 0) || (len < 3) || (data[2] != 0)) {
/* Error clearing flags */
pr_warn(PFX "Error clearing flags: %d %d, %x\n",
- result, len, data[2]);
+ result, len, (len >= 3) ? data[2] : 0);
} else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| data[1] != IPMI_CLEAR_MSG_FLAGS_CMD) {
pr_warn(PFX "Invalid response clearing flags: %x %x\n",
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index a0d9ac6b6cc9..e759100e41a7 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -26,6 +26,7 @@
#include <linux/spinlock.h>
#include <linux/freezer.h>
#include <linux/major.h>
+#include <linux/of.h>
#include "tpm.h"
#include "tpm_eventlog.h"
@@ -324,8 +325,20 @@ static void tpm1_chip_unregister(struct tpm_chip *chip)
*/
int tpm_chip_register(struct tpm_chip *chip)
{
+#ifdef CONFIG_OF
+ struct device_node *np;
+#endif
int rc;
+#ifdef CONFIG_OF
+ np = of_find_node_by_name(NULL, "vtpm");
+ if (np) {
+ if (of_property_read_bool(np, "powered-while-suspended"))
+ chip->flags |= TPM_CHIP_FLAG_ALWAYS_POWERED;
+ }
+ of_node_put(np);
+#endif
+
rc = tpm1_chip_register(chip);
if (rc)
return rc;
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 36afc1a21699..95a40ec854ad 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -787,6 +787,10 @@ int tpm_do_selftest(struct tpm_chip *chip)
loops = jiffies_to_msecs(duration) / delay_msec;
rc = tpm_continue_selftest(chip);
+ if (rc == TPM_ERR_INVALID_POSTINIT) {
+ chip->flags |= TPM_CHIP_FLAG_ALWAYS_POWERED;
+ dev_info(&chip->dev, "TPM not ready (%d)\n", rc);
+ }
/* This may fail if there was no TPM driver during a suspend/resume
* cycle; some may return 10 (BAD_ORDINAL), others 28 (FAILEDSELFTEST)
*/
@@ -931,6 +935,9 @@ int tpm_pm_suspend(struct device *dev)
if (chip == NULL)
return -ENODEV;
+ if (chip->flags & TPM_CHIP_FLAG_ALWAYS_POWERED)
+ return 0;
+
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
tpm2_shutdown(chip, TPM2_SU_STATE);
return 0;
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 772d99b3a8e4..36e1abda00f9 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -168,6 +168,7 @@ struct tpm_vendor_specific {
enum tpm_chip_flags {
TPM_CHIP_FLAG_REGISTERED = BIT(0),
TPM_CHIP_FLAG_TPM2 = BIT(1),
+ TPM_CHIP_FLAG_ALWAYS_POWERED = BIT(5),
};
struct tpm_chip {
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 73d65813de8b..d859b34d2b60 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2244,6 +2244,9 @@ static int clk_core_get_phase(struct clk_core *core)
int ret;
clk_prepare_lock();
+ /* Always try to update cached phase if possible */
+ if (core->ops->get_phase)
+ core->phase = core->ops->get_phase(core->hw);
ret = core->phase;
clk_prepare_unlock();
diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c
index 33c20c6b45af..b840e4ace623 100644
--- a/drivers/clk/rockchip/clk-mmc-phase.c
+++ b/drivers/clk/rockchip/clk-mmc-phase.c
@@ -60,6 +60,12 @@ static int rockchip_mmc_get_phase(struct clk_hw *hw)
u16 degrees;
u32 delay_num = 0;
+ /* See the comment for rockchip_mmc_set_phase below */
+ if (!rate) {
+ pr_err("%s: invalid clk rate\n", __func__);
+ return -EINVAL;
+ }
+
raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift);
degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
@@ -86,6 +92,23 @@ static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees)
u32 raw_value;
u32 delay;
+ /*
+ * The below calculation is based on the output clock from
+ * MMC host to the card, which expects the phase clock inherits
+ * the clock rate from its parent, namely the output clock
+ * provider of MMC host. However, things may go wrong if
+ * (1) It is orphan.
+ * (2) It is assigned to the wrong parent.
+ *
+ * This check help debug the case (1), which seems to be the
+ * most likely problem we often face and which makes it difficult
+ * for people to debug unstable mmc tuning results.
+ */
+ if (!rate) {
+ pr_err("%s: invalid clk rate\n", __func__);
+ return -EINVAL;
+ }
+
nineties = degrees / 90;
remainder = (degrees % 90);
diff --git a/drivers/clk/samsung/clk-exynos3250.c b/drivers/clk/samsung/clk-exynos3250.c
index fdd41b17a24f..294efaef5b82 100644
--- a/drivers/clk/samsung/clk-exynos3250.c
+++ b/drivers/clk/samsung/clk-exynos3250.c
@@ -683,7 +683,7 @@ static struct samsung_pll_rate_table exynos3250_epll_rates[] = {
PLL_36XX_RATE(144000000, 96, 2, 3, 0),
PLL_36XX_RATE( 96000000, 128, 2, 4, 0),
PLL_36XX_RATE( 84000000, 112, 2, 4, 0),
- PLL_36XX_RATE( 80000004, 106, 2, 4, 43691),
+ PLL_36XX_RATE( 80000003, 106, 2, 4, 43691),
PLL_36XX_RATE( 73728000, 98, 2, 4, 19923),
PLL_36XX_RATE( 67737598, 270, 3, 5, 62285),
PLL_36XX_RATE( 65535999, 174, 2, 5, 49982),
@@ -719,7 +719,7 @@ static struct samsung_pll_rate_table exynos3250_vpll_rates[] = {
PLL_36XX_RATE(148352005, 98, 2, 3, 59070),
PLL_36XX_RATE(108000000, 144, 2, 4, 0),
PLL_36XX_RATE( 74250000, 99, 2, 4, 0),
- PLL_36XX_RATE( 74176002, 98, 3, 4, 59070),
+ PLL_36XX_RATE( 74176002, 98, 2, 4, 59070),
PLL_36XX_RATE( 54054000, 216, 3, 5, 14156),
PLL_36XX_RATE( 54000000, 144, 2, 5, 0),
{ /* sentinel */ }
diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c
index 5bebf8cb0d70..f0b564c7c9c1 100644
--- a/drivers/clk/samsung/clk-exynos5250.c
+++ b/drivers/clk/samsung/clk-exynos5250.c
@@ -711,13 +711,13 @@ static struct samsung_pll_rate_table epll_24mhz_tbl[] __initdata = {
/* sorted in descending order */
/* PLL_36XX_RATE(rate, m, p, s, k) */
PLL_36XX_RATE(192000000, 64, 2, 2, 0),
- PLL_36XX_RATE(180633600, 90, 3, 2, 20762),
+ PLL_36XX_RATE(180633605, 90, 3, 2, 20762),
PLL_36XX_RATE(180000000, 90, 3, 2, 0),
PLL_36XX_RATE(73728000, 98, 2, 4, 19923),
- PLL_36XX_RATE(67737600, 90, 2, 4, 20762),
+ PLL_36XX_RATE(67737602, 90, 2, 4, 20762),
PLL_36XX_RATE(49152000, 98, 3, 4, 19923),
- PLL_36XX_RATE(45158400, 90, 3, 4, 20762),
- PLL_36XX_RATE(32768000, 131, 3, 5, 4719),
+ PLL_36XX_RATE(45158401, 90, 3, 4, 20762),
+ PLL_36XX_RATE(32768001, 131, 3, 5, 4719),
{ },
};
diff --git a/drivers/clk/samsung/clk-exynos5260.c b/drivers/clk/samsung/clk-exynos5260.c
index d1a29f6c1084..7027e77bf859 100644
--- a/drivers/clk/samsung/clk-exynos5260.c
+++ b/drivers/clk/samsung/clk-exynos5260.c
@@ -65,7 +65,7 @@ static struct samsung_pll_rate_table pll2650_24mhz_tbl[] __initdata = {
PLL_36XX_RATE(480000000, 160, 2, 2, 0),
PLL_36XX_RATE(432000000, 144, 2, 2, 0),
PLL_36XX_RATE(400000000, 200, 3, 2, 0),
- PLL_36XX_RATE(394073130, 459, 7, 2, 49282),
+ PLL_36XX_RATE(394073128, 459, 7, 2, 49282),
PLL_36XX_RATE(333000000, 111, 2, 2, 0),
PLL_36XX_RATE(300000000, 100, 2, 2, 0),
PLL_36XX_RATE(266000000, 266, 3, 3, 0),
diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
index cee062c588de..91c89ac193b9 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -747,7 +747,7 @@ static struct samsung_pll_rate_table exynos5443_pll_rates[] = {
PLL_35XX_RATE(800000000U, 400, 6, 1),
PLL_35XX_RATE(733000000U, 733, 12, 1),
PLL_35XX_RATE(700000000U, 175, 3, 1),
- PLL_35XX_RATE(667000000U, 222, 4, 1),
+ PLL_35XX_RATE(666000000U, 222, 4, 1),
PLL_35XX_RATE(633000000U, 211, 4, 1),
PLL_35XX_RATE(600000000U, 500, 5, 2),
PLL_35XX_RATE(552000000U, 460, 5, 2),
@@ -773,12 +773,12 @@ static struct samsung_pll_rate_table exynos5443_pll_rates[] = {
/* AUD_PLL */
static struct samsung_pll_rate_table exynos5443_aud_pll_rates[] = {
PLL_36XX_RATE(400000000U, 200, 3, 2, 0),
- PLL_36XX_RATE(393216000U, 197, 3, 2, -25690),
+ PLL_36XX_RATE(393216003U, 197, 3, 2, -25690),
PLL_36XX_RATE(384000000U, 128, 2, 2, 0),
- PLL_36XX_RATE(368640000U, 246, 4, 2, -15729),
- PLL_36XX_RATE(361507200U, 181, 3, 2, -16148),
- PLL_36XX_RATE(338688000U, 113, 2, 2, -6816),
- PLL_36XX_RATE(294912000U, 98, 1, 3, 19923),
+ PLL_36XX_RATE(368639991U, 246, 4, 2, -15729),
+ PLL_36XX_RATE(361507202U, 181, 3, 2, -16148),
+ PLL_36XX_RATE(338687988U, 113, 2, 2, -6816),
+ PLL_36XX_RATE(294912002U, 98, 1, 3, 19923),
PLL_36XX_RATE(288000000U, 96, 1, 3, 0),
PLL_36XX_RATE(252000000U, 84, 1, 3, 0),
{ /* sentinel */ }
diff --git a/drivers/clk/samsung/clk-s3c2410.c b/drivers/clk/samsung/clk-s3c2410.c
index 0945a8852299..69e3e848716a 100644
--- a/drivers/clk/samsung/clk-s3c2410.c
+++ b/drivers/clk/samsung/clk-s3c2410.c
@@ -168,7 +168,7 @@ static struct samsung_pll_rate_table pll_s3c2410_12mhz_tbl[] __initdata = {
PLL_35XX_RATE(226000000, 105, 1, 1),
PLL_35XX_RATE(210000000, 132, 2, 1),
/* 2410 common */
- PLL_35XX_RATE(203000000, 161, 3, 1),
+ PLL_35XX_RATE(202800000, 161, 3, 1),
PLL_35XX_RATE(192000000, 88, 1, 1),
PLL_35XX_RATE(186000000, 85, 1, 1),
PLL_35XX_RATE(180000000, 82, 1, 1),
@@ -178,18 +178,18 @@ static struct samsung_pll_rate_table pll_s3c2410_12mhz_tbl[] __initdata = {
PLL_35XX_RATE(147000000, 90, 2, 1),
PLL_35XX_RATE(135000000, 82, 2, 1),
PLL_35XX_RATE(124000000, 116, 1, 2),
- PLL_35XX_RATE(118000000, 150, 2, 2),
+ PLL_35XX_RATE(118500000, 150, 2, 2),
PLL_35XX_RATE(113000000, 105, 1, 2),
- PLL_35XX_RATE(101000000, 127, 2, 2),
+ PLL_35XX_RATE(101250000, 127, 2, 2),
PLL_35XX_RATE(90000000, 112, 2, 2),
- PLL_35XX_RATE(85000000, 105, 2, 2),
+ PLL_35XX_RATE(84750000, 105, 2, 2),
PLL_35XX_RATE(79000000, 71, 1, 2),
- PLL_35XX_RATE(68000000, 82, 2, 2),
- PLL_35XX_RATE(56000000, 142, 2, 3),
+ PLL_35XX_RATE(67500000, 82, 2, 2),
+ PLL_35XX_RATE(56250000, 142, 2, 3),
PLL_35XX_RATE(48000000, 120, 2, 3),
- PLL_35XX_RATE(51000000, 161, 3, 3),
+ PLL_35XX_RATE(50700000, 161, 3, 3),
PLL_35XX_RATE(45000000, 82, 1, 3),
- PLL_35XX_RATE(34000000, 82, 2, 3),
+ PLL_35XX_RATE(33750000, 82, 2, 3),
{ /* sentinel */ },
};
diff --git a/drivers/clocksource/fsl_ftm_timer.c b/drivers/clocksource/fsl_ftm_timer.c
index 517e1c7624d4..a00209702f39 100644
--- a/drivers/clocksource/fsl_ftm_timer.c
+++ b/drivers/clocksource/fsl_ftm_timer.c
@@ -281,7 +281,7 @@ static int __init __ftm_clk_init(struct device_node *np, char *cnt_name,
static unsigned long __init ftm_clk_init(struct device_node *np)
{
- unsigned long freq;
+ long freq;
freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt");
if (freq <= 0)
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 7c0bdfb1a2ca..0dcbf951ad1b 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -100,9 +100,19 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
policy->cpuinfo.max_freq = policy->max;
policy->shared_type = cpu->shared_type;
- if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
+ if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) {
+ int i;
+
cpumask_copy(policy->cpus, cpu->shared_cpu_map);
- else if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL) {
+
+ for_each_cpu(i, policy->cpus) {
+ if (unlikely(i == policy->cpu))
+ continue;
+
+ memcpy(&all_cpu_data[i]->perf_caps, &cpu->perf_caps,
+ sizeof(cpu->perf_caps));
+ }
+ } else if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL) {
/* Support only SW_ANY for now. */
pr_debug("Unsupported CPU co-ord type\n");
return -EFAULT;
@@ -166,8 +176,13 @@ static int __init cppc_cpufreq_init(void)
return ret;
out:
- for_each_possible_cpu(i)
- kfree(all_cpu_data[i]);
+ for_each_possible_cpu(i) {
+ cpu = all_cpu_data[i];
+ if (!cpu)
+ break;
+ free_cpumask_var(cpu->shared_cpu_map);
+ kfree(cpu);
+ }
kfree(all_cpu_data);
return -ENODEV;
diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c
index da2d6777bd09..97a364694bfc 100644
--- a/drivers/crypto/padlock-aes.c
+++ b/drivers/crypto/padlock-aes.c
@@ -515,7 +515,7 @@ static int __init padlock_init(void)
if (!x86_match_cpu(padlock_cpu_id))
return -ENODEV;
- if (!cpu_has_xcrypt_enabled) {
+ if (!boot_cpu_has(X86_FEATURE_XCRYPT_EN)) {
printk(KERN_NOTICE PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n");
return -ENODEV;
}
diff --git a/drivers/crypto/padlock-sha.c b/drivers/crypto/padlock-sha.c
index 4e154c9b9206..8c5f90647b7a 100644
--- a/drivers/crypto/padlock-sha.c
+++ b/drivers/crypto/padlock-sha.c
@@ -540,7 +540,7 @@ static int __init padlock_init(void)
struct shash_alg *sha1;
struct shash_alg *sha256;
- if (!x86_match_cpu(padlock_sha_ids) || !cpu_has_phe_enabled)
+ if (!x86_match_cpu(padlock_sha_ids) || !boot_cpu_has(X86_FEATURE_PHE_EN))
return -ENODEV;
/* Register the newly added algorithm module if on *
diff --git a/drivers/crypto/sunxi-ss/sun4i-ss-core.c b/drivers/crypto/sunxi-ss/sun4i-ss-core.c
index 107cd2a41cae..24651d3217cd 100644
--- a/drivers/crypto/sunxi-ss/sun4i-ss-core.c
+++ b/drivers/crypto/sunxi-ss/sun4i-ss-core.c
@@ -422,6 +422,7 @@ static struct platform_driver sun4i_ss_driver = {
module_platform_driver(sun4i_ss_driver);
+MODULE_ALIAS("platform:sun4i-ss");
MODULE_DESCRIPTION("Allwinner Security System cryptographic accelerator");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Corentin LABBE <clabbe.montjoie@gmail.com>");
diff --git a/drivers/crypto/vmx/aes.c b/drivers/crypto/vmx/aes.c
index 263af709e536..b907e4b1bbe2 100644
--- a/drivers/crypto/vmx/aes.c
+++ b/drivers/crypto/vmx/aes.c
@@ -53,8 +53,6 @@ static int p8_aes_init(struct crypto_tfm *tfm)
alg, PTR_ERR(fallback));
return PTR_ERR(fallback);
}
- printk(KERN_INFO "Using '%s' as fallback implementation.\n",
- crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
crypto_cipher_set_flags(fallback,
crypto_cipher_get_flags((struct
diff --git a/drivers/crypto/vmx/aes_cbc.c b/drivers/crypto/vmx/aes_cbc.c
index 3f8bb9a40df1..9506e8693c81 100644
--- a/drivers/crypto/vmx/aes_cbc.c
+++ b/drivers/crypto/vmx/aes_cbc.c
@@ -55,8 +55,6 @@ static int p8_aes_cbc_init(struct crypto_tfm *tfm)
alg, PTR_ERR(fallback));
return PTR_ERR(fallback);
}
- printk(KERN_INFO "Using '%s' as fallback implementation.\n",
- crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
crypto_blkcipher_set_flags(
fallback,
diff --git a/drivers/crypto/vmx/aes_ctr.c b/drivers/crypto/vmx/aes_ctr.c
index d83ab4bac8b1..7d070201b3d3 100644
--- a/drivers/crypto/vmx/aes_ctr.c
+++ b/drivers/crypto/vmx/aes_ctr.c
@@ -53,8 +53,6 @@ static int p8_aes_ctr_init(struct crypto_tfm *tfm)
alg, PTR_ERR(fallback));
return PTR_ERR(fallback);
}
- printk(KERN_INFO "Using '%s' as fallback implementation.\n",
- crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
crypto_blkcipher_set_flags(
fallback,
diff --git a/drivers/crypto/vmx/ghash.c b/drivers/crypto/vmx/ghash.c
index 9cb3a0b715e2..84b9389bf1ed 100644
--- a/drivers/crypto/vmx/ghash.c
+++ b/drivers/crypto/vmx/ghash.c
@@ -64,8 +64,6 @@ static int p8_ghash_init_tfm(struct crypto_tfm *tfm)
alg, PTR_ERR(fallback));
return PTR_ERR(fallback);
}
- printk(KERN_INFO "Using '%s' as fallback implementation.\n",
- crypto_tfm_alg_driver_name(crypto_shash_tfm(fallback)));
crypto_shash_set_flags(fallback,
crypto_shash_get_flags((struct crypto_shash
diff --git a/drivers/devfreq/arm-memlat-mon.c b/drivers/devfreq/arm-memlat-mon.c
index 4fb0a5ffda50..739d02300ac8 100644
--- a/drivers/devfreq/arm-memlat-mon.c
+++ b/drivers/devfreq/arm-memlat-mon.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2016, 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -352,6 +352,7 @@ static struct platform_driver arm_memlat_mon_driver = {
.name = "arm-memlat-mon",
.of_match_table = match_table,
.owner = THIS_MODULE,
+ .suppress_bind_attrs = true,
},
};
diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c
index 315d3a67e43e..4db5a29fa849 100644
--- a/drivers/devfreq/bimc-bwmon.c
+++ b/drivers/devfreq/bimc-bwmon.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2016, 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -669,6 +669,7 @@ static struct platform_driver bimc_bwmon_driver = {
.name = "bimc-bwmon",
.of_match_table = match_table,
.owner = THIS_MODULE,
+ .suppress_bind_attrs = true,
},
};
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 66d84bcf9bbf..8db791ef2027 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -1533,7 +1533,7 @@ static void pl330_dotask(unsigned long data)
/* Returns 1 if state was updated, 0 otherwise */
static int pl330_update(struct pl330_dmac *pl330)
{
- struct dma_pl330_desc *descdone, *tmp;
+ struct dma_pl330_desc *descdone;
unsigned long flags;
void __iomem *regs;
u32 val;
@@ -1611,7 +1611,9 @@ static int pl330_update(struct pl330_dmac *pl330)
}
/* Now that we are in no hurry, do the callbacks */
- list_for_each_entry_safe(descdone, tmp, &pl330->req_done, rqd) {
+ while (!list_empty(&pl330->req_done)) {
+ descdone = list_first_entry(&pl330->req_done,
+ struct dma_pl330_desc, rqd);
list_del(&descdone->rqd);
spin_unlock_irqrestore(&pl330->lock, flags);
dma_pl330_rqcb(descdone, PL330_ERR_NONE);
diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c
index 7820d07e7bee..2b36d1c63aa5 100644
--- a/drivers/dma/sh/rcar-dmac.c
+++ b/drivers/dma/sh/rcar-dmac.c
@@ -851,7 +851,7 @@ rcar_dmac_chan_prep_sg(struct rcar_dmac_chan *chan, struct scatterlist *sgl,
rcar_dmac_chan_configure_desc(chan, desc);
- max_chunk_size = (RCAR_DMATCR_MASK + 1) << desc->xfer_shift;
+ max_chunk_size = RCAR_DMATCR_MASK << desc->xfer_shift;
/*
* Allocate and fill the transfer chunk descriptors. We own the only
diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c
index 56410ea75ac5..6682b3eec2b6 100644
--- a/drivers/dma/sh/usb-dmac.c
+++ b/drivers/dma/sh/usb-dmac.c
@@ -448,7 +448,7 @@ usb_dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
static int usb_dmac_chan_terminate_all(struct dma_chan *chan)
{
struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan);
- struct usb_dmac_desc *desc;
+ struct usb_dmac_desc *desc, *_desc;
unsigned long flags;
LIST_HEAD(head);
LIST_HEAD(list);
@@ -459,7 +459,7 @@ static int usb_dmac_chan_terminate_all(struct dma_chan *chan)
if (uchan->desc)
uchan->desc = NULL;
list_splice_init(&uchan->desc_got, &list);
- list_for_each_entry(desc, &list, node)
+ list_for_each_entry_safe(desc, _desc, &list, node)
list_move_tail(&desc->node, &uchan->desc_freed);
spin_unlock_irqrestore(&uchan->vc.lock, flags);
vchan_dma_desc_free_list(&uchan->vc, &head);
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index c2f5117fd8cb..5545a7f3a98f 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -1130,7 +1130,13 @@ static int context_add_buffer(struct context *ctx)
return -ENOMEM;
offset = (void *)&desc->buffer - (void *)desc;
- desc->buffer_size = PAGE_SIZE - offset;
+ /*
+ * Some controllers, like JMicron ones, always issue 0x20-byte DMA reads
+ * for descriptors, even 0x10-byte ones. This can cause page faults when
+ * an IOMMU is in use and the oversized read crosses a page boundary.
+ * Work around this by always leaving at least 0x10 bytes of padding.
+ */
+ desc->buffer_size = PAGE_SIZE - offset - 0x10;
desc->buffer_bus = bus_addr + offset;
desc->used = 0;
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 0e08e665f715..053a23a7be94 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -18,7 +18,7 @@ EXPORT_SYMBOL_GPL(dmi_kobj);
* of and an antecedent to, SMBIOS, which stands for System
* Management BIOS. See further: http://www.dmtf.org/standards
*/
-static const char dmi_empty_string[] = " ";
+static const char dmi_empty_string[] = "";
static u32 dmi_ver __initdata;
static u32 dmi_len;
@@ -44,25 +44,21 @@ static int dmi_memdev_nr;
static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s)
{
const u8 *bp = ((u8 *) dm) + dm->length;
+ const u8 *nsp;
if (s) {
- s--;
- while (s > 0 && *bp) {
+ while (--s > 0 && *bp)
bp += strlen(bp) + 1;
- s--;
- }
-
- if (*bp != 0) {
- size_t len = strlen(bp)+1;
- size_t cmp_len = len > 8 ? 8 : len;
- if (!memcmp(bp, dmi_empty_string, cmp_len))
- return dmi_empty_string;
+ /* Strings containing only spaces are considered empty */
+ nsp = bp;
+ while (*nsp == ' ')
+ nsp++;
+ if (*nsp != '\0')
return bp;
- }
}
- return "";
+ return dmi_empty_string;
}
static const char * __init dmi_string(const struct dmi_header *dm, u8 s)
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index 6b5625e66119..88ceac091454 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -209,6 +209,7 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor)
return -ENOMEM;
filp->private_data = priv;
+ filp->f_mode |= FMODE_UNSIGNED_OFFSET;
priv->filp = filp;
priv->uid = current_euid();
priv->pid = get_pid(task_pid(current));
diff --git a/drivers/gpu/drm/exynos/regs-fimc.h b/drivers/gpu/drm/exynos/regs-fimc.h
index 30496134a3d0..d7cbe53c4c01 100644
--- a/drivers/gpu/drm/exynos/regs-fimc.h
+++ b/drivers/gpu/drm/exynos/regs-fimc.h
@@ -569,7 +569,7 @@
#define EXYNOS_CIIMGEFF_FIN_EMBOSSING (4 << 26)
#define EXYNOS_CIIMGEFF_FIN_SILHOUETTE (5 << 26)
#define EXYNOS_CIIMGEFF_FIN_MASK (7 << 26)
-#define EXYNOS_CIIMGEFF_PAT_CBCR_MASK ((0xff < 13) | (0xff < 0))
+#define EXYNOS_CIIMGEFF_PAT_CBCR_MASK ((0xff << 13) | (0xff << 0))
/* Real input DMA size register */
#define EXYNOS_CIREAL_ISIZE_AUTOLOAD_ENABLE (1 << 31)
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 7f39b8ad88ae..de6710fe3ff4 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -768,6 +768,14 @@ static const struct dmi_system_id intel_no_lvds[] = {
DMI_EXACT_MATCH(DMI_BOARD_NAME, "D525MW"),
},
},
+ {
+ .callback = intel_no_lvds_dmi_callback,
+ .ident = "Radiant P845",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Radiant Systems Inc"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "P845"),
+ },
+ },
{ } /* terminating entry */
};
diff --git a/drivers/gpu/drm/msm/dba_bridge.c b/drivers/gpu/drm/msm/dba_bridge.c
index 7887bda23df0..62294ddf8034 100644
--- a/drivers/gpu/drm/msm/dba_bridge.c
+++ b/drivers/gpu/drm/msm/dba_bridge.c
@@ -132,7 +132,9 @@ static void _dba_bridge_pre_enable(struct drm_bridge *bridge)
}
d_bridge = to_dba_bridge(bridge);
- if (d_bridge->ops.power_on)
+
+ /* Skip power_on calling when splash is enabled in bootloader. */
+ if ((d_bridge->ops.power_on) && (!d_bridge->cont_splash_enabled))
d_bridge->ops.power_on(d_bridge->dba_ctx, true, 0);
}
@@ -193,7 +195,8 @@ static void _dba_bridge_enable(struct drm_bridge *bridge)
video_cfg.scaninfo, video_cfg.ar, video_cfg.vic);
}
- if (d_bridge->ops.video_on) {
+ /* Skip video_on calling if splash is enabled in bootloader. */
+ if ((d_bridge->ops.video_on) && (!d_bridge->cont_splash_enabled)) {
rc = d_bridge->ops.video_on(d_bridge->dba_ctx, true,
&video_cfg, 0);
if (rc)
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
index 7a90c7be4e5c..b1cd666f8be4 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
@@ -1608,14 +1608,15 @@ int dsi_ctrl_host_init(struct dsi_ctrl *dsi_ctrl, bool cont_splash_enabled)
}
mutex_lock(&dsi_ctrl->ctrl_lock);
- rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_HOST_INIT, 0x1);
- if (rc) {
- pr_err("[DSI_%d] Controller state check failed, rc=%d\n",
- dsi_ctrl->index, rc);
- goto error;
- }
-
if (!cont_splash_enabled) {
+ rc = dsi_ctrl_check_state(
+ dsi_ctrl, DSI_CTRL_OP_HOST_INIT, 0x1);
+ if (rc) {
+ pr_err("[DSI_%d] Ctrl state check failed, rc=%d\n",
+ dsi_ctrl->index, rc);
+ goto error;
+ }
+
dsi_ctrl->hw.ops.setup_lane_map(&dsi_ctrl->hw,
&dsi_ctrl->host_config.lane_map);
@@ -1970,12 +1971,6 @@ error:
return rc;
}
-void dsi_ctrl_update_power_state(struct dsi_ctrl *dsi_ctrl,
- enum dsi_power_state state)
-{
- dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_POWER_STATE_CHANGE, state);
-}
-
/**
* dsi_ctrl_set_tpg_state() - enable/disable test pattern on the controller
* @dsi_ctrl: DSI controller handle.
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
index c0ba532011b5..c343c41eb8e1 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
@@ -405,16 +405,6 @@ int dsi_ctrl_set_power_state(struct dsi_ctrl *dsi_ctrl,
enum dsi_power_state state);
/**
- * dsi_ctrl_update_power_state() - update power state for dsi controller
- * @dsi_ctrl: DSI controller handle.
- * @state: Power state.
- *
- * Update power state for DSI controller.
- *
- */
-void dsi_ctrl_update_power_state(struct dsi_ctrl *dsi_ctrl,
- enum dsi_power_state state);
-/**
* dsi_ctrl_set_cmd_engine_state() - set command engine state
* @dsi_ctrl: DSI Controller handle.
* @state: Engine state.
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index c468a6f5caa2..7c9c3c7b4cca 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -236,6 +236,12 @@ static int dsi_display_phy_power_on(struct dsi_display *display)
int i;
struct dsi_display_ctrl *ctrl;
+ /* early return for splash enabled case */
+ if (display->cont_splash_enabled) {
+ pr_debug("skip phy power on\n");
+ return rc;
+ }
+
/* Sequence does not matter for split dsi usecases */
for (i = 0; i < display->ctrl_count; i++) {
@@ -292,6 +298,12 @@ static int dsi_display_ctrl_core_clk_on(struct dsi_display *display)
int i;
struct dsi_display_ctrl *m_ctrl, *ctrl;
+ /* early return for splash enabled case */
+ if (display->cont_splash_enabled) {
+ pr_debug("skip core clk on calling\n");
+ return rc;
+ }
+
/*
* In case of split DSI usecases, the clock for master controller should
* be enabled before the other controller. Master controller in the
@@ -334,6 +346,12 @@ static int dsi_display_ctrl_link_clk_on(struct dsi_display *display)
int i;
struct dsi_display_ctrl *m_ctrl, *ctrl;
+ /* early return for splash enabled case */
+ if (display->cont_splash_enabled) {
+ pr_debug("skip ctrl link clk on calling\n");
+ return rc;
+ }
+
/*
* In case of split DSI usecases, the clock for master controller should
* be enabled before the other controller. Master controller in the
@@ -1166,6 +1184,13 @@ static int dsi_display_parse_dt(struct dsi_display *display)
goto error;
}
+ /* Only read swap property in split case */
+ if (display->ctrl_count > 1) {
+ display->dsi_split_swap =
+ of_property_read_bool(display->pdev->dev.of_node,
+ "qcom,dsi-split-swap");
+ }
+
if (of_get_property(display->pdev->dev.of_node, "qcom,dsi-panel",
&size)) {
display->panel_count = size / sizeof(int);
@@ -2281,6 +2306,14 @@ int dsi_display_get_info(struct msm_display_info *info, void *disp)
for (i = 0; i < info->num_of_h_tiles; i++)
info->h_tile_instance[i] = display->ctrl[i].ctrl->index;
+ /*
+ * h_tile_instance[2] = {0, 1} means DSI0 left(master), DSI1 right
+ * h_tile_instance[2] = {1, 0} means DSI1 left(master), DSI0 right
+ * So in case of split case and swap property is set, swap two DSIs.
+ */
+ if (info->num_of_h_tiles > 1 && display->dsi_split_swap)
+ swap(info->h_tile_instance[0], info->h_tile_instance[1]);
+
info->is_connected = true;
info->width_mm = phy_props.panel_width_mm;
info->height_mm = phy_props.panel_height_mm;
@@ -2827,28 +2860,12 @@ int dsi_dsiplay_setup_splash_resource(struct dsi_display *display)
if (!ctrl)
return -EINVAL;
- dsi_pwr_enable_regulator(&ctrl->ctrl->pwr_info.host_pwr, true);
- dsi_pwr_enable_regulator(&ctrl->ctrl->pwr_info.digital, true);
- dsi_pwr_enable_regulator(&ctrl->phy->pwr_info.phy_pwr, true);
-
- ret = dsi_clk_enable_core_clks(&ctrl->ctrl->clk_info.core_clks,
- true);
- if (ret) {
- SDE_ERROR("failed to set core clk for dsi, ret = %d\n",
- ret);
- return -EINVAL;
- }
-
- ret = dsi_clk_enable_link_clks(&ctrl->ctrl->clk_info.link_clks,
- true);
+ ret = dsi_ctrl_set_power_state(ctrl->ctrl,
+ DSI_CTRL_POWER_LINK_CLK_ON);
if (ret) {
- SDE_ERROR("failed to set link clk for dsi, ret = %d\n",
- ret);
- return -EINVAL;
+ SDE_ERROR("calling dsi_ctrl_set_power_state failed\n");
+ return ret;
}
-
- dsi_ctrl_update_power_state(ctrl->ctrl,
- DSI_CTRL_POWER_LINK_CLK_ON);
}
return ret;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
index 3723f19fd0e7..25b2d0c1ec53 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
@@ -121,6 +121,8 @@ struct dsi_display_clk_info {
* @bridge: Pointer to DRM bridge object.
* @cmd_engine_refcount: Reference count enforcing single instance of cmd eng
* @root: Debugfs root directory
+ * @cont_splash_enabled: Early splash status.
+ * @dsi_split_swap: Swap dsi output in split mode.
*/
struct dsi_display {
struct platform_device *pdev;
@@ -160,6 +162,7 @@ struct dsi_display {
struct dentry *root;
bool cont_splash_enabled;
+ bool dsi_split_swap;
};
int dsi_display_dev_probe(struct platform_device *pdev);
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c
index a0f6b5c6a732..8a086dc68328 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.c
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016, 2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
@@ -78,7 +78,7 @@ enum {
SDE_CP_CRTC_DSPP_IGC,
SDE_CP_CRTC_DSPP_PCC,
SDE_CP_CRTC_DSPP_GC,
- SDE_CP_CRTC_DSPP_HUE,
+ SDE_CP_CRTC_DSPP_HSIC,
SDE_CP_CRTC_DSPP_SAT,
SDE_CP_CRTC_DSPP_VAL,
SDE_CP_CRTC_DSPP_CONT,
@@ -444,6 +444,7 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node,
struct sde_hw_cp_cfg hw_cfg;
struct sde_hw_mixer *hw_lm;
struct sde_hw_dspp *hw_dspp;
+ struct drm_msm_pa_hsic *hsic_cfg;
u32 num_mixers = sde_crtc->num_mixers;
int i = 0;
bool feature_enabled = false;
@@ -484,33 +485,28 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node,
}
hw_dspp->ops.setup_gc(hw_dspp, &hw_cfg);
break;
- case SDE_CP_CRTC_DSPP_HUE:
- if (!hw_dspp || !hw_dspp->ops.setup_hue) {
+ case SDE_CP_CRTC_DSPP_HSIC:
+ if (!hw_dspp || !hw_dspp->ops.setup_pa_hsic) {
ret = -EINVAL;
continue;
}
- hw_dspp->ops.setup_hue(hw_dspp, &hw_cfg);
- break;
- case SDE_CP_CRTC_DSPP_SAT:
- if (!hw_dspp || !hw_dspp->ops.setup_sat) {
- ret = -EINVAL;
- continue;
- }
- hw_dspp->ops.setup_sat(hw_dspp, &hw_cfg);
- break;
- case SDE_CP_CRTC_DSPP_VAL:
- if (!hw_dspp || !hw_dspp->ops.setup_val) {
- ret = -EINVAL;
- continue;
+ if (hw_cfg.payload && (hw_cfg.len ==
+ sizeof(struct drm_msm_pa_hsic))) {
+ /* hw_cfg is valid, check for feature flag */
+ hsic_cfg = (struct drm_msm_pa_hsic *)
+ hw_cfg.payload;
+ if ((hsic_cfg->flags &
+ PA_HSIC_LEFT_DISPLAY_ONLY) && (i > 0)) {
+ /* skip right side programming */
+ continue;
+ } else if ((hsic_cfg->flags &
+ PA_HSIC_RIGHT_DISPLAY_ONLY)
+ && (i == 0)) {
+ /* skip left side programming */
+ continue;
+ }
}
- hw_dspp->ops.setup_val(hw_dspp, &hw_cfg);
- break;
- case SDE_CP_CRTC_DSPP_CONT:
- if (!hw_dspp || !hw_dspp->ops.setup_cont) {
- ret = -EINVAL;
- continue;
- }
- hw_dspp->ops.setup_cont(hw_dspp, &hw_cfg);
+ hw_dspp->ops.setup_pa_hsic(hw_dspp, &hw_cfg);
break;
case SDE_CP_CRTC_DSPP_MEMCOLOR:
if (!hw_dspp || !hw_dspp->ops.setup_pa_memcolor)
@@ -907,9 +903,9 @@ static void dspp_hsic_install_property(struct drm_crtc *crtc)
switch (version) {
case 1:
snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
- "SDE_DSPP_HUE_V", version);
- sde_cp_crtc_install_range_property(crtc, feature_name,
- SDE_CP_CRTC_DSPP_HUE, 0, U32_MAX, 0);
+ "SDE_DSPP_PA_HSIC_V", version);
+ sde_cp_crtc_create_blob_property(crtc, feature_name,
+ SDE_CP_CRTC_DSPP_HSIC);
break;
default:
DRM_ERROR("version %d not supported\n", version);
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 7db98afad713..6433d3f3bca4 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -233,7 +233,7 @@ struct sde_connector {
* Returns: Pointer to associated private display structure
*/
#define sde_connector_get_display(C) \
- ((C) ? to_sde_connector((C))->display : 0)
+ ((C) ? to_sde_connector((C))->display : NULL)
/**
* sde_connector_get_panel - get sde connector's private panel pointer
@@ -241,7 +241,7 @@ struct sde_connector {
* Returns: Pointer to associated private display structure
*/
#define sde_connector_get_panel(C) \
- ((C) ? to_sde_connector((C))->panel : 0)
+ ((C) ? to_sde_connector((C))->panel : NULL)
/**
* sde_connector_get_encoder - get sde connector's private encoder pointer
@@ -249,7 +249,7 @@ struct sde_connector {
* Returns: Pointer to associated private encoder structure
*/
#define sde_connector_get_encoder(C) \
- ((C) ? to_sde_connector((C))->encoder : 0)
+ ((C) ? to_sde_connector((C))->encoder : NULL)
/**
* sde_connector_get_propinfo - get sde connector's property info pointer
@@ -257,7 +257,7 @@ struct sde_connector {
* Returns: Pointer to associated private property info structure
*/
#define sde_connector_get_propinfo(C) \
- ((C) ? &to_sde_connector((C))->property_info : 0)
+ ((C) ? &to_sde_connector((C))->property_info : NULL)
/**
* struct sde_connector_state - private connector status structure
@@ -300,7 +300,7 @@ struct sde_connector_state {
* Returns: Integer value of requested property
*/
#define sde_connector_get_property_values(S) \
- ((S) ? (to_sde_connector_state((S))->property_values) : 0)
+ ((S) ? (to_sde_connector_state((S))->property_values) : NULL)
/**
* sde_connector_get_out_fb - query out_fb value from sde connector state
@@ -308,7 +308,7 @@ struct sde_connector_state {
* Returns: Output fb associated with specified connector state
*/
#define sde_connector_get_out_fb(S) \
- ((S) ? to_sde_connector_state((S))->out_fb : 0)
+ ((S) ? to_sde_connector_state((S))->out_fb : NULL)
/**
* sde_connector_get_topology_name - helper accessor to retrieve topology_name
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 9e0bf09bff0a..95a25462cadc 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -91,6 +91,7 @@
enum {
HW_OFF,
HW_LEN,
+ HW_DISP,
HW_PROP_MAX,
};
@@ -201,6 +202,7 @@ enum {
MIXER_OFF,
MIXER_LEN,
MIXER_BLOCKS,
+ MIXER_DISP,
MIXER_PROP_MAX,
};
@@ -320,12 +322,15 @@ static struct sde_prop_type rgb_prop[] = {
static struct sde_prop_type ctl_prop[] = {
{HW_OFF, "qcom,sde-ctl-off", true, PROP_TYPE_U32_ARRAY},
{HW_LEN, "qcom,sde-ctl-size", false, PROP_TYPE_U32},
+ {HW_DISP, "qcom,sde-ctl-display-pref", false, PROP_TYPE_STRING_ARRAY},
};
static struct sde_prop_type mixer_prop[] = {
{MIXER_OFF, "qcom,sde-mixer-off", true, PROP_TYPE_U32_ARRAY},
{MIXER_LEN, "qcom,sde-mixer-size", false, PROP_TYPE_U32},
{MIXER_BLOCKS, "qcom,sde-mixer-blocks", false, PROP_TYPE_NODE},
+ {MIXER_DISP, "qcom,sde-mixer-display-pref", false,
+ PROP_TYPE_STRING_ARRAY},
};
static struct sde_prop_type mixer_blocks_prop[] = {
@@ -1102,6 +1107,7 @@ static int sde_ctl_parse_dt(struct device_node *np,
goto end;
for (i = 0; i < off_count; i++) {
+ const char *disp_pref = NULL;
ctl = sde_cfg->ctl + i;
ctl->base = PROP_VALUE_ACCESS(prop_value, HW_OFF, i);
ctl->len = PROP_VALUE_ACCESS(prop_value, HW_LEN, 0);
@@ -1109,6 +1115,16 @@ static int sde_ctl_parse_dt(struct device_node *np,
snprintf(ctl->name, SDE_HW_BLK_NAME_LEN, "ctl_%u",
ctl->id - CTL_0);
+ of_property_read_string_index(np,
+ ctl_prop[HW_DISP].prop_name, i, &disp_pref);
+ if (disp_pref) {
+ if (!strcmp(disp_pref, "primary"))
+ set_bit(SDE_CTL_PRIMARY_PREF, &ctl->features);
+ else if (!strcmp(disp_pref, "secondary"))
+ set_bit(SDE_CTL_SECONDARY_PREF, &ctl->features);
+ else if (!strcmp(disp_pref, "tertiary"))
+ set_bit(SDE_CTL_TERTIARY_PREF, &ctl->features);
+ }
if (i < MAX_SPLIT_DISPLAY_CTL)
set_bit(SDE_CTL_SPLIT_DISPLAY, &ctl->features);
if (i < MAX_PP_SPLIT_DISPLAY_CTL)
@@ -1187,6 +1203,7 @@ static int sde_mixer_parse_dt(struct device_node *np,
}
for (i = 0, pp_idx = 0, dspp_idx = 0; i < off_count; i++) {
+ const char *disp_pref = NULL;
mixer = sde_cfg->mixer + i;
sblk = kzalloc(sizeof(*sblk), GFP_KERNEL);
if (!sblk) {
@@ -1216,6 +1233,21 @@ static int sde_mixer_parse_dt(struct device_node *np,
if (sde_cfg->has_src_split)
set_bit(SDE_MIXER_SOURCESPLIT, &mixer->features);
+ of_property_read_string_index(np,
+ mixer_prop[MIXER_DISP].prop_name, i, &disp_pref);
+
+ if (disp_pref) {
+ if (!strcmp(disp_pref, "primary"))
+ set_bit(SDE_DISP_PRIMARY_PREF,
+ &mixer->features);
+ else if (!strcmp(disp_pref, "secondary"))
+ set_bit(SDE_DISP_SECONDARY_PREF,
+ &mixer->features);
+ else if (!strcmp(disp_pref, "tertiary"))
+ set_bit(SDE_DISP_TERTIARY_PREF,
+ &mixer->features);
+ }
+
if ((i < ROT_LM_OFFSET) || (i >= LINE_LM_OFFSET)) {
mixer->pingpong = pp_count > 0 ? pp_idx + PINGPONG_0
: PINGPONG_MAX;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index 81e6bfe6defe..0d09f05bb195 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -119,12 +119,18 @@ enum {
* @SDE_MIXER_LAYER Layer mixer layer blend configuration,
* @SDE_MIXER_SOURCESPLIT Layer mixer supports source-split configuration
* @SDE_MIXER_GC Gamma correction block
+ * @SDE_DISP_PRIMARY_PREF Primary display prefers this mixer
+ * @SDE_DISP_SECONDARY_PREF Secondary display prefers this mixer
+ * @SDE_DISP_TERTIARY_PREF Tertiary display prefers this mixer
* @SDE_MIXER_MAX maximum value
*/
enum {
SDE_MIXER_LAYER = 0x1,
SDE_MIXER_SOURCESPLIT,
SDE_MIXER_GC,
+ SDE_DISP_PRIMARY_PREF,
+ SDE_DISP_SECONDARY_PREF,
+ SDE_DISP_TERTIARY_PREF,
SDE_MIXER_MAX
};
@@ -180,11 +186,17 @@ enum {
* CTL sub-blocks
* @SDE_CTL_SPLIT_DISPLAY CTL supports video mode split display
* @SDE_CTL_PINGPONG_SPLIT CTL supports pingpong split
+ * @SDE_CTL_PRIMARY_PREF Primary display perfers this CTL
+ * @SDE_CTL_SECONDARY_PREF Secondary display perfers this CTL
+ * @SDE_CTL_TERTIARY_PREF Tertiary display perfers this CTL
* @SDE_CTL_MAX
*/
enum {
SDE_CTL_SPLIT_DISPLAY = 0x1,
SDE_CTL_PINGPONG_SPLIT,
+ SDE_CTL_PRIMARY_PREF,
+ SDE_CTL_SECONDARY_PREF,
+ SDE_CTL_TERTIARY_PREF,
SDE_CTL_MAX
};
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
index f1f66f37ba6a..6a8d9e0cf2e3 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016,2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -256,6 +256,47 @@ void sde_setup_pipe_pa_cont_v1_7(struct sde_hw_pipe *ctx, void *cfg)
__setup_pa_cont(&ctx->hw, &ctx->cap->sblk->hsic_blk, contrast, SSPP);
}
+void sde_setup_dspp_pa_hsic_v1_7(struct sde_hw_dspp *ctx, void *cfg)
+{
+ struct sde_hw_cp_cfg *hw_cfg = cfg;
+ struct drm_msm_pa_hsic *hsic_cfg;
+ u32 hue = 0;
+ u32 sat = 0;
+ u32 val = 0;
+ u32 cont = 0;
+
+ if (!ctx || !cfg) {
+ DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg);
+ return;
+ }
+
+ if (hw_cfg->payload &&
+ (hw_cfg->len != sizeof(struct drm_msm_pa_hsic))) {
+ DRM_ERROR("invalid size of payload len %d exp %zd\n",
+ hw_cfg->len, sizeof(struct drm_msm_pa_hsic));
+ return;
+ }
+
+ if (!hw_cfg->payload) {
+ DRM_DEBUG_DRIVER("disable pa hsic feature\n");
+ } else {
+ hsic_cfg = hw_cfg->payload;
+ if (hsic_cfg->flags & PA_HSIC_HUE_ENABLE)
+ hue = hsic_cfg->hue;
+ if (hsic_cfg->flags & PA_HSIC_SAT_ENABLE)
+ sat = hsic_cfg->saturation;
+ if (hsic_cfg->flags & PA_HSIC_VAL_ENABLE)
+ val = hsic_cfg->value;
+ if (hsic_cfg->flags & PA_HSIC_CONT_ENABLE)
+ cont = hsic_cfg->contrast;
+ }
+
+ __setup_pa_hue(&ctx->hw, &ctx->cap->sblk->hsic, hue, DSPP);
+ __setup_pa_sat(&ctx->hw, &ctx->cap->sblk->hsic, sat, DSPP);
+ __setup_pa_val(&ctx->hw, &ctx->cap->sblk->hsic, val, DSPP);
+ __setup_pa_cont(&ctx->hw, &ctx->cap->sblk->hsic, cont, DSPP);
+}
+
void sde_setup_pipe_pa_memcol_v1_7(struct sde_hw_pipe *ctx,
enum sde_memcolor_type type,
void *cfg)
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
index 0f9bc0e38322..185f6b548b65 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016,2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -45,6 +45,13 @@ void sde_setup_pipe_pa_val_v1_7(struct sde_hw_pipe *ctx, void *cfg);
void sde_setup_pipe_pa_cont_v1_7(struct sde_hw_pipe *ctx, void *cfg);
/**
+ * sde_setup_dspp_pa_hsic_v1_7 - setup DSPP hsic feature in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to hsic data
+ */
+void sde_setup_dspp_pa_hsic_v1_7(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
* sde_setup_pipe_pa_memcol_v1_7 - setup SSPP memory color in v1.7 hardware
* @ctx: Pointer to pipe context
* @type: Memory color type (Skin, sky, or foliage)
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
index 2fd879a0030d..4c5af0666d88 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -78,7 +78,8 @@ static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features)
case SDE_DSPP_HSIC:
if (c->cap->sblk->hsic.version ==
(SDE_COLOR_PROCESS_VER(0x1, 0x7)))
- c->ops.setup_hue = sde_setup_dspp_pa_hue_v1_7;
+ c->ops.setup_pa_hsic =
+ sde_setup_dspp_pa_hsic_v1_7;
break;
case SDE_DSPP_VLUT:
if (c->cap->sblk->vlut.version ==
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
index 6e6ad2f8d0e5..e1e8622dd11f 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -92,32 +92,11 @@ struct sde_hw_dspp_ops {
void (*setup_dither)(struct sde_hw_dspp *ctx, void *cfg);
/**
- * setup_hue - setup dspp PA hue
+ * setup_cont - setup dspp PA hsic
* @ctx: Pointer to dspp context
* @cfg: Pointer to configuration
*/
- void (*setup_hue)(struct sde_hw_dspp *ctx, void *cfg);
-
- /**
- * setup_sat - setup dspp PA saturation
- * @ctx: Pointer to dspp context
- * @cfg: Pointer to configuration
- */
- void (*setup_sat)(struct sde_hw_dspp *ctx, void *cfg);
-
- /**
- * setup_val - setup dspp PA value
- * @ctx: Pointer to dspp context
- * @cfg: Pointer to configuration
- */
- void (*setup_val)(struct sde_hw_dspp *ctx, void *cfg);
-
- /**
- * setup_cont - setup dspp PA contrast
- * @ctx: Pointer to dspp context
- * @cfg: Pointer to configuration
- */
- void (*setup_cont)(struct sde_hw_dspp *ctx, void *cfg);
+ void (*setup_pa_hsic)(struct sde_hw_dspp *dspp, void *cfg);
/**
* setup_vlut - setup dspp PA VLUT
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 86a5c23b5258..95ab14ffc3ac 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -1128,6 +1128,7 @@ static int _sde_kms_mmu_init(struct sde_kms *sde_kms)
{
struct msm_mmu *mmu;
int i, ret;
+ int data = 0;
for (i = 0; i < MSM_SMMU_DOMAIN_MAX; i++) {
struct msm_gem_address_space *aspace;
@@ -1188,6 +1189,20 @@ static int _sde_kms_mmu_init(struct sde_kms *sde_kms)
msm_gem_address_space_put(aspace);
goto fail;
}
+
+ /*
+ * Enable stage 1 smmu after user has finished early
+ * mapping of splash memory.
+ */
+ ret = mmu->funcs->set_property(mmu,
+ DOMAIN_ATTR_EARLY_MAP,
+ &data);
+ if (ret) {
+ SDE_ERROR("failed to set map att(%d): %d\n",
+ data, ret);
+ msm_gem_address_space_put(aspace);
+ goto fail;
+ }
}
}
diff --git a/drivers/gpu/drm/msm/sde/sde_rm.c b/drivers/gpu/drm/msm/sde/sde_rm.c
index 6055dc861c72..384b6f50979c 100644
--- a/drivers/gpu/drm/msm/sde/sde_rm.c
+++ b/drivers/gpu/drm/msm/sde/sde_rm.c
@@ -25,6 +25,8 @@
#include "sde_connector.h"
#include "sde_hw_sspp.h"
#include "sde_splash.h"
+#include "dsi_display.h"
+#include "sde_hdmi.h"
#define RESERVED_BY_OTHER(h, r) \
((h)->rsvp && ((h)->rsvp->enc_id != (r)->enc_id))
@@ -41,6 +43,7 @@
* @dspp: Whether the user requires a DSPP
* @num_lm: Number of layer mixers needed in the use case
* @hw_res: Hardware resources required as reported by the encoders
+ * @disp_id: Current display ID, lm/ctl may have prefer display
*/
struct sde_rm_requirements {
enum sde_rm_topology_name top_name;
@@ -49,6 +52,7 @@ struct sde_rm_requirements {
int num_ctl;
bool needs_split_display;
struct sde_encoder_hw_resources hw_res;
+ uint32_t disp_id;
};
/**
@@ -565,7 +569,9 @@ static bool _sde_rm_check_lm_and_get_connected_blks(
struct sde_lm_cfg *lm_cfg = (struct sde_lm_cfg *)lm->catalog;
struct sde_pingpong_cfg *pp_cfg;
struct sde_rm_hw_iter iter;
-
+ unsigned long caps = ((struct sde_lm_cfg *)lm->catalog)->features;
+ unsigned int preferred_disp_id = 0;
+ bool preferred_disp_match = false;
*dspp = NULL;
*pp = NULL;
@@ -584,9 +590,21 @@ static bool _sde_rm_check_lm_and_get_connected_blks(
}
}
+ /* bypass rest of the checks if preferred display is found */
+ if (BIT(SDE_DISP_PRIMARY_PREF) & caps)
+ preferred_disp_id = 1;
+ else if (BIT(SDE_DISP_SECONDARY_PREF) & caps)
+ preferred_disp_id = 2;
+ else if (BIT(SDE_DISP_TERTIARY_PREF) & caps)
+ preferred_disp_id = 3;
+
+ if (reqs->disp_id == preferred_disp_id)
+ preferred_disp_match = true;
+
/* Matches user requirements? */
- if ((RM_RQ_DSPP(reqs) && lm_cfg->dspp == DSPP_MAX) ||
- (!RM_RQ_DSPP(reqs) && lm_cfg->dspp != DSPP_MAX)) {
+ if (!preferred_disp_match &&
+ ((RM_RQ_DSPP(reqs) && lm_cfg->dspp == DSPP_MAX) ||
+ (!RM_RQ_DSPP(reqs) && lm_cfg->dspp != DSPP_MAX))) {
SDE_DEBUG("dspp req mismatch lm %d reqdspp %d, lm->dspp %d\n",
lm_cfg->id, (bool)(RM_RQ_DSPP(reqs)),
lm_cfg->dspp);
@@ -769,6 +787,7 @@ static int _sde_rm_reserve_ctls(
while (_sde_rm_get_hw_locked(rm, &iter)) {
unsigned long caps;
bool has_split_display, has_ppsplit;
+ bool ctl_found = false;
if (RESERVED_BY_OTHER(iter.blk, rsvp))
continue;
@@ -780,11 +799,33 @@ static int _sde_rm_reserve_ctls(
SDE_DEBUG("ctl %d caps 0x%lX\n", iter.blk->id, caps);
/* early return when finding the matched ctl id */
- if ((prefer_ctl_id > 0) && (iter.blk->id == prefer_ctl_id)) {
- ctls[i] = iter.blk;
+ if ((prefer_ctl_id > 0) && (iter.blk->id == prefer_ctl_id))
+ ctl_found = true;
+ switch (reqs->disp_id) {
+ case 1:
+ if (BIT(SDE_CTL_PRIMARY_PREF) & caps)
+ ctl_found = true;
+ break;
+ case 2:
+ if (BIT(SDE_CTL_SECONDARY_PREF) & caps)
+ ctl_found = true;
+ break;
+ case 3:
+ if (BIT(SDE_CTL_TERTIARY_PREF) & caps)
+ ctl_found = true;
+ break;
+ default:
+ break;
+ }
+
+ if (ctl_found) {
+ ctls[i] = iter.blk;
+ prefer_ctl_id = 0;
if (++i == reqs->num_ctl)
break;
+ else
+ continue;
}
if (reqs->needs_split_display != has_split_display)
@@ -933,6 +974,30 @@ static int _sde_rm_make_next_rsvp(
struct sde_rm_requirements *reqs)
{
int ret;
+ struct sde_connector *sde_conn =
+ to_sde_connector(conn_state->connector);
+ struct dsi_display *dsi;
+ struct sde_hdmi *hdmi;
+ const char *display_type;
+
+ if (sde_conn->connector_type == DRM_MODE_CONNECTOR_DSI) {
+ dsi = (struct dsi_display *)sde_conn->display;
+ display_type = dsi->display_type;
+ } else if (sde_conn->connector_type == DRM_MODE_CONNECTOR_HDMIA) {
+ hdmi = (struct sde_hdmi *)sde_conn->display;
+ display_type = hdmi->display_type;
+ } else {
+ /* virtual display does not have display type */
+ display_type = "none";
+ }
+ if (!strcmp("primary", display_type))
+ reqs->disp_id = 1;
+ else if (!strcmp("secondary", display_type))
+ reqs->disp_id = 2;
+ else if (!strcmp("tertiary", display_type))
+ reqs->disp_id = 3;
+ else /* No display type set in dtsi */
+ reqs->disp_id = 0;
/* Create reservation info, tag reserved blocks with it as we go */
rsvp->seq = ++rm->rsvp_next_seq;
diff --git a/drivers/gpu/drm/msm/sde/sde_splash.c b/drivers/gpu/drm/msm/sde/sde_splash.c
index 9c3964e99c1f..e5231c4b1aae 100644
--- a/drivers/gpu/drm/msm/sde/sde_splash.c
+++ b/drivers/gpu/drm/msm/sde/sde_splash.c
@@ -275,6 +275,25 @@ static void _sde_splash_destroy_splash_node(struct sde_splash_info *sinfo)
sinfo->splash_mem_size = NULL;
}
+static void _sde_splash_update_display_splash_status(struct sde_kms *sde_kms)
+{
+ struct dsi_display *dsi_display;
+ struct sde_hdmi *sde_hdmi;
+ int i = 0;
+
+ for (i = 0; i < sde_kms->dsi_display_count; i++) {
+ dsi_display = (struct dsi_display *)sde_kms->dsi_displays[i];
+
+ dsi_display->cont_splash_enabled = false;
+ }
+
+ for (i = 0; i < sde_kms->hdmi_display_count; i++) {
+ sde_hdmi = (struct sde_hdmi *)sde_kms->hdmi_displays[i];
+
+ sde_hdmi->cont_splash_enabled = false;
+ }
+}
+
static void _sde_splash_sent_pipe_update_uevent(struct sde_kms *sde_kms)
{
char *event_string;
@@ -737,6 +756,7 @@ bool sde_splash_get_lk_complete_status(struct msm_kms *kms)
intr = sde_kms->hw_intr;
if (sde_kms->splash_info.handoff &&
+ !sde_kms->splash_info.display_splash_enabled &&
SDE_LK_EXIT_VALUE == SDE_REG_READ(&intr->hw,
SCRATCH_REGISTER_1)) {
SDE_DEBUG("LK totoally exits\n");
@@ -816,6 +836,9 @@ int sde_splash_free_resource(struct msm_kms *kms,
/* send uevent to notify user to recycle resource */
_sde_splash_sent_pipe_update_uevent(sde_kms);
+ /* set display's splash status to false after handoff is done */
+ _sde_splash_update_display_splash_status(sde_kms);
+
/* Finally mark handoff flag to false to say
* handoff is complete.
*/
@@ -856,17 +879,13 @@ int sde_splash_free_resource(struct msm_kms *kms,
}
/*
- * In below function, it will
- * 1. Notify LK to stop display splash.
- * 2. Set DOMAIN_ATTR_EARLY_MAP to 1 to enable stage 1 translation in iommu.
+ * Below function will notify LK to stop display splash.
*/
int sde_splash_lk_stop_splash(struct msm_kms *kms,
struct drm_atomic_state *state)
{
struct sde_splash_info *sinfo;
- struct msm_mmu *mmu;
struct sde_kms *sde_kms = to_sde_kms(kms);
- int ret;
sinfo = &sde_kms->splash_info;
@@ -886,26 +905,5 @@ int sde_splash_lk_stop_splash(struct msm_kms *kms,
}
mutex_unlock(&sde_splash_lock);
- if (!sde_kms->aspace[0] || !sde_kms->aspace[0]->mmu) {
- /* We do not return fault value here, to ensure
- * flag "lk_is_exited" is set.
- */
- SDE_ERROR("invalid mmu\n");
- WARN_ON(1);
- } else {
- mmu = sde_kms->aspace[0]->mmu;
- /* After LK has exited, set early domain map attribute
- * to 1 to enable stage 1 translation in iommu driver.
- */
- if (mmu->funcs && mmu->funcs->set_property) {
- ret = mmu->funcs->set_property(mmu,
- DOMAIN_ATTR_EARLY_MAP,
- &sinfo->display_splash_enabled);
-
- if (ret)
- SDE_ERROR("set_property failed\n");
- }
- }
-
return 0;
}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
index d908321b94ce..e6d07680eb05 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -67,7 +67,6 @@ static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj,
* VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap().
*/
vma->vm_flags &= ~VM_PFNMAP;
- vma->vm_pgoff = 0;
ret = dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
obj->size, &rk_obj->dma_attrs);
@@ -99,6 +98,12 @@ int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma)
if (ret)
return ret;
+ /*
+ * Set vm_pgoff (used as a fake buffer offset by DRM) to 0 and map the
+ * whole buffer from the start.
+ */
+ vma->vm_pgoff = 0;
+
obj = vma->vm_private_data;
return rockchip_drm_gem_object_mmap(obj, vma);
diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
index b4de18e65db8..6296e9f270ca 100644
--- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c
+++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
@@ -208,6 +208,9 @@ static int virtio_gpu_getparam_ioctl(struct drm_device *dev, void *data,
case VIRTGPU_PARAM_3D_FEATURES:
value = vgdev->has_virgl_3d == true ? 1 : 0;
break;
+ case VIRTGPU_PARAM_CAPSET_QUERY_FIX:
+ value = 1;
+ break;
default:
return -EINVAL;
}
@@ -483,7 +486,7 @@ static int virtio_gpu_get_caps_ioctl(struct drm_device *dev,
{
struct virtio_gpu_device *vgdev = dev->dev_private;
struct drm_virtgpu_get_caps *args = data;
- int size;
+ unsigned size, host_caps_size;
int i;
int found_valid = -1;
int ret;
@@ -492,6 +495,10 @@ static int virtio_gpu_get_caps_ioctl(struct drm_device *dev,
if (vgdev->num_capsets == 0)
return -ENOSYS;
+ /* don't allow userspace to pass 0 */
+ if (args->size == 0)
+ return -EINVAL;
+
spin_lock(&vgdev->display_info_lock);
for (i = 0; i < vgdev->num_capsets; i++) {
if (vgdev->capsets[i].id == args->cap_set_id) {
@@ -507,11 +514,9 @@ static int virtio_gpu_get_caps_ioctl(struct drm_device *dev,
return -EINVAL;
}
- size = vgdev->capsets[found_valid].max_size;
- if (args->size > size) {
- spin_unlock(&vgdev->display_info_lock);
- return -EINVAL;
- }
+ host_caps_size = vgdev->capsets[found_valid].max_size;
+ /* only copy to user the minimum of the host caps size or the guest caps size */
+ size = min(args->size, host_caps_size);
list_for_each_entry(cache_ent, &vgdev->cap_cache, head) {
if (cache_ent->id == args->cap_set_id &&
diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c
index 966047711fbf..1073c0d1fae5 100644
--- a/drivers/hid/hid-roccat-kovaplus.c
+++ b/drivers/hid/hid-roccat-kovaplus.c
@@ -37,6 +37,8 @@ static uint kovaplus_convert_event_cpi(uint value)
static void kovaplus_profile_activated(struct kovaplus_device *kovaplus,
uint new_profile_index)
{
+ if (new_profile_index >= ARRAY_SIZE(kovaplus->profile_settings))
+ return;
kovaplus->actual_profile = new_profile_index;
kovaplus->actual_cpi = kovaplus->profile_settings[new_profile_index].cpi_startup_level;
kovaplus->actual_x_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_x;
diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c
index d7ebdf8651f5..d3c6115f16b9 100644
--- a/drivers/hwmon/nct6775.c
+++ b/drivers/hwmon/nct6775.c
@@ -1390,7 +1390,7 @@ static void nct6775_update_pwm(struct device *dev)
duty_is_dc = data->REG_PWM_MODE[i] &&
(nct6775_read_value(data, data->REG_PWM_MODE[i])
& data->PWM_MODE_MASK[i]);
- data->pwm_mode[i] = duty_is_dc;
+ data->pwm_mode[i] = !duty_is_dc;
fanmodecfg = nct6775_read_value(data, data->REG_FAN_MODE[i]);
for (j = 0; j < ARRAY_SIZE(data->REG_PWM); j++) {
@@ -2267,7 +2267,7 @@ show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- return sprintf(buf, "%d\n", !data->pwm_mode[sattr->index]);
+ return sprintf(buf, "%d\n", data->pwm_mode[sattr->index]);
}
static ssize_t
@@ -2288,9 +2288,9 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
if (val > 1)
return -EINVAL;
- /* Setting DC mode is not supported for all chips/channels */
+ /* Setting DC mode (0) is not supported for all chips/channels */
if (data->REG_PWM_MODE[nr] == 0) {
- if (val)
+ if (!val)
return -EINVAL;
return count;
}
@@ -2299,7 +2299,7 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
data->pwm_mode[nr] = val;
reg = nct6775_read_value(data, data->REG_PWM_MODE[nr]);
reg &= ~data->PWM_MODE_MASK[nr];
- if (val)
+ if (!val)
reg |= data->PWM_MODE_MASK[nr];
nct6775_write_value(data, data->REG_PWM_MODE[nr], reg);
mutex_unlock(&data->update_lock);
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index 18477dd1e243..c3f4c9ef6705 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -141,7 +141,7 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
const struct adm1275_data *data = to_adm1275_data(info);
int ret = 0;
- if (page)
+ if (page > 0)
return -ENXIO;
switch (reg) {
@@ -218,7 +218,7 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
const struct adm1275_data *data = to_adm1275_data(info);
int ret;
- if (page)
+ if (page > 0)
return -ENXIO;
switch (reg) {
diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c
index dd4883a19045..e951f9b87abb 100644
--- a/drivers/hwmon/pmbus/max8688.c
+++ b/drivers/hwmon/pmbus/max8688.c
@@ -45,7 +45,7 @@ static int max8688_read_word_data(struct i2c_client *client, int page, int reg)
{
int ret;
- if (page)
+ if (page > 0)
return -ENXIO;
switch (reg) {
diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c
index cb07713aceda..129fcf1c06d9 100644
--- a/drivers/hwtracing/stm/core.c
+++ b/drivers/hwtracing/stm/core.c
@@ -26,6 +26,7 @@
#include <linux/stm.h>
#include <linux/fs.h>
#include <linux/mm.h>
+#include <linux/vmalloc.h>
#include "stm.h"
#include <uapi/linux/stm.h>
@@ -650,7 +651,7 @@ static void stm_device_release(struct device *dev)
{
struct stm_device *stm = to_stm_device(dev);
- kfree(stm);
+ vfree(stm);
}
int stm_register_device(struct device *parent, struct stm_data *stm_data,
@@ -667,7 +668,7 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data,
return -EINVAL;
nmasters = stm_data->sw_end - stm_data->sw_start;
- stm = kzalloc(sizeof(*stm) + nmasters * sizeof(void *), GFP_KERNEL);
+ stm = vzalloc(sizeof(*stm) + nmasters * sizeof(void *));
if (!stm)
return -ENOMEM;
@@ -709,7 +710,7 @@ err_device:
/* matches device_initialize() above */
put_device(&stm->dev);
err_free:
- kfree(stm);
+ vfree(stm);
return err;
}
diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c
index 67261bc10e80..d72953f2df23 100644
--- a/drivers/i2c/busses/i2c-msm-v2.c
+++ b/drivers/i2c/busses/i2c-msm-v2.c
@@ -1274,10 +1274,10 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl)
tx->dir,
(SPS_IOVEC_FLAG_EOT |
SPS_IOVEC_FLAG_NWD));
- if (dma_desc_tx < 0) {
+ if (IS_ERR_OR_NULL(dma_desc_tx)) {
dev_err(ctrl->dev, "error dmaengine_prep_slave_sg tx:%ld\n",
PTR_ERR(dma_desc_tx));
- ret = PTR_ERR(dma_desc_tx);
+ ret = dma_desc_tx ? PTR_ERR(dma_desc_tx) : -ENOMEM;
goto dma_xfer_end;
}
@@ -1292,11 +1292,11 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl)
sg_rx_itr - sg_rx, rx->dir,
(SPS_IOVEC_FLAG_EOT |
SPS_IOVEC_FLAG_NWD));
- if (dma_desc_rx < 0) {
+ if (IS_ERR_OR_NULL(dma_desc_rx)) {
dev_err(ctrl->dev,
"error dmaengine_prep_slave_sg rx:%ld\n",
PTR_ERR(dma_desc_rx));
- ret = PTR_ERR(dma_desc_rx);
+ ret = dma_desc_rx ? PTR_ERR(dma_desc_rx) : -ENOMEM;
goto dma_xfer_end;
}
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index 43207f52e5a3..332d32c53c41 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -856,12 +856,16 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
*/
if (of_device_is_compatible(np, "marvell,mv78230-i2c")) {
drv_data->offload_enabled = true;
- drv_data->errata_delay = true;
+ /* The delay is only needed in standard mode (100kHz) */
+ if (bus_freq <= 100000)
+ drv_data->errata_delay = true;
}
if (of_device_is_compatible(np, "marvell,mv78230-a0-i2c")) {
drv_data->offload_enabled = false;
- drv_data->errata_delay = true;
+ /* The delay is only needed in standard mode (100kHz) */
+ if (bus_freq <= 100000)
+ drv_data->errata_delay = true;
}
if (of_device_is_compatible(np, "allwinner,sun6i-a31-i2c"))
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index 599c0d7bd906..6f89484765e3 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -33,7 +33,6 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
-#include <linux/spinlock.h>
/* register offsets */
#define ICSCR 0x00 /* slave ctrl */
@@ -84,6 +83,7 @@
#define RCAR_BUS_PHASE_START (MDBS | MIE | ESG)
#define RCAR_BUS_PHASE_DATA (MDBS | MIE)
+#define RCAR_BUS_MASK_DATA (~(ESG | FSB) & 0xFF)
#define RCAR_BUS_PHASE_STOP (MDBS | MIE | FSB)
#define RCAR_IRQ_SEND (MNR | MAL | MST | MAT | MDE)
@@ -94,7 +94,6 @@
#define RCAR_IRQ_ACK_RECV (~(MAT | MDR) & 0xFF)
#define ID_LAST_MSG (1 << 0)
-#define ID_IOERROR (1 << 1)
#define ID_DONE (1 << 2)
#define ID_ARBLOST (1 << 3)
#define ID_NACK (1 << 4)
@@ -108,10 +107,10 @@ enum rcar_i2c_type {
struct rcar_i2c_priv {
void __iomem *io;
struct i2c_adapter adap;
- struct i2c_msg *msg;
+ struct i2c_msg *msg;
+ int msgs_left;
struct clk *clk;
- spinlock_t lock;
wait_queue_head_t wait;
int pos;
@@ -144,9 +143,10 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv)
{
/* reset master mode */
rcar_i2c_write(priv, ICMIER, 0);
- rcar_i2c_write(priv, ICMCR, 0);
+ rcar_i2c_write(priv, ICMCR, MDBS);
rcar_i2c_write(priv, ICMSR, 0);
- rcar_i2c_write(priv, ICMAR, 0);
+ /* start clock */
+ rcar_i2c_write(priv, ICCCR, priv->icccr);
}
static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
@@ -257,16 +257,28 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv)
{
int read = !!rcar_i2c_is_recv(priv);
+ priv->pos = 0;
+ priv->flags = 0;
+ if (priv->msgs_left == 1)
+ rcar_i2c_flags_set(priv, ID_LAST_MSG);
+
rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | read);
rcar_i2c_write(priv, ICMSR, 0);
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
}
+static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv)
+{
+ priv->msg++;
+ priv->msgs_left--;
+ rcar_i2c_prepare_msg(priv);
+}
+
/*
* interrupt functions
*/
-static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
+static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
{
struct i2c_msg *msg = priv->msg;
@@ -276,14 +288,7 @@ static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
* Do nothing
*/
if (!(msr & MDE))
- return 0;
-
- /*
- * If address transfer phase finished,
- * goto data phase.
- */
- if (msr & MAT)
- rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_DATA);
+ return;
if (priv->pos < msg->len) {
/*
@@ -305,29 +310,23 @@ static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
* [ICRXTX] -> [SHIFT] -> [I2C bus]
*/
- if (priv->flags & ID_LAST_MSG)
+ if (priv->flags & ID_LAST_MSG) {
/*
* If current msg is the _LAST_ msg,
* prepare stop condition here.
* ID_DONE will be set on STOP irq.
*/
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
- else
- /*
- * If current msg is _NOT_ last msg,
- * it doesn't call stop phase.
- * thus, there is no STOP irq.
- * return ID_DONE here.
- */
- return ID_DONE;
+ } else {
+ rcar_i2c_next_msg(priv);
+ return;
+ }
}
rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_SEND);
-
- return 0;
}
-static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
+static void rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
{
struct i2c_msg *msg = priv->msg;
@@ -337,14 +336,10 @@ static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
* Do nothing
*/
if (!(msr & MDR))
- return 0;
+ return;
if (msr & MAT) {
- /*
- * Address transfer phase finished,
- * but, there is no data at this point.
- * Do nothing.
- */
+ /* Address transfer phase finished, but no data at this point. */
} else if (priv->pos < msg->len) {
/*
* get received data
@@ -360,12 +355,11 @@ static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
*/
if (priv->pos + 1 >= msg->len)
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
- else
- rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_DATA);
- rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_RECV);
-
- return 0;
+ if (priv->pos == msg->len && !(priv->flags & ID_LAST_MSG))
+ rcar_i2c_next_msg(priv);
+ else
+ rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_RECV);
}
static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
@@ -426,22 +420,21 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
{
struct rcar_i2c_priv *priv = ptr;
- irqreturn_t result = IRQ_HANDLED;
- u32 msr;
-
- /*-------------- spin lock -----------------*/
- spin_lock(&priv->lock);
+ u32 msr, val;
- if (rcar_i2c_slave_irq(priv))
- goto exit;
+ /* Clear START or STOP as soon as we can */
+ val = rcar_i2c_read(priv, ICMCR);
+ rcar_i2c_write(priv, ICMCR, val & RCAR_BUS_MASK_DATA);
msr = rcar_i2c_read(priv, ICMSR);
/* Only handle interrupts that are currently enabled */
msr &= rcar_i2c_read(priv, ICMIER);
if (!msr) {
- result = IRQ_NONE;
- goto exit;
+ if (rcar_i2c_slave_irq(priv))
+ return IRQ_HANDLED;
+
+ return IRQ_NONE;
}
/* Arbitration lost */
@@ -452,8 +445,7 @@ static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
/* Nack */
if (msr & MNR) {
- /* go to stop phase */
- rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
+ /* HW automatically sends STOP after received NACK */
rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP);
rcar_i2c_flags_set(priv, ID_NACK);
goto out;
@@ -461,14 +453,15 @@ static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
/* Stop */
if (msr & MST) {
+ priv->msgs_left--; /* The last message also made it */
rcar_i2c_flags_set(priv, ID_DONE);
goto out;
}
if (rcar_i2c_is_recv(priv))
- rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr));
+ rcar_i2c_irq_recv(priv, msr);
else
- rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr));
+ rcar_i2c_irq_send(priv, msr);
out:
if (rcar_i2c_flags_has(priv, ID_DONE)) {
@@ -477,11 +470,7 @@ out:
wake_up(&priv->wait);
}
-exit:
- spin_unlock(&priv->lock);
- /*-------------- spin unlock -----------------*/
-
- return result;
+ return IRQ_HANDLED;
}
static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
@@ -490,22 +479,11 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
{
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
struct device *dev = rcar_i2c_priv_to_dev(priv);
- unsigned long flags;
int i, ret;
- long timeout;
+ long time_left;
pm_runtime_get_sync(dev);
- /*-------------- spin lock -----------------*/
- spin_lock_irqsave(&priv->lock, flags);
-
- rcar_i2c_init(priv);
- /* start clock */
- rcar_i2c_write(priv, ICCCR, priv->icccr);
-
- spin_unlock_irqrestore(&priv->lock, flags);
- /*-------------- spin unlock -----------------*/
-
ret = rcar_i2c_bus_barrier(priv);
if (ret < 0)
goto out;
@@ -514,48 +492,28 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
/* This HW can't send STOP after address phase */
if (msgs[i].len == 0) {
ret = -EOPNOTSUPP;
- break;
- }
-
- /*-------------- spin lock -----------------*/
- spin_lock_irqsave(&priv->lock, flags);
-
- /* init each data */
- priv->msg = &msgs[i];
- priv->pos = 0;
- priv->flags = 0;
- if (i == num - 1)
- rcar_i2c_flags_set(priv, ID_LAST_MSG);
-
- rcar_i2c_prepare_msg(priv);
-
- spin_unlock_irqrestore(&priv->lock, flags);
- /*-------------- spin unlock -----------------*/
-
- timeout = wait_event_timeout(priv->wait,
- rcar_i2c_flags_has(priv, ID_DONE),
- adap->timeout);
- if (!timeout) {
- ret = -ETIMEDOUT;
- break;
- }
-
- if (rcar_i2c_flags_has(priv, ID_NACK)) {
- ret = -ENXIO;
- break;
- }
-
- if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
- ret = -EAGAIN;
- break;
- }
-
- if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
- ret = -EIO;
- break;
+ goto out;
}
+ }
- ret = i + 1; /* The number of transfer */
+ /* init data */
+ priv->msg = msgs;
+ priv->msgs_left = num;
+
+ rcar_i2c_prepare_msg(priv);
+
+ time_left = wait_event_timeout(priv->wait,
+ rcar_i2c_flags_has(priv, ID_DONE),
+ num * adap->timeout);
+ if (!time_left) {
+ rcar_i2c_init(priv);
+ ret = -ETIMEDOUT;
+ } else if (rcar_i2c_flags_has(priv, ID_NACK)) {
+ ret = -ENXIO;
+ } else if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
+ ret = -EAGAIN;
+ } else {
+ ret = num - priv->msgs_left; /* The number of transfer */
}
out:
pm_runtime_put(dev);
@@ -650,23 +608,27 @@ static int rcar_i2c_probe(struct platform_device *pdev)
return PTR_ERR(priv->clk);
}
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->io = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->io))
+ return PTR_ERR(priv->io);
+
bus_speed = 100000; /* default 100 kHz */
of_property_read_u32(dev->of_node, "clock-frequency", &bus_speed);
priv->devtype = (enum rcar_i2c_type)of_match_device(rcar_i2c_dt_ids, dev)->data;
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
if (ret < 0)
- return ret;
+ goto out_pm_put;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->io = devm_ioremap_resource(dev, res);
- if (IS_ERR(priv->io))
- return PTR_ERR(priv->io);
+ rcar_i2c_init(priv);
+ pm_runtime_put(dev);
irq = platform_get_irq(pdev, 0);
init_waitqueue_head(&priv->wait);
- spin_lock_init(&priv->lock);
adap = &priv->adap;
adap->nr = pdev->id;
@@ -682,22 +644,26 @@ static int rcar_i2c_probe(struct platform_device *pdev)
dev_name(dev), priv);
if (ret < 0) {
dev_err(dev, "cannot get irq %d\n", irq);
- return ret;
+ goto out_pm_disable;
}
- pm_runtime_enable(dev);
platform_set_drvdata(pdev, priv);
ret = i2c_add_numbered_adapter(adap);
if (ret < 0) {
dev_err(dev, "reg adap failed: %d\n", ret);
- pm_runtime_disable(dev);
- return ret;
+ goto out_pm_disable;
}
dev_info(dev, "probed\n");
return 0;
+
+ out_pm_put:
+ pm_runtime_put(dev);
+ out_pm_disable:
+ pm_runtime_disable(dev);
+ return ret;
}
static int rcar_i2c_remove(struct platform_device *pdev)
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index ef907fd5ba98..08a21d635d0d 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -1593,6 +1593,8 @@ static int idecd_open(struct block_device *bdev, fmode_t mode)
struct cdrom_info *info;
int rc = -ENXIO;
+ check_disk_change(bdev);
+
mutex_lock(&ide_cd_mutex);
info = ide_cd_get(bdev->bd_disk);
if (!info)
diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c
index c5b999f0c519..7ef9b13262a8 100644
--- a/drivers/iio/buffer/kfifo_buf.c
+++ b/drivers/iio/buffer/kfifo_buf.c
@@ -24,6 +24,13 @@ static inline int __iio_allocate_kfifo(struct iio_kfifo *buf,
if ((length == 0) || (bytes_per_datum == 0))
return -EINVAL;
+ /*
+ * Make sure we don't overflow an unsigned int after kfifo rounds up to
+ * the next power of 2.
+ */
+ if (roundup_pow_of_two(length) > UINT_MAX / bytes_per_datum)
+ return -EINVAL;
+
return __kfifo_alloc((struct __kfifo *)&buf->kf, length,
bytes_per_datum, GFP_KERNEL);
}
diff --git a/drivers/iio/imu/inv_mpu/Kconfig b/drivers/iio/imu/inv_mpu/Kconfig
new file mode 100644
index 000000000000..7505454f8763
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/Kconfig
@@ -0,0 +1,63 @@
+#
+# inv-mpu-iio driver for Invensense MPU devices
+#
+
+config INV_MPU_IIO
+ tristate
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ select IIO_TRIGGER
+ select CRC32
+
+choice
+ prompt "Chip name"
+ depends on INV_MPU_IIO
+
+config INV_MPU_IIO_ICM20648
+ bool "ICM20648/ICM20948"
+ help
+ Select this if you are using a ICM20648/ICM20948 chip.
+
+config INV_MPU_IIO_ICM20608D
+ bool "ICM20608D/ICM20609/ICM20689"
+ help
+ Select this if you are using a ICM20608D/ICM20609/ICM20689 chip.
+
+config INV_MPU_IIO_ICM20602
+ bool "ICM20602"
+ help
+ Select this if you are using a ICM20602 chip.
+
+config INV_MPU_IIO_ICM20690
+ bool "ICM20690"
+ help
+ Select this if you are using a ICM20690 chip.
+
+config INV_MPU_IIO_IAM20680
+ bool "IAM20680"
+ help
+ Select this if you are using a IAM20680 chip.
+
+endchoice
+
+config INV_MPU_IIO_I2C
+ tristate "Invensense ICM20xxx devices (I2C)"
+ depends on I2C && !INV_MPU6050_IIO
+ select INV_MPU_IIO
+ default n
+ help
+ This driver supports Invensense ICM20xxx devices over I2C.
+ This driver can be built as a module. The module will be called
+ inv-mpu-iio-i2c.
+
+config INV_MPU_IIO_SPI
+ tristate "Invensense ICM20xxx devices (SPI)"
+ depends on SPI_MASTER && !INV_MPU6050_IIO
+ select INV_MPU_IIO
+ default n
+ help
+ This driver supports Invensense ICM20xxx devices over SPI.
+ This driver can be built as a module. The module will be called
+ inv-mpu-iio-spi.
+
+source "drivers/iio/imu/inv_mpu/inv_test/Kconfig"
diff --git a/drivers/iio/imu/inv_mpu/Makefile b/drivers/iio/imu/inv_mpu/Makefile
new file mode 100644
index 000000000000..dfc4c257ef73
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/Makefile
@@ -0,0 +1,61 @@
+#
+# Makefile for Invensense inv-mpu-iio device.
+#
+
+obj-$(CONFIG_INV_MPU_IIO) += inv-mpu-iio.o
+
+inv-mpu-iio-objs += inv_mpu_common.o
+inv-mpu-iio-objs += inv_mpu_ring.o
+inv-mpu-iio-objs += inv_mpu_timestamp.o
+inv-mpu-iio-objs += inv_mpu_dts.o
+
+# chip support
+ifeq ($(CONFIG_INV_MPU_IIO_ICM20648), y)
+inv-mpu-iio-objs += icm20648/inv_mpu_init.o
+inv-mpu-iio-objs += icm20648/inv_mpu_core.o
+inv-mpu-iio-objs += icm20648/inv_mpu_parsing.o
+inv-mpu-iio-objs += icm20648/inv_mpu_setup.o
+inv-mpu-iio-objs += icm20648/inv_mpu_dmp_fifo.o
+inv-mpu-iio-objs += icm20648/inv_slave_compass.o
+inv-mpu-iio-objs += icm20648/inv_slave_pressure.o
+inv-mpu-iio-objs += icm20648/inv_slave_als.o
+inv-mpu-iio-objs += icm20648/inv_mpu_load_dmp.o
+inv-mpu-iio-objs += icm20648/inv_mpu_selftest.o
+inv-mpu-iio-objs += dmp_support/inv_mpu_misc.o
+else ifeq ($(CONFIG_INV_MPU_IIO_ICM20690), y)
+inv-mpu-iio-objs += icm20690/inv_mpu_init_20690.o
+inv-mpu-iio-objs += icm20690/inv_mpu_core_20690.o
+inv-mpu-iio-objs += icm20690/inv_mpu_parsing_20690.o
+inv-mpu-iio-objs += icm20690/inv_mpu_setup_20690.o
+inv-mpu-iio-objs += icm20690/inv_mpu_selftest_20690.o
+inv-mpu-iio-objs += icm20690/inv_slave_compass.o
+else ifeq ($(CONFIG_INV_MPU_IIO_ICM20602), y)
+inv-mpu-iio-objs += icm20602/inv_mpu_init_20602.o
+inv-mpu-iio-objs += icm20602/inv_mpu_core_20602.o
+inv-mpu-iio-objs += icm20602/inv_mpu_parsing_20602.o
+inv-mpu-iio-objs += icm20602/inv_mpu_setup_20602.o
+inv-mpu-iio-objs += icm20602/inv_mpu_selftest_20602.o
+else ifeq ($(CONFIG_INV_MPU_IIO_ICM20608D), y)
+inv-mpu-iio-objs += icm20608d/inv_mpu_init_20608.o
+inv-mpu-iio-objs += icm20608d/inv_mpu_core_20608.o
+inv-mpu-iio-objs += icm20608d/inv_mpu_parsing_20608.o
+inv-mpu-iio-objs += icm20608d/inv_mpu_setup_20608D.o
+inv-mpu-iio-objs += icm20608d/inv_mpu_dmp_fifo.o
+inv-mpu-iio-objs += icm20608d/inv_mpu_load_dmp.o
+inv-mpu-iio-objs += icm20608d/inv_mpu_selftest_20608.o
+inv-mpu-iio-objs += dmp_support/inv_mpu_misc.o
+else ifeq ($(CONFIG_INV_MPU_IIO_IAM20680), y)
+inv-mpu-iio-objs += iam20680/inv_mpu_init_20680.o
+inv-mpu-iio-objs += iam20680/inv_mpu_core_20680.o
+inv-mpu-iio-objs += iam20680/inv_mpu_parsing_20680.o
+inv-mpu-iio-objs += iam20680/inv_mpu_setup_20680.o
+inv-mpu-iio-objs += iam20680/inv_mpu_selftest_20680.o
+endif
+
+# Bus support
+obj-$(CONFIG_INV_MPU_IIO_I2C) += inv-mpu-iio-i2c.o
+inv-mpu-iio-i2c-objs := inv_mpu_i2c.o
+obj-$(CONFIG_INV_MPU_IIO_SPI) += inv-mpu-iio-spi.o
+inv-mpu-iio-spi-objs := inv_mpu_spi.o
+
+obj-y += inv_test/
diff --git a/drivers/iio/imu/inv_mpu/README b/drivers/iio/imu/inv_mpu/README
new file mode 100644
index 000000000000..47ff5029ee6e
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/README
@@ -0,0 +1,117 @@
+Kernel driver inv-mpu-iio
+Author: InvenSense, Inc.
+
+
+Table of Contents
+=================
+- Description
+- Integrating the Driver in the Linux Kernel
+- Dts file
+- Communicating with the Driver in Userspace
+
+
+Description
+===========
+This document describes how to install the Invensense device driver into a
+Linux kernel. The supported chips are listed in Kconfig and user selects an
+appropriate one from .e.g. menuconfig.
+
+
+Integrating the Driver in the Linux Kernel
+==========================================
+Please add the files as follows (kernel 3.10):
+- Copy mpu.h to <kernel_root>/include/linux/iio/imu/
+- Copy inv_mpu folder under <kernel_root>/drivers/iio/imu/
+
+In order to see the driver in menuconfig when building the kernel, please
+make modifications as shown below:
+
+ add "source "drivers/iio/imu/inv_mpu/Kconfig""
+ in <kernel_root>/drivers/iio/imu/Kconfig
+
+ add "obj-y += inv_mpu/"
+ in <kernel_root>/drivers/iio/imu/Makefile
+
+
+
+Dts file
+========
+In order to recognize the Invensense device on the I2C/SPI bus, dts(or dtsi)
+file must be modified.
+
+Example)
+ICM20648 + AK09911/BMP280/APDS9930 on AUX I2C
+
+ i2c@f9968000 {
+ /* Invensense */
+ mpu6515_acc@68 {
+ compatible = "inven,icm20648";
+ reg = <0x68>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <73 0x2>;
+ inven,vdd_ana-supply = <&pm8941_l17>;
+ inven,vcc_i2c-supply = <&pm8941_lvs1>;
+ inven,gpio_int1 = <&msmgpio 73 0x00>;
+ fs_range = <0x00>;
+ /* mount matrix */
+ axis_map_x = <1>;
+ axis_map_y = <0>;
+ axis_map_z = <2>;
+ negate_x = <0>;
+ negate_y = <0>;
+ negate_z = <1>;
+ poll_interval = <200>;
+ min_interval = <5>;
+ inven,secondary_reg = <0x0c>;
+ /* If no compass sensor,
+ * replace "compass" with "none"
+ */
+ inven,secondary_type = "compass";
+ inven,secondary_name = "ak09911";
+ inven,secondary_axis_map_x = <1>;
+ inven,secondary_axis_map_y = <0>;
+ inven,secondary_axis_map_z = <2>;
+ inven,secondary_negate_x = <1>;
+ inven,secondary_negate_y = <1>;
+ inven,secondary_negate_z = <1>;
+ /* If no pressure sensor,
+ * replace "pressure" with "none"
+ */
+ inven,aux_type = "pressure";
+ inven,aux_name = "bmp280";
+ inven,aux_reg = <0x76>;
+ /* If no ALS sensor
+ * replace "als" with "none"
+ */
+ inven,read_only_slave_type = "als";
+ inven,read_only_slave_name = "apds9930";
+ inven,read_only_slave_reg = <0x39>;
+ };
+ };
+
+
+Communicating with the Driver in Userspace
+==========================================
+The driver generates several files in sysfs upon installation.
+These files are used to communicate with the driver. The files can be found at:
+
+(I2C) /sys/devices/*.i2c/i2c-*/*-*/iio:device*
+(SPI) /sys/devices/*.spi/spi_master/spi*/spi*.*/iio:device*
+
+Group and Owner for all entries should be updated to system/system at
+boot time to allow userspace to access properly.
+
+
+License
+=======
+Copyright (C) 2018 InvenSense, 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.
+
diff --git a/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_core_20680.c b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_core_20680.c
new file mode 100644
index 000000000000..b429f57be5ca
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_core_20680.c
@@ -0,0 +1,1072 @@
+/*
+ * Copyright (C) 2017-2018 InvenSense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "inv_mpu: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+
+#include "../inv_mpu_iio.h"
+
+static const struct inv_hw_s hw_info[INV_NUM_PARTS] = {
+ {128, "ICM20608D"},
+ {128, "ICM20690"},
+ {128, "ICM20602"},
+ {128, "IAM20680"},
+};
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static char debug_reg_addr = 0x6;
+#endif
+
+const char sensor_l_info[][30] = {
+ "SENSOR_L_ACCEL",
+ "SENSOR_L_GYRO",
+ "SENSOR_L_MAG",
+ "SENSOR_L_ALS",
+ "SENSOR_L_SIXQ",
+ "SENSOR_L_THREEQ",
+ "SENSOR_L_NINEQ",
+ "SENSOR_L_PEDQ",
+ "SENSOR_L_GEOMAG",
+ "SENSOR_L_PRESSURE",
+ "SENSOR_L_GYRO_CAL",
+ "SENSOR_L_MAG_CAL",
+ "SENSOR_L_EIS_GYRO",
+ "SENSOR_L_ACCEL_WAKE",
+ "SENSOR_L_GYRO_WAKE",
+ "SENSOR_L_MAG_WAKE",
+ "SENSOR_L_ALS_WAKE",
+ "SENSOR_L_SIXQ_WAKE",
+ "SENSOR_L_NINEQ_WAKE",
+ "SENSOR_L_PEDQ_WAKE",
+ "SENSOR_L_GEOMAG_WAKE",
+ "SENSOR_L_PRESSURE_WAKE",
+ "SENSOR_L_GYRO_CAL_WAKE",
+ "SENSOR_L_MAG_CAL_WAKE",
+ "SENSOR_L_NUM_MAX",
+};
+
+static int inv_set_accel_bias_reg(struct inv_mpu_state *st,
+ int accel_bias, int axis)
+{
+ int accel_reg_bias;
+ u8 addr;
+ u8 d[2];
+ int result = 0;
+
+ switch (axis) {
+ case 0:
+ /* X */
+ addr = REG_XA_OFFS_H;
+ break;
+ case 1:
+ /* Y */
+ addr = REG_YA_OFFS_H;
+ break;
+ case 2:
+ /* Z* */
+ addr = REG_ZA_OFFS_H;
+ break;
+ default:
+ result = -EINVAL;
+ goto accel_bias_set_err;
+ }
+
+ result = inv_plat_read(st, addr, 2, d);
+ if (result)
+ goto accel_bias_set_err;
+ accel_reg_bias = ((int)d[0] << 8) | d[1];
+
+ /* accel_bias is 2g scaled by 1<<16.
+ * Convert to 16g, and mask bit0 */
+ accel_reg_bias -= ((accel_bias / 8 / 65536) & ~1);
+
+ d[0] = (accel_reg_bias >> 8) & 0xff;
+ d[1] = (accel_reg_bias) & 0xff;
+ result = inv_plat_single_write(st, addr, d[0]);
+ if (result)
+ goto accel_bias_set_err;
+ result = inv_plat_single_write(st, addr + 1, d[1]);
+ if (result)
+ goto accel_bias_set_err;
+
+accel_bias_set_err:
+ return result;
+}
+
+static int inv_set_gyro_bias_reg(struct inv_mpu_state *st,
+ const int gyro_bias, int axis)
+{
+ int gyro_reg_bias;
+ u8 addr;
+ u8 d[2];
+ int result = 0;
+
+ switch (axis) {
+ case 0:
+ /* X */
+ addr = REG_XG_OFFS_USR_H;
+ break;
+ case 1:
+ /* Y */
+ addr = REG_YG_OFFS_USR_H;
+ break;
+ case 2:
+ /* Z */
+ addr = REG_ZG_OFFS_USR_H;
+ break;
+ default:
+ result = -EINVAL;
+ goto gyro_bias_set_err;
+ }
+
+ /* gyro_bias is 2000dps scaled by 1<<16.
+ * Convert to 1000dps */
+ gyro_reg_bias = (-gyro_bias * 2 / 65536);
+
+ d[0] = (gyro_reg_bias >> 8) & 0xff;
+ d[1] = (gyro_reg_bias) & 0xff;
+ result = inv_plat_single_write(st, addr, d[0]);
+ if (result)
+ goto gyro_bias_set_err;
+ result = inv_plat_single_write(st, addr + 1, d[1]);
+ if (result)
+ goto gyro_bias_set_err;
+
+gyro_bias_set_err:
+ return result;
+}
+
+static int _bias_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result, data;
+
+ result = inv_switch_power_in_lp(st, true);
+ if (result)
+ return result;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ goto bias_store_fail;
+ switch (this_attr->address) {
+ case ATTR_ACCEL_X_OFFSET:
+ result = inv_set_accel_bias_reg(st, data, 0);
+ if (result)
+ goto bias_store_fail;
+ st->input_accel_bias[0] = data;
+ break;
+ case ATTR_ACCEL_Y_OFFSET:
+ result = inv_set_accel_bias_reg(st, data, 1);
+ if (result)
+ goto bias_store_fail;
+ st->input_accel_bias[1] = data;
+ break;
+ case ATTR_ACCEL_Z_OFFSET:
+ result = inv_set_accel_bias_reg(st, data, 2);
+ if (result)
+ goto bias_store_fail;
+ st->input_accel_bias[2] = data;
+ break;
+ case ATTR_GYRO_X_OFFSET:
+ result = inv_set_gyro_bias_reg(st, data, 0);
+ if (result)
+ goto bias_store_fail;
+ st->input_gyro_bias[0] = data;
+ break;
+ case ATTR_GYRO_Y_OFFSET:
+ result = inv_set_gyro_bias_reg(st, data, 1);
+ if (result)
+ goto bias_store_fail;
+ st->input_gyro_bias[1] = data;
+ break;
+ case ATTR_GYRO_Z_OFFSET:
+ result = inv_set_gyro_bias_reg(st, data, 2);
+ if (result)
+ goto bias_store_fail;
+ st->input_gyro_bias[2] = data;
+ break;
+ default:
+ break;
+ }
+
+bias_store_fail:
+ if (result)
+ return result;
+ result = inv_switch_power_in_lp(st, false);
+ if (result)
+ return result;
+
+ return count;
+}
+
+static ssize_t inv_bias_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ result = _bias_store(dev, attr, buf, count);
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static ssize_t inv_debug_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result, data;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return result;
+ switch (this_attr->address) {
+ case ATTR_DMP_LP_EN_OFF:
+ st->chip_config.lp_en_mode_off = !!data;
+ inv_switch_power_in_lp(st, !!data);
+ break;
+ case ATTR_DMP_CLK_SEL:
+ st->chip_config.clk_sel = !!data;
+ inv_switch_power_in_lp(st, !!data);
+ break;
+ case ATTR_DEBUG_REG_ADDR:
+ debug_reg_addr = data;
+ break;
+ case ATTR_DEBUG_REG_WRITE:
+ inv_plat_single_write(st, debug_reg_addr, data);
+ break;
+ }
+ return count;
+}
+#endif
+
+static int _misc_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int result, data;
+
+ result = inv_switch_power_in_lp(st, true);
+ if (result)
+ return result;
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return result;
+ switch (this_attr->address) {
+ case ATTR_GYRO_SCALE:
+ if (data > 3)
+ return -EINVAL;
+ st->chip_config.fsr = data;
+ result = inv_set_gyro_sf(st);
+ return result;
+ case ATTR_ACCEL_SCALE:
+ if (data > 3)
+ return -EINVAL;
+ st->chip_config.accel_fs = data;
+ result = inv_set_accel_sf(st);
+ return result;
+ default:
+ return -EINVAL;
+ }
+ st->trigger_state = MISC_TRIGGER;
+ result = set_inv_enable(indio_dev);
+
+ return result;
+}
+
+/*
+ * inv_misc_attr_store() - calling this function
+ */
+static ssize_t inv_misc_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ result = _misc_attr_store(dev, attr, buf, count);
+ mutex_unlock(&indio_dev->mlock);
+ if (result)
+ return result;
+
+ return count;
+}
+
+static ssize_t inv_sensor_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ return snprintf(buf, MAX_WR_SZ, "%d\n",
+ st->sensor_l[this_attr->address].rate);
+}
+
+static ssize_t inv_sensor_rate_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int data, rate, ind;
+ int result;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return -EINVAL;
+ if (data <= 0) {
+ pr_err("sensor_rate_store: invalid data=%d\n", data);
+ return -EINVAL;
+ }
+ ind = this_attr->address;
+ rate = inv_rate_convert(st, ind, data);
+
+ pr_debug("sensor [%s] requested rate %d input [%d]\n",
+ sensor_l_info[ind], rate, data);
+
+ if (rate == st->sensor_l[ind].rate)
+ return count;
+ mutex_lock(&indio_dev->mlock);
+ st->sensor_l[ind].rate = rate;
+ st->trigger_state = DATA_TRIGGER;
+ inv_check_sensor_on(st);
+ result = set_inv_enable(indio_dev);
+ pr_debug("%s rate %d div %d\n", sensor_l_info[ind],
+ st->sensor_l[ind].rate, st->sensor_l[ind].div);
+ mutex_unlock(&indio_dev->mlock);
+
+ return count;
+}
+
+static ssize_t inv_sensor_on_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->sensor_l[this_attr->address].on);
+}
+
+static ssize_t inv_sensor_on_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int data, on, ind;
+ int result;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return -EINVAL;
+ if (data < 0) {
+ pr_err("sensor_on_store: invalid data=%d\n", data);
+ return -EINVAL;
+ }
+ ind = this_attr->address;
+ on = !!data;
+
+ pr_debug("sensor [%s] requested %s, input [%d]\n",
+ sensor_l_info[ind], (on == 1) ? "On" : "Off", data);
+
+ if (on == st->sensor_l[ind].on) {
+ pr_debug("sensor [%s] is already %s, input [%d]\n",
+ sensor_l_info[ind], (on == 1) ? "On" : "Off", data);
+ return count;
+ }
+
+ mutex_lock(&indio_dev->mlock);
+ st->sensor_l[ind].on = on;
+ st->trigger_state = RATE_TRIGGER;
+ inv_check_sensor_on(st);
+ result = set_inv_enable(indio_dev);
+ mutex_unlock(&indio_dev->mlock);
+ if (result)
+ return result;
+
+ pr_debug("Sensor [%s] is %s by sysfs\n",
+ sensor_l_info[ind], (on == 1) ? "On" : "Off");
+ return count;
+}
+
+static int inv_check_l_step(struct inv_mpu_state *st)
+{
+ if (st->step_counter_l_on || st->step_counter_wake_l_on)
+ st->ped.on = true;
+ else
+ st->ped.on = false;
+
+ return 0;
+}
+
+static int _basic_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int data;
+ int result;
+ u32 power_on_data;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result || (data < 0))
+ return -EINVAL;
+
+ switch (this_attr->address) {
+ case ATTR_DMP_PED_ON:
+ if ((!!data) == st->ped.on)
+ return count;
+ st->ped.on = !!data;
+ break;
+ case ATTR_DMP_TILT_ENABLE:
+ if ((!!data) == st->chip_config.tilt_enable)
+ return count;
+ st->chip_config.tilt_enable = !!data;
+ pr_info("Tile %s\n",
+ st->chip_config.tilt_enable ==
+ 1 ? "Enabled" : "Disabled");
+ break;
+ case ATTR_DMP_PICK_UP_ENABLE:
+ if ((!!data) == st->chip_config.pick_up_enable) {
+ pr_info("Pick_up enable already %s\n",
+ st->chip_config.pick_up_enable ==
+ 1 ? "Enabled" : "Disabled");
+ return count;
+ }
+ st->chip_config.pick_up_enable = !!data;
+ pr_info("Pick up %s\n",
+ st->chip_config.pick_up_enable ==
+ 1 ? "Enable" : "Disable");
+ break;
+ case ATTR_IN_POWER_ON:
+ {
+ u8 p0[2];
+ u8 p1[2];
+
+ power_on_data = (u32)data;
+ p0[0] = (power_on_data & 0xff);
+ p0[1] = ((power_on_data >> 8) & 0xff);
+ p1[0] = ((power_on_data >> 16) & 0xff);
+ p1[1] = ((power_on_data >> 24) & 0xff);
+
+ if (st->bus_type == BUS_SPI) {
+ struct spi_transfer power_on;
+ struct spi_message msg;
+
+ memset(&power_on, 0, sizeof(struct spi_transfer));
+
+ power_on.bits_per_word = 8;
+ power_on.len = 2;
+
+ power_on.tx_buf = p0;
+ power_on.rx_buf = p1;
+ spi_message_init(&msg);
+ spi_message_add_tail(&power_on, &msg);
+ spi_sync(to_spi_device(st->dev), &msg);
+
+ } else if (st->bus_type == BUS_I2C) {
+ struct i2c_msg msgs[2];
+
+ p0[0] &= 0x7f;
+
+ msgs[0].addr = st->i2c_addr;
+ msgs[0].flags = 0; /* write */
+ msgs[0].buf = &p0[0];
+ msgs[0].len = 1;
+
+ msgs[1].addr = st->i2c_addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].buf = &p1[1];
+ msgs[1].len = 1;
+
+ result = i2c_transfer(st->sl_handle, msgs, 2);
+ if (result < 2)
+ return -EIO;
+ }
+ st->power_on_data = ((p0[0] << 24) | (p0[1] << 16) |
+ (p1[0] << 8) | p1[1]);
+ return count;
+ }
+ case ATTR_DMP_EIS_ENABLE:
+ if ((!!data) == st->chip_config.eis_enable)
+ return count;
+ st->chip_config.eis_enable = !!data;
+ pr_info("Eis %s\n",
+ st->chip_config.eis_enable == 1 ? "Enable" : "Disable");
+ break;
+ case ATTR_DMP_STEP_DETECTOR_ON:
+ st->step_detector_l_on = !!data;
+ break;
+ case ATTR_DMP_STEP_DETECTOR_WAKE_ON:
+ st->step_detector_wake_l_on = !!data;
+ break;
+ case ATTR_DMP_STEP_COUNTER_ON:
+ st->step_counter_l_on = !!data;
+ break;
+ case ATTR_DMP_STEP_COUNTER_WAKE_ON:
+ st->step_counter_wake_l_on = !!data;
+ break;
+ case ATTR_DMP_BATCHMODE_TIMEOUT:
+ if (data == st->batch.timeout)
+ return count;
+ st->batch.timeout = data;
+ break;
+ default:
+ return -EINVAL;
+ };
+ inv_check_l_step(st);
+ inv_check_sensor_on(st);
+
+ st->trigger_state = EVENT_TRIGGER;
+ result = set_inv_enable(indio_dev);
+ if (result)
+ return result;
+
+ return count;
+}
+
+/*
+ * inv_basic_attr_store()
+ */
+static ssize_t inv_basic_attr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ result = _basic_attr_store(dev, attr, buf, count);
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return result;
+}
+
+/*
+ * inv_attr_show()
+ */
+static ssize_t inv_attr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ s8 *m;
+
+ switch (this_attr->address) {
+ case ATTR_GYRO_SCALE:
+ {
+ const s16 gyro_scale[] = { 250, 500, 1000, 2000 };
+
+ return snprintf(buf, MAX_WR_SZ, "%d\n",
+ gyro_scale[st->chip_config.fsr]);
+ }
+ case ATTR_ACCEL_SCALE:
+ {
+ const s16 accel_scale[] = { 2, 4, 8, 16 };
+ return snprintf(buf, MAX_WR_SZ, "%d\n",
+ accel_scale[st->chip_config.accel_fs]);
+ }
+ case ATTR_GYRO_ENABLE:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->chip_config.gyro_enable);
+ case ATTR_ACCEL_ENABLE:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->chip_config.accel_enable);
+ case ATTR_IN_POWER_ON:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->power_on_data);
+ case ATTR_DMP_BATCHMODE_TIMEOUT:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->batch.timeout);
+ case ATTR_DMP_PED_ON:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->ped.on);
+ case ATTR_DMP_TILT_ENABLE:
+ return snprintf(buf, MAX_WR_SZ, "%d\n",
+ st->chip_config.tilt_enable);
+ case ATTR_DMP_PICK_UP_ENABLE:
+ return snprintf(buf, MAX_WR_SZ, "%d\n",
+ st->chip_config.pick_up_enable);
+ case ATTR_DMP_EIS_ENABLE:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->chip_config.eis_enable);
+ case ATTR_DMP_LP_EN_OFF:
+ return snprintf(buf, MAX_WR_SZ, "%d\n",
+ st->chip_config.lp_en_mode_off);
+ case ATTR_DMP_STEP_COUNTER_ON:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->step_counter_l_on);
+ case ATTR_DMP_STEP_COUNTER_WAKE_ON:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->step_counter_wake_l_on);
+ case ATTR_DMP_STEP_DETECTOR_ON:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->step_detector_l_on);
+ case ATTR_DMP_STEP_DETECTOR_WAKE_ON:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->step_detector_wake_l_on);
+ case ATTR_GYRO_MATRIX:
+ m = st->plat_data.orientation;
+ return snprintf(buf, MAX_WR_SZ, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7],
+ m[8]);
+ case ATTR_ACCEL_MATRIX:
+ m = st->plat_data.orientation;
+ return snprintf(buf, MAX_WR_SZ, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7],
+ m[8]);
+ case ATTR_GYRO_SF:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->gyro_sf);
+ case ATTR_ANGLVEL_X_ST_CALIBBIAS:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->gyro_st_bias[0]);
+ case ATTR_ANGLVEL_Y_ST_CALIBBIAS:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->gyro_st_bias[1]);
+ case ATTR_ANGLVEL_Z_ST_CALIBBIAS:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->gyro_st_bias[2]);
+ case ATTR_ACCEL_X_ST_CALIBBIAS:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->accel_st_bias[0]);
+ case ATTR_ACCEL_Y_ST_CALIBBIAS:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->accel_st_bias[1]);
+ case ATTR_ACCEL_Z_ST_CALIBBIAS:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->accel_st_bias[2]);
+ case ATTR_GYRO_X_OFFSET:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_gyro_bias[0]);
+ case ATTR_GYRO_Y_OFFSET:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_gyro_bias[1]);
+ case ATTR_GYRO_Z_OFFSET:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_gyro_bias[2]);
+ case ATTR_ACCEL_X_OFFSET:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_accel_bias[0]);
+ case ATTR_ACCEL_Y_OFFSET:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_accel_bias[1]);
+ case ATTR_ACCEL_Z_OFFSET:
+ return snprintf(buf, MAX_WR_SZ, "%d\n", st->input_accel_bias[2]);
+ default:
+ return -EPERM;
+ }
+}
+
+static ssize_t inv_self_test(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int res;
+
+ mutex_lock(&indio_dev->mlock);
+ res = inv_hw_self_test(st);
+ set_inv_enable(indio_dev);
+ mutex_unlock(&indio_dev->mlock);
+
+ return snprintf(buf, MAX_WR_SZ, "%d\n", res);
+}
+
+
+/*
+ * inv_temperature_show() - Read temperature data directly from registers.
+ */
+static ssize_t inv_temperature_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ u8 data[2];
+ s32 temp;
+ int res;
+
+ mutex_lock(&indio_dev->mlock);
+ res = inv_plat_read(st, REG_RAW_TEMP, 2, data);
+ if (res)
+ return res;
+ mutex_unlock(&indio_dev->mlock);
+
+ temp = (s32)be16_to_cpup((__be16 *)(data)) * 10000;
+ temp = temp / TEMP_SENSITIVITY + TEMP_OFFSET;
+
+ return snprintf(buf, MAX_WR_SZ, "%d %lld\n", temp, get_time_ns());
+}
+
+/*
+ * inv_reg_dump_show() - Register dump for testing.
+ */
+static ssize_t inv_reg_dump_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ii;
+ char data;
+ int bytes_printed = 0;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ bytes_printed += snprintf(buf + bytes_printed, MAX_WR_SZ, "bank 0\n");
+
+ for (ii = 0; ii < 0x7F; ii++) {
+ /* don't read fifo r/w register */
+ if ((ii == REG_MEM_R_W) || (ii == REG_FIFO_R_W))
+ data = 0;
+ else
+ inv_plat_read(st, ii, 1, &data);
+ bytes_printed += snprintf(buf + bytes_printed, MAX_WR_SZ,
+ "%#2x: %#2x\n", ii, data);
+ }
+ set_inv_enable(indio_dev);
+ mutex_unlock(&indio_dev->mlock);
+
+ return bytes_printed;
+}
+
+static ssize_t inv_flush_batch_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int result, data;
+
+ result = kstrtoint(buf, 10, &data);
+ if (result)
+ return result;
+
+ mutex_lock(&indio_dev->mlock);
+ result = inv_flush_batch_data(indio_dev, data);
+ mutex_unlock(&indio_dev->mlock);
+
+ return count;
+}
+
+static const struct iio_chan_spec inv_mpu_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(INV_MPU_SCAN_TIMESTAMP),
+};
+
+/* special run time sysfs entry, read only */
+static DEVICE_ATTR(debug_reg_dump, S_IRUGO | S_IWUSR, inv_reg_dump_show, NULL);
+static DEVICE_ATTR(out_temperature, S_IRUGO | S_IWUSR,
+ inv_temperature_show, NULL);
+static DEVICE_ATTR(misc_self_test, S_IRUGO | S_IWUSR, inv_self_test, NULL);
+
+static IIO_DEVICE_ATTR(info_anglvel_matrix, S_IRUGO, inv_attr_show, NULL,
+ ATTR_GYRO_MATRIX);
+static IIO_DEVICE_ATTR(info_accel_matrix, S_IRUGO, inv_attr_show, NULL,
+ ATTR_ACCEL_MATRIX);
+
+static IIO_DEVICE_ATTR(info_gyro_sf, S_IRUGO, inv_attr_show, NULL,
+ ATTR_GYRO_SF);
+/* write only sysfs */
+static DEVICE_ATTR(misc_flush_batch, S_IWUSR, NULL, inv_flush_batch_store);
+
+/* sensor on/off sysfs control */
+static IIO_DEVICE_ATTR(in_accel_enable, S_IRUGO | S_IWUSR,
+ inv_sensor_on_show, inv_sensor_on_store, SENSOR_L_ACCEL);
+static IIO_DEVICE_ATTR(in_anglvel_enable, S_IRUGO | S_IWUSR,
+ inv_sensor_on_show, inv_sensor_on_store, SENSOR_L_GYRO);
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static IIO_DEVICE_ATTR(in_eis_enable, S_IRUGO | S_IWUSR,
+ inv_sensor_on_show, inv_sensor_on_store,
+ SENSOR_L_EIS_GYRO);
+#endif
+static IIO_DEVICE_ATTR(in_accel_wake_enable, S_IRUGO | S_IWUSR,
+ inv_sensor_on_show, inv_sensor_on_store,
+ SENSOR_L_ACCEL_WAKE);
+static IIO_DEVICE_ATTR(in_anglvel_wake_enable, S_IRUGO | S_IWUSR,
+ inv_sensor_on_show, inv_sensor_on_store,
+ SENSOR_L_GYRO_WAKE);
+
+/* sensor rate sysfs control */
+static IIO_DEVICE_ATTR(in_accel_rate, S_IRUGO | S_IWUSR,
+ inv_sensor_rate_show, inv_sensor_rate_store,
+ SENSOR_L_ACCEL);
+static IIO_DEVICE_ATTR(in_anglvel_rate, S_IRUGO | S_IWUSR, inv_sensor_rate_show,
+ inv_sensor_rate_store, SENSOR_L_GYRO);
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static IIO_DEVICE_ATTR(in_eis_rate, S_IRUGO | S_IWUSR,
+ inv_sensor_rate_show, inv_sensor_rate_store,
+ SENSOR_L_EIS_GYRO);
+#endif
+static IIO_DEVICE_ATTR(in_accel_wake_rate, S_IRUGO | S_IWUSR,
+ inv_sensor_rate_show, inv_sensor_rate_store,
+ SENSOR_L_ACCEL_WAKE);
+static IIO_DEVICE_ATTR(in_anglvel_wake_rate, S_IRUGO | S_IWUSR,
+ inv_sensor_rate_show, inv_sensor_rate_store,
+ SENSOR_L_GYRO_WAKE);
+
+static IIO_DEVICE_ATTR(misc_batchmode_timeout, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_DMP_BATCHMODE_TIMEOUT);
+
+/* engine scale */
+static IIO_DEVICE_ATTR(in_accel_scale, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_misc_attr_store, ATTR_ACCEL_SCALE);
+static IIO_DEVICE_ATTR(in_anglvel_scale, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_misc_attr_store, ATTR_GYRO_SCALE);
+
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static IIO_DEVICE_ATTR(debug_lp_en_off, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_debug_store, ATTR_DMP_LP_EN_OFF);
+static IIO_DEVICE_ATTR(debug_clock_sel, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_debug_store, ATTR_DMP_CLK_SEL);
+static IIO_DEVICE_ATTR(debug_reg_write, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_debug_store, ATTR_DEBUG_REG_WRITE);
+static IIO_DEVICE_ATTR(debug_reg_write_addr, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_debug_store, ATTR_DEBUG_REG_ADDR);
+#endif
+
+static IIO_DEVICE_ATTR(in_accel_x_st_calibbias, S_IRUGO | S_IWUSR,
+ inv_attr_show, NULL, ATTR_ACCEL_X_ST_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_accel_y_st_calibbias, S_IRUGO | S_IWUSR,
+ inv_attr_show, NULL, ATTR_ACCEL_Y_ST_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_accel_z_st_calibbias, S_IRUGO | S_IWUSR,
+ inv_attr_show, NULL, ATTR_ACCEL_Z_ST_CALIBBIAS);
+
+static IIO_DEVICE_ATTR(in_anglvel_x_st_calibbias, S_IRUGO | S_IWUSR,
+ inv_attr_show, NULL, ATTR_ANGLVEL_X_ST_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_anglvel_y_st_calibbias, S_IRUGO | S_IWUSR,
+ inv_attr_show, NULL, ATTR_ANGLVEL_Y_ST_CALIBBIAS);
+static IIO_DEVICE_ATTR(in_anglvel_z_st_calibbias, S_IRUGO | S_IWUSR,
+ inv_attr_show, NULL, ATTR_ANGLVEL_Z_ST_CALIBBIAS);
+
+static IIO_DEVICE_ATTR(in_accel_x_offset, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_bias_store, ATTR_ACCEL_X_OFFSET);
+static IIO_DEVICE_ATTR(in_accel_y_offset, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_bias_store, ATTR_ACCEL_Y_OFFSET);
+static IIO_DEVICE_ATTR(in_accel_z_offset, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_bias_store, ATTR_ACCEL_Z_OFFSET);
+
+static IIO_DEVICE_ATTR(in_anglvel_x_offset, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_bias_store, ATTR_GYRO_X_OFFSET);
+static IIO_DEVICE_ATTR(in_anglvel_y_offset, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_bias_store, ATTR_GYRO_Y_OFFSET);
+static IIO_DEVICE_ATTR(in_anglvel_z_offset, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_bias_store, ATTR_GYRO_Z_OFFSET);
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static IIO_DEVICE_ATTR(in_step_detector_enable, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_DMP_STEP_DETECTOR_ON);
+static IIO_DEVICE_ATTR(in_step_detector_wake_enable, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_DMP_STEP_DETECTOR_WAKE_ON);
+static IIO_DEVICE_ATTR(in_step_counter_enable, S_IRUGO | S_IWUSR, inv_attr_show,
+ inv_basic_attr_store, ATTR_DMP_STEP_COUNTER_ON);
+static IIO_DEVICE_ATTR(in_step_counter_wake_enable, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_DMP_STEP_COUNTER_WAKE_ON);
+
+static IIO_DEVICE_ATTR(event_tilt_enable, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_DMP_TILT_ENABLE);
+
+static IIO_DEVICE_ATTR(event_eis_enable, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_DMP_EIS_ENABLE);
+
+static IIO_DEVICE_ATTR(event_pick_up_enable, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_DMP_PICK_UP_ENABLE);
+
+static IIO_DEVICE_ATTR(in_power_on, S_IRUGO | S_IWUSR,
+ inv_attr_show, inv_basic_attr_store,
+ ATTR_IN_POWER_ON);
+#endif
+
+static const struct attribute *inv_raw_attributes[] = {
+ &dev_attr_debug_reg_dump.attr,
+ &dev_attr_out_temperature.attr,
+ &dev_attr_misc_flush_batch.attr,
+ &dev_attr_misc_self_test.attr,
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+ &iio_dev_attr_in_power_on.dev_attr.attr,
+#endif
+ &iio_dev_attr_in_accel_enable.dev_attr.attr,
+ &iio_dev_attr_in_accel_wake_enable.dev_attr.attr,
+ &iio_dev_attr_info_accel_matrix.dev_attr.attr,
+ &iio_dev_attr_in_accel_scale.dev_attr.attr,
+ &iio_dev_attr_misc_batchmode_timeout.dev_attr.attr,
+ &iio_dev_attr_in_accel_rate.dev_attr.attr,
+ &iio_dev_attr_in_accel_wake_rate.dev_attr.attr,
+};
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static const struct attribute *inv_debug_attributes[] = {
+ &iio_dev_attr_debug_lp_en_off.dev_attr.attr,
+ &iio_dev_attr_debug_clock_sel.dev_attr.attr,
+ &iio_dev_attr_debug_reg_write.dev_attr.attr,
+ &iio_dev_attr_debug_reg_write_addr.dev_attr.attr,
+};
+#endif
+
+static const struct attribute *inv_gyro_attributes[] = {
+ &iio_dev_attr_info_anglvel_matrix.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_enable.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_rate.dev_attr.attr,
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+ &iio_dev_attr_in_eis_enable.dev_attr.attr,
+#endif
+ &iio_dev_attr_in_anglvel_wake_enable.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_scale.dev_attr.attr,
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+ &iio_dev_attr_in_eis_rate.dev_attr.attr,
+#endif
+ &iio_dev_attr_in_anglvel_wake_rate.dev_attr.attr,
+ &iio_dev_attr_info_gyro_sf.dev_attr.attr,
+};
+
+static const struct attribute *inv_bias_attributes[] = {
+ &iio_dev_attr_in_accel_x_st_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_accel_y_st_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_accel_z_st_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_accel_x_offset.dev_attr.attr,
+ &iio_dev_attr_in_accel_y_offset.dev_attr.attr,
+ &iio_dev_attr_in_accel_z_offset.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_x_st_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_y_st_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_z_st_calibbias.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_x_offset.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_y_offset.dev_attr.attr,
+ &iio_dev_attr_in_anglvel_z_offset.dev_attr.attr,
+};
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+static const struct attribute *inv_pedometer_attributes[] = {
+ &iio_dev_attr_event_tilt_enable.dev_attr.attr,
+ &iio_dev_attr_event_eis_enable.dev_attr.attr,
+ &iio_dev_attr_event_pick_up_enable.dev_attr.attr,
+ &iio_dev_attr_in_step_counter_enable.dev_attr.attr,
+ &iio_dev_attr_in_step_counter_wake_enable.dev_attr.attr,
+ &iio_dev_attr_in_step_detector_enable.dev_attr.attr,
+ &iio_dev_attr_in_step_detector_wake_enable.dev_attr.attr,
+};
+#endif
+
+static struct attribute *inv_attributes[ARRAY_SIZE(inv_raw_attributes) +
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+ ARRAY_SIZE(inv_debug_attributes) +
+#endif
+ ARRAY_SIZE(inv_gyro_attributes) +
+ ARRAY_SIZE(inv_bias_attributes) +
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+ ARRAY_SIZE(inv_pedometer_attributes) +
+#endif
+ + 1];
+
+static const struct attribute_group inv_attribute_group = {
+ .name = "mpu",
+ .attrs = inv_attributes
+};
+
+static const struct iio_info mpu_info = {
+ .driver_module = THIS_MODULE,
+ .attrs = &inv_attribute_group,
+};
+
+/*
+ * inv_check_chip_type() - check and setup chip type.
+ */
+int inv_check_chip_type(struct iio_dev *indio_dev, const char *name)
+{
+ int result;
+ int t_ind;
+ struct inv_chip_config_s *conf;
+ struct mpu_platform_data *plat;
+ struct inv_mpu_state *st;
+
+ st = iio_priv(indio_dev);
+ conf = &st->chip_config;
+ plat = &st->plat_data;
+
+ if (!strcmp(name, "iam20680"))
+ st->chip_type = IAM20680;
+ else
+ return -EPERM;
+ st->chip_config.has_gyro = 1;
+
+ st->hw = &hw_info[st->chip_type];
+ result = inv_mpu_initialize(st);
+ if (result)
+ return result;
+
+ t_ind = 0;
+ memcpy(&inv_attributes[t_ind], inv_raw_attributes,
+ sizeof(inv_raw_attributes));
+ t_ind += ARRAY_SIZE(inv_raw_attributes);
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+ memcpy(&inv_attributes[t_ind], inv_pedometer_attributes,
+ sizeof(inv_pedometer_attributes));
+ t_ind += ARRAY_SIZE(inv_pedometer_attributes);
+#endif
+
+ memcpy(&inv_attributes[t_ind], inv_gyro_attributes,
+ sizeof(inv_gyro_attributes));
+ t_ind += ARRAY_SIZE(inv_gyro_attributes);
+
+ memcpy(&inv_attributes[t_ind], inv_bias_attributes,
+ sizeof(inv_bias_attributes));
+ t_ind += ARRAY_SIZE(inv_bias_attributes);
+
+#ifndef SUPPORT_ONLY_BASIC_FEATURES
+ memcpy(&inv_attributes[t_ind], inv_debug_attributes,
+ sizeof(inv_debug_attributes));
+ t_ind += ARRAY_SIZE(inv_debug_attributes);
+#endif
+
+ inv_attributes[t_ind] = NULL;
+
+ indio_dev->channels = inv_mpu_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
+
+ indio_dev->info = &mpu_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+
+ return result;
+}
+EXPORT_SYMBOL_GPL(inv_check_chip_type);
+
+int inv_create_dmp_sysfs(struct iio_dev *ind)
+{
+ // dummy
+ return 0;
+}
+EXPORT_SYMBOL_GPL(inv_create_dmp_sysfs);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device ICM20xxx driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_iio_reg_20680.h b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_iio_reg_20680.h
new file mode 100644
index 000000000000..3f8ce71be024
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_iio_reg_20680.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2017-2018 InvenSense, 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 _INV_MPU_IIO_REG_20680_H_
+#define _INV_MPU_IIO_REG_20680_H_
+
+/* Uncomment when HAL does not support the algorithm library
+ * for calibration and sensor fusion not to expose unused
+ * sysfs entries */
+#define SUPPORT_ONLY_BASIC_FEATURES
+
+/* Uncomment to read data registers for sensor data instead of FIFO */
+//#define SENSOR_DATA_FROM_REGISTERS
+
+/* Uncomment to enable timer based batching */
+#define TIMER_BASED_BATCHING
+
+/* Polling (batch mode) can be enabled only when FIFO read */
+#if defined(SENSOR_DATA_FROM_REGISTERS)
+#undef TIMER_BASED_BATCHING
+#endif
+
+/*register and associated bit definition*/
+#define REG_XA_OFFS_H 0x77
+#define REG_YA_OFFS_H 0x7A
+#define REG_ZA_OFFS_H 0x7D
+#define REG_XG_OFFS_USR_H 0x13
+#define REG_YG_OFFS_USR_H 0x15
+#define REG_ZG_OFFS_USR_H 0x17
+#define REG_SAMPLE_RATE_DIV 0x19
+
+#define REG_CONFIG 0x1A
+#define EXT_SYNC_SET 8
+
+#define REG_GYRO_CONFIG 0x1B
+#define BITS_SELF_TEST_EN 0xE0
+#define SHIFT_GYRO_FS_SEL 0x03
+
+#define REG_ACCEL_CONFIG 0x1C
+#define SHIFT_ACCEL_FS 0x03
+
+#define REG_LP_MODE_CTRL 0x1E
+#define BIT_GYRO_CYCLE_EN 0x80
+
+#define REG_ACCEL_WOM_THR 0x1F
+#define REG_ACCEL_WOM_X_THR 0x20
+#define REG_ACCEL_WOM_Y_THR 0x21
+#define REG_ACCEL_WOM_Z_THR 0x22
+
+#define REG_ACCEL_MOT_THR 0x1F
+#define REG_ACCEL_MOT_DUR 0x20
+
+#define REG_ACCEL_CONFIG_2 0x1D
+#define BIT_ACCEL_FCHOCIE_B 0x08
+
+#define REG_FIFO_EN 0x23
+#define BITS_GYRO_FIFO_EN 0x70
+#define BIT_ACCEL_FIFO_EN 0x08
+
+#define REG_FSYNC_INT 0x36
+#define BIT_FSYNC_INT 0x80
+
+#define REG_INT_PIN_CFG 0x37
+
+#define REG_INT_ENABLE 0x38
+#define BIT_WOM_X_INT_EN 0x80
+#define BIT_WOM_Y_INT_EN 0x40
+#define BIT_WOM_Z_INT_EN 0x20
+#define BIT_WOM_ALL_INT_EN 0xE0
+#define BIT_FSYNC_INT_EN 0x8
+#define BIT_DATA_RDY_EN 0x1
+
+#define REG_INT_STATUS 0x3A
+#define BIT_WOM_X_INT 0x80
+#define BIT_WOM_Y_INT 0x40
+#define BIT_WOM_Z_INT 0x20
+
+#define REG_RAW_ACCEL 0x3B
+#define REG_RAW_TEMP 0x41
+#define REG_RAW_GYRO 0x43
+#define REG_EXT_SENS_DATA_00 0x49
+#define REG_EXT_SENS_DATA_08 0x51
+#define REG_EXT_SENS_DATA_09 0x52
+
+#define REG_ACCEL_INTEL_CTRL 0x69
+#define BIT_ACCEL_INTEL_EN 0x80
+#define BIT_ACCEL_INTEL_MODE 0x40
+
+#define REG_USER_CTRL 0x6A
+#define BIT_COND_RST 0x01
+#define BIT_FIFO_RST 0x04
+#define BIT_FIFO_EN 0x40
+
+#define REG_PWR_MGMT_1 0x6B
+#define BIT_H_RESET 0x80
+#define BIT_SLEEP 0x40
+#define BIT_LP_EN 0x20
+#define BIT_CLK_PLL 0x01
+#define BIT_CLK_MASK 0x07
+
+#define REG_PWR_MGMT_2 0x6C
+#define BIT_PWR_ACCEL_STBY 0x38
+#define BIT_PWR_GYRO_STBY 0x07
+#define BIT_PWR_ALL_OFF 0x3F
+#define BIT_FIFO_LP_EN 0x80
+
+#define REG_MEM_BANK_SEL 0x6D
+#define REG_MEM_START_ADDR 0x6E
+#define REG_MEM_R_W 0x6F
+
+#define REG_FIFO_COUNT_H 0x72
+#define REG_FIFO_R_W 0x74
+#define REG_WHO_AM_I 0x75
+
+#define REG_6500_XG_ST_DATA 0x50
+#define REG_6500_XA_ST_DATA 0xD
+#define REG_6500_XA_OFFS_H 0x77
+#define REG_6500_YA_OFFS_H 0x7A
+#define REG_6500_ZA_OFFS_H 0x7D
+#define REG_6500_ACCEL_CONFIG2 0x1D
+#define BIT_ACCEL_FCHOCIE_B 0x08
+#define BIT_FIFO_SIZE_1K 0x40
+
+#define REG_LP_MODE_CFG 0x1E
+
+#define REG_6500_LP_ACCEL_ODR 0x1E
+#define REG_6500_ACCEL_WOM_THR 0x1F
+
+/* data output control reg 2 */
+#define ACCEL_ACCURACY_SET 0x4000
+#define GYRO_ACCURACY_SET 0x2000
+#define CPASS_ACCURACY_SET 0x1000
+
+/* data definitions */
+#define ACCEL_COVARIANCE 0
+#define BYTES_PER_SENSOR 6
+#define BYTES_FOR_TEMP 2
+#define FIFO_COUNT_BYTE 2
+#define HARDWARE_FIFO_SIZE 512
+#define FIFO_SIZE (HARDWARE_FIFO_SIZE * 7 / 10)
+#define POWER_UP_TIME 100
+#define REG_UP_TIME_USEC 100
+#define LEFT_OVER_BYTES 128
+#define IIO_BUFFER_BYTES 8
+#define BASE_SAMPLE_RATE 1000
+#define DRY_RUN_TIME 50
+#define INV_IAM20680_GYRO_START_TIME 35
+#define INV_IAM20680_ACCEL_START_TIME 30
+#define MODE_1K_INIT_SAMPLE 5
+#define FIRST_SAMPLE_BUF_MS 30
+
+#ifdef BIAS_CONFIDENCE_HIGH
+#define DEFAULT_ACCURACY 3
+#else
+#define DEFAULT_ACCURACY 1
+#endif
+
+/* temperature */
+#define TEMP_SENSITIVITY 32680 // 326.8 LSB/degC * 100
+#define TEMP_OFFSET 2500 // 25 degC * 100
+
+/* enum for sensor */
+enum INV_SENSORS {
+ SENSOR_ACCEL = 0,
+ SENSOR_TEMP,
+ SENSOR_GYRO,
+ SENSOR_COMPASS,
+ SENSOR_NUM_MAX,
+ SENSOR_INVALID,
+};
+
+enum inv_filter_e {
+ INV_FILTER_256HZ_NOLPF2 = 0,
+ INV_FILTER_188HZ,
+ INV_FILTER_98HZ,
+ INV_FILTER_42HZ,
+ INV_FILTER_20HZ,
+ INV_FILTER_10HZ,
+ INV_FILTER_5HZ,
+ INV_FILTER_2100HZ_NOLPF,
+ NUM_FILTER
+};
+
+#define MPU_DEFAULT_DMP_FREQ 200
+#define PEDOMETER_FREQ (MPU_DEFAULT_DMP_FREQ >> 2)
+#define SENSOR_FUSION_MIN_RATE 100
+#define GESTURE_ACCEL_RATE 50
+#define ESI_GYRO_RATE 1000
+#define MAX_FIFO_PACKET_READ 6
+#define MAX_BATCH_FIFO_SIZE FIFO_SIZE
+
+#define MIN_MST_ODR_CONFIG 4
+#define MAX_MST_ODR_CONFIG 5
+/* initial rate is important. For non-DMP mode, it is set as 4 1000/256*/
+#define MPU_INIT_SENSOR_RATE 4
+#define MAX_MST_NON_COMPASS_ODR_CONFIG 7
+#define THREE_AXES 3
+#define NINE_ELEM (THREE_AXES * THREE_AXES)
+#define MPU_TEMP_SHIFT 16
+
+#define DMP_DIVIDER (BASE_SAMPLE_RATE / MPU_DEFAULT_DMP_FREQ)
+#define DEFAULT_BATCH_RATE 400
+#define DEFAULT_BATCH_TIME (MSEC_PER_SEC / DEFAULT_BATCH_RATE)
+
+#define TEMPERATURE_SCALE 3340827L
+#define TEMPERATURE_OFFSET 1376256L
+#define SECONDARY_INIT_WAIT 100
+#define MPU_SOFT_REV_ADDR 0x86
+#define MPU_SOFT_REV_MASK 0xf
+#define SW_REV_LP_EN_MODE 4
+
+/* data limit definitions */
+#define MIN_FIFO_RATE 4
+#define MAX_FIFO_RATE MPU_DEFAULT_DMP_FREQ
+
+#define MAX_MPU_MEM 8192
+#define MAX_PRS_RATE 281
+
+enum inv_devices {
+ ICM20608D,
+ ICM20690,
+ ICM20602,
+ IAM20680,
+ INV_NUM_PARTS,
+};
+#endif
diff --git a/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_init_20680.c b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_init_20680.c
new file mode 100644
index 000000000000..58bd8d073890
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_init_20680.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2017-2018 InvenSense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "inv_mpu: " fmt
+#include "../inv_mpu_iio.h"
+
+static int inv_calc_gyro_sf(s8 pll)
+{
+ int a, r;
+ int value, t;
+
+ t = 102870L + 81L * pll;
+ a = (1L << 30) / t;
+ r = (1L << 30) - a * t;
+ value = a * 797 * DMP_DIVIDER;
+ value += (s64) ((a * 1011387LL * DMP_DIVIDER) >> 20);
+ value += r * 797L * DMP_DIVIDER / t;
+ value += (s32) ((s64) ((r * 1011387LL * DMP_DIVIDER) >> 20)) / t;
+ value <<= 1;
+
+ return value;
+}
+
+static int inv_read_timebase(struct inv_mpu_state *st)
+{
+
+ inv_plat_single_write(st, REG_CONFIG, 3);
+
+ st->eng_info[ENGINE_ACCEL].base_time = NSEC_PER_SEC;
+ st->eng_info[ENGINE_ACCEL].base_time_1k = NSEC_PER_SEC;
+ /* talor expansion to calculate base time unit */
+ st->eng_info[ENGINE_GYRO].base_time = NSEC_PER_SEC;
+ st->eng_info[ENGINE_GYRO].base_time_1k = NSEC_PER_SEC;
+ st->eng_info[ENGINE_I2C].base_time = NSEC_PER_SEC;
+ st->eng_info[ENGINE_I2C].base_time_1k = NSEC_PER_SEC;
+
+ st->eng_info[ENGINE_ACCEL].orig_rate = BASE_SAMPLE_RATE;
+ st->eng_info[ENGINE_GYRO].orig_rate = BASE_SAMPLE_RATE;
+ st->eng_info[ENGINE_I2C].orig_rate = BASE_SAMPLE_RATE;
+
+ st->gyro_sf = inv_calc_gyro_sf(0);
+
+ return 0;
+}
+
+int inv_set_gyro_sf(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_plat_single_write(st, REG_GYRO_CONFIG,
+ st->chip_config.fsr << SHIFT_GYRO_FS_SEL);
+
+ return result;
+}
+
+int inv_set_accel_sf(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_plat_single_write(st, REG_ACCEL_CONFIG,
+ st->chip_config.accel_fs << SHIFT_ACCEL_FS);
+ return result;
+}
+
+// dummy for 20602
+int inv_set_accel_intel(struct inv_mpu_state *st)
+{
+ return 0;
+}
+
+static void inv_init_sensor_struct(struct inv_mpu_state *st)
+{
+ int i;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].rate = MPU_INIT_SENSOR_RATE;
+
+ st->sensor[SENSOR_ACCEL].sample_size = BYTES_PER_SENSOR;
+ st->sensor[SENSOR_TEMP].sample_size = BYTES_FOR_TEMP;
+ st->sensor[SENSOR_GYRO].sample_size = BYTES_PER_SENSOR;
+
+ st->sensor_l[SENSOR_L_SIXQ].base = SENSOR_GYRO;
+ st->sensor_l[SENSOR_L_PEDQ].base = SENSOR_GYRO;
+
+ st->sensor_l[SENSOR_L_SIXQ_WAKE].base = SENSOR_GYRO;
+ st->sensor_l[SENSOR_L_PEDQ_WAKE].base = SENSOR_GYRO;
+
+ st->sensor[SENSOR_ACCEL].a_en = true;
+ st->sensor[SENSOR_GYRO].a_en = false;
+
+ st->sensor[SENSOR_ACCEL].g_en = false;
+ st->sensor[SENSOR_GYRO].g_en = true;
+
+ st->sensor[SENSOR_ACCEL].c_en = false;
+ st->sensor[SENSOR_GYRO].c_en = false;
+
+ st->sensor[SENSOR_ACCEL].p_en = false;
+ st->sensor[SENSOR_GYRO].p_en = false;
+
+ st->sensor[SENSOR_ACCEL].engine_base = ENGINE_ACCEL;
+ st->sensor[SENSOR_GYRO].engine_base = ENGINE_GYRO;
+
+ st->sensor_l[SENSOR_L_ACCEL].base = SENSOR_ACCEL;
+ st->sensor_l[SENSOR_L_GESTURE_ACCEL].base = SENSOR_ACCEL;
+ st->sensor_l[SENSOR_L_GYRO].base = SENSOR_GYRO;
+ st->sensor_l[SENSOR_L_GYRO_CAL].base = SENSOR_GYRO;
+ st->sensor_l[SENSOR_L_EIS_GYRO].base = SENSOR_GYRO;
+
+ st->sensor_l[SENSOR_L_ACCEL_WAKE].base = SENSOR_ACCEL;
+ st->sensor_l[SENSOR_L_GYRO_WAKE].base = SENSOR_GYRO;
+
+ st->sensor_l[SENSOR_L_GYRO_CAL_WAKE].base = SENSOR_GYRO;
+
+ st->sensor_l[SENSOR_L_ACCEL].header = ACCEL_HDR;
+ st->sensor_l[SENSOR_L_GESTURE_ACCEL].header = ACCEL_HDR;
+ st->sensor_l[SENSOR_L_GYRO].header = GYRO_HDR;
+ st->sensor_l[SENSOR_L_GYRO_CAL].header = GYRO_CALIB_HDR;
+
+ st->sensor_l[SENSOR_L_EIS_GYRO].header = EIS_GYRO_HDR;
+ st->sensor_l[SENSOR_L_SIXQ].header = SIXQUAT_HDR;
+ st->sensor_l[SENSOR_L_THREEQ].header = LPQ_HDR;
+ st->sensor_l[SENSOR_L_NINEQ].header = NINEQUAT_HDR;
+ st->sensor_l[SENSOR_L_PEDQ].header = PEDQUAT_HDR;
+
+ st->sensor_l[SENSOR_L_ACCEL_WAKE].header = ACCEL_WAKE_HDR;
+ st->sensor_l[SENSOR_L_GYRO_WAKE].header = GYRO_WAKE_HDR;
+ st->sensor_l[SENSOR_L_GYRO_CAL_WAKE].header = GYRO_CALIB_WAKE_HDR;
+ st->sensor_l[SENSOR_L_MAG_WAKE].header = COMPASS_WAKE_HDR;
+ st->sensor_l[SENSOR_L_MAG_CAL_WAKE].header = COMPASS_CALIB_WAKE_HDR;
+ st->sensor_l[SENSOR_L_SIXQ_WAKE].header = SIXQUAT_WAKE_HDR;
+ st->sensor_l[SENSOR_L_NINEQ_WAKE].header = NINEQUAT_WAKE_HDR;
+ st->sensor_l[SENSOR_L_PEDQ_WAKE].header = PEDQUAT_WAKE_HDR;
+
+ st->sensor_l[SENSOR_L_ACCEL].wake_on = false;
+ st->sensor_l[SENSOR_L_GYRO].wake_on = false;
+ st->sensor_l[SENSOR_L_GYRO_CAL].wake_on = false;
+ st->sensor_l[SENSOR_L_MAG].wake_on = false;
+ st->sensor_l[SENSOR_L_MAG_CAL].wake_on = false;
+ st->sensor_l[SENSOR_L_EIS_GYRO].wake_on = false;
+ st->sensor_l[SENSOR_L_SIXQ].wake_on = false;
+ st->sensor_l[SENSOR_L_NINEQ].wake_on = false;
+ st->sensor_l[SENSOR_L_PEDQ].wake_on = false;
+
+ st->sensor_l[SENSOR_L_ACCEL_WAKE].wake_on = true;
+ st->sensor_l[SENSOR_L_GYRO_WAKE].wake_on = true;
+ st->sensor_l[SENSOR_L_GYRO_CAL_WAKE].wake_on = true;
+ st->sensor_l[SENSOR_L_MAG_WAKE].wake_on = true;
+ st->sensor_l[SENSOR_L_SIXQ_WAKE].wake_on = true;
+ st->sensor_l[SENSOR_L_NINEQ_WAKE].wake_on = true;
+ st->sensor_l[SENSOR_L_PEDQ_WAKE].wake_on = true;
+}
+
+static int inv_init_config(struct inv_mpu_state *st)
+{
+ int res, i;
+
+ st->batch.overflow_on = 0;
+ st->chip_config.fsr = MPU_INIT_GYRO_SCALE;
+ st->chip_config.accel_fs = MPU_INIT_ACCEL_SCALE;
+ st->ped.int_thresh = MPU_INIT_PED_INT_THRESH;
+ st->ped.step_thresh = MPU_INIT_PED_STEP_THRESH;
+ st->chip_config.low_power_gyro_on = 1;
+ st->eis.count_precision = NSEC_PER_MSEC;
+ st->firmware = 0;
+ st->fifo_count_mode = BYTE_MODE;
+#ifdef TIMER_BASED_BATCHING
+ st->batch_timeout = 0;
+ st->is_batch_timer_running = false;
+#endif
+
+ st->eng_info[ENGINE_GYRO].base_time = NSEC_PER_SEC;
+ st->eng_info[ENGINE_ACCEL].base_time = NSEC_PER_SEC;
+
+ inv_init_sensor_struct(st);
+ res = inv_read_timebase(st);
+ if (res)
+ return res;
+
+ res = inv_set_gyro_sf(st);
+ if (res)
+ return res;
+ res = inv_set_accel_sf(st);
+ if (res)
+ return res;
+ res = inv_set_accel_intel(st);
+ if (res)
+ return res;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].ts = 0;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].previous_ts = 0;
+
+ return res;
+}
+
+int inv_mpu_initialize(struct inv_mpu_state *st)
+{
+ u8 v;
+ int result;
+ struct inv_chip_config_s *conf;
+ struct mpu_platform_data *plat;
+
+ conf = &st->chip_config;
+ plat = &st->plat_data;
+
+ /* verify whoami */
+ result = inv_plat_read(st, REG_WHO_AM_I, 1, &v);
+ if (result)
+ return result;
+ pr_info("whoami= %x\n", v);
+ if (v == 0x00 || v == 0xff)
+ return -ENODEV;
+
+ /* reset to make sure previous state are not there */
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1, BIT_H_RESET);
+ if (result)
+ return result;
+ usleep_range(REG_UP_TIME_USEC, REG_UP_TIME_USEC);
+ msleep(100);
+ /* toggle power state */
+ result = inv_set_power(st, false);
+ if (result)
+ return result;
+ result = inv_set_power(st, true);
+ if (result)
+ return result;
+
+ result = inv_plat_single_write(st, REG_USER_CTRL, st->i2c_dis);
+ if (result)
+ return result;
+ result = inv_init_config(st);
+ if (result)
+ return result;
+
+ result = mem_r(MPU_SOFT_REV_ADDR, 1, &v);
+ pr_info("sw_rev=%x, res=%d\n", v, result);
+ if (result)
+ return result;
+ st->chip_config.lp_en_mode_off = 0;
+
+ pr_info("%s: Mask %X, v = %X, lp mode = %d\n", __func__,
+ MPU_SOFT_REV_MASK, v, st->chip_config.lp_en_mode_off);
+ result = inv_set_power(st, false);
+
+ pr_info("%s: initialize result is %d....\n", __func__, result);
+ return 0;
+}
diff --git a/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_parsing_20680.c b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_parsing_20680.c
new file mode 100644
index 000000000000..0f17b6dd2c52
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_parsing_20680.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2017-2018 InvenSense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "inv_mpu: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/math64.h>
+
+#include "../inv_mpu_iio.h"
+
+static char iden[] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
+
+static int inv_process_gyro(struct inv_mpu_state *st, u8 *d, u64 t)
+{
+ s16 raw[3];
+ s32 calib[3];
+ int i;
+#define BIAS_UNIT 2859
+
+ for (i = 0; i < 3; i++)
+ raw[i] = be16_to_cpup((__be16 *) (d + i * 2));
+
+ for (i = 0; i < 3; i++)
+ calib[i] = (raw[i] << 15);
+
+
+ inv_push_gyro_data(st, raw, calib, t);
+
+ return 0;
+}
+
+static int inv_check_fsync(struct inv_mpu_state *st, u8 fsync_status)
+{
+ u8 data[1];
+
+ if (!st->chip_config.eis_enable)
+ return 0;
+ inv_plat_read(st, REG_FSYNC_INT, 1, data);
+ if (data[0] & BIT_FSYNC_INT) {
+ pr_debug("fsync\n");
+ st->eis.eis_triggered = true;
+ st->eis.fsync_delay = 1;
+ st->eis.prev_state = 1;
+ st->eis.frame_count++;
+ st->eis.eis_frame = true;
+ }
+ st->header_count--;
+
+ return 0;
+}
+
+static int inv_push_sensor(struct inv_mpu_state *st, int ind, u64 t, u8 *d)
+{
+#ifdef ACCEL_BIAS_TEST
+ s16 acc[3], avg[3];
+#endif
+
+ switch (ind) {
+ case SENSOR_ACCEL:
+ inv_convert_and_push_8bytes(st, ind, d, t, iden);
+#ifdef ACCEL_BIAS_TEST
+ acc[0] = be16_to_cpup((__be16 *) (d));
+ acc[1] = be16_to_cpup((__be16 *) (d + 2));
+ acc[2] = be16_to_cpup((__be16 *) (d + 4));
+ if(inv_get_3axis_average(acc, avg, 0)){
+ pr_debug("accel 200 samples average = %5d, %5d, %5d\n", avg[0], avg[1], avg[2]);
+ }
+#endif
+ break;
+ case SENSOR_TEMP:
+ inv_check_fsync(st, d[1]);
+ break;
+ case SENSOR_GYRO:
+ inv_process_gyro(st, d, t);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int inv_push_20680_data(struct inv_mpu_state *st, u8 *d)
+{
+ u8 *dptr;
+ int i;
+
+ dptr = d;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on) {
+ inv_get_dmp_ts(st, i);
+ if (st->sensor[i].send && (!st->ts_algo.first_sample)) {
+ st->sensor[i].sample_calib++;
+ inv_push_sensor(st, i, st->sensor[i].ts, dptr);
+ }
+ dptr += st->sensor[i].sample_size;
+ }
+ }
+ if (st->ts_algo.first_sample)
+ st->ts_algo.first_sample--;
+ st->header_count--;
+
+ return 0;
+}
+
+static int inv_process_20680_data(struct inv_mpu_state *st)
+{
+ int total_bytes, tmp, res, fifo_count, pk_size, i;
+ u8 *dptr, *d;
+ u8 data[14];
+ bool done_flag;
+ u8 v;
+#ifdef SENSOR_DATA_FROM_REGISTERS
+ u8 reg;
+ int len;
+#endif
+
+ if(st->gesture_only_on && (!st->batch.timeout)) {
+ res = inv_plat_read(st, REG_INT_STATUS, 1, data);
+ if (res)
+ return res;
+ pr_debug("ges cnt=%d, statu=%x\n",
+ st->gesture_int_count, data[0]);
+ if (data[0] & (BIT_WOM_ALL_INT_EN)) {
+ if (!st->gesture_int_count) {
+ inv_switch_power_in_lp(st, true);
+ res = inv_plat_single_write(st, REG_INT_ENABLE,
+ BIT_WOM_ALL_INT_EN | BIT_DATA_RDY_EN);
+ if (res)
+ return res;
+ v = 0;
+ if (st->chip_config.gyro_enable)
+ v |= BITS_GYRO_FIFO_EN;
+
+ if (st->chip_config.accel_enable)
+ v |= BIT_ACCEL_FIFO_EN;
+ res = inv_plat_single_write(st, REG_FIFO_EN, v);
+ if (res)
+ return res;
+ /* First time wake up from WOM.
+ We don't need data in the FIFO */
+ res = inv_reset_fifo(st, true);
+ if (res)
+ return res;
+ res = inv_switch_power_in_lp(st, false);
+ st->gesture_int_count = WOM_DELAY_THRESHOLD;
+
+ return res;
+ }
+ st->gesture_int_count = WOM_DELAY_THRESHOLD;
+ } else {
+ if (!st->gesture_int_count) {
+ inv_switch_power_in_lp(st, true);
+ res = inv_plat_single_write(st, REG_FIFO_EN, 0);
+ res = inv_plat_single_write(st, REG_INT_ENABLE,
+ BIT_WOM_ALL_INT_EN);
+ inv_switch_power_in_lp(st, false);
+
+ return res;
+ }
+ st->gesture_int_count--;
+ }
+ }
+
+ fifo_count = inv_get_last_run_time_non_dmp_record_mode(st);
+ pr_debug("fifc= %d\n", fifo_count);
+ if (!fifo_count) {
+ pr_debug("REG_FIFO_COUNT_H size is 0\n");
+ return 0;
+ }
+ pk_size = st->batch.pk_size;
+ if (!pk_size)
+ return -EINVAL;
+
+ if (fifo_count >= (HARDWARE_FIFO_SIZE / st->batch.pk_size)) {
+ pr_warn("fifo overflow pkt count=%d pkt sz=%d\n", fifo_count, st->batch.pk_size);
+ return -EOVERFLOW;
+ }
+
+ fifo_count *= st->batch.pk_size;
+ st->fifo_count = fifo_count;
+ d = st->fifo_data_store;
+ dptr = d;
+ total_bytes = fifo_count;
+
+#ifdef SENSOR_DATA_FROM_REGISTERS
+ len = 0;
+ if (st->sensor[SENSOR_GYRO].on) {
+ reg = REG_RAW_GYRO;
+ len += BYTES_PER_SENSOR;
+ if (st->sensor[SENSOR_ACCEL].on && !st->sensor[SENSOR_TEMP].on)
+ len += BYTES_FOR_TEMP;
+ }
+ if (st->sensor[SENSOR_TEMP].on) {
+ reg = REG_RAW_TEMP;
+ len += BYTES_FOR_TEMP;
+ }
+ if (st->sensor[SENSOR_ACCEL].on) {
+ reg = REG_RAW_ACCEL;
+ len += BYTES_PER_SENSOR;
+ }
+
+ if (len == 0) {
+ pr_debug("No sensor is enabled\n");
+ return 0;
+ }
+
+ /* read data registers */
+ res = inv_plat_read(st, reg, len, data);
+ if (res < 0) {
+ pr_err("read data registers is failed\n");
+ return res;
+ }
+
+ /* copy sensor data to buffer as FIFO data format */
+ tmp = 0;
+ if (st->sensor[SENSOR_ACCEL].on) {
+ for (i = 0; i < BYTES_PER_SENSOR; i++)
+ dptr[i] = data[tmp + i];
+ dptr += BYTES_PER_SENSOR;
+ tmp += BYTES_PER_SENSOR;
+ }
+
+ if (st->sensor[SENSOR_TEMP].on) {
+ for (i = 0; i < BYTES_FOR_TEMP; i++)
+ dptr[i] = data[tmp + i];
+ dptr += BYTES_FOR_TEMP;
+ tmp += BYTES_FOR_TEMP;
+ }
+
+ if (st->sensor[SENSOR_GYRO].on) {
+ if (st->sensor[SENSOR_ACCEL].on && !st->sensor[SENSOR_TEMP].on)
+ tmp += BYTES_FOR_TEMP;
+ for (i = 0; i < BYTES_PER_SENSOR; i++)
+ dptr[i] = data[tmp + i];
+ }
+#else
+ while (total_bytes > 0) {
+ if (total_bytes < pk_size * MAX_FIFO_PACKET_READ)
+ tmp = total_bytes;
+ else
+ tmp = pk_size * MAX_FIFO_PACKET_READ;
+ res = inv_plat_read(st, REG_FIFO_R_W, tmp, dptr);
+ if (res < 0) {
+ pr_err("read REG_FIFO_R_W is failed\n");
+ return res;
+ }
+ pr_debug("inside: %x, %x, %x, %x, %x, %x, %x, %x\n", dptr[0], dptr[1], dptr[2],
+ dptr[3], dptr[4], dptr[5], dptr[6], dptr[7]);
+ pr_debug("insid2: %x, %x, %x, %x, %x, %x, %x, %x\n", dptr[8], dptr[9], dptr[10],
+ dptr[11], dptr[12], dptr[13], dptr[14], dptr[15]);
+
+ dptr += tmp;
+ total_bytes -= tmp;
+ }
+#endif /* SENSOR_DATA_FROM_REGISTERS */
+ dptr = d;
+ pr_debug("dd: %x, %x, %x, %x, %x, %x, %x, %x\n", d[0], d[1], d[2],
+ d[3], d[4], d[5], d[6], d[7]);
+ pr_debug("dd2: %x, %x, %x, %x, %x, %x, %x, %x\n", d[8], d[9], d[10],
+ d[11], d[12], d[13], d[14], d[15]);
+ total_bytes = fifo_count;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on) {
+ st->sensor[i].count = total_bytes / pk_size;
+ }
+ }
+ st->header_count = 0;
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on)
+ st->header_count = max(st->header_count,
+ st->sensor[i].count);
+ }
+
+ st->ts_algo.calib_counter++;
+ inv_bound_timestamp(st);
+
+ dptr = d;
+ done_flag = false;
+
+ while (!done_flag) {
+ pr_debug("total%d, pk=%d\n", total_bytes, pk_size);
+ if (total_bytes >= pk_size) {
+ res = inv_push_20680_data(st, dptr);
+ if (res)
+ return res;
+ total_bytes -= pk_size;
+ dptr += pk_size;
+ } else {
+ done_flag = true;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * _inv_read_fifo() - Transfer data from FIFO to ring buffer.
+ */
+static void _inv_read_fifo(struct inv_mpu_state *st)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ int result;
+
+ result = wait_event_interruptible_timeout(st->wait_queue,
+ st->resume_state, msecs_to_jiffies(300));
+ if (result <= 0)
+ return;
+ mutex_lock(&indio_dev->mlock);
+#ifdef TIMER_BASED_BATCHING
+ if (st->batch_timeout) {
+ if (inv_plat_single_write(st, REG_INT_ENABLE, st->int_en))
+ pr_err("REG_INT_ENABLE write error\n");
+ }
+#endif
+ st->wake_sensor_received = false;
+ result = inv_process_20680_data(st);
+ if (result)
+ goto err_reset_fifo;
+ mutex_unlock(&indio_dev->mlock);
+
+ if (st->wake_sensor_received)
+#ifdef CONFIG_HAS_WAKELOCK
+ wake_lock_timeout(&st->wake_lock, msecs_to_jiffies(200));
+#else
+ __pm_wakeup_event(&st->wake_lock, 200); /* 200 msecs */
+#endif
+ return;
+
+err_reset_fifo:
+ if ((!st->chip_config.gyro_enable) &&
+ (!st->chip_config.accel_enable) &&
+ (!st->chip_config.slave_enable) &&
+ (!st->chip_config.pressure_enable)) {
+ inv_switch_power_in_lp(st, false);
+ mutex_unlock(&indio_dev->mlock);
+
+ return;
+ }
+
+ pr_err("error to reset fifo\n");
+ inv_switch_power_in_lp(st, true);
+ inv_reset_fifo(st, true);
+ inv_switch_power_in_lp(st, false);
+ mutex_unlock(&indio_dev->mlock);
+
+ return;
+}
+
+irqreturn_t inv_read_fifo(int irq, void *dev_id)
+{
+ struct inv_mpu_state *st = (struct inv_mpu_state *)dev_id;
+
+ _inv_read_fifo(st);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef TIMER_BASED_BATCHING
+void inv_batch_work(struct work_struct *work)
+{
+ struct inv_mpu_state *st =
+ container_of(work, struct inv_mpu_state, batch_work);
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+
+ mutex_lock(&indio_dev->mlock);
+ if (inv_plat_single_write(st, REG_INT_ENABLE, st->int_en | BIT_DATA_RDY_EN))
+ pr_err("REG_INT_ENABLE write error\n");
+ mutex_unlock(&indio_dev->mlock);
+
+ return;
+}
+#endif
+
+int inv_flush_batch_data(struct iio_dev *indio_dev, int data)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+#ifndef SENSOR_DATA_FROM_REGISTERS
+ if (st->chip_config.gyro_enable ||
+ st->chip_config.accel_enable ||
+ st->chip_config.slave_enable ||
+ st->chip_config.pressure_enable) {
+ st->wake_sensor_received = 0;
+ inv_process_20680_data(st);
+ if (st->wake_sensor_received)
+#ifdef CONFIG_HAS_WAKELOCK
+ wake_lock_timeout(&st->wake_lock, msecs_to_jiffies(200));
+#else
+ __pm_wakeup_event(&st->wake_lock, 200); /* 200 msecs */
+#endif
+ inv_switch_power_in_lp(st, false);
+ }
+#endif /* SENSOR_DATA_FROM_REGISTERS */
+ inv_push_marker_to_buffer(st, END_MARKER, data);
+
+ return 0;
+}
+
diff --git a/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_selftest_20680.c b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_selftest_20680.c
new file mode 100644
index 000000000000..7a90b4d8b882
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_selftest_20680.c
@@ -0,0 +1,752 @@
+/*
+* Copyright (C) 2017-2018 InvenSense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#define pr_fmt(fmt) "inv_mpu: " fmt
+
+#include "../inv_mpu_iio.h"
+
+/* register settings */
+#define DEF_SELFTEST_GYRO_SENS (32768 / 250)
+/* wait time before collecting data */
+#define MAX_PACKETS 20
+#define SELFTEST_WAIT_TIME (MAX_PACKETS * 10)
+#define DEF_ST_STABLE_TIME 20
+#define DEF_GYRO_SCALE 131
+#define DEF_ST_PRECISION 1000
+#define DEF_ST_ACCEL_FS_MG 2000UL
+#define DEF_ST_SCALE 32768
+#define DEF_ST_TRY_TIMES 2
+#define DEF_ST_ACCEL_RESULT_SHIFT 1
+#define DEF_ST_SAMPLES 200
+
+#define DEF_ACCEL_ST_SHIFT_DELTA_MIN 500
+#define DEF_ACCEL_ST_SHIFT_DELTA_MAX 1500
+#define DEF_GYRO_CT_SHIFT_DELTA 500
+
+#define SENSOR_UP_TIME 30
+#define REG_UP_TIME 2
+
+#define DEF_ST_ACCEL_FS_MG 2000UL
+#define DEF_ACCEL_ST_SHIFT_DELTA 500
+#define ACCEL_ST_AL_MIN ((DEF_ACCEL_ST_AL_MIN * DEF_ST_SCALE \
+ / DEF_ST_ACCEL_FS_MG) * DEF_ST_PRECISION)
+#define ACCEL_ST_AL_MAX ((DEF_ACCEL_ST_AL_MAX * DEF_ST_SCALE \
+ / DEF_ST_ACCEL_FS_MG) * DEF_ST_PRECISION)
+
+#define THREE_AXIS 3
+#define DEF_ST_MPU6500_ACCEL_LPF 2
+#define DEF_SELFTEST_SAMPLE_RATE 0 /* 1000Hz */
+#define DEF_SELFTEST_SAMPLE_RATE_LP 3 /* 250Hz */
+#define DEF_SELFTEST_SAMPLE_RATE_ACC_LP 10 /* 250Hz LPOSC_CLKSEL */
+#define INV_MPU_SAMPLE_RATE_CHANGE_STABLE 50
+#define DEF_SELFTEST_6500_ACCEL_FS (0 << 3)
+#define DEF_SELFTEST_GYRO_FS (0 << 3)
+#define DEF_ST_6500_STABLE_TIME 20
+#define BIT_ACCEL_OUT 0x08
+#define BITS_GYRO_OUT 0x70
+#define THREE_AXIS 3
+#define DEF_GYRO_WAIT_TIME 10
+#define DEF_GYRO_WAIT_TIME_LP 50
+
+/* Gyro Offset Max Value (dps) */
+#define DEF_GYRO_OFFSET_MAX 20
+/* Gyro Self Test Absolute Limits ST_AL (dps) */
+#define DEF_GYRO_ST_AL 60
+/* Accel Self Test Absolute Limits ST_AL (mg) */
+#define DEF_ACCEL_ST_AL_MIN 225
+#define DEF_ACCEL_ST_AL_MAX 675
+
+struct recover_regs {
+ u8 int_enable; /* REG_INT_ENABLE */
+ u8 fifo_en; /* REG_FIFO_EN */
+ u8 user_ctrl; /* REG_USER_CTRL */
+ u8 config; /* REG_CONFIG */
+ u8 gyro_config; /* REG_GYRO_CONFIG */
+ u8 accel_config; /* REG_ACCEL_CONFIG */
+ u8 accel_config_2; /* REG_ACCEL_CONFIG_2 */
+ u8 smplrt_div; /* REG_SAMPLE_RATE_DIV */
+ u8 lp_mode; /* REG_LP_MODE_CTRL */
+ u8 pwr_mgmt_1; /* REG_PWR_MGMT_1 */
+ u8 pwr_mgmt_2; /* REG_PWR_MGMT_2 */
+};
+
+static struct recover_regs saved_regs;
+
+static const u16 mpu_st_tb[256] = {
+ 2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808,
+ 2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041,
+ 3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293,
+ 3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566,
+ 3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862,
+ 3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182,
+ 4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528,
+ 4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903,
+ 4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310,
+ 5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750,
+ 5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226,
+ 6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742,
+ 6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301,
+ 7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906,
+ 7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561,
+ 8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270,
+ 9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038,
+ 10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870,
+ 10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771,
+ 11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746,
+ 12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802,
+ 13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946,
+ 15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184,
+ 16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526,
+ 17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978,
+ 19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550,
+ 20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253,
+ 22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097,
+ 24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093,
+ 26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255,
+ 28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597,
+ 30903, 31212, 31524, 31839, 32157, 32479, 32804
+};
+
+static void inv_show_saved_setting(struct inv_mpu_state *st)
+{
+ pr_debug(" REG_INT_ENABLE : 0x%02X\n", saved_regs.int_enable);
+ pr_debug(" REG_FIFO_EN : 0x%02X\n", saved_regs.fifo_en);
+ pr_debug(" REG_USER_CTRL : 0x%02X\n", saved_regs.user_ctrl);
+ pr_debug(" REG_CONFIG : 0x%02X\n", saved_regs.config);
+ pr_debug(" REG_GYRO_CONFIG : 0x%02X\n", saved_regs.gyro_config);
+ pr_debug(" REG_ACCEL_CONFIG : 0x%02X\n", saved_regs.accel_config);
+ pr_debug(" REG_ACCEL_CONFIG_2 : 0x%02X\n", saved_regs.accel_config_2);
+ pr_debug(" REG_SAMPLE_RATE_DIV : 0x%02X\n", saved_regs.smplrt_div);
+ pr_debug(" REG_LP_MODE_CTRL : 0x%02X\n", saved_regs.lp_mode);
+ pr_debug(" REG_PWR_MGMT_1 : 0x%02X\n", saved_regs.pwr_mgmt_1);
+ pr_debug(" REG_PWR_MGMT_2 : 0x%02X\n", saved_regs.pwr_mgmt_2);
+}
+
+static int inv_save_setting(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_plat_read(st, REG_PWR_MGMT_1, 1,
+ &saved_regs.pwr_mgmt_1);
+ if (result)
+ return result;
+
+ /* wake up */
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1,
+ (saved_regs.pwr_mgmt_1 & ~BIT_SLEEP));
+ if (result)
+ return result;
+
+ result = inv_plat_read(st, REG_INT_ENABLE, 1,
+ &saved_regs.int_enable);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_FIFO_EN, 1,
+ &saved_regs.fifo_en);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_USER_CTRL, 1,
+ &saved_regs.user_ctrl);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_CONFIG, 1,
+ &saved_regs.config);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_GYRO_CONFIG, 1,
+ &saved_regs.gyro_config);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_ACCEL_CONFIG, 1,
+ &saved_regs.accel_config);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_ACCEL_CONFIG_2, 1,
+ &saved_regs.accel_config_2);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_SAMPLE_RATE_DIV, 1,
+ &saved_regs.smplrt_div);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_LP_MODE_CTRL, 1,
+ &saved_regs.lp_mode);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_PWR_MGMT_2, 1,
+ &saved_regs.pwr_mgmt_2);
+ if (result)
+ return result;
+
+ inv_show_saved_setting(st);
+
+ return result;
+}
+
+static int inv_recover_setting(struct inv_mpu_state *st)
+{
+ int result;
+ /* Stop sensors */
+ result = inv_plat_single_write(st, REG_PWR_MGMT_2,
+ BIT_PWR_ACCEL_STBY | BIT_PWR_GYRO_STBY);
+ if (result)
+ return result;
+
+ /* Restore sensor configurations */
+ result = inv_plat_single_write(st, REG_INT_ENABLE,
+ saved_regs.int_enable);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_FIFO_EN,
+ saved_regs.fifo_en);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_USER_CTRL,
+ saved_regs.user_ctrl);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_CONFIG,
+ saved_regs.config);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_GYRO_CONFIG,
+ saved_regs.gyro_config);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_ACCEL_CONFIG,
+ saved_regs.accel_config);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_ACCEL_CONFIG_2,
+ saved_regs.accel_config_2);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_SAMPLE_RATE_DIV,
+ saved_regs.smplrt_div);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_LP_MODE_CTRL,
+ saved_regs.lp_mode);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1,
+ saved_regs.pwr_mgmt_1);
+ if (result)
+ return result;
+
+ result = inv_plat_single_write(st, REG_PWR_MGMT_2,
+ saved_regs.pwr_mgmt_2);
+ if (result)
+ return result;
+
+ return result;
+}
+
+int inv_switch_engine(struct inv_mpu_state *st, bool en, u32 mask)
+{
+ u8 data, mgmt_1;
+ int result;
+
+ if (BIT_PWR_GYRO_STBY == mask) {
+ result = inv_plat_read(st, REG_PWR_MGMT_1, 1, &mgmt_1);
+ if (result)
+ return result;
+ mgmt_1 &= ~BIT_CLK_MASK;
+ }
+
+ if ((BIT_PWR_GYRO_STBY == mask) && (!en)) {
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1, mgmt_1);
+ if (result)
+ return result;
+ }
+
+ result = inv_plat_read(st, REG_PWR_MGMT_2, 1, &data);
+ if (result)
+ return result;
+ if (en)
+ data &= (~mask);
+ else
+ data |= mask;
+ data |= BIT_FIFO_LP_EN;
+ result = inv_plat_single_write(st, REG_PWR_MGMT_2, data);
+ if (result)
+ return result;
+
+ if ((BIT_PWR_GYRO_STBY == mask) && en) {
+ /* only gyro on needs sensor up time */
+ msleep(SENSOR_UP_TIME);
+ /* after gyro is on & stable, switch internal clock to PLL */
+ mgmt_1 |= BIT_CLK_PLL;
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1, mgmt_1);
+ if (result)
+ return result;
+ }
+ if ((BIT_PWR_ACCEL_STBY == mask) && en)
+ msleep(REG_UP_TIME);
+
+ return 0;
+}
+
+int inv_set_offset_reg(struct inv_mpu_state *st, int reg, int val)
+{
+ int result;
+ u8 d;
+
+ d = ((val >> 8) & 0xff);
+ result = inv_plat_single_write(st, reg, d);
+ if (result)
+ return result;
+
+ d = (val & 0xff);
+ result = inv_plat_single_write(st, reg + 1, d);
+
+ return result;
+}
+
+/**
+* inv_check_gyro_self_test() - check gyro self test. this function
+* returns zero as success. A non-zero return
+* value indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+int inv_check_gyro_self_test(struct inv_mpu_state *st,
+ int *reg_avg, int *st_avg) {
+ u8 regs[3];
+ int ret_val, result;
+ int otp_value_zero = 0;
+ int st_shift_prod[3], st_shift_cust[3], i;
+
+ ret_val = 0;
+ result = inv_plat_read(st, REG_6500_XG_ST_DATA, 3, regs);
+ if (result)
+ return result;
+ pr_debug("%s self_test gyro shift_code - %02x %02x %02x\n",
+ st->hw->name, regs[0], regs[1], regs[2]);
+
+ for (i = 0; i < 3; i++) {
+ if (regs[i] != 0) {
+ st_shift_prod[i] = mpu_st_tb[regs[i] - 1];
+ } else {
+ st_shift_prod[i] = 0;
+ otp_value_zero = 1;
+ }
+ }
+ pr_debug("%s self_test gyro st_shift_prod - %+d %+d %+d\n",
+ st->hw->name, st_shift_prod[0], st_shift_prod[1],
+ st_shift_prod[2]);
+
+ for (i = 0; i < 3; i++) {
+ st_shift_cust[i] = st_avg[i] - reg_avg[i];
+ if (!otp_value_zero) {
+ /* Self Test Pass/Fail Criteria A */
+ if (st_shift_cust[i] < DEF_GYRO_CT_SHIFT_DELTA
+ * st_shift_prod[i])
+ ret_val = 1;
+ } else {
+ /* Self Test Pass/Fail Criteria B */
+ if (st_shift_cust[i] < DEF_GYRO_ST_AL *
+ DEF_SELFTEST_GYRO_SENS *
+ DEF_ST_PRECISION)
+ ret_val = 1;
+ }
+ }
+ pr_debug("%s self_test gyro st_shift_cust - %+d %+d %+d\n",
+ st->hw->name, st_shift_cust[0], st_shift_cust[1],
+ st_shift_cust[2]);
+
+ if (ret_val == 0) {
+ /* Self Test Pass/Fail Criteria C */
+ for (i = 0; i < 3; i++)
+ if (abs(reg_avg[i]) > DEF_GYRO_OFFSET_MAX *
+ DEF_SELFTEST_GYRO_SENS *
+ DEF_ST_PRECISION)
+ ret_val = 1;
+ }
+
+ return ret_val;
+}
+
+/**
+* inv_check_accel_self_test() - check 6500 accel self test. this function
+* returns zero as success. A non-zero return
+* value indicates failure in self test.
+* @*st: main data structure.
+* @*reg_avg: average value of normal test.
+* @*st_avg: average value of self test
+*/
+int inv_check_accel_self_test(struct inv_mpu_state *st,
+ int *reg_avg, int *st_avg) {
+ int ret_val, result;
+ int st_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i;
+ u8 regs[3];
+ int otp_value_zero = 0;
+
+ ret_val = 0;
+ result = inv_plat_read(st, REG_6500_XA_ST_DATA, 3, regs);
+ if (result)
+ return result;
+ pr_debug("%s self_test accel shift_code - %02x %02x %02x\n",
+ st->hw->name, regs[0], regs[1], regs[2]);
+
+ for (i = 0; i < 3; i++) {
+ if (regs[i] != 0) {
+ st_shift_prod[i] = mpu_st_tb[regs[i] - 1];
+ } else {
+ st_shift_prod[i] = 0;
+ otp_value_zero = 1;
+ }
+ }
+ pr_debug("%s self_test accel st_shift_prod - %+d %+d %+d\n",
+ st->hw->name, st_shift_prod[0], st_shift_prod[1],
+ st_shift_prod[2]);
+
+ if (!otp_value_zero) {
+ /* Self Test Pass/Fail Criteria A */
+ for (i = 0; i < 3; i++) {
+ st_shift_cust[i] = st_avg[i] - reg_avg[i];
+ st_shift_ratio[i] = abs(st_shift_cust[i] /
+ st_shift_prod[i] - DEF_ST_PRECISION);
+ if (st_shift_ratio[i] > DEF_ACCEL_ST_SHIFT_DELTA)
+ ret_val = 1;
+ }
+ } else {
+ /* Self Test Pass/Fail Criteria B */
+ for (i = 0; i < 3; i++) {
+ st_shift_cust[i] = abs(st_avg[i] - reg_avg[i]);
+ if (st_shift_cust[i] < ACCEL_ST_AL_MIN ||
+ st_shift_cust[i] > ACCEL_ST_AL_MAX)
+ ret_val = 1;
+ }
+ }
+ pr_debug("%s self_test accel st_shift_cust - %+d %+d %+d\n",
+ st->hw->name, st_shift_cust[0], st_shift_cust[1],
+ st_shift_cust[2]);
+
+ return ret_val;
+}
+
+/*
+ * inv_do_test() - do the actual test of self testing
+ */
+int inv_do_test(struct inv_mpu_state *st, int self_test_flag,
+ int *gyro_result, int *accel_result, int lp_mode)
+{
+ int result, i, j, packet_size;
+ u8 data[BYTES_PER_SENSOR * 2], d, dd;
+ int fifo_count, packet_count, ind, s;
+
+ packet_size = BYTES_PER_SENSOR * 2;
+
+ /* disable interrupt */
+ result = inv_plat_single_write(st, REG_INT_ENABLE, 0);
+ if (result)
+ return result;
+ /* disable the sensor output to FIFO */
+ result = inv_plat_single_write(st, REG_FIFO_EN, 0);
+ if (result)
+ return result;
+ /* disable fifo reading */
+ result = inv_plat_single_write(st, REG_USER_CTRL, 0);
+ if (result)
+ return result;
+ /* clear FIFO */
+ result = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_RST);
+ if (result)
+ return result;
+ /* setup parameters */
+ result = inv_plat_single_write(st, REG_CONFIG, INV_FILTER_98HZ);
+ if (result)
+ return result;
+
+ /* gyro lp mode */
+ if (lp_mode == 1)
+ d = BIT_GYRO_CYCLE_EN;
+ else if (lp_mode == 2)
+ d = DEF_SELFTEST_SAMPLE_RATE_ACC_LP;
+ else
+ d = 0;
+ result = inv_plat_single_write(st, REG_LP_MODE_CTRL, d);
+ if (result)
+ return result;
+
+ /* config accel LPF register */
+ if (lp_mode == 2)
+ d = BIT_ACCEL_FCHOCIE_B;
+ else
+ d = DEF_ST_MPU6500_ACCEL_LPF;
+ result = inv_plat_single_write(st, REG_6500_ACCEL_CONFIG2, d);
+ if (result)
+ return result;
+
+ if (lp_mode) {
+ result = inv_plat_single_write(st, REG_SAMPLE_RATE_DIV,
+ DEF_SELFTEST_SAMPLE_RATE_LP);
+ } else {
+ result = inv_plat_single_write(st, REG_SAMPLE_RATE_DIV,
+ DEF_SELFTEST_SAMPLE_RATE);
+ }
+ if (result)
+ return result;
+ /* wait for the sampling rate change to stabilize */
+ mdelay(INV_MPU_SAMPLE_RATE_CHANGE_STABLE);
+ result = inv_plat_single_write(st, REG_GYRO_CONFIG,
+ self_test_flag | DEF_SELFTEST_GYRO_FS);
+ if (result)
+ return result;
+
+ d = DEF_SELFTEST_6500_ACCEL_FS;
+ d |= self_test_flag;
+ result = inv_plat_single_write(st, REG_ACCEL_CONFIG, d);
+ if (result)
+ return result;
+
+ /* wait for the output to get stable */
+ msleep(DEF_ST_6500_STABLE_TIME);
+
+ /* enable FIFO reading */
+ result = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_EN);
+ if (result)
+ return result;
+ /* enable sensor output to FIFO */
+ d = BITS_GYRO_OUT | BIT_ACCEL_OUT;
+ for (i = 0; i < THREE_AXIS; i++) {
+ gyro_result[i] = 0;
+ accel_result[i] = 0;
+ }
+ s = 0;
+ while (s < 200 /*st->self_test.samples*/) {
+ /* Stop FIFO */
+ result = inv_plat_single_write(st, REG_USER_CTRL, 0);
+ if (result)
+ return result;
+ /* clear FIFO */
+ result = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_RST);
+ if (result)
+ return result;
+ /* enable FIFO reading */
+ result = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_EN);
+ if (result)
+ return result;
+
+ /* accel lp mode */
+ dd = BIT_CLK_PLL;
+ if (lp_mode == 2)
+ dd |= BIT_LP_EN;
+ else
+ dd &= ~BIT_LP_EN;
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1, dd);
+ if (result)
+ return result;
+
+ result = inv_plat_single_write(st, REG_FIFO_EN, d);
+ if (result)
+ return result;
+ if (lp_mode)
+ mdelay(DEF_GYRO_WAIT_TIME_LP);
+ else
+ mdelay(DEF_GYRO_WAIT_TIME);
+
+ result = inv_plat_single_write(st, REG_FIFO_EN, 0);
+ if (result)
+ return result;
+
+ result = inv_plat_read(st, REG_FIFO_COUNT_H,
+ FIFO_COUNT_BYTE, data);
+ if (result)
+ return result;
+ fifo_count = be16_to_cpup((__be16 *)(&data[0]));
+ pr_debug("%s self_test fifo_count - %d\n",
+ st->hw->name, fifo_count);
+ packet_count = fifo_count / packet_size;
+ i = 0;
+ while ((i < packet_count) && (s < 200 /*st->self_test.samples*/)) {
+ short vals[3];
+ result = inv_plat_read(st, REG_FIFO_R_W,
+ packet_size, data);
+ if (result)
+ return result;
+ ind = 0;
+
+ for (j = 0; j < THREE_AXIS; j++) {
+ vals[j] = (short)be16_to_cpup(
+ (__be16 *)(&data[ind + 2 * j]));
+ accel_result[j] += vals[j];
+ }
+ ind += BYTES_PER_SENSOR;
+ pr_debug(
+ "%s self_test accel data - %d %+d %+d %+d",
+ st->hw->name, s, vals[0], vals[1], vals[2]);
+
+ for (j = 0; j < THREE_AXIS; j++) {
+ vals[j] = (short)be16_to_cpup(
+ (__be16 *)(&data[ind + 2 * j]));
+ gyro_result[j] += vals[j];
+ }
+ pr_debug("%s self_test gyro data - %d %+d %+d %+d",
+ st->hw->name, s, vals[0], vals[1], vals[2]);
+
+ s++;
+ i++;
+ }
+ }
+
+ for (j = 0; j < THREE_AXIS; j++) {
+ accel_result[j] = accel_result[j] / s;
+ accel_result[j] *= DEF_ST_PRECISION;
+ }
+ for (j = 0; j < THREE_AXIS; j++) {
+ gyro_result[j] = gyro_result[j] / s;
+ gyro_result[j] *= DEF_ST_PRECISION;
+ }
+
+ return 0;
+}
+
+
+int inv_power_up_self_test(struct inv_mpu_state *st)
+{
+ int result;
+
+ result = inv_switch_power_in_lp(st, true);
+
+ /* make sure no interrupts */
+ result = inv_plat_single_write(st, REG_INT_ENABLE, 0);
+ if (result)
+ return result;
+
+ if (result)
+ return result;
+ result = inv_switch_engine(st, true, BIT_PWR_ACCEL_STBY);
+ if (result)
+ return result;
+ result = inv_switch_engine(st, true, BIT_PWR_GYRO_STBY);
+ if (result)
+ return result;
+
+ return 0;
+}
+
+/*
+ * inv_hw_self_test() - main function to do hardware self test
+ */
+int inv_hw_self_test(struct inv_mpu_state *st)
+{
+ int result;
+ int gyro_bias_st[THREE_AXIS], gyro_bias_regular[THREE_AXIS];
+ int accel_bias_st[THREE_AXIS], accel_bias_regular[THREE_AXIS];
+#if 0
+ int gyro_bias_regular_lp[THREE_AXIS];
+ int accel_bias_regular_lp[THREE_AXIS];
+ int dummy_bias_regular[THREE_AXIS];
+#endif
+ int test_times, i;
+ char accel_result, gyro_result;
+
+ result = inv_save_setting(st);
+ if (result)
+ return result;
+
+ result = inv_power_up_self_test(st);
+ if (result)
+ return result;
+ accel_result = 0;
+ gyro_result = 0;
+ test_times = DEF_ST_TRY_TIMES;
+ while (test_times > 0) {
+ result = inv_do_test(st, 0, gyro_bias_regular,
+ accel_bias_regular, 0);
+ if (result == -EAGAIN)
+ test_times--;
+ else
+ test_times = 0;
+ }
+ if (result)
+ goto test_fail;
+ pr_debug("%s self_test accel bias_regular - %+d %+d %+d\n",
+ st->hw->name, accel_bias_regular[0],
+ accel_bias_regular[1], accel_bias_regular[2]);
+ pr_debug("%s self_test gyro bias_regular - %+d %+d %+d\n",
+ st->hw->name, gyro_bias_regular[0], gyro_bias_regular[1],
+ gyro_bias_regular[2]);
+
+ test_times = DEF_ST_TRY_TIMES;
+ while (test_times > 0) {
+ result = inv_do_test(st, BITS_SELF_TEST_EN, gyro_bias_st,
+ accel_bias_st, 0);
+ if (result == -EAGAIN)
+ test_times--;
+ else
+ break;
+ }
+ if (result)
+ goto test_fail;
+ pr_debug("%s self_test accel bias_st - %+d %+d %+d\n",
+ st->hw->name, accel_bias_st[0], accel_bias_st[1],
+ accel_bias_st[2]);
+ pr_debug("%s self_test gyro bias_st - %+d %+d %+d\n",
+ st->hw->name, gyro_bias_st[0], gyro_bias_st[1],
+ gyro_bias_st[2]);
+
+#if 0
+ /* lp gyro mode */
+ test_times = DEF_ST_TRY_TIMES;
+ while (test_times > 0) {
+ result = inv_do_test(st, 0, gyro_bias_regular_lp,
+ dummy_bias_regular, 1);
+ if (result == -EAGAIN)
+ test_times--;
+ else
+ test_times = 0;
+ }
+ if (result)
+ goto test_fail;
+ pr_debug("%s self_test gyro bias_regular lp - %+d %+d %+d\n",
+ st->hw->name, gyro_bias_regular_lp[0], gyro_bias_regular_lp[1],
+ gyro_bias_regular_lp[2]);
+
+ /* lp accel mode */
+ test_times = DEF_ST_TRY_TIMES;
+ while (test_times > 0) {
+ result = inv_do_test(st, 0, dummy_bias_regular,
+ accel_bias_regular_lp, 2);
+ if (result == -EAGAIN)
+ test_times--;
+ else
+ test_times = 0;
+ }
+ if (result)
+ goto test_fail;
+ pr_debug("%s self_test accel bias_regular lp - %+d %+d %+d\n",
+ st->hw->name, accel_bias_regular_lp[0],
+ accel_bias_regular_lp[1], accel_bias_regular_lp[2]);
+#endif
+
+ /* copy bias */
+ for (i = 0; i < 3; i++) {
+ /* gyro : LN bias as LN is default mode */
+ st->gyro_st_bias[i] = gyro_bias_regular[i] / DEF_ST_PRECISION;
+ /* accel : LN bias as LN is default mode */
+ st->accel_st_bias[i] = accel_bias_regular[i] / DEF_ST_PRECISION;
+ }
+
+ /* Check is done on continuous mode data */
+ accel_result = !inv_check_accel_self_test(st,
+ accel_bias_regular, accel_bias_st);
+ gyro_result = !inv_check_gyro_self_test(st,
+ gyro_bias_regular, gyro_bias_st);
+
+test_fail:
+ inv_recover_setting(st);
+ return (accel_result << DEF_ST_ACCEL_RESULT_SHIFT) | gyro_result;
+}
diff --git a/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_setup_20680.c b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_setup_20680.c
new file mode 100644
index 000000000000..5e9cf8906d79
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/iam20680/inv_mpu_setup_20680.c
@@ -0,0 +1,466 @@
+/*
+* Copyright (C) 2017-2018 InvenSense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#define pr_fmt(fmt) "inv_mpu: " fmt
+#include "../inv_mpu_iio.h"
+
+/* set LN mode for gyro regardless of conditions */
+#define USE_GYRO_LN_MODE
+
+static int inv_calc_engine_dur(struct inv_engine_info *ei)
+{
+ if (!ei->running_rate)
+ return -EINVAL;
+ ei->dur = ei->base_time / ei->orig_rate;
+ ei->dur *= ei->divider;
+
+ return 0;
+}
+
+static int inv_turn_on_fifo(struct inv_mpu_state *st)
+{
+ u8 int_en, fifo_en, mode, user;
+ int r;
+
+ r = inv_plat_single_write(st, REG_FIFO_EN, 0);
+ if (r)
+ return r;
+ r = inv_plat_single_write(st, REG_USER_CTRL, BIT_FIFO_RST);
+ if (r)
+ return r;
+ fifo_en = 0;
+ int_en = 0;
+
+ if (st->gesture_only_on && (!st->batch.timeout)) {
+ st->gesture_int_count = WOM_DELAY_THRESHOLD;
+ int_en |= BIT_WOM_ALL_INT_EN;
+ }
+#ifdef TIMER_BASED_BATCHING
+ if (st->chip_config.eis_enable)
+ int_en |= BIT_FSYNC_INT_EN;
+ if (!st->batch_timeout) {
+ int_en |= BIT_DATA_RDY_EN;
+ }
+#else
+ if (st->batch.timeout) {
+ if(!st->batch.fifo_wm_th)
+ int_en = BIT_DATA_RDY_EN;
+ } else {
+ int_en = BIT_DATA_RDY_EN;
+ if (st->chip_config.eis_enable)
+ int_en |= BIT_FSYNC_INT_EN;
+ }
+#endif
+ if (st->sensor[SENSOR_GYRO].on)
+ fifo_en |= BITS_GYRO_FIFO_EN;
+
+ if (st->sensor[SENSOR_ACCEL].on)
+ fifo_en |= BIT_ACCEL_FIFO_EN;
+ r = inv_plat_single_write(st, REG_FIFO_EN, fifo_en);
+ if (r)
+ return r;
+ st->int_en = int_en;
+ r = inv_plat_single_write(st, REG_INT_ENABLE, int_en);
+ if (r)
+ return r;
+ if (st->gesture_only_on && (!st->batch.timeout)) {
+ mode = BIT_ACCEL_INTEL_EN | BIT_ACCEL_INTEL_MODE;
+ } else {
+ mode = 0;
+ }
+ r = inv_plat_single_write(st, REG_ACCEL_INTEL_CTRL, mode);
+#ifdef SENSOR_DATA_FROM_REGISTERS
+ user = 0;
+#else
+ user = BIT_FIFO_EN;
+#endif
+ r = inv_plat_single_write(st, REG_USER_CTRL, user | st->i2c_dis);
+#ifdef TIMER_BASED_BATCHING
+ if (fifo_en && st->batch_timeout) {
+ if (st->is_batch_timer_running)
+ hrtimer_cancel(&st ->hr_batch_timer);
+ st->is_batch_timer_running = true;
+ hrtimer_start(&st ->hr_batch_timer,
+ ns_to_ktime(st->batch_timeout), HRTIMER_MODE_REL);
+ } else {
+ if (st->is_batch_timer_running)
+ hrtimer_cancel(&st ->hr_batch_timer);
+ st->is_batch_timer_running = false;
+ }
+#endif
+
+ return r;
+}
+
+/*
+ * inv_reset_fifo() - Reset FIFO related registers.
+ */
+int inv_reset_fifo(struct inv_mpu_state *st, bool turn_off)
+{
+ int r, i;
+ struct inv_timestamp_algo *ts_algo = &st->ts_algo;
+ int dur_ms;
+
+ r = inv_turn_on_fifo(st);
+ if (r)
+ return r;
+
+ ts_algo->last_run_time = get_time_ns();
+ ts_algo->reset_ts = ts_algo->last_run_time;
+ if (st->mode_1k_on)
+ ts_algo->first_sample = MODE_1K_INIT_SAMPLE;
+ else
+ ts_algo->first_sample = 1;
+
+ dur_ms = st->smplrt_div + 1;
+ if ((ts_algo->first_sample * dur_ms) < FIRST_SAMPLE_BUF_MS)
+ ts_algo->first_sample = FIRST_SAMPLE_BUF_MS / dur_ms;
+ if (ts_algo->first_sample == 0)
+ ts_algo->first_sample = 1;
+
+ st->last_temp_comp_time = ts_algo->last_run_time;
+ st->left_over_size = 0;
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ st->sensor[i].calib_flag = 0;
+ st->sensor[i].sample_calib = 0;
+ st->sensor[i].time_calib = ts_algo->last_run_time;
+ }
+
+ ts_algo->calib_counter = 0;
+
+ return 0;
+}
+
+static int inv_turn_on_engine(struct inv_mpu_state *st)
+{
+ u8 v, w;
+ int r;
+ unsigned int wait_ms;
+
+ if (st->chip_config.gyro_enable | st->chip_config.accel_enable) {
+ w = 0;
+ if (!st->chip_config.gyro_enable)
+ w |= BIT_PWR_GYRO_STBY;
+ if (!st->chip_config.accel_enable)
+ w |= BIT_PWR_ACCEL_STBY;
+ } else if (st->chip_config.compass_enable) {
+ w = BIT_PWR_GYRO_STBY;
+ } else {
+ w = (BIT_PWR_GYRO_STBY | BIT_PWR_ACCEL_STBY);
+ }
+
+ r = inv_plat_read(st, REG_PWR_MGMT_2, 1, &v);
+ if (r)
+ return r;
+ r = inv_plat_single_write(st, REG_PWR_MGMT_2, w);
+ if (r)
+ return r;
+
+ wait_ms = 0;
+ if (st->chip_config.gyro_enable
+ && (v & BIT_PWR_GYRO_STBY)) {
+ wait_ms = INV_IAM20680_GYRO_START_TIME;
+ }
+ if (st->chip_config.accel_enable
+ && (v & BIT_PWR_ACCEL_STBY)) {
+ if (INV_IAM20680_ACCEL_START_TIME > wait_ms)
+ wait_ms = INV_IAM20680_ACCEL_START_TIME;
+ }
+ if (wait_ms)
+ msleep(wait_ms);
+
+ if (st->chip_config.has_compass) {
+ if (st->chip_config.compass_enable)
+ r = st->slave_compass->resume(st);
+ else
+ r = st->slave_compass->suspend(st);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
+static int inv_setup_dmp_rate(struct inv_mpu_state *st)
+{
+ int i;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on) {
+ st->cntl |= st->sensor[i].output;
+ st->sensor[i].dur =
+ st->eng_info[st->sensor[i].engine_base].dur;
+ st->sensor[i].div = 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * inv_set_lpf() - set low pass filer based on fifo rate.
+ */
+static int inv_set_lpf(struct inv_mpu_state *st, int rate)
+{
+ const short hz[] = {188, 98, 42, 20, 10, 5};
+ const int d[] = {INV_FILTER_188HZ, INV_FILTER_98HZ,
+ INV_FILTER_42HZ, INV_FILTER_20HZ,
+ INV_FILTER_10HZ, INV_FILTER_5HZ};
+ int i, h, data, result;
+
+#ifdef USE_GYRO_LN_MODE
+ if (1) {
+#else
+ if (st->chip_config.eis_enable || st->ois.en || st->mode_1k_on) {
+#endif
+ h = (rate >> 1);
+ i = 0;
+ while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1))
+ i++;
+ data = d[i];
+ data |= EXT_SYNC_SET;
+ result = inv_plat_single_write(st, REG_CONFIG, data);
+ if (result)
+ return result;
+
+ st->chip_config.lpf = data;
+ result = inv_plat_single_write(st, REG_LP_MODE_CTRL, 0);
+ } else {
+ result = inv_plat_single_write(st, REG_LP_MODE_CTRL,
+ BIT_GYRO_CYCLE_EN);
+ if (result)
+ return result;
+ data = 0;
+ result = inv_plat_single_write(st, REG_CONFIG, data | 3);
+ }
+
+ return result;
+}
+
+static int inv_set_div(struct inv_mpu_state *st, int a_d, int g_d)
+{
+ int result, div;
+
+ if (st->chip_config.gyro_enable)
+ div = g_d;
+ else
+ div = a_d;
+ if (st->chip_config.eis_enable)
+ div = 0;
+
+ st->smplrt_div = div;
+ pr_debug("div= %d\n", div);
+ result = inv_plat_single_write(st, REG_SAMPLE_RATE_DIV, div);
+
+ return result;
+}
+
+// 20680 does not support batching
+static int inv_set_batch(struct inv_mpu_state *st)
+{
+#ifdef TIMER_BASED_BATCHING
+ u64 timeout;
+ int required_fifo_size;
+
+ if (st->batch.timeout) {
+ required_fifo_size = st->batch.timeout * st->eng_info[ENGINE_GYRO].running_rate
+ * st->batch.pk_size / 1000;
+ if (required_fifo_size > MAX_BATCH_FIFO_SIZE) {
+ required_fifo_size = MAX_BATCH_FIFO_SIZE;
+ timeout = (required_fifo_size / st->batch.pk_size) * (1000 / st->eng_info[ENGINE_GYRO].running_rate);
+ } else {
+ timeout = st->batch.timeout;
+ }
+ } else {
+ timeout = 1000 / st->eng_info[ENGINE_GYRO].running_rate;
+ }
+ if (timeout <= 1000 / st->eng_info[ENGINE_GYRO].running_rate)
+ st->batch_timeout = 0;
+ else
+ st->batch_timeout = timeout * 1000000; // ms to ns
+#endif
+ st->batch.fifo_wm_th = 0;
+
+ return 0;
+}
+
+static int inv_set_rate(struct inv_mpu_state *st)
+{
+ int g_d, a_d, result, i;
+
+ result = inv_setup_dmp_rate(st);
+ if (result)
+ return result;
+
+ g_d = st->eng_info[ENGINE_GYRO].divider - 1;
+ a_d = st->eng_info[ENGINE_ACCEL].divider - 1;
+ result = inv_set_div(st, a_d, g_d);
+ if (result)
+ return result;
+ result = inv_set_lpf(st, st->eng_info[ENGINE_GYRO].running_rate);
+ if (result)
+ return result;
+ // set ADLPF at this point not to change after accel is enabled
+ result = inv_set_accel_config2(st, false);
+ st->batch.pk_size = 0;
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on)
+ st->batch.pk_size += st->sensor[i].sample_size;
+ }
+
+ inv_set_batch(st);
+
+ return result;
+}
+
+static int inv_determine_engine(struct inv_mpu_state *st)
+{
+ int i;
+ bool a_en, g_en;
+ int accel_rate, gyro_rate;
+
+ a_en = false;
+ g_en = false;
+ gyro_rate = MPU_INIT_SENSOR_RATE;
+ accel_rate = MPU_INIT_SENSOR_RATE;
+ /* loop the streaming sensors to see which engine needs to be turned on
+ */
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on) {
+ a_en |= st->sensor[i].a_en;
+ g_en |= st->sensor[i].g_en;
+ }
+ }
+
+ if (st->chip_config.eis_enable) {
+ g_en = true;
+ st->eis.frame_count = 0;
+ st->eis.fsync_delay = 0;
+ st->eis.gyro_counter = 0;
+ st->eis.voting_count = 0;
+ st->eis.voting_count_sub = 0;
+ gyro_rate = BASE_SAMPLE_RATE;
+ } else {
+ st->eis.eis_triggered = false;
+ st->eis.prev_state = false;
+ }
+
+ accel_rate = st->sensor[SENSOR_ACCEL].rate;
+ gyro_rate = max(gyro_rate, st->sensor[SENSOR_GYRO].rate);
+
+ st->ts_algo.clock_base = ENGINE_ACCEL;
+
+ if (g_en) {
+ /* gyro engine needs to be fastest */
+ if (a_en)
+ gyro_rate = max(gyro_rate, accel_rate);
+ accel_rate = gyro_rate;
+ st->ts_algo.clock_base = ENGINE_GYRO;
+ } else if (a_en) {
+ /* accel engine needs to be fastest if gyro engine is off */
+ gyro_rate = accel_rate;
+ st->ts_algo.clock_base = ENGINE_ACCEL;
+ }
+
+ st->eng_info[ENGINE_GYRO].running_rate = gyro_rate;
+ st->eng_info[ENGINE_ACCEL].running_rate = accel_rate;
+ if ((gyro_rate >= BASE_SAMPLE_RATE) ||
+ (accel_rate >= BASE_SAMPLE_RATE))
+ st->mode_1k_on = true;
+ else
+ st->mode_1k_on = false;
+ /* engine divider for pressure and compass is set later */
+ if (st->chip_config.eis_enable || st->mode_1k_on) {
+ st->eng_info[ENGINE_GYRO].divider = 1;
+ st->eng_info[ENGINE_ACCEL].divider = 1;
+ // need to update rate and div for 1khz mode
+ for ( i = 0 ; i < SENSOR_L_NUM_MAX ; i++ ) {
+ if (st->sensor_l[i].on) {
+ st->sensor_l[i].counter = 0;
+ if (st->sensor_l[i].rate)
+ st->sensor_l[i].div =
+ BASE_SAMPLE_RATE
+ / st->sensor_l[i].rate;
+ else
+ st->sensor_l[i].div = 0xffff;
+ }
+ }
+ } else {
+ st->eng_info[ENGINE_GYRO].divider = BASE_SAMPLE_RATE /
+ st->eng_info[ENGINE_GYRO].running_rate;
+ st->eng_info[ENGINE_ACCEL].divider = BASE_SAMPLE_RATE /
+ st->eng_info[ENGINE_ACCEL].running_rate;
+ }
+
+ for ( i = 0 ; i < SENSOR_L_NUM_MAX ; i++ )
+ st->sensor_l[i].counter = 0;
+
+ inv_calc_engine_dur(&st->eng_info[ENGINE_GYRO]);
+ inv_calc_engine_dur(&st->eng_info[ENGINE_ACCEL]);
+
+ pr_debug("gen: %d aen: %d grate: %d arate: %d\n",
+ g_en, a_en, gyro_rate, accel_rate);
+
+ st->chip_config.gyro_enable = g_en;
+ st->chip_config.accel_enable = a_en;
+
+ return 0;
+}
+
+/*
+ * set_inv_enable() - enable function.
+ */
+int set_inv_enable(struct iio_dev *indio_dev)
+{
+ int result;
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ result = inv_switch_power_in_lp(st, true);
+ if (result)
+ return result;
+ inv_stop_interrupt(st);
+ inv_determine_engine(st);
+ result = inv_set_rate(st);
+ if (result) {
+ pr_err("inv_set_rate error\n");
+ return result;
+ }
+ result = inv_turn_on_engine(st);
+ if (result) {
+ pr_err("inv_turn_on_engine error\n");
+ return result;
+ }
+ result = inv_reset_fifo(st, false);
+ if (result)
+ return result;
+ result = inv_switch_power_in_lp(st, false);
+ if ((!st->chip_config.gyro_enable) &&
+ (!st->chip_config.accel_enable)) {
+ inv_set_power(st, false);
+ return 0;
+ }
+
+ return result;
+}
+/* dummy function for 20608D */
+int inv_enable_pedometer_interrupt(struct inv_mpu_state *st, bool en)
+{
+ return 0;
+}
+int inv_dmp_read(struct inv_mpu_state *st, int off, int size, u8 *buf)
+{
+ return 0;
+}
+int inv_firmware_load(struct inv_mpu_state *st)
+{
+ return 0;
+}
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_common.c b/drivers/iio/imu/inv_mpu/inv_mpu_common.c
new file mode 100644
index 000000000000..33db03418b92
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_common.c
@@ -0,0 +1,988 @@
+/*
+ * Copyright (C) 2012-2017 InvenSense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "inv_mpu: " fmt
+#include "inv_mpu_iio.h"
+#ifdef CONFIG_RTC_INTF_ALARM
+#include <linux/android_alarm.h>
+#endif
+#include <linux/export.h>
+
+#ifdef CONFIG_RTC_INTF_ALARM
+s64 get_time_ns(void)
+{
+ struct timespec ts;
+
+ /* get_monotonic_boottime(&ts); */
+
+ /* Workaround for some platform on which monotonic clock and
+ * Android SystemClock has a gap.
+ * Use ktime_to_timespec(alarm_get_elapsed_realtime()) instead of
+ * get_monotonic_boottime() for these platform
+ */
+
+ ts = ktime_to_timespec(alarm_get_elapsed_realtime());
+
+ return timespec_to_ns(&ts);
+}
+#else
+s64 get_time_ns(void)
+{
+ struct timespec ts;
+
+ get_monotonic_boottime(&ts);
+
+ /* Workaround for some platform on which monotonic clock and
+ * Android SystemClock has a gap.
+ * Use ktime_to_timespec(alarm_get_elapsed_realtime()) instead of
+ * get_monotonic_boottime() for these platform
+ */
+ return timespec_to_ns(&ts);
+}
+
+#endif
+
+#ifdef ACCEL_BIAS_TEST
+int inv_get_3axis_average(s16 src[], s16 dst[], s16 reset)
+{
+#define BUFFER_SIZE 200
+ static s16 buffer[BUFFER_SIZE][3];
+ static s16 current_position = 0;
+ static s16 ready = 0;
+ int sum[3]= {0,};
+ int i;
+
+ if(reset){
+ current_position = 0;
+ ready = 0;
+ }
+ buffer[current_position][0] = src[0];
+ buffer[current_position][1] = src[1];
+ buffer[current_position][2] = src[2];
+ current_position++;
+ if(current_position == BUFFER_SIZE){
+ ready = 1;
+ current_position = 0;
+ }
+ if(ready){
+ for(i = 0 ; i < BUFFER_SIZE ; i++){
+ sum[0] += buffer[i][0];
+ sum[1] += buffer[i][1];
+ sum[2] += buffer[i][2];
+ }
+ dst[0] = sum[0]/BUFFER_SIZE;
+ dst[1] = sum[1]/BUFFER_SIZE;
+ dst[2] = sum[2]/BUFFER_SIZE;
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+int inv_q30_mult(int a, int b)
+{
+#define DMP_MULTI_SHIFT 30
+ u64 temp;
+ int result;
+
+ temp = ((u64)a) * b;
+ result = (int)(temp >> DMP_MULTI_SHIFT);
+
+ return result;
+}
+#if defined(CONFIG_INV_MPU_IIO_ICM20648) || \
+ defined(CONFIG_INV_MPU_IIO_ICM20690)
+/* inv_read_secondary(): set secondary registers for reading.
+ The chip must be set as bank 3 before calling.
+ */
+int inv_read_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int len)
+{
+ int result;
+
+ result = inv_plat_single_write(st, st->slv_reg[ind].addr,
+ INV_MPU_BIT_I2C_READ | addr);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, st->slv_reg[ind].reg, reg);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, st->slv_reg[ind].ctrl,
+ INV_MPU_BIT_SLV_EN | len);
+
+ return result;
+}
+
+int inv_execute_read_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int len, u8 *d)
+{
+ int result;
+
+ inv_set_bank(st, BANK_SEL_3);
+ result = inv_read_secondary(st, ind, addr, reg, len);
+ if (result)
+ return result;
+ inv_set_bank(st, BANK_SEL_0);
+ result = inv_plat_single_write(st, REG_USER_CTRL, st->i2c_dis |
+ BIT_I2C_MST_EN);
+ msleep(SECONDARY_INIT_WAIT);
+ result = inv_plat_single_write(st, REG_USER_CTRL, st->i2c_dis);
+ if (result)
+ return result;
+ result = inv_plat_read(st, REG_EXT_SLV_SENS_DATA_00, len, d);
+
+ return result;
+}
+
+/* inv_write_secondary(): set secondary registers for writing.
+ The chip must be set as bank 3 before calling.
+ */
+int inv_write_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int v)
+{
+ int result;
+
+ result = inv_plat_single_write(st, st->slv_reg[ind].addr, addr);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, st->slv_reg[ind].reg, reg);
+ if (result)
+ return result;
+ result = inv_plat_single_write(st, st->slv_reg[ind].ctrl,
+ INV_MPU_BIT_SLV_EN | 1);
+
+ result = inv_plat_single_write(st, st->slv_reg[ind].d0, v);
+
+ return result;
+}
+
+int inv_execute_write_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int v)
+{
+ int result;
+
+ inv_set_bank(st, BANK_SEL_3);
+ result = inv_write_secondary(st, ind, addr, reg, v);
+ if (result)
+ return result;
+ inv_set_bank(st, BANK_SEL_0);
+ result = inv_plat_single_write(st, REG_USER_CTRL, st->i2c_dis |
+ BIT_I2C_MST_EN);
+ msleep(SECONDARY_INIT_WAIT);
+ result = inv_plat_single_write(st, REG_USER_CTRL, st->i2c_dis);
+
+ return result;
+}
+
+int inv_set_bank(struct inv_mpu_state *st, u8 bank)
+{
+#ifdef CONFIG_INV_MPU_IIO_ICM20648
+ int r;
+
+ r = inv_plat_single_write(st, REG_BANK_SEL, bank);
+
+ return r;
+#else
+ return 0;
+#endif
+}
+#endif
+
+#ifdef CONFIG_INV_MPU_IIO_ICM20648
+/**
+ * inv_write_cntl() - Write control word to designated address.
+ * @st: Device driver instance.
+ * @wd: control word.
+ * @en: enable/disable.
+ * @cntl: control address to be written.
+ */
+int inv_write_cntl(struct inv_mpu_state *st, u16 wd, bool en, int cntl)
+{
+ int result;
+ u8 reg[2], d_out[2];
+
+ result = mem_r(cntl, 2, d_out);
+ if (result)
+ return result;
+ reg[0] = ((wd >> 8) & 0xff);
+ reg[1] = (wd & 0xff);
+ if (!en) {
+ d_out[0] &= ~reg[0];
+ d_out[1] &= ~reg[1];
+ } else {
+ d_out[0] |= reg[0];
+ d_out[1] |= reg[1];
+ }
+ result = mem_w(cntl, 2, d_out);
+
+ return result;
+}
+#endif
+
+int inv_set_power(struct inv_mpu_state *st, bool power_on)
+{
+ u8 d;
+ int r;
+
+ if ((!power_on) == st->chip_config.is_asleep)
+ return 0;
+
+ d = BIT_CLK_PLL;
+ if (!power_on)
+ d |= BIT_SLEEP;
+
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, d);
+ if (r)
+ return r;
+
+ if (power_on)
+ usleep_range(REG_UP_TIME_USEC, REG_UP_TIME_USEC);
+
+ st->chip_config.is_asleep = !power_on;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(inv_set_power);
+
+int inv_stop_interrupt(struct inv_mpu_state *st)
+{
+ int res;
+#if defined(CONFIG_INV_MPU_IIO_ICM20648)
+ /* disable_irq_wake alone should work already. However,
+ it might need system configuration change. From driver side,
+ we will disable IRQ altogether for non-wakeup sensors. */
+ res = inv_plat_read(st, REG_INT_ENABLE, 1, &st->int_en);
+ if (res)
+ return res;
+ res = inv_plat_read(st, REG_INT_ENABLE_2, 1, &st->int_en_2);
+ if (res)
+ return res;
+ res = inv_plat_single_write(st, REG_INT_ENABLE, 0);
+ if (res)
+ return res;
+ res = inv_plat_single_write(st, REG_INT_ENABLE_2, 0);
+ if (res)
+ return res;
+#endif
+#if defined(CONFIG_INV_MPU_IIO_ICM20608D)
+ res = inv_plat_read(st, REG_INT_ENABLE, 1, &st->int_en);
+ if (res)
+ return res;
+ res = inv_plat_single_write(st, REG_INT_ENABLE, 0);
+ if (res)
+ return res;
+#endif
+#if defined(CONFIG_INV_MPU_IIO_ICM20602) \
+ || defined(CONFIG_INV_MPU_IIO_ICM20690) \
+ || defined(CONFIG_INV_MPU_IIO_IAM20680)
+ res = inv_plat_read(st, REG_INT_ENABLE, 1, &st->int_en);
+ if (res)
+ return res;
+ res = inv_plat_single_write(st, REG_INT_ENABLE, 0);
+ if (res)
+ return res;
+#endif
+ return 0;
+}
+int inv_reenable_interrupt(struct inv_mpu_state *st)
+{
+ int res = 0;
+#if defined(CONFIG_INV_MPU_IIO_ICM20648)
+ res = inv_plat_single_write(st, REG_INT_ENABLE, st->int_en);
+ if (res)
+ return res;
+ res = inv_plat_single_write(st, REG_INT_ENABLE_2, st->int_en_2);
+ if (res)
+ return res;
+#elif defined(CONFIG_INV_MPU_IIO_ICM20608D)
+ res = inv_plat_single_write(st, REG_INT_ENABLE, st->int_en);
+ if (res)
+ return res;
+#endif
+#if defined(CONFIG_INV_MPU_IIO_ICM20602) \
+ || defined(CONFIG_INV_MPU_IIO_ICM20690) \
+ || defined(CONFIG_INV_MPU_IIO_IAM20680)
+ res = inv_plat_single_write(st, REG_INT_ENABLE, st->int_en);
+ if (res)
+ return res;
+#endif
+ return res;
+}
+
+static int inv_lp_en_off_mode(struct inv_mpu_state *st, bool on)
+{
+ int r;
+
+ if (!st->chip_config.is_asleep)
+ return 0;
+
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, BIT_CLK_PLL);
+ st->chip_config.is_asleep = 0;
+
+ return r;
+}
+#ifdef CONFIG_INV_MPU_IIO_ICM20648
+static int inv_lp_en_on_mode(struct inv_mpu_state *st, bool on)
+{
+ int r = 0;
+ u8 w;
+
+ if ((!st->chip_config.is_asleep) &&
+ ((!on) == st->chip_config.lp_en_set))
+ return 0;
+
+ w = BIT_CLK_PLL;
+ if ((!on) && (!st->eis.eis_triggered))
+ w |= BIT_LP_EN;
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, w);
+ st->chip_config.is_asleep = 0;
+ st->chip_config.lp_en_set = (!on);
+ return r;
+}
+#endif
+#if defined(CONFIG_INV_MPU_IIO_ICM20602) \
+ || defined(CONFIG_INV_MPU_IIO_ICM20690) \
+ || defined(CONFIG_INV_MPU_IIO_IAM20680)
+int inv_set_accel_config2(struct inv_mpu_state *st, bool cycle_mode)
+{
+ int cycle_freq[] = {275, 192, 111, 59};
+ int cont_freq[] = {219, 219, 99, 45, 22, 11, 6};
+ int i, r, rate;
+ u8 v;
+
+ v = 0;
+#ifdef CONFIG_INV_MPU_IIO_ICM20690
+ v |= BIT_FIFO_SIZE_1K;
+#endif
+ if (cycle_mode) {
+ rate = (st->eng_info[ENGINE_ACCEL].running_rate << 1);
+ i = ARRAY_SIZE(cycle_freq) - 1;
+ while (i > 0) {
+ if (rate < cycle_freq[i]) {
+ break;
+ }
+ i--;
+ }
+ r = inv_plat_single_write(st, REG_ACCEL_CONFIG_2, v |
+ (i << 4) | 7);
+ if (r)
+ return r;
+ } else {
+ rate = (st->eng_info[ENGINE_ACCEL].running_rate >> 1);
+ for (i = 1; i < ARRAY_SIZE(cont_freq); i++) {
+ if (rate >= cont_freq[i])
+ break;
+ }
+ if (i > 6)
+ i = 6;
+ r = inv_plat_single_write(st, REG_ACCEL_CONFIG_2, v | i);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+static int inv_lp_en_on_mode(struct inv_mpu_state *st, bool on)
+{
+ int r = 0;
+ u8 w;
+ bool cond_check;
+
+ if ((!st->chip_config.is_asleep) &&
+ ((!on) == st->chip_config.lp_en_set))
+ return 0;
+ cond_check = (!on) && st->cycle_on;
+
+ w = BIT_CLK_PLL;
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, w);
+ if (cond_check) {
+ w |= BIT_LP_EN;
+ inv_set_accel_config2(st, true);
+ st->chip_config.lp_en_set = true;
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, w);
+ } else {
+ inv_set_accel_config2(st, false);
+#ifdef CONFIG_INV_MPU_IIO_ICM20690
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, w | BIT_SLEEP);
+ if (r)
+ return r;
+#endif
+ st->chip_config.lp_en_set = false;
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, w);
+ msleep(10);
+ }
+ st->chip_config.is_asleep = 0;
+
+ return r;
+}
+#endif
+#ifdef CONFIG_INV_MPU_IIO_ICM20608D
+static int inv_set_accel_config2(struct inv_mpu_state *st)
+{
+ int cont_freq[] = {219, 219, 99, 45, 22, 11, 6};
+ int dec2_cfg = 0;
+ int i, r, rate;
+
+ rate = (st->eng_info[ENGINE_ACCEL].running_rate << 1);
+ i = 0;
+ if (!st->chip_config.eis_enable){
+ while ((rate < cont_freq[i]) && (i < ARRAY_SIZE(cont_freq) - 1))
+ i++;
+ dec2_cfg = 2<<4; //4x
+ }
+ r = inv_plat_single_write(st, REG_ACCEL_CONFIG_2, i | dec2_cfg);
+ if (r)
+ return r;
+ return 0;
+}
+static int inv_lp_en_on_mode(struct inv_mpu_state *st, bool on)
+{
+ int r = 0;
+ u8 w;
+
+ w = BIT_CLK_PLL;
+ if ((!on) && (!st->chip_config.eis_enable))
+ w |= BIT_LP_EN;
+ inv_set_accel_config2(st);
+ r = inv_plat_single_write(st, REG_PWR_MGMT_1, w);
+
+ return r;
+}
+#endif
+int inv_switch_power_in_lp(struct inv_mpu_state *st, bool on)
+{
+ int r;
+
+ if (st->chip_config.lp_en_mode_off)
+ r = inv_lp_en_off_mode(st, on);
+ else
+ r = inv_lp_en_on_mode(st, on);
+
+ return r;
+}
+EXPORT_SYMBOL_GPL(inv_switch_power_in_lp);
+
+int write_be16_to_mem(struct inv_mpu_state *st, u16 data, int addr)
+{
+ u8 d[2];
+
+ d[0] = (data >> 8) & 0xff;
+ d[1] = data & 0xff;
+
+ return mem_w(addr, sizeof(d), d);
+}
+
+int write_be32_to_mem(struct inv_mpu_state *st, u32 data, int addr)
+{
+ cpu_to_be32s(&data);
+ return mem_w(addr, sizeof(data), (u8 *)&data);
+}
+
+int read_be16_from_mem(struct inv_mpu_state *st, u16 *o, int addr)
+{
+ int result;
+ u8 d[2];
+
+ result = mem_r(addr, 2, (u8 *) &d);
+ *o = d[0] << 8 | d[1];
+
+ return result;
+}
+
+int read_be32_from_mem(struct inv_mpu_state *st, u32 *o, int addr)
+{
+ int result;
+ u32 d = 0;
+
+ result = mem_r(addr, 4, (u8 *) &d);
+ *o = be32_to_cpup((__be32 *)(&d));
+
+ return result;
+}
+
+int be32_to_int(u8 *d)
+{
+ return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+u32 inv_get_cntr_diff(u32 curr_counter, u32 prev)
+{
+ u32 diff;
+
+ if (curr_counter > prev)
+ diff = curr_counter - prev;
+ else
+ diff = 0xffffffff - prev + curr_counter + 1;
+
+ return diff;
+}
+
+int inv_write_2bytes(struct inv_mpu_state *st, int addr, int data)
+{
+ u8 d[2];
+
+ if (data < 0 || data > USHRT_MAX)
+ return -EINVAL;
+
+ d[0] = (u8) ((data >> 8) & 0xff);
+ d[1] = (u8) (data & 0xff);
+
+ return mem_w(addr, ARRAY_SIZE(d), d);
+}
+
+
+
+int inv_process_eis(struct inv_mpu_state *st, u16 delay)
+{
+ int tmp1, tmp2, tmp3;
+
+ switch (st->eis.voting_state) {
+ case 0:
+ st->eis.gyro_counter_s[0] = st->eis.gyro_counter;
+ st->eis.fsync_delay_s[0] = delay - st->eis.fsync_delay;
+ st->eis.voting_count = 1;
+ st->eis.voting_count_sub = 0;
+ st->eis.voting_state = 1;
+ break;
+ case 1:
+ if (abs(st->eis.gyro_counter_s[0] -
+ st->eis.gyro_counter) <= 1) {
+ st->eis.voting_count++;
+ } else {
+ st->eis.gyro_counter_s[2] = st->eis.gyro_counter;
+ st->eis.voting_count_sub++;
+ st->eis.voting_state = 2;
+ }
+ if (st->eis.voting_count > 5)
+ st->eis.voting_state = 3;
+ break;
+ case 2:
+ tmp1 = abs(st->eis.gyro_counter_s[0] - st->eis.gyro_counter);
+ tmp2 = abs(st->eis.gyro_counter_s[2] - st->eis.gyro_counter);
+
+ if ((tmp1 < tmp2) && (tmp1 <= 1))
+ st->eis.voting_count++;
+ else
+ st->eis.voting_count_sub++;
+ if (st->eis.voting_count > 5) {
+ st->eis.voting_state = 3;
+ st->eis.voting_count = 0;
+ st->eis.voting_count_sub = 0;
+ }
+
+ if (st->eis.voting_count_sub > 5) {
+ st->eis.gyro_counter_s[0] = st->eis.gyro_counter;
+ st->eis.fsync_delay_s[0] = delay - st->eis.fsync_delay;
+ st->eis.voting_state = 1;
+ st->eis.voting_count = 1;
+ st->eis.voting_count_sub = 0;
+ }
+ break;
+ case 3:
+ tmp1 = abs(st->eis.gyro_counter_s[0] - st->eis.gyro_counter);
+ if (tmp1 == 1) {
+ st->eis.gyro_counter_s[1] = st->eis.gyro_counter;
+ st->eis.fsync_delay_s[1] = delay - st->eis.fsync_delay;
+ st->eis.voting_state = 4;
+ st->eis.voting_count_sub = 1;
+ st->eis.voting_count = 1;
+ }
+ break;
+ case 4:
+ if (st->eis.gyro_counter == st->eis.gyro_counter_s[0]) {
+ tmp1 = delay - st->eis.fsync_delay;
+ tmp2 = abs(tmp1 - st->eis.fsync_delay_s[0]);
+ if (tmp2 < 3) {
+ st->eis.voting_count++;
+ } else {
+ st->eis.fsync_delay_s[2] = tmp1;
+ st->eis.voting_count_sub = 1;
+ st->eis.voting_state = 5;
+ }
+ if (st->eis.voting_count > 5) {
+ st->eis.voting_count = 1;
+ st->eis.voting_state = 6;
+ }
+ }
+ break;
+ case 5:
+ if (st->eis.gyro_counter == st->eis.gyro_counter_s[0]) {
+ tmp1 = delay - st->eis.fsync_delay;
+
+ tmp2 = abs(tmp1 - st->eis.fsync_delay_s[0]);
+ tmp3 = abs(tmp1 - st->eis.fsync_delay_s[2]);
+ if ((tmp2 < tmp3) && (tmp2 < 3))
+ st->eis.voting_count++;
+ else
+ st->eis.voting_count_sub++;
+ if ((st->eis.voting_count > 5) &&
+ (st->eis.voting_count_sub
+ < st->eis.voting_count)) {
+ st->eis.voting_state = 6;
+ st->eis.voting_count = 1;
+ } else if (st->eis.voting_count_sub > 5) {
+ st->eis.fsync_delay_s[0] = tmp1;
+ st->eis.voting_state = 4;
+ st->eis.voting_count = 1;
+ }
+
+ }
+ break;
+ case 6:
+ if (st->eis.gyro_counter == st->eis.gyro_counter_s[1]) {
+ tmp1 = delay - st->eis.fsync_delay;
+ tmp2 = abs(tmp1 - st->eis.fsync_delay_s[1]);
+ if (tmp2 < 3) {
+ st->eis.voting_count++;
+ } else {
+ st->eis.fsync_delay_s[2] = tmp1;
+ st->eis.voting_count_sub = 1;
+ st->eis.voting_count = 1;
+ st->eis.voting_state = 7;
+ }
+ if (st->eis.voting_count > 5)
+ st->eis.voting_state = 8;
+ }
+ break;
+ case 7:
+ if (st->eis.gyro_counter == st->eis.gyro_counter_s[1]) {
+ tmp1 = delay - st->eis.fsync_delay;
+
+ tmp2 = abs(tmp1 - st->eis.fsync_delay_s[1]);
+ tmp3 = abs(tmp1 - st->eis.fsync_delay_s[2]);
+ if ((tmp2 < tmp3) && (tmp2 < 3))
+ st->eis.voting_count++;
+ else
+ st->eis.voting_count_sub++;
+ if ((st->eis.voting_count > 5) &&
+ (st->eis.voting_count_sub
+ < st->eis.voting_count)) {
+ st->eis.voting_state = 8;
+ } else if (st->eis.voting_count_sub > 5) {
+ st->eis.fsync_delay_s[1] = tmp1;
+ st->eis.voting_state = 6;
+ st->eis.voting_count = 1;
+ }
+
+ }
+ break;
+ default:
+ break;
+ }
+
+ pr_debug("de= %d gc= %d\n", delay, st->eis.gyro_counter);
+ st->eis.fsync_delay = delay;
+ st->eis.gyro_counter = 0;
+
+ pr_debug("state=%d g1= %d d1= %d g2= %d d2= %d\n",
+ st->eis.voting_state,
+ st->eis.gyro_counter_s[0],
+ st->eis.fsync_delay_s[0],
+ st->eis.gyro_counter_s[1],
+ st->eis.fsync_delay_s[1]);
+
+ return 0;
+}
+
+int inv_rate_convert(struct inv_mpu_state *st, int ind, int data)
+{
+ int t, out, out1, out2;
+ int base_freq;
+
+ if (data <= MPU_DEFAULT_DMP_FREQ)
+ base_freq = MPU_DEFAULT_DMP_FREQ;
+ else
+ base_freq = BASE_SAMPLE_RATE;
+
+ t = base_freq / data;
+ if (!t)
+ t = 1;
+ out1 = base_freq / (t + 1);
+ out2 = base_freq / t;
+ if ((data - out1) * INV_ODR_BUFFER_MULTI < data)
+ out = out1;
+ else
+ out = out2;
+
+ return out;
+}
+
+static void inv_check_wake_non_wake(struct inv_mpu_state *st,
+ enum SENSOR_L wake, enum SENSOR_L non_wake)
+{
+ int tmp_rate;
+
+ if (!st->sensor_l[wake].on && !st->sensor_l[non_wake].on)
+ return;
+
+ tmp_rate = MPU_INIT_SENSOR_RATE;
+ if (st->sensor_l[wake].on)
+ tmp_rate = st->sensor_l[wake].rate;
+ if (st->sensor_l[non_wake].on)
+ tmp_rate = max(tmp_rate, st->sensor_l[non_wake].rate);
+ st->sensor_l[wake].rate = tmp_rate;
+ st->sensor_l[non_wake].rate = tmp_rate;
+}
+
+static void inv_check_wake_non_wake_divider(struct inv_mpu_state *st,
+ enum SENSOR_L wake, enum SENSOR_L non_wake)
+{
+ if (st->sensor_l[wake].on && st->sensor_l[non_wake].on)
+ st->sensor_l[non_wake].div = 0xffff;
+
+}
+
+#if defined(CONFIG_INV_MPU_IIO_ICM20602) \
+ || defined(CONFIG_INV_MPU_IIO_ICM20690) \
+ || defined(CONFIG_INV_MPU_IIO_IAM20680)
+int inv_check_sensor_on(struct inv_mpu_state *st)
+{
+ int i, max_rate;
+ enum SENSOR_L wake[] = {SENSOR_L_GYRO_WAKE, SENSOR_L_ACCEL_WAKE,
+ SENSOR_L_MAG_WAKE};
+ enum SENSOR_L non_wake[] = {SENSOR_L_GYRO, SENSOR_L_ACCEL,
+ SENSOR_L_MAG};
+
+ st->sensor_l[SENSOR_L_GESTURE_ACCEL].rate = GESTURE_ACCEL_RATE;
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].on = false;
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].rate = MPU_INIT_SENSOR_RATE;
+
+ if ((st->step_detector_l_on
+ || st->step_detector_wake_l_on
+ || st->step_counter_l_on
+ || st->step_counter_wake_l_on
+ || st->chip_config.pick_up_enable
+ || st->chip_config.tilt_enable)
+ && (!st->sensor_l[SENSOR_L_ACCEL].on)
+ && (!st->sensor_l[SENSOR_L_ACCEL_WAKE].on))
+ st->sensor_l[SENSOR_L_GESTURE_ACCEL].on = true;
+ else
+ st->sensor_l[SENSOR_L_GESTURE_ACCEL].on = false;
+
+
+ st->chip_config.wake_on = false;
+ for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
+ if (st->sensor_l[i].on && st->sensor_l[i].rate) {
+ st->sensor[st->sensor_l[i].base].on = true;
+ st->chip_config.wake_on |= st->sensor_l[i].wake_on;
+ }
+ }
+ if (st->sensor_l[SENSOR_L_GESTURE_ACCEL].on &&
+ (!st->sensor[SENSOR_GYRO].on) &&
+ (!st->sensor[SENSOR_COMPASS].on))
+ st->gesture_only_on = true;
+ else
+ st->gesture_only_on = false;
+
+ for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
+ if (st->sensor_l[i].on) {
+ st->sensor[st->sensor_l[i].base].rate =
+ max(st->sensor[st->sensor_l[i].base].rate,
+ st->sensor_l[i].rate);
+ }
+ }
+ max_rate = MPU_INIT_SENSOR_RATE;
+ if (st->chip_config.eis_enable) {
+ max_rate = ESI_GYRO_RATE;
+ st->sensor_l[SENSOR_L_EIS_GYRO].rate = ESI_GYRO_RATE;
+ }
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on) {
+ max_rate = max(max_rate, st->sensor[i].rate);
+ }
+ }
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on) {
+ st->sensor[i].rate = max_rate;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(wake); i++)
+ inv_check_wake_non_wake(st, wake[i], non_wake[i]);
+
+ for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
+ if (st->sensor_l[i].on) {
+ if (st->sensor_l[i].rate)
+ st->sensor_l[i].div =
+ st->sensor[st->sensor_l[i].base].rate
+ / st->sensor_l[i].rate;
+ else
+ st->sensor_l[i].div = 0xffff;
+ pr_debug("sensor= %d, div= %d\n",
+ i, st->sensor_l[i].div);
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(wake); i++)
+ inv_check_wake_non_wake_divider(st, wake[i], non_wake[i]);
+
+ if (st->step_detector_wake_l_on ||
+ st->step_counter_wake_l_on ||
+ st->chip_config.pick_up_enable ||
+ st->chip_config.tilt_enable)
+ st->chip_config.wake_on = true;
+
+ return 0;
+}
+#else
+static void inv_do_check_sensor_on(struct inv_mpu_state *st,
+ enum SENSOR_L *wake,
+ enum SENSOR_L *non_wake, int sensor_size)
+{
+ int i;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].on = false;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++)
+ st->sensor[i].rate = MPU_INIT_SENSOR_RATE;
+
+ st->chip_config.wake_on = false;
+ for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
+ if (st->sensor_l[i].on && st->sensor_l[i].rate) {
+ st->sensor[st->sensor_l[i].base].on = true;
+ st->chip_config.wake_on |= st->sensor_l[i].wake_on;
+ }
+ }
+
+ for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
+ if (st->sensor_l[i].on) {
+ st->sensor[st->sensor_l[i].base].rate =
+ max(st->sensor[st->sensor_l[i].base].rate,
+ st->sensor_l[i].rate);
+ }
+ }
+ for (i = 0; i < sensor_size; i++)
+ inv_check_wake_non_wake(st, wake[i], non_wake[i]);
+
+ for (i = 0; i < SENSOR_L_NUM_MAX; i++) {
+ if (st->sensor_l[i].on) {
+ if (st->sensor_l[i].rate)
+ st->sensor_l[i].div =
+ st->sensor[st->sensor_l[i].base].rate
+ / st->sensor_l[i].rate;
+ else
+ st->sensor_l[i].div = 0xffff;
+ }
+ }
+ for (i = 0; i < sensor_size; i++)
+ inv_check_wake_non_wake_divider(st, wake[i], non_wake[i]);
+
+ if (st->step_detector_wake_l_on ||
+ st->step_counter_wake_l_on ||
+ st->chip_config.pick_up_enable ||
+ st->chip_config.tilt_enable ||
+ st->smd.on)
+ st->chip_config.wake_on = true;
+
+}
+#endif
+
+#if defined(CONFIG_INV_MPU_IIO_ICM20608D)
+int inv_check_sensor_on(struct inv_mpu_state *st)
+{
+ enum SENSOR_L wake[] = {SENSOR_L_GYRO_WAKE, SENSOR_L_ACCEL_WAKE,
+ SENSOR_L_SIXQ_WAKE, SENSOR_L_PEDQ_WAKE,
+ SENSOR_L_GYRO_CAL_WAKE};
+ enum SENSOR_L non_wake[] = {SENSOR_L_GYRO, SENSOR_L_ACCEL,
+ SENSOR_L_SIXQ, SENSOR_L_PEDQ,
+ SENSOR_L_GYRO_CAL};
+
+ inv_do_check_sensor_on(st, wake, non_wake, ARRAY_SIZE(wake));
+
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_INV_MPU_IIO_ICM20648)
+int inv_check_sensor_on(struct inv_mpu_state *st)
+{
+ enum SENSOR_L wake[] = {SENSOR_L_GYRO_WAKE, SENSOR_L_ACCEL_WAKE,
+ SENSOR_L_MAG_WAKE, SENSOR_L_ALS_WAKE,
+ SENSOR_L_SIXQ_WAKE, SENSOR_L_PEDQ_WAKE,
+ SENSOR_L_NINEQ_WAKE, SENSOR_L_GEOMAG_WAKE,
+ SENSOR_L_PRESSURE_WAKE,
+ SENSOR_L_GYRO_CAL_WAKE,
+ SENSOR_L_MAG_CAL_WAKE};
+ enum SENSOR_L non_wake[] = {SENSOR_L_GYRO, SENSOR_L_ACCEL,
+ SENSOR_L_MAG, SENSOR_L_ALS,
+ SENSOR_L_SIXQ, SENSOR_L_PEDQ,
+ SENSOR_L_NINEQ, SENSOR_L_GEOMAG,
+ SENSOR_L_PRESSURE,
+ SENSOR_L_GYRO_CAL,
+ SENSOR_L_MAG_CAL};
+
+ inv_do_check_sensor_on(st, wake, non_wake, ARRAY_SIZE(wake));
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+int inv_mpu_suspend(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ /* add code according to different request Start */
+ dev_info(st->dev, "%s suspend\n", st->hw->name);
+ mutex_lock(&indio_dev->mlock);
+
+ st->resume_state = false;
+ if (st->chip_config.wake_on) {
+ enable_irq_wake(st->irq);
+ } else {
+ inv_stop_interrupt(st);
+ }
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(inv_mpu_suspend);
+
+/*
+ * inv_mpu_complete(): complete method for this driver.
+ * This method can be modified according to the request of different
+ * customers. It basically undo everything suspend is doing
+ * and recover the chip to what it was before suspend. We use complete to
+ * make sure that alarm clock resume is finished. If we use resume, the
+ * alarm clock may not resume yet and get incorrect clock reading.
+ */
+void inv_mpu_complete(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+ dev_info(st->dev, "%s resume\n", st->hw->name);
+ if (st->resume_state)
+ return;
+
+ mutex_lock(&indio_dev->mlock);
+
+ if (!st->chip_config.wake_on) {
+ inv_reenable_interrupt(st);
+ } else {
+ disable_irq_wake(st->irq);
+ }
+ /* resume state is used to synchronize read_fifo such that it won't
+ proceed unless resume is finished. */
+ st->resume_state = true;
+ /* resume flag is indicating that current clock reading is from resume,
+ it has up to 1 second drift and should do proper processing */
+ st->ts_algo.resume_flag = true;
+ mutex_unlock(&indio_dev->mlock);
+ wake_up_interruptible(&st->wait_queue);
+
+ return;
+}
+EXPORT_SYMBOL_GPL(inv_mpu_complete);
+#endif
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_dts.c b/drivers/iio/imu/inv_mpu/inv_mpu_dts.c
new file mode 100644
index 000000000000..0b8b3fc29b0a
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_dts.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2012-2017 InvenSense, 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/device.h>
+#include <linux/err.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/export.h>
+
+#include <linux/iio/imu/mpu.h>
+#include "inv_mpu_dts.h"
+#include "inv_mpu_iio.h"
+
+#ifdef CONFIG_OF
+
+static int inv_mpu_power_on(struct mpu_platform_data *pdata)
+{
+ int err;
+
+ if (!IS_ERR(pdata->vdd_ana)) {
+ err = regulator_enable(pdata->vdd_ana);
+ if (err)
+ return err;
+ }
+ if (!IS_ERR(pdata->vdd_i2c)) {
+ err = regulator_enable(pdata->vdd_i2c);
+ if (err)
+ goto error_disable_vdd_ana;
+ }
+
+ return 0;
+
+error_disable_vdd_ana:
+ regulator_disable(pdata->vdd_ana);
+ return err;
+}
+
+static int inv_mpu_power_off(struct mpu_platform_data *pdata)
+{
+ if (!IS_ERR(pdata->vdd_ana))
+ regulator_disable(pdata->vdd_ana);
+ if (!IS_ERR(pdata->vdd_i2c))
+ regulator_disable(pdata->vdd_i2c);
+
+ return 0;
+}
+
+static int inv_parse_orientation_matrix(struct device *dev, s8 *orient)
+{
+ int rc, i;
+ struct device_node *np = dev->of_node;
+ u32 temp_val, temp_val2;
+
+ for (i = 0; i < 9; i++)
+ orient[i] = 0;
+
+ /* parsing axis x orientation matrix */
+ rc = of_property_read_u32(np, "axis_map_x", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read axis_map_x\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "negate_x", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read negate_x\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[temp_val] = -1;
+ else
+ orient[temp_val] = 1;
+
+ /* parsing axis y orientation matrix */
+ rc = of_property_read_u32(np, "axis_map_y", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read axis_map_y\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "negate_y", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read negate_y\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[3 + temp_val] = -1;
+ else
+ orient[3 + temp_val] = 1;
+
+ /* parsing axis z orientation matrix */
+ rc = of_property_read_u32(np, "axis_map_z", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read axis_map_z\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "negate_z", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read negate_z\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[6 + temp_val] = -1;
+ else
+ orient[6 + temp_val] = 1;
+
+ return 0;
+}
+
+static int inv_parse_secondary_orientation_matrix(struct device *dev,
+ s8 *orient)
+{
+ int rc, i;
+ struct device_node *np = dev->of_node;
+ u32 temp_val, temp_val2;
+
+ for (i = 0; i < 9; i++)
+ orient[i] = 0;
+
+ /* parsing axis x orientation matrix */
+ rc = of_property_read_u32(np, "inven,secondary_axis_map_x", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary axis_map_x\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "inven,secondary_negate_x", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary negate_x\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[temp_val] = -1;
+ else
+ orient[temp_val] = 1;
+
+ /* parsing axis y orientation matrix */
+ rc = of_property_read_u32(np, "inven,secondary_axis_map_y", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary axis_map_y\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "inven,secondary_negate_y", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary negate_y\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[3 + temp_val] = -1;
+ else
+ orient[3 + temp_val] = 1;
+
+ /* parsing axis z orientation matrix */
+ rc = of_property_read_u32(np, "inven,secondary_axis_map_z", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary axis_map_z\n");
+ return rc;
+ }
+ rc = of_property_read_u32(np, "inven,secondary_negate_z", &temp_val2);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary negate_z\n");
+ return rc;
+ }
+ if (temp_val2)
+ orient[6 + temp_val] = -1;
+ else
+ orient[6 + temp_val] = 1;
+
+ return 0;
+}
+
+static int inv_parse_secondary(struct device *dev,
+ struct mpu_platform_data *pdata)
+{
+ int rc;
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+ const char *name;
+
+ if (of_property_read_string(np, "inven,secondary_type", &name)) {
+ dev_err(dev, "Missing secondary type.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "compass")) {
+ pdata->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS;
+ } else if (!strcmp(name, "none")) {
+ pdata->sec_slave_type = SECONDARY_SLAVE_TYPE_NONE;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+
+ if (of_property_read_string(np, "inven,secondary_name", &name)) {
+ dev_err(dev, "Missing secondary name.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "ak8963"))
+ pdata->sec_slave_id = COMPASS_ID_AK8963;
+ else if (!strcmp(name, "ak8975"))
+ pdata->sec_slave_id = COMPASS_ID_AK8975;
+ else if (!strcmp(name, "ak8972"))
+ pdata->sec_slave_id = COMPASS_ID_AK8972;
+ else if (!strcmp(name, "ak09911"))
+ pdata->sec_slave_id = COMPASS_ID_AK09911;
+ else if (!strcmp(name, "ak09912"))
+ pdata->sec_slave_id = COMPASS_ID_AK09912;
+ else if (!strcmp(name, "ak09916"))
+ pdata->sec_slave_id = COMPASS_ID_AK09916;
+ else
+ return -EINVAL;
+ rc = of_property_read_u32(np, "inven,secondary_reg", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read secondary register\n");
+ return rc;
+ }
+ pdata->secondary_i2c_addr = temp_val;
+ rc = inv_parse_secondary_orientation_matrix(dev,
+ pdata->
+ secondary_orientation);
+
+ return rc;
+}
+
+static int inv_parse_aux(struct device *dev, struct mpu_platform_data *pdata)
+{
+ int rc;
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+ const char *name;
+
+ if (of_property_read_string(np, "inven,aux_type", &name)) {
+ dev_err(dev, "Missing aux type.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "pressure")) {
+ pdata->aux_slave_type = SECONDARY_SLAVE_TYPE_PRESSURE;
+ } else if (!strcmp(name, "none")) {
+ pdata->aux_slave_type = SECONDARY_SLAVE_TYPE_NONE;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+
+ if (of_property_read_string(np, "inven,aux_name", &name)) {
+ dev_err(dev, "Missing aux name.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "bmp280"))
+ pdata->aux_slave_id = PRESSURE_ID_BMP280;
+ else
+ return -EINVAL;
+
+ rc = of_property_read_u32(np, "inven,aux_reg", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read aux register\n");
+ return rc;
+ }
+ pdata->aux_i2c_addr = temp_val;
+
+ return 0;
+}
+
+static int inv_parse_readonly_secondary(struct device *dev,
+ struct mpu_platform_data *pdata)
+{
+ int rc;
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+ const char *name;
+
+ if (of_property_read_string(np, "inven,read_only_slave_type", &name)) {
+ dev_err(dev, "Missing read only slave type type.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "als")) {
+ pdata->read_only_slave_type = SECONDARY_SLAVE_TYPE_ALS;
+ } else if (!strcmp(name, "none")) {
+ pdata->read_only_slave_type = SECONDARY_SLAVE_TYPE_NONE;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+
+ if (of_property_read_string(np, "inven,read_only_slave_name", &name)) {
+ dev_err(dev, "Missing read only slave type name.\n");
+ return -EINVAL;
+ }
+ if (!strcmp(name, "apds9930"))
+ pdata->read_only_slave_id = ALS_ID_APDS_9930;
+ else
+ return -EINVAL;
+
+ rc = of_property_read_u32(np, "inven,read_only_slave_reg", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read read only slave reg register\n");
+ return rc;
+ }
+ pdata->read_only_i2c_addr = temp_val;
+
+ return 0;
+}
+
+int invensense_mpu_parse_dt(struct device *dev, struct mpu_platform_data *pdata)
+{
+ int rc;
+
+ rc = inv_parse_orientation_matrix(dev, pdata->orientation);
+ if (rc)
+ return rc;
+ rc = inv_parse_secondary(dev, pdata);
+ if (rc)
+ return rc;
+ inv_parse_aux(dev, pdata);
+
+ inv_parse_readonly_secondary(dev, pdata);
+
+ pdata->vdd_ana = regulator_get(dev, "inven,vdd_ana");
+ if (IS_ERR(pdata->vdd_ana)) {
+ rc = PTR_ERR(pdata->vdd_ana);
+ dev_warn(dev, "regulator get failed vdd_ana-supply rc=%d\n", rc);
+ }
+ pdata->vdd_i2c = regulator_get(dev, "inven,vcc_i2c");
+ if (IS_ERR(pdata->vdd_i2c)) {
+ rc = PTR_ERR(pdata->vdd_i2c);
+ dev_warn(dev, "regulator get failed vcc-i2c-supply rc=%d\n", rc);
+ }
+ pdata->power_on = inv_mpu_power_on;
+ pdata->power_off = inv_mpu_power_off;
+ dev_dbg(dev, "parse dt complete\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(invensense_mpu_parse_dt);
+
+#endif /* CONFIG_OF */
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_dts.h b/drivers/iio/imu/inv_mpu/inv_mpu_dts.h
new file mode 100644
index 000000000000..90966febb930
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_dts.h
@@ -0,0 +1,25 @@
+/*
+* Copyright (C) 2012-2017 InvenSense, 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 _INV_MPU_DTS_H_
+#define _INV_MPU_DTS_H_
+
+#include <linux/kernel.h>
+#include <linux/iio/imu/mpu.h>
+
+#ifdef CONFIG_OF
+int invensense_mpu_parse_dt(struct device *dev,
+ struct mpu_platform_data *pdata);
+#endif
+
+#endif /* #ifndef _INV_MPU_DTS_H_ */
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu/inv_mpu_i2c.c
new file mode 100644
index 000000000000..e7838fce84a8
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_i2c.c
@@ -0,0 +1,556 @@
+/*
+* Copyright (C) 2012-2018 InvenSense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#define pr_fmt(fmt) "inv_mpu: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_mpu_iio.h"
+#include "inv_mpu_dts.h"
+
+#define CONFIG_DYNAMIC_DEBUG_I2C 0
+
+/**
+ * inv_i2c_read_base() - Read one or more bytes from the device registers.
+ * @st: Device driver instance.
+ * @i2c_addr: i2c address of device.
+ * @reg: First device register to be read from.
+ * @length: Number of bytes to read.
+ * @data: Data read from device.
+ * NOTE:This is not re-implementation of i2c_smbus_read because i2c
+ * address could be specified in this case. We could have two different
+ * i2c address due to secondary i2c interface.
+ */
+int inv_i2c_read_base(struct inv_mpu_state *st, u16 i2c_addr,
+ u8 reg, u16 length, u8 *data)
+{
+ struct i2c_msg msgs[2];
+ int res;
+
+ if (!data)
+ return -EINVAL;
+
+ msgs[0].addr = i2c_addr;
+ msgs[0].flags = 0; /* write */
+ msgs[0].buf = &reg;
+ msgs[0].len = 1;
+
+ msgs[1].addr = i2c_addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].buf = data;
+ msgs[1].len = length;
+
+ res = i2c_transfer(st->sl_handle, msgs, 2);
+
+ if (res < 2) {
+ if (res >= 0)
+ res = -EIO;
+ } else
+ res = 0;
+ INV_I2C_INC_MPUWRITE(3);
+ INV_I2C_INC_MPUREAD(length);
+
+ return res;
+}
+
+/**
+ * inv_i2c_single_write_base() - Write a byte to a device register.
+ * @st: Device driver instance.
+ * @i2c_addr: I2C address of the device.
+ * @reg: Device register to be written to.
+ * @data: Byte to write to device.
+ * NOTE:This is not re-implementation of i2c_smbus_write because i2c
+ * address could be specified in this case. We could have two different
+ * i2c address due to secondary i2c interface.
+ */
+int inv_i2c_single_write_base(struct inv_mpu_state *st,
+ u16 i2c_addr, u8 reg, u8 data)
+{
+ u8 tmp[2];
+ struct i2c_msg msg;
+ int res;
+
+ tmp[0] = reg;
+ tmp[1] = data;
+
+ msg.addr = i2c_addr;
+ msg.flags = 0; /* write */
+ msg.buf = tmp;
+ msg.len = 2;
+
+ INV_I2C_INC_MPUWRITE(3);
+
+ res = i2c_transfer(st->sl_handle, &msg, 1);
+ if (res < 1) {
+ if (res == 0)
+ res = -EIO;
+ return res;
+ } else
+ return 0;
+}
+
+static int inv_i2c_single_write(struct inv_mpu_state *st, u8 reg, u8 data)
+{
+ return inv_i2c_single_write_base(st, st->i2c_addr, reg, data);
+}
+
+static int inv_i2c_read(struct inv_mpu_state *st, u8 reg, int len, u8 *data)
+{
+ return inv_i2c_read_base(st, st->i2c_addr, reg, len, data);
+}
+
+static int _memory_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 const *data)
+{
+ u8 bank[2];
+ u8 addr[2];
+ u8 buf[513];
+
+ struct i2c_msg msgs[3];
+ int res;
+
+ if (!data || !st)
+ return -EINVAL;
+
+ if (len >= (sizeof(buf) - 1))
+ return -ENOMEM;
+
+ bank[0] = REG_MEM_BANK_SEL;
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = REG_MEM_START_ADDR;
+ addr[1] = mem_addr & 0xFF;
+
+ buf[0] = REG_MEM_R_W;
+ memcpy(buf + 1, data, len);
+
+ /* write message */
+ msgs[0].addr = mpu_addr;
+ msgs[0].flags = 0;
+ msgs[0].buf = bank;
+ msgs[0].len = sizeof(bank);
+
+ msgs[1].addr = mpu_addr;
+ msgs[1].flags = 0;
+ msgs[1].buf = addr;
+ msgs[1].len = sizeof(addr);
+
+ msgs[2].addr = mpu_addr;
+ msgs[2].flags = 0;
+ msgs[2].buf = (u8 *) buf;
+ msgs[2].len = len + 1;
+
+ INV_I2C_INC_MPUWRITE(3 + 3 + (2 + len));
+
+#if CONFIG_DYNAMIC_DEBUG_I2C
+ {
+ char *write = 0;
+ pr_debug("%s WM%02X%02X%02X%s%s - %d\n", st->hw->name,
+ mpu_addr, bank[1], addr[1],
+ wr_pr_debug_begin(data, len, write),
+ wr_pr_debug_end(write), len);
+ }
+#endif
+
+ res = i2c_transfer(st->sl_handle, msgs, 3);
+ if (res != 3) {
+ if (res >= 0)
+ res = -EIO;
+ return res;
+ } else {
+ return 0;
+ }
+}
+
+static int inv_i2c_mem_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 const *data)
+{
+ int r, i, j;
+#define DMP_MEM_CMP_SIZE 16
+ u8 w[DMP_MEM_CMP_SIZE];
+ bool retry;
+
+ j = 0;
+ retry = true;
+ while ((j < 3) && retry) {
+ retry = false;
+ r = _memory_write(st, mpu_addr, mem_addr, len, data);
+ if (len < DMP_MEM_CMP_SIZE) {
+ r = mem_r(mem_addr, len, w);
+ for (i = 0; i < len; i++) {
+ if (data[i] != w[i]) {
+ pr_debug
+ ("error write=%x, len=%d,data=%x, w=%x, i=%d\n",
+ mem_addr, len, data[i], w[i], i);
+ retry = true;
+ }
+ }
+ }
+ j++;
+ }
+
+ return r;
+}
+
+static int inv_i2c_mem_read(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 *data)
+{
+ u8 bank[2];
+ u8 addr[2];
+ u8 buf;
+
+ struct i2c_msg msgs[4];
+ int res;
+
+ if (!data || !st)
+ return -EINVAL;
+
+ bank[0] = REG_MEM_BANK_SEL;
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = REG_MEM_START_ADDR;
+ addr[1] = mem_addr & 0xFF;
+
+ buf = REG_MEM_R_W;
+
+ /* write message */
+ msgs[0].addr = mpu_addr;
+ msgs[0].flags = 0;
+ msgs[0].buf = bank;
+ msgs[0].len = sizeof(bank);
+
+ msgs[1].addr = mpu_addr;
+ msgs[1].flags = 0;
+ msgs[1].buf = addr;
+ msgs[1].len = sizeof(addr);
+
+ msgs[2].addr = mpu_addr;
+ msgs[2].flags = 0;
+ msgs[2].buf = &buf;
+ msgs[2].len = 1;
+
+ msgs[3].addr = mpu_addr;
+ msgs[3].flags = I2C_M_RD;
+ msgs[3].buf = data;
+ msgs[3].len = len;
+
+ res = i2c_transfer(st->sl_handle, msgs, 4);
+ if (res != 4) {
+ if (res >= 0)
+ res = -EIO;
+ } else
+ res = 0;
+ INV_I2C_INC_MPUWRITE(3 + 3 + 3);
+ INV_I2C_INC_MPUREAD(len);
+
+#if CONFIG_DYNAMIC_DEBUG_I2C
+ {
+ char *read = 0;
+ pr_debug("%s RM%02X%02X%02X%02X - %s%s\n", st->hw->name,
+ mpu_addr, bank[1], addr[1], len,
+ wr_pr_debug_begin(data, len, read),
+ wr_pr_debug_end(read));
+ }
+#endif
+
+ return res;
+}
+
+/*
+ * inv_mpu_probe() - probe function.
+ */
+static int inv_mpu_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct inv_mpu_state *st;
+ struct iio_dev *indio_dev;
+ int result;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENOSYS;
+ pr_err("I2c function error\n");
+ goto out_no_free;
+ }
+
+#ifdef KERNEL_VERSION_4_X
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
+ if (indio_dev == NULL) {
+ pr_err("memory allocation failed\n");
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+#else
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ pr_err("memory allocation failed\n");
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+#endif
+ st = iio_priv(indio_dev);
+ st->client = client;
+ st->sl_handle = client->adapter;
+ st->i2c_addr = client->addr;
+ st->write = inv_i2c_single_write;
+ st->read = inv_i2c_read;
+ st->mem_write = inv_i2c_mem_write;
+ st->mem_read = inv_i2c_mem_read;
+ st->dev = &client->dev;
+ st->bus_type = BUS_I2C;
+#ifdef CONFIG_OF
+ result = invensense_mpu_parse_dt(st->dev, &st->plat_data);
+ if (result)
+# ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+# else
+ goto out_free;
+# endif
+
+ /* Power on device */
+ if (st->plat_data.power_on) {
+ result = st->plat_data.power_on(&st->plat_data);
+ if (result < 0) {
+ dev_err(st->dev, "power_on failed: %d\n", result);
+# ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+# else
+ goto out_free;
+# endif
+ }
+ pr_info("%s: power on here.\n", __func__);
+ }
+ pr_info("%s: power on.\n", __func__);
+
+ msleep(100);
+#else
+ if (dev_get_platdata(st->dev) == NULL)
+# ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+# else
+ goto out_free;
+# endif
+ st->plat_data = *(struct mpu_platform_data *)dev_get_platdata(st->dev);
+#endif
+
+ /* power is turned on inside check chip type */
+ result = inv_check_chip_type(indio_dev, id->name);
+ if (result)
+#ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+#else
+ goto out_free;
+#endif
+
+ /* Make state variables available to all _show and _store functions. */
+ i2c_set_clientdata(client, indio_dev);
+ indio_dev->dev.parent = st->dev;
+ indio_dev->name = id->name;
+
+ st->irq = client->irq;
+
+ result = inv_mpu_configure_ring(indio_dev);
+ if (result) {
+ pr_err("configure ring buffer fail\n");
+ goto out_free;
+ }
+#ifdef KERNEL_VERSION_4_X
+ INV_I2C_SETIRQ(IRQ_MPU, st->irq);
+ result = devm_iio_device_register(st->dev, indio_dev);
+ if (result) {
+ pr_err("IIO device register fail\n");
+ goto out_unreg_ring;
+ }
+#else
+ result = iio_buffer_register(indio_dev, indio_dev->channels,
+ indio_dev->num_channels);
+ if (result) {
+ pr_err("ring buffer register fail\n");
+ goto out_unreg_ring;
+ }
+ INV_I2C_SETIRQ(IRQ_MPU, client->irq);
+ result = iio_device_register(indio_dev);
+ if (result) {
+ pr_err("IIO device register fail\n");
+ goto out_remove_ring;
+ }
+#endif
+
+ result = inv_create_dmp_sysfs(indio_dev);
+ if (result) {
+ pr_err("create dmp sysfs failed\n");
+ goto out_unreg_iio;
+ }
+ init_waitqueue_head(&st->wait_queue);
+ st->resume_state = true;
+#ifdef CONFIG_HAS_WAKELOCK
+ wake_lock_init(&st->wake_lock, WAKE_LOCK_SUSPEND, "inv_mpu");
+#else
+ wakeup_source_init(&st->wake_lock, "inv_mpu");
+#endif
+ dev_info(st->dev, "%s ma-kernel-%s is ready to go!\n",
+ indio_dev->name, INVENSENSE_DRIVER_VERSION);
+
+#ifdef SENSOR_DATA_FROM_REGISTERS
+ pr_info("Data read from registers\n");
+#else
+ pr_info("Data read from FIFO\n");
+#endif
+#ifdef TIMER_BASED_BATCHING
+ pr_info("Timer based batching\n");
+#endif
+
+ return 0;
+#ifdef KERNEL_VERSION_4_X
+out_unreg_iio:
+ devm_iio_device_unregister(st->dev, indio_dev);
+out_unreg_ring:
+ inv_mpu_unconfigure_ring(indio_dev);
+out_free:
+ devm_iio_device_free(st->dev, indio_dev);
+out_no_free:
+#else
+out_unreg_iio:
+ iio_device_unregister(indio_dev);
+out_remove_ring:
+ iio_buffer_unregister(indio_dev);
+out_unreg_ring:
+ inv_mpu_unconfigure_ring(indio_dev);
+out_free:
+ iio_device_free(indio_dev);
+out_no_free:
+#endif
+ dev_err(st->dev, "%s failed %d\n", __func__, result);
+
+ return -EIO;
+}
+
+static void inv_mpu_shutdown(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ inv_switch_power_in_lp(st, true);
+ dev_dbg(st->dev, "Shutting down %s...\n", st->hw->name);
+
+ /* reset to make sure previous state are not there */
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1, BIT_H_RESET);
+ if (result)
+ dev_err(st->dev, "Failed to reset %s\n",
+ st->hw->name);
+ msleep(POWER_UP_TIME);
+ /* turn off power to ensure gyro engine is off */
+ result = inv_set_power(st, false);
+ if (result)
+ dev_err(st->dev, "Failed to turn off %s\n",
+ st->hw->name);
+ inv_switch_power_in_lp(st, false);
+ mutex_unlock(&indio_dev->mlock);
+}
+
+/*
+ * inv_mpu_remove() - remove function.
+ */
+static int inv_mpu_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+#ifdef KERNEL_VERSION_4_X
+ devm_iio_device_unregister(st->dev, indio_dev);
+#else
+ iio_device_unregister(indio_dev);
+ iio_buffer_unregister(indio_dev);
+#endif
+ inv_mpu_unconfigure_ring(indio_dev);
+#ifdef KERNEL_VERSION_4_X
+ devm_iio_device_free(st->dev, indio_dev);
+#else
+ iio_device_free(indio_dev);
+#endif
+ dev_info(st->dev, "inv-mpu-iio module removed.\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int inv_mpu_i2c_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+
+ return inv_mpu_suspend(indio_dev);
+}
+
+static void inv_mpu_i2c_complete(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+
+ inv_mpu_complete(indio_dev);
+}
+#endif
+
+static const struct dev_pm_ops inv_mpu_i2c_pmops = {
+#ifdef CONFIG_PM_SLEEP
+ .suspend = inv_mpu_i2c_suspend,
+ .complete = inv_mpu_i2c_complete,
+#endif
+};
+
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id inv_mpu_id[] = {
+#ifdef CONFIG_INV_MPU_IIO_ICM20648
+ {"icm20645", ICM20645},
+ {"icm10340", ICM10340},
+ {"icm20648", ICM20648},
+#else
+ {"icm20608d", ICM20608D},
+ {"icm20690", ICM20690},
+ {"icm20602", ICM20602},
+ {"iam20680", IAM20680},
+#endif
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
+
+static struct i2c_driver inv_mpu_driver = {
+ .probe = inv_mpu_probe,
+ .remove = inv_mpu_remove,
+ .shutdown = inv_mpu_shutdown,
+ .id_table = inv_mpu_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv-mpu-iio-i2c",
+ .pm = &inv_mpu_i2c_pmops,
+ },
+};
+module_i2c_driver(inv_mpu_driver);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense I2C device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu/inv_mpu_iio.h
new file mode 100644
index 000000000000..9e7316558eae
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_iio.h
@@ -0,0 +1,1138 @@
+/*
+ * Copyright (C) 2012-2018 InvenSense, 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 _INV_MPU_IIO_H_
+#define _INV_MPU_IIO_H_
+
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,0,0))
+#define KERNEL_VERSION_4_X
+#endif
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/iio/imu/mpu.h>
+#include <linux/interrupt.h>
+#include <linux/semaphore.h>
+#ifdef CONFIG_HAS_WAKELOCK
+#include <linux/wakelock.h>
+#else
+#include <linux/pm_wakeup.h>
+#endif
+#include <linux/wait.h>
+
+#include <linux/iio/sysfs.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+
+#ifdef CONFIG_INV_MPU_IIO_ICM20648
+#include "icm20648/dmp3Default.h"
+#endif
+#ifdef CONFIG_INV_MPU_IIO_ICM20608D
+#include "icm20608d/dmp3Default_20608D.h"
+#endif
+
+#include "inv_test/inv_counters.h"
+
+#if defined(CONFIG_INV_MPU_IIO_ICM20648)
+#include "icm20648/inv_mpu_iio_reg_20648.h"
+#elif defined(CONFIG_INV_MPU_IIO_ICM20602)
+#include "icm20602/inv_mpu_iio_reg_20602.h"
+#elif defined(CONFIG_INV_MPU_IIO_ICM20608D)
+#include "icm20608d/inv_mpu_iio_reg_20608.h"
+#elif defined(CONFIG_INV_MPU_IIO_ICM20690)
+#include "icm20690/inv_mpu_iio_reg_20690.h"
+#elif defined(CONFIG_INV_MPU_IIO_IAM20680)
+#include "iam20680/inv_mpu_iio_reg_20680.h"
+#endif
+
+#define INVENSENSE_DRIVER_VERSION "8.1.2-simple-test1"
+
+/* #define DEBUG */
+
+/* #define ACCEL_BIAS_TEST */
+
+/* #define BIAS_CONFIDENCE_HIGH 1 */
+
+#define MAX_FIFO_READ_SIZE 128
+#define MAX_DMP_READ_SIZE 16
+
+/* data header defines */
+#define WAKE_HDR 0x8000
+
+#define ACCEL_HDR 1
+#define GYRO_HDR 2
+#define COMPASS_HDR 3
+#define ALS_HDR 4
+#define SIXQUAT_HDR 5
+#define NINEQUAT_HDR 6
+#define PEDQUAT_HDR 7
+#define GEOMAG_HDR 8
+#define PRESSURE_HDR 9
+#define GYRO_CALIB_HDR 10
+#define COMPASS_CALIB_HDR 11
+#define STEP_COUNTER_HDR 12
+#define STEP_DETECTOR_HDR 13
+#define STEP_COUNT_HDR 14
+#define ACTIVITY_HDR 15
+#define PICK_UP_HDR 16
+#define EMPTY_MARKER 17
+#define END_MARKER 18
+#define COMPASS_ACCURACY_HDR 19
+#define ACCEL_ACCURACY_HDR 20
+#define GYRO_ACCURACY_HDR 21
+#define EIS_GYRO_HDR 36
+#define EIS_CALIB_HDR 37
+#define LPQ_HDR 38
+
+#define ACCEL_WAKE_HDR (ACCEL_HDR | WAKE_HDR)
+#define GYRO_WAKE_HDR (GYRO_HDR | WAKE_HDR)
+#define COMPASS_WAKE_HDR (COMPASS_HDR | WAKE_HDR)
+#define ALS_WAKE_HDR (ALS_HDR | WAKE_HDR)
+#define SIXQUAT_WAKE_HDR (SIXQUAT_HDR | WAKE_HDR)
+#define NINEQUAT_WAKE_HDR (NINEQUAT_HDR | WAKE_HDR)
+#define PEDQUAT_WAKE_HDR (PEDQUAT_HDR | WAKE_HDR)
+#define GEOMAG_WAKE_HDR (GEOMAG_HDR | WAKE_HDR)
+#define PRESSURE_WAKE_HDR (PRESSURE_HDR | WAKE_HDR)
+#define GYRO_CALIB_WAKE_HDR (GYRO_CALIB_HDR | WAKE_HDR)
+#define COMPASS_CALIB_WAKE_HDR (COMPASS_CALIB_HDR | WAKE_HDR)
+#define STEP_COUNTER_WAKE_HDR (STEP_COUNTER_HDR | WAKE_HDR)
+#define STEP_DETECTOR_WAKE_HDR (STEP_DETECTOR_HDR | WAKE_HDR)
+
+/* init parameters */
+#define MPU_INIT_SMD_THLD 1500
+#define MPU_INIT_GYRO_SCALE 3
+#define MPU_INIT_ACCEL_SCALE 2
+#define MPU_INIT_PED_INT_THRESH 2
+#define MPU_INIT_PED_STEP_THRESH 6
+#define MPU_4X_TS_GYRO_SHIFT (3160000 / 2)
+#define DMP_START_ADDR_20645 0x900
+#define DMP_START_ADDR_20648 0x1000
+#define DMP_START_ADDR_10340 0x0a60
+#define DMP_START_ADDR_20608D 0x4B0
+#define MAX_WR_SZ 100
+#define WOM_DELAY_THRESHOLD 200
+#define INV_ODR_BUFFER_MULTI 20
+#define INV_ODR_OVER_FACTOR 20
+
+#define COVARIANCE_SIZE 14
+#define ACCEL_COVARIANCE_SIZE (COVARIANCE_SIZE * sizeof(int))
+
+enum inv_bus_type {
+ BUS_I2C = 0,
+ BUS_SPI,
+};
+
+struct inv_mpu_state;
+
+enum INV_ENGINE {
+ ENGINE_GYRO = 0,
+ ENGINE_ACCEL,
+ ENGINE_PRESSURE,
+ ENGINE_I2C,
+ ENGINE_NUM_MAX,
+};
+
+/**
+ * struct inv_hw_s - Other important hardware information.
+ * @num_reg: Number of registers on device.
+ * @name: name of the chip
+ */
+struct inv_hw_s {
+ u8 num_reg;
+ u8 *name;
+};
+
+/**
+ * struct inv_sensor - information for each sensor.
+ * @ts: this sensors timestamp.
+ * @ts_adj: sensor timestamp adjustment.
+ * @previous_ts: previous timestamp for this sensor.
+ * @dur: duration between samples in ns.
+ * @rate: sensor data rate.
+ * @sample_size: number of bytes for the sensor.
+ * @odr_addr: output data rate address in DMP.
+ * @counter_addr: output counter address in DMP.
+ * @output: output on/off control word.
+ * @time_calib: calibrate timestamp.
+ * @sample_calib: calibrate bytes accumulated.
+ * @div: divider in DMP mode.
+ * @calib_flag: calibrate flag used to improve the accuracy of estimation.
+ * @on: sensor on/off.
+ * @a_en: accel engine requirement.
+ * @g_en: gyro engine requirement.
+ * @c_en: compass_engine requirement.
+ * @p_en: pressure engine requirement.
+ * @engine_base: engine base for this sensor.
+ * @count: number of samples in one session.
+ * @send: decide whether to send this sample or not.
+ */
+struct inv_sensor {
+ u64 ts;
+ s64 ts_adj;
+ u64 previous_ts;
+ int dur;
+ int rate;
+ u8 sample_size;
+ int odr_addr;
+ int counter_addr;
+ u16 output;
+ u64 time_calib;
+ u32 sample_calib;
+ int div;
+ bool calib_flag;
+ bool on;
+ bool a_en;
+ bool g_en;
+ bool c_en;
+ bool p_en;
+ enum INV_ENGINE engine_base;
+ int count;
+ bool send;
+};
+
+/**
+ * struct inv_sensor - information for each sensor.
+ * @sample_size: number of bytes for the sensor.
+ * @output: output on/off control word.
+ * @on: sensor on/off.
+ * @header: accuracy header for communicate with HAL
+ *dd @count: number of samples in one session.
+ */
+struct inv_sensor_accuracy {
+ u16 output;
+ u8 sample_size;
+ bool on;
+ u16 header;
+};
+
+enum SENSOR_ACCURACY {
+ SENSOR_ACCEL_ACCURACY = 0,
+ SENSOR_GYRO_ACCURACY,
+ SENSOR_COMPASS_ACCURACY,
+ SENSOR_ACCURACY_NUM_MAX,
+};
+
+enum SENSOR_L {
+ SENSOR_L_ACCEL = 0,
+ SENSOR_L_GYRO,
+ SENSOR_L_MAG,
+ SENSOR_L_ALS,
+ SENSOR_L_SIXQ,
+ SENSOR_L_THREEQ,
+ SENSOR_L_NINEQ,
+ SENSOR_L_PEDQ,
+ SENSOR_L_GEOMAG,
+ SENSOR_L_PRESSURE,
+ SENSOR_L_GYRO_CAL,
+ SENSOR_L_MAG_CAL,
+ SENSOR_L_EIS_GYRO,
+ /*wake sensors */
+ SENSOR_L_ACCEL_WAKE = 13,
+ SENSOR_L_GYRO_WAKE,
+ SENSOR_L_MAG_WAKE,
+ SENSOR_L_ALS_WAKE,
+ SENSOR_L_SIXQ_WAKE,
+ SENSOR_L_NINEQ_WAKE,
+ SENSOR_L_PEDQ_WAKE,
+ SENSOR_L_GEOMAG_WAKE,
+ SENSOR_L_PRESSURE_WAKE,
+ SENSOR_L_GYRO_CAL_WAKE,
+ SENSOR_L_MAG_CAL_WAKE,
+ SENSOR_L_GESTURE_ACCEL,
+ SENSOR_L_NUM_MAX,
+};
+
+/**
+ * struct android_l_sensor - information for each android sensor.
+ * @ts: this sensors timestamp.
+ * @base: android sensor based on invensense sensor.
+ * @rate: output rate.
+ * @on: sensor on/off.
+ * @wake_on: wake on sensor is on/off.
+ * @div: divider for the output.
+ * @counter: counter works with the divider.
+ * @header: header for the output.
+ */
+struct android_l_sensor {
+ u64 ts;
+ enum INV_SENSORS base;
+ int rate;
+ bool on;
+ bool wake_on;
+ int div;
+ int counter;
+ u16 header;
+};
+
+/**
+ * struct inv_batch - information for batchmode.
+ * @on: normal batch mode on.
+ * @default_on: default batch on. This is optimization option.
+ * @overflow_on: overflow mode for batchmode.
+ * @wake_fifo_on: overflow for suspend mode.
+ * @step_only: mean only step detector data is batched.
+ * @post_isr_run: mean post isr has runned once.
+ * @counter: counter for batch mode.
+ * @timeout: nominal timeout value for batchmode in milliseconds.
+ * @max_rate: max rate for all batched sensors.
+ * @pk_size: packet size;
+ * @engine_base: engine base batch mode should stick to.
+ */
+struct inv_batch {
+ bool on;
+ bool default_on;
+ bool overflow_on;
+ bool wake_fifo_on;
+ bool step_only;
+ bool post_isr_run;
+ u32 counter;
+ u32 timeout;
+ u32 max_rate;
+ u32 pk_size;
+ u32 fifo_wm_th;
+ enum INV_ENGINE engine_base;
+};
+
+/**
+ * struct inv_chip_config_s - Cached chip configuration data.
+ * @fsr: Full scale range.
+ * @lpf: Digital low pass filter frequency.
+ * @accel_fs: accel full scale range.
+ * @accel_enable: enable accel functionality
+ * @gyro_enable: enable gyro functionality
+ * @compass_enable: enable compass functinality.
+ * @geomag_enable: enable geomag sensor functions.
+ * @als_enable: enable ALS functionality.
+ * @pressure_enable: eanble pressure functionality.
+ * @secondary_enable: secondary I2C bus enabled or not.
+ * @has_gyro: has gyro or not.
+ * @has_compass: has secondary I2C compass or not.
+ * @has_pressure: has secondary I2C pressure or not.
+ * @has_als: has secondary I2C als or not.
+ * @slave_enable: secondary I2C interface enabled or not.
+ * @normal_compass_measure: discard first compass data after reset.
+ * @is_asleep: 1 if chip is powered down.
+ * @lp_en_set: 1 if LP_EN bit is set;
+ * @lp_en_mode_off: debug mode that turns off LP_EN mode off.
+ * @clk_sel: debug_mode that turns on/off clock selection.
+ * @dmp_on: dmp is on/off.
+ * @dmp_event_int_on: dmp event interrupt on/off.
+ * @wom_on: WOM interrupt on. This is an internal variable.
+ * @step_indicator_on: step indicate bit added to the sensor or not.
+ * @tilt_enable: tilt enable.
+ * @pick_up_enable: pick up gesture enable.
+ * @step_detector_on: step detector on or not.
+ * @activity_on: turn on/off activity.
+ * @activity_eng_on: activity engine on/off.
+ * @firmware_loaded: flag indicate firmware loaded or not.
+ * @low_power_gyro_on: flag indicating low power gyro on/off.
+ * @wake_on: any wake on sensor is on/off.
+ * @compass_rate: compass engine rate. Determined by underlying data.
+ */
+struct inv_chip_config_s {
+ u32 fsr:2;
+ u32 lpf:3;
+ u32 accel_fs:2;
+ u32 accel_enable:1;
+ u32 gyro_enable:1;
+ u32 compass_enable:1;
+ u32 geomag_enable:1;
+ u32 als_enable:1;
+ u32 prox_enable:1;
+ u32 pressure_enable:1;
+ u32 has_gyro:1;
+ u32 has_compass:1;
+ u32 has_pressure:1;
+ u32 has_als:1;
+ u32 slave_enable:1;
+ u32 normal_compass_measure:1;
+ u32 is_asleep:1;
+ u32 lp_en_set:1;
+ u32 lp_en_mode_off:1;
+ u32 clk_sel:1;
+ u32 dmp_on:1;
+ u32 dmp_event_int_on:1;
+ u32 wom_on:1;
+ u32 step_indicator_on:1;
+ u32 tilt_enable:1;
+ u32 pick_up_enable:1;
+ u32 eis_enable:1;
+ u32 step_detector_on:1;
+ u32 activity_on:1;
+ u32 activity_eng_on:1;
+ u32 firmware_loaded:1;
+ u32 low_power_gyro_on:1;
+ u32 wake_on:1;
+ int compass_rate;
+};
+
+/**
+ * struct inv_temp_comp - temperature compensation structure.
+ * @t_lo: raw temperature in low temperature.
+ * @t_hi: raw temperature in high temperature.
+ * @b_lo: gyro bias in low temperature.
+ * @b_hi: gyro bias in high temperature.
+ * @has_low: flag indicate low temperature parameters is updated.
+ * @has_high: flag indicates high temperature parameters is updated.
+ * @slope: slope for temperature compensation.
+ */
+struct inv_temp_comp {
+ int t_lo;
+ int t_hi;
+ int b_lo[3];
+ int b_hi[3];
+ bool has_low;
+ bool has_high;
+ int slope[3];
+};
+
+/**
+ * struct inv_chip_info_s - Chip related information.
+ * @product_id: Product id.
+ * @product_revision: Product revision.
+ * @silicon_revision: Silicon revision.
+ * @software_revision: software revision.
+ * @compass_sens: compass sensitivity.
+ * @gyro_sens_trim: Gyro sensitivity trim factor.
+ * @accel_sens_trim: accel sensitivity trim factor.
+ */
+struct inv_chip_info_s {
+ u8 product_id;
+ u8 product_revision;
+ u8 silicon_revision;
+ u8 software_revision;
+ u8 compass_sens[3];
+ u32 gyro_sens_trim;
+ u32 accel_sens_trim;
+};
+
+/**
+ * struct inv_smd significant motion detection structure.
+ * @threshold: accel threshold for motion detection.
+ * @delay: delay time to confirm 2nd motion.
+ * @delay2: delay window parameter.
+ * @on: smd on/off.
+ */
+struct inv_smd {
+ u32 threshold;
+ u32 delay;
+ u32 delay2;
+ bool on;
+};
+
+/**
+ * struct inv_ped pedometer related data structure.
+ * @step: steps taken.
+ * @time: time taken during the period.
+ * @last_step_time: last time the step is taken.
+ * @step_thresh: step threshold to show steps.
+ * @int_thresh: step threshold to generate interrupt.
+ * @int_on: pedometer interrupt enable/disable.
+ * @on: pedometer on/off.
+ * @engine_on: pedometer engine on/off.
+ */
+struct inv_ped {
+ u64 step;
+ u64 time;
+ u64 last_step_time;
+ u16 step_thresh;
+ u16 int_thresh;
+ bool int_on;
+ bool on;
+ bool engine_on;
+};
+
+/**
+ * struct inv_eis EIS related data structure.
+ * @prev_gyro: latest gyro data just before FSYNC triggerd
+ * @prev_timestamp: latest gyro timestamp just before FSYNC triggered
+ * @current_gyro: gyro data just after FSYNC triggerd
+ * @current_timestamp: gyro timestamp just after FSYNC triggered
+ * @fsync_timestamp: timestamp of FSYNC event
+ * @fsync_delay: delay time of FSYNC and Gyro data. DMP data of FSYNC event
+ * @eis_triggered: check fsync event is triggered or not.
+ * @eis_frame: current frame is eis frame;
+ * @current_sync: current frame contains fsync counter.
+ * @frame_count: frame count for synchronization.
+ */
+struct inv_eis {
+ int prev_gyro[3];
+ u64 prev_timestamp;
+ int current_gyro[3];
+ u64 current_timestamp;
+ u32 frame_dur;
+ u64 slope[3];
+ u64 fsync_timestamp;
+ u64 last_fsync_timestamp;
+ u16 fsync_delay;
+ bool eis_triggered;
+ bool eis_frame;
+ bool current_sync;
+ bool prev_state;
+ u32 frame_count;
+ int gyro_counter;
+ int gyro_counter_s[3];
+ int fsync_delay_s[3];
+ int voting_count;
+ int voting_count_sub;
+ int voting_state;
+ int count_precision;
+};
+
+enum TRIGGER_STATE {
+ DATA_TRIGGER = 0,
+ RATE_TRIGGER,
+ EVENT_TRIGGER,
+ MISC_TRIGGER,
+ DEBUG_TRIGGER,
+};
+
+enum inv_fifo_count_mode {
+ BYTE_MODE,
+ RECORD_MODE
+};
+
+/**
+ * struct inv_secondary_reg - secondary registers data structure.
+ * @addr: address of the slave.
+ * @reg: register address of slave.
+ * @ctrl: control register.
+ * @d0: data out register.
+ */
+struct inv_secondary_reg {
+ u8 addr;
+ u8 reg;
+ u8 ctrl;
+ u8 d0;
+};
+
+struct inv_secondary_set {
+ u8 delay_enable;
+ u8 delay_time;
+ u8 odr_config;
+};
+/**
+ * struct inv_engine_info - data structure for engines.
+ * @base_time: base time for each engine.
+ * @base_time_1k: base time when chip is running at 1K;
+ * @divider: divider used to downsample engine rate from original rate.
+ * @running_rate: the actually running rate of engine.
+ * @orig_rate: original rate for each engine before downsample.
+ * @dur: duration for one tick.
+ * @last_update_time: last update time.
+ */
+struct inv_engine_info {
+ u32 base_time;
+ u32 base_time_1k;
+ u32 divider;
+ u32 running_rate;
+ u32 orig_rate;
+ u32 dur;
+ u64 last_update_time;
+};
+
+struct inv_ois {
+ int gyro_fs;
+ int accel_fs;
+ bool en;
+};
+
+/**
+ * struct inv_timestamp_algo - timestamp algorithm .
+ * @last_run_time: last time the post ISR runs.
+ * @ts_for_calib: ts storage for calibration.
+ * @reset_ts: reset time.
+ * @dmp_ticks: dmp ticks storage for calibration.
+ * @start_dmp_counter: dmp counter when start a new session.
+ * @calib_counter: calibration counter for timestamp.
+ * @resume_flag: flag to indicate this is the first time after resume. time
+ could have up to 1 seconds difference.
+ * @clock_base: clock base to calculate the timestamp.
+ * @gyro_ts_shift: 9 K counter for EIS.
+ * @first_sample: first of 1K running should be dropped it affects timing
+ */
+struct inv_timestamp_algo {
+ u64 last_run_time;
+ u64 ts_for_calib;
+ u64 reset_ts;
+ u32 dmp_ticks;
+ u32 start_dmp_counter;
+ int calib_counter;
+ bool resume_flag;
+ enum INV_ENGINE clock_base;
+ u32 gyro_ts_shift;
+ u32 first_sample;
+};
+
+struct inv_mpu_slave;
+/**
+ * struct inv_mpu_state - Driver state variables.
+ * @dev: device address of the current bus, i2c or spi.
+ * @chip_config: Cached attribute information.
+ * @chip_info: Chip information from read-only registers.
+ * @smd: SMD data structure.
+ * @ped: pedometer data structure.
+ * @batch: batchmode data structure.
+ * @temp_comp: gyro temperature compensation structure.
+ * @slave_compass: slave compass.
+ * @slave_pressure: slave pressure.
+ * @slave_als: slave als.
+ * @slv_reg: slave register data structure.
+ * @ts_algo: timestamp algorithm data structure.
+ * @sec_set: slave register odr config.
+ * @eng_info: information for each engine.
+ * @hw: Other hardware-specific information.
+ * @chip_type: chip type.
+ * @suspend_resume_sema: semaphore for suspend/resume.
+ * @wake_lock: wake lock of the system.
+ * @client: i2c client handle.
+ * @plat_data: platform data.
+ * @sl_handle: Handle to I2C port.
+ * @sensor{SENSOR_NUM_MAX]: sensor individual properties.
+ * @sensor_l[SENSOR_L_NUM_MAX]: android L sensors properties.
+ * @sensor_accuracy[SENSOR_ACCURACY_NUM_MAX]: sensor accuracy.
+ * @sensor_acurracy_flag: flag indiciate whether to check output accuracy.
+ * @irq: irq number store.
+ * @accel_bias: accel bias store.
+ * @gyro_bias: gyro bias store.
+ * @accel_st_bias: accel bias store, result of self-test.
+ * @gyro_st_bias: gyro bias store, result of self-test.
+ * @gyro_ois_st_bias: gyro bias store from ois self test result.
+ * @input_accel_dmp_bias[3]: accel bias for dmp.
+ * @input_gyro_dmp_bias[3]: gyro bias for dmp.
+ * @input_compass_dmp_bias[3]: compass bias for dmp.
+ * @input_accel_bias[3]: accel bias for offset register.
+ * @input_gyro_bias[3]: gyro bias for offset register.
+ * @fifo_data[8]: fifo data storage.
+ * @i2c_addr: i2c address.
+ * @header_count: header count in current FIFO.
+ * @step_det_count: number of step detectors in one batch.
+ * @gyro_sf: gyro scale factor.
+ * @left_over[LEFT_OVER_BYTES]: left over bytes storage.
+ * @left_over_size: left over size.
+ * @fifo_count: current fifo_count;
+ * @wake_sensor_received: wake up sensor received.
+ * @accel_cal_enable: accel calibration on/off
+ * @gyro_cal_enable: gyro calibration on/off
+ * @calib_compass_on: calibrate compass on.
+ * @debug_determine_engine_on: determine engine on/off.
+ * @poke_mode_on: poke mode on/off.
+ * @mode_1k_on: indicate 1K Hz mode is on.
+ * @poke_ts: time stamp for poke feature.
+ * @step_detector_base_ts: base time stamp for step detector calculation.
+ * @last_temp_comp_time: last time temperature compensation is done.
+ * @i2c_dis: disable I2C interface or not.
+ * @name: name for the chip.
+ * @gyro_st_data: gyro self test data.
+ * @accel_st_data: accel self test data.
+ * @secondary_name: name for the slave device in the secondary I2C.
+ * @compass_var: compass variance from DMP.
+ * @current_compass_matrix: matrix compass data multiplied to before soft iron.
+ * @final_compass_matrix: matrix compass data multiplied to before soft iron.
+ * @trigger_state: information that which part triggers set_inv_enable.
+ * @firmware: firmware data pointer.
+ * @accel_calib_threshold: accel calibration threshold;
+ * @accel_calib_rate: divider for accel calibration rate.
+ * @accel_covariance[COVARIANCE_SIZE]: accel covariance data;
+ * @kf: kfifo for activity store.
+ * @activity_size: size for activity.
+ * @cntl: control word for sensor enable.
+ * @cntl2: control word for sensor extension.
+ * @motion_event_cntl: control word for events.
+ * @dmp_image_size: dmp image size.
+ * @dmp_start_address: start address of dmp.
+ * @step_counter_l_on: step counter android L sensor on/off.
+ * @step_counter_wake_l_on: step counter android L sensor wake on/off .
+ * @step_detector_l_on: step detector android L sensor on/off.
+ * @step_detector_wake_l_on: step detector android L sensor wake on/off .
+ * @gesture_only_on: indicate it is gesture only.
+ * @mag_divider: mag divider when gyro/accel is faster than mag maximum rate.
+ * @special_mag_mode: for 20690, there is special mag mode need to be handled.
+ * @mag_start_flag: when mag divider is non zero, need to check the start.
+ * @prev_steps: previous steps sent to the user.
+ * @aut_key_in: authentication key input.
+ * @aut_key_out: authentication key output.
+ * @suspend_state: state variable to indicate that we are in suspend state.
+ * @secondary_gyro_on: DMP out signal to turn on gyro.
+ * @secondary_mag_on: DMP out signal to turn on mag.
+ * @secondary_prox_on: DMP out signal to turn on proximity.
+ * @secondary_switch: showing this setup is triggerred by secondary switch.
+ * @send_calib_gyro: flag to indicate to send calibrated gyro.
+ * @send_raw_compass: flag to send raw compass.
+ * @resume_state: flag to synchronize the processing of inv_read_fifo()
+ * @cycle_on: variable indicate accel cycle mode is on.
+ * @secondary_switch_data: secondary switch data for activity.
+ * @raw_gyro_data[6]: save raw gyro data.
+ * @raw_compass_data[3]: save raw compass data.
+ * @wait_queue: wait queue to wake up inv_read_fifo()
+ * @bac_drive_conf: bac drive configuration.
+ * @bac_walk_conf: bac walk configuration.
+ * @bac_smd_conf: bac smd configuration.
+ * @bac_bike_conf: bac bike configuration.
+ * @bac_run_conf: bac run configuration.
+ * @bac_still_conf: back still configuration.
+ * @power_on_data: power on data.
+ * @fifo_data_store: store of FIFO data.
+ * @int_en: store interrupt enable register data.
+ * @int_en2: store interrupt enable register 2 data.
+ * @gesture_int_count: interrupt count for gesture only mode.
+ * @smplrt_div: SMPLRT_DIV register value.
+ */
+struct inv_mpu_state {
+ struct device *dev;
+ int (*write)(struct inv_mpu_state *st, u8 reg, u8 data);
+ int (*read)(struct inv_mpu_state *st, u8 reg, int len, u8 *data);
+ int (*mem_write)(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 const *data);
+ int (*mem_read)(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 *data);
+ struct inv_chip_config_s chip_config;
+ struct inv_chip_info_s chip_info;
+ struct inv_smd smd;
+ struct inv_ped ped;
+ struct inv_eis eis;
+ struct inv_batch batch;
+ struct inv_temp_comp temp_comp;
+ struct inv_mpu_slave *slave_compass;
+ struct inv_mpu_slave *slave_pressure;
+ struct inv_mpu_slave *slave_als;
+ struct inv_secondary_reg slv_reg[4];
+ struct inv_timestamp_algo ts_algo;
+ struct inv_secondary_set sec_set;
+ struct inv_engine_info eng_info[ENGINE_NUM_MAX];
+ const struct inv_hw_s *hw;
+ enum inv_devices chip_type;
+ enum inv_bus_type bus_type;
+ enum inv_fifo_count_mode fifo_count_mode;
+#ifdef CONFIG_HAS_WAKELOCK
+ struct wake_lock wake_lock;
+#else
+ struct wakeup_source wake_lock;
+#endif
+#ifdef TIMER_BASED_BATCHING
+ struct hrtimer hr_batch_timer;
+ u64 batch_timeout;
+ bool is_batch_timer_running;
+ struct work_struct batch_work;
+#endif
+ struct i2c_client *client;
+ struct mpu_platform_data plat_data;
+ void *sl_handle;
+ struct inv_sensor sensor[SENSOR_NUM_MAX];
+ struct android_l_sensor sensor_l[SENSOR_L_NUM_MAX];
+ struct inv_sensor_accuracy sensor_accuracy[SENSOR_ACCURACY_NUM_MAX];
+ struct inv_ois ois;
+ bool sensor_acurracy_flag[SENSOR_ACCURACY_NUM_MAX];
+ short irq;
+ int accel_bias[3];
+ int gyro_bias[3];
+ int accel_st_bias[3];
+ int accel_ois_st_bias[3];
+ int gyro_st_bias[3];
+ int gyro_ois_st_bias[3];
+ int input_accel_dmp_bias[3];
+ int input_gyro_dmp_bias[3];
+ int input_compass_dmp_bias[3];
+ int input_accel_bias[3];
+ int input_gyro_bias[3];
+ u8 fifo_data[8];
+ u8 i2c_addr;
+ int header_count;
+ int step_det_count;
+ s32 gyro_sf;
+ u8 left_over[LEFT_OVER_BYTES];
+ u32 left_over_size;
+ u32 fifo_count;
+ bool wake_sensor_received;
+ bool accel_cal_enable;
+ bool gyro_cal_enable;
+ bool calib_compass_on;
+ bool debug_determine_engine_on;
+ bool poke_mode_on;
+ bool mode_1k_on;
+ u64 poke_ts;
+ u64 step_detector_base_ts;
+ u64 last_temp_comp_time;
+ u8 i2c_dis;
+ u8 name[20];
+ u8 gyro_st_data[3];
+ u8 accel_st_data[3];
+ u8 secondary_name[20];
+ s32 compass_var;
+ int current_compass_matrix[9];
+ int final_compass_matrix[9];
+ enum TRIGGER_STATE trigger_state;
+ u8 *firmware;
+ int accel_calib_threshold;
+ int accel_calib_rate;
+ u32 accel_covariance[COVARIANCE_SIZE];
+ DECLARE_KFIFO(kf, u8, 128);
+ u32 activity_size;
+ int wom_thld;
+ u16 cntl;
+ u16 cntl2;
+ u16 motion_event_cntl;
+ int dmp_image_size;
+ int dmp_start_address;
+ bool step_counter_l_on;
+ bool step_counter_wake_l_on;
+ bool step_detector_l_on;
+ bool step_detector_wake_l_on;
+ bool gesture_only_on;
+ bool mag_start_flag;
+ int mag_divider;
+ bool special_mag_mode;
+ int prev_steps;
+ u32 curr_steps;
+ int aut_key_in;
+ int aut_key_out;
+ bool secondary_gyro_on;
+ bool secondary_mag_on;
+ bool secondary_prox_on;
+ bool secondary_switch;
+ bool send_calib_gyro;
+ bool send_raw_compass;
+ bool send_raw_gyro;
+ bool resume_state;
+ bool cycle_on;
+ int secondary_switch_data;
+ u8 raw_gyro_data[6];
+ u32 raw_compass_data[3];
+ wait_queue_head_t wait_queue;
+ u32 bac_drive_conf;
+ u32 bac_walk_conf;
+ u32 bac_smd_conf;
+ u32 bac_bike_conf;
+ u32 bac_run_conf;
+ u32 bac_still_conf;
+ u32 power_on_data;
+ u8 fifo_data_store[HARDWARE_FIFO_SIZE + LEFT_OVER_BYTES];
+ u8 int_en;
+ u8 int_en_2;
+ u8 gesture_int_count;
+ u8 smplrt_div;
+};
+
+/**
+ * struct inv_mpu_slave - MPU slave structure.
+ * @st_upper: compass self test upper limit.
+ * @st_lower: compass self test lower limit.
+ * @scale: compass scale.
+ * @rate_scale: decide how fast a compass can read.
+ * @min_read_time: minimum time between each reading.
+ * @self_test: self test method of the slave.
+ * @set_scale: set scale of slave
+ * @get_scale: read scale back of the slave.
+ * @suspend: suspend operation.
+ * @resume: resume operation.
+ * @setup: setup chip. initialization.
+ * @combine_data: combine raw data into meaningful data.
+ * @read_data: read external sensor and output
+ * @get_mode: get current chip mode.
+ * @set_lpf: set low pass filter.
+ * @set_fs: set full scale
+ * @prev_ts: last time it is read.
+ */
+struct inv_mpu_slave {
+ const short *st_upper;
+ const short *st_lower;
+ int scale;
+ int rate_scale;
+ int min_read_time;
+ int (*self_test) (struct inv_mpu_state *);
+ int (*set_scale) (struct inv_mpu_state *, int scale);
+ int (*get_scale) (struct inv_mpu_state *, int *val);
+ int (*suspend) (struct inv_mpu_state *);
+ int (*resume) (struct inv_mpu_state *);
+ int (*setup) (struct inv_mpu_state *);
+ int (*combine_data) (u8 *in, short *out);
+ int (*read_data) (struct inv_mpu_state *, short *out);
+ int (*get_mode) (void);
+ int (*set_lpf) (struct inv_mpu_state *, int rate);
+ int (*set_fs) (struct inv_mpu_state *, int fs);
+ u64 prev_ts;
+};
+
+/* scan element definition */
+enum inv_mpu_scan {
+ INV_MPU_SCAN_TIMESTAMP,
+};
+
+/* IIO attribute address */
+enum MPU_IIO_ATTR_ADDR {
+ ATTR_DMP_GYRO_X_DMP_BIAS,
+ ATTR_DMP_GYRO_Y_DMP_BIAS,
+ ATTR_DMP_GYRO_Z_DMP_BIAS,
+ ATTR_DMP_GYRO_CAL_ENABLE,
+ ATTR_DMP_ACCEL_X_DMP_BIAS,
+ ATTR_DMP_ACCEL_Y_DMP_BIAS,
+ ATTR_DMP_ACCEL_Z_DMP_BIAS,
+ ATTR_DMP_MAGN_X_DMP_BIAS,
+ ATTR_DMP_MAGN_Y_DMP_BIAS,
+ ATTR_DMP_MAGN_Z_DMP_BIAS,
+ ATTR_DMP_MAGN_ACCURACY,
+ ATTR_GYRO_X_OFFSET,
+ ATTR_GYRO_Y_OFFSET,
+ ATTR_GYRO_Z_OFFSET,
+ ATTR_ACCEL_X_OFFSET,
+ ATTR_ACCEL_Y_OFFSET,
+ ATTR_ACCEL_Z_OFFSET,
+ ATTR_DMP_SC_AUTH,
+ ATTR_DMP_EIS_AUTH,
+ ATTR_DMP_ACCEL_CAL_ENABLE,
+ ATTR_DMP_PED_INT_ON,
+ ATTR_DMP_PED_STEP_THRESH,
+ ATTR_DMP_PED_INT_THRESH,
+ ATTR_DMP_PED_ON,
+ ATTR_DMP_SMD_ENABLE,
+ ATTR_DMP_TILT_ENABLE,
+ ATTR_DMP_PICK_UP_ENABLE,
+ ATTR_DMP_EIS_ENABLE,
+ ATTR_DMP_PEDOMETER_STEPS,
+ ATTR_DMP_PEDOMETER_TIME,
+ ATTR_DMP_PEDOMETER_COUNTER,
+ ATTR_DMP_LOW_POWER_GYRO_ON,
+ ATTR_DMP_LP_EN_OFF,
+ ATTR_DMP_CLK_SEL,
+ ATTR_DMP_DEBUG_MEM_READ,
+ ATTR_DMP_DEBUG_MEM_WRITE,
+ ATTR_DEBUG_REG_WRITE,
+ ATTR_DEBUG_WRITE_CFG,
+ ATTR_DEBUG_REG_ADDR,
+ ATTR_WOM_THLD,
+ /* *****above this line, are DMP features, power needs on/off */
+ /* *****below this line, are DMP features, no power needed */
+ ATTR_IN_POWER_ON,
+ ATTR_DMP_ON,
+ ATTR_DMP_EVENT_INT_ON,
+ ATTR_DMP_STEP_COUNTER_ON,
+ ATTR_DMP_STEP_COUNTER_WAKE_ON,
+ ATTR_DMP_BATCHMODE_TIMEOUT,
+ ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL,
+ ATTR_DMP_STEP_DETECTOR_ON,
+ ATTR_DMP_STEP_DETECTOR_WAKE_ON,
+ ATTR_DMP_ACTIVITY_ON,
+ ATTR_DMP_IN_ANGLVEL_ACCURACY_ENABLE,
+ ATTR_DMP_IN_ACCEL_ACCURACY_ENABLE,
+ ATTR_DMP_DEBUG_DETERMINE_ENGINE_ON,
+ ATTR_DMP_MISC_GYRO_RECALIBRATION,
+ ATTR_DMP_MISC_ACCEL_RECALIBRATION,
+ ATTR_DMP_PARAMS_ACCEL_CALIBRATION_THRESHOLD,
+ ATTR_DMP_PARAMS_ACCEL_CALIBRATION_RATE,
+ ATTR_GYRO_SCALE,
+ ATTR_ACCEL_SCALE,
+ ATTR_COMPASS_SCALE,
+ ATTR_COMPASS_SENSITIVITY_X,
+ ATTR_COMPASS_SENSITIVITY_Y,
+ ATTR_COMPASS_SENSITIVITY_Z,
+ ATTR_GYRO_ENABLE,
+ ATTR_ACCEL_ENABLE,
+ ATTR_COMPASS_ENABLE,
+ ATTR_FIRMWARE_LOADED,
+ ATTR_POKE_MODE,
+ ATTR_ANGLVEL_X_CALIBBIAS,
+ ATTR_ANGLVEL_Y_CALIBBIAS,
+ ATTR_ANGLVEL_Z_CALIBBIAS,
+ ATTR_ACCEL_X_CALIBBIAS,
+ ATTR_ACCEL_Y_CALIBBIAS,
+ ATTR_ACCEL_Z_CALIBBIAS,
+ ATTR_ANGLVEL_X_ST_CALIBBIAS,
+ ATTR_ANGLVEL_Y_ST_CALIBBIAS,
+ ATTR_ANGLVEL_Z_ST_CALIBBIAS,
+ ATTR_ANGLVEL_X_OIS_ST_CALIBBIAS,
+ ATTR_ANGLVEL_Y_OIS_ST_CALIBBIAS,
+ ATTR_ANGLVEL_Z_OIS_ST_CALIBBIAS,
+ ATTR_ACCEL_X_ST_CALIBBIAS,
+ ATTR_ACCEL_Y_ST_CALIBBIAS,
+ ATTR_ACCEL_Z_ST_CALIBBIAS,
+ ATTR_ACCEL_X_OIS_ST_CALIBBIAS,
+ ATTR_ACCEL_Y_OIS_ST_CALIBBIAS,
+ ATTR_ACCEL_Z_OIS_ST_CALIBBIAS,
+ ATTR_GYRO_MATRIX,
+ ATTR_ACCEL_MATRIX,
+ ATTR_COMPASS_MATRIX,
+ ATTR_FSYNC_FRAME_COUNT,
+ ATTR_SECONDARY_NAME,
+ ATTR_GYRO_SF,
+ ATTR_BAC_DRIVE_CONFIDENCE,
+ ATTR_BAC_WALK_CONFIDENCE,
+ ATTR_BAC_SMD_CONFIDENCE,
+ ATTR_BAC_BIKE_CONFIDENCE,
+ ATTR_BAC_STILL_CONFIDENCE,
+ ATTR_BAC_RUN_CONFIDENCE,
+ IN_OIS_ACCEL_FS,
+ IN_OIS_GYRO_FS,
+ IN_OIS_ENABLE,
+};
+
+int inv_mpu_configure_ring(struct iio_dev *indio_dev);
+int inv_mpu_probe_trigger(struct iio_dev *indio_dev);
+void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev);
+void inv_mpu_remove_trigger(struct iio_dev *indio_dev);
+#ifdef CONFIG_PM_SLEEP
+int inv_mpu_suspend(struct iio_dev *indio_dev);
+void inv_mpu_complete(struct iio_dev *indio_dev);
+#endif
+
+int inv_get_pedometer_steps(struct inv_mpu_state *st, int *ped);
+int inv_get_pedometer_time(struct inv_mpu_state *st, int *ped);
+int inv_read_pedometer_counter(struct inv_mpu_state *st);
+
+int inv_dmp_read(struct inv_mpu_state *st, int off, int size, u8 *buf);
+int inv_firmware_load(struct inv_mpu_state *st);
+
+int set_inv_enable(struct iio_dev *indio_dev);
+
+int inv_mpu_setup_compass_slave(struct inv_mpu_state *st);
+int inv_mpu_setup_pressure_slave(struct inv_mpu_state *st);
+int inv_mpu_setup_als_slave(struct inv_mpu_state *st);
+int inv_mpu_initialize(struct inv_mpu_state *st);
+int inv_set_accel_sf(struct inv_mpu_state *st);
+int inv_set_gyro_sf(struct inv_mpu_state *st);
+s64 get_time_ns(void);
+int inv_i2c_read_base(struct inv_mpu_state *st, u16 i, u8 r, u16 l, u8 *d);
+int inv_i2c_single_write_base(struct inv_mpu_state *st, u16 i, u8 r, u8 d);
+int write_be32_to_mem(struct inv_mpu_state *st, u32 data, int addr);
+int write_be16_to_mem(struct inv_mpu_state *st, u16 data, int addr);
+int read_be32_from_mem(struct inv_mpu_state *st, u32 *o, int addr);
+int read_be16_from_mem(struct inv_mpu_state *st, u16 *o, int addr);
+u32 inv_get_cntr_diff(u32 curr_counter, u32 prev);
+int inv_write_2bytes(struct inv_mpu_state *st, int k, int data);
+int inv_set_bank(struct inv_mpu_state *st, u8 bank);
+int inv_set_power(struct inv_mpu_state *st, bool power_on);
+int inv_switch_power_in_lp(struct inv_mpu_state *st, bool on);
+#ifndef CONFIG_INV_MPU_IIO_ICM20608D
+int inv_set_accel_config2(struct inv_mpu_state *st, bool cycle_mode);
+#endif
+int inv_stop_dmp(struct inv_mpu_state *st);
+int inv_reset_fifo(struct inv_mpu_state *st, bool turn_off);
+int inv_create_dmp_sysfs(struct iio_dev *ind);
+int inv_check_chip_type(struct iio_dev *indio_dev, const char *name);
+int inv_write_compass_matrix(struct inv_mpu_state *st, int *adj);
+irqreturn_t inv_read_fifo(int irq, void *dev_id);
+#ifdef TIMER_BASED_BATCHING
+void inv_batch_work(struct work_struct *work);
+#endif
+int inv_flush_batch_data(struct iio_dev *indio_dev, int data);
+static inline int mpu_memory_write(struct inv_mpu_state *st, u8 mpu_addr,
+ u16 mem_addr, u32 len, u8 const *data)
+{
+ int ret = -1;
+
+ if (st->mem_write)
+ ret = st->mem_write(st, mpu_addr, mem_addr, len, data);
+
+ return ret;
+}
+static inline int mpu_memory_read(struct inv_mpu_state *st, u8 mpu_addr,
+ u16 mem_addr, u32 len, u8 *data)
+{
+ int ret = -1;
+
+ if (st->mem_read)
+ ret = st->mem_read(st, mpu_addr, mem_addr, len, data);
+
+ return ret;
+}
+int inv_read_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int len);
+int inv_write_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int v);
+int inv_execute_write_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int v);
+int inv_execute_read_secondary(struct inv_mpu_state *st, int ind, int addr,
+ int reg, int len, u8 *d);
+
+int inv_push_16bytes_buffer(struct inv_mpu_state *st, u16 hdr,
+ u64 t, int *q, s16 accur);
+int inv_push_gyro_data(struct inv_mpu_state *st, s16 *raw, s32 *calib, u64 t);
+int inv_push_8bytes_buffer(struct inv_mpu_state *st, u16 hdr, u64 t, s16 *d);
+int inv_push_8bytes_kf(struct inv_mpu_state *st, u16 hdr, u64 t, s16 *d);
+
+void inv_push_step_indicator(struct inv_mpu_state *st, u64 t);
+int inv_send_steps(struct inv_mpu_state *st, int step, u64 t);
+int inv_push_marker_to_buffer(struct inv_mpu_state *st, u16 hdr, int data);
+
+int inv_check_sensor_on(struct inv_mpu_state *st);
+int inv_write_cntl(struct inv_mpu_state *st, u16 wd, bool en, int cntl);
+
+int inv_get_packet_size(struct inv_mpu_state *st, u16 hdr,
+ u32 *pk_size, u8 *dptr);
+int inv_parse_packet(struct inv_mpu_state *st, u16 hdr, u8 *dptr);
+int inv_pre_parse_packet(struct inv_mpu_state *st, u16 hdr, u8 *dptr);
+int inv_process_dmp_data(struct inv_mpu_state *st);
+
+int be32_to_int(u8 *d);
+void inv_convert_and_push_16bytes(struct inv_mpu_state *st, u16 hdr,
+ u8 *d, u64 t, s8 *m);
+void inv_convert_and_push_8bytes(struct inv_mpu_state *st, u16 hdr,
+ u8 *d, u64 t, s8 *m);
+int inv_get_dmp_ts(struct inv_mpu_state *st, int i);
+int inv_process_step_det(struct inv_mpu_state *st, u8 *dptr);
+int inv_process_eis(struct inv_mpu_state *st, u16 delay);
+int inv_rate_convert(struct inv_mpu_state *st, int ind, int data);
+
+int inv_setup_dmp_firmware(struct inv_mpu_state *st);
+/* used to print i2c data using pr_debug */
+char *wr_pr_debug_begin(u8 const *data, u32 len, char *string);
+char *wr_pr_debug_end(char *string);
+
+int inv_hw_self_test(struct inv_mpu_state *st);
+int inv_q30_mult(int a, int b);
+#ifdef ACCEL_BIAS_TEST
+int inv_get_3axis_average(s16 src[], s16 dst[], s16 reset);
+#endif
+
+static inline int inv_plat_single_write(struct inv_mpu_state *st,
+ u8 reg, u8 data)
+{
+ int ret = -1;
+
+ if (st->write)
+ ret = st->write(st, reg, data);
+
+ return ret;
+}
+static inline int inv_plat_read(struct inv_mpu_state *st, u8 reg,
+ int len, u8 *data)
+{
+ int ret = -1;
+
+ if (st->read)
+ ret = st->read(st, reg, len, data);
+
+ return ret;
+}
+irqreturn_t inv_read_fifo(int , void *);
+
+int inv_stop_interrupt(struct inv_mpu_state *st);
+int inv_reenable_interrupt(struct inv_mpu_state *st);
+
+int inv_enable_pedometer_interrupt(struct inv_mpu_state *st, bool en);
+int inv_dataout_control1(struct inv_mpu_state *st, u16 cntl1);
+int inv_dataout_control2(struct inv_mpu_state *st, u16 cntl2);
+int inv_motion_interrupt_control(struct inv_mpu_state *st,
+ u16 motion_event_cntl);
+
+int inv_bound_timestamp(struct inv_mpu_state *st);
+int inv_update_dmp_ts(struct inv_mpu_state *st, int ind);
+int inv_get_last_run_time_non_dmp_record_mode(struct inv_mpu_state *st);
+
+#define mem_w(a, b, c) mpu_memory_write(st, st->i2c_addr, a, b, c)
+#define mem_r(a, b, c) mpu_memory_read(st, st->i2c_addr, a, b, c)
+
+#endif /* #ifndef _INV_MPU_IIO_H_ */
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu/inv_mpu_ring.c
new file mode 100644
index 000000000000..3e5bccbea0df
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_ring.c
@@ -0,0 +1,643 @@
+/*
+* Copyright (C) 2012-2018 InvenSense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#define pr_fmt(fmt) "inv_mpu: " fmt
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/math64.h>
+#include <linux/miscdevice.h>
+
+#include "inv_mpu_iio.h"
+
+static void inv_push_timestamp(struct iio_dev *indio_dev, u64 t)
+{
+ u8 buf[IIO_BUFFER_BYTES];
+ struct inv_mpu_state *st;
+
+ st = iio_priv(indio_dev);
+ if (st->poke_mode_on)
+ memcpy(buf, &st->poke_ts, sizeof(t));
+ else
+ memcpy(buf, &t, sizeof(t));
+ iio_push_to_buffers(indio_dev, buf);
+}
+
+int inv_push_marker_to_buffer(struct inv_mpu_state *st, u16 hdr, int data)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+
+ memcpy(buf, &hdr, sizeof(hdr));
+ memcpy(&buf[4], &data, sizeof(data));
+ iio_push_to_buffers(indio_dev, buf);
+
+ return 0;
+}
+static int inv_calc_precision(struct inv_mpu_state *st)
+{
+ int diff;
+ int init;
+
+ if (st->eis.voting_state != 8)
+ return 0;
+ diff = abs(st->eis.fsync_delay_s[1] - st->eis.fsync_delay_s[0]);
+ init = 0;
+ if (diff)
+ init = st->sensor[SENSOR_GYRO].dur / diff;
+
+ if (abs(init - NSEC_PER_USEC) < (NSEC_PER_USEC >> 3))
+ st->eis.count_precision = init;
+ else
+ st->eis.voting_state = 0;
+
+ pr_debug("dur= %d prc= %d\n", st->sensor[SENSOR_GYRO].dur,
+ st->eis.count_precision);
+
+ return 0;
+}
+
+static s64 calc_frame_ave(struct inv_mpu_state *st, int delay)
+{
+ s64 ts;
+
+ ts = st->eis.current_timestamp - delay;
+#if defined(CONFIG_INV_MPU_IIO_ICM20648) | defined(CONFIG_INV_MPU_IIO_ICM20690)
+ ts -= st->ts_algo.gyro_ts_shift;
+#endif
+ pr_debug("shift= %d ts = %lld\n", st->ts_algo.gyro_ts_shift, ts);
+
+ return ts;
+}
+
+static void inv_push_eis_ring(struct inv_mpu_state *st, int *q, bool sync,
+ s64 t)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ struct inv_eis *eis = &st->eis;
+ u8 buf[IIO_BUFFER_BYTES];
+ int tmp, ii;
+
+ buf[0] = (EIS_GYRO_HDR & 0xff);
+ buf[1] = (EIS_GYRO_HDR >> 8);
+ memcpy(buf + 4, &q[0], sizeof(q[0]));
+ iio_push_to_buffers(indio_dev, buf);
+ for (ii = 0; ii < 2; ii++)
+ memcpy(buf + 4 * ii, &q[ii + 1], sizeof(q[ii]));
+ iio_push_to_buffers(indio_dev, buf);
+ tmp = eis->frame_count;
+ if (sync)
+ tmp |= 0x80000000;
+ memcpy(buf, &tmp, sizeof(tmp));
+ iio_push_to_buffers(indio_dev, buf);
+ inv_push_timestamp(indio_dev, t);
+}
+static int inv_do_interpolation_gyro(struct inv_mpu_state *st, int *prev,
+ s64 prev_t, int *curr, s64 curr_t, s64 t, bool trigger)
+{
+ int i;
+ int out[3];
+#if defined(CONFIG_INV_MPU_IIO_ICM20648) | defined(CONFIG_INV_MPU_IIO_ICM20690)
+ prev_t -= st->ts_algo.gyro_ts_shift;
+ prev_t += MPU_4X_TS_GYRO_SHIFT;
+ curr_t -= st->ts_algo.gyro_ts_shift;
+ curr_t += MPU_4X_TS_GYRO_SHIFT;
+#endif
+ if ((t > prev_t) && (t < curr_t)) {
+ for (i = 0; i < 3; i++)
+ out[i] = (int)div_s64((s64)(curr[i] - prev[i]) *
+ (s64)(t - prev_t), curr_t - prev_t) + prev[i];
+ } else if (t < prev_t) {
+ for (i = 0; i < 3; i++)
+ out[i] = prev[i];
+ } else {
+ for (i = 0; i < 3; i++)
+ out[i] = curr[i];
+ }
+ pr_debug("prev= %lld t = %lld curr= %lld\n", prev_t, t, curr_t);
+ pr_debug("prev = %d, %d, %d\n", prev[0], prev[1], prev[2]);
+ pr_debug("curr = %d, %d, %d\n", curr[0], curr[1], curr[2]);
+ pr_debug("out = %d, %d, %d\n", out[0], out[1], out[2]);
+ inv_push_eis_ring(st, out, trigger, t);
+
+ return 0;
+}
+#if defined(CONFIG_INV_MPU_IIO_ICM20648) | defined(CONFIG_INV_MPU_IIO_ICM20690)
+static void inv_handle_triggered_eis(struct inv_mpu_state *st)
+{
+ struct inv_eis *eis = &st->eis;
+ int delay;
+
+ if (st->eis.eis_frame) {
+ inv_calc_precision(st);
+ delay = ((int)st->eis.fsync_delay) * st->eis.count_precision;
+ eis->fsync_timestamp = calc_frame_ave(st, delay);
+ inv_do_interpolation_gyro(st,
+ st->eis.prev_gyro, st->eis.prev_timestamp,
+ st->eis.current_gyro, st->eis.current_timestamp,
+ eis->fsync_timestamp, true);
+ pr_debug("fsync=%lld, curr=%lld, delay=%d\n",
+ eis->fsync_timestamp, eis->current_timestamp, delay);
+ inv_push_eis_ring(st, st->eis.current_gyro, false,
+ st->eis.current_timestamp - st->ts_algo.gyro_ts_shift
+ + MPU_4X_TS_GYRO_SHIFT);
+ eis->last_fsync_timestamp = eis->fsync_timestamp;
+ } else {
+ pr_debug("cur= %lld\n", st->eis.current_timestamp);
+ inv_push_eis_ring(st, st->eis.current_gyro, false,
+ st->eis.current_timestamp - st->ts_algo.gyro_ts_shift
+ + MPU_4X_TS_GYRO_SHIFT);
+ }
+}
+#else
+static void inv_handle_triggered_eis(struct inv_mpu_state *st)
+{
+ struct inv_eis *eis = &st->eis;
+ int delay;
+
+ if ((st->eis.eis_frame && (st->eis.fsync_delay != 5)) ||
+ (st->eis.eis_frame && (st->eis.fsync_delay == 5) &&
+ (!st->eis.current_sync))
+ ) {
+ inv_calc_precision(st);
+ delay = ((int)st->eis.fsync_delay) * st->eis.count_precision;
+ eis->fsync_timestamp = calc_frame_ave(st, delay);
+ inv_do_interpolation_gyro(st,
+ st->eis.prev_gyro, st->eis.prev_timestamp,
+ st->eis.current_gyro, st->eis.current_timestamp,
+ eis->fsync_timestamp, true);
+ pr_debug("fsync=%lld, curr=%lld, delay=%d\n",
+ eis->fsync_timestamp, eis->current_timestamp, delay);
+ inv_push_eis_ring(st, st->eis.current_gyro, false,
+ st->eis.current_timestamp);
+ eis->last_fsync_timestamp = eis->fsync_timestamp;
+ st->eis.eis_frame = false;
+ } else {
+ st->eis.current_sync = false;
+ pr_debug("cur= %lld\n", st->eis.current_timestamp);
+ inv_push_eis_ring(st, st->eis.current_gyro, false,
+ st->eis.current_timestamp);
+ }
+}
+#endif
+static void inv_push_eis_buffer(struct inv_mpu_state *st, u64 t, int *q)
+{
+ int ii;
+
+ if (st->eis.eis_triggered) {
+ for (ii = 0; ii < 3; ii++)
+ st->eis.prev_gyro[ii] = st->eis.current_gyro[ii];
+ st->eis.prev_timestamp = st->eis.current_timestamp;
+
+ for (ii = 0; ii < 3; ii++)
+ st->eis.current_gyro[ii] = q[ii];
+ st->eis.current_timestamp = t;
+ inv_handle_triggered_eis(st);
+ } else {
+ for (ii = 0; ii < 3; ii++)
+ st->eis.current_gyro[ii] = q[ii];
+ st->eis.current_timestamp = t;
+ }
+}
+static int inv_push_16bytes_final(struct inv_mpu_state *st, int j,
+ s32 *q, u64 t, s16 accur)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+ int ii;
+
+ memcpy(buf, &st->sensor_l[j].header, sizeof(st->sensor_l[j].header));
+ memcpy(buf + 2, &accur, sizeof(accur));
+ memcpy(buf + 4, &q[0], sizeof(q[0]));
+ iio_push_to_buffers(indio_dev, buf);
+ for (ii = 0; ii < 2; ii++)
+ memcpy(buf + 4 * ii, &q[ii + 1], sizeof(q[ii]));
+ iio_push_to_buffers(indio_dev, buf);
+ inv_push_timestamp(indio_dev, t);
+ st->sensor_l[j].counter = 0;
+ if (st->sensor_l[j].wake_on)
+ st->wake_sensor_received = true;
+
+ return 0;
+}
+int inv_push_16bytes_buffer(struct inv_mpu_state *st, u16 sensor,
+ u64 t, int *q, s16 accur)
+{
+ int j;
+
+ for (j = 0; j < SENSOR_L_NUM_MAX; j++) {
+ if (st->sensor_l[j].on && (st->sensor_l[j].base == sensor)) {
+ st->sensor_l[j].counter++;
+ if ((st->sensor_l[j].div != 0xffff) &&
+ (st->sensor_l[j].counter >=
+ st->sensor_l[j].div)) {
+ pr_debug(
+ "Sensor_l = %d sensor = %d header [%04X] div [%d] ts [%lld] %d %d %d\n",
+ j, sensor,
+ st->sensor_l[j].header,
+ st->sensor_l[j].div,
+ t, q[0], q[1], q[2]);
+ inv_push_16bytes_final(st, j, q, t, accur);
+ }
+ }
+ }
+ return 0;
+}
+
+void inv_convert_and_push_16bytes(struct inv_mpu_state *st, u16 hdr,
+ u8 *d, u64 t, s8 *m)
+{
+ int i, j;
+ s32 in[3], out[3];
+
+ for (i = 0; i < 3; i++)
+ in[i] = be32_to_int(d + i * 4);
+ /* multiply with orientation matrix can be optimized like this */
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ if (m[i * 3 + j])
+ out[i] = in[j] * m[i * 3 + j];
+
+ inv_push_16bytes_buffer(st, hdr, t, out, 0);
+}
+
+void inv_convert_and_push_8bytes(struct inv_mpu_state *st, u16 hdr,
+ u8 *d, u64 t, s8 *m)
+{
+ int i, j;
+ s16 in[3], out[3];
+
+ for (i = 0; i < 3; i++)
+ in[i] = be16_to_cpup((__be16 *) (d + i * 2));
+
+ /* multiply with orientation matrix can be optimized like this */
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ if (m[i * 3 + j])
+ out[i] = in[j] * m[i * 3 + j];
+
+ inv_push_8bytes_buffer(st, hdr, t, out);
+}
+
+int inv_push_special_8bytes_buffer(struct inv_mpu_state *st,
+ u16 hdr, u64 t, s16 *d)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+ int j;
+
+ memcpy(buf, &hdr, sizeof(hdr));
+ memcpy(&buf[2], &d[0], sizeof(d[0]));
+ for (j = 0; j < 2; j++)
+ memcpy(&buf[4 + j * 2], &d[j + 1], sizeof(d[j]));
+ iio_push_to_buffers(indio_dev, buf);
+ inv_push_timestamp(indio_dev, t);
+
+ return 0;
+}
+
+static int inv_s16_gyro_push(struct inv_mpu_state *st, int i, s16 *raw, u64 t)
+{
+ if (st->sensor_l[i].on) {
+ st->sensor_l[i].counter++;
+ if ((st->sensor_l[i].div != 0xffff) &&
+ (st->sensor_l[i].counter >= st->sensor_l[i].div)) {
+ inv_push_special_8bytes_buffer(st,
+ st->sensor_l[i].header, t, raw);
+ st->sensor_l[i].counter = 0;
+ if (st->sensor_l[i].wake_on)
+ st->wake_sensor_received = true;
+ }
+ }
+
+ return 0;
+}
+
+static int inv_s32_gyro_push(struct inv_mpu_state *st, int i, s32 *calib, u64 t)
+{
+ if (st->sensor_l[i].on) {
+ st->sensor_l[i].counter++;
+ if ((st->sensor_l[i].div != 0xffff) &&
+ (st->sensor_l[i].counter >= st->sensor_l[i].div)) {
+ inv_push_16bytes_final(st, i, calib, t, 0);
+ st->sensor_l[i].counter = 0;
+ if (st->sensor_l[i].wake_on)
+ st->wake_sensor_received = true;
+ }
+ }
+
+ return 0;
+}
+
+int inv_push_gyro_data(struct inv_mpu_state *st, s16 *raw, s32 *calib, u64 t)
+{
+ int gyro_data[] = {SENSOR_L_GYRO, SENSOR_L_GYRO_WAKE};
+ int calib_data[] = {SENSOR_L_GYRO_CAL, SENSOR_L_GYRO_CAL_WAKE};
+ int i;
+
+ if (st->sensor_l[SENSOR_L_EIS_GYRO].on)
+ inv_push_eis_buffer(st, t, calib);
+
+ for (i = 0; i < 2; i++)
+ inv_s16_gyro_push(st, gyro_data[i], raw, t);
+ for (i = 0; i < 2; i++)
+ inv_s32_gyro_push(st, calib_data[i], calib, t);
+
+ return 0;
+}
+int inv_push_8bytes_buffer(struct inv_mpu_state *st, u16 sensor, u64 t, s16 *d)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+ int ii, j;
+
+ if ((sensor == STEP_DETECTOR_HDR) ||
+ (sensor == STEP_DETECTOR_WAKE_HDR)) {
+ memcpy(buf, &sensor, sizeof(sensor));
+ memcpy(&buf[2], &d[0], sizeof(d[0]));
+ for (j = 0; j < 2; j++)
+ memcpy(&buf[4 + j * 2], &d[j + 1], sizeof(d[j]));
+ iio_push_to_buffers(indio_dev, buf);
+ inv_push_timestamp(indio_dev, t);
+ if (sensor == STEP_DETECTOR_WAKE_HDR)
+ st->wake_sensor_received = true;
+ return 0;
+ }
+ for (ii = 0; ii < SENSOR_L_NUM_MAX; ii++) {
+ if (st->sensor_l[ii].on &&
+ (st->sensor_l[ii].base == sensor) &&
+ (st->sensor_l[ii].div != 0xffff)) {
+ st->sensor_l[ii].counter++;
+ if (st->sensor_l[ii].counter >= st->sensor_l[ii].div) {
+ pr_debug(
+ "Sensor_l = %d sensor = %d header [%04X] div [%d] ts [%lld] %d %d %d\n",
+ ii, sensor, st->sensor_l[ii].header,
+ st->sensor_l[ii].div, t, d[0], d[1], d[2]);
+
+ memcpy(buf, &st->sensor_l[ii].header,
+ sizeof(st->sensor_l[ii].header));
+ memcpy(&buf[2], &d[0], sizeof(d[0]));
+ for (j = 0; j < 2; j++)
+ memcpy(&buf[4 + j * 2], &d[j + 1],
+ sizeof(d[j]));
+
+ iio_push_to_buffers(indio_dev, buf);
+ inv_push_timestamp(indio_dev, t);
+ st->sensor_l[ii].counter = 0;
+ if (st->sensor_l[ii].wake_on)
+ st->wake_sensor_received = true;
+ }
+ }
+ }
+
+ return 0;
+}
+#ifdef CONFIG_INV_MPU_IIO_ICM20648
+/* Implemented activity to string function for BAC test */
+#define TILT_DETECTED 0x1000
+#define NONE 0x00
+#define DRIVE 0x01
+#define WALK 0x02
+#define RUN 0x04
+#define BIKE 0x08
+#define TILT 0x10
+#define STILL 0x20
+#define DRIVE_WALK (DRIVE | WALK)
+#define DRIVE_RUN (DRIVE | RUN)
+
+char *act_string(s16 data)
+{
+ data &= (~TILT);
+ switch (data) {
+ case NONE:
+ return "None";
+ case DRIVE:
+ return "Drive";
+ case WALK:
+ return "Walk";
+ case RUN:
+ return "Run";
+ case BIKE:
+ return "Bike";
+ case STILL:
+ return "Still";
+ case DRIVE_WALK:
+ return "drive and walk";
+ case DRIVE_RUN:
+ return "drive and run";
+ default:
+ return "Unknown";
+ }
+ return "Unknown";
+}
+
+char *inv_tilt_check(s16 data)
+{
+ if (data & TILT)
+ return "Tilt";
+ else
+ return "None";
+}
+
+int inv_push_8bytes_kf(struct inv_mpu_state *st, u16 hdr, u64 t, s16 *d)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ u8 buf[IIO_BUFFER_BYTES];
+ int i;
+
+ if (st->chip_config.activity_on) {
+ memcpy(buf, &hdr, sizeof(hdr));
+ for (i = 0; i < 3; i++)
+ memcpy(&buf[2 + i * 2], &d[i], sizeof(d[i]));
+
+ kfifo_in(&st->kf, buf, IIO_BUFFER_BYTES);
+ memcpy(buf, &t, sizeof(t));
+ kfifo_in(&st->kf, buf, IIO_BUFFER_BYTES);
+ st->activity_size += IIO_BUFFER_BYTES * 2;
+ }
+ if (st->chip_config.tilt_enable) {
+ pr_debug("d[0] = %04X, [%X : %s] to [%X : %s]",
+ d[0], d[0] & 0x00FF,
+ inv_tilt_check(d[0] & 0x00FF),
+ (d[0] & 0xFF00) >> 8, inv_tilt_check((d[0] & 0xFF00) >> 8));
+ sysfs_notify(&indio_dev->dev.kobj, NULL, "poll_tilt");
+ }
+
+ pr_debug("d[0] = %04X, [%X : %s] to [%X : %s]", d[0], d[0] & 0x00FF,
+ act_string(d[0] & 0x00FF),
+ (d[0] & 0xFF00) >> 8, act_string((d[0] & 0xFF00) >> 8));
+
+ read_be32_from_mem(st, &st->bac_drive_conf, BAC_DRIVE_CONFIDENCE);
+ read_be32_from_mem(st, &st->bac_walk_conf, BAC_WALK_CONFIDENCE);
+ read_be32_from_mem(st, &st->bac_smd_conf, BAC_SMD_CONFIDENCE);
+ read_be32_from_mem(st, &st->bac_bike_conf, BAC_BIKE_CONFIDENCE);
+ read_be32_from_mem(st, &st->bac_still_conf, BAC_STILL_CONFIDENCE);
+ read_be32_from_mem(st, &st->bac_run_conf, BAC_RUN_CONFIDENCE);
+
+ return 0;
+}
+#endif
+
+int inv_send_steps(struct inv_mpu_state *st, int step, u64 ts)
+{
+ s16 s[3];
+
+ s[0] = 0;
+ s[1] = (s16) (step & 0xffff);
+ s[2] = (s16) ((step >> 16) & 0xffff);
+ if (st->step_counter_l_on)
+ inv_push_special_8bytes_buffer(st, STEP_COUNTER_HDR, ts, s);
+ if (st->step_counter_wake_l_on) {
+ inv_push_special_8bytes_buffer(st, STEP_COUNTER_WAKE_HDR,
+ ts, s);
+ st->wake_sensor_received = true;
+ }
+ return 0;
+}
+
+void inv_push_step_indicator(struct inv_mpu_state *st, u64 t)
+{
+ s16 sen[3];
+#define STEP_INDICATOR_HEADER 0x0001
+
+ sen[0] = 0;
+ sen[1] = 0;
+ sen[2] = 0;
+ inv_push_8bytes_buffer(st, STEP_INDICATOR_HEADER, t, sen);
+}
+
+/*
+ * inv_irq_handler() - Cache a timestamp at each data ready interrupt.
+ */
+static irqreturn_t inv_irq_handler(int irq, void *dev_id)
+{
+ return IRQ_WAKE_THREAD;
+}
+
+#ifdef TIMER_BASED_BATCHING
+static enum hrtimer_restart inv_batch_timer_handler(struct hrtimer *timer)
+{
+ struct inv_mpu_state *st =
+ container_of(timer, struct inv_mpu_state, hr_batch_timer);
+
+ if (st->chip_config.gyro_enable || st->chip_config.accel_enable) {
+ hrtimer_forward_now(&st->hr_batch_timer,
+ ns_to_ktime(st->batch_timeout));
+ schedule_work(&st->batch_work);
+ return HRTIMER_RESTART;
+ }
+ st->is_batch_timer_running = 0;
+ return HRTIMER_NORESTART;
+}
+#endif
+
+void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+#ifdef KERNEL_VERSION_4_X
+ devm_free_irq(st->dev, st->irq, st);
+ devm_iio_kfifo_free(st->dev, indio_dev->buffer);
+#else
+ free_irq(st->irq, st);
+ iio_kfifo_free(indio_dev->buffer);
+#endif
+};
+EXPORT_SYMBOL_GPL(inv_mpu_unconfigure_ring);
+
+#ifndef KERNEL_VERSION_4_X
+static int inv_predisable(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static int inv_preenable(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops inv_mpu_ring_setup_ops = {
+ .preenable = &inv_preenable,
+ .predisable = &inv_predisable,
+};
+#endif
+
+int inv_mpu_configure_ring(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ struct iio_buffer *ring;
+
+#ifdef TIMER_BASED_BATCHING
+ /* configure hrtimer */
+ hrtimer_init(&st->hr_batch_timer, CLOCK_BOOTTIME, HRTIMER_MODE_REL);
+ st->hr_batch_timer.function = inv_batch_timer_handler;
+ INIT_WORK(&st->batch_work, inv_batch_work);
+#endif
+#ifdef KERNEL_VERSION_4_X
+ ring = devm_iio_kfifo_allocate(st->dev);
+ if (!ring)
+ return -ENOMEM;
+ ring->scan_timestamp = true;
+ iio_device_attach_buffer(indio_dev, ring);
+ ret = devm_request_threaded_irq(st->dev,
+ st->irq,
+ inv_irq_handler,
+ inv_read_fifo,
+ IRQF_TRIGGER_RISING | IRQF_SHARED,
+ "inv_irq",
+ st);
+ if (ret) {
+ devm_iio_kfifo_free(st->dev, ring);
+ return ret;
+ }
+
+ // this mode does not use ops
+ indio_dev->modes = INDIO_ALL_BUFFER_MODES;
+
+ return ret;
+#else
+ ring = iio_kfifo_allocate(indio_dev);
+ if (!ring)
+ return -ENOMEM;
+ indio_dev->buffer = ring;
+ /* setup ring buffer */
+ ring->scan_timestamp = true;
+ indio_dev->setup_ops = &inv_mpu_ring_setup_ops;
+ ret = request_threaded_irq(st->irq,
+ inv_irq_handler,
+ inv_read_fifo,
+ IRQF_TRIGGER_RISING | IRQF_SHARED,
+ "inv_irq",
+ st);
+ if (ret)
+ goto error_iio_sw_rb_free;
+
+ indio_dev->modes |= INDIO_BUFFER_HARDWARE;
+
+ return 0;
+error_iio_sw_rb_free:
+ iio_kfifo_free(indio_dev->buffer);
+
+ return ret;
+#endif
+}
+EXPORT_SYMBOL_GPL(inv_mpu_configure_ring);
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu/inv_mpu_spi.c
new file mode 100644
index 000000000000..fb916788a9df
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_spi.c
@@ -0,0 +1,410 @@
+/*
+* Copyright (C) 2012-2018 InvenSense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#define pr_fmt(fmt) "inv_mpu: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+
+#include "inv_mpu_iio.h"
+#include "inv_mpu_dts.h"
+
+#define INV_SPI_READ 0x80
+
+static int inv_spi_single_write(struct inv_mpu_state *st, u8 reg, u8 data)
+{
+ struct spi_message msg;
+ int res;
+ u8 d[2];
+ struct spi_transfer xfers = {
+ .tx_buf = d,
+ .bits_per_word = 8,
+ .len = 2,
+ };
+
+ pr_debug("reg_write: reg=0x%x data=0x%x\n", reg, data);
+ d[0] = reg;
+ d[1] = data;
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfers, &msg);
+ res = spi_sync(to_spi_device(st->dev), &msg);
+
+ return res;
+}
+
+static int inv_spi_read(struct inv_mpu_state *st, u8 reg, int len, u8 *data)
+{
+ struct spi_message msg;
+ int res;
+ u8 d[1];
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = d,
+ .bits_per_word = 8,
+ .len = 1,
+ },
+ {
+ .rx_buf = data,
+ .bits_per_word = 8,
+ .len = len,
+ }
+ };
+
+ if (!data)
+ return -EINVAL;
+
+ d[0] = (reg | INV_SPI_READ);
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfers[0], &msg);
+ spi_message_add_tail(&xfers[1], &msg);
+ res = spi_sync(to_spi_device(st->dev), &msg);
+
+ if (len ==1)
+ pr_debug("reg_read: reg=0x%x length=%d data=0x%x\n",
+ reg, len, data[0]);
+ else
+ pr_debug("reg_read: reg=0x%x length=%d d0=0x%x d1=0x%x\n",
+ reg, len, data[0], data[1]);
+
+ return res;
+
+}
+
+static int inv_spi_mem_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 const *data)
+{
+ struct spi_message msg;
+ u8 buf[258];
+ int res;
+
+ struct spi_transfer xfers = {
+ .tx_buf = buf,
+ .bits_per_word = 8,
+ .len = len + 1,
+ };
+
+ if (!data || !st)
+ return -EINVAL;
+
+ if (len > (sizeof(buf) - 1))
+ return -ENOMEM;
+
+ inv_plat_single_write(st, REG_MEM_BANK_SEL, mem_addr >> 8);
+ inv_plat_single_write(st, REG_MEM_START_ADDR, mem_addr & 0xFF);
+
+ buf[0] = REG_MEM_R_W;
+ memcpy(buf + 1, data, len);
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfers, &msg);
+ res = spi_sync(to_spi_device(st->dev), &msg);
+
+ return res;
+}
+
+static int inv_spi_mem_read(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr,
+ u32 len, u8 *data)
+{
+ int res;
+
+ if (!data || !st)
+ return -EINVAL;
+
+ if (len > 256)
+ return -EINVAL;
+
+ res = inv_plat_single_write(st, REG_MEM_BANK_SEL, mem_addr >> 8);
+ res = inv_plat_single_write(st, REG_MEM_START_ADDR, mem_addr & 0xFF);
+ res = inv_plat_read(st, REG_MEM_R_W, len, data);
+
+ return res;
+}
+
+/*
+ * inv_mpu_probe() - probe function.
+ */
+static int inv_mpu_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct inv_mpu_state *st;
+ struct iio_dev *indio_dev;
+ int result;
+
+#ifdef KERNEL_VERSION_4_X
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL) {
+ pr_err("memory allocation failed\n");
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+#else
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ pr_err("memory allocation failed\n");
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+#endif
+ st = iio_priv(indio_dev);
+ st->write = inv_spi_single_write;
+ st->read = inv_spi_read;
+ st->mem_write = inv_spi_mem_write;
+ st->mem_read = inv_spi_mem_read;
+ st->dev = &spi->dev;
+ st->irq = spi->irq;
+#if !defined(CONFIG_INV_MPU_IIO_ICM20602) \
+ && !defined(CONFIG_INV_MPU_IIO_IAM20680)
+ st->i2c_dis = BIT_I2C_IF_DIS;
+#endif
+ st->bus_type = BUS_SPI;
+ spi_set_drvdata(spi, indio_dev);
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = id->name;
+
+#ifdef CONFIG_OF
+ result = invensense_mpu_parse_dt(st->dev, &st->plat_data);
+ if (result)
+# ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+# else
+ goto out_free;
+# endif
+ /* Power on device */
+ if (st->plat_data.power_on) {
+ result = st->plat_data.power_on(&st->plat_data);
+ if (result < 0) {
+ dev_err(st->dev, "power_on failed: %d\n", result);
+# ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+# else
+ goto out_free;
+# endif
+ }
+ pr_info("%s: power on here.\n", __func__);
+ }
+ pr_info("%s: power on.\n", __func__);
+
+ msleep(100);
+#else
+ if (dev_get_platdata(st->dev) == NULL)
+# ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+# else
+ goto out_free;
+# endif
+ st->plat_data = *(struct mpu_platform_data *)dev_get_platdata(st->dev);
+#endif
+
+ /* power is turned on inside check chip type */
+ result = inv_check_chip_type(indio_dev, id->name);
+ if (result)
+#ifdef KERNEL_VERSION_4_X
+ return -ENODEV;
+#else
+ goto out_free;
+#endif
+
+ result = inv_mpu_configure_ring(indio_dev);
+ if (result) {
+ pr_err("configure ring buffer fail\n");
+ goto out_free;
+ }
+#ifdef KERNEL_VERSION_4_X
+ result = devm_iio_device_register(st->dev, indio_dev);
+ if (result) {
+ pr_err("IIO device register fail\n");
+ goto out_unreg_ring;
+ }
+#else
+ result = iio_buffer_register(indio_dev, indio_dev->channels,
+ indio_dev->num_channels);
+ if (result) {
+ pr_err("ring buffer register fail\n");
+ goto out_unreg_ring;
+ }
+
+ result = iio_device_register(indio_dev);
+ if (result) {
+ pr_err("IIO device register fail\n");
+ goto out_remove_ring;
+ }
+#endif
+
+ result = inv_create_dmp_sysfs(indio_dev);
+ if (result) {
+ pr_err("create dmp sysfs failed\n");
+ goto out_unreg_iio;
+ }
+ init_waitqueue_head(&st->wait_queue);
+ st->resume_state = true;
+#ifdef CONFIG_HAS_WAKELOCK
+ wake_lock_init(&st->wake_lock, WAKE_LOCK_SUSPEND, "inv_mpu");
+#else
+ wakeup_source_init(&st->wake_lock, "inv_mpu");
+#endif
+ dev_info(st->dev, "%s ma-kernel-%s is ready to go!\n",
+ indio_dev->name, INVENSENSE_DRIVER_VERSION);
+
+#ifdef SENSOR_DATA_FROM_REGISTERS
+ pr_info("Data read from registers\n");
+#else
+ pr_info("Data read from FIFO\n");
+#endif
+#ifdef TIMER_BASED_BATCHING
+ pr_info("Timer based batching\n");
+#endif
+
+ return 0;
+#ifdef KERNEL_VERSION_4_X
+out_unreg_iio:
+ devm_iio_device_unregister(st->dev, indio_dev);
+out_unreg_ring:
+ inv_mpu_unconfigure_ring(indio_dev);
+out_free:
+ devm_iio_device_free(st->dev, indio_dev);
+out_no_free:
+#else
+out_unreg_iio:
+ iio_device_unregister(indio_dev);
+out_remove_ring:
+ iio_buffer_unregister(indio_dev);
+out_unreg_ring:
+ inv_mpu_unconfigure_ring(indio_dev);
+out_free:
+ iio_device_free(indio_dev);
+out_no_free:
+#endif
+ dev_err(st->dev, "%s failed %d\n", __func__, result);
+
+ return -EIO;
+}
+
+static void inv_mpu_shutdown(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+ int result;
+
+ mutex_lock(&indio_dev->mlock);
+ inv_switch_power_in_lp(st, true);
+ dev_dbg(st->dev, "Shutting down %s...\n", st->hw->name);
+
+ /* reset to make sure previous state are not there */
+ result = inv_plat_single_write(st, REG_PWR_MGMT_1, BIT_H_RESET);
+ if (result)
+ dev_err(st->dev, "Failed to reset %s\n",
+ st->hw->name);
+ msleep(POWER_UP_TIME);
+ /* turn off power to ensure gyro engine is off */
+ result = inv_set_power(st, false);
+ if (result)
+ dev_err(st->dev, "Failed to turn off %s\n",
+ st->hw->name);
+ inv_switch_power_in_lp(st, false);
+ mutex_unlock(&indio_dev->mlock);
+}
+
+/*
+ * inv_mpu_remove() - remove function.
+ */
+static int inv_mpu_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct inv_mpu_state *st = iio_priv(indio_dev);
+
+#ifdef KERNEL_VERSION_4_X
+ devm_iio_device_unregister(st->dev, indio_dev);
+#else
+ iio_device_unregister(indio_dev);
+ iio_buffer_unregister(indio_dev);
+#endif
+ inv_mpu_unconfigure_ring(indio_dev);
+#ifdef KERNEL_VERSION_4_X
+ devm_iio_device_free(st->dev, indio_dev);
+#else
+ iio_device_free(indio_dev);
+#endif
+ dev_info(st->dev, "inv-mpu-iio module removed.\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int inv_mpu_spi_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(to_spi_device(dev));
+
+ return inv_mpu_suspend(indio_dev);
+}
+
+static void inv_mpu_spi_complete(struct device *dev)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(to_spi_device(dev));
+
+ inv_mpu_complete(indio_dev);
+}
+#endif
+
+static const struct dev_pm_ops inv_mpu_spi_pmops = {
+#ifdef CONFIG_PM_SLEEP
+ .suspend = inv_mpu_spi_suspend,
+ .complete = inv_mpu_spi_complete,
+#endif
+};
+
+/* device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct spi_device_id inv_mpu_id[] = {
+#ifdef CONFIG_INV_MPU_IIO_ICM20648
+ {"icm20645", ICM20645},
+ {"icm10340", ICM10340},
+ {"icm20648", ICM20648},
+#else
+ {"icm20608d", ICM20608D},
+ {"icm20690", ICM20690},
+ {"icm20602", ICM20602},
+ {"iam20680", IAM20680},
+#endif
+ {}
+};
+
+MODULE_DEVICE_TABLE(spi, inv_mpu_id);
+
+static struct spi_driver inv_mpu_driver = {
+ .probe = inv_mpu_probe,
+ .remove = inv_mpu_remove,
+ .shutdown = inv_mpu_shutdown,
+ .id_table = inv_mpu_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv-mpu-iio-spi",
+ .pm = &inv_mpu_spi_pmops,
+ },
+};
+module_spi_driver(inv_mpu_driver);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense SPI device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/inv_mpu/inv_mpu_timestamp.c b/drivers/iio/imu/inv_mpu/inv_mpu_timestamp.c
new file mode 100644
index 000000000000..2cc721b18596
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_mpu_timestamp.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2012-2018 InvenSense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "inv_mpu: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/math64.h>
+
+#include "inv_mpu_iio.h"
+
+#define INV_TIME_CALIB_THRESHOLD_1 2
+
+#define MIN_DELAY (3 * NSEC_PER_MSEC)
+#define JITTER_THRESH ( 1 * NSEC_PER_MSEC)
+
+int inv_update_dmp_ts(struct inv_mpu_state *st, int ind)
+{
+ int i;
+ u32 counter;
+ u64 ts;
+ enum INV_ENGINE en_ind;
+ struct inv_timestamp_algo *ts_algo = &st->ts_algo;
+ u32 base_time;
+ u64 cal_period;
+
+ if (st->mode_1k_on)
+ cal_period = (NSEC_PER_SEC >> 2);
+ else
+ cal_period = 2 * NSEC_PER_SEC;
+
+ ts = ts_algo->last_run_time - st->sensor[ind].time_calib;
+ counter = st->sensor[ind].sample_calib;
+ en_ind = st->sensor[ind].engine_base;
+ if (en_ind != ts_algo->clock_base)
+ return 0;
+ /* we average over 2 seconds period to do the timestamp calculation */
+ if (ts < cal_period)
+ return 0;
+ /* this is the first time we do timestamp averaging, return */
+ /* after resume from suspend, the clock of linux has up to 1 seconds
+ drift. We should start from the resume clock instead of using clock
+ before resume */
+ if ((!st->sensor[ind].calib_flag) || ts_algo->resume_flag) {
+ st->sensor[ind].sample_calib = 0;
+ st->sensor[ind].time_calib = ts_algo->last_run_time;
+ st->sensor[ind].calib_flag = 1;
+ ts_algo->resume_flag = false;
+
+ return 0;
+ }
+ /* if the sample number in current FIFO is not zero and between now and
+ last update time is more than 2 seconds, we do calculation */
+ if ((counter > 0) &&
+ (ts_algo->last_run_time - st->eng_info[en_ind].last_update_time >
+ cal_period)) {
+ /* duration for each sensor */
+ st->sensor[ind].dur = (u32) div_u64(ts, counter);
+ /* engine duration derived from each sensor */
+ if (st->sensor[ind].div)
+ st->eng_info[en_ind].dur = st->sensor[ind].dur /
+ st->sensor[ind].div;
+ else
+ pr_err("sensor %d divider zero!\n", ind);
+ /* update base time for each sensor */
+ if (st->eng_info[en_ind].divider) {
+ base_time = (st->eng_info[en_ind].dur /
+ st->eng_info[en_ind].divider) *
+ st->eng_info[en_ind].orig_rate;
+ if (st->mode_1k_on)
+ st->eng_info[en_ind].base_time_1k = base_time;
+ else
+ st->eng_info[en_ind].base_time = base_time;
+ } else {
+ pr_err("engine %d divider zero!\n", en_ind);
+ }
+
+ st->eng_info[en_ind].last_update_time = ts_algo->last_run_time;
+ /* update all the sensors duration based on the same engine */
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on &&
+ (st->sensor[i].engine_base == en_ind))
+ st->sensor[i].dur = st->sensor[i].div *
+ st->eng_info[en_ind].dur;
+ }
+
+ }
+ st->sensor[ind].sample_calib = 0;
+ st->sensor[ind].time_calib = ts_algo->last_run_time;
+
+ return 0;
+}
+/**
+ * int inv_get_last_run_time_non_dmp_record_mode(struct inv_mpu_state *st)
+ * This is the function to get last run time in non dmp and record mode.
+ * This function will update the last_run_time, which is important parameter
+ * in overall timestamp algorithm.
+ * return value: this function returns fifo count value.
+*/
+int inv_get_last_run_time_non_dmp_record_mode(struct inv_mpu_state *st)
+{
+ long long t_pre, t_post, dur;
+ int fifo_count;
+#ifndef SENSOR_DATA_FROM_REGISTERS
+ int res;
+ u8 data[2];
+#endif
+
+ t_pre = get_time_ns();
+#ifndef SENSOR_DATA_FROM_REGISTERS
+ res = inv_plat_read(st, REG_FIFO_COUNT_H, FIFO_COUNT_BYTE, data);
+ if (res) {
+ pr_info("read REG_FIFO_COUNT_H failed= %d\n", res);
+ return 0;
+ }
+#endif
+ t_post = get_time_ns();
+
+#ifdef SENSOR_DATA_FROM_REGISTERS
+ if (st->fifo_count_mode == BYTE_MODE)
+ fifo_count = st->batch.pk_size;
+ else
+ fifo_count = 1;
+#else
+ fifo_count = be16_to_cpup((__be16 *) (data));
+#endif
+ pr_debug("fifc=%d\n", fifo_count);
+ if (!fifo_count)
+ return 0;
+ if (st->special_mag_mode && (fifo_count == 2)) {
+ pr_debug("special trigger\n");
+ fifo_count = 1;
+ }
+
+ /* In non DMP mode, either gyro or accel duration is the duration
+ for each sample */
+ if (st->chip_config.gyro_enable)
+ dur = st->eng_info[ENGINE_GYRO].dur;
+ else
+ dur = st->eng_info[ENGINE_ACCEL].dur;
+
+ if (st->fifo_count_mode == BYTE_MODE) {
+ fifo_count /= st->batch.pk_size;
+ }
+
+ /* In record mode, each number in fifo_count is 1 record or 1 sample */
+ st->ts_algo.last_run_time += dur * fifo_count;
+ if (st->ts_algo.last_run_time < t_pre)
+ st->ts_algo.last_run_time = t_pre;
+ if (st->ts_algo.last_run_time > t_post)
+ st->ts_algo.last_run_time = t_post;
+
+ return fifo_count;
+}
+
+int inv_get_dmp_ts(struct inv_mpu_state *st, int i)
+{
+ u64 current_time;
+ int expected_lower_duration, expected_upper_duration;
+
+ current_time = get_time_ns();
+
+ st->sensor[i].ts += st->sensor[i].dur + st->sensor[i].ts_adj;
+
+ if (st->sensor[i].ts < st->sensor[i].previous_ts)
+ st->sensor[i].ts = st->sensor[i].previous_ts + st->sensor[i].dur;
+
+ //hifi sensor limits ts jitter to +/- 2%
+ expected_upper_duration = st->eng_info[st->sensor[i].engine_base].divider * 1020000;
+ expected_lower_duration = st->eng_info[st->sensor[i].engine_base].divider * 980000;
+#if defined(CONFIG_INV_MPU_IIO_ICM20602) || defined(CONFIG_INV_MPU_IIO_ICM20690) || defined(CONFIG_INV_MPU_IIO_IAM20680)
+ if (st->sensor[i].ts < st->sensor[i].previous_ts + expected_lower_duration)
+ st->sensor[i].ts = st->sensor[i].previous_ts + expected_lower_duration;
+ if (st->sensor[i].ts > st->sensor[i].previous_ts + expected_upper_duration)
+ st->sensor[i].ts = st->sensor[i].previous_ts + expected_upper_duration;
+#endif
+ if (st->sensor[i].ts > current_time )
+ st->sensor[i].ts = current_time;
+
+ st->sensor[i].previous_ts = st->sensor[i].ts;
+
+ pr_debug("ts=%lld, reset=%lld\n", st->sensor[i].ts, st->ts_algo.reset_ts);
+ if (st->sensor[i].ts < st->ts_algo.reset_ts) {
+ pr_debug("less than reset\n");
+ st->sensor[i].send = false;
+ } else {
+ st->sensor[i].send = true;
+ }
+
+ if (st->header_count == 1)
+ inv_update_dmp_ts(st, i);
+
+ return 0;
+}
+
+static void process_sensor_bounding(struct inv_mpu_state *st, int i)
+{
+ s64 elaps_time, thresh1, thresh2;
+ struct inv_timestamp_algo *ts_algo = &st->ts_algo;
+ u32 dur;
+
+ elaps_time = ((u64) (st->sensor[i].dur)) * st->sensor[i].count;
+ thresh1 = ts_algo->last_run_time - elaps_time;
+
+ dur = max(st->sensor[i].dur, (int)MIN_DELAY);
+ thresh2 = thresh1 - dur;
+ if (thresh1 < 0)
+ thresh1 = 0;
+ if (thresh2 < 0)
+ thresh2 = 0;
+ st->sensor[i].ts_adj = 0;
+ if ((ts_algo->calib_counter >= INV_TIME_CALIB_THRESHOLD_1) &&
+ (!ts_algo->resume_flag)) {
+ if (st->sensor[i].ts < thresh2)
+ st->sensor[i].ts_adj = thresh2 - st->sensor[i].ts;
+ } else if ((ts_algo->calib_counter >=
+ INV_TIME_CALIB_THRESHOLD_1) && ts_algo->resume_flag) {
+ if (st->sensor[i].ts < thresh2)
+ st->sensor[i].ts = ts_algo->last_run_time -
+ elaps_time - JITTER_THRESH;
+ } else {
+ st->sensor[i].ts = ts_algo->last_run_time - elaps_time -
+ JITTER_THRESH;
+ st->sensor[i].previous_ts = st->sensor[i].ts;
+ }
+
+ if (st->sensor[i].ts > thresh1)
+ st->sensor[i].ts_adj = thresh1 - st->sensor[i].ts;
+ pr_debug("cali=%d\n", st->ts_algo.calib_counter);
+ pr_debug("adj= %lld\n", st->sensor[i].ts_adj);
+ pr_debug("dur= %d count= %d last= %lld\n", st->sensor[i].dur,
+ st->sensor[i].count, ts_algo->last_run_time);
+ if (st->sensor[i].ts_adj && (st->sensor[i].count > 1))
+ st->sensor[i].ts_adj = div_s64(st->sensor[i].ts_adj,
+ st->sensor[i].count);
+}
+/* inv_bound_timestamp (struct inv_mpu_state *st)
+ The purpose this function is to give a generic bound to each
+ sensor timestamp. The timestamp cannot exceed current time.
+ The timestamp cannot backwards one sample time either, otherwise, there
+ would be another sample in between. Using this principle, we can bound
+ the sensor samples */
+int inv_bound_timestamp(struct inv_mpu_state *st)
+{
+ int i;
+ struct inv_timestamp_algo *ts_algo = &st->ts_algo;
+
+ for (i = 0; i < SENSOR_NUM_MAX; i++) {
+ if (st->sensor[i].on) {
+ if (st->sensor[i].count) {
+ process_sensor_bounding(st, i);
+ } else if (ts_algo->calib_counter <
+ INV_TIME_CALIB_THRESHOLD_1) {
+ st->sensor[i].ts = ts_algo->reset_ts;
+ st->sensor[i].previous_ts = st->sensor[i].ts;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/iio/imu/inv_mpu/inv_test/Kconfig b/drivers/iio/imu/inv_mpu/inv_test/Kconfig
new file mode 100644
index 000000000000..a4dfd95db886
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_test/Kconfig
@@ -0,0 +1,13 @@
+#
+# Kconfig for Invensense IIO testing hooks
+#
+
+config INV_TESTING
+ boolean "Invensense IIO testing hooks"
+ depends on INV_MPU_IIO || INV_AMI306_IIO || INV_YAS530 || INV_HUB_IIO
+ default n
+ help
+ This flag enables display of additional testing information from the
+ Invensense IIO drivers
+ It also enables the I2C counters facility to perform IO profiling.
+ Some additional sysfs entries will appear when this flag is enabled.
diff --git a/drivers/iio/imu/inv_mpu/inv_test/Makefile b/drivers/iio/imu/inv_mpu/inv_test/Makefile
new file mode 100644
index 000000000000..4f0edd3de901
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_test/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Invensense IIO testing hooks.
+#
+
+obj-$(CONFIG_INV_TESTING) += inv_counters.o
+
diff --git a/drivers/iio/imu/inv_mpu/inv_test/inv_counters.c b/drivers/iio/imu/inv_mpu/inv_test/inv_counters.c
new file mode 100644
index 000000000000..f60337caeeed
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_test/inv_counters.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2012-2017 InvenSense, 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/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/kdev_t.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+#include <linux/kernel_stat.h>
+
+#include "inv_counters.h"
+
+static int mpu_irq;
+static int accel_irq;
+static int compass_irq;
+
+struct inv_counters {
+ uint32_t i2c_tempreads;
+ uint32_t i2c_mpureads;
+ uint32_t i2c_mpuwrites;
+ uint32_t i2c_accelreads;
+ uint32_t i2c_accelwrites;
+ uint32_t i2c_compassreads;
+ uint32_t i2c_compasswrites;
+ uint32_t i2c_compassirq;
+ uint32_t i2c_accelirq;
+};
+
+static struct inv_counters Counters;
+
+static ssize_t i2c_counters_show(struct class *cls,
+ struct class_attribute *attr, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE,
+ "%ld.%03ld %u %u %u %u %u %u %u %u %u %u\n",
+ jiffies / HZ, ((jiffies % HZ) * (1024 / HZ)),
+ mpu_irq ? kstat_irqs(mpu_irq) : 0,
+ Counters.i2c_tempreads,
+ Counters.i2c_mpureads, Counters.i2c_mpuwrites,
+ accel_irq ? kstat_irqs(accel_irq) : Counters.i2c_accelirq,
+ Counters.i2c_accelreads, Counters.i2c_accelwrites,
+ compass_irq ? kstat_irqs(compass_irq) : Counters.i2c_compassirq,
+ Counters.i2c_compassreads, Counters.i2c_compasswrites);
+}
+
+void inv_iio_counters_set_i2cirq(enum irqtype type, int irq)
+{
+ switch (type) {
+ case IRQ_MPU:
+ mpu_irq = irq;
+ break;
+ case IRQ_ACCEL:
+ accel_irq = irq;
+ break;
+ case IRQ_COMPASS:
+ compass_irq = irq;
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_set_i2cirq);
+
+void inv_iio_counters_tempread(int count)
+{
+ Counters.i2c_tempreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_tempread);
+
+void inv_iio_counters_mpuread(int count)
+{
+ Counters.i2c_mpureads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_mpuread);
+
+void inv_iio_counters_mpuwrite(int count)
+{
+ Counters.i2c_mpuwrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_mpuwrite);
+
+void inv_iio_counters_accelread(int count)
+{
+ Counters.i2c_accelreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelread);
+
+void inv_iio_counters_accelwrite(int count)
+{
+ Counters.i2c_accelwrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelwrite);
+
+void inv_iio_counters_compassread(int count)
+{
+ Counters.i2c_compassreads += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compassread);
+
+void inv_iio_counters_compasswrite(int count)
+{
+ Counters.i2c_compasswrites += count;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compasswrite);
+
+void inv_iio_counters_compassirq(void)
+{
+ Counters.i2c_compassirq++;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_compassirq);
+
+void inv_iio_counters_accelirq(void)
+{
+ Counters.i2c_accelirq++;
+}
+EXPORT_SYMBOL_GPL(inv_iio_counters_accelirq);
+
+static struct class_attribute inv_class_attr[] = {
+ __ATTR(i2c_counter, S_IRUGO, i2c_counters_show, NULL),
+ __ATTR_NULL
+};
+
+static struct class inv_counters_class = {
+ .name = "inv_counters",
+ .owner = THIS_MODULE,
+ .class_attrs = (struct class_attribute *) &inv_class_attr
+};
+
+static int __init inv_counters_init(void)
+{
+ memset(&Counters, 0, sizeof(Counters));
+
+ return class_register(&inv_counters_class);
+}
+
+static void __exit inv_counters_exit(void)
+{
+ class_unregister(&inv_counters_class);
+}
+
+module_init(inv_counters_init);
+module_exit(inv_counters_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("GESL");
+MODULE_DESCRIPTION("inv_counters debug support");
+
diff --git a/drivers/iio/imu/inv_mpu/inv_test/inv_counters.h b/drivers/iio/imu/inv_mpu/inv_test/inv_counters.h
new file mode 100644
index 000000000000..62f76279e703
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu/inv_test/inv_counters.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2012-2017 InvenSense, 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 _INV_COUNTERS_H_
+#define _INV_COUNTERS_H_
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_INV_TESTING
+
+enum irqtype {
+ IRQ_MPU,
+ IRQ_ACCEL,
+ IRQ_COMPASS
+};
+
+#define INV_I2C_INC_MPUREAD(x) inv_iio_counters_mpuread(x)
+#define INV_I2C_INC_MPUWRITE(x) inv_iio_counters_mpuwrite(x)
+#define INV_I2C_INC_ACCELREAD(x) inv_iio_counters_accelread(x)
+#define INV_I2C_INC_ACCELWRITE(x) inv_iio_counters_accelwrite(x)
+#define INV_I2C_INC_COMPASSREAD(x) inv_iio_counters_compassread(x)
+#define INV_I2C_INC_COMPASSWRITE(x) inv_iio_counters_compasswrite(x)
+
+#define INV_I2C_INC_TEMPREAD(x) inv_iio_counters_tempread(x)
+
+#define INV_I2C_SETIRQ(type, irq) inv_iio_counters_set_i2cirq(type, irq)
+#define INV_I2C_INC_COMPASSIRQ() inv_iio_counters_compassirq()
+#define INV_I2C_INC_ACCELIRQ() inv_iio_counters_accelirq()
+
+void inv_iio_counters_mpuread(int count);
+void inv_iio_counters_mpuwrite(int count);
+void inv_iio_counters_accelread(int count);
+void inv_iio_counters_accelwrite(int count);
+void inv_iio_counters_compassread(int count);
+void inv_iio_counters_compasswrite(int count);
+
+void inv_iio_counters_tempread(int count);
+
+void inv_iio_counters_set_i2cirq(enum irqtype type, int irq);
+void inv_iio_counters_compassirq(void);
+void inv_iio_counters_accelirq(void);
+
+#else
+
+#define INV_I2C_INC_MPUREAD(x)
+#define INV_I2C_INC_MPUWRITE(x)
+#define INV_I2C_INC_ACCELREAD(x)
+#define INV_I2C_INC_ACCELWRITE(x)
+#define INV_I2C_INC_COMPASSREAD(x)
+#define INV_I2C_INC_COMPASSWRITE(x)
+
+#define INV_I2C_INC_TEMPREAD(x)
+
+#define INV_I2C_SETIRQ(type, irq)
+#define INV_I2C_INC_COMPASSIRQ()
+#define INV_I2C_INC_ACCELIRQ()
+
+#endif /* CONFIG_INV_TESTING */
+
+#endif /* _INV_COUNTERS_H_ */
+
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index 2b9c00faca7d..795938edce3f 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -1295,7 +1295,7 @@ static ssize_t ucma_set_option(struct ucma_file *file, const char __user *inbuf,
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- if (unlikely(cmd.optval > KMALLOC_MAX_SIZE))
+ if (unlikely(cmd.optlen > KMALLOC_MAX_SIZE))
return -EINVAL;
optval = memdup_user((void __user *) (unsigned long) cmd.optval,
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index c5390f6f94c5..43d277a931c2 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -3161,12 +3161,9 @@ int mlx5_ib_dealloc_xrcd(struct ib_xrcd *xrcd)
int err;
err = mlx5_core_xrcd_dealloc(dev->mdev, xrcdn);
- if (err) {
+ if (err)
mlx5_ib_warn(dev, "failed to dealloc xrcdn 0x%x\n", xrcdn);
- return err;
- }
kfree(xrcd);
-
return 0;
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 37b42447045d..fcb18b11db75 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -1953,6 +1953,9 @@ static struct net_device *ipoib_add_port(const char *format,
goto event_failed;
}
+ /* call event handler to ensure pkey in sync */
+ queue_work(ipoib_workqueue, &priv->flush_heavy);
+
result = register_netdev(priv->dev);
if (result) {
printk(KERN_WARNING "%s: couldn't register ipoib port %d; error %d\n",
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index 3851d5715772..aeb8250ab079 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -1249,6 +1249,7 @@ static const struct acpi_device_id elan_acpi_id[] = {
{ "ELAN060B", 0 },
{ "ELAN060C", 0 },
{ "ELAN0611", 0 },
+ { "ELAN0612", 0 },
{ "ELAN1000", 0 },
{ }
};
diff --git a/drivers/input/mouse/elan_i2c_smbus.c b/drivers/input/mouse/elan_i2c_smbus.c
index cb6aecbc1dc2..25dba1d7aa57 100644
--- a/drivers/input/mouse/elan_i2c_smbus.c
+++ b/drivers/input/mouse/elan_i2c_smbus.c
@@ -130,7 +130,7 @@ static int elan_smbus_get_baseline_data(struct i2c_client *client,
bool max_baseline, u8 *value)
{
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
max_baseline ?
@@ -149,7 +149,7 @@ static int elan_smbus_get_version(struct i2c_client *client,
bool iap, u8 *version)
{
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
iap ? ETP_SMBUS_IAP_VERSION_CMD :
@@ -169,7 +169,7 @@ static int elan_smbus_get_sm_version(struct i2c_client *client,
u8 *ic_type, u8 *version)
{
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
ETP_SMBUS_SM_VERSION_CMD, val);
@@ -186,7 +186,7 @@ static int elan_smbus_get_sm_version(struct i2c_client *client,
static int elan_smbus_get_product_id(struct i2c_client *client, u16 *id)
{
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
ETP_SMBUS_UNIQUEID_CMD, val);
@@ -203,7 +203,7 @@ static int elan_smbus_get_checksum(struct i2c_client *client,
bool iap, u16 *csum)
{
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
iap ? ETP_SMBUS_FW_CHECKSUM_CMD :
@@ -223,7 +223,7 @@ static int elan_smbus_get_max(struct i2c_client *client,
unsigned int *max_x, unsigned int *max_y)
{
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client, ETP_SMBUS_RANGE_CMD, val);
if (error) {
@@ -241,7 +241,7 @@ static int elan_smbus_get_resolution(struct i2c_client *client,
u8 *hw_res_x, u8 *hw_res_y)
{
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
ETP_SMBUS_RESOLUTION_CMD, val);
@@ -261,7 +261,7 @@ static int elan_smbus_get_num_traces(struct i2c_client *client,
unsigned int *y_traces)
{
int error;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
ETP_SMBUS_XY_TRACENUM_CMD, val);
@@ -288,7 +288,7 @@ static int elan_smbus_iap_get_mode(struct i2c_client *client,
{
int error;
u16 constant;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client, ETP_SMBUS_IAP_CTRL_CMD, val);
if (error < 0) {
@@ -339,7 +339,7 @@ static int elan_smbus_prepare_fw_update(struct i2c_client *client)
int len;
int error;
enum tp_mode mode;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
u8 cmd[4] = {0x0F, 0x78, 0x00, 0x06};
u16 password;
@@ -413,7 +413,7 @@ static int elan_smbus_write_fw_block(struct i2c_client *client,
struct device *dev = &client->dev;
int error;
u16 result;
- u8 val[3];
+ u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
/*
* Due to the limitation of smbus protocol limiting
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 4d113c9e4b77..7bf2597ce44c 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -425,6 +425,7 @@ MODULE_DEVICE_TABLE(i2c, goodix_ts_id);
#ifdef CONFIG_ACPI
static const struct acpi_device_id goodix_acpi_match[] = {
{ "GDIX1001", 0 },
+ { "GDIX1002", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 6317478916ef..56f2980adc28 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -2256,8 +2256,17 @@ static int arm_smmu_attach_dynamic(struct iommu_domain *domain,
smmu_domain->pgtbl_ops = pgtbl_ops;
ret = 0;
out:
- if (ret)
+ if (ret) {
free_io_pgtable_ops(pgtbl_ops);
+ /* unassign any freed page table memory */
+ if (arm_smmu_is_master_side_secure(smmu_domain)) {
+ arm_smmu_secure_domain_lock(smmu_domain);
+ arm_smmu_secure_pool_destroy(smmu_domain);
+ arm_smmu_unassign_table(smmu_domain);
+ arm_smmu_secure_domain_unlock(smmu_domain);
+ }
+ smmu_domain->pgtbl_ops = NULL;
+ }
mutex_unlock(&smmu->attach_lock);
return ret;
diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c
index e9b241b1c9dd..ac596928f6b4 100644
--- a/drivers/iommu/intel_irq_remapping.c
+++ b/drivers/iommu/intel_irq_remapping.c
@@ -753,7 +753,7 @@ static inline void set_irq_posting_cap(void)
* should have X86_FEATURE_CX16 support, this has been confirmed
* with Intel hardware guys.
*/
- if ( cpu_has_cx16 )
+ if (boot_cpu_has(X86_FEATURE_CX16))
intel_irq_remap_ops.capability |= 1 << IRQ_POSTING_CAP;
for_each_iommu(iommu, drhd)
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 9a22494a2371..2b8b0e4036b5 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -750,7 +750,7 @@ static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq)
MPIDR_TO_SGI_AFFINITY(cluster_id, 1) |
tlist << ICC_SGI1R_TARGET_LIST_SHIFT);
- pr_debug("CPU%d: ICC_SGI1R_EL1 %llx\n", smp_processor_id(), val);
+ pr_devel("CPU%d: ICC_SGI1R_EL1 %llx\n", smp_processor_id(), val);
gic_write_sgi1r(val);
}
diff --git a/drivers/isdn/hardware/eicon/diva.c b/drivers/isdn/hardware/eicon/diva.c
index d91dd580e978..37aaea88a6ad 100644
--- a/drivers/isdn/hardware/eicon/diva.c
+++ b/drivers/isdn/hardware/eicon/diva.c
@@ -387,10 +387,10 @@ void divasa_xdi_driver_unload(void)
** Receive and process command from user mode utility
*/
void *diva_xdi_open_adapter(void *os_handle, const void __user *src,
- int length,
+ int length, void *mptr,
divas_xdi_copy_from_user_fn_t cp_fn)
{
- diva_xdi_um_cfg_cmd_t msg;
+ diva_xdi_um_cfg_cmd_t *msg = (diva_xdi_um_cfg_cmd_t *)mptr;
diva_os_xdi_adapter_t *a = NULL;
diva_os_spin_lock_magic_t old_irql;
struct list_head *tmp;
@@ -400,21 +400,21 @@ void *diva_xdi_open_adapter(void *os_handle, const void __user *src,
length, sizeof(diva_xdi_um_cfg_cmd_t)))
return NULL;
}
- if ((*cp_fn) (os_handle, &msg, src, sizeof(msg)) <= 0) {
+ if ((*cp_fn) (os_handle, msg, src, sizeof(*msg)) <= 0) {
DBG_ERR(("A: A(?) open, write error"))
return NULL;
}
diva_os_enter_spin_lock(&adapter_lock, &old_irql, "open_adapter");
list_for_each(tmp, &adapter_queue) {
a = list_entry(tmp, diva_os_xdi_adapter_t, link);
- if (a->controller == (int)msg.adapter)
+ if (a->controller == (int)msg->adapter)
break;
a = NULL;
}
diva_os_leave_spin_lock(&adapter_lock, &old_irql, "open_adapter");
if (!a) {
- DBG_ERR(("A: A(%d) open, adapter not found", msg.adapter))
+ DBG_ERR(("A: A(%d) open, adapter not found", msg->adapter))
}
return (a);
@@ -436,8 +436,10 @@ void diva_xdi_close_adapter(void *adapter, void *os_handle)
int
diva_xdi_write(void *adapter, void *os_handle, const void __user *src,
- int length, divas_xdi_copy_from_user_fn_t cp_fn)
+ int length, void *mptr,
+ divas_xdi_copy_from_user_fn_t cp_fn)
{
+ diva_xdi_um_cfg_cmd_t *msg = (diva_xdi_um_cfg_cmd_t *)mptr;
diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) adapter;
void *data;
@@ -458,7 +460,13 @@ diva_xdi_write(void *adapter, void *os_handle, const void __user *src,
return (-2);
}
- length = (*cp_fn) (os_handle, data, src, length);
+ if (msg) {
+ *(diva_xdi_um_cfg_cmd_t *)data = *msg;
+ length = (*cp_fn) (os_handle, (char *)data + sizeof(*msg),
+ src + sizeof(*msg), length - sizeof(*msg));
+ } else {
+ length = (*cp_fn) (os_handle, data, src, length);
+ }
if (length > 0) {
if ((*(a->interface.cmd_proc))
(a, (diva_xdi_um_cfg_cmd_t *) data, length)) {
diff --git a/drivers/isdn/hardware/eicon/diva.h b/drivers/isdn/hardware/eicon/diva.h
index e979085d1b89..a0a607c0c32e 100644
--- a/drivers/isdn/hardware/eicon/diva.h
+++ b/drivers/isdn/hardware/eicon/diva.h
@@ -19,10 +19,11 @@ int diva_xdi_read(void *adapter, void *os_handle, void __user *dst,
int max_length, divas_xdi_copy_to_user_fn_t cp_fn);
int diva_xdi_write(void *adapter, void *os_handle, const void __user *src,
- int length, divas_xdi_copy_from_user_fn_t cp_fn);
+ int length, void *msg,
+ divas_xdi_copy_from_user_fn_t cp_fn);
void *diva_xdi_open_adapter(void *os_handle, const void __user *src,
- int length,
+ int length, void *msg,
divas_xdi_copy_from_user_fn_t cp_fn);
void diva_xdi_close_adapter(void *adapter, void *os_handle);
diff --git a/drivers/isdn/hardware/eicon/divasmain.c b/drivers/isdn/hardware/eicon/divasmain.c
index a2e0ed6c9a4d..91bd2ba0bdd8 100644
--- a/drivers/isdn/hardware/eicon/divasmain.c
+++ b/drivers/isdn/hardware/eicon/divasmain.c
@@ -591,19 +591,22 @@ static int divas_release(struct inode *inode, struct file *file)
static ssize_t divas_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
+ diva_xdi_um_cfg_cmd_t msg;
int ret = -EINVAL;
if (!file->private_data) {
file->private_data = diva_xdi_open_adapter(file, buf,
- count,
+ count, &msg,
xdi_copy_from_user);
- }
- if (!file->private_data) {
- return (-ENODEV);
+ if (!file->private_data)
+ return (-ENODEV);
+ ret = diva_xdi_write(file->private_data, file,
+ buf, count, &msg, xdi_copy_from_user);
+ } else {
+ ret = diva_xdi_write(file->private_data, file,
+ buf, count, NULL, xdi_copy_from_user);
}
- ret = diva_xdi_write(file->private_data, file,
- buf, count, xdi_copy_from_user);
switch (ret) {
case -1: /* Message should be removed from rx mailbox first */
ret = -EBUSY;
@@ -622,11 +625,12 @@ static ssize_t divas_write(struct file *file, const char __user *buf,
static ssize_t divas_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
+ diva_xdi_um_cfg_cmd_t msg;
int ret = -EINVAL;
if (!file->private_data) {
file->private_data = diva_xdi_open_adapter(file, buf,
- count,
+ count, &msg,
xdi_copy_from_user);
}
if (!file->private_data) {
diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c
index aa84fcfd59fc..16c3390e5d9f 100644
--- a/drivers/md/bcache/alloc.c
+++ b/drivers/md/bcache/alloc.c
@@ -285,8 +285,10 @@ do { \
break; \
\
mutex_unlock(&(ca)->set->bucket_lock); \
- if (kthread_should_stop()) \
+ if (kthread_should_stop()) { \
+ set_current_state(TASK_RUNNING); \
return 0; \
+ } \
\
try_to_freeze(); \
schedule(); \
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
index 02619cabda8b..7fe7df56fa33 100644
--- a/drivers/md/bcache/bcache.h
+++ b/drivers/md/bcache/bcache.h
@@ -904,7 +904,7 @@ void bcache_write_super(struct cache_set *);
int bch_flash_dev_create(struct cache_set *c, uint64_t size);
-int bch_cached_dev_attach(struct cached_dev *, struct cache_set *);
+int bch_cached_dev_attach(struct cached_dev *, struct cache_set *, uint8_t *);
void bch_cached_dev_detach(struct cached_dev *);
void bch_cached_dev_run(struct cached_dev *);
void bcache_device_stop(struct bcache_device *);
diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
index a5a6909280fe..4ed621ad27e4 100644
--- a/drivers/md/bcache/btree.c
+++ b/drivers/md/bcache/btree.c
@@ -1869,14 +1869,17 @@ void bch_initial_gc_finish(struct cache_set *c)
*/
for_each_cache(ca, c, i) {
for_each_bucket(b, ca) {
- if (fifo_full(&ca->free[RESERVE_PRIO]))
+ if (fifo_full(&ca->free[RESERVE_PRIO]) &&
+ fifo_full(&ca->free[RESERVE_BTREE]))
break;
if (bch_can_invalidate_bucket(ca, b) &&
!GC_MARK(b)) {
__bch_invalidate_one_bucket(ca, b);
- fifo_push(&ca->free[RESERVE_PRIO],
- b - ca->buckets);
+ if (!fifo_push(&ca->free[RESERVE_PRIO],
+ b - ca->buckets))
+ fifo_push(&ca->free[RESERVE_BTREE],
+ b - ca->buckets);
}
}
}
diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
index 53c0fa005821..c772fc27b332 100644
--- a/drivers/md/bcache/request.c
+++ b/drivers/md/bcache/request.c
@@ -633,11 +633,11 @@ static void do_bio_hook(struct search *s, struct bio *orig_bio)
static void search_free(struct closure *cl)
{
struct search *s = container_of(cl, struct search, cl);
- bio_complete(s);
if (s->iop.bio)
bio_put(s->iop.bio);
+ bio_complete(s);
closure_debug_destroy(cl);
mempool_free(s, s->d->c->search);
}
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index b9a526271f02..69356e9a3be5 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -936,7 +936,8 @@ void bch_cached_dev_detach(struct cached_dev *dc)
cached_dev_put(dc);
}
-int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c)
+int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c,
+ uint8_t *set_uuid)
{
uint32_t rtime = cpu_to_le32(get_seconds());
struct uuid_entry *u;
@@ -945,7 +946,8 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c)
bdevname(dc->bdev, buf);
- if (memcmp(dc->sb.set_uuid, c->sb.set_uuid, 16))
+ if ((set_uuid && memcmp(set_uuid, c->sb.set_uuid, 16)) ||
+ (!set_uuid && memcmp(dc->sb.set_uuid, c->sb.set_uuid, 16)))
return -ENOENT;
if (dc->disk.c) {
@@ -1189,7 +1191,7 @@ static void register_bdev(struct cache_sb *sb, struct page *sb_page,
list_add(&dc->list, &uncached_devices);
list_for_each_entry(c, &bch_cache_sets, list)
- bch_cached_dev_attach(dc, c);
+ bch_cached_dev_attach(dc, c, NULL);
if (BDEV_STATE(&dc->sb) == BDEV_STATE_NONE ||
BDEV_STATE(&dc->sb) == BDEV_STATE_STALE)
@@ -1711,7 +1713,7 @@ static void run_cache_set(struct cache_set *c)
bcache_write_super(c);
list_for_each_entry_safe(dc, t, &uncached_devices, list)
- bch_cached_dev_attach(dc, c);
+ bch_cached_dev_attach(dc, c, NULL);
flash_devs_run(c);
@@ -1828,6 +1830,7 @@ void bch_cache_release(struct kobject *kobj)
static int cache_alloc(struct cache_sb *sb, struct cache *ca)
{
size_t free;
+ size_t btree_buckets;
struct bucket *b;
__module_get(THIS_MODULE);
@@ -1837,9 +1840,19 @@ static int cache_alloc(struct cache_sb *sb, struct cache *ca)
ca->journal.bio.bi_max_vecs = 8;
ca->journal.bio.bi_io_vec = ca->journal.bio.bi_inline_vecs;
+ /*
+ * when ca->sb.njournal_buckets is not zero, journal exists,
+ * and in bch_journal_replay(), tree node may split,
+ * so bucket of RESERVE_BTREE type is needed,
+ * the worst situation is all journal buckets are valid journal,
+ * and all the keys need to replay,
+ * so the number of RESERVE_BTREE type buckets should be as much
+ * as journal buckets
+ */
+ btree_buckets = ca->sb.njournal_buckets ?: 8;
free = roundup_pow_of_two(ca->sb.nbuckets) >> 10;
- if (!init_fifo(&ca->free[RESERVE_BTREE], 8, GFP_KERNEL) ||
+ if (!init_fifo(&ca->free[RESERVE_BTREE], btree_buckets, GFP_KERNEL) ||
!init_fifo_exact(&ca->free[RESERVE_PRIO], prio_buckets(ca), GFP_KERNEL) ||
!init_fifo(&ca->free[RESERVE_MOVINGGC], free, GFP_KERNEL) ||
!init_fifo(&ca->free[RESERVE_NONE], free, GFP_KERNEL) ||
diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c
index 4fbb5532f24c..5a5c1f1bd8a5 100644
--- a/drivers/md/bcache/sysfs.c
+++ b/drivers/md/bcache/sysfs.c
@@ -191,7 +191,7 @@ STORE(__cached_dev)
{
struct cached_dev *dc = container_of(kobj, struct cached_dev,
disk.kobj);
- ssize_t v = size;
+ ssize_t v;
struct cache_set *c;
struct kobj_uevent_env *env;
@@ -263,17 +263,20 @@ STORE(__cached_dev)
}
if (attr == &sysfs_attach) {
- if (bch_parse_uuid(buf, dc->sb.set_uuid) < 16)
+ uint8_t set_uuid[16];
+
+ if (bch_parse_uuid(buf, set_uuid) < 16)
return -EINVAL;
+ v = -ENOENT;
list_for_each_entry(c, &bch_cache_sets, list) {
- v = bch_cached_dev_attach(dc, c);
+ v = bch_cached_dev_attach(dc, c, set_uuid);
if (!v)
return size;
}
pr_err("Can't attach %s: cache set not found", buf);
- size = v;
+ return v;
}
if (attr == &sysfs_detach && dc->disk.c)
diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c
index bbb1dc9e1639..f2c0000de613 100644
--- a/drivers/md/bcache/writeback.c
+++ b/drivers/md/bcache/writeback.c
@@ -425,19 +425,28 @@ static int bch_writeback_thread(void *arg)
while (!kthread_should_stop()) {
down_write(&dc->writeback_lock);
- if (!atomic_read(&dc->has_dirty) ||
- (!test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags) &&
- !dc->writeback_running)) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ /*
+ * If the bache device is detaching, skip here and continue
+ * to perform writeback. Otherwise, if no dirty data on cache,
+ * or there is dirty data on cache but writeback is disabled,
+ * the writeback thread should sleep here and wait for others
+ * to wake up it.
+ */
+ if (!test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags) &&
+ (!atomic_read(&dc->has_dirty) || !dc->writeback_running)) {
up_write(&dc->writeback_lock);
- set_current_state(TASK_INTERRUPTIBLE);
- if (kthread_should_stop())
+ if (kthread_should_stop()) {
+ set_current_state(TASK_RUNNING);
return 0;
+ }
try_to_freeze();
schedule();
continue;
}
+ set_current_state(TASK_RUNNING);
searched_full_index = refill_dirty(dc);
@@ -447,6 +456,14 @@ static int bch_writeback_thread(void *arg)
cached_dev_put(dc);
SET_BDEV_STATE(&dc->sb, BDEV_STATE_CLEAN);
bch_write_bdev_super(dc, NULL);
+ /*
+ * If bcache device is detaching via sysfs interface,
+ * writeback thread should stop after there is no dirty
+ * data on cache. BCACHE_DEV_DETACHING flag is set in
+ * bch_cached_dev_detach().
+ */
+ if (test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags))
+ break;
}
up_write(&dc->writeback_lock);
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index a3ec3c5a8ee9..b5a5081ac736 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1686,6 +1686,17 @@ static int raid1_remove_disk(struct mddev *mddev, struct md_rdev *rdev)
struct md_rdev *repl =
conf->mirrors[conf->raid_disks + number].rdev;
freeze_array(conf, 0);
+ if (atomic_read(&repl->nr_pending)) {
+ /* It means that some queued IO of retry_list
+ * hold repl. Thus, we cannot set replacement
+ * as NULL, avoiding rdev NULL pointer
+ * dereference in sync_request_write and
+ * handle_write_finished.
+ */
+ err = -EBUSY;
+ unfreeze_array(conf);
+ goto abort;
+ }
clear_bit(Replacement, &repl->flags);
p->rdev = repl;
conf->mirrors[conf->raid_disks + number].rdev = NULL;
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 45e7a47e5f7b..2ebd180b0071 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -2630,7 +2630,8 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio)
for (m = 0; m < conf->copies; m++) {
int dev = r10_bio->devs[m].devnum;
rdev = conf->mirrors[dev].rdev;
- if (r10_bio->devs[m].bio == NULL)
+ if (r10_bio->devs[m].bio == NULL ||
+ r10_bio->devs[m].bio->bi_end_io == NULL)
continue;
if (!r10_bio->devs[m].bio->bi_error) {
rdev_clear_badblocks(
@@ -2645,7 +2646,8 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio)
md_error(conf->mddev, rdev);
}
rdev = conf->mirrors[dev].replacement;
- if (r10_bio->devs[m].repl_bio == NULL)
+ if (r10_bio->devs[m].repl_bio == NULL ||
+ r10_bio->devs[m].repl_bio->bi_end_io == NULL)
continue;
if (!r10_bio->devs[m].repl_bio->bi_error) {
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 9284acea4f7b..bb77d20f2ef2 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -2028,15 +2028,16 @@ static int grow_one_stripe(struct r5conf *conf, gfp_t gfp)
static int grow_stripes(struct r5conf *conf, int num)
{
struct kmem_cache *sc;
+ size_t namelen = sizeof(conf->cache_name[0]);
int devs = max(conf->raid_disks, conf->previous_raid_disks);
if (conf->mddev->gendisk)
- sprintf(conf->cache_name[0],
+ snprintf(conf->cache_name[0], namelen,
"raid%d-%s", conf->level, mdname(conf->mddev));
else
- sprintf(conf->cache_name[0],
+ snprintf(conf->cache_name[0], namelen,
"raid%d-%p", conf->level, conf->mddev);
- sprintf(conf->cache_name[1], "%s-alt", conf->cache_name[0]);
+ snprintf(conf->cache_name[1], namelen, "%.27s-alt", conf->cache_name[0]);
conf->active_name = 0;
sc = kmem_cache_create(conf->cache_name[conf->active_name],
diff --git a/drivers/media/i2c/adv7481.c b/drivers/media/i2c/adv7481.c
index 19ec11174632..713a47cfea7f 100644
--- a/drivers/media/i2c/adv7481.c
+++ b/drivers/media/i2c/adv7481.c
@@ -49,12 +49,12 @@
#define I2C_SW_RST_DELAY 5000
#define GPIO_HW_RST_DELAY_HI 10000
#define GPIO_HW_RST_DELAY_LOW 10000
-#define SDP_MIN_SLEEP 5000
-#define SDP_MAX_SLEEP 6000
-#define SDP_NUM_TRIES 30
-#define LOCK_MIN_SLEEP 5000
-#define LOCK_MAX_SLEEP 6000
-#define LOCK_NUM_TRIES 200
+#define SDP_MIN_SLEEP 5000
+#define SDP_MAX_SLEEP 6000
+#define SDP_NUM_TRIES 50
+#define LOCK_MIN_SLEEP 5000
+#define LOCK_MAX_SLEEP 6000
+#define LOCK_NUM_TRIES 200
#define MAX_DEFAULT_WIDTH 1280
#define MAX_DEFAULT_HEIGHT 720
@@ -193,19 +193,20 @@ const uint8_t adv7481_default_edid_data[] = {
/* Display Parameters */
0x80, 0x10, 0x09, 0x78, 0x0A,
/* Color characteristics */
-0x0D, 0xC9, 0xA0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4C,
+0x0D, 0xC9, 0xA0, 0x57, 0x47, 0x98, 0x27, 0x12,
+0x48, 0x4C,
/* Established Timings */
0x21, 0x08, 0x00,
/* Standard Timings */
-0x81, 0xC0, 0x81, 0x40, 0x3B, 0xC0, 0x3B, 0x40,
-0x31, 0xC0, 0x31, 0x40, 0x01, 0x01, 0x01, 0x01,
+0xD1, 0xC0, 0xD1, 0x40, 0x81, 0xC0, 0x81, 0x40,
+0x3B, 0xC0, 0x3B, 0x40, 0x31, 0xC0, 0x31, 0x40,
/* Detailed Timings Block */
-0x01, 0x1D, 0x00, 0xBC, 0x52, 0xD0, 0x1E, 0x20,
-0xB8, 0x28, 0x55, 0x40, 0xA0, 0x5A, 0x00, 0x00,
+0x1A, 0x36, 0x80, 0xA0, 0x70, 0x38, 0x1F, 0x40,
+0x30, 0x20, 0x35, 0x00, 0x40, 0x44, 0x21, 0x00,
0x00, 0x1E,
/* Monitor Descriptor Block 2 */
-0x8C, 0x0A, 0xD0, 0xB4, 0x20, 0xE0, 0x14, 0x10,
-0x12, 0x48, 0x3A, 0x00, 0xD8, 0xA2, 0x00, 0x00,
+0x00, 0x19, 0x00, 0xA0, 0x50, 0xD0, 0x15, 0x20,
+0x30, 0x20, 0x35, 0x00, 0x80, 0xD8, 0x10, 0x00,
0x00, 0x1E,
/* Monitor Descriptor Block 3 */
0x00, 0x00, 0x00, 0xFD, 0x00, 0x17, 0x4B, 0x0F,
@@ -218,16 +219,16 @@ const uint8_t adv7481_default_edid_data[] = {
/* Extension Flag CEA */
0x01,
/* Checksum */
-0x5B,
+0x16,
/* Block 1 (Extension Block) */
/* Extension Header */
-0x02, 0x03, 0x1E,
+0x02, 0x03, 0x22,
/* Display supports */
0x71,
-/* Video Data Bock */
-0x48, 0x84, 0x13, 0x3C, 0x03, 0x02, 0x11, 0x12,
-0x01,
+/* Video Data Block */
+0x4C, 0x84, 0x13, 0x3C, 0x03, 0x02, 0x11, 0x12,
+0x01, 0x90, 0x1F, 0x20, 0x22,
/* HDMI VSDB */
/* Deep color All, Max_TMDS_Clock = 150 MHz */
0x68, 0x03, 0x0C, 0x00, 0x10, 0x00, 0x80,
@@ -239,17 +240,17 @@ const uint8_t adv7481_default_edid_data[] = {
/* Speaker Allocation Data Block */
0x83, 0x01, 0x00, 0x00,
/* Detailed Timing Descriptor */
-0x01, 0x1D, 0x00, 0x72, 0x51, 0xD0, 0x1E, 0x20,
-0x6E, 0x28, 0x55, 0x00, 0xA0, 0x2A, 0x53, 0x00,
+0x1A, 0x36, 0x80, 0xA0, 0x70, 0x38, 0x1F, 0x40,
+0x30, 0x20, 0x35, 0x00, 0x40, 0x44, 0x21, 0x00,
0x00, 0x1E,
/* Detailed Timing Descriptor */
-0x8C, 0x0A, 0xD0, 0xB4, 0x20, 0xE0, 0x14, 0x10,
-0x12, 0x48, 0x3A, 0x00, 0xD8, 0xA2, 0x00, 0x00,
+0x00, 0x19, 0x00, 0xA0, 0x50, 0xD0, 0x15, 0x20,
+0x30, 0x20, 0x35, 0x00, 0x80, 0xD8, 0x10, 0x00,
0x00, 0x1E,
/* Detailed Timing Descriptor */
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00,
+0x41, 0x0A, 0xD0, 0xA0, 0x20, 0xE0, 0x13, 0x10,
+0x30, 0x20, 0x3A, 0x00, 0xD8, 0x90, 0x00, 0x00,
+0x00, 0x18,
/* Detailed Timing Descriptor */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -259,9 +260,9 @@ const uint8_t adv7481_default_edid_data[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
/* DTD padding */
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00,
/* Checksum */
-0xC6
+0x8C
};
#define ADV7481_EDID_SIZE ARRAY_SIZE(adv7481_default_edid_data)
@@ -410,7 +411,7 @@ static int adv7481_set_irq(struct adv7481_state *state)
ADV_REG_SETFIELD(AD_MID_DRIVE_STRNGTH, IO_DRV_LLC_PAD));
ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
IO_REG_INT1_CONF_ADDR,
- ADV_REG_SETFIELD(AD_ACTIVE_UNTIL_CLR,
+ ADV_REG_SETFIELD(AD_4_XTAL_PER,
IO_INTRQ_DUR_SEL) |
ADV_REG_SETFIELD(AD_OP_DRIVE_LOW, IO_INTRQ_OP_SEL));
ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr,
@@ -612,7 +613,8 @@ static void adv7481_irq_delay_work(struct work_struct *work)
pr_debug("%s: dev: %d got datapath raw status: 0x%x\n",
__func__, state->device_num, raw_status);
- if (ADV_REG_GETFIELD(int_status, IO_INT_SD_ST) &&
+ if ((state->mode == ADV7481_IP_CVBS_1) &&
+ ADV_REG_GETFIELD(int_status, IO_INT_SD_ST) &&
ADV_REG_GETFIELD(raw_status, IO_INT_SD_RAW)) {
uint8_t sdp_sts = 0;
@@ -648,7 +650,7 @@ static void adv7481_irq_delay_work(struct work_struct *work)
adv7481_wr_byte(&state->i2c_client,
state->i2c_sdp_addr, SDP_RW_MAP_REG,
0x00);
- } else {
+ } else if (state->mode == ADV7481_IP_HDMI) {
if (ADV_REG_GETFIELD(int_status,
IO_CP_LOCK_CP_ST) &&
ADV_REG_GETFIELD(raw_status,
@@ -1211,10 +1213,12 @@ static long adv7481_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
return ret;
}
-static int adv7481_get_sd_timings(struct adv7481_state *state, int *sd_standard)
+static int adv7481_get_sd_timings(struct adv7481_state *state,
+ int *sd_standard, struct adv7481_vid_params *vid_params)
{
int ret = 0;
int sdp_stat, sdp_stat2;
+ int interlace_reg = 0;
int timeout = 0;
if (sd_standard == NULL)
@@ -1231,6 +1235,25 @@ static int adv7481_get_sd_timings(struct adv7481_state *state, int *sd_standard)
sdp_stat2 = adv7481_rd_byte(&state->i2c_client,
state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR);
} while ((sdp_stat != sdp_stat2) && (timeout < SDP_NUM_TRIES));
+
+ interlace_reg = adv7481_rd_byte(&state->i2c_client,
+ state->i2c_sdp_addr, SDP_RO_MAIN_INTERLACE_STATE_ADDR);
+
+ if (ADV_REG_GETFIELD(interlace_reg, SDP_RO_MAIN_INTERLACE_STATE))
+ pr_debug("%s: Interlaced video detected\n", __func__);
+ else
+ pr_debug("%s: Interlaced video not detected\n", __func__);
+
+ if (ADV_REG_GETFIELD(interlace_reg, SDP_RO_MAIN_FIELD_LEN))
+ pr_debug("%s: Field length is correct\n", __func__);
+ else
+ pr_debug("%s: Field length is not correct\n", __func__);
+
+ if (ADV_REG_GETFIELD(interlace_reg, SDP_RO_MAIN_SD_FIELD_RATE))
+ pr_debug("%s: SD 50 Hz detected\n", __func__);
+ else
+ pr_debug("%s: SD 60 Hz detected\n", __func__);
+
adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr,
SDP_RW_MAP_REG, 0x00);
@@ -1244,36 +1267,58 @@ static int adv7481_get_sd_timings(struct adv7481_state *state, int *sd_standard)
__func__, __LINE__, sdp_stat);
return -EBUSY;
}
+ vid_params->act_pix = 720;
+ vid_params->intrlcd = 1;
switch (ADV_REG_GETFIELD(sdp_stat, SDP_RO_MAIN_AD_RESULT)) {
case AD_NTSM_M_J:
*sd_standard = V4L2_STD_NTSC;
+ pr_debug("%s, V4L2_STD_NTSC\n", __func__);
+ vid_params->act_lines = 507;
break;
case AD_NTSC_4_43:
*sd_standard = V4L2_STD_NTSC_443;
+ pr_debug("%s, V4L2_STD_NTSC_443\n", __func__);
+ vid_params->act_lines = 507;
break;
case AD_PAL_M:
*sd_standard = V4L2_STD_PAL_M;
+ pr_debug("%s, V4L2_STD_PAL_M\n", __func__);
+ vid_params->act_lines = 576;
break;
case AD_PAL_60:
*sd_standard = V4L2_STD_PAL_60;
+ pr_debug("%s, V4L2_STD_PAL_60\n", __func__);
+ vid_params->act_lines = 576;
break;
case AD_PAL_B_G:
*sd_standard = V4L2_STD_PAL;
+ pr_debug("%s, V4L2_STD_PAL\n", __func__);
+ vid_params->act_lines = 576;
break;
case AD_SECAM:
*sd_standard = V4L2_STD_SECAM;
+ pr_debug("%s, V4L2_STD_SECAM\n", __func__);
+ vid_params->act_lines = 576;
break;
case AD_PAL_COMB_N:
*sd_standard = V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
+ pr_debug("%s, V4L2_STD_PAL_Nc | V4L2_STD_PAL_N\n", __func__);
+ vid_params->act_lines = 576;
break;
case AD_SECAM_525:
*sd_standard = V4L2_STD_SECAM;
+ pr_debug("%s, V4L2_STD_SECAM (AD_SECAM_525)\n", __func__);
+ vid_params->act_lines = 576;
break;
default:
*sd_standard = V4L2_STD_UNKNOWN;
+ pr_debug("%s, V4L2_STD_UNKNOWN\n", __func__);
+ vid_params->act_lines = 507;
break;
}
+ pr_debug("%s(%d), adv7481 TMDS Resolution: %d x %d\n",
+ __func__, __LINE__, vid_params->act_pix, vid_params->act_lines);
return ret;
}
@@ -1892,6 +1937,8 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state,
vid_params->pix_clk = hdmi_params->tmds_freq;
+ vid_params->act_lines = vid_params->act_lines * fieldfactor;
+
switch (hdmi_params->color_depth) {
case CD_10BIT:
vid_params->pix_clk = ((vid_params->pix_clk*4)/5);
@@ -1992,6 +2039,9 @@ static int adv7481_query_sd_std(struct v4l2_subdev *sd, v4l2_std_id *std)
struct adv7481_state *state = to_state(sd);
uint8_t tStatus = 0x0;
uint32_t count = 0;
+ struct adv7481_vid_params vid_params;
+
+ memset(&vid_params, 0, sizeof(vid_params));
pr_debug("Enter %s\n", __func__);
/* Select SDP read-only main Map */
@@ -2032,7 +2082,7 @@ static int adv7481_query_sd_std(struct v4l2_subdev *sd, v4l2_std_id *std)
case ADV7481_IP_CVBS_6_HDMI_SIM:
case ADV7481_IP_CVBS_7_HDMI_SIM:
case ADV7481_IP_CVBS_8_HDMI_SIM:
- ret = adv7481_get_sd_timings(state, &temp);
+ ret = adv7481_get_sd_timings(state, &temp, &vid_params);
break;
default:
return -EINVAL;
@@ -2062,6 +2112,7 @@ static int adv7481_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_format *format)
{
int ret;
+ int sd_standard;
struct adv7481_vid_params vid_params;
struct adv7481_hdmi_params hdmi_params;
struct adv7481_state *state = to_state(sd);
@@ -2086,8 +2137,9 @@ static int adv7481_get_fmt(struct v4l2_subdev *sd,
if (!ret) {
fmt->width = vid_params.act_pix;
fmt->height = vid_params.act_lines;
+ fmt->field = V4L2_FIELD_NONE;
if (vid_params.intrlcd)
- fmt->height /= 2;
+ fmt->field = V4L2_FIELD_INTERLACED;
} else {
pr_err("%s: Error %d in adv7481_get_hdmi_timings\n",
__func__, ret);
@@ -2096,8 +2148,14 @@ static int adv7481_get_fmt(struct v4l2_subdev *sd,
case ADV7481_IP_CVBS_1:
fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
- fmt->width = 720;
- fmt->height = 507;
+ ret = adv7481_get_sd_timings(state, &sd_standard, &vid_params);
+ if (!ret) {
+ fmt->width = vid_params.act_pix;
+ fmt->height = vid_params.act_lines;
+ fmt->field = V4L2_FIELD_INTERLACED;
+ } else {
+ pr_err("%s: Unable to get sd_timings\n", __func__);
+ }
break;
default:
return -EINVAL;
diff --git a/drivers/media/i2c/adv7481_reg.h b/drivers/media/i2c/adv7481_reg.h
index 3d484c6d335a..403e538b6127 100644
--- a/drivers/media/i2c/adv7481_reg.h
+++ b/drivers/media/i2c/adv7481_reg.h
@@ -416,6 +416,13 @@
#define SDP_RO_MAIN_LOST_LOCK_SHFT 1
#define SDP_RO_MAIN_IN_LOCK_BMSK 0x0001
#define SDP_RO_MAIN_IN_LOCK_SHFT 0
+#define SDP_RO_MAIN_INTERLACE_STATE_ADDR 0x13
+#define SDP_RO_MAIN_INTERLACE_STATE_BMSK 0x0040
+#define SDP_RO_MAIN_INTERLACE_STATE_SHFT 6
+#define SDP_RO_MAIN_FIELD_LEN_BMSK 0x0020
+#define SDP_RO_MAIN_FIELD_LEN_SHFT 5
+#define SDP_RO_MAIN_SD_FIELD_RATE_BMSK 0x0004
+#define SDP_RO_MAIN_SD_FIELD_RATE_SHFT 2
/* SDP R/O Map 1 Registers */
#define SDP_RO_MAP_1_FIELD_ADDR 0x45
diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c
index f384f295676e..679d122af63c 100644
--- a/drivers/media/pci/cx23885/cx23885-cards.c
+++ b/drivers/media/pci/cx23885/cx23885-cards.c
@@ -2124,6 +2124,10 @@ void cx23885_card_setup(struct cx23885_dev *dev)
&dev->i2c_bus[2].i2c_adap,
"cx25840", 0x88 >> 1, NULL);
if (dev->sd_cx25840) {
+ /* set host data for clk_freq configuration */
+ v4l2_set_subdev_hostdata(dev->sd_cx25840,
+ &dev->clk_freq);
+
dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE;
v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
}
diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
index e8f847226a19..6eb3be13b430 100644
--- a/drivers/media/pci/cx23885/cx23885-core.c
+++ b/drivers/media/pci/cx23885/cx23885-core.c
@@ -872,6 +872,16 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
if (cx23885_boards[dev->board].clk_freq > 0)
dev->clk_freq = cx23885_boards[dev->board].clk_freq;
+ if (dev->board == CX23885_BOARD_HAUPPAUGE_IMPACTVCBE &&
+ dev->pci->subsystem_device == 0x7137) {
+ /* Hauppauge ImpactVCBe device ID 0x7137 is populated
+ * with an 888, and a 25Mhz crystal, instead of the
+ * usual third overtone 50Mhz. The default clock rate must
+ * be overridden so the cx25840 is properly configured
+ */
+ dev->clk_freq = 25000000;
+ }
+
dev->pci_bus = dev->pci->bus->number;
dev->pci_slot = PCI_SLOT(dev->pci->devfn);
cx23885_irq_add(dev, 0x001f00);
diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c
index 0042803a9de7..54398d8a4696 100644
--- a/drivers/media/pci/cx25821/cx25821-core.c
+++ b/drivers/media/pci/cx25821/cx25821-core.c
@@ -871,6 +871,10 @@ static int cx25821_dev_setup(struct cx25821_dev *dev)
dev->nr = ++cx25821_devcount;
sprintf(dev->name, "cx25821[%d]", dev->nr);
+ if (dev->nr >= ARRAY_SIZE(card)) {
+ CX25821_INFO("dev->nr >= %zd", ARRAY_SIZE(card));
+ return -ENODEV;
+ }
if (dev->pci->device != 0x8210) {
pr_info("%s(): Exiting. Incorrect Hardware device = 0x%02x\n",
__func__, dev->pci->device);
@@ -886,9 +890,6 @@ static int cx25821_dev_setup(struct cx25821_dev *dev)
dev->channels[i].sram_channels = &cx25821_sram_channels[i];
}
- if (dev->nr > 1)
- CX25821_INFO("dev->nr > 1!");
-
/* board config */
dev->board = 1; /* card[dev->nr]; */
dev->_max_num_decoders = MAX_DECODERS;
diff --git a/drivers/media/platform/msm/ais/Makefile b/drivers/media/platform/msm/ais/Makefile
index 4387b96f01d0..8c596dfcdcd8 100644
--- a/drivers/media/platform/msm/ais/Makefile
+++ b/drivers/media/platform/msm/ais/Makefile
@@ -22,4 +22,5 @@ obj-$(CONFIG_MSM_AIS_JPEG) += jpeg_10/
obj-$(CONFIG_MSM_AIS_JPEGDMA) += jpeg_dma/
obj-$(CONFIG_MSM_AIS) += msm_buf_mgr/
obj-$(CONFIG_MSM_AIS) += msm_ais_mgr/
+obj-$(CONFIG_MSM_AIS) += msm_ais_diag/
obj-$(CONFIG_MSM_AIS_FD) += fd/
diff --git a/drivers/media/platform/msm/ais/common/Makefile b/drivers/media/platform/msm/ais/common/Makefile
index e1fa3f2ea848..1849d9c9af4c 100644
--- a/drivers/media/platform/msm/ais/common/Makefile
+++ b/drivers/media/platform/msm/ais/common/Makefile
@@ -1,2 +1,2 @@
ccflags-y += -Idrivers/media/platform/msm/ais/
-obj-$(CONFIG_MSM_AIS) += msm_camera_io_util.o cam_smmu_api.o cam_hw_ops.o cam_soc_api.o
+obj-$(CONFIG_MSM_AIS) += msm_camera_io_util.o cam_smmu_api.o cam_hw_ops.o cam_soc_api.o msm_camera_diag_util.o
diff --git a/drivers/media/platform/msm/ais/common/cam_hw_ops.c b/drivers/media/platform/msm/ais/common/cam_hw_ops.c
index cf28e0ca6536..9110c88f9d8a 100644
--- a/drivers/media/platform/msm/ais/common/cam_hw_ops.c
+++ b/drivers/media/platform/msm/ais/common/cam_hw_ops.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,6 +19,7 @@
#include <linux/pm_opp.h>
#include <linux/regulator/rpm-smd-regulator.h>
#include "cam_hw_ops.h"
+#include "msm_camera_diag_util.h"
#ifdef CONFIG_CAM_AHB_DBG
#define CDBG(fmt, args...) pr_err(fmt, ##args)
@@ -242,6 +243,8 @@ static int cam_consolidate_ahb_vote(enum cam_ahb_clk_client id,
data.ahb_clk_state = max;
CDBG("dbg: state : %u, vector : %d\n",
data.ahb_clk_state, max);
+
+ msm_camera_diag_update_ahb_state(data.ahb_clk_state);
}
} else {
pr_err("err: no bus vector found\n");
diff --git a/drivers/media/platform/msm/ais/common/cam_hw_ops.h b/drivers/media/platform/msm/ais/common/cam_hw_ops.h
index 32f93f7b6e0e..e3e9f1381ad8 100644
--- a/drivers/media/platform/msm/ais/common/cam_hw_ops.h
+++ b/drivers/media/platform/msm/ais/common/cam_hw_ops.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,16 +12,7 @@
#ifndef _CAM_HW_OPS_H_
#define _CAM_HW_OPS_H_
-enum cam_ahb_clk_vote {
- /* need to update the voting requests
- * according to dtsi entries.
- */
- CAM_AHB_SUSPEND_VOTE = 0x0,
- CAM_AHB_SVS_VOTE = 0x01,
- CAM_AHB_NOMINAL_VOTE = 0x02,
- CAM_AHB_TURBO_VOTE = 0x03,
- CAM_AHB_DYNAMIC_VOTE = 0xFF,
-};
+#include <media/ais/msm_ais_mgr.h>
enum cam_ahb_clk_client {
CAM_AHB_CLIENT_CSIPHY,
diff --git a/drivers/media/platform/msm/ais/common/cam_soc_api.c b/drivers/media/platform/msm/ais/common/cam_soc_api.c
index 92f3e4007390..46738a1ca381 100644
--- a/drivers/media/platform/msm/ais/common/cam_soc_api.c
+++ b/drivers/media/platform/msm/ais/common/cam_soc_api.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -25,6 +25,8 @@
#include <linux/of_platform.h>
#include <linux/msm-bus.h>
#include "cam_soc_api.h"
+#include "msm_camera_diag_util.h"
+#include <linux/clk/msm-clock-generic.h>
struct msm_cam_bus_pscale_data {
struct msm_bus_scale_pdata *pdata;
@@ -361,6 +363,7 @@ int msm_camera_clk_enable(struct device *dev,
clk_info[i].clk_name);
goto cam_clk_set_err;
}
+ clk_ptr[i]->flags |= CLKFLAG_NO_RATE_CACHE;
rc = clk_set_rate(clk_ptr[i],
clk_rate);
if (rc < 0) {
@@ -374,12 +377,15 @@ 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) {
pr_err("%s round rate failed\n",
clk_info[i].clk_name);
goto cam_clk_set_err;
}
}
+ clk_ptr[i]->flags |= CLKFLAG_NO_RATE_CACHE;
rc = clk_set_rate(clk_ptr[i],
clk_rate);
if (rc < 0) {
@@ -410,6 +416,8 @@ int msm_camera_clk_enable(struct device *dev,
}
}
}
+
+ msm_camera_diag_update_clklist(clk_info, clk_ptr, num_clk, enable);
return rc;
cam_clk_enable_err:
diff --git a/drivers/media/platform/msm/ais/common/msm_camera_diag_util.c b/drivers/media/platform/msm/ais/common/msm_camera_diag_util.c
new file mode 100644
index 000000000000..d4d417090210
--- /dev/null
+++ b/drivers/media/platform/msm/ais/common/msm_camera_diag_util.c
@@ -0,0 +1,364 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "msm_camera_diag_util.h"
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/irqreturn.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+#include <linux/ratelimit.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include "msm_camera_io_util.h"
+#include "msm.h"
+
+
+#define MAX_CLK_NUM 100
+struct camera_diag_clk_list {
+ struct msm_ais_diag_clk_info_t *clk_infolist;
+ struct clk **ppclk;
+ uint32_t clk_num;
+ uint32_t clk_capacity;
+ struct mutex lock;
+};
+
+struct camera_diag_ddrbw {
+ struct msm_ais_diag_bus_info_t bus_info;
+ struct mutex lock;
+};
+
+static struct camera_diag_clk_list s_diag_clk_list;
+static struct camera_diag_ddrbw s_ddrbw;
+
+int msm_camera_get_reg_list(void __iomem *base,
+ struct msm_camera_reg_list_cmd *reg_list)
+{
+ int rc = 0;
+ uint32_t i;
+ uint32_t *reg_values = NULL;
+ uint32_t addrs_size = sizeof(uint32_t) * reg_list->reg_num;
+ uint32_t *reg_addrs = kzalloc(addrs_size, GFP_KERNEL);
+
+ if (!reg_addrs) {
+ rc = -ENOMEM;
+ goto alloc_addr_failed;
+ }
+
+ if (copy_from_user(reg_addrs,
+ (void __user *)(reg_list->regaddr_list),
+ sizeof(uint32_t) * reg_list->reg_num)) {
+ rc = -EFAULT;
+ pr_err("%s copy_from_user fail\n", __func__);
+ goto copy_addr_failed;
+ }
+
+ reg_values = kzalloc(addrs_size, GFP_KERNEL);
+ if (!reg_values) {
+ rc = -ENOMEM;
+ goto copy_addr_failed;
+ }
+
+ for (i = 0 ; i < reg_list->reg_num; ++i) {
+ reg_values[i] = msm_camera_io_r(base + reg_addrs[i]);
+ pr_debug("reg 0x%x 0x%x\n",
+ reg_addrs[i],
+ reg_values[i]);
+ }
+
+ if (copy_to_user(reg_list->value_list, reg_values,
+ sizeof(uint32_t) * reg_list->reg_num)) {
+ rc = -EFAULT;
+ pr_err("%s copy_to_user fail %u\n",
+ __func__,
+ reg_list->reg_num);
+ goto copy_value_failed;
+ }
+
+copy_value_failed:
+ kfree(reg_values);
+
+copy_addr_failed:
+ kfree(reg_addrs);
+
+alloc_addr_failed:
+
+ return rc;
+}
+
+int msm_camera_diag_init(void)
+{
+ s_diag_clk_list.clk_num = 0;
+ s_diag_clk_list.clk_capacity = MAX_CLK_NUM;
+ s_diag_clk_list.clk_infolist = kzalloc(
+ sizeof(struct msm_ais_diag_clk_info_t) *
+ s_diag_clk_list.clk_capacity,
+ GFP_KERNEL);
+
+ if (!s_diag_clk_list.clk_infolist)
+ return -ENOMEM;
+
+ s_diag_clk_list.ppclk = kzalloc(sizeof(struct clk *) *
+ s_diag_clk_list.clk_capacity,
+ GFP_KERNEL);
+ if (!s_diag_clk_list.ppclk) {
+ kfree(s_diag_clk_list.clk_infolist);
+ return -ENOMEM;
+ }
+
+ mutex_init(&s_diag_clk_list.lock);
+ mutex_init(&s_ddrbw.lock);
+ return 0;
+}
+
+int msm_camera_diag_uninit(void)
+{
+ mutex_destroy(&s_ddrbw.lock);
+ mutex_destroy(&s_diag_clk_list.lock);
+
+ kfree(s_diag_clk_list.clk_infolist);
+ s_diag_clk_list.clk_infolist = NULL;
+
+ kfree(s_diag_clk_list.ppclk);
+ s_diag_clk_list.ppclk = NULL;
+
+ return 0;
+}
+
+static uint32_t msm_camera_diag_find_clk_idx(
+ struct msm_cam_clk_info *clk_info,
+ struct clk *clk_ptr)
+{
+ uint32_t i = 0;
+
+ for (; i < s_diag_clk_list.clk_num; ++i) {
+ if (clk_ptr == s_diag_clk_list.ppclk[i])
+ return i;
+ }
+
+ return s_diag_clk_list.clk_capacity;
+}
+
+int msm_camera_diag_update_clklist(
+ struct msm_cam_clk_info *clk_info,
+ struct clk **clk_ptr, int num_clk, int enable)
+{
+ uint32_t i = 0;
+ uint32_t idx = 0;
+ uint32_t actual_idx = 0;
+ struct msm_ais_diag_clk_info_t *pclk_info = NULL;
+
+ mutex_lock(&s_diag_clk_list.lock);
+ for (; i < num_clk; ++i) {
+ idx = msm_camera_diag_find_clk_idx(&clk_info[i], clk_ptr[i]);
+ if (idx < s_diag_clk_list.clk_num) {
+ actual_idx = idx;
+ pclk_info =
+ &s_diag_clk_list.clk_infolist[actual_idx];
+ } else if (s_diag_clk_list.clk_num <
+ s_diag_clk_list.clk_capacity) {
+ actual_idx = s_diag_clk_list.clk_num++;
+ memset(&s_diag_clk_list.clk_infolist[actual_idx],
+ 0,
+ sizeof(struct msm_ais_diag_clk_info_t));
+ pclk_info =
+ &s_diag_clk_list.clk_infolist[actual_idx];
+ memcpy(pclk_info->clk_name,
+ clk_info[i].clk_name,
+ sizeof(pclk_info->clk_name));
+ s_diag_clk_list.ppclk[actual_idx] = clk_ptr[i];
+ pr_debug("%s new clk %s clk_num %u\n",
+ __func__,
+ clk_info[i].clk_name,
+ s_diag_clk_list.clk_num);
+ } else {
+ pr_err("%s too many clks\n", __func__);
+ continue;
+ }
+
+ pclk_info->clk_rate = clk_get_rate(clk_ptr[i]);
+ if (enable) {
+ ++pclk_info->enable;
+ } else {
+ int cnt = pclk_info->enable;
+
+ if (cnt > 0)
+ --pclk_info->enable;
+ }
+ }
+
+ mutex_unlock(&s_diag_clk_list.lock);
+ return 0;
+}
+
+int msm_camera_diag_get_clk_list(
+ struct msm_ais_diag_clk_list_t *clk_infolist)
+{
+ int rc = 0;
+
+ mutex_lock(&s_diag_clk_list.lock);
+ clk_infolist->clk_num = s_diag_clk_list.clk_num;
+ if (copy_to_user(clk_infolist->clk_info,
+ s_diag_clk_list.clk_infolist,
+ sizeof(struct msm_ais_diag_clk_info_t) *
+ s_diag_clk_list.clk_num)) {
+ rc = -EFAULT;
+ }
+ mutex_unlock(&s_diag_clk_list.lock);
+ return rc;
+}
+
+int msm_camera_diag_get_gpio_list(
+ struct msm_ais_diag_gpio_list_t *gpio_list)
+{
+ int rc = 0;
+ uint32_t gpio_num = gpio_list->gpio_num;
+ uint32_t i = 0;
+ int32_t *vals = NULL;
+ uint32_t idxs_size = sizeof(uint32_t) * gpio_num;
+ uint32_t vals_size = sizeof(int32_t) * gpio_num;
+ uint32_t *idxs = kzalloc(idxs_size, GFP_KERNEL);
+
+ if (!idxs) {
+ rc = -ENOMEM;
+ goto alloc_idxs_failed;
+ }
+
+ if (copy_from_user(idxs,
+ (void __user *)(gpio_list->gpio_idx_list),
+ idxs_size)) {
+ rc = -EFAULT;
+ pr_err("%s copy_from_user fail\n", __func__);
+ goto copy_idxs_failed;
+ }
+
+ vals = kzalloc(vals_size, GFP_KERNEL);
+ if (!vals) {
+ rc = -ENOMEM;
+ goto copy_idxs_failed;
+ }
+
+ for (; i < gpio_num; ++i)
+ vals[i] =
+ gpio_get_value(idxs[i]);
+
+ if (copy_to_user(gpio_list->gpio_val_list, vals,
+ vals_size)) {
+ rc = -EFAULT;
+ pr_err("%s copy_to_user fail %u\n",
+ __func__,
+ gpio_num);
+ }
+
+ kfree(vals);
+
+copy_idxs_failed:
+ kfree(idxs);
+
+alloc_idxs_failed:
+ return rc;
+}
+
+int msm_camera_diag_set_gpio_list(
+ struct msm_ais_diag_gpio_list_t *gpio_list)
+{
+ int rc = 0;
+ uint32_t gpio_num = gpio_list->gpio_num;
+ uint32_t i = 0;
+ int32_t val;
+ int32_t *vals = NULL;
+ uint32_t idxs_size = sizeof(uint32_t) * gpio_num;
+ uint32_t vals_size = sizeof(int32_t) * gpio_num;
+ uint32_t *idxs = kzalloc(idxs_size, GFP_KERNEL);
+
+ if (!idxs) {
+ rc = -ENOMEM;
+ goto alloc_idxs_failed;
+ }
+
+ if (copy_from_user(idxs,
+ (void __user *)(gpio_list->gpio_idx_list),
+ idxs_size)) {
+ rc = -EFAULT;
+ pr_err("%s copy_from_user fail\n", __func__);
+ goto copy_idxs_failed;
+ }
+
+ vals = kzalloc(vals_size, GFP_KERNEL);
+ if (!vals) {
+ rc = -ENOMEM;
+ goto copy_idxs_failed;
+ }
+
+ if (copy_from_user(vals,
+ (void __user *)(gpio_list->gpio_val_list),
+ vals_size)) {
+ rc = -EFAULT;
+ pr_err("%s copy_from_user fail\n", __func__);
+ goto copy_vals_failed;
+ }
+
+ for (; i < gpio_num; ++i) {
+ gpio_set_value(idxs[i], vals[i]);
+ val = gpio_get_value(idxs[i]);
+ pr_debug("val set %d after %d\n", vals[i], val);
+ }
+
+copy_vals_failed:
+ kfree(vals);
+
+copy_idxs_failed:
+ kfree(idxs);
+
+alloc_idxs_failed:
+ return rc;
+}
+
+int msm_camera_diag_update_ahb_state(enum cam_ahb_clk_vote vote)
+{
+ mutex_lock(&s_ddrbw.lock);
+ s_ddrbw.bus_info.ahb_clk_vote_state = vote;
+ mutex_unlock(&s_ddrbw.lock);
+ return 0;
+}
+
+int msm_camera_diag_update_isp_state(
+ uint32_t isp_bus_vector_idx,
+ uint64_t isp_ab, uint64_t isp_ib)
+{
+ mutex_lock(&s_ddrbw.lock);
+ s_ddrbw.bus_info.isp_bus_vector_idx = isp_bus_vector_idx;
+ s_ddrbw.bus_info.isp_ab = isp_ab;
+ s_ddrbw.bus_info.isp_ib = isp_ib;
+ mutex_unlock(&s_ddrbw.lock);
+ return 0;
+}
+
+int msm_camera_diag_get_ddrbw(struct msm_ais_diag_bus_info_t *info)
+{
+ int rc = 0;
+
+ mutex_lock(&s_ddrbw.lock);
+ info->ahb_clk_vote_state = s_ddrbw.bus_info.ahb_clk_vote_state;
+ info->isp_bus_vector_idx = s_ddrbw.bus_info.isp_bus_vector_idx;
+ info->isp_ab = s_ddrbw.bus_info.isp_ab;
+ info->isp_ib = s_ddrbw.bus_info.isp_ib;
+
+ mutex_unlock(&s_ddrbw.lock);
+ return rc;
+}
+
+
diff --git a/drivers/media/platform/msm/ais/common/msm_camera_diag_util.h b/drivers/media/platform/msm/ais/common/msm_camera_diag_util.h
new file mode 100644
index 000000000000..1d4b09d726e6
--- /dev/null
+++ b/drivers/media/platform/msm/ais/common/msm_camera_diag_util.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_CAMERA_DIAG_UTIL_H
+#define __MSM_CAMERA_DIAG_UTIL_H
+
+#include <media/ais/msm_ais_mgr.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/msm-bus.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <soc/qcom/ais.h>
+
+int msm_camera_get_reg_list(void __iomem *base,
+ struct msm_camera_reg_list_cmd *reg_list);
+int msm_camera_diag_init(void);
+int msm_camera_diag_uninit(void);
+
+int msm_camera_diag_update_clklist(struct msm_cam_clk_info *clk_info,
+ struct clk **clk_ptr, int num_clk, int enable);
+int msm_camera_diag_get_clk_list(
+ struct msm_ais_diag_clk_list_t *clk_infolist);
+
+int msm_camera_diag_update_ahb_state(enum cam_ahb_clk_vote vote);
+int msm_camera_diag_update_isp_state(uint32_t isp_bus_vector_idx,
+ uint64_t isp_ab, uint64_t isp_ib);
+int msm_camera_diag_get_ddrbw(struct msm_ais_diag_bus_info_t *info);
+int msm_camera_diag_get_gpio_list(
+ struct msm_ais_diag_gpio_list_t *gpio_list);
+int msm_camera_diag_set_gpio_list(
+ struct msm_ais_diag_gpio_list_t *gpio_list);
+
+#endif
diff --git a/drivers/media/platform/msm/ais/common/msm_camera_io_util.c b/drivers/media/platform/msm/ais/common/msm_camera_io_util.c
index a09237f3d5ef..c6c2e0d02b65 100644
--- a/drivers/media/platform/msm/ais/common/msm_camera_io_util.c
+++ b/drivers/media/platform/msm/ais/common/msm_camera_io_util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,6 +19,7 @@
#include <soc/qcom/ais.h>
#include <linux/msm-bus.h>
#include "msm_camera_io_util.h"
+#include "msm_camera_diag_util.h"
#define BUFF_SIZE_128 128
@@ -365,6 +366,8 @@ int msm_cam_clk_enable(struct device *dev, struct msm_cam_clk_info *clk_info,
}
}
}
+
+ msm_camera_diag_update_clklist(clk_info, clk_ptr, num_clk, enable);
return rc;
diff --git a/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c b/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c
index 585865b12387..c23fddf6e52f 100644
--- a/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c
+++ b/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1144,6 +1144,43 @@ static void msm_isp_release_all_bufq(
}
}
+static int msm_isp_get_bufq_state(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_vfe_bufq_state *bufq_state)
+{
+ int rc = 0;
+ struct msm_isp_bufq *bufq = NULL;
+ uint32_t i = 0;
+ int32_t *k_bufq_states = NULL;
+ uint32_t size = 0;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_state->handle);
+ if (bufq) {
+ bufq_state->nbufs = bufq->num_bufs;
+ size = bufq->num_bufs*sizeof(int32_t);
+ k_bufq_states = kzalloc(size, GFP_KERNEL);
+ if (!k_bufq_states) {
+ rc = -ENOMEM;
+ goto alloc_states_failed;
+ }
+
+ for (i = 0; i < bufq_state->nbufs; ++i)
+ k_bufq_states[i] = bufq->bufs[i].state;
+
+ if (copy_to_user(bufq_state->buf_state,
+ k_bufq_states,
+ sizeof(int32_t) * bufq->num_bufs)) {
+ rc = -EFAULT;
+ pr_err("%s copy_to_user fail\n", __func__);
+ goto copy_failed;
+ }
+
+copy_failed:
+ kfree(k_bufq_states);
+ }
+
+alloc_states_failed:
+ return rc;
+}
/**
* msm_isp_buf_put_scratch() - Release scratch buffers
@@ -1357,6 +1394,14 @@ int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr,
rc = buf_mgr->ops->unmap_buf(buf_mgr, unmap_req->fd);
break;
}
+ case VIDIOC_MSM_ISP_CMD_EXT: {
+ struct msm_vfe_cmd_ext *cmd_ext = (struct msm_vfe_cmd_ext *)arg;
+
+ if (cmd_ext->type == VFE_GET_BUFQ_STATE)
+ rc = buf_mgr->ops->get_bufq_state(buf_mgr,
+ &cmd_ext->data.bufq_state);
+ break;
+ }
}
return rc;
}
@@ -1507,6 +1552,7 @@ static struct msm_isp_buf_ops isp_buf_ops = {
.buf_mgr_debug = msm_isp_buf_mgr_debug,
.get_bufq = msm_isp_get_bufq,
.update_put_buf_cnt = msm_isp_update_put_buf_cnt,
+ .get_bufq_state = msm_isp_get_bufq_state,
};
int msm_isp_create_isp_buf_mgr(
diff --git a/drivers/media/platform/msm/ais/isp/msm_buf_mgr.h b/drivers/media/platform/msm/ais/isp/msm_buf_mgr.h
index 4794771d3213..d9a3661306e3 100644
--- a/drivers/media/platform/msm/ais/isp/msm_buf_mgr.h
+++ b/drivers/media/platform/msm/ais/isp/msm_buf_mgr.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -44,16 +44,6 @@ enum msm_isp_buffer_src_t {
MSM_ISP_BUFFER_SRC_MAX,
};
-enum msm_isp_buffer_state {
- MSM_ISP_BUFFER_STATE_UNUSED, /* not used */
- MSM_ISP_BUFFER_STATE_INITIALIZED, /* REQBUF done */
- MSM_ISP_BUFFER_STATE_PREPARED, /* BUF mapped */
- MSM_ISP_BUFFER_STATE_QUEUED, /* buf queued */
- MSM_ISP_BUFFER_STATE_DEQUEUED, /* in use in VFE */
- MSM_ISP_BUFFER_STATE_DIVERTED, /* Sent to other hardware*/
- MSM_ISP_BUFFER_STATE_DISPATCHED, /* Sent to HAL*/
-};
-
enum msm_isp_buffer_put_state {
MSM_ISP_BUFFER_STATE_PUT_PREPARED, /* on init */
MSM_ISP_BUFFER_STATE_PUT_BUF, /* on rotation */
@@ -182,6 +172,9 @@ struct msm_isp_buf_ops {
int (*update_put_buf_cnt)(struct msm_isp_buf_mgr *buf_mgr,
uint32_t id, uint32_t bufq_handle, int32_t buf_index,
struct timeval *tv, uint32_t frame_id, uint32_t pingpong_bit);
+
+ int (*get_bufq_state)(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_vfe_bufq_state *bufq_state);
};
struct msm_isp_buf_mgr {
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp47.c b/drivers/media/platform/msm/ais/isp/msm_isp47.c
index 52bf8121f32b..1ddcab3ed331 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp47.c
+++ b/drivers/media/platform/msm/ais/isp/msm_isp47.c
@@ -22,6 +22,7 @@
#include "cam_hw_ops.h"
#include "msm_isp47.h"
#include "cam_soc_api.h"
+#include "msm_camera_diag_util.h"
#undef CDBG
#define CDBG(fmt, args...) pr_debug(fmt, ##args)
@@ -2396,6 +2397,8 @@ int msm_vfe47_update_bandwidth(
ab, ib,
isp_bandwidth_mgr->client_info,
sched_clock());
+ msm_camera_diag_update_isp_state(
+ isp_bandwidth_mgr->bus_vector_active_idx, ab, ib);
return 0;
}
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c
index 77f2ab5e7c3d..f135cfcd6ccd 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c
+++ b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c
@@ -1956,7 +1956,8 @@ static void msm_isp_handle_done_buf_frame_id_mismatch(
static int msm_isp_process_done_buf(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, struct msm_isp_buffer *buf,
- struct timeval *time_stamp, uint32_t frame_id)
+ struct timeval *time_stamp, struct timeval *time_stamp_system,
+ uint32_t frame_id)
{
int rc;
unsigned long flags;
@@ -2037,7 +2038,13 @@ static int msm_isp_process_done_buf(struct vfe_device *vfe_dev,
}
buf_event.frame_id = frame_id;
+ /* timestamp stores monotonic time */
buf_event.timestamp = *time_stamp;
+ /* for buf_event, mono_timestamp is unused attribute
+ * reuse this to store system time and propagate to
+ * userspace
+ */
+ buf_event.mono_timestamp = *time_stamp_system;
buf_event.u.buf_done.session_id = stream_info->session_id;
buf_event.u.buf_done.stream_id = stream_info->stream_id;
buf_event.u.buf_done.handle = buf->bufq_handle;
@@ -2831,7 +2838,6 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev,
msm_isp_get_stream_wm_mask(stream_info, &wm_reload_mask);
spin_lock_irqsave(&stream_info->lock, flags);
- msm_isp_reset_framedrop(vfe_dev, stream_info);
rc = msm_isp_init_stream_ping_pong_reg(vfe_dev, stream_info);
if (rc < 0) {
pr_err("%s: No buffer for stream%d\n", __func__,
@@ -3579,6 +3585,11 @@ int msm_isp_axi_output_cfg(struct vfe_device *vfe_dev, void *arg)
pstream_info, plane_idx);
}
+ vfe_dev->hw_info->vfe_ops.axi_ops.cfg_framedrop(
+ vfe_dev->vfe_base, pstream_info,
+ pCmd->output_path_cfg[axi_src_idx].framedrop_pattern,
+ pCmd->output_path_cfg[axi_src_idx].framedrop_period);
+
if (axi_src_idx <= PIX_ENCODER && axi_src_idx <= IDEAL_RAW) {
if (axi_src_idx == CAMIF_RAW) {
vfe_dev->axi_data.src_info[VFE_PIX_0].
@@ -3619,6 +3630,29 @@ int msm_isp_axi_output_cfg(struct vfe_device *vfe_dev, void *arg)
return rc;
}
+void msm_isp_framedrop_update(struct vfe_device *vfe_dev, void *arg)
+{
+ struct msm_vfe_axi_framedrop_update *pCmd = arg;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *pstream_info;
+
+ pr_debug("%s: entry\n", __func__);
+
+ if (pCmd->stream_src < VFE_AXI_SRC_MAX) {
+
+ pstream_info = &axi_data->stream_info[pCmd->stream_src];
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.cfg_framedrop(
+ vfe_dev->vfe_base, pstream_info,
+ pCmd->framedrop_pattern,
+ pCmd->framedrop_period);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
+ vfe_dev, SRC_TO_INTF(pstream_info->stream_src));
+ }
+
+ pr_debug("%s: exit\n", __func__);
+}
int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
{
@@ -3934,6 +3968,7 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev,
struct msm_isp_buffer *done_buf = NULL;
unsigned long flags;
struct timeval *time_stamp;
+ struct timeval *time_stamp_system;
uint32_t frame_id, buf_index = -1;
struct msm_vfe_axi_stream *temp_stream;
@@ -3947,6 +3982,8 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev,
time_stamp = &ts->vt_time;
} else {
time_stamp = &ts->buf_time;
+ /* store system time */
+ time_stamp_system = &ts->event_time;
}
frame_id = vfe_dev->axi_data.
@@ -4089,7 +4126,7 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev,
}
msm_isp_process_done_buf(vfe_dev, stream_info,
- done_buf, time_stamp, frame_id);
+ done_buf, time_stamp, time_stamp_system, frame_id);
}
void msm_isp_process_axi_irq(struct vfe_device *vfe_dev,
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h
index 7babd750a05a..d695c4c0edf3 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h
+++ b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h
@@ -84,6 +84,7 @@ int msm_isp_axi_restart(struct vfe_device *vfe_dev,
int msm_isp_axi_output_cfg(struct vfe_device *vfe_dev, void *arg);
+void msm_isp_framedrop_update(struct vfe_device *vfe_dev, void *arg);
void msm_isp_axi_stream_update(struct vfe_device *vfe_dev,
enum msm_vfe_input_src frame_src);
diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_util.c
index 2ba19b13535b..a9b6e5e6a861 100644
--- a/drivers/media/platform/msm/ais/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/ais/isp/msm_isp_util.c
@@ -847,6 +847,24 @@ static int msm_isp_proc_cmd_list(struct vfe_device *vfe_dev, void *arg)
}
#endif /* CONFIG_COMPAT */
+static int process_isp_cmd_ext(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ struct msm_vfe_cmd_ext *cmd = (struct msm_vfe_cmd_ext *)arg;
+
+ switch (cmd->type) {
+ case VFE_GET_BUFQ_STATE: {
+ mutex_lock(&vfe_dev->buf_mgr->lock);
+ rc = msm_isp_proc_buf_cmd(vfe_dev->buf_mgr,
+ VIDIOC_MSM_ISP_CMD_EXT, arg);
+ mutex_unlock(&vfe_dev->buf_mgr->lock);
+ break;
+ }
+ }
+
+ return rc;
+}
+
static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
@@ -983,12 +1001,14 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd,
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);
rc = msm_isp_start_fetch_engine(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
+ case VIDIOC_MSM_ISP_CMD_EXT:
+ process_isp_cmd_ext(vfe_dev, arg);
+ break;
case VIDIOC_MSM_ISP_FETCH_ENG_MULTI_PASS_START:
case VIDIOC_MSM_ISP_MAP_BUF_START_MULTI_PASS_FE:
mutex_lock(&vfe_dev->core_mutex);
@@ -1053,6 +1073,11 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd,
rc = msm_isp_camif_cfg(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
+ case VIDIOC_MSM_ISP_FRAMEDROP_UPDATE:
+ mutex_lock(&vfe_dev->core_mutex);
+ msm_isp_framedrop_update(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
case MSM_SD_NOTIFY_FREEZE:
vfe_dev->isp_sof_debug = 0;
vfe_dev->isp_raw0_debug = 0;
@@ -2350,16 +2375,6 @@ int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
mutex_lock(&vfe_dev->realtime_mutex);
mutex_lock(&vfe_dev->core_mutex);
- /* Enable vfe clks to wake up from XO shutdown mode */
- if (vfe_dev->pdev->id == 0)
- id = CAM_AHB_CLIENT_VFE0;
- else
- id = CAM_AHB_CLIENT_VFE1;
- if (cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SVS_VOTE) < 0)
- pr_err("%s: failed to vote for AHB\n", __func__);
- vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks(vfe_dev, 1);
- vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 1);
-
if (!vfe_dev->vfe_open_cnt) {
pr_err("%s invalid state open cnt %d\n", __func__,
vfe_dev->vfe_open_cnt);
@@ -2374,6 +2389,17 @@ int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
mutex_unlock(&vfe_dev->realtime_mutex);
return 0;
}
+
+ /* Enable vfe clks to wake up from XO shutdown mode */
+ if (vfe_dev->pdev->id == 0)
+ id = CAM_AHB_CLIENT_VFE0;
+ else
+ id = CAM_AHB_CLIENT_VFE1;
+ if (cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SVS_VOTE) < 0)
+ pr_err("%s: failed to vote for AHB\n", __func__);
+ vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks(vfe_dev, 1);
+ vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 1);
+
/* Unregister page fault handler */
cam_smmu_reg_client_page_fault_handler(
vfe_dev->buf_mgr->iommu_hdl,
diff --git a/drivers/media/platform/msm/ais/ispif/msm_ispif.c b/drivers/media/platform/msm/ais/ispif/msm_ispif.c
index a72ac566bb8c..5ddf554d6ef3 100644
--- a/drivers/media/platform/msm/ais/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/ais/ispif/msm_ispif.c
@@ -29,6 +29,7 @@
#include "msm_camera_io_util.h"
#include "cam_hw_ops.h"
#include "cam_soc_api.h"
+#include "msm_camera_diag_util.h"
#ifdef CONFIG_AIS_MSM_ISPIF_V1
#include "msm_ispif_hwreg_v1.h"
@@ -1526,6 +1527,22 @@ static long msm_ispif_cmd(struct v4l2_subdev *sd, void *arg)
case ISPIF_SET_VFE_INFO:
rc = msm_ispif_set_vfe_info(ispif, &pcdata->vfe_info);
break;
+ case ISPIF_READ_REG_LIST_CMD:
+ {
+ struct msm_camera_reg_list_cmd reg_list_cmd;
+
+ if (copy_from_user(&reg_list_cmd,
+ (void __user *)pcdata->reg_list,
+ sizeof(struct msm_camera_reg_list_cmd))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = msm_camera_get_reg_list(ispif->base, &reg_list_cmd);
+ break;
+ }
+ case ISPIF_WRITE_REG_LIST_CMD:
+ break;
default:
pr_err("%s: invalid cfg_type\n", __func__);
rc = -EINVAL;
diff --git a/drivers/media/platform/msm/ais/msm.c b/drivers/media/platform/msm/ais/msm.c
index 902e05b3329b..c3f3542cc87a 100644
--- a/drivers/media/platform/msm/ais/msm.c
+++ b/drivers/media/platform/msm/ais/msm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -31,6 +31,7 @@
#include "msm_sd.h"
#include "cam_hw_ops.h"
#include <media/ais/msm_ais_buf_mgr.h>
+#include "msm_camera_diag_util.h"
static struct v4l2_device *msm_v4l2_dev;
@@ -1384,6 +1385,12 @@ static int msm_probe(struct platform_device *pdev)
goto v4l2_fail;
}
+ rc = msm_camera_diag_init();
+ if (rc < 0) {
+ pr_err("%s: failed to init diag clk list\n", __func__);
+ goto v4l2_fail;
+ }
+
goto probe_end;
v4l2_fail:
@@ -1428,6 +1435,7 @@ static int __init msm_init(void)
static void __exit msm_exit(void)
{
+ msm_camera_diag_uninit();
platform_driver_unregister(&msm_driver);
}
diff --git a/drivers/media/platform/msm/ais/msm_ais_diag/Makefile b/drivers/media/platform/msm/ais/msm_ais_diag/Makefile
new file mode 100644
index 000000000000..7c40ea02b70a
--- /dev/null
+++ b/drivers/media/platform/msm/ais/msm_ais_diag/Makefile
@@ -0,0 +1,4 @@
+ccflags-y += -Idrivers/media/platform/msm/ais
+ccflags-y += -Idrivers/media/platform/msm/ais/common
+ccflags-y += -Idrivers/media/platform/msm/ais/sensor/io
+obj-$(CONFIG_MSM_AIS) += msm_diag_cam.o
diff --git a/drivers/media/platform/msm/ais/msm_ais_diag/msm_diag_cam.c b/drivers/media/platform/msm/ais/msm_ais_diag/msm_diag_cam.c
new file mode 100644
index 000000000000..c2933d79babc
--- /dev/null
+++ b/drivers/media/platform/msm/ais/msm_ais_diag/msm_diag_cam.c
@@ -0,0 +1,267 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/regulator/consumer.h>
+#include "msm_sd.h"
+#include "msm_diag_cam.h"
+#include "msm_camera_io_util.h"
+#include "msm_camera_dt_util.h"
+#include "cam_hw_ops.h"
+#include "msm_camera_diag_util.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+#undef DIAG_CAM_DBG
+#ifdef MSM_DIAG_CAM_DEBUG
+#define DIAG_CAM_DBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define DIAG_CAM_DBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+#define MSM_DIAG_CAM_DRV_NAME "msm_diag_cam"
+static struct platform_driver msm_diag_camera_driver;
+static struct diag_cam_device *new_diag_cam_dev;
+
+int msm_ais_enable_allclocks(void)
+{
+ int rc = 0;
+
+ CDBG("%s:\n", __func__);
+ /* Vote ON for clocks */
+ if (new_diag_cam_dev == NULL) {
+ rc = -EINVAL;
+ pr_err("%s: clock structure uninitialised %d\n", __func__,
+ rc);
+ return rc;
+ }
+
+ rc = msm_camera_enable_vreg(&new_diag_cam_dev->pdev->dev,
+ new_diag_cam_dev->diag_cam_vreg,
+ new_diag_cam_dev->regulator_count,
+ NULL,
+ 0,
+ &new_diag_cam_dev->diag_cam_reg_ptr[0], 1);
+ if (rc < 0)
+ pr_err("%s:%d diag_cam enable_vreg failed\n", __func__,
+ __LINE__);
+
+ rc = msm_camera_clk_enable(&new_diag_cam_dev->pdev->dev,
+ new_diag_cam_dev->diag_cam_clk_info,
+ new_diag_cam_dev->diag_cam_clk,
+ new_diag_cam_dev->num_clk, true);
+
+ if (rc < 0) {
+ pr_err("%s: clk enable failed %d\n", __func__, rc);
+ rc = 0;
+ return rc;
+ }
+ pr_debug("Turned ON camera clocks\n");
+ return 0;
+
+}
+
+int msm_ais_disable_allclocks(void)
+{
+ int rc = 0;
+
+ CDBG("%s:\n", __func__);
+ /* Vote OFF for clocks */
+ if (new_diag_cam_dev == NULL) {
+ rc = -EINVAL;
+ pr_err("%s: clock structure uninitialised %d\n", __func__,
+ rc);
+ return rc;
+ }
+
+ if ((new_diag_cam_dev->pdev == NULL) ||
+ (new_diag_cam_dev->diag_cam_clk_info == NULL) ||
+ (new_diag_cam_dev->diag_cam_clk == NULL) ||
+ (new_diag_cam_dev->num_clk == 0)) {
+ rc = -EINVAL;
+ pr_err("%s: Clock details uninitialised %d\n", __func__,
+ rc);
+ return rc;
+ }
+
+ rc = msm_camera_clk_enable(&new_diag_cam_dev->pdev->dev,
+ new_diag_cam_dev->diag_cam_clk_info,
+ new_diag_cam_dev->diag_cam_clk,
+ new_diag_cam_dev->num_clk, false);
+ if (rc < 0) {
+ pr_err("%s: clk disable failed %d\n", __func__, rc);
+ return rc;
+ }
+
+ rc = msm_camera_enable_vreg(&new_diag_cam_dev->pdev->dev,
+ new_diag_cam_dev->diag_cam_vreg,
+ new_diag_cam_dev->regulator_count,
+ NULL,
+ 0,
+ &new_diag_cam_dev->diag_cam_reg_ptr[0], 0);
+ if (rc < 0)
+ pr_err("%s:%d diag_cam disable_vreg failed\n", __func__,
+ __LINE__);
+
+ pr_debug("Turned OFF camera clocks\n");
+ return 0;
+}
+
+int msm_diag_camera_get_vreginfo_list(
+ struct msm_ais_diag_regulator_info_list_t *p_vreglist)
+{
+ int rc = 0;
+ uint32_t i = 0;
+ uint32_t len = 0;
+ uint32_t len1 = 0;
+ struct regulator *vreg = NULL;
+ char *vreg_name_inuser = NULL;
+
+ p_vreglist->regulator_num = new_diag_cam_dev->regulator_count;
+
+ pr_debug("ais diag regulator_count %u\n",
+ new_diag_cam_dev->regulator_count);
+
+ for (; i < p_vreglist->regulator_num ; ++i) {
+ vreg = new_diag_cam_dev->diag_cam_reg_ptr[i];
+ p_vreglist->infolist[i].enable =
+ regulator_is_enabled(vreg);
+ len = strlen(new_diag_cam_dev->diag_cam_vreg[i].reg_name);
+ len1 = sizeof(p_vreglist->infolist[i].regulatorname);
+ len = (len >= len1) ? len1 : (len+1);
+ vreg_name_inuser =
+ p_vreglist->infolist[i].regulatorname;
+ if (copy_to_user((void __user *)vreg_name_inuser,
+ (void *)new_diag_cam_dev->diag_cam_vreg[i].reg_name,
+ len)) {
+ rc = -EFAULT;
+ pr_err("%s copy_to_user fail\n", __func__);
+ break;
+ }
+ }
+
+ pr_debug("msm_diag_camera_get_vreginfo_list exit\n");
+ return rc;
+}
+
+static int msm_diag_cam_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+
+ CDBG("%s: pdev %pK device id = %d\n", __func__, pdev, pdev->id);
+
+ new_diag_cam_dev = kzalloc(sizeof(struct diag_cam_device),
+ GFP_KERNEL);
+ if (!new_diag_cam_dev)
+ return -ENOMEM;
+
+ if (pdev->dev.of_node)
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+
+ rc = msm_camera_get_clk_info(pdev,
+ &new_diag_cam_dev->diag_cam_clk_info,
+ &new_diag_cam_dev->diag_cam_clk,
+ &new_diag_cam_dev->num_clk);
+ if (rc < 0) {
+ pr_err("%s: msm_diag_cam_get_clk_info() failed", __func__);
+ kfree(new_diag_cam_dev);
+ return -EFAULT;
+ }
+
+ new_diag_cam_dev->ref_count = 0;
+ new_diag_cam_dev->pdev = pdev;
+
+ rc = msm_camera_get_dt_vreg_data(
+ new_diag_cam_dev->pdev->dev.of_node,
+ &(new_diag_cam_dev->diag_cam_vreg),
+ &(new_diag_cam_dev->regulator_count));
+ if (rc < 0) {
+ pr_err("%s: msm_camera_get_dt_vreg_data fail\n", __func__);
+ rc = -EFAULT;
+ goto diag_cam_release_mem;
+ }
+
+ if ((new_diag_cam_dev->regulator_count < 0) ||
+ (new_diag_cam_dev->regulator_count > MAX_REGULATOR)) {
+ pr_err("%s: invalid reg count = %d, max is %d\n", __func__,
+ new_diag_cam_dev->regulator_count, MAX_REGULATOR);
+ rc = -EFAULT;
+ goto diag_cam_invalid_vreg_data;
+ }
+
+ rc = msm_camera_config_vreg(&new_diag_cam_dev->pdev->dev,
+ new_diag_cam_dev->diag_cam_vreg,
+ new_diag_cam_dev->regulator_count,
+ NULL,
+ 0,
+ &new_diag_cam_dev->diag_cam_reg_ptr[0], 1);
+ if (rc < 0)
+ pr_err("%s:%d diag_cam config_vreg failed\n", __func__,
+ __LINE__);
+
+ platform_set_drvdata(pdev, new_diag_cam_dev);
+
+ return 0;
+
+diag_cam_invalid_vreg_data:
+ kfree(new_diag_cam_dev->diag_cam_vreg);
+diag_cam_release_mem:
+ kfree(new_diag_cam_dev);
+ new_diag_cam_dev = NULL;
+ return rc;
+}
+
+static int msm_diag_cam_exit(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int __init msm_diag_cam_init_module(void)
+{
+ return platform_driver_register(&msm_diag_camera_driver);
+}
+
+static void __exit msm_diag_cam_exit_module(void)
+{
+ kfree(new_diag_cam_dev);
+ platform_driver_unregister(&msm_diag_camera_driver);
+}
+
+static const struct of_device_id msm_diag_camera_match_table[] = {
+ { .compatible = "qcom,diag-cam" },
+ {},
+};
+
+static struct platform_driver msm_diag_camera_driver = {
+ .probe = msm_diag_cam_probe,
+ .remove = msm_diag_cam_exit,
+ .driver = {
+ .name = MSM_DIAG_CAM_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_diag_camera_match_table,
+ },
+};
+
+MODULE_DEVICE_TABLE(of, msm_diag_camera_match_table);
+
+module_init(msm_diag_cam_init_module);
+module_exit(msm_diag_cam_exit_module);
+MODULE_DESCRIPTION("MSM diag camera driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/ais/msm_ais_diag/msm_diag_cam.h b/drivers/media/platform/msm/ais/msm_ais_diag/msm_diag_cam.h
new file mode 100644
index 000000000000..572ba8dfba3a
--- /dev/null
+++ b/drivers/media/platform/msm/ais/msm_ais_diag/msm_diag_cam.h
@@ -0,0 +1,57 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_DIAG_CAM_H
+#define MSM_DIAG_CAM_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <linux/workqueue.h>
+#include <media/ais/msm_ais_sensor.h>
+#include <soc/qcom/ais.h>
+#include <media/ais/msm_ais.h>
+#include "msm_sd.h"
+#include "cam_soc_api.h"
+
+#define NUM_MASTERS 2
+#define NUM_QUEUES 2
+
+#define TRUE 1
+#define FALSE 0
+
+
+enum msm_diag_cam_state_t {
+ AIS_DIAG_STATE_DISABLED,
+ AIS_DIAG_STATE_ENABLED,
+};
+
+struct diag_cam_device {
+ struct platform_device *pdev;
+ uint8_t ref_count;
+ enum msm_diag_cam_state_t diag_cam_state;
+ size_t num_clk;
+ size_t num_clk_cases;
+ struct clk **diag_cam_clk;
+ uint32_t **diag_cam_clk_rates;
+ struct msm_cam_clk_info *diag_cam_clk_info;
+ struct camera_vreg_t *diag_cam_vreg;
+ struct regulator *diag_cam_reg_ptr[MAX_REGULATOR];
+ int32_t regulator_count;
+};
+
+int msm_ais_enable_allclocks(void);
+int msm_ais_disable_allclocks(void);
+int msm_diag_camera_get_vreginfo_list(
+ struct msm_ais_diag_regulator_info_list_t *p_vreglist);
+#endif
diff --git a/drivers/media/platform/msm/ais/msm_ais_mgr/Makefile b/drivers/media/platform/msm/ais/msm_ais_mgr/Makefile
index b7a078738489..bb14aec1ee29 100644
--- a/drivers/media/platform/msm/ais/msm_ais_mgr/Makefile
+++ b/drivers/media/platform/msm/ais/msm_ais_mgr/Makefile
@@ -2,4 +2,5 @@ ccflags-y += -Idrivers/media/platform/msm/ais
ccflags-y += -Idrivers/media/platform/msm/ais/common
ccflags-y += -Idrivers/media/platform/msm/ais/sensor/io
ccflags-y += -Idrivers/media/platform/msm/ais/sensor/cci
+ccflags-y += -Idrivers/media/platform/msm/ais/msm_ais_diag
obj-$(CONFIG_MSM_AIS) += msm_ais_mgr.o
diff --git a/drivers/media/platform/msm/ais/msm_ais_mgr/msm_ais_mgr.c b/drivers/media/platform/msm/ais/msm_ais_mgr/msm_ais_mgr.c
index 9391c1d0d4ab..4ae07932f5da 100644
--- a/drivers/media/platform/msm/ais/msm_ais_mgr/msm_ais_mgr.c
+++ b/drivers/media/platform/msm/ais/msm_ais_mgr/msm_ais_mgr.c
@@ -15,6 +15,8 @@
#include <media/ais/msm_ais_mgr.h>
#include "msm_ais_mngr.h"
#include "msm_early_cam.h"
+#include "msm_camera_diag_util.h"
+#include "msm_diag_cam.h"
#undef CDBG
#define CDBG(fmt, args...) pr_debug(fmt, ##args)
@@ -42,7 +44,56 @@ static long msm_ais_hndl_ioctl(struct v4l2_subdev *sd, void *arg)
case AIS_CLK_DISABLE:
rc = msm_ais_disable_clocks();
break;
+ case AIS_CLK_ENABLE_ALLCLK:
+ rc = msm_ais_enable_allclocks();
+ break;
+ case AIS_CLK_DISABLE_ALLCLK:
+ rc = msm_ais_disable_allclocks();
+ break;
+ default:
+ pr_err("invalid cfg_type\n");
+ rc = -EINVAL;
+ }
+
+ if (rc)
+ pr_err("msm_ais_hndl_ioctl failed %ld\n", rc);
+
+ mutex_unlock(&clk_mngr_dev->cont_mutex);
+ return rc;
+}
+
+static long msm_ais_hndl_ext_ioctl(struct v4l2_subdev *sd, void *arg)
+{
+ long rc = 0;
+ struct clk_mgr_cfg_data_ext *pcdata =
+ (struct clk_mgr_cfg_data_ext *)arg;
+ struct msm_ais_mngr_device *clk_mngr_dev =
+ (struct msm_ais_mngr_device *)v4l2_get_subdevdata(sd);
+ if (WARN_ON(!clk_mngr_dev) || WARN_ON(!pcdata)) {
+ rc = -EINVAL;
+ return rc;
+ }
+
+ mutex_lock(&clk_mngr_dev->cont_mutex);
+ CDBG(pr_fmt("cfg_type = %d\n"), pcdata->cfg_type);
+ switch (pcdata->cfg_type) {
+ case AIS_DIAG_GET_REGULATOR_INFO_LIST:
+ rc = msm_diag_camera_get_vreginfo_list(
+ &pcdata->data.vreg_infolist);
+ break;
+ case AIS_DIAG_GET_BUS_INFO_STATE:
+ rc = msm_camera_diag_get_ddrbw(&pcdata->data.bus_info);
+ break;
+ case AIS_DIAG_GET_CLK_INFO_LIST:
+ rc = msm_camera_diag_get_clk_list(&pcdata->data.clk_infolist);
+ break;
+ case AIS_DIAG_GET_GPIO_LIST:
+ rc = msm_camera_diag_get_gpio_list(&pcdata->data.gpio_list);
+ break;
+ case AIS_DIAG_SET_GPIO_LIST:
+ rc = msm_camera_diag_set_gpio_list(&pcdata->data.gpio_list);
+ break;
default:
pr_err("invalid cfg_type\n");
rc = -EINVAL;
@@ -63,6 +114,11 @@ static long msm_ais_mngr_subdev_ioctl(struct v4l2_subdev *sd,
if (rc)
pr_err("msm_ais_mngr_subdev_ioctl failed\n");
break;
+ case VIDIOC_MSM_AIS_CLK_CFG_EXT:
+ rc = msm_ais_hndl_ext_ioctl(sd, arg);
+ if (rc)
+ pr_err("msm_ais_hndl_ext_ioctl failed\n");
+ break;
default:
rc = -ENOIOCTLCMD;
}
@@ -136,6 +192,7 @@ static int32_t __init msm_ais_mngr_init(void)
static void __exit msm_ais_mngr_exit(void)
{
+
msm_sd_unregister(&msm_ais_mngr_dev->subdev);
mutex_destroy(&msm_ais_mngr_dev->cont_mutex);
kfree(msm_ais_mngr_dev);
diff --git a/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c b/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c
index 6d26dff7525d..b820aa45136a 100644
--- a/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c
+++ b/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c
@@ -36,6 +36,7 @@
#include "include/msm_csid_3_6_0_hwreg.h"
#include "include/msm_csid_3_5_1_hwreg.h"
#include "cam_hw_ops.h"
+#include "msm_camera_diag_util.h"
#define V4L2_IDENT_CSID 50002
#define CSID_VERSION_V20 0x02000011
@@ -870,6 +871,20 @@ static int32_t msm_csid_cmd(struct csid_device *csid_dev, void *arg)
case CSID_STOP:
rc = msm_csid_stop(csid_dev, cdata->cfg.csid_cidmask);
break;
+ case CSID_READ_REG_LIST_CMD:
+ {
+ struct msm_camera_reg_list_cmd reg_list_cmd;
+
+ if (copy_from_user(&reg_list_cmd,
+ (void __user *)cdata->cfg.csid_reg_list_cmd,
+ sizeof(struct msm_camera_reg_list_cmd))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = msm_camera_get_reg_list(csid_dev->base, &reg_list_cmd);
+ break;
+ }
default:
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -ENOIOCTLCMD;
diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c
index c3b087f61888..ebf817149184 100644
--- a/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c
+++ b/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -27,6 +27,7 @@
#include "include/msm_csiphy_3_4_2_1_hwreg.h"
#include "include/msm_csiphy_3_5_hwreg.h"
#include "cam_hw_ops.h"
+#include "msm_camera_diag_util.h"
#define DBG_CSIPHY 0
#define SOF_DEBUG_ENABLE 1
@@ -1264,6 +1265,20 @@ static int32_t msm_csiphy_cmd(struct csiphy_device *csiphy_dev, void *arg)
}
break;
+ case CSIPHY_READ_REG_LIST_CMD:
+ {
+ struct msm_camera_reg_list_cmd reg_list_cmd;
+
+ if (copy_from_user(&reg_list_cmd,
+ (void __user *)cdata->cfg.csiphy_reg_list_cmd,
+ sizeof(struct msm_camera_reg_list_cmd))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = msm_camera_get_reg_list(csiphy_dev->base, &reg_list_cmd);
+ break;
+ }
default:
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -ENOIOCTLCMD;
diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor.c b/drivers/media/platform/msm/ais/sensor/msm_sensor.c
index 0dda3a64b1a2..657c6ee7c0b6 100644
--- a/drivers/media/platform/msm/ais/sensor/msm_sensor.c
+++ b/drivers/media/platform/msm/ais/sensor/msm_sensor.c
@@ -842,7 +842,8 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl,
if (s_ctrl->is_csid_tg_mode)
goto DONE;
- if (s_ctrl->sensor_state != MSM_SENSOR_POWER_DOWN) {
+ if ((s_ctrl->sensor_state != MSM_SENSOR_POWER_DOWN) &&
+ (s_ctrl->sensor_state != MSM_SENSOR_CCI_DOWN)) {
pr_err("%s:%d failed: invalid state %d\n", __func__,
__LINE__, s_ctrl->sensor_state);
rc = -EFAULT;
@@ -1242,7 +1243,12 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void *argp)
pr_err("%s:%d: i2c_read failed\n", __func__, __LINE__);
break;
}
- read_config_ptr->data = local_data;
+ if (copy_to_user((void __user *)&read_config_ptr->data,
+ &local_data, sizeof(local_data))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
break;
}
case CFG_SLAVE_WRITE_I2C_ARRAY: {
@@ -1398,7 +1404,8 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void *argp)
if (s_ctrl->is_csid_tg_mode)
goto DONE;
- if (s_ctrl->sensor_state != MSM_SENSOR_POWER_DOWN) {
+ if ((s_ctrl->sensor_state != MSM_SENSOR_POWER_DOWN) &&
+ (s_ctrl->sensor_state != MSM_SENSOR_CCI_DOWN)) {
pr_err("%s:%d failed: invalid state %d\n", __func__,
__LINE__, s_ctrl->sensor_state);
rc = -EFAULT;
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 6db3c3c527a3..925a89601636 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
@@ -236,6 +236,7 @@ static int msm_isp_validate_axi_request(struct vfe_device *vfe_dev,
case V4L2_PIX_FMT_META:
case V4L2_PIX_FMT_META10:
case V4L2_PIX_FMT_GREY:
+ case V4L2_PIX_FMT_Y10:
stream_info->num_planes = 1;
stream_info->format_factor = ISP_Q2;
break;
@@ -345,6 +346,7 @@ static uint32_t msm_isp_axi_get_plane_size(
case V4L2_PIX_FMT_QGRBG10:
case V4L2_PIX_FMT_QRGGB10:
case V4L2_PIX_FMT_META10:
+ case V4L2_PIX_FMT_Y10:
/* TODO: fix me */
size = plane_cfg[plane_idx].output_height *
plane_cfg[plane_idx].output_width;
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 a2381557070d..6640e414a798 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
@@ -1150,8 +1150,10 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file,
goto debug_read_error;
}
- if (dbg->off % sizeof(u32))
- return -EFAULT;
+ if (dbg->off % sizeof(u32)) {
+ rc = -EFAULT;
+ goto debug_read_error;
+ }
ptr = dbg->base + dbg->off;
tot = 0;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 3af6e53b21e7..ee3cfb88855c 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -2614,6 +2614,7 @@ int msm_comm_check_core_init(struct msm_vidc_core *core)
int rc = 0;
struct hfi_device *hdev;
struct msm_vidc_inst *inst = NULL;
+ int dref = 0;
mutex_lock(&core->lock);
if (core->state >= VIDC_CORE_INIT_DONE) {
@@ -2637,11 +2638,16 @@ int msm_comm_check_core_init(struct msm_vidc_core *core)
* Just grab one of the inst from instances list and
* use it.
*/
- inst = list_first_entry(&core->instances,
+ inst = list_first_entry_or_null(&core->instances,
struct msm_vidc_inst, list);
+ if (inst)
+ dref = kref_get_unless_zero(&inst->kref);
mutex_unlock(&core->lock);
- msm_comm_print_debug_info(inst);
+ if (dref) {
+ msm_comm_print_debug_info(inst);
+ put_inst(inst);
+ }
mutex_lock(&core->lock);
BUG_ON(msm_vidc_debug_timeout);
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
index 537b858cb94a..fa6af4a7dae1 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -1268,16 +1268,17 @@ static void __camif_subdev_try_format(struct camif_dev *camif,
{
const struct s3c_camif_variant *variant = camif->variant;
const struct vp_pix_limits *pix_lim;
- int i = ARRAY_SIZE(camif_mbus_formats);
+ unsigned int i;
/* FIXME: constraints against codec or preview path ? */
pix_lim = &variant->vp_pix_limits[VP_CODEC];
- while (i-- >= 0)
+ for (i = 0; i < ARRAY_SIZE(camif_mbus_formats); i++)
if (camif_mbus_formats[i] == mf->code)
break;
- mf->code = camif_mbus_formats[i];
+ if (i == ARRAY_SIZE(camif_mbus_formats))
+ mf->code = camif_mbus_formats[0];
if (pad == CAMIF_SD_PAD_SINK) {
v4l_bound_align_image(&mf->width, 8, CAMIF_MAX_PIX_WIDTH,
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index 76bf8ba372b3..5b53e31ce262 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -187,7 +187,7 @@
USB 2.0 spec says bulk packet size is always 512 bytes
*/
#define EM28XX_BULK_PACKET_MULTIPLIER 384
-#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 384
+#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 94
#define EM28XX_INTERLACED_DEFAULT 1
diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c
index 02b5f69e1a42..14cf6dfc3b14 100644
--- a/drivers/message/fusion/mptctl.c
+++ b/drivers/message/fusion/mptctl.c
@@ -2698,6 +2698,8 @@ mptctl_hp_targetinfo(unsigned long arg)
__FILE__, __LINE__, iocnum);
return -ENODEV;
}
+ if (karg.hdr.id >= MPT_MAX_FC_DEVICES)
+ return -EINVAL;
dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_hp_targetinfo called.\n",
ioc->name));
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index ce47780e5936..f0140e8bbe68 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -8729,6 +8729,7 @@ exit_unreg_chrdev_region:
static int qseecom_remove(struct platform_device *pdev)
{
struct qseecom_registered_kclient_list *kclient = NULL;
+ struct qseecom_registered_kclient_list *kclient_tmp = NULL;
unsigned long flags = 0;
int ret = 0;
int i;
@@ -8738,10 +8739,8 @@ static int qseecom_remove(struct platform_device *pdev)
atomic_set(&qseecom.qseecom_state, QSEECOM_STATE_NOT_READY);
spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
- list_for_each_entry(kclient, &qseecom.registered_kclient_list_head,
- list) {
- if (!kclient)
- goto exit_irqrestore;
+ list_for_each_entry_safe(kclient, kclient_tmp,
+ &qseecom.registered_kclient_list_head, list) {
/* Break the loop if client handle is NULL */
if (!kclient->handle)
@@ -8765,7 +8764,7 @@ exit_free_kc_handle:
kzfree(kclient->handle);
exit_free_kclient:
kzfree(kclient);
-exit_irqrestore:
+
spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
if (qseecom.qseos_version > QSEEE_VERSION_00)
diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c
index 1e688bfec567..fe90b7e04427 100644
--- a/drivers/misc/vmw_balloon.c
+++ b/drivers/misc/vmw_balloon.c
@@ -576,15 +576,9 @@ static void vmballoon_pop(struct vmballoon *b)
}
}
- if (b->batch_page) {
- vunmap(b->batch_page);
- b->batch_page = NULL;
- }
-
- if (b->page) {
- __free_page(b->page);
- b->page = NULL;
- }
+ /* Clearing the batch_page unconditionally has no adverse effect */
+ free_page((unsigned long)b->batch_page);
+ b->batch_page = NULL;
}
/*
@@ -991,16 +985,13 @@ static const struct vmballoon_ops vmballoon_batched_ops = {
static bool vmballoon_init_batching(struct vmballoon *b)
{
- b->page = alloc_page(VMW_PAGE_ALLOC_NOSLEEP);
- if (!b->page)
- return false;
+ struct page *page;
- b->batch_page = vmap(&b->page, 1, VM_MAP, PAGE_KERNEL);
- if (!b->batch_page) {
- __free_page(b->page);
+ page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!page)
return false;
- }
+ b->batch_page = page_address(page);
return true;
}
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 13e0df67d3b7..0747f22ce56c 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -3995,6 +3995,7 @@ cmdq_switch:
pr_err("%s: %s: mmc_blk_cmdq_switch failed: %d\n",
mmc_hostname(host), __func__, err);
ret = err;
+ goto out;
}
cmdq_unhalt:
err = mmc_cmdq_halt(host, false);
diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index f280744578e4..ffd448149796 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -32,6 +32,8 @@ struct sdhci_iproc_host {
const struct sdhci_iproc_data *data;
u32 shadow_cmd;
u32 shadow_blk;
+ bool is_cmd_shadowed;
+ bool is_blk_shadowed;
};
#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18)
@@ -47,8 +49,22 @@ static inline u32 sdhci_iproc_readl(struct sdhci_host *host, int reg)
static u16 sdhci_iproc_readw(struct sdhci_host *host, int reg)
{
- u32 val = sdhci_iproc_readl(host, (reg & ~3));
- u16 word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_iproc_host *iproc_host = sdhci_pltfm_priv(pltfm_host);
+ u32 val;
+ u16 word;
+
+ if ((reg == SDHCI_TRANSFER_MODE) && iproc_host->is_cmd_shadowed) {
+ /* Get the saved transfer mode */
+ val = iproc_host->shadow_cmd;
+ } else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
+ iproc_host->is_blk_shadowed) {
+ /* Get the saved block info */
+ val = iproc_host->shadow_blk;
+ } else {
+ val = sdhci_iproc_readl(host, (reg & ~3));
+ }
+ word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
return word;
}
@@ -104,13 +120,15 @@ static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg)
if (reg == SDHCI_COMMAND) {
/* Write the block now as we are issuing a command */
- if (iproc_host->shadow_blk != 0) {
+ if (iproc_host->is_blk_shadowed) {
sdhci_iproc_writel(host, iproc_host->shadow_blk,
SDHCI_BLOCK_SIZE);
- iproc_host->shadow_blk = 0;
+ iproc_host->is_blk_shadowed = false;
}
oldval = iproc_host->shadow_cmd;
- } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
+ iproc_host->is_cmd_shadowed = false;
+ } else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
+ iproc_host->is_blk_shadowed) {
/* Block size and count are stored in shadow reg */
oldval = iproc_host->shadow_blk;
} else {
@@ -122,9 +140,11 @@ static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg)
if (reg == SDHCI_TRANSFER_MODE) {
/* Save the transfer mode until the command is issued */
iproc_host->shadow_cmd = newval;
+ iproc_host->is_cmd_shadowed = true;
} else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
/* Save the block info until the command is issued */
iproc_host->shadow_blk = newval;
+ iproc_host->is_blk_shadowed = true;
} else {
/* Command or other regular 32-bit write */
sdhci_iproc_writel(host, newval, reg & ~3);
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index aea00ce708b6..81a781c1f9d6 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -39,6 +39,7 @@
#include <linux/msm-bus.h>
#include <linux/pm_runtime.h>
#include <trace/events/mmc.h>
+#include <soc/qcom/boot_stats.h>
#include "sdhci-msm.h"
#include "sdhci-msm-ice.h"
@@ -801,19 +802,23 @@ static int msm_init_cm_dll(struct sdhci_host *host)
| CORE_CK_OUT_EN), host->ioaddr +
msm_host_offset->CORE_DLL_CONFIG);
- wait_cnt = 50;
- /* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */
- while (!(readl_relaxed(host->ioaddr +
- msm_host_offset->CORE_DLL_STATUS) & CORE_DLL_LOCK)) {
- /* max. wait for 50us sec for LOCK bit to be set */
- if (--wait_cnt == 0) {
- pr_err("%s: %s: DLL failed to LOCK\n",
- mmc_hostname(mmc), __func__);
- rc = -ETIMEDOUT;
- goto out;
+ /* For hs400es mode, no need to wait for core dll lock */
+ if (!(msm_host->enhanced_strobe &&
+ mmc_card_strobe(msm_host->mmc->card))) {
+ wait_cnt = 50;
+ /* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */
+ while (!(readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_STATUS) & CORE_DLL_LOCK)) {
+ /* max. wait for 50us sec for LOCK bit to be set */
+ if (--wait_cnt == 0) {
+ pr_err("%s: %s: DLL failed to LOCK\n",
+ mmc_hostname(mmc), __func__);
+ rc = -ETIMEDOUT;
+ goto out;
+ }
+ /* wait for 1us before polling again */
+ udelay(1);
}
- /* wait for 1us before polling again */
- udelay(1);
}
out:
@@ -3166,7 +3171,10 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
| CORE_HC_SELECT_IN_EN), host->ioaddr +
msm_host_offset->CORE_VENDOR_SPEC);
}
- if (!host->mmc->ios.old_rate && !msm_host->use_cdclp533) {
+ /* No need to check for DLL lock for HS400es mode */
+ if (!host->mmc->ios.old_rate && !msm_host->use_cdclp533 &&
+ !((card && mmc_card_strobe(card) &&
+ msm_host->enhanced_strobe))) {
/*
* Poll on DLL_LOCK and DDR_DLL_LOCK bits in
* CORE_DLL_STATUS to be set. This should get set
@@ -4250,6 +4258,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
void __iomem *tlmm_mem;
unsigned long flags;
bool force_probe;
+ char boot_marker[40];
pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__);
msm_host = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_msm_host),
@@ -4274,6 +4283,10 @@ static int sdhci_msm_probe(struct platform_device *pdev)
goto out_host_free;
}
+ snprintf(boot_marker, sizeof(boot_marker),
+ "M - DRIVER %s Init", mmc_hostname(host->mmc));
+ place_marker(boot_marker);
+
pltfm_host = sdhci_priv(host);
pltfm_host->priv = msm_host;
msm_host->mmc = host->mmc;
@@ -4747,6 +4760,10 @@ static int sdhci_msm_probe(struct platform_device *pdev)
if (sdhci_msm_is_bootdevice(&pdev->dev))
mmc_flush_detect_work(host->mmc);
+ snprintf(boot_marker, sizeof(boot_marker),
+ "M - DRIVER %s Ready", mmc_hostname(host->mmc));
+ place_marker(boot_marker);
+
/* Successful initialization */
goto out;
diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c
index a5e4b4b93d1b..ec3766264408 100644
--- a/drivers/net/ethernet/broadcom/bgmac.c
+++ b/drivers/net/ethernet/broadcom/bgmac.c
@@ -531,7 +531,8 @@ static void bgmac_dma_tx_ring_free(struct bgmac *bgmac,
int i;
for (i = 0; i < BGMAC_TX_RING_SLOTS; i++) {
- int len = dma_desc[i].ctl1 & BGMAC_DESC_CTL1_LEN;
+ u32 ctl1 = le32_to_cpu(dma_desc[i].ctl1);
+ unsigned int len = ctl1 & BGMAC_DESC_CTL1_LEN;
slot = &ring->slots[i];
dev_kfree_skb(slot->skb);
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
index d946bba43726..87534c6efd66 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
@@ -594,7 +594,7 @@ static void bnx2x_ets_e3b0_nig_disabled(const struct link_params *params,
* slots for the highest priority.
*/
REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_NUM_STRICT_ARB_SLOTS :
- NIG_REG_P1_TX_ARB_NUM_STRICT_ARB_SLOTS, 0x100);
+ NIG_REG_P0_TX_ARB_NUM_STRICT_ARB_SLOTS, 0x100);
/* Mapping between the CREDIT_WEIGHT registers and actual client
* numbers
*/
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index a38a9cb3d544..9904d768a20a 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -2925,6 +2925,9 @@ static int bnxt_hwrm_vnic_set_tpa(struct bnxt *bp, u16 vnic_id, u32 tpa_flags)
struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id];
struct hwrm_vnic_tpa_cfg_input req = {0};
+ if (vnic->fw_vnic_id == INVALID_HW_RING_ID)
+ return 0;
+
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_VNIC_TPA_CFG, -1, -1);
if (tpa_flags) {
diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index b36643ef0593..029fa5bee520 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -1726,6 +1726,8 @@ static int enic_open(struct net_device *netdev)
}
for (i = 0; i < enic->rq_count; i++) {
+ /* enable rq before updating rq desc */
+ vnic_rq_enable(&enic->rq[i]);
vnic_rq_fill(&enic->rq[i], enic_rq_alloc_buf);
/* Need at least one buffer on ring to get going */
if (vnic_rq_desc_used(&enic->rq[i]) == 0) {
@@ -1737,8 +1739,6 @@ static int enic_open(struct net_device *netdev)
for (i = 0; i < enic->wq_count; i++)
vnic_wq_enable(&enic->wq[i]);
- for (i = 0; i < enic->rq_count; i++)
- vnic_rq_enable(&enic->rq[i]);
if (!enic_is_dynamic(enic) && !enic_is_sriov_vf(enic))
enic_dev_add_station_addr(enic);
@@ -1765,8 +1765,12 @@ static int enic_open(struct net_device *netdev)
return 0;
err_out_free_rq:
- for (i = 0; i < enic->rq_count; i++)
+ for (i = 0; i < enic->rq_count; i++) {
+ err = vnic_rq_disable(&enic->rq[i]);
+ if (err)
+ return err;
vnic_rq_clean(&enic->rq[i], enic_free_rq_buf);
+ }
enic_dev_notify_unset(enic);
err_out_free_intr:
enic_unset_affinity_hint(enic);
@@ -2539,11 +2543,11 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_master(pdev);
/* Query PCI controller on system for DMA addressing
- * limitation for the device. Try 64-bit first, and
+ * limitation for the device. Try 47-bit first, and
* fail to 32-bit.
*/
- err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(47));
if (err) {
err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (err) {
@@ -2557,10 +2561,10 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_out_release_regions;
}
} else {
- err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+ err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(47));
if (err) {
dev_err(dev, "Unable to obtain %u-bit DMA "
- "for consistent allocations, aborting\n", 64);
+ "for consistent allocations, aborting\n", 47);
goto err_out_release_regions;
}
using_dac = 1;
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index 901661149b44..2d61369f586f 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -3053,9 +3053,6 @@ static void gfar_process_frame(struct net_device *ndev, struct sk_buff *skb)
if (ndev->features & NETIF_F_RXCSUM)
gfar_rx_checksum(skb, fcb);
- /* Tell the skb what kind of packet this is */
- skb->protocol = eth_type_trans(skb, ndev);
-
/* There's need to check for NETIF_F_HW_VLAN_CTAG_RX here.
* Even if vlan rx accel is disabled, on some chips
* RXFCB_VLN is pseudo randomly set.
@@ -3126,13 +3123,15 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit)
continue;
}
+ gfar_process_frame(ndev, skb);
+
/* Increment the number of packets */
total_pkts++;
total_bytes += skb->len;
skb_record_rx_queue(skb, rx_queue->qindex);
- gfar_process_frame(ndev, skb);
+ skb->protocol = eth_type_trans(skb, ndev);
/* Send the packet up the stack */
napi_gro_receive(&rx_queue->grp->napi_rx, skb);
diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c
index 1908a38e7f31..485b9cc53f8b 100644
--- a/drivers/net/ethernet/intel/e1000e/ich8lan.c
+++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c
@@ -1574,7 +1574,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
* we have already determined whether we have link or not.
*/
if (!mac->autoneg)
- return -E1000_ERR_CONFIG;
+ return 1;
/* Auto-Neg is enabled. Auto Speed Detection takes care
* of MAC speed/duplex configuration. So we only need to
diff --git a/drivers/net/ethernet/intel/e1000e/mac.c b/drivers/net/ethernet/intel/e1000e/mac.c
index 645ace74429e..fe133f33a6c6 100644
--- a/drivers/net/ethernet/intel/e1000e/mac.c
+++ b/drivers/net/ethernet/intel/e1000e/mac.c
@@ -450,7 +450,7 @@ s32 e1000e_check_for_copper_link(struct e1000_hw *hw)
* we have already determined whether we have link or not.
*/
if (!mac->autoneg)
- return -E1000_ERR_CONFIG;
+ return 1;
/* Auto-Neg is enabled. Auto Speed Detection takes care
* of MAC speed/duplex configuration. So we only need to
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index 20d8806d2bff..6369d88b81c1 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -2330,8 +2330,8 @@ static int e1000_alloc_ring_dma(struct e1000_adapter *adapter,
{
struct pci_dev *pdev = adapter->pdev;
- ring->desc = dma_alloc_coherent(&pdev->dev, ring->size, &ring->dma,
- GFP_KERNEL);
+ ring->desc = dma_zalloc_coherent(&pdev->dev, ring->size, &ring->dma,
+ GFP_KERNEL);
if (!ring->desc)
return -ENOMEM;
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 7430dd44019e..ea693bbf56d8 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -818,6 +818,7 @@ static void mvneta_port_up(struct mvneta_port *pp)
}
mvreg_write(pp, MVNETA_TXQ_CMD, q_map);
+ q_map = 0;
/* Enable all initialized RXQs. */
mvreg_write(pp, MVNETA_RXQ_CMD, BIT(rxq_def));
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c
index 62f1a3433a62..d6d87dd8a28f 100644
--- a/drivers/net/ethernet/mellanox/mlx4/qp.c
+++ b/drivers/net/ethernet/mellanox/mlx4/qp.c
@@ -386,11 +386,11 @@ struct mlx4_qp *mlx4_qp_lookup(struct mlx4_dev *dev, u32 qpn)
struct mlx4_qp_table *qp_table = &mlx4_priv(dev)->qp_table;
struct mlx4_qp *qp;
- spin_lock(&qp_table->lock);
+ spin_lock_irq(&qp_table->lock);
qp = __mlx4_qp_lookup(dev, qpn);
- spin_unlock(&qp_table->lock);
+ spin_unlock_irq(&qp_table->lock);
return qp;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
index 6c66d2979795..16bd585365a8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
@@ -1623,7 +1623,7 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev)
cmd->checksum_disabled = 1;
cmd->max_reg_cmds = (1 << cmd->log_sz) - 1;
- cmd->bitmask = (1 << cmd->max_reg_cmds) - 1;
+ cmd->bitmask = (1UL << cmd->max_reg_cmds) - 1;
cmd->cmdif_rev = ioread32be(&dev->iseg->cmdif_rev_fw_sub) >> 16;
if (cmd->cmdif_rev > CMD_IF_REV) {
diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c
index 7ccdb46c6764..21e0af2620ee 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c
@@ -43,7 +43,7 @@
#define ILT_CFG_REG(cli, reg) PSWRQ2_REG_ ## cli ## _ ## reg ## _RT_OFFSET
/* ILT entry structure */
-#define ILT_ENTRY_PHY_ADDR_MASK 0x000FFFFFFFFFFFULL
+#define ILT_ENTRY_PHY_ADDR_MASK (~0ULL >> 12)
#define ILT_ENTRY_PHY_ADDR_SHIFT 0
#define ILT_ENTRY_VALID_MASK 0x1ULL
#define ILT_ENTRY_VALID_SHIFT 52
diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index cc106d892e29..b15e322b8bfe 100644
--- a/drivers/net/ethernet/sun/sunvnet.c
+++ b/drivers/net/ethernet/sun/sunvnet.c
@@ -1787,7 +1787,7 @@ static struct vnet *vnet_new(const u64 *local_mac,
dev->ethtool_ops = &vnet_ethtool_ops;
dev->watchdog_timeo = VNET_TX_TIMEOUT;
- dev->hw_features = NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GSO_SOFTWARE |
+ dev->hw_features = NETIF_F_TSO | NETIF_F_GSO | NETIF_F_ALL_TSO |
NETIF_F_HW_CSUM | NETIF_F_SG;
dev->features = dev->hw_features;
diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c
index 4e3d2e7c697c..e8c3a8c32534 100644
--- a/drivers/net/irda/w83977af_ir.c
+++ b/drivers/net/irda/w83977af_ir.c
@@ -518,7 +518,9 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb,
mtt = irda_get_mtt(skb);
pr_debug("%s(%ld), mtt=%d\n", __func__ , jiffies, mtt);
- if (mtt)
+ if (mtt > 1000)
+ mdelay(mtt/1000);
+ else if (mtt)
udelay(mtt);
/* Enable DMA interrupt */
diff --git a/drivers/net/phy/bcm-cygnus.c b/drivers/net/phy/bcm-cygnus.c
index 49bbc6826883..9a7dca2bb618 100644
--- a/drivers/net/phy/bcm-cygnus.c
+++ b/drivers/net/phy/bcm-cygnus.c
@@ -61,17 +61,17 @@ static int bcm_cygnus_afe_config(struct phy_device *phydev)
return rc;
/* make rcal=100, since rdb default is 000 */
- rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB1, 0x10);
+ rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB1, 0x10);
if (rc < 0)
return rc;
/* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */
- rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB0, 0x10);
+ rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x10);
if (rc < 0)
return rc;
/* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */
- rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB0, 0x00);
+ rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x00);
return 0;
}
diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h
index b2091c88b44d..ce16b26d49ff 100644
--- a/drivers/net/phy/bcm-phy-lib.h
+++ b/drivers/net/phy/bcm-phy-lib.h
@@ -14,11 +14,18 @@
#ifndef _LINUX_BCM_PHY_LIB_H
#define _LINUX_BCM_PHY_LIB_H
+#include <linux/brcmphy.h>
#include <linux/phy.h>
int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val);
int bcm_phy_read_exp(struct phy_device *phydev, u16 reg);
+static inline int bcm_phy_write_exp_sel(struct phy_device *phydev,
+ u16 reg, u16 val)
+{
+ return bcm_phy_write_exp(phydev, reg | MII_BCM54XX_EXP_SEL_ER, val);
+}
+
int bcm_phy_write_misc(struct phy_device *phydev,
u16 reg, u16 chl, u16 value);
int bcm_phy_read_misc(struct phy_device *phydev,
diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c
index 03d4809a9126..bffa70e46202 100644
--- a/drivers/net/phy/bcm7xxx.c
+++ b/drivers/net/phy/bcm7xxx.c
@@ -48,10 +48,10 @@
static void r_rc_cal_reset(struct phy_device *phydev)
{
/* Reset R_CAL/RC_CAL Engine */
- bcm_phy_write_exp(phydev, 0x00b0, 0x0010);
+ bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010);
/* Disable Reset R_AL/RC_CAL Engine */
- bcm_phy_write_exp(phydev, 0x00b0, 0x0000);
+ bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000);
}
static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index e83acc608678..dc934347ae28 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -1203,6 +1203,23 @@ static void dp83640_remove(struct phy_device *phydev)
kfree(dp83640);
}
+static int dp83640_soft_reset(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_soft_reset(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* From DP83640 datasheet: "Software driver code must wait 3 us
+ * following a software reset before allowing further serial MII
+ * operations with the DP83640."
+ */
+ udelay(10); /* Taking udelay inaccuracy into account */
+
+ return 0;
+}
+
static int dp83640_config_init(struct phy_device *phydev)
{
struct dp83640_private *dp83640 = phydev->priv;
@@ -1496,6 +1513,7 @@ static struct phy_driver dp83640_driver = {
.flags = PHY_HAS_INTERRUPT,
.probe = dp83640_probe,
.remove = dp83640_remove,
+ .soft_reset = dp83640_soft_reset,
.config_init = dp83640_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index e74709e4b5dd..49174837c2ba 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -983,7 +983,8 @@ static void team_port_disable(struct team *team,
static void ___team_compute_features(struct team *team)
{
struct team_port *port;
- u32 vlan_features = TEAM_VLAN_FEATURES & NETIF_F_ALL_FOR_ALL;
+ netdev_features_t vlan_features = TEAM_VLAN_FEATURES &
+ NETIF_F_ALL_FOR_ALL;
unsigned short max_hard_header_len = ETH_HLEN;
unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
IFF_XMIT_DST_RELEASE_PERM;
diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c
index 96a5028621c8..8edbccf06b7b 100644
--- a/drivers/net/usb/cdc_mbim.c
+++ b/drivers/net/usb/cdc_mbim.c
@@ -593,7 +593,7 @@ static const struct driver_info cdc_mbim_info_zlp = {
*/
static const struct driver_info cdc_mbim_info_ndp_to_end = {
.description = "CDC MBIM",
- .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN,
+ .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN | FLAG_SEND_ZLP,
.bind = cdc_mbim_bind,
.unbind = cdc_mbim_unbind,
.manage_power = cdc_mbim_manage_power,
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 8aaa09b3c753..d72205f06a1d 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -637,6 +637,9 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x0846, 0x68a2, 8)},
{QMI_FIXED_INTF(0x12d1, 0x140c, 1)}, /* Huawei E173 */
{QMI_FIXED_INTF(0x12d1, 0x14ac, 1)}, /* Huawei E1820 */
+ {QMI_FIXED_INTF(0x1435, 0xd181, 3)}, /* Wistron NeWeb D18Q1 */
+ {QMI_FIXED_INTF(0x1435, 0xd181, 4)}, /* Wistron NeWeb D18Q1 */
+ {QMI_FIXED_INTF(0x1435, 0xd181, 5)}, /* Wistron NeWeb D18Q1 */
{QMI_FIXED_INTF(0x16d8, 0x6003, 0)}, /* CMOTech 6003 */
{QMI_FIXED_INTF(0x16d8, 0x6007, 0)}, /* CMOTech CHE-628S */
{QMI_FIXED_INTF(0x16d8, 0x6008, 0)}, /* CMOTech CMU-301 */
@@ -713,6 +716,7 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */
{QMI_FIXED_INTF(0x2001, 0x7e19, 4)}, /* D-Link DWM-221 B1 */
{QMI_FIXED_INTF(0x2001, 0x7e35, 4)}, /* D-Link DWM-222 */
+ {QMI_FIXED_INTF(0x2020, 0x2033, 4)}, /* BroadMobi BM806U */
{QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)}, /* Sierra Wireless MC7700 */
{QMI_FIXED_INTF(0x114f, 0x68a2, 8)}, /* Sierra Wireless MC7750 */
{QMI_FIXED_INTF(0x1199, 0x68a2, 8)}, /* Sierra Wireless MC7710 in QMI mode */
@@ -762,6 +766,7 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x413c, 0x81a9, 8)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
{QMI_FIXED_INTF(0x413c, 0x81b1, 8)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */
{QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
+ {QMI_FIXED_INTF(0x03f0, 0x9d1d, 1)}, /* HP lt4120 Snapdragon X5 LTE */
{QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */
{QMI_FIXED_INTF(0x1e0e, 0x9001, 5)}, /* SIMCom 7230E */
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index b2c1a435357f..2991d7155540 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -1610,7 +1610,7 @@ static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
tx_data += len;
agg->skb_len += len;
- agg->skb_num++;
+ agg->skb_num += skb_shinfo(skb)->gso_segs ?: 1;
dev_kfree_skb_any(skb);
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c
index c5f375befd2f..7337e6c0e126 100644
--- a/drivers/net/usb/smsc75xx.c
+++ b/drivers/net/usb/smsc75xx.c
@@ -945,10 +945,11 @@ static int smsc75xx_set_features(struct net_device *netdev,
/* it's racing here! */
ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl);
- if (ret < 0)
+ if (ret < 0) {
netdev_warn(dev->net, "Error writing RFE_CTL\n");
-
- return ret;
+ return ret;
+ }
+ return 0;
}
static int smsc75xx_wait_ready(struct usbnet *dev, int in_pm)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index d01285250204..2759d386ade7 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -1912,8 +1912,8 @@ static int virtnet_probe(struct virtio_device *vdev)
/* Assume link up if device can't report link status,
otherwise get link status from config. */
+ netif_carrier_off(dev);
if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) {
- netif_carrier_off(dev);
schedule_work(&vi->config_work);
} else {
vi->status = VIRTIO_NET_S_LINK_UP;
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index f02c1b148545..187b60c8a672 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -7060,10 +7060,20 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw,
{
struct ath10k *ar = hw->priv;
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+ struct ath10k_vif *arvif = (void *)vif->drv_priv;
+ struct ath10k_peer *peer;
u32 bw, smps;
spin_lock_bh(&ar->data_lock);
+ peer = ath10k_peer_find(ar, arvif->vdev_id, sta->addr);
+ if (!peer) {
+ spin_unlock_bh(&ar->data_lock);
+ ath10k_warn(ar, "mac sta rc update failed to find peer %pM on vdev %i\n",
+ sta->addr, arvif->vdev_id);
+ return;
+ }
+
ath10k_dbg(ar, ATH10K_DBG_MAC,
"mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n",
sta->addr, changed, sta->bandwidth, sta->rx_nss,
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
index 83e5aa6a9f28..ad35e760ed3f 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
@@ -6167,7 +6167,7 @@ static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
req->alpha2[0], req->alpha2[1]);
/* ignore non-ISO3166 country codes */
- for (i = 0; i < sizeof(req->alpha2); i++)
+ for (i = 0; i < 2; i++)
if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
brcmf_err("not a ISO3166 code\n");
return;
diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c
index 03219cf1693a..545a1515d7fe 100644
--- a/drivers/net/wireless/cnss/cnss_pci.c
+++ b/drivers/net/wireless/cnss/cnss_pci.c
@@ -2717,10 +2717,14 @@ err_pcie_link_up:
cnss_configure_wlan_en_gpio(WLAN_EN_LOW);
cnss_wlan_vreg_set(vreg_info, VREG_OFF);
if (penv->pdev) {
- pr_err("%d: Unregistering pci device\n", __LINE__);
- pci_unregister_driver(&cnss_wlan_pci_driver);
- penv->pdev = NULL;
- penv->pci_register_again = true;
+ if (wdrv && wdrv->update_status)
+ wdrv->update_status(penv->pdev, CNSS_SSR_FAIL);
+ if (!penv->recovery_in_progress) {
+ pr_err("%d: Unregistering pci device\n", __LINE__);
+ pci_unregister_driver(&cnss_wlan_pci_driver);
+ penv->pdev = NULL;
+ penv->pci_register_again = true;
+ }
}
err_wlan_vreg_on:
diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c
index fc35b0892768..5183f3de7c9b 100644
--- a/drivers/net/wireless/cnss2/main.c
+++ b/drivers/net/wireless/cnss2/main.c
@@ -1890,7 +1890,7 @@ static int cnss_init_dump_entry(struct cnss_plat_data *plat_priv)
return msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
}
-static int cnss_qca6174_register_ramdump(struct cnss_plat_data *plat_priv)
+static int cnss_register_ramdump_v1(struct cnss_plat_data *plat_priv)
{
int ret = 0;
struct device *dev;
@@ -1941,7 +1941,7 @@ out:
return ret;
}
-static void cnss_qca6174_unregister_ramdump(struct cnss_plat_data *plat_priv)
+static void cnss_unregister_ramdump_v1(struct cnss_plat_data *plat_priv)
{
struct device *dev;
struct cnss_ramdump_info *ramdump_info;
@@ -1958,7 +1958,7 @@ static void cnss_qca6174_unregister_ramdump(struct cnss_plat_data *plat_priv)
ramdump_info->ramdump_pa);
}
-static int cnss_qca6290_register_ramdump(struct cnss_plat_data *plat_priv)
+static int cnss_register_ramdump_v2(struct cnss_plat_data *plat_priv)
{
int ret = 0;
struct cnss_subsys_info *subsys_info;
@@ -2014,7 +2014,7 @@ free_ramdump:
return ret;
}
-static void cnss_qca6290_unregister_ramdump(struct cnss_plat_data *plat_priv)
+static void cnss_unregister_ramdump_v2(struct cnss_plat_data *plat_priv)
{
struct cnss_ramdump_info_v2 *info_v2;
@@ -2034,11 +2034,11 @@ int cnss_register_ramdump(struct cnss_plat_data *plat_priv)
switch (plat_priv->device_id) {
case QCA6174_DEVICE_ID:
- ret = cnss_qca6174_register_ramdump(plat_priv);
+ ret = cnss_register_ramdump_v1(plat_priv);
break;
case QCA6290_EMULATION_DEVICE_ID:
case QCA6290_DEVICE_ID:
- ret = cnss_qca6290_register_ramdump(plat_priv);
+ ret = cnss_register_ramdump_v2(plat_priv);
break;
default:
cnss_pr_err("Unknown device ID: 0x%lx\n", plat_priv->device_id);
@@ -2052,11 +2052,11 @@ void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv)
{
switch (plat_priv->device_id) {
case QCA6174_DEVICE_ID:
- cnss_qca6174_unregister_ramdump(plat_priv);
+ cnss_unregister_ramdump_v1(plat_priv);
break;
case QCA6290_EMULATION_DEVICE_ID:
case QCA6290_DEVICE_ID:
- cnss_qca6290_unregister_ramdump(plat_priv);
+ cnss_unregister_ramdump_v2(plat_priv);
break;
default:
cnss_pr_err("Unknown device ID: 0x%lx\n", plat_priv->device_id);
diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c
index 5746366ff852..2356caa3af78 100644
--- a/drivers/net/wireless/cnss2/pci.c
+++ b/drivers/net/wireless/cnss2/pci.c
@@ -133,7 +133,8 @@ int cnss_suspend_pci_link(struct cnss_pci_data *pci_priv)
pci_disable_device(pci_priv->pci_dev);
if (pci_priv->pci_dev->device != QCA6174_DEVICE_ID) {
- if (pci_set_power_state(pci_priv->pci_dev, PCI_D3hot))
+ ret = pci_set_power_state(pci_priv->pci_dev, PCI_D3hot);
+ if (ret)
cnss_pr_err("Failed to set D3Hot, err = %d\n", ret);
}
@@ -404,10 +405,12 @@ static int cnss_pci_suspend(struct device *dev)
SAVE_PCI_CONFIG_SPACE);
pci_disable_device(pci_dev);
- ret = pci_set_power_state(pci_dev, PCI_D3hot);
- if (ret)
- cnss_pr_err("Failed to set D3Hot, err = %d\n",
- ret);
+ if (pci_dev->device != QCA6174_DEVICE_ID) {
+ ret = pci_set_power_state(pci_dev, PCI_D3hot);
+ if (ret)
+ cnss_pr_err("Failed to set D3Hot, err = %d\n",
+ ret);
+ }
}
cnss_pci_set_monitor_wake_intr(pci_priv, false);
@@ -643,9 +646,12 @@ int cnss_auto_suspend(struct device *dev)
cnss_set_pci_config_space(pci_priv, SAVE_PCI_CONFIG_SPACE);
pci_disable_device(pci_dev);
- ret = pci_set_power_state(pci_dev, PCI_D3hot);
- if (ret)
- cnss_pr_err("Failed to set D3Hot, err = %d\n", ret);
+ if (pci_dev->device != QCA6174_DEVICE_ID) {
+ ret = pci_set_power_state(pci_dev, PCI_D3hot);
+ if (ret)
+ cnss_pr_err("Failed to set D3Hot, err = %d\n",
+ ret);
+ }
cnss_pr_dbg("Suspending PCI link\n");
if (cnss_set_pci_link(pci_priv, PCI_LINK_DOWN)) {
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 8a9164da6c50..e8b770a95f7a 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -2925,8 +2925,10 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) {
u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]);
- if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom))
+ if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom)) {
+ kfree(hwname);
return -EINVAL;
+ }
param.regd = hwsim_world_regdom_custom[idx];
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c
index 5624ade92cc0..c2a156a8acec 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c
@@ -304,9 +304,6 @@ static void _rtl92c_get_txpower_writeval_by_regulatory(struct ieee80211_hw *hw,
writeVal = 0x00000000;
if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_BT1)
writeVal = writeVal - 0x06060606;
- else if (rtlpriv->dm.dynamic_txhighpower_lvl ==
- TXHIGHPWRLEVEL_BT2)
- writeVal = writeVal;
*(p_outwriteval + rf) = writeVal;
}
}
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index fee4c01fbdfd..a0de2453fa09 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -342,6 +342,9 @@ static int xennet_open(struct net_device *dev)
unsigned int i = 0;
struct netfront_queue *queue = NULL;
+ if (!np->queues)
+ return -ENODEV;
+
for (i = 0; i < num_queues; ++i) {
queue = &np->queues[i];
napi_enable(&queue->napi);
@@ -1363,18 +1366,8 @@ static int netfront_probe(struct xenbus_device *dev,
#ifdef CONFIG_SYSFS
info->netdev->sysfs_groups[0] = &xennet_dev_group;
#endif
- err = register_netdev(info->netdev);
- if (err) {
- pr_warn("%s: register_netdev err=%d\n", __func__, err);
- goto fail;
- }
return 0;
-
- fail:
- xennet_free_netdev(netdev);
- dev_set_drvdata(&dev->dev, NULL);
- return err;
}
static void xennet_end_access(int ref, void *page)
@@ -1743,8 +1736,6 @@ static void xennet_destroy_queues(struct netfront_info *info)
{
unsigned int i;
- rtnl_lock();
-
for (i = 0; i < info->netdev->real_num_tx_queues; i++) {
struct netfront_queue *queue = &info->queues[i];
@@ -1753,8 +1744,6 @@ static void xennet_destroy_queues(struct netfront_info *info)
netif_napi_del(&queue->napi);
}
- rtnl_unlock();
-
kfree(info->queues);
info->queues = NULL;
}
@@ -1770,8 +1759,6 @@ static int xennet_create_queues(struct netfront_info *info,
if (!info->queues)
return -ENOMEM;
- rtnl_lock();
-
for (i = 0; i < *num_queues; i++) {
struct netfront_queue *queue = &info->queues[i];
@@ -1780,7 +1767,7 @@ static int xennet_create_queues(struct netfront_info *info,
ret = xennet_init_queue(queue);
if (ret < 0) {
- dev_warn(&info->netdev->dev,
+ dev_warn(&info->xbdev->dev,
"only created %d queues\n", i);
*num_queues = i;
break;
@@ -1794,10 +1781,8 @@ static int xennet_create_queues(struct netfront_info *info,
netif_set_real_num_tx_queues(info->netdev, *num_queues);
- rtnl_unlock();
-
if (*num_queues == 0) {
- dev_err(&info->netdev->dev, "no queues\n");
+ dev_err(&info->xbdev->dev, "no queues\n");
return -EINVAL;
}
return 0;
@@ -1839,6 +1824,7 @@ static int talk_to_netback(struct xenbus_device *dev,
goto out;
}
+ rtnl_lock();
if (info->queues)
xennet_destroy_queues(info);
@@ -1849,6 +1835,7 @@ static int talk_to_netback(struct xenbus_device *dev,
info->queues = NULL;
goto out;
}
+ rtnl_unlock();
/* Create shared ring, alloc event channel -- for each queue */
for (i = 0; i < num_queues; ++i) {
@@ -1945,8 +1932,10 @@ abort_transaction_no_dev_fatal:
xenbus_transaction_end(xbt, 1);
destroy_ring:
xennet_disconnect_backend(info);
+ rtnl_lock();
xennet_destroy_queues(info);
out:
+ rtnl_unlock();
device_unregister(&dev->dev);
return err;
}
@@ -1982,6 +1971,15 @@ static int xennet_connect(struct net_device *dev)
netdev_update_features(dev);
rtnl_unlock();
+ if (dev->reg_state == NETREG_UNINITIALIZED) {
+ err = register_netdev(dev);
+ if (err) {
+ pr_warn("%s: register_netdev err=%d\n", __func__, err);
+ device_unregister(&np->xbdev->dev);
+ return err;
+ }
+ }
+
/*
* All public and private state should now be sane. Get
* ready to start sending and receiving packets and give the driver
@@ -2172,10 +2170,14 @@ static int xennet_remove(struct xenbus_device *dev)
xennet_disconnect_backend(info);
- unregister_netdev(info->netdev);
+ if (info->netdev->reg_state == NETREG_REGISTERED)
+ unregister_netdev(info->netdev);
- if (info->queues)
+ if (info->queues) {
+ rtnl_lock();
xennet_destroy_queues(info);
+ rtnl_unlock();
+ }
xennet_free_netdev(info->netdev);
return 0;
diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index 3bbdf60f8908..49f3fba75f4d 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -955,6 +955,9 @@ static int ntb_transport_init_queue(struct ntb_transport_ctx *nt,
mw_base = nt->mw_vec[mw_num].phys_addr;
mw_size = nt->mw_vec[mw_num].phys_size;
+ if (max_mw_size && mw_size > max_mw_size)
+ mw_size = max_mw_size;
+
tx_size = (unsigned int)mw_size / num_qps_mw;
qp_offset = tx_size * (qp_num / mw_count);
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index 1c8aedf21370..e86fcc9e9852 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -1583,7 +1583,7 @@ static int nvme_create_queue(struct nvme_queue *nvmeq, int qid)
nvmeq->cq_vector = qid - 1;
result = adapter_alloc_cq(dev, qid, nvmeq);
if (result < 0)
- return result;
+ goto release_vector;
result = adapter_alloc_sq(dev, qid, nvmeq);
if (result < 0)
@@ -1597,9 +1597,12 @@ static int nvme_create_queue(struct nvme_queue *nvmeq, int qid)
return result;
release_sq:
+ dev->online_queues--;
adapter_delete_sq(dev, qid);
release_cq:
adapter_delete_cq(dev, qid);
+ release_vector:
+ nvmeq->cq_vector = -1;
return result;
}
diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c
index 312cb5b74dec..1d288fa4f4d6 100644
--- a/drivers/parisc/lba_pci.c
+++ b/drivers/parisc/lba_pci.c
@@ -1365,9 +1365,27 @@ lba_hw_init(struct lba_device *d)
WRITE_REG32(stat, d->hba.base_addr + LBA_ERROR_CONFIG);
}
- /* Set HF mode as the default (vs. -1 mode). */
+
+ /*
+ * Hard Fail vs. Soft Fail on PCI "Master Abort".
+ *
+ * "Master Abort" means the MMIO transaction timed out - usually due to
+ * the device not responding to an MMIO read. We would like HF to be
+ * enabled to find driver problems, though it means the system will
+ * crash with a HPMC.
+ *
+ * In SoftFail mode "~0L" is returned as a result of a timeout on the
+ * pci bus. This is like how PCI busses on x86 and most other
+ * architectures behave. In order to increase compatibility with
+ * existing (x86) PCI hardware and existing Linux drivers we enable
+ * Soft Faul mode on PA-RISC now too.
+ */
stat = READ_REG32(d->hba.base_addr + LBA_STAT_CTL);
+#if defined(ENABLE_HARDFAIL)
WRITE_REG32(stat | HF_ENABLE, d->hba.base_addr + LBA_STAT_CTL);
+#else
+ WRITE_REG32(stat & ~HF_ENABLE, d->hba.base_addr + LBA_STAT_CTL);
+#endif
/*
** Writing a zero to STAT_CTL.rf (bit 0) will clear reset signal
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 32bd8ab79d53..dd9ebdc968c8 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -1140,11 +1140,14 @@ static int pci_pm_runtime_suspend(struct device *dev)
int error;
/*
- * If pci_dev->driver is not set (unbound), the device should
- * always remain in D0 regardless of the runtime PM status
+ * If pci_dev->driver is not set (unbound), we leave the device in D0,
+ * but it may go to D3cold when the bridge above it runtime suspends.
+ * Save its config space in case that happens.
*/
- if (!pci_dev->driver)
+ if (!pci_dev->driver) {
+ pci_save_state(pci_dev);
return 0;
+ }
if (!pm || !pm->runtime_suspend)
return -ENOSYS;
@@ -1195,16 +1198,18 @@ static int pci_pm_runtime_resume(struct device *dev)
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
/*
- * If pci_dev->driver is not set (unbound), the device should
- * always remain in D0 regardless of the runtime PM status
+ * Restoring config space is necessary even if the device is not bound
+ * to a driver because although we left it in D0, it may have gone to
+ * D3cold when the bridge above it runtime suspended.
*/
+ pci_restore_standard_config(pci_dev);
+
if (!pci_dev->driver)
return 0;
if (!pm || !pm->runtime_resume)
return -ENOSYS;
- pci_restore_standard_config(pci_dev);
pci_fixup_device(pci_fixup_resume_early, pci_dev);
__pci_enable_wake(pci_dev, PCI_D0, true, false);
pci_fixup_device(pci_fixup_resume, pci_dev);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 4eb1cf0ed00c..5697b32819cb 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -3614,6 +3614,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9120,
quirk_dma_func1_alias);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9123,
quirk_dma_func1_alias);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9128,
+ quirk_dma_func1_alias);
/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c14 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9130,
quirk_dma_func1_alias);
@@ -3626,6 +3628,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x917a,
/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c46 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0,
quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c127 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9220,
+ quirk_dma_func1_alias);
/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c49 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9230,
quirk_dma_func1_alias);
diff --git a/drivers/platform/goldfish/goldfish_pipe_v2.c b/drivers/platform/goldfish/goldfish_pipe_v2.c
index 90bac4b055a3..f0b9b46047be 100644
--- a/drivers/platform/goldfish/goldfish_pipe_v2.c
+++ b/drivers/platform/goldfish/goldfish_pipe_v2.c
@@ -951,7 +951,8 @@ static int goldfish_pipe_dma_alloc_locked(struct goldfish_pipe *pipe)
dma->dma_size,
&dma->phys_begin,
GFP_KERNEL);
- return -ENOMEM;
+ if (!dma->dma_vaddr)
+ return -ENOMEM;
dma->phys_end = dma->phys_begin + dma->dma_size;
pipe->dev->dma_alloc_total += dma->dma_size;
@@ -996,7 +997,6 @@ static int goldfish_dma_mmap_locked(
dma->phys_begin >> PAGE_SHIFT,
sz_requested,
vma->vm_page_prot);
-
if (status < 0) {
dev_err(pdev_dev, "Cannot remap pfn range....\n");
return -EAGAIN;
@@ -1025,7 +1025,6 @@ static int goldfish_dma_mmap(struct file *filp, struct vm_area_struct *vma)
status = goldfish_dma_mmap_locked(pipe, vma);
mutex_unlock(&pipe->lock);
return status;
-
}
static int goldfish_pipe_dma_create_region(
@@ -1154,6 +1153,15 @@ static struct miscdevice goldfish_pipe_miscdev = {
.fops = &goldfish_pipe_fops,
};
+
+static void write_pa_addr(void *addr, void __iomem *portl, void __iomem *porth)
+{
+ const unsigned long paddr = __pa(addr);
+
+ writel(paddr >> 32, porth);
+ writel((u32)paddr, portl);
+}
+
static int goldfish_pipe_device_init_v2(struct platform_device *pdev)
{
struct goldfish_pipe_dev *dev = &goldfish_pipe_dev;
@@ -1197,14 +1205,14 @@ static int goldfish_pipe_device_init_v2(struct platform_device *pdev)
dev->buffers = (struct goldfish_pipe_dev_buffers *)page;
/* Send the buffer addresses to the host */
- gf_write_ptr(&dev->buffers->signalled_pipe_buffers,
+ write_pa_addr(&dev->buffers->signalled_pipe_buffers,
dev->base + PIPE_REG_SIGNAL_BUFFER,
dev->base + PIPE_REG_SIGNAL_BUFFER_HIGH);
writel((u32)MAX_SIGNALLED_PIPES,
dev->base + PIPE_REG_SIGNAL_BUFFER_COUNT);
- gf_write_ptr(&dev->buffers->open_command_params,
+ write_pa_addr(&dev->buffers->open_command_params,
dev->base + PIPE_REG_OPEN_BUFFER,
dev->base + PIPE_REG_OPEN_BUFFER_HIGH);
diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig
index 333d28e64087..92773ff33188 100644
--- a/drivers/platform/msm/Kconfig
+++ b/drivers/platform/msm/Kconfig
@@ -133,16 +133,6 @@ config IPA_UT
The user interface to run and control the tests is debugfs file
system.
-config SSM
- tristate "QTI Secure Service Module"
- depends on QSEECOM
- depends on MSM_SMD
- help
- Provides an interface for OEM driver to communicate with Trustzone
- and modem for key exchange and mode change.
- This driver uses Secure Channel Manager interface for trustzone
- communication and communicates with modem over SMD channel.
-
config GPIO_USB_DETECT
tristate "GPIO-based USB VBUS Detection"
depends on POWER_SUPPLY
diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile
index d985aa81a3bb..f2a0d7e1ba1c 100644
--- a/drivers/platform/msm/Makefile
+++ b/drivers/platform/msm/Makefile
@@ -13,7 +13,6 @@ obj-$(CONFIG_EP_PCIE) += ep_pcie/
obj-$(CONFIG_GPIO_USB_DETECT) += gpio-usbdetect.o
obj-$(CONFIG_MSM_11AD) += msm_11ad/
obj-$(CONFIG_SEEMP_CORE) += seemp_core/
-obj-$(CONFIG_SSM) += ssm.o
obj-$(CONFIG_USB_BAM) += usb_bam.o
obj-$(CONFIG_MSM_MHI_DEV) += mhi_dev/
obj-$(CONFIG_MSM_EXT_DISPLAY) += msm_ext_display.o
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
index 826d449edbd2..e470183fc3b5 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-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -2115,8 +2115,10 @@ static void ipa_replenish_rx_cache(struct ipa_sys_context *sys)
goto fail_dma_mapping;
}
+ spin_lock_bh(&sys->spinlock);
list_add_tail(&rx_pkt->link, &sys->head_desc_list);
rx_len_cached = ++sys->len;
+ spin_unlock_bh(&sys->spinlock);
ret = sps_transfer_one(sys->ep->ep_hdl,
rx_pkt->data.dma_addr, sys->rx_buff_sz, rx_pkt, 0);
@@ -2130,8 +2132,10 @@ static void ipa_replenish_rx_cache(struct ipa_sys_context *sys)
return;
fail_sps_transfer:
+ spin_lock_bh(&sys->spinlock);
list_del(&rx_pkt->link);
rx_len_cached = --sys->len;
+ spin_unlock_bh(&sys->spinlock);
dma_unmap_single(ipa_ctx->pdev, rx_pkt->data.dma_addr,
sys->rx_buff_sz, DMA_FROM_DEVICE);
fail_dma_mapping:
@@ -2171,8 +2175,10 @@ static void ipa_replenish_rx_cache_recycle(struct ipa_sys_context *sys)
goto fail_dma_mapping;
}
+ spin_lock_bh(&sys->spinlock);
list_add_tail(&rx_pkt->link, &sys->head_desc_list);
rx_len_cached = ++sys->len;
+ spin_unlock_bh(&sys->spinlock);
ret = sps_transfer_one(sys->ep->ep_hdl,
rx_pkt->data.dma_addr, sys->rx_buff_sz, rx_pkt, 0);
@@ -2185,9 +2191,11 @@ static void ipa_replenish_rx_cache_recycle(struct ipa_sys_context *sys)
return;
fail_sps_transfer:
+ spin_lock_bh(&sys->spinlock);
rx_len_cached = --sys->len;
list_del(&rx_pkt->link);
INIT_LIST_HEAD(&rx_pkt->link);
+ spin_unlock_bh(&sys->spinlock);
dma_unmap_single(ipa_ctx->pdev, rx_pkt->data.dma_addr,
sys->rx_buff_sz, DMA_FROM_DEVICE);
fail_dma_mapping:
@@ -2219,7 +2227,9 @@ static void ipa_fast_replenish_rx_cache(struct ipa_sys_context *sys)
}
rx_pkt = sys->repl.cache[curr];
+ spin_lock_bh(&sys->spinlock);
list_add_tail(&rx_pkt->link, &sys->head_desc_list);
+ spin_unlock_bh(&sys->spinlock);
ret = sps_transfer_one(sys->ep->ep_hdl,
rx_pkt->data.dma_addr, sys->rx_buff_sz, rx_pkt, 0);
@@ -2278,6 +2288,7 @@ static void ipa_cleanup_rx(struct ipa_sys_context *sys)
u32 head;
u32 tail;
+ spin_lock_bh(&sys->spinlock);
list_for_each_entry_safe(rx_pkt, r,
&sys->head_desc_list, link) {
list_del(&rx_pkt->link);
@@ -2295,6 +2306,7 @@ static void ipa_cleanup_rx(struct ipa_sys_context *sys)
sys->free_skb(rx_pkt->data.skb);
kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt);
}
+ spin_unlock_bh(&sys->spinlock);
if (sys->repl.cache) {
head = atomic_read(&sys->repl.head_idx);
@@ -2970,8 +2982,10 @@ static void ipa_wq_rx_common(struct ipa_sys_context *sys, u32 size)
struct ipa_rx_pkt_wrapper *rx_pkt_expected;
struct sk_buff *rx_skb;
+ spin_lock_bh(&sys->spinlock);
if (unlikely(list_empty(&sys->head_desc_list))) {
WARN_ON(1);
+ spin_unlock_bh(&sys->spinlock);
return;
}
rx_pkt_expected = list_first_entry(&sys->head_desc_list,
@@ -2979,6 +2993,7 @@ static void ipa_wq_rx_common(struct ipa_sys_context *sys, u32 size)
link);
list_del(&rx_pkt_expected->link);
sys->len--;
+ spin_unlock_bh(&sys->spinlock);
if (size)
rx_pkt_expected->len = size;
rx_skb = rx_pkt_expected->data.skb;
@@ -2999,8 +3014,10 @@ static void ipa_wlan_wq_rx_common(struct ipa_sys_context *sys, u32 size)
struct ipa_rx_pkt_wrapper *rx_pkt_expected;
struct sk_buff *rx_skb;
+ spin_lock_bh(&sys->spinlock);
if (unlikely(list_empty(&sys->head_desc_list))) {
WARN_ON(1);
+ spin_unlock_bh(&sys->spinlock);
return;
}
rx_pkt_expected = list_first_entry(&sys->head_desc_list,
@@ -3008,6 +3025,7 @@ static void ipa_wlan_wq_rx_common(struct ipa_sys_context *sys, u32 size)
link);
list_del(&rx_pkt_expected->link);
sys->len--;
+ spin_unlock_bh(&sys->spinlock);
if (size)
rx_pkt_expected->len = size;
diff --git a/drivers/platform/msm/ssm.c b/drivers/platform/msm/ssm.c
deleted file mode 100644
index 6a2909b8c5f4..000000000000
--- a/drivers/platform/msm/ssm.c
+++ /dev/null
@@ -1,516 +0,0 @@
-/* Copyright (c) 2013-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.
- */
-/*
- * QTI Secure Service Module(SSM) driver
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/of.h>
-#include <linux/cdev.h>
-#include <linux/uaccess.h>
-#include <linux/mutex.h>
-#include <linux/ion.h>
-#include <linux/types.h>
-#include <linux/elf.h>
-#include <linux/platform_device.h>
-#include <linux/msm_ion.h>
-#include <linux/platform_data/qcom_ssm.h>
-#include <soc/qcom/scm.h>
-#include <soc/qcom/smd.h>
-
-#include "../../misc/qseecom_kernel.h"
-#include "ssm.h"
-
-/* Macros */
-#define SSM_DEV_NAME "ssm"
-#define MPSS_SUBSYS 0
-#define SSM_INFO_CMD_ID 1
-#define MAX_APP_NAME_SIZE 32
-#define SSM_MSG_LEN 200
-#define SSM_MSG_FIELD_LEN 11
-#define ATOM_MSG_LEN (SSM_MSG_FIELD_LEN + SSM_MSG_LEN + 40)
-
-#define TZAPP_NAME "SsmApp"
-#define CHANNEL_NAME "SSM_RTR_MODEM_APPS"
-
-/* SSM driver structure.*/
-struct ssm_driver {
- int32_t app_status;
- int32_t update_status;
- unsigned char *channel_name;
- unsigned char *smd_buffer;
- struct device *dev;
- smd_channel_t *ch;
- struct work_struct ipc_work;
- struct mutex mutex;
- struct qseecom_handle *qseecom_handle;
- struct tzapp_get_mode_info_rsp *resp;
- bool key_status;
- bool ready;
-};
-
-static struct ssm_driver *ssm_drv;
-
-static unsigned int getint(char *buff, unsigned long *res)
-{
- char value[SSM_MSG_FIELD_LEN];
-
- memcpy(value, buff, SSM_MSG_FIELD_LEN);
- value[SSM_MSG_FIELD_LEN - 1] = '\0';
-
- return kstrtoul(skip_spaces(value), 10, res);
-}
-
-/*
- * Setup CMD/RSP pointers.
- */
-static void setup_cmd_rsp_buffers(struct qseecom_handle *handle, void **cmd,
- int *cmd_len, void **resp, int *resp_len)
-{
- *cmd = handle->sbuf;
- if (*cmd_len & QSEECOM_ALIGN_MASK)
- *cmd_len = QSEECOM_ALIGN(*cmd_len);
-
- *resp = handle->sbuf + *cmd_len;
- if (*resp_len & QSEECOM_ALIGN_MASK)
- *resp_len = QSEECOM_ALIGN(*resp_len);
-}
-
-/*
- * Send packet to modem over SMD channel.
- */
-static int update_modem(enum ssm_ipc_req ipc_req, struct ssm_driver *ssm,
- int length, char *data)
-{
- unsigned int packet_len = length + SSM_MSG_FIELD_LEN;
- int rc = 0, count;
-
- snprintf(ssm->smd_buffer, SSM_MSG_FIELD_LEN + 1, "%10u|", ipc_req);
- memcpy(ssm->smd_buffer + SSM_MSG_FIELD_LEN, data, length);
-
- if (smd_write_avail(ssm->ch) < packet_len) {
- dev_err(ssm->dev, "Not enough space dropping request\n");
- rc = -ENOSPC;
- goto out;
- }
-
- count = smd_write(ssm->ch, ssm->smd_buffer, packet_len);
- if (count < packet_len) {
- dev_err(ssm->dev, "smd_write failed for %d\n", ipc_req);
- rc = -EIO;
- }
-
-out:
- return rc;
-}
-
-/*
- * Header Format
- * Each member of header is of 10 byte (ASCII).
- * Each entry is separated by '|' delimiter.
- * |<-10 bytes->|<-10 bytes->|
- * |-------------------------|
- * | IPC code | error code |
- * |-------------------------|
- *
- */
-static int decode_packet(char *buffer, struct ssm_common_msg *pkt)
-{
- int rc;
-
- rc = getint(buffer, (unsigned long *)&pkt->ipc_req);
- if (rc < 0)
- return -EINVAL;
-
- buffer += SSM_MSG_FIELD_LEN;
- rc = getint(buffer, (unsigned long *)&pkt->err_code);
- if (rc < 0)
- return -EINVAL;
-
- dev_dbg(ssm_drv->dev, "req %d error code %d\n",
- pkt->ipc_req, pkt->err_code);
- return 0;
-}
-
-static void process_message(struct ssm_common_msg pkt, struct ssm_driver *ssm)
-{
-
- switch (pkt.ipc_req) {
-
- case SSM_MTOA_MODE_UPDATE_STATUS:
- if (pkt.err_code) {
- dev_err(ssm->dev, "Modem mode update failed\n");
- ssm->update_status = FAILED;
- } else
- ssm->update_status = SUCCESS;
-
- dev_dbg(ssm->dev, "Modem mode update status %d\n",
- pkt.err_code);
- break;
-
- default:
- dev_dbg(ssm->dev, "Invalid message\n");
- break;
- };
-}
-
-/*
- * Work function to handle and process packets coming from modem.
- */
-static void ssm_app_modem_work_fn(struct work_struct *work)
-{
- int sz, rc;
- struct ssm_common_msg pkt;
- struct ssm_driver *ssm;
-
- ssm = container_of(work, struct ssm_driver, ipc_work);
-
- mutex_lock(&ssm->mutex);
- sz = smd_cur_packet_size(ssm->ch);
- if ((sz < SSM_MSG_FIELD_LEN) || (sz > ATOM_MSG_LEN)) {
- dev_dbg(ssm_drv->dev, "Garbled message size\n");
- goto unlock;
- }
-
- if (smd_read_avail(ssm->ch) < sz) {
- dev_err(ssm_drv->dev, "SMD error data in channel\n");
- goto unlock;
- }
-
- if (smd_read(ssm->ch, ssm->smd_buffer, sz) != sz) {
- dev_err(ssm_drv->dev, "Incomplete data\n");
- goto unlock;
- }
-
- rc = decode_packet(ssm->smd_buffer, &pkt);
- if (rc < 0) {
- dev_err(ssm_drv->dev, "Corrupted header\n");
- goto unlock;
- }
-
- process_message(pkt, ssm);
-
-unlock:
- mutex_unlock(&ssm->mutex);
-}
-
-/*
- * MODEM-APPS smd channel callback function.
- */
-static void modem_request(void *ctxt, unsigned event)
-{
- struct ssm_driver *ssm;
-
- ssm = (struct ssm_driver *)ctxt;
-
- switch (event) {
- case SMD_EVENT_OPEN:
- case SMD_EVENT_CLOSE:
- dev_dbg(ssm->dev, "SMD port status changed\n");
- break;
- case SMD_EVENT_DATA:
- if (smd_read_avail(ssm->ch) > 0)
- schedule_work(&ssm->ipc_work);
- break;
- };
-}
-
-/*
- * Load SSM application in TZ and start application:
- */
-static int ssm_load_app(struct ssm_driver *ssm)
-{
- int rc;
-
- /* Load the APP */
- rc = qseecom_start_app(&ssm->qseecom_handle, TZAPP_NAME, SZ_4K);
- if (rc < 0) {
- dev_err(ssm->dev, "Unable to load SSM app\n");
- ssm->app_status = FAILED;
- return -EIO;
- }
-
- ssm->app_status = SUCCESS;
- return 0;
-}
-
-static struct ssm_platform_data *populate_ssm_pdata(struct device *dev)
-{
- struct ssm_platform_data *pdata;
-
- pdata = devm_kzalloc(dev, sizeof(struct ssm_platform_data),
- GFP_KERNEL);
- if (!pdata)
- return NULL;
-
- pdata->need_key_exchg =
- of_property_read_bool(dev->of_node, "qcom,need-keyexhg");
-
- pdata->channel_name = CHANNEL_NAME;
-
- return pdata;
-}
-
-static int ssm_probe(struct platform_device *pdev)
-{
- int rc;
- struct ssm_platform_data *pdata;
- struct ssm_driver *drv;
-
- if (pdev->dev.of_node)
- pdata = populate_ssm_pdata(&pdev->dev);
- else
- pdata = pdev->dev.platform_data;
-
- if (!pdata) {
- dev_err(&pdev->dev, "Empty platform data\n");
- return -ENOMEM;
- }
-
- drv = devm_kzalloc(&pdev->dev, sizeof(struct ssm_driver),
- GFP_KERNEL);
- if (!drv)
- return -ENOMEM;
-
- /* Allocate response buffer */
- drv->resp = devm_kzalloc(&pdev->dev,
- sizeof(struct tzapp_get_mode_info_rsp),
- GFP_KERNEL);
- if (!drv->resp) {
- devm_kfree(&pdev->dev, drv);
- rc = -ENOMEM;
- goto exit;
- }
-
- /* Initialize the driver structure */
- drv->app_status = RETRY;
- drv->ready = false;
- drv->update_status = FAILED;
- mutex_init(&drv->mutex);
- drv->key_status = !pdata->need_key_exchg;
- drv->channel_name = (char *)pdata->channel_name;
- INIT_WORK(&drv->ipc_work, ssm_app_modem_work_fn);
-
- /* Allocate memory for smd buffer */
- drv->smd_buffer = devm_kzalloc(&pdev->dev,
- (sizeof(char) * ATOM_MSG_LEN), GFP_KERNEL);
- if (!drv->smd_buffer) {
- devm_kfree(&pdev->dev, drv->resp);
- devm_kfree(&pdev->dev, drv);
- rc = -ENOMEM;
- goto exit;
- }
-
- drv->dev = &pdev->dev;
- ssm_drv = drv;
- platform_set_drvdata(pdev, ssm_drv);
-
- dev_dbg(&pdev->dev, "probe success\n");
- return 0;
-
-exit:
- mutex_destroy(&drv->mutex);
- platform_set_drvdata(pdev, NULL);
- return rc;
-
-}
-
-static int ssm_remove(struct platform_device *pdev)
-{
-
- if (!ssm_drv)
- return 0;
- /*
- * Step to exit
- * 1. set ready to 0 (oem access closed).
- * 2. Close SMD modem connection closed.
- * 3. cleanup ion.
- */
- ssm_drv->ready = false;
- smd_close(ssm_drv->ch);
- flush_work(&ssm_drv->ipc_work);
-
- /* Shutdown tzapp */
- dev_dbg(&pdev->dev, "Shutting down TZapp\n");
- qseecom_shutdown_app(&ssm_drv->qseecom_handle);
-
- /* freeing the memory allocations
- for the driver and the buffer */
- devm_kfree(&pdev->dev, ssm_drv->smd_buffer);
- devm_kfree(&pdev->dev, ssm_drv->resp);
- devm_kfree(&pdev->dev, ssm_drv);
-
- return 0;
-}
-
-static struct of_device_id ssm_match_table[] = {
- {
- .compatible = "qcom,ssm",
- },
- {}
-};
-
-static struct platform_driver ssm_pdriver = {
- .probe = ssm_probe,
- .remove = ssm_remove,
- .driver = {
- .name = SSM_DEV_NAME,
- .owner = THIS_MODULE,
- .of_match_table = ssm_match_table,
- },
-};
-module_platform_driver(ssm_pdriver);
-
-/*
- * Interface for external OEM driver.
- * This interface supports following functionalities:
- * 1. Set mode (encrypted mode and it's length is passed as parameter).
- * 2. Set mode from TZ (read encrypted mode from TZ)
- * 3. Get status of mode update.
- *
- */
-int ssm_oem_driver_intf(int cmd, char *mode, int len)
-{
- int rc, req_len, resp_len;
- struct tzapp_get_mode_info_req *get_mode_req;
- struct tzapp_get_mode_info_rsp *get_mode_resp;
-
- /* If ssm_drv is NULL, probe failed */
- if (!ssm_drv)
- return -ENODEV;
-
- mutex_lock(&ssm_drv->mutex);
-
- if (ssm_drv->app_status == RETRY) {
- /* Load TZAPP */
- rc = ssm_load_app(ssm_drv);
- if (rc) {
- rc = -ENODEV;
- goto unlock;
- }
- } else if (ssm_drv->app_status == FAILED) {
- rc = -ENODEV;
- goto unlock;
- }
-
- /* Open modem SMD interface */
- if (!ssm_drv->ready) {
- rc = smd_named_open_on_edge(ssm_drv->channel_name,
- SMD_APPS_MODEM,
- &ssm_drv->ch,
- ssm_drv,
- modem_request);
- if (rc) {
- rc = -EAGAIN;
- goto unlock;
- } else
- ssm_drv->ready = true;
- }
-
- /* Try again modem key-exchange not yet done.*/
- if (!ssm_drv->key_status) {
- rc = -EAGAIN;
- goto unlock;
- }
-
- /* Set return status to success */
- rc = 0;
-
- switch (cmd) {
- case SSM_READY:
- break;
-
- case SSM_MODE_INFO_READY:
- ssm_drv->update_status = RETRY;
- /* Fill command structure */
- req_len = sizeof(struct tzapp_get_mode_info_req);
- resp_len = sizeof(struct tzapp_get_mode_info_rsp);
- setup_cmd_rsp_buffers(ssm_drv->qseecom_handle,
- (void **)&get_mode_req, &req_len,
- (void **)&get_mode_resp, &resp_len);
- get_mode_req->tzapp_ssm_cmd = GET_ENC_MODE;
-
- rc = qseecom_set_bandwidth(ssm_drv->qseecom_handle, 1);
- if (rc) {
- ssm_drv->update_status = FAILED;
- dev_err(ssm_drv->dev, "set bandwidth failed\n");
- rc = -EIO;
- break;
- }
- rc = qseecom_send_command(ssm_drv->qseecom_handle,
- (void *)get_mode_req, req_len,
- (void *)get_mode_resp, resp_len);
- if (rc || get_mode_resp->status) {
- ssm_drv->update_status = FAILED;
- break;
- }
- rc = qseecom_set_bandwidth(ssm_drv->qseecom_handle, 0);
- if (rc) {
- ssm_drv->update_status = FAILED;
- dev_err(ssm_drv->dev, "clear bandwidth failed\n");
- rc = -EIO;
- break;
- }
-
- if (get_mode_resp->enc_mode_len > ENC_MODE_MAX_SIZE) {
- ssm_drv->update_status = FAILED;
- rc = -EINVAL;
- break;
- }
- /* Send mode_info to modem */
- rc = update_modem(SSM_ATOM_MODE_UPDATE, ssm_drv,
- get_mode_resp->enc_mode_len,
- get_mode_resp->enc_mode_info);
- if (rc)
- ssm_drv->update_status = FAILED;
- break;
-
- case SSM_SET_MODE:
- ssm_drv->update_status = RETRY;
-
- if (len > ENC_MODE_MAX_SIZE) {
- ssm_drv->update_status = FAILED;
- rc = -EINVAL;
- break;
- }
- memcpy(ssm_drv->resp->enc_mode_info, mode, len);
- ssm_drv->resp->enc_mode_len = len;
-
- /* Send mode_info to modem */
- rc = update_modem(SSM_ATOM_MODE_UPDATE, ssm_drv,
- ssm_drv->resp->enc_mode_len,
- ssm_drv->resp->enc_mode_info);
- if (rc)
- ssm_drv->update_status = FAILED;
- break;
-
- case SSM_GET_MODE_STATUS:
- rc = ssm_drv->update_status;
- break;
-
- default:
- rc = -EINVAL;
- dev_err(ssm_drv->dev, "Invalid command\n");
- break;
- };
-
-unlock:
- mutex_unlock(&ssm_drv->mutex);
- return rc;
-}
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("QTI Secure Service Module");
-
diff --git a/drivers/platform/msm/ssm.h b/drivers/platform/msm/ssm.h
deleted file mode 100644
index ee4f1bc1d83f..000000000000
--- a/drivers/platform/msm/ssm.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/* Copyright (c) 2013-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.
- */
-
-#ifndef __SSM_H_
-#define __SSM_H_
-
-#define MAX_APP_NAME_SIZE 32
-#define ENC_MODE_MAX_SIZE 200
-
-/* tzapp response.*/
-enum tz_response {
- RESULT_SUCCESS = 0,
- RESULT_FAILURE = 0xFFFFFFFF,
-};
-
-/* tzapp command list.*/
-enum tz_commands {
- ENC_MODE,
- GET_ENC_MODE,
- KEY_EXCHANGE = 11,
-};
-
-/* MODEM/SSM command list.*/
-enum ssm_ipc_req {
- SSM_IPC_MIN = 0x0000AAAB,
- SSM_ATOM_MODE_UPDATE,
- SSM_MTOA_MODE_UPDATE_STATUS = SSM_IPC_MIN + 4,
- SSM_INVALID_REQ,
-};
-
-/* OEM request commands list.*/
-enum oem_req {
- SSM_READY,
- SSM_MODE_INFO_READY,
- SSM_SET_MODE,
- SSM_GET_MODE_STATUS,
- SSM_INVALID,
-};
-
-/* Modem mode update status.*/
-enum modem_mode_status {
- SUCCESS,
- RETRY,
- FAILED = -1,
-};
-
-/* tzapp encode mode request.*/
-__packed struct tzapp_mode_enc_req {
- uint32_t tzapp_ssm_cmd;
- uint8_t mode_info[4];
-};
-
-/* tzapp encode mode response.*/
-__packed struct tzapp_mode_enc_rsp {
- uint32_t tzapp_ssm_cmd;
- uint8_t enc_mode_info[ENC_MODE_MAX_SIZE];
- uint32_t enc_mode_len;
- uint32_t status;
-};
-
-/* tzapp get mode request.*/
-__packed struct tzapp_get_mode_info_req {
- uint32_t tzapp_ssm_cmd;
-};
-
-/* tzapp get mode response.*/
-__packed struct tzapp_get_mode_info_rsp {
- uint32_t tzapp_ssm_cmd;
- uint8_t enc_mode_info[ENC_MODE_MAX_SIZE];
- uint32_t enc_mode_len;
- uint32_t status;
-};
-
-/* Modem/SSM packet format.*/
-struct ssm_common_msg {
- enum ssm_ipc_req ipc_req;
- int err_code;
-
-};
-
-#endif
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 2e9ff2afcba2..1974d6ee032b 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -305,6 +305,7 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(pd_voltage_max),
POWER_SUPPLY_ATTR(pd_voltage_min),
POWER_SUPPLY_ATTR(sdp_current_max),
+ POWER_SUPPLY_ATTR(fcc_stepper_enable),
/* Local extensions of type int64_t */
POWER_SUPPLY_ATTR(charge_counter_ext),
/* Properties of type `const char *' */
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index e068bec8b85e..6d5308b3dd0b 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -40,12 +40,14 @@
#define ICL_CHANGE_VOTER "ICL_CHANGE_VOTER"
#define PL_INDIRECT_VOTER "PL_INDIRECT_VOTER"
#define USBIN_I_VOTER "USBIN_I_VOTER"
+#define FCC_STEPPER_VOTER "FCC_STEPPER_VOTER"
struct pl_data {
int pl_mode;
int slave_pct;
int taper_pct;
int slave_fcc_ua;
+ int main_fcc_ua;
int restricted_current;
bool restricted_charging_enabled;
struct votable *fcc_votable;
@@ -59,6 +61,7 @@ struct pl_data {
struct work_struct pl_disable_forever_work;
struct delayed_work pl_taper_work;
struct delayed_work pl_awake_work;
+ struct delayed_work fcc_step_update_work;
struct power_supply *main_psy;
struct power_supply *pl_psy;
struct power_supply *batt_psy;
@@ -66,6 +69,13 @@ struct pl_data {
int charge_type;
int total_settled_ua;
int pl_settled_ua;
+ int fcc_step_update;
+ int main_step_fcc_dir;
+ int main_step_fcc_count;
+ int main_step_fcc_residual;
+ int parallel_step_fcc_dir;
+ int parallel_step_fcc_count;
+ int parallel_step_fcc_residual;
struct class qcom_batt_class;
struct wakeup_source *pl_ws;
struct notifier_block nb;
@@ -380,6 +390,10 @@ done:
* FCC *
**********/
#define EFFICIENCY_PCT 80
+#define FCC_STEP_SIZE_UA 100000
+#define FCC_STEP_UPDATE_DELAY_MS 1000
+#define STEP_UP 1
+#define STEP_DOWN -1
static void get_fcc_split(struct pl_data *chip, int total_ua,
int *master_ua, int *slave_ua)
{
@@ -432,6 +446,43 @@ static void get_fcc_split(struct pl_data *chip, int total_ua,
*slave_ua = (*slave_ua * chip->taper_pct) / 100;
}
+static void get_fcc_step_update_params(struct pl_data *chip, int main_fcc_ua,
+ int parallel_fcc_ua)
+{
+ union power_supply_propval pval = {0, };
+ int rc;
+
+ /* Read current FCC of main charger */
+ rc = power_supply_get_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't get main charger current fcc, rc=%d\n", rc);
+ return;
+ }
+ chip->main_fcc_ua = pval.intval;
+
+ chip->main_step_fcc_dir = (main_fcc_ua > pval.intval) ?
+ STEP_UP : STEP_DOWN;
+ chip->main_step_fcc_count = abs((main_fcc_ua - pval.intval) /
+ FCC_STEP_SIZE_UA);
+ chip->main_step_fcc_residual = (main_fcc_ua - pval.intval) %
+ FCC_STEP_SIZE_UA;
+
+ chip->parallel_step_fcc_dir = (parallel_fcc_ua > chip->slave_fcc_ua) ?
+ STEP_UP : STEP_DOWN;
+ chip->parallel_step_fcc_count = abs((parallel_fcc_ua -
+ chip->slave_fcc_ua) / FCC_STEP_SIZE_UA);
+ chip->parallel_step_fcc_residual = (parallel_fcc_ua -
+ chip->slave_fcc_ua) % FCC_STEP_SIZE_UA;
+
+ pr_debug("Main FCC Stepper parameters: main_step_direction: %d, main_step_count: %d, main_residual_fcc: %d\n",
+ chip->main_step_fcc_dir, chip->main_step_fcc_count,
+ chip->main_step_fcc_residual);
+ pr_debug("Parallel FCC Stepper parameters: parallel_step_direction: %d, parallel_step_count: %d, parallel_residual_fcc: %d\n",
+ chip->parallel_step_fcc_dir, chip->parallel_step_fcc_count,
+ chip->parallel_step_fcc_residual);
+}
+
static int pl_fcc_vote_callback(struct votable *votable, void *data,
int total_fcc_ua, const char *client)
{
@@ -445,80 +496,305 @@ static int pl_fcc_vote_callback(struct votable *votable, void *data,
if (!chip->main_psy)
return 0;
+ if (!chip->batt_psy) {
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (!chip->batt_psy)
+ return 0;
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't read FCC step update status, rc=%d\n",
+ rc);
+ return rc;
+ }
+ chip->fcc_step_update = pval.intval;
+ pr_debug("FCC Stepper %s\n",
+ pval.intval ? "enabled" : "disabled");
+ }
+
+ if (chip->fcc_step_update)
+ cancel_delayed_work_sync(&chip->fcc_step_update_work);
+
+
if (chip->pl_mode == POWER_SUPPLY_PL_NONE
|| get_effective_result_locked(chip->pl_disable_votable)) {
+ if (chip->fcc_step_update) {
+ vote(chip->pl_awake_votable, FCC_STEPPER_VOTER,
+ true, 0);
+ get_fcc_step_update_params(chip, total_fcc_ua, 0);
+ schedule_delayed_work(&chip->fcc_step_update_work, 0);
+
+ return 0;
+ }
pval.intval = total_fcc_ua;
rc = power_supply_set_property(chip->main_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
&pval);
if (rc < 0)
pr_err("Couldn't set main fcc, rc=%d\n", rc);
+
return rc;
}
if (chip->pl_mode != POWER_SUPPLY_PL_NONE) {
get_fcc_split(chip, total_fcc_ua,
&master_fcc_ua, &slave_fcc_ua);
+ if (chip->fcc_step_update) {
+ vote(chip->pl_awake_votable, FCC_STEPPER_VOTER,
+ true, 0);
+ get_fcc_step_update_params(chip, master_fcc_ua,
+ slave_fcc_ua);
+ schedule_delayed_work(&chip->fcc_step_update_work, 0);
+ } else {
+ /*
+ * If there is an increase in slave share
+ * (Also handles parallel enable case)
+ * Set Main ICL then slave FCC
+ * else
+ * (Also handles parallel disable case)
+ * Set slave ICL then main FCC.
+ */
+ if (slave_fcc_ua > chip->slave_fcc_ua) {
+ pval.intval = master_fcc_ua;
+ rc = power_supply_set_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ &pval);
+ if (rc < 0) {
+ pr_err("Could not set main fcc, rc=%d\n",
+ rc);
+ return rc;
+ }
- /*
- * If there is an increase in slave share
- * (Also handles parallel enable case)
- * Set Main ICL then slave FCC
- * else
- * (Also handles parallel disable case)
- * Set slave ICL then main FCC.
- */
- if (slave_fcc_ua > chip->slave_fcc_ua) {
- pval.intval = master_fcc_ua;
- rc = power_supply_set_property(chip->main_psy,
+ pval.intval = slave_fcc_ua;
+ rc = power_supply_set_property(chip->pl_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
&pval);
- if (rc < 0) {
- pr_err("Could not set main fcc, rc=%d\n", rc);
- return rc;
+ if (rc < 0) {
+ pr_err("Couldn't set parallel fcc, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ chip->slave_fcc_ua = slave_fcc_ua;
+ } else {
+ pval.intval = slave_fcc_ua;
+ rc = power_supply_set_property(chip->pl_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ &pval);
+ if (rc < 0) {
+ pr_err("Couldn't set parallel fcc, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ chip->slave_fcc_ua = slave_fcc_ua;
+
+ pval.intval = master_fcc_ua;
+ rc = power_supply_set_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ &pval);
+ if (rc < 0) {
+ pr_err("Could not set main fcc, rc=%d\n",
+ rc);
+ return rc;
+ }
}
+ }
+ }
+
+ pl_dbg(chip, PR_PARALLEL, "master_fcc=%d slave_fcc=%d distribution=(%d/%d)\n",
+ master_fcc_ua, slave_fcc_ua,
+ (master_fcc_ua * 100) / total_fcc_ua,
+ (slave_fcc_ua * 100) / total_fcc_ua);
+
+ return 0;
+}
+
+static void fcc_step_update_work(struct work_struct *work)
+{
+ struct pl_data *chip = container_of(work,
+ struct pl_data, fcc_step_update_work.work);
+ union power_supply_propval pval = {0, };
+ int reschedule_ms = 0, rc = 0;
+ int main_fcc = chip->main_fcc_ua;
+ int parallel_fcc = chip->slave_fcc_ua;
+
+ if (!chip->usb_psy) {
+ chip->usb_psy = power_supply_get_by_name("usb");
+ if (!chip->usb_psy) {
+ pr_err("Couldn't get usb psy\n");
+ return;
+ }
+ }
+
+ /* Check whether USB is present or not */
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't get USB Present status, rc=%d\n", rc);
+ return;
+ }
+
+ /*
+ * If USB is not present, then disable parallel and
+ * Main FCC to the effective value of FCC votable and exit.
+ */
+ if (!pval.intval) {
+ /* Disable parallel */
+ parallel_fcc = 0;
+ pval.intval = 1;
+ rc = power_supply_set_property(chip->pl_psy,
+ POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval);
+ if (rc < 0)
+ pr_err("Couldn't change slave suspend state rc=%d\n",
+ rc);
+
+ main_fcc = get_effective_result_locked(chip->fcc_votable);
+ pval.intval = main_fcc;
+ rc = power_supply_set_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't set main charger fcc, rc=%d\n", rc);
+ return;
+ }
+
+ goto stepper_exit;
+ }
+
+ if (chip->main_step_fcc_count) {
+ main_fcc += (FCC_STEP_SIZE_UA * chip->main_step_fcc_dir);
+ chip->main_step_fcc_count--;
+ reschedule_ms = FCC_STEP_UPDATE_DELAY_MS;
+ } else if (chip->main_step_fcc_residual) {
+ main_fcc += chip->main_step_fcc_residual;
+ chip->main_step_fcc_residual = 0;
+ }
- pval.intval = slave_fcc_ua;
+ if (chip->parallel_step_fcc_count) {
+ parallel_fcc += (FCC_STEP_SIZE_UA *
+ chip->parallel_step_fcc_dir);
+ chip->parallel_step_fcc_count--;
+ reschedule_ms = FCC_STEP_UPDATE_DELAY_MS;
+ } else if (chip->parallel_step_fcc_residual) {
+ parallel_fcc += chip->parallel_step_fcc_residual;
+ chip->parallel_step_fcc_residual = 0;
+ }
+
+ if (chip->pl_mode == POWER_SUPPLY_PL_NONE ||
+ get_effective_result_locked(chip->pl_disable_votable)) {
+ /* Set Parallel FCC */
+ pval.intval = parallel_fcc;
+ rc = power_supply_set_property(chip->pl_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't set parallel charger fcc, rc=%d\n",
+ rc);
+ return;
+ }
+
+ /* Set main FCC */
+ pval.intval = main_fcc;
+ rc = power_supply_set_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't set main charger fcc, rc=%d\n", rc);
+ return;
+ }
+
+ if (parallel_fcc < MINIMUM_PARALLEL_FCC_UA) {
+ pval.intval = 1;
+ rc = power_supply_set_property(chip->pl_psy,
+ POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't change slave suspend state rc=%d\n",
+ rc);
+ return;
+ }
+ }
+ } else {
+ if (parallel_fcc < chip->slave_fcc_ua) {
+ pval.intval = parallel_fcc;
rc = power_supply_set_property(chip->pl_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
&pval);
if (rc < 0) {
- pr_err("Couldn't set parallel fcc, rc=%d\n",
- rc);
- return rc;
+ pr_err("Couldn't set parallel charger fcc, rc=%d\n",
+ rc);
+ return;
}
- chip->slave_fcc_ua = slave_fcc_ua;
+ pval.intval = main_fcc;
+ rc = power_supply_set_property(chip->main_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ &pval);
+ if (rc < 0) {
+ pr_err("Couldn't set main charger fcc, rc=%d\n",
+ rc);
+ return;
+ }
} else {
- pval.intval = slave_fcc_ua;
- rc = power_supply_set_property(chip->pl_psy,
+ pval.intval = main_fcc;
+ rc = power_supply_set_property(chip->main_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
&pval);
if (rc < 0) {
- pr_err("Couldn't set parallel fcc, rc=%d\n",
- rc);
- return rc;
+ pr_err("Couldn't set main charger fcc, rc=%d\n",
+ rc);
+ return;
}
- chip->slave_fcc_ua = slave_fcc_ua;
-
- pval.intval = master_fcc_ua;
- rc = power_supply_set_property(chip->main_psy,
+ pval.intval = parallel_fcc;
+ rc = power_supply_set_property(chip->pl_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
&pval);
if (rc < 0) {
- pr_err("Could not set main fcc, rc=%d\n", rc);
- return rc;
+ pr_err("Couldn't set parallel charger fcc, rc=%d\n",
+ rc);
+ return;
+ }
+ }
+
+ rc = power_supply_get_property(chip->pl_psy,
+ POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't get slave suspend status, rc=%d\n",
+ rc);
+ return;
+ }
+
+ /*
+ * Enable parallel charger only if it was disabled earlier and
+ * configured slave fcc is greater than or equal to 100mA.
+ */
+ if (pval.intval == 1 && parallel_fcc >= 100000) {
+ pval.intval = 0;
+ rc = power_supply_set_property(chip->pl_psy,
+ POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't change slave suspend state rc=%d\n",
+ rc);
+ return;
}
+
+ if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) ||
+ (chip->pl_mode ==
+ POWER_SUPPLY_PL_USBIN_USBIN_EXT))
+ split_settled(chip);
}
}
- pl_dbg(chip, PR_PARALLEL, "master_fcc=%d slave_fcc=%d distribution=(%d/%d)\n",
- master_fcc_ua, slave_fcc_ua,
- (master_fcc_ua * 100) / total_fcc_ua,
- (slave_fcc_ua * 100) / total_fcc_ua);
+stepper_exit:
+ chip->main_fcc_ua = main_fcc;
+ chip->slave_fcc_ua = parallel_fcc;
- return 0;
+ if (reschedule_ms) {
+ schedule_delayed_work(&chip->fcc_step_update_work,
+ msecs_to_jiffies(reschedule_ms));
+ pr_debug("Rescheduling FCC_STEPPER work\n");
+ } else {
+ vote(chip->pl_awake_votable, FCC_STEPPER_VOTER, false, 0);
+ }
}
#define PARALLEL_FLOAT_VOLTAGE_DELTA_UV 50000
@@ -660,6 +936,9 @@ static int pl_disable_vote_callback(struct votable *votable,
chip->total_settled_ua = 0;
chip->pl_settled_ua = 0;
+ /* Cancel FCC step change work */
+ cancel_delayed_work_sync(&chip->fcc_step_update_work);
+
if (!pl_disable) { /* enable */
/* keep system awake to talk to slave charger through i2c */
cancel_delayed_work_sync(&chip->pl_awake_work);
@@ -685,16 +964,19 @@ static int pl_disable_vote_callback(struct votable *votable,
* PARALLEL_PSY_VOTER keeps it disabled unless a pl_psy
* is seen.
*/
- pval.intval = 0;
- rc = power_supply_set_property(chip->pl_psy,
+ if (!chip->fcc_step_update) {
+ pval.intval = 0;
+ rc = power_supply_set_property(chip->pl_psy,
POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval);
- if (rc < 0)
- pr_err("Couldn't change slave suspend state rc=%d\n",
- rc);
+ if (rc < 0)
+ pr_err("Couldn't change slave suspend state rc=%d\n",
+ rc);
- if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN)
- || (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT))
- split_settled(chip);
+ if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) ||
+ (chip->pl_mode ==
+ POWER_SUPPLY_PL_USBIN_USBIN_EXT))
+ split_settled(chip);
+ }
/*
* we could have been enabled while in taper mode,
* start the taper work if so
@@ -715,15 +997,18 @@ static int pl_disable_vote_callback(struct votable *votable,
|| (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT))
split_settled(chip);
- /* pl_psy may be NULL while in the disable branch */
- if (chip->pl_psy) {
- pval.intval = 1;
- rc = power_supply_set_property(chip->pl_psy,
+ if (!chip->fcc_step_update) {
+ /* pl_psy may be NULL while in the disable branch */
+ if (chip->pl_psy) {
+ pval.intval = 1;
+ rc = power_supply_set_property(chip->pl_psy,
POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval);
- if (rc < 0)
- pr_err("Couldn't change slave suspend state rc=%d\n",
- rc);
+ if (rc < 0)
+ pr_err("Couldn't change slave suspend state rc=%d\n",
+ rc);
+ }
}
+
rerun_election(chip->fcc_votable);
rerun_election(chip->fv_votable);
@@ -1118,6 +1403,7 @@ int qcom_batt_init(void)
INIT_DELAYED_WORK(&chip->pl_taper_work, pl_taper_work);
INIT_WORK(&chip->pl_disable_forever_work, pl_disable_forever_work);
INIT_DELAYED_WORK(&chip->pl_awake_work, pl_awake_work);
+ INIT_DELAYED_WORK(&chip->fcc_step_update_work, fcc_step_update_work);
rc = pl_register_notifier(chip);
if (rc < 0) {
@@ -1172,6 +1458,7 @@ void qcom_batt_deinit(void)
cancel_delayed_work_sync(&chip->pl_taper_work);
cancel_work_sync(&chip->pl_disable_forever_work);
cancel_delayed_work_sync(&chip->pl_awake_work);
+ cancel_delayed_work_sync(&chip->fcc_step_update_work);
power_supply_unreg_notifier(&chip->nb);
destroy_votable(chip->pl_enable_votable_indirect);
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index 02b1204789bf..8e57bf9d2c31 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -325,6 +325,9 @@ static int smb2_parse_dt(struct smb2 *chip)
if (rc < 0)
chg->otg_delay_ms = OTG_DEFAULT_DEGLITCH_TIME_MS;
+ chg->fcc_stepper_mode = of_property_read_bool(node,
+ "qcom,fcc-stepping-enable");
+
return 0;
}
@@ -941,6 +944,7 @@ static enum power_supply_property smb2_batt_props[] = {
POWER_SUPPLY_PROP_RERUN_AICL,
POWER_SUPPLY_PROP_DP_DM,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE,
};
static int smb2_batt_get_prop(struct power_supply *psy,
@@ -1049,6 +1053,9 @@ static int smb2_batt_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
rc = smblib_get_prop_batt_charge_counter(chg, val);
break;
+ case POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE:
+ val->intval = chg->fcc_stepper_mode;
+ break;
default:
pr_err("batt power supply prop %d not supported\n", psp);
return -EINVAL;
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 2cf8eb4e7ceb..e96523a4d43e 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -2238,6 +2238,7 @@ int smblib_get_prop_usb_voltage_max(struct smb_charger *chg,
{
switch (chg->real_charger_type) {
case POWER_SUPPLY_TYPE_USB_HVDCP:
+ case POWER_SUPPLY_TYPE_USB_HVDCP_3:
case POWER_SUPPLY_TYPE_USB_PD:
if (chg->smb_version == PM660_SUBTYPE)
val->intval = MICRO_9V;
@@ -3359,7 +3360,8 @@ void smblib_usb_plugin_locked(struct smb_charger *chg)
rc = smblib_request_dpdm(chg, true);
if (rc < 0)
smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc);
-
+ if (chg->fcc_stepper_mode)
+ vote(chg->fcc_votable, FCC_STEPPER_VOTER, false, 0);
/* Schedule work to enable parallel charger */
vote(chg->awake_votable, PL_DELAY_VOTER, true, 0);
schedule_delayed_work(&chg->pl_enable_work,
@@ -3378,6 +3380,11 @@ void smblib_usb_plugin_locked(struct smb_charger *chg)
}
}
+ /* Force 1500mA FCC on removal */
+ if (chg->fcc_stepper_mode)
+ vote(chg->fcc_votable, FCC_STEPPER_VOTER,
+ true, 1500000);
+
rc = smblib_request_dpdm(chg, false);
if (rc < 0)
smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc);
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index 0de99b9da7bd..4475ccc21a2a 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -67,6 +67,7 @@ enum print_reason {
#define WEAK_CHARGER_VOTER "WEAK_CHARGER_VOTER"
#define WBC_VOTER "WBC_VOTER"
#define OV_VOTER "OV_VOTER"
+#define FCC_STEPPER_VOTER "FCC_STEPPER_VOTER"
#define VCONN_MAX_ATTEMPTS 3
#define OTG_MAX_ATTEMPTS 3
@@ -346,6 +347,7 @@ struct smb_charger {
u8 float_cfg;
bool use_extcon;
bool otg_present;
+ bool fcc_stepper_mode;
/* workaround flag */
u32 wa_flags;
diff --git a/drivers/regulator/cpr3-hmss-regulator.c b/drivers/regulator/cpr3-hmss-regulator.c
index 21381d1ad7fa..1aface23668e 100644
--- a/drivers/regulator/cpr3-hmss-regulator.c
+++ b/drivers/regulator/cpr3-hmss-regulator.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1665,20 +1665,29 @@ static int cpr3_hmss_init_controller(struct cpr3_controller *ctrl)
return 0;
}
-static int cpr3_hmss_regulator_suspend(struct platform_device *pdev,
- pm_message_t state)
+#if CONFIG_PM
+static int cpr3_hmss_regulator_suspend(struct device *dev)
{
- struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+ struct cpr3_controller *ctrl = dev_get_drvdata(dev);
return cpr3_regulator_suspend(ctrl);
}
-static int cpr3_hmss_regulator_resume(struct platform_device *pdev)
+static int cpr3_hmss_regulator_resume(struct device *dev)
{
- struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+ struct cpr3_controller *ctrl = dev_get_drvdata(dev);
return cpr3_regulator_resume(ctrl);
}
+#else
+#define cpr3_hmss_regulator_suspend NULL
+#define cpr3_hmss_regulator_resume NULL
+#endif
+
+static const struct dev_pm_ops cpr3_hmss_regulator_pm_ops = {
+ .suspend = cpr3_hmss_regulator_suspend,
+ .resume = cpr3_hmss_regulator_resume,
+};
/* Data corresponds to the SoC revision */
static const struct of_device_id cpr_regulator_match_table[] = {
@@ -1811,11 +1820,10 @@ static struct platform_driver cpr3_hmss_regulator_driver = {
.name = "qcom,cpr3-hmss-regulator",
.of_match_table = cpr_regulator_match_table,
.owner = THIS_MODULE,
+ .pm = &cpr3_hmss_regulator_pm_ops,
},
.probe = cpr3_hmss_regulator_probe,
.remove = cpr3_hmss_regulator_remove,
- .suspend = cpr3_hmss_regulator_suspend,
- .resume = cpr3_hmss_regulator_resume,
};
static int cpr_regulator_init(void)
diff --git a/drivers/regulator/cpr3-mmss-regulator.c b/drivers/regulator/cpr3-mmss-regulator.c
index 5a031f503b51..ec14ffe6b4c4 100644
--- a/drivers/regulator/cpr3-mmss-regulator.c
+++ b/drivers/regulator/cpr3-mmss-regulator.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1078,20 +1078,29 @@ static int cpr3_mmss_init_controller(struct cpr3_controller *ctrl)
return 0;
}
-static int cpr3_mmss_regulator_suspend(struct platform_device *pdev,
- pm_message_t state)
+#if CONFIG_PM
+static int cpr3_mmss_regulator_suspend(struct device *dev)
{
- struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+ struct cpr3_controller *ctrl = dev_get_drvdata(dev);
return cpr3_regulator_suspend(ctrl);
}
-static int cpr3_mmss_regulator_resume(struct platform_device *pdev)
+static int cpr3_mmss_regulator_resume(struct device *dev)
{
- struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+ struct cpr3_controller *ctrl = dev_get_drvdata(dev);
return cpr3_regulator_resume(ctrl);
}
+#else
+#define cpr3_mmss_regulator_suspend NULL
+#define cpr3_mmss_regulator_resume NULL
+#endif
+
+static const struct dev_pm_ops cpr3_mmss_regulator_pm_ops = {
+ .suspend = cpr3_mmss_regulator_suspend,
+ .resume = cpr3_mmss_regulator_resume,
+};
/* Data corresponds to the SoC revision */
static const struct of_device_id cpr_regulator_match_table[] = {
@@ -1233,11 +1242,10 @@ static struct platform_driver cpr3_mmss_regulator_driver = {
.name = "qcom,cpr3-mmss-regulator",
.of_match_table = cpr_regulator_match_table,
.owner = THIS_MODULE,
+ .pm = &cpr3_mmss_regulator_pm_ops,
},
.probe = cpr3_mmss_regulator_probe,
.remove = cpr3_mmss_regulator_remove,
- .suspend = cpr3_mmss_regulator_suspend,
- .resume = cpr3_mmss_regulator_resume,
};
static int cpr_regulator_init(void)
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index 499e437c7e91..f9d77b4c44ef 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -274,6 +274,7 @@ int of_regulator_match(struct device *dev, struct device_node *node,
dev_err(dev,
"failed to parse DT for regulator %s\n",
child->name);
+ of_node_put(child);
return -EINVAL;
}
match->of_node = of_node_get(child);
diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c
index e1cfa06810ef..e79f2a181ad2 100644
--- a/drivers/rtc/hctosys.c
+++ b/drivers/rtc/hctosys.c
@@ -49,6 +49,11 @@ static int __init rtc_hctosys(void)
tv64.tv_sec = rtc_tm_to_time64(&tm);
+#if BITS_PER_LONG == 32
+ if (tv64.tv_sec > INT_MAX)
+ goto err_read;
+#endif
+
err = do_settimeofday64(&tv64);
dev_info(rtc->dev.parent,
diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c
index afab89f5be48..a161fbf6f172 100644
--- a/drivers/rtc/rtc-snvs.c
+++ b/drivers/rtc/rtc-snvs.c
@@ -132,20 +132,23 @@ static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct snvs_rtc_data *data = dev_get_drvdata(dev);
unsigned long time;
+ int ret;
rtc_tm_to_time(tm, &time);
/* Disable RTC first */
- snvs_rtc_enable(data, false);
+ ret = snvs_rtc_enable(data, false);
+ if (ret)
+ return ret;
/* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
regmap_write(data->regmap, data->offset + SNVS_LPSRTCLR, time << CNTR_TO_SECS_SH);
regmap_write(data->regmap, data->offset + SNVS_LPSRTCMR, time >> (32 - CNTR_TO_SECS_SH));
/* Enable RTC again */
- snvs_rtc_enable(data, true);
+ ret = snvs_rtc_enable(data, true);
- return 0;
+ return ret;
}
static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
@@ -287,7 +290,11 @@ static int snvs_rtc_probe(struct platform_device *pdev)
regmap_write(data->regmap, data->offset + SNVS_LPSR, 0xffffffff);
/* Enable RTC */
- snvs_rtc_enable(data, true);
+ ret = snvs_rtc_enable(data, true);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable rtc %d\n", ret);
+ goto error_rtc_device_register;
+ }
device_init_wakeup(&pdev->dev, true);
diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c
index 560d9a5e0225..a9528083061d 100644
--- a/drivers/rtc/rtc-tx4939.c
+++ b/drivers/rtc/rtc-tx4939.c
@@ -86,7 +86,8 @@ static int tx4939_rtc_read_time(struct device *dev, struct rtc_time *tm)
for (i = 2; i < 6; i++)
buf[i] = __raw_readl(&rtcreg->dat);
spin_unlock_irq(&pdata->lock);
- sec = (buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2];
+ sec = ((unsigned long)buf[5] << 24) | (buf[4] << 16) |
+ (buf[3] << 8) | buf[2];
rtc_time_to_tm(sec, tm);
return rtc_valid_tm(tm);
}
@@ -147,7 +148,8 @@ static int tx4939_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
alrm->enabled = (ctl & TX4939_RTCCTL_ALME) ? 1 : 0;
alrm->pending = (ctl & TX4939_RTCCTL_ALMD) ? 1 : 0;
spin_unlock_irq(&pdata->lock);
- sec = (buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2];
+ sec = ((unsigned long)buf[5] << 24) | (buf[4] << 16) |
+ (buf[3] << 8) | buf[2];
rtc_time_to_tm(sec, &alrm->time);
return rtc_valid_tm(&alrm->time);
}
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 92e03b42e661..3fc73b5894f0 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -822,6 +822,7 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event)
ccw_device_set_timeout(cdev, 0);
cdev->private->iretry = 255;
+ cdev->private->async_kill_io_rc = -ETIMEDOUT;
ret = ccw_device_cancel_halt_clear(cdev);
if (ret == -EBUSY) {
ccw_device_set_timeout(cdev, 3*HZ);
@@ -898,7 +899,7 @@ ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event)
/* OK, i/o is dead now. Call interrupt handler. */
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
- ERR_PTR(-EIO));
+ ERR_PTR(cdev->private->async_kill_io_rc));
}
static void
@@ -915,14 +916,16 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event)
ccw_device_online_verify(cdev, 0);
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
- ERR_PTR(-EIO));
+ ERR_PTR(cdev->private->async_kill_io_rc));
}
void ccw_device_kill_io(struct ccw_device *cdev)
{
int ret;
+ ccw_device_set_timeout(cdev, 0);
cdev->private->iretry = 255;
+ cdev->private->async_kill_io_rc = -EIO;
ret = ccw_device_cancel_halt_clear(cdev);
if (ret == -EBUSY) {
ccw_device_set_timeout(cdev, 3*HZ);
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index b108f4a5c7dd..b142c7a389b7 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -155,6 +155,7 @@ struct ccw_device_private {
unsigned long intparm; /* user interruption parameter */
struct qdio_irq *qdio_data;
struct irb irb; /* device status */
+ int async_kill_io_rc;
struct senseid senseid; /* SenseID info */
struct pgid pgid[8]; /* path group IDs per chpid*/
struct ccw1 iccws[2]; /* ccws for SNID/SID/SPGID commands */
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
index 766a9176b4ad..cf531ad8b6ee 100644
--- a/drivers/scsi/aacraid/commsup.c
+++ b/drivers/scsi/aacraid/commsup.c
@@ -1321,9 +1321,10 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced)
host = aac->scsi_host_ptr;
scsi_block_requests(host);
aac_adapter_disable_int(aac);
- if (aac->thread->pid != current->pid) {
+ if (aac->thread && aac->thread->pid != current->pid) {
spin_unlock_irq(host->host_lock);
kthread_stop(aac->thread);
+ aac->thread = NULL;
jafo = 1;
}
@@ -1392,6 +1393,7 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced)
aac->name);
if (IS_ERR(aac->thread)) {
retval = PTR_ERR(aac->thread);
+ aac->thread = NULL;
goto out;
}
}
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index aa6eccb8940b..8da8b46da722 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -1085,6 +1085,7 @@ static void __aac_shutdown(struct aac_dev * aac)
up(&fib->event_wait);
}
kthread_stop(aac->thread);
+ aac->thread = NULL;
}
aac_send_shutdown(aac);
aac_adapter_disable_int(aac);
@@ -1189,8 +1190,10 @@ static int aac_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
* Map in the registers from the adapter.
*/
aac->base_size = AAC_MIN_FOOTPRINT_SIZE;
- if ((*aac_drivers[index].init)(aac))
+ if ((*aac_drivers[index].init)(aac)) {
+ error = -ENODEV;
goto out_unmap;
+ }
if (aac->sync_mode) {
if (aac_sync_mode)
diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c
index decdc71b6b86..f6d7c4712e66 100644
--- a/drivers/scsi/arm/fas216.c
+++ b/drivers/scsi/arm/fas216.c
@@ -2009,7 +2009,7 @@ static void fas216_rq_sns_done(FAS216_Info *info, struct scsi_cmnd *SCpnt,
* have valid data in the sense buffer that could
* confuse the higher levels.
*/
- memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer));
+ memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
//printk("scsi%d.%c: sense buffer: ", info->host->host_no, '0' + SCpnt->device->id);
//{ int i; for (i = 0; i < 32; i++) printk("%02x ", SCpnt->sense_buffer[i]); printk("\n"); }
/*
diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c
index 0002caf687dd..eb3b5c0f299f 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_io.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_io.c
@@ -1858,6 +1858,7 @@ void bnx2fc_process_scsi_cmd_compl(struct bnx2fc_cmd *io_req,
/* we will not receive ABTS response for this IO */
BNX2FC_IO_DBG(io_req, "Timer context finished processing "
"this scsi cmd\n");
+ return;
}
/* Cancel the timeout_work, as we received IO completion */
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index 4639dac64e7f..f096766150bc 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -634,7 +634,12 @@ lpfc_issue_lip(struct Scsi_Host *shost)
LPFC_MBOXQ_t *pmboxq;
int mbxstatus = MBXERR_ERROR;
+ /*
+ * If the link is offline, disabled or BLOCK_MGMT_IO
+ * it doesn't make any sense to allow issue_lip
+ */
if ((vport->fc_flag & FC_OFFLINE_MODE) ||
+ (phba->hba_flag & LINK_DISABLED) ||
(phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO))
return -EPERM;
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index be901f6db6d3..4131addfb872 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -691,8 +691,9 @@ lpfc_work_done(struct lpfc_hba *phba)
(phba->hba_flag & HBA_SP_QUEUE_EVT)) {
if (pring->flag & LPFC_STOP_IOCB_EVENT) {
pring->flag |= LPFC_DEFERRED_RING_EVENT;
- /* Set the lpfc data pending flag */
- set_bit(LPFC_DATA_READY, &phba->data_flags);
+ /* Preserve legacy behavior. */
+ if (!(phba->hba_flag & HBA_SP_QUEUE_EVT))
+ set_bit(LPFC_DATA_READY, &phba->data_flags);
} else {
if (phba->link_state >= LPFC_LINK_UP) {
pring->flag &= ~LPFC_DEFERRED_RING_EVENT;
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index ef43847153ea..3406586b9201 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -115,6 +115,8 @@ lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe *wqe)
/* set consumption flag every once in a while */
if (!((q->host_index + 1) % q->entry_repost))
bf_set(wqe_wqec, &wqe->generic.wqe_com, 1);
+ else
+ bf_set(wqe_wqec, &wqe->generic.wqe_com, 0);
if (q->phba->sli3_options & LPFC_SLI4_PHWQ_ENABLED)
bf_set(wqe_wqid, &wqe->generic.wqe_com, q->queue_id);
lpfc_sli_pcimem_bcopy(wqe, temp_wqe, q->entry_size);
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index b868ef3b2ca3..7d67a68bcc62 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -8637,7 +8637,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name),
"fw_event_%s%d", ioc->driver_name, ioc->id);
ioc->firmware_event_thread = alloc_ordered_workqueue(
- ioc->firmware_event_name, WQ_MEM_RECLAIM);
+ ioc->firmware_event_name, 0);
if (!ioc->firmware_event_thread) {
pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
ioc->name, __FILE__, __LINE__, __func__);
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 1f6a3b86965f..440d79e6aea5 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -268,7 +268,8 @@ qla2x00_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
/* Read all mbox registers? */
- mboxes = (1 << ha->mbx_count) - 1;
+ WARN_ON_ONCE(ha->mbx_count > 32);
+ mboxes = (1ULL << ha->mbx_count) - 1;
if (!ha->mcp)
ql_dbg(ql_dbg_async, vha, 0x5001, "MBX pointer ERROR.\n");
else
@@ -2495,7 +2496,8 @@ qla24xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
/* Read all mbox registers? */
- mboxes = (1 << ha->mbx_count) - 1;
+ WARN_ON_ONCE(ha->mbx_count > 32);
+ mboxes = (1ULL << ha->mbx_count) - 1;
if (!ha->mcp)
ql_dbg(ql_dbg_async, vha, 0x504e, "MBX pointer ERROR.\n");
else
diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h
index a7cfc270bd08..ce1d063f3e83 100644
--- a/drivers/scsi/qla4xxx/ql4_def.h
+++ b/drivers/scsi/qla4xxx/ql4_def.h
@@ -168,6 +168,8 @@
#define DEV_DB_NON_PERSISTENT 0
#define DEV_DB_PERSISTENT 1
+#define QL4_ISP_REG_DISCONNECT 0xffffffffU
+
#define COPY_ISID(dst_isid, src_isid) { \
int i, j; \
for (i = 0, j = ISID_SIZE - 1; i < ISID_SIZE;) \
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index 01c3610a60cf..d8c03431d0aa 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -262,6 +262,24 @@ static struct iscsi_transport qla4xxx_iscsi_transport = {
static struct scsi_transport_template *qla4xxx_scsi_transport;
+static int qla4xxx_isp_check_reg(struct scsi_qla_host *ha)
+{
+ u32 reg_val = 0;
+ int rval = QLA_SUCCESS;
+
+ if (is_qla8022(ha))
+ reg_val = readl(&ha->qla4_82xx_reg->host_status);
+ else if (is_qla8032(ha) || is_qla8042(ha))
+ reg_val = qla4_8xxx_rd_direct(ha, QLA8XXX_PEG_ALIVE_COUNTER);
+ else
+ reg_val = readw(&ha->reg->ctrl_status);
+
+ if (reg_val == QL4_ISP_REG_DISCONNECT)
+ rval = QLA_ERROR;
+
+ return rval;
+}
+
static int qla4xxx_send_ping(struct Scsi_Host *shost, uint32_t iface_num,
uint32_t iface_type, uint32_t payload_size,
uint32_t pid, struct sockaddr *dst_addr)
@@ -9196,10 +9214,17 @@ static int qla4xxx_eh_abort(struct scsi_cmnd *cmd)
struct srb *srb = NULL;
int ret = SUCCESS;
int wait = 0;
+ int rval;
ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%llu: Abort command issued cmd=%p, cdb=0x%x\n",
ha->host_no, id, lun, cmd, cmd->cmnd[0]);
+ rval = qla4xxx_isp_check_reg(ha);
+ if (rval != QLA_SUCCESS) {
+ ql4_printk(KERN_INFO, ha, "PCI/Register disconnect, exiting.\n");
+ return FAILED;
+ }
+
spin_lock_irqsave(&ha->hardware_lock, flags);
srb = (struct srb *) CMD_SP(cmd);
if (!srb) {
@@ -9251,6 +9276,7 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd)
struct scsi_qla_host *ha = to_qla_host(cmd->device->host);
struct ddb_entry *ddb_entry = cmd->device->hostdata;
int ret = FAILED, stat;
+ int rval;
if (!ddb_entry)
return ret;
@@ -9270,6 +9296,12 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd)
cmd, jiffies, cmd->request->timeout / HZ,
ha->dpc_flags, cmd->result, cmd->allowed));
+ rval = qla4xxx_isp_check_reg(ha);
+ if (rval != QLA_SUCCESS) {
+ ql4_printk(KERN_INFO, ha, "PCI/Register disconnect, exiting.\n");
+ return FAILED;
+ }
+
/* FIXME: wait for hba to go online */
stat = qla4xxx_reset_lun(ha, ddb_entry, cmd->device->lun);
if (stat != QLA_SUCCESS) {
@@ -9313,6 +9345,7 @@ static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd)
struct scsi_qla_host *ha = to_qla_host(cmd->device->host);
struct ddb_entry *ddb_entry = cmd->device->hostdata;
int stat, ret;
+ int rval;
if (!ddb_entry)
return FAILED;
@@ -9330,6 +9363,12 @@ static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd)
ha->host_no, cmd, jiffies, cmd->request->timeout / HZ,
ha->dpc_flags, cmd->result, cmd->allowed));
+ rval = qla4xxx_isp_check_reg(ha);
+ if (rval != QLA_SUCCESS) {
+ ql4_printk(KERN_INFO, ha, "PCI/Register disconnect, exiting.\n");
+ return FAILED;
+ }
+
stat = qla4xxx_reset_target(ha, ddb_entry);
if (stat != QLA_SUCCESS) {
starget_printk(KERN_INFO, scsi_target(cmd->device),
@@ -9384,9 +9423,16 @@ static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd)
{
int return_status = FAILED;
struct scsi_qla_host *ha;
+ int rval;
ha = to_qla_host(cmd->device->host);
+ rval = qla4xxx_isp_check_reg(ha);
+ if (rval != QLA_SUCCESS) {
+ ql4_printk(KERN_INFO, ha, "PCI/Register disconnect, exiting.\n");
+ return FAILED;
+ }
+
if ((is_qla8032(ha) || is_qla8042(ha)) && ql4xdontresethba)
qla4_83xx_set_idc_dontreset(ha);
diff --git a/drivers/scsi/scsi_transport_srp.c b/drivers/scsi/scsi_transport_srp.c
index e3cd3ece4412..c3d1891d2d3f 100644
--- a/drivers/scsi/scsi_transport_srp.c
+++ b/drivers/scsi/scsi_transport_srp.c
@@ -52,6 +52,8 @@ struct srp_internal {
struct transport_container rport_attr_cont;
};
+static int scsi_is_srp_rport(const struct device *dev);
+
#define to_srp_internal(tmpl) container_of(tmpl, struct srp_internal, t)
#define dev_to_rport(d) container_of(d, struct srp_rport, dev)
@@ -61,9 +63,24 @@ static inline struct Scsi_Host *rport_to_shost(struct srp_rport *r)
return dev_to_shost(r->dev.parent);
}
+static int find_child_rport(struct device *dev, void *data)
+{
+ struct device **child = data;
+
+ if (scsi_is_srp_rport(dev)) {
+ WARN_ON_ONCE(*child);
+ *child = dev;
+ }
+ return 0;
+}
+
static inline struct srp_rport *shost_to_rport(struct Scsi_Host *shost)
{
- return transport_class_to_srp_rport(&shost->shost_gendev);
+ struct device *child = NULL;
+
+ WARN_ON_ONCE(device_for_each_child(&shost->shost_gendev, &child,
+ find_child_rport) < 0);
+ return child ? dev_to_rport(child) : NULL;
}
/**
@@ -637,7 +654,8 @@ static enum blk_eh_timer_return srp_timed_out(struct scsi_cmnd *scmd)
struct srp_rport *rport = shost_to_rport(shost);
pr_debug("timeout for sdev %s\n", dev_name(&sdev->sdev_gendev));
- return rport->fast_io_fail_tmo < 0 && rport->dev_loss_tmo < 0 &&
+ return rport && rport->fast_io_fail_tmo < 0 &&
+ rport->dev_loss_tmo < 0 &&
i->f->reset_timer_if_blocked && scsi_device_blocked(sdev) ?
BLK_EH_RESET_TIMER : BLK_EH_NOT_HANDLED;
}
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 6e66e2ad9daf..c83dadfff171 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1420,6 +1420,85 @@ static int media_not_present(struct scsi_disk *sdkp,
return 0;
}
+/**
+ * sd_check_events - check media events
+ * @disk: kernel device descriptor
+ * @clearing: disk events currently being cleared
+ *
+ * Returns mask of DISK_EVENT_*.
+ *
+ * Note: this function is invoked from the block subsystem.
+ **/
+static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing)
+{
+ struct scsi_disk *sdkp = scsi_disk(disk);
+ struct scsi_device *sdp = sdkp->device;
+ struct scsi_sense_hdr *sshdr = NULL;
+ int retval;
+
+ SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_check_events\n"));
+
+ /* Simply return for embedded storage media such as UFS */
+ if (!sdp->removable)
+ goto out;
+
+ /*
+ * If the device is offline, don't send any commands - just pretend as
+ * if the command failed. If the device ever comes back online, we
+ * can deal with it then. It is only because of unrecoverable errors
+ * that we would ever take a device offline in the first place.
+ */
+ if (!scsi_device_online(sdp)) {
+ set_media_not_present(sdkp);
+ goto out;
+ }
+
+ /*
+ * Using TEST_UNIT_READY enables differentiation between drive with
+ * no cartridge loaded - NOT READY, drive with changed cartridge -
+ * UNIT ATTENTION, or with same cartridge - GOOD STATUS.
+ *
+ * Drives that auto spin down. eg iomega jaz 1G, will be started
+ * by sd_spinup_disk() from sd_revalidate_disk(), which happens whenever
+ * sd_revalidate() is called.
+ */
+ retval = -ENODEV;
+
+ if (scsi_block_when_processing_errors(sdp)) {
+ sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
+ retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES,
+ sshdr);
+ }
+
+ /* failed to execute TUR, assume media not present */
+ if (host_byte(retval)) {
+ set_media_not_present(sdkp);
+ goto out;
+ }
+
+ if (media_not_present(sdkp, sshdr))
+ goto out;
+
+ /*
+ * For removable scsi disk we have to recognise the presence
+ * of a disk in the drive.
+ */
+ if (!sdkp->media_present)
+ sdp->changed = 1;
+ sdkp->media_present = 1;
+out:
+ /*
+ * sdp->changed is set under the following conditions:
+ *
+ * Medium present state has changed in either direction.
+ * Device has indicated UNIT_ATTENTION.
+ */
+ kfree(sshdr);
+ retval = sdp->changed ? DISK_EVENT_MEDIA_CHANGE : 0;
+ sdp->changed = 0;
+ return retval;
+}
+
static int sd_sync_cache(struct scsi_disk *sdkp)
{
int retries, res;
@@ -1612,6 +1691,7 @@ static const struct block_device_operations sd_fops = {
#ifdef CONFIG_COMPAT
.compat_ioctl = sd_compat_ioctl,
#endif
+ .check_events = sd_check_events,
.revalidate_disk = sd_revalidate_disk,
.unlock_native_capacity = sd_unlock_native_capacity,
.pr_ops = &sd_pr_ops,
@@ -2334,6 +2414,7 @@ sd_read_write_protect_flag(struct scsi_disk *sdkp, unsigned char *buffer)
int res;
struct scsi_device *sdp = sdkp->device;
struct scsi_mode_data data;
+ int disk_ro = get_disk_ro(sdkp->disk);
set_disk_ro(sdkp->disk, 0);
if (sdp->skip_ms_page_3f) {
@@ -2373,7 +2454,7 @@ sd_read_write_protect_flag(struct scsi_disk *sdkp, unsigned char *buffer)
"Test WP failed, assume Write Enabled\n");
} else {
sdkp->write_prot = ((data.device_specific & 0x80) != 0);
- set_disk_ro(sdkp->disk, sdkp->write_prot);
+ set_disk_ro(sdkp->disk, sdkp->write_prot || disk_ro);
}
}
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 804586aeaffe..de53c9694b68 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -522,6 +522,8 @@ static int sr_block_open(struct block_device *bdev, fmode_t mode)
struct scsi_cd *cd;
int ret = -ENXIO;
+ check_disk_change(bdev);
+
mutex_lock(&sr_mutex);
cd = scsi_cd_get(bdev->bd_disk);
if (cd) {
@@ -582,18 +584,28 @@ out:
static unsigned int sr_block_check_events(struct gendisk *disk,
unsigned int clearing)
{
- struct scsi_cd *cd = scsi_cd(disk);
+ unsigned int ret = 0;
+ struct scsi_cd *cd;
- if (atomic_read(&cd->device->disk_events_disable_depth))
+ cd = scsi_cd_get(disk);
+ if (!cd)
return 0;
- return cdrom_check_events(&cd->cdi, clearing);
+ if (!atomic_read(&cd->device->disk_events_disable_depth))
+ ret = cdrom_check_events(&cd->cdi, clearing);
+
+ scsi_cd_put(cd);
+ return ret;
}
static int sr_block_revalidate_disk(struct gendisk *disk)
{
- struct scsi_cd *cd = scsi_cd(disk);
struct scsi_sense_hdr sshdr;
+ struct scsi_cd *cd;
+
+ cd = scsi_cd_get(disk);
+ if (!cd)
+ return -ENXIO;
/* if the unit is not ready, nothing more to do */
if (scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr))
@@ -602,6 +614,7 @@ static int sr_block_revalidate_disk(struct gendisk *disk)
sr_cd_check(&cd->cdi);
get_sectorsize(cd);
out:
+ scsi_cd_put(cd);
return 0;
}
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index 351d81dc2200..44b7a69d022a 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -1538,7 +1538,7 @@ static struct scsi_host_template scsi_driver = {
.eh_timed_out = storvsc_eh_timed_out,
.slave_alloc = storvsc_device_alloc,
.slave_configure = storvsc_device_configure,
- .cmd_per_lun = 255,
+ .cmd_per_lun = 2048,
.this_id = -1,
.use_clustering = ENABLE_CLUSTERING,
/* Make sure we dont get a sg segment crosses a page boundary */
diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c
index 6b349e301869..c6425e3df5a0 100644
--- a/drivers/scsi/sym53c8xx_2/sym_hipd.c
+++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c
@@ -536,7 +536,7 @@ sym_getsync(struct sym_hcb *np, u_char dt, u_char sfac, u_char *divp, u_char *fa
* Look for the greatest clock divisor that allows an
* input speed faster than the period.
*/
- while (div-- > 0)
+ while (--div > 0)
if (kpc >= (div_10M[div] << 2)) break;
/*
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 6d43254d84b9..6af0ca6eb7e2 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5216,7 +5216,7 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev)
/* REPORT SUPPORTED OPERATION CODES is not supported */
sdev->no_report_opcodes = 1;
- /* WRITE_SAME command is not supported*/
+ /* WRITE_SAME command is not supported */
sdev->no_write_same = 1;
ufshcd_set_queue_depth(sdev);
diff --git a/drivers/soc/qcom/hab/Kconfig b/drivers/soc/qcom/hab/Kconfig
index 2e4f5114e29f..2e6126f3734e 100644
--- a/drivers/soc/qcom/hab/Kconfig
+++ b/drivers/soc/qcom/hab/Kconfig
@@ -5,3 +5,7 @@ config MSM_HAB
Required for drivers to use the HAB API to communicate with the host
OS.
+config MSM_AGL
+ bool "Enable built-in hab config"
+ help
+ Use built-in configuration to setup hab driver.
diff --git a/drivers/soc/qcom/hab/Makefile b/drivers/soc/qcom/hab/Makefile
index 0ad19931776c..945ae52de196 100644
--- a/drivers/soc/qcom/hab/Makefile
+++ b/drivers/soc/qcom/hab/Makefile
@@ -8,9 +8,24 @@ msm_hab-objs = \
hab_mimex.o \
hab_mem_linux.o \
hab_pipe.o \
- qvm_comm.o \
- hab_qvm.o \
hab_parser.o \
khab_test.o
-obj-$(CONFIG_MSM_HAB) += msm_hab.o
+ifdef CONFIG_GHS_VMM
+msm_hab_hyp-objs = \
+ ghs_comm.o \
+ hab_ghs.o
+
+ifndef CONFIG_MSM_AGL
+ccflags-y += -DHABMM_HC_VMID
+endif
+
+else
+ifdef CONFIG_MSM_GVM_QUIN
+msm_hab_hyp-objs = \
+ qvm_comm.o \
+ hab_qvm.o
+endif
+endif
+
+obj-$(CONFIG_MSM_HAB) += msm_hab.o msm_hab_hyp.o
diff --git a/drivers/soc/qcom/hab/ghs_comm.c b/drivers/soc/qcom/hab/ghs_comm.c
new file mode 100644
index 000000000000..825f33a23858
--- /dev/null
+++ b/drivers/soc/qcom/hab/ghs_comm.c
@@ -0,0 +1,141 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "hab.h"
+#include "hab_ghs.h"
+
+int physical_channel_read(struct physical_channel *pchan,
+ void *payload,
+ size_t read_size)
+{
+ struct ghs_vdev *dev = (struct ghs_vdev *)pchan->hyp_data;
+
+ /* size in header is only for payload excluding the header itself */
+ if (dev->read_size < read_size + sizeof(struct hab_header)) {
+ pr_warn("read %zd is less than requested %zd plus header %zd\n",
+ dev->read_size, read_size, sizeof(struct hab_header));
+ read_size = dev->read_size;
+ }
+
+ /* always skip the header */
+ memcpy(payload, (unsigned char *)dev->read_data +
+ sizeof(struct hab_header) + dev->read_offset, read_size);
+ dev->read_offset += read_size;
+
+ return read_size;
+}
+
+int physical_channel_send(struct physical_channel *pchan,
+ struct hab_header *header,
+ void *payload)
+{
+ int sizebytes = HAB_HEADER_GET_SIZE(*header);
+ struct ghs_vdev *dev = (struct ghs_vdev *)pchan->hyp_data;
+ GIPC_Result result;
+ uint8_t *msg;
+
+ spin_lock_bh(&dev->io_lock);
+
+ result = GIPC_PrepareMessage(dev->endpoint, sizebytes+sizeof(*header),
+ (void **)&msg);
+ if (result == GIPC_Full) {
+ spin_unlock_bh(&dev->io_lock);
+ /* need to wait for space! */
+ pr_err("failed to reserve send msg for %zd bytes\n",
+ sizebytes+sizeof(*header));
+ return -EBUSY;
+ } else if (result != GIPC_Success) {
+ spin_unlock_bh(&dev->io_lock);
+ pr_err("failed to send due to error %d\n", result);
+ return -ENOMEM;
+ }
+
+ if (HAB_HEADER_GET_TYPE(*header) == HAB_PAYLOAD_TYPE_PROFILE) {
+ struct timeval tv;
+ struct habmm_xing_vm_stat *pstat =
+ (struct habmm_xing_vm_stat *)payload;
+
+ do_gettimeofday(&tv);
+ pstat->tx_sec = tv.tv_sec;
+ pstat->tx_usec = tv.tv_usec;
+ }
+
+ memcpy(msg, header, sizeof(*header));
+
+ if (sizebytes)
+ memcpy(msg+sizeof(*header), payload, sizebytes);
+
+ result = GIPC_IssueMessage(dev->endpoint, sizebytes+sizeof(*header),
+ header->id_type_size);
+ spin_unlock_bh(&dev->io_lock);
+ if (result != GIPC_Success) {
+ pr_err("send error %d, sz %zd, prot %x\n",
+ result, sizebytes+sizeof(*header),
+ header->id_type_size);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+void physical_channel_rx_dispatch(unsigned long physical_channel)
+{
+ struct hab_header header;
+ struct physical_channel *pchan =
+ (struct physical_channel *)physical_channel;
+ struct ghs_vdev *dev = (struct ghs_vdev *)pchan->hyp_data;
+ GIPC_Result result;
+
+ uint32_t events;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pchan->rxbuf_lock, flags);
+ events = kgipc_dequeue_events(dev->endpoint);
+ spin_unlock_irqrestore(&pchan->rxbuf_lock, flags);
+
+ if (events & (GIPC_EVENT_RESET))
+ pr_err("hab gipc %s remote vmid %d RESET\n",
+ dev->name, pchan->vmid_remote);
+ if (events & (GIPC_EVENT_RESETINPROGRESS))
+ pr_err("hab gipc %s remote vmid %d RESETINPROGRESS\n",
+ dev->name, pchan->vmid_remote);
+
+ if (events & (GIPC_EVENT_RECEIVEREADY)) {
+ spin_lock_bh(&pchan->rxbuf_lock);
+ while (1) {
+ dev->read_size = 0;
+ dev->read_offset = 0;
+ result = GIPC_ReceiveMessage(dev->endpoint,
+ dev->read_data,
+ GIPC_RECV_BUFF_SIZE_BYTES,
+ &dev->read_size,
+ &header.id_type_size);
+
+ if (result == GIPC_Success || dev->read_size > 0) {
+ /* handle corrupted msg? */
+ hab_msg_recv(pchan, dev->read_data);
+ continue;
+ } else if (result == GIPC_Empty) {
+ /* no more pending msg */
+ break;
+ }
+ pr_err("recv unhandled result %d, size %zd\n",
+ result, dev->read_size);
+ break;
+ }
+ spin_unlock_bh(&pchan->rxbuf_lock);
+ }
+
+ if (events & (GIPC_EVENT_SENDREADY))
+ pr_debug("kgipc send ready\n");
+}
diff --git a/drivers/soc/qcom/hab/hab.c b/drivers/soc/qcom/hab/hab.c
index 52de57b766f2..ef249bcba68c 100644
--- a/drivers/soc/qcom/hab/hab.c
+++ b/drivers/soc/qcom/hab/hab.c
@@ -16,11 +16,13 @@
.name = __name__,\
.id = __id__,\
.pchannels = LIST_HEAD_INIT(hab_devices[__num__].pchannels),\
- .pchan_lock = __MUTEX_INITIALIZER(hab_devices[__num__].pchan_lock),\
+ .pchan_lock = __SPIN_LOCK_UNLOCKED(hab_devices[__num__].pchan_lock),\
.openq_list = LIST_HEAD_INIT(hab_devices[__num__].openq_list),\
.openlock = __SPIN_LOCK_UNLOCKED(&hab_devices[__num__].openlock)\
}
+static const char hab_info_str[] = "Change: 16239527 Revision: #65";
+
/*
* The following has to match habmm definitions, order does not matter if
* hab config does not care either. When hab config is not present, the default
@@ -54,6 +56,8 @@ static struct hab_device hab_devices[] = {
struct hab_driver hab_driver = {
.ndevices = ARRAY_SIZE(hab_devices),
.devp = hab_devices,
+ .uctx_list = LIST_HEAD_INIT(hab_driver.uctx_list),
+ .drvlock = __SPIN_LOCK_UNLOCKED(hab_driver.drvlock),
};
struct uhab_context *hab_ctx_alloc(int kernel)
@@ -77,6 +81,7 @@ struct uhab_context *hab_ctx_alloc(int kernel)
rwlock_init(&ctx->exp_lock);
rwlock_init(&ctx->ctx_lock);
+ INIT_LIST_HEAD(&ctx->pending_open);
kref_init(&ctx->refcount);
ctx->import_ctx = habmem_imp_hyp_open();
if (!ctx->import_ctx) {
@@ -86,14 +91,53 @@ struct uhab_context *hab_ctx_alloc(int kernel)
}
ctx->kernel = kernel;
+ spin_lock_bh(&hab_driver.drvlock);
+ list_add_tail(&ctx->node, &hab_driver.uctx_list);
+ hab_driver.ctx_cnt++;
+ ctx->lb_be = hab_driver.b_loopback_be; /* loopback only */
+ hab_driver.b_loopback_be = ~hab_driver.b_loopback_be; /* loopback only*/
+ spin_unlock_bh(&hab_driver.drvlock);
+ pr_debug("ctx %pK live %d loopback be %d\n",
+ ctx, hab_driver.ctx_cnt, ctx->lb_be);
+
return ctx;
}
+/* ctx can only be freed when all the vchan releases the refcnt */
void hab_ctx_free(struct kref *ref)
{
struct uhab_context *ctx =
container_of(ref, struct uhab_context, refcount);
struct hab_export_ack_recvd *ack_recvd, *tmp;
+ struct virtual_channel *vchan;
+ struct physical_channel *pchan;
+ int i;
+ struct uhab_context *ctxdel, *ctxtmp;
+ struct hab_open_node *node;
+ struct export_desc *exp, *exp_tmp;
+
+ /* garbage-collect exp/imp buffers */
+ write_lock(&ctx->exp_lock);
+ list_for_each_entry_safe(exp, exp_tmp, &ctx->exp_whse, node) {
+ list_del(&exp->node);
+ pr_debug("potential leak exp %d vcid %X recovered\n",
+ exp->export_id, exp->vcid_local);
+ habmem_hyp_revoke(exp->payload, exp->payload_count);
+ habmem_remove_export(exp);
+ }
+ write_unlock(&ctx->exp_lock);
+
+ spin_lock_bh(&ctx->imp_lock);
+ list_for_each_entry_safe(exp, exp_tmp, &ctx->imp_whse, node) {
+ list_del(&exp->node);
+ ctx->import_total--;
+ pr_debug("leaked imp %d vcid %X for ctx is collected total %d\n",
+ exp->export_id, exp->vcid_local,
+ ctx->import_total);
+ habmm_imp_hyp_unmap(ctx->import_ctx, exp, ctx->kernel);
+ kfree(exp);
+ }
+ spin_unlock_bh(&ctx->imp_lock);
habmem_imp_hyp_close(ctx->import_ctx, ctx->kernel);
@@ -102,9 +146,70 @@ void hab_ctx_free(struct kref *ref)
kfree(ack_recvd);
}
+ /* walk vchan list to find the leakage */
+ spin_lock_bh(&hab_driver.drvlock);
+ hab_driver.ctx_cnt--;
+ list_for_each_entry_safe(ctxdel, ctxtmp, &hab_driver.uctx_list, node) {
+ if (ctxdel == ctx)
+ list_del(&ctxdel->node);
+ }
+ spin_unlock_bh(&hab_driver.drvlock);
+ pr_debug("live ctx %d refcnt %d kernel %d close %d owner %d\n",
+ hab_driver.ctx_cnt, get_refcnt(ctx->refcount),
+ ctx->kernel, ctx->closing, ctx->owner);
+
+ /* check vchans in this ctx */
+ write_lock(&ctx->ctx_lock);
+ list_for_each_entry(vchan, &ctx->vchannels, node) {
+ pr_warn("leak vchan id %X cnt %X remote %d in ctx\n",
+ vchan->id, get_refcnt(vchan->refcount),
+ vchan->otherend_id);
+ }
+ write_unlock(&ctx->ctx_lock);
+
+ /* check pending open */
+ if (ctx->pending_cnt)
+ pr_warn("potential leak of pendin_open nodes %d\n",
+ ctx->pending_cnt);
+
+ write_lock(&ctx->ctx_lock);
+ list_for_each_entry(node, &ctx->pending_open, node) {
+ pr_warn("leak pending open vcid %X type %d subid %d openid %d\n",
+ node->request.xdata.vchan_id, node->request.type,
+ node->request.xdata.sub_id,
+ node->request.xdata.open_id);
+ }
+ write_unlock(&ctx->ctx_lock);
+
+ /* check vchans belong to this ctx in all hab/mmid devices */
+ for (i = 0; i < hab_driver.ndevices; i++) {
+ struct hab_device *habdev = &hab_driver.devp[i];
+
+ spin_lock_bh(&habdev->pchan_lock);
+ list_for_each_entry(pchan, &habdev->pchannels, node) {
+
+ /* check vchan ctx owner */
+ write_lock(&pchan->vchans_lock);
+ list_for_each_entry(vchan, &pchan->vchannels, pnode) {
+ if (vchan->ctx == ctx) {
+ pr_warn("leak vcid %X cnt %d pchan %s local %d remote %d\n",
+ vchan->id,
+ get_refcnt(vchan->refcount),
+ pchan->name, pchan->vmid_local,
+ pchan->vmid_remote);
+ }
+ }
+ write_unlock(&pchan->vchans_lock);
+ }
+ spin_unlock_bh(&habdev->pchan_lock);
+ }
kfree(ctx);
}
+/*
+ * caller needs to call vchan_put() afterwards. this is used to refcnt
+ * the local ioctl access based on ctx
+ */
struct virtual_channel *hab_get_vchan_fromvcid(int32_t vcid,
struct uhab_context *ctx)
{
@@ -140,14 +245,14 @@ struct hab_device *find_hab_device(unsigned int mm_id)
* frontend backend
* send(INIT) wait(INIT)
* wait(INIT_ACK) send(INIT_ACK)
- * send(ACK) wait(ACK)
+ * send(INIT_DONE) wait(INIT_DONE)
*/
struct virtual_channel *frontend_open(struct uhab_context *ctx,
unsigned int mm_id,
int dom_id)
{
- int ret, open_id = 0;
+ int ret, ret2, open_id = 0;
struct physical_channel *pchan = NULL;
struct hab_device *dev;
struct virtual_channel *vchan = NULL;
@@ -155,6 +260,7 @@ struct virtual_channel *frontend_open(struct uhab_context *ctx,
struct hab_open_request request;
struct hab_open_request *recv_request;
int sub_id = HAB_MMID_GET_MINOR(mm_id);
+ struct hab_open_node pending_open = { { 0 } };
dev = find_hab_device(mm_id);
if (dev == NULL) {
@@ -163,6 +269,7 @@ struct virtual_channel *frontend_open(struct uhab_context *ctx,
goto err;
}
+ /* guest can find its own id */
pchan = hab_pchan_find_domid(dev, dom_id);
if (!pchan) {
pr_err("hab_pchan_find_domid failed: dom_id=%d\n", dom_id);
@@ -170,44 +277,83 @@ struct virtual_channel *frontend_open(struct uhab_context *ctx,
goto err;
}
- vchan = hab_vchan_alloc(ctx, pchan);
+ open_id = atomic_inc_return(&open_id_counter);
+ vchan = hab_vchan_alloc(ctx, pchan, open_id);
if (!vchan) {
pr_err("vchan alloc failed\n");
ret = -ENOMEM;
goto err;
- }
+ } else
/* Send Init sequence */
- open_id = atomic_inc_return(&open_id_counter);
hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT, pchan,
vchan->id, sub_id, open_id);
+ request.xdata.ver_fe = HAB_API_VER;
ret = hab_open_request_send(&request);
if (ret) {
pr_err("hab_open_request_send failed: %d\n", ret);
goto err;
}
+ pending_open.request = request;
+
+ /* during wait app could be terminated */
+ hab_open_pending_enter(ctx, pchan, &pending_open);
+
/* Wait for Init-Ack sequence */
hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT_ACK, pchan,
0, sub_id, open_id);
+ /* wait forever */
ret = hab_open_listen(ctx, dev, &request, &recv_request, 0);
- if (ret || !recv_request) {
- pr_err("hab_open_listen failed: %d\n", ret);
+ if (!ret && recv_request && ((recv_request->xdata.ver_fe & 0xFFFF0000)
+ != (recv_request->xdata.ver_be & 0xFFFF0000))) {
+ /* version check */
+ pr_err("hab major version mismatch fe %X be %X on mmid %d\n",
+ recv_request->xdata.ver_fe,
+ recv_request->xdata.ver_be, mm_id);
+
+ hab_open_pending_exit(ctx, pchan, &pending_open);
+ ret = -EPROTO;
+ goto err;
+ } else if (ret || !recv_request) {
+ pr_err("hab_open_listen failed: %d, send cancel vcid %x subid %d openid %d\n",
+ ret, vchan->id,
+ sub_id, open_id);
+ /* send cancel to BE due to FE's local close */
+ hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT_CANCEL,
+ pchan, vchan->id, sub_id, open_id);
+ request.xdata.ver_fe = HAB_API_VER;
+ ret2 = hab_open_request_send(&request);
+ if (ret2)
+ pr_err("send init_cancel failed %d on vcid %x\n", ret2,
+ vchan->id);
+ hab_open_pending_exit(ctx, pchan, &pending_open);
+
+ if (ret != -EINTR)
+ ret = -EINVAL;
goto err;
}
- vchan->otherend_id = recv_request->vchan_id;
- hab_open_request_free(recv_request);
+ /* remove pending open locally after good pairing */
+ hab_open_pending_exit(ctx, pchan, &pending_open);
+
+ pr_debug("hab version match fe %X be %X on mmid %d\n",
+ recv_request->xdata.ver_fe, recv_request->xdata.ver_be,
+ mm_id);
- vchan->session_id = open_id;
- pr_debug("vchan->session_id:%d\n", vchan->session_id);
+ vchan->otherend_id = recv_request->xdata.vchan_id;
+ hab_open_request_free(recv_request);
/* Send Ack sequence */
- hab_open_request_init(&request, HAB_PAYLOAD_TYPE_ACK, pchan,
+ hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT_DONE, pchan,
0, sub_id, open_id);
+ request.xdata.ver_fe = HAB_API_VER;
ret = hab_open_request_send(&request);
- if (ret)
+ if (ret) {
+ pr_err("failed to send init-done vcid %x remote %x openid %d\n",
+ vchan->id, vchan->otherend_id, vchan->session_id);
goto err;
+ }
hab_pchan_put(pchan);
@@ -222,10 +368,10 @@ err:
}
struct virtual_channel *backend_listen(struct uhab_context *ctx,
- unsigned int mm_id)
+ unsigned int mm_id, int timeout)
{
- int ret;
- int open_id;
+ int ret, ret2;
+ int open_id, ver_fe;
int sub_id = HAB_MMID_GET_MINOR(mm_id);
struct physical_channel *pchan = NULL;
struct hab_device *dev;
@@ -233,6 +379,7 @@ struct virtual_channel *backend_listen(struct uhab_context *ctx,
struct hab_open_request request;
struct hab_open_request *recv_request;
uint32_t otherend_vchan_id;
+ struct hab_open_node pending_open = { { 0 } };
dev = find_hab_device(mm_id);
if (dev == NULL) {
@@ -245,19 +392,50 @@ struct virtual_channel *backend_listen(struct uhab_context *ctx,
/* Wait for Init sequence */
hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT,
NULL, 0, sub_id, 0);
- ret = hab_open_listen(ctx, dev, &request, &recv_request, 0);
+ /* cancel should not happen at this moment */
+ ret = hab_open_listen(ctx, dev, &request, &recv_request,
+ timeout);
if (ret || !recv_request) {
- pr_err("hab_open_listen failed: %d\n", ret);
+ if (!ret && !recv_request)
+ ret = -EINVAL;
+ if (-EAGAIN == ret) {
+ ret = -ETIMEDOUT;
+ } else {
+ /* device is closed */
+ pr_err("open request wait failed ctx closing %d\n",
+ ctx->closing);
+ }
+ goto err;
+ } else if (!ret && recv_request &&
+ ((recv_request->xdata.ver_fe & 0xFFFF0000) !=
+ (HAB_API_VER & 0xFFFF0000))) {
+ int ret2;
+ /* version check */
+ pr_err("version mismatch fe %X be %X on mmid %d\n",
+ recv_request->xdata.ver_fe, HAB_API_VER, mm_id);
+ hab_open_request_init(&request,
+ HAB_PAYLOAD_TYPE_INIT_ACK,
+ NULL, 0, sub_id, recv_request->xdata.open_id);
+ request.xdata.ver_be = HAB_API_VER;
+ /* reply to allow FE to bail out */
+ ret2 = hab_open_request_send(&request);
+ if (ret2)
+ pr_err("send FE version mismatch failed mmid %d sub %d\n",
+ mm_id, sub_id);
+ ret = -EPROTO;
goto err;
}
- otherend_vchan_id = recv_request->vchan_id;
- open_id = recv_request->open_id;
+ /* guest id from guest */
+ otherend_vchan_id = recv_request->xdata.vchan_id;
+ open_id = recv_request->xdata.open_id;
+ ver_fe = recv_request->xdata.ver_fe;
pchan = recv_request->pchan;
hab_pchan_get(pchan);
hab_open_request_free(recv_request);
+ recv_request = NULL;
- vchan = hab_vchan_alloc(ctx, pchan);
+ vchan = hab_vchan_alloc(ctx, pchan, open_id);
if (!vchan) {
ret = -ENOMEM;
goto err;
@@ -265,23 +443,64 @@ struct virtual_channel *backend_listen(struct uhab_context *ctx,
vchan->otherend_id = otherend_vchan_id;
- vchan->session_id = open_id;
- pr_debug("vchan->session_id:%d\n", vchan->session_id);
-
/* Send Init-Ack sequence */
hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT_ACK,
pchan, vchan->id, sub_id, open_id);
+ request.xdata.ver_fe = ver_fe; /* carry over */
+ request.xdata.ver_be = HAB_API_VER;
ret = hab_open_request_send(&request);
if (ret)
goto err;
+ pending_open.request = request;
+ /* wait only after init-ack is sent */
+ hab_open_pending_enter(ctx, pchan, &pending_open);
+
/* Wait for Ack sequence */
- hab_open_request_init(&request, HAB_PAYLOAD_TYPE_ACK,
+ hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT_DONE,
pchan, 0, sub_id, open_id);
- ret = hab_open_listen(ctx, dev, &request, &recv_request, 0);
-
- if (ret != -EAGAIN)
+ ret = hab_open_listen(ctx, dev, &request, &recv_request,
+ HAB_HS_TIMEOUT);
+ hab_open_pending_exit(ctx, pchan, &pending_open);
+ if (ret && recv_request &&
+ recv_request->type == HAB_PAYLOAD_TYPE_INIT_CANCEL) {
+ pr_err("listen cancelled vcid %x subid %d openid %d ret %d\n",
+ request.xdata.vchan_id, request.xdata.sub_id,
+ request.xdata.open_id, ret);
+
+ /* FE cancels this session.
+ * So BE has to cancel its too
+ */
+ hab_open_request_init(&request,
+ HAB_PAYLOAD_TYPE_INIT_CANCEL, pchan,
+ vchan->id, sub_id, open_id);
+ ret2 = hab_open_request_send(&request);
+ if (ret2)
+ pr_err("send init_ack failed %d on vcid %x\n",
+ ret2, vchan->id);
+ hab_open_pending_exit(ctx, pchan, &pending_open);
+
+ ret = -ENODEV; /* open request cancelled remotely */
break;
+ } else if (ret != -EAGAIN) {
+ hab_open_pending_exit(ctx, pchan, &pending_open);
+ break; /* received something. good case! */
+ }
+
+ /* stay in the loop retry */
+ pr_warn("retry open ret %d vcid %X remote %X sub %d open %d\n",
+ ret, vchan->id, vchan->otherend_id, sub_id, open_id);
+
+ /* retry path starting here. free previous vchan */
+ hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT_CANCEL,
+ pchan, vchan->id, sub_id, open_id);
+ request.xdata.ver_fe = ver_fe;
+ request.xdata.ver_be = HAB_API_VER;
+ ret2 = hab_open_request_send(&request);
+ if (ret2)
+ pr_err("send init_ack failed %d on vcid %x\n", ret2,
+ vchan->id);
+ hab_open_pending_exit(ctx, pchan, &pending_open);
hab_vchan_put(vchan);
vchan = NULL;
@@ -290,7 +509,7 @@ struct virtual_channel *backend_listen(struct uhab_context *ctx,
}
if (ret || !recv_request) {
- pr_err("backend_listen failed: %d\n", ret);
+ pr_err("backend mmid %d listen error %d\n", mm_id, ret);
ret = -EINVAL;
goto err;
}
@@ -299,7 +518,8 @@ struct virtual_channel *backend_listen(struct uhab_context *ctx,
hab_pchan_put(pchan);
return vchan;
err:
- pr_err("listen on mmid %d failed\n", mm_id);
+ if (ret != -ETIMEDOUT)
+ pr_err("listen on mmid %d failed\n", mm_id);
if (vchan)
hab_vchan_put(vchan);
if (pchan)
@@ -318,8 +538,9 @@ long hab_vchan_send(struct uhab_context *ctx,
struct hab_header header = HAB_HEADER_INITIALIZER;
int nonblocking_flag = flags & HABMM_SOCKET_SEND_FLAGS_NON_BLOCKING;
- if (sizebytes > HAB_MAX_MSG_SIZEBYTES) {
- pr_err("Message too large, %lu bytes\n", sizebytes);
+ if (sizebytes > HAB_HEADER_SIZE_MASK) {
+ pr_err("Message too large, %lu bytes, max is %d\n",
+ sizebytes, HAB_HEADER_SIZE_MASK);
return -EINVAL;
}
@@ -330,11 +551,17 @@ long hab_vchan_send(struct uhab_context *ctx,
}
HAB_HEADER_SET_SIZE(header, sizebytes);
- if (flags & HABMM_SOCKET_SEND_FLAGS_XING_VM_STAT)
+ if (flags & HABMM_SOCKET_SEND_FLAGS_XING_VM_STAT) {
HAB_HEADER_SET_TYPE(header, HAB_PAYLOAD_TYPE_PROFILE);
- else
+ if (sizebytes < sizeof(struct habmm_xing_vm_stat)) {
+ pr_err("wrong profiling buffer size %zd, expect %zd\n",
+ sizebytes,
+ sizeof(struct habmm_xing_vm_stat));
+ return -EINVAL;
+ }
+ } else {
HAB_HEADER_SET_TYPE(header, HAB_PAYLOAD_TYPE_MSG);
-
+ }
HAB_HEADER_SET_ID(header, vchan->otherend_id);
HAB_HEADER_SET_SESSION_ID(header, vchan->session_id);
@@ -347,8 +574,6 @@ long hab_vchan_send(struct uhab_context *ctx,
schedule();
}
-
-
err:
if (vchan)
hab_vchan_put(vchan);
@@ -403,23 +628,22 @@ bool hab_is_loopback(void)
int hab_vchan_open(struct uhab_context *ctx,
unsigned int mmid,
int32_t *vcid,
+ int32_t timeout,
uint32_t flags)
{
struct virtual_channel *vchan = NULL;
struct hab_device *dev;
- pr_debug("Open mmid=%d, loopback mode=%d, loopback num=%d\n",
- mmid, hab_driver.b_loopback, hab_driver.loopback_num);
+ pr_debug("Open mmid=%d, loopback mode=%d, loopback be ctx %d\n",
+ mmid, hab_driver.b_loopback, ctx->lb_be);
if (!vcid)
return -EINVAL;
if (hab_is_loopback()) {
- if (!hab_driver.loopback_num) {
- hab_driver.loopback_num = 1;
- vchan = backend_listen(ctx, mmid);
+ if (ctx->lb_be) {
+ vchan = backend_listen(ctx, mmid, timeout);
} else {
- hab_driver.loopback_num = 0;
vchan = frontend_open(ctx, mmid, LOOPBACK_DOM);
}
} else {
@@ -427,28 +651,37 @@ int hab_vchan_open(struct uhab_context *ctx,
if (dev) {
struct physical_channel *pchan =
- hab_pchan_find_domid(dev, HABCFG_VMID_DONT_CARE);
-
- if (pchan->is_be)
- vchan = backend_listen(ctx, mmid);
- else
- vchan = frontend_open(ctx, mmid,
- HABCFG_VMID_DONT_CARE);
+ hab_pchan_find_domid(dev,
+ HABCFG_VMID_DONT_CARE);
+ if (pchan) {
+ if (pchan->is_be)
+ vchan = backend_listen(ctx, mmid,
+ timeout);
+ else
+ vchan = frontend_open(ctx, mmid,
+ HABCFG_VMID_DONT_CARE);
+ } else {
+ pr_err("open on nonexistent pchan (mmid %x)",
+ mmid);
+ return -ENODEV;
+ }
} else {
pr_err("failed to find device, mmid %d\n", mmid);
}
}
if (IS_ERR(vchan)) {
- pr_err("vchan open failed over mmid=%d\n", mmid);
+ if (-ETIMEDOUT != PTR_ERR(vchan) && -EAGAIN != PTR_ERR(vchan))
+ pr_err("vchan open failed mmid=%d\n", mmid);
return PTR_ERR(vchan);
}
- pr_debug("vchan id %x, remote id %x\n",
- vchan->id, vchan->otherend_id);
+ pr_debug("vchan id %x remote id %x session %d\n", vchan->id,
+ vchan->otherend_id, vchan->session_id);
write_lock(&ctx->ctx_lock);
list_add_tail(&vchan->node, &ctx->vchannels);
+ ctx->vcnt++;
write_unlock(&ctx->ctx_lock);
*vcid = vchan->id;
@@ -469,17 +702,6 @@ void hab_send_close_msg(struct virtual_channel *vchan)
}
}
-static void hab_vchan_close_impl(struct kref *ref)
-{
- struct virtual_channel *vchan =
- container_of(ref, struct virtual_channel, usagecnt);
-
- list_del(&vchan->node);
- hab_vchan_stop_notify(vchan);
- hab_vchan_put(vchan);
-}
-
-
void hab_vchan_close(struct uhab_context *ctx, int32_t vcid)
{
struct virtual_channel *vchan, *tmp;
@@ -490,11 +712,29 @@ void hab_vchan_close(struct uhab_context *ctx, int32_t vcid)
write_lock(&ctx->ctx_lock);
list_for_each_entry_safe(vchan, tmp, &ctx->vchannels, node) {
if (vchan->id == vcid) {
- kref_put(&vchan->usagecnt, hab_vchan_close_impl);
+ write_unlock(&ctx->ctx_lock);
+ pr_debug("vcid %x remote %x session %d refcnt %d\n",
+ vchan->id, vchan->otherend_id,
+ vchan->session_id, get_refcnt(vchan->refcount));
+ /*
+ * only set when vc close is called locally by user
+ * explicity. Used to block remote msg. if forked once
+ * before, this local close is skipped due to child
+ * usage. if forked but not closed locally, the local
+ * context could NOT be closed, vchan can be prolonged
+ * by arrived remote msgs
+ */
+ if (vchan->forked)
+ vchan->forked = 0;
+ else {
+ vchan->closed = 1;
+ hab_vchan_stop_notify(vchan);
+ }
+ hab_vchan_put(vchan); /* there is a lock inside */
+ write_lock(&ctx->ctx_lock);
break;
}
}
-
write_unlock(&ctx->ctx_lock);
}
@@ -511,7 +751,7 @@ static int hab_initialize_pchan_entry(struct hab_device *mmid_device,
char pchan_name[MAX_VMID_NAME_SIZE];
struct physical_channel *pchan = NULL;
int ret;
- int vmid = is_be ? vmid_remote : vmid_local;
+ int vmid = is_be ? vmid_remote : vmid_local; /* used for naming only */
if (!mmid_device) {
pr_err("habdev %pK, vmid local %d, remote %d, is be %d\n",
@@ -541,7 +781,11 @@ static int hab_initialize_pchan_entry(struct hab_device *mmid_device,
return ret;
}
-static void hab_generate_pchan(struct local_vmid *settings, int i, int j)
+/*
+ * generate pchan list based on hab settings table.
+ * return status 0: success, otherwise failure
+ */
+static int hab_generate_pchan(struct local_vmid *settings, int i, int j)
{
int k, ret = 0;
@@ -657,6 +901,7 @@ static void hab_generate_pchan(struct local_vmid *settings, int i, int j)
break;
}
+ return ret;
}
/*
@@ -665,7 +910,7 @@ static void hab_generate_pchan(struct local_vmid *settings, int i, int j)
*/
static int hab_generate_pchan_list(struct local_vmid *settings)
{
- int i, j;
+ int i, j, ret = 0;
/* scan by valid VMs, then mmid */
pr_debug("self vmid is %d\n", settings->self);
@@ -677,24 +922,34 @@ static int hab_generate_pchan_list(struct local_vmid *settings)
for (j = 1; j <= HABCFG_MMID_AREA_MAX; j++) {
if (HABCFG_GET_MMID(settings, i, j)
!= HABCFG_VMID_INVALID)
- hab_generate_pchan(settings, i, j);
+ ret = hab_generate_pchan(settings,
+ i, j);
}
}
}
-
- return 0;
+ return ret;
}
/*
* This function checks hypervisor plug-in readiness, read in hab configs,
* and configure pchans
*/
+#ifdef HABMM_HC_VMID
+#define DEFAULT_GVMID 3
+#else
+#define DEFAULT_GVMID 2
+#endif
+
int do_hab_parse(void)
{
int result;
int i;
struct hab_device *device;
- int pchan_total = 0;
+
+ /* single GVM is 2, multigvm is 2 or 3. GHS LV-GVM 2, LA-GVM 3 */
+ int default_gvmid = DEFAULT_GVMID;
+
+ pr_debug("hab parse starts for %s\n", hab_info_str);
/* first check if hypervisor plug-in is ready */
result = hab_hypervisor_register();
@@ -703,7 +958,10 @@ int do_hab_parse(void)
return result;
}
- /* Initialize open Q before first pchan starts */
+ /*
+ * Initialize open Q before first pchan starts.
+ * Each is for one pchan list
+ */
for (i = 0; i < hab_driver.ndevices; i++) {
device = &hab_driver.devp[i];
init_waitqueue_head(&device->openq);
@@ -712,12 +970,12 @@ int do_hab_parse(void)
/* read in hab config and create pchans*/
memset(&hab_driver.settings, HABCFG_VMID_INVALID,
sizeof(hab_driver.settings));
-
result = hab_parse(&hab_driver.settings);
if (result) {
- pr_warn("hab_parse failed and use the default settings\n");
- fill_default_gvm_settings(&hab_driver.settings, 2,
- MM_AUD_START, MM_ID_MAX);
+ pr_err("hab config open failed, prepare default gvm %d settings\n",
+ default_gvmid);
+ fill_default_gvm_settings(&hab_driver.settings, default_gvmid,
+ MM_AUD_START, MM_ID_MAX);
}
/* now generate hab pchan list */
@@ -725,6 +983,7 @@ int do_hab_parse(void)
if (result) {
pr_err("generate pchan list failed, ret %d\n", result);
} else {
+ int pchan_total = 0;
for (i = 0; i < hab_driver.ndevices; i++) {
device = &hab_driver.devp[i];
pchan_total += device->pchan_cnt;
@@ -736,6 +995,48 @@ int do_hab_parse(void)
return result;
}
+int get_refcnt(struct kref ref)
+{
+ return ref.refcount.counter;
+}
+
+void hab_hypervisor_unregister_common(void)
+{
+ int status, i;
+ struct uhab_context *ctx;
+ struct virtual_channel *vchan;
+
+ for (i = 0; i < hab_driver.ndevices; i++) {
+ struct hab_device *habdev = &hab_driver.devp[i];
+ struct physical_channel *pchan, *pchan_tmp;
+
+ list_for_each_entry_safe(pchan, pchan_tmp,
+ &habdev->pchannels, node) {
+ status = habhyp_commdev_dealloc(pchan);
+ if (status) {
+ pr_err("failed to free pchan %pK, i %d, ret %d\n",
+ pchan, i, status);
+ }
+ }
+ }
+
+ /* detect leaking uctx */
+ spin_lock_bh(&hab_driver.drvlock);
+ list_for_each_entry(ctx, &hab_driver.uctx_list, node) {
+ pr_warn("leaking ctx owner %d refcnt %d kernel %d\n",
+ ctx->owner, get_refcnt(ctx->refcount), ctx->kernel);
+ /* further check vchan leak */
+ read_lock(&ctx->ctx_lock);
+ list_for_each_entry(vchan, &ctx->vchannels, node) {
+ pr_warn("leaking vchan id %X remote %X refcnt %d\n",
+ vchan->id, vchan->otherend_id,
+ get_refcnt(vchan->refcount));
+ }
+ read_unlock(&ctx->ctx_lock);
+ }
+ spin_unlock_bh(&hab_driver.drvlock);
+}
+
static int hab_open(struct inode *inodep, struct file *filep)
{
int result = 0;
@@ -749,7 +1050,10 @@ static int hab_open(struct inode *inodep, struct file *filep)
return -ENOMEM;
}
+ ctx->owner = task_pid_nr(current);
filep->private_data = ctx;
+ pr_debug("ctx owner %d refcnt %d\n", ctx->owner,
+ get_refcnt(ctx->refcount));
return result;
}
@@ -758,25 +1062,50 @@ static int hab_release(struct inode *inodep, struct file *filep)
{
struct uhab_context *ctx = filep->private_data;
struct virtual_channel *vchan, *tmp;
+ struct hab_open_node *node;
if (!ctx)
return 0;
- pr_debug("inode %pK, filep %pK\n", inodep, filep);
+ pr_debug("inode %pK, filep %pK ctx %pK\n", inodep, filep, ctx);
write_lock(&ctx->ctx_lock);
-
+ /* notify remote side on vchan closing */
list_for_each_entry_safe(vchan, tmp, &ctx->vchannels, node) {
- list_del(&vchan->node);
+ list_del(&vchan->node); /* vchan is not in this ctx anymore */
hab_vchan_stop_notify(vchan);
- hab_vchan_put(vchan);
+ write_unlock(&ctx->ctx_lock);
+ if (!vchan->closed) {
+ pr_warn("potential leak vc %pK %x remote %x session %d refcnt %d\n",
+ vchan, vchan->id, vchan->otherend_id,
+ vchan->session_id,
+ get_refcnt(vchan->refcount));
+ hab_vchan_put(vchan); /* there is a lock inside */
+ }
+ write_lock(&ctx->ctx_lock);
}
+ /* notify remote side on pending open */
+ list_for_each_entry(node, &ctx->pending_open, node) {
+ /* no touch to the list itself. it is allocated on the stack */
+ if (hab_open_cancel_notify(&node->request))
+ pr_err("failed to send open cancel vcid %x subid %d openid %d pchan %s\n",
+ node->request.xdata.vchan_id,
+ node->request.xdata.sub_id,
+ node->request.xdata.open_id,
+ node->request.pchan->habdev->name);
+ }
write_unlock(&ctx->ctx_lock);
hab_ctx_put(ctx);
filep->private_data = NULL;
+ /* ctx leak check */
+ if (get_refcnt(ctx->refcount))
+ pr_warn("pending ctx release owner %d refcnt %d total %d\n",
+ ctx->owner, get_refcnt(ctx->refcount),
+ hab_driver.ctx_cnt);
+
return 0;
}
@@ -809,7 +1138,9 @@ static long hab_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
case IOCTL_HAB_VC_OPEN:
open_param = (struct hab_open *)data;
ret = hab_vchan_open(ctx, open_param->mmid,
- &open_param->vcid, open_param->flags);
+ &open_param->vcid,
+ open_param->timeout,
+ open_param->flags);
break;
case IOCTL_HAB_VC_CLOSE:
close_param = (struct hab_close *)data;
@@ -817,7 +1148,7 @@ static long hab_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
break;
case IOCTL_HAB_SEND:
send_param = (struct hab_send *)data;
- if (send_param->sizebytes > HAB_MAX_MSG_SIZEBYTES) {
+ if (send_param->sizebytes > HAB_HEADER_SIZE_MASK) {
ret = -EINVAL;
break;
}
@@ -858,6 +1189,9 @@ static long hab_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
recv_param->sizebytes = 0;
ret = -EFAULT;
}
+ } else if (ret && msg) {
+ pr_warn("vcid %X recv failed %d and msg is still of %zd bytes\n",
+ recv_param->vcid, (int)ret, msg->sizebytes);
}
if (msg)
@@ -879,22 +1213,22 @@ static long hab_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
info_param = (struct hab_info *)data;
if (!info_param->names || !info_param->namesize ||
info_param->namesize > sizeof(names)) {
- pr_err("wrong vm info vcid %X, names %llX, sz %d\n",
- info_param->vcid, info_param->names,
- info_param->namesize);
+ pr_err("wrong param for vm info vcid %X, names %llX, sz %d\n",
+ info_param->vcid, info_param->names,
+ info_param->namesize);
ret = -EINVAL;
break;
}
ret = hab_vchan_query(ctx, info_param->vcid,
(uint64_t *)&info_param->ids,
- names, info_param->namesize, 0);
+ names, info_param->namesize, 0);
if (!ret) {
if (copy_to_user((void __user *)info_param->names,
names,
info_param->namesize)) {
pr_err("copy_to_user failed: vc=%x size=%d\n",
- info_param->vcid,
- info_param->namesize*2);
+ info_param->vcid,
+ info_param->namesize*2);
info_param->namesize = 0;
ret = -EFAULT;
}
@@ -904,7 +1238,7 @@ static long hab_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
ret = -ENOIOCTLCMD;
}
- if (ret == 0 && _IOC_SIZE(cmd) && (cmd & IOC_OUT))
+ if (_IOC_SIZE(cmd) && (cmd & IOC_OUT))
if (copy_to_user((void __user *) arg, data, _IOC_SIZE(cmd))) {
pr_err("copy_to_user failed: cmd=%x\n", cmd);
ret = -EFAULT;
@@ -955,6 +1289,26 @@ static const struct dma_map_ops hab_dma_ops = {
.unmap_sg = hab_unmap_sg,
};
+static int hab_power_down_callback(
+ struct notifier_block *nfb, unsigned long action, void *data)
+{
+
+ switch (action) {
+ case SYS_DOWN:
+ case SYS_HALT:
+ case SYS_POWER_OFF:
+ pr_debug("reboot called %ld\n", action);
+ hab_hypervisor_unregister(); /* only for single VM guest */
+ break;
+ }
+ pr_debug("reboot called %ld done\n", action);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block hab_reboot_notifier = {
+ .notifier_call = hab_power_down_callback,
+};
+
static int __init hab_init(void)
{
int result;
@@ -997,6 +1351,10 @@ static int __init hab_init(void)
goto err;
}
+ result = register_reboot_notifier(&hab_reboot_notifier);
+ if (result)
+ pr_err("failed to register reboot notifier %d\n", result);
+
/* read in hab config, then configure pchans */
result = do_hab_parse();
@@ -1007,12 +1365,10 @@ static int __init hab_init(void)
result = -ENOMEM;
hab_hypervisor_unregister();
goto err;
- }
-
- set_dma_ops(hab_driver.dev, &hab_dma_ops);
-
- return result;
+ } else
+ set_dma_ops(hab_driver.dev, &hab_dma_ops);
}
+ return result;
err:
if (!IS_ERR_OR_NULL(hab_driver.dev))
@@ -1037,6 +1393,8 @@ static void __exit hab_exit(void)
class_destroy(hab_driver.class);
cdev_del(&hab_driver.cdev);
unregister_chrdev_region(dev, 1);
+ unregister_reboot_notifier(&hab_reboot_notifier);
+ pr_debug("hab exit called\n");
}
subsys_initcall(hab_init);
diff --git a/drivers/soc/qcom/hab/hab.h b/drivers/soc/qcom/hab/hab.h
index facb0a068221..d1aa88e3978e 100644
--- a/drivers/soc/qcom/hab/hab.h
+++ b/drivers/soc/qcom/hab/hab.h
@@ -16,7 +16,7 @@
#ifdef pr_fmt
#undef pr_fmt
#endif
-#define pr_fmt(fmt) "|hab:%s:%d|" fmt, __func__, __LINE__
+#define pr_fmt(fmt) "hab:%s:%d " fmt, __func__, __LINE__
#include <linux/types.h>
@@ -41,16 +41,19 @@
#include <linux/uaccess.h>
#include <linux/dma-direction.h>
#include <linux/dma-mapping.h>
+#include <linux/jiffies.h>
+#include <linux/reboot.h>
enum hab_payload_type {
HAB_PAYLOAD_TYPE_MSG = 0x0,
HAB_PAYLOAD_TYPE_INIT,
HAB_PAYLOAD_TYPE_INIT_ACK,
- HAB_PAYLOAD_TYPE_ACK,
+ HAB_PAYLOAD_TYPE_INIT_DONE,
HAB_PAYLOAD_TYPE_EXPORT,
HAB_PAYLOAD_TYPE_EXPORT_ACK,
HAB_PAYLOAD_TYPE_PROFILE,
HAB_PAYLOAD_TYPE_CLOSE,
+ HAB_PAYLOAD_TYPE_INIT_CANCEL,
HAB_PAYLOAD_TYPE_MAX,
};
#define LOOPBACK_DOM 0xFF
@@ -128,21 +131,21 @@ struct hab_header {
/* "Size" of the HAB_HEADER_ID and HAB_VCID_ID must match */
#define HAB_HEADER_SIZE_SHIFT 0
#define HAB_HEADER_TYPE_SHIFT 16
-#define HAB_HEADER_ID_SHIFT 24
+#define HAB_HEADER_ID_SHIFT 20
#define HAB_HEADER_SIZE_MASK 0x0000FFFF
-#define HAB_HEADER_TYPE_MASK 0x00FF0000
-#define HAB_HEADER_ID_MASK 0xFF000000
+#define HAB_HEADER_TYPE_MASK 0x000F0000
+#define HAB_HEADER_ID_MASK 0xFFF00000
#define HAB_HEADER_INITIALIZER {0}
#define HAB_MMID_GET_MAJOR(mmid) (mmid & 0xFFFF)
#define HAB_MMID_GET_MINOR(mmid) ((mmid>>16) & 0xFF)
#define HAB_VCID_ID_SHIFT 0
-#define HAB_VCID_DOMID_SHIFT 8
-#define HAB_VCID_MMID_SHIFT 16
-#define HAB_VCID_ID_MASK 0x000000FF
-#define HAB_VCID_DOMID_MASK 0x0000FF00
-#define HAB_VCID_MMID_MASK 0xFFFF0000
+#define HAB_VCID_DOMID_SHIFT 12
+#define HAB_VCID_MMID_SHIFT 20
+#define HAB_VCID_ID_MASK 0x00000FFF
+#define HAB_VCID_DOMID_MASK 0x000FF000
+#define HAB_VCID_MMID_MASK 0xFFF00000
#define HAB_VCID_GET_ID(vcid) \
(((vcid) & HAB_VCID_ID_MASK) >> HAB_VCID_ID_SHIFT)
@@ -182,12 +185,14 @@ struct hab_header {
#define HAB_HEADER_GET_SESSION_ID(header) ((header).session_id)
+#define HAB_HS_TIMEOUT (10*1000*1000)
+
struct physical_channel {
+ struct list_head node;
char name[MAX_VMID_NAME_SIZE];
int is_be;
struct kref refcount;
struct hab_device *habdev;
- struct list_head node;
struct idr vchan_idr;
spinlock_t vid_lock;
@@ -195,38 +200,44 @@ struct physical_channel {
spinlock_t expid_lock;
void *hyp_data;
- int dom_id;
- int vmid_local;
+ int dom_id; /* BE role: remote vmid; FE role: don't care */
+ int vmid_local; /* from DT or hab_config */
int vmid_remote;
- char vmname_local[12];
+ char vmname_local[12]; /* from DT */
char vmname_remote[12];
int closed;
spinlock_t rxbuf_lock;
- /* vchans over this pchan */
+ /* debug only */
+ uint32_t sequence_tx;
+ uint32_t sequence_rx;
+
+ /* vchans on this pchan */
struct list_head vchannels;
+ int vcnt;
rwlock_t vchans_lock;
};
-
+/* this payload has to be used together with type */
struct hab_open_send_data {
int vchan_id;
int sub_id;
int open_id;
+ int ver_fe;
+ int ver_be;
+ int reserved;
};
struct hab_open_request {
int type;
struct physical_channel *pchan;
- int vchan_id;
- int sub_id;
- int open_id;
+ struct hab_open_send_data xdata;
};
struct hab_open_node {
struct hab_open_request request;
struct list_head node;
- int age;
+ int64_t age; /* sec */
};
struct hab_export_ack {
@@ -247,20 +258,25 @@ struct hab_message {
uint32_t data[];
};
+/* for all the pchans of same kind */
struct hab_device {
char name[MAX_VMID_NAME_SIZE];
- unsigned int id;
+ uint32_t id;
struct list_head pchannels;
int pchan_cnt;
- struct mutex pchan_lock;
- struct list_head openq_list;
+ spinlock_t pchan_lock;
+ struct list_head openq_list; /* received */
spinlock_t openlock;
wait_queue_head_t openq;
+ int openq_cnt;
};
struct uhab_context {
+ struct list_head node; /* managed by the driver */
struct kref refcount;
+
struct list_head vchannels;
+ int vcnt;
struct list_head exp_whse;
uint32_t export_total;
@@ -276,9 +292,15 @@ struct uhab_context {
void *import_ctx;
+ struct list_head pending_open; /* sent to remote */
+ int pending_cnt;
+
rwlock_t ctx_lock;
int closing;
int kernel;
+ int owner;
+
+ int lb_be; /* loopback only */
};
/*
@@ -297,7 +319,7 @@ struct local_vmid {
};
struct hab_driver {
- struct device *dev;
+ struct device *dev; /* mmid dev list */
struct cdev cdev;
dev_t major;
struct class *class;
@@ -305,33 +327,30 @@ struct hab_driver {
struct hab_device *devp;
struct uhab_context *kctx;
+ struct list_head uctx_list;
+ int ctx_cnt;
+ spinlock_t drvlock;
+
struct local_vmid settings; /* parser results */
int b_server_dom;
- int loopback_num;
+ int b_loopback_be; /* only allow 2 apps simultaneously 1 fe 1 be */
int b_loopback;
void *hyp_priv; /* hypervisor plug-in storage */
};
struct virtual_channel {
- struct work_struct work;
/*
* refcount is used to track the references from hab core to the virtual
* channel such as references from physical channels,
* i.e. references from the "other" side
*/
struct kref refcount;
- /*
- * usagecnt is used to track the clients who are using this virtual
- * channel such as local clients, client sowftware etc,
- * i.e. references from "this" side
- */
- struct kref usagecnt;
struct physical_channel *pchan;
struct uhab_context *ctx;
- struct list_head node;
- struct list_head pnode;
+ struct list_head node; /* for ctx */
+ struct list_head pnode; /* for pchan */
struct list_head rx_list;
wait_queue_head_t rx_queue;
spinlock_t rx_lock;
@@ -339,6 +358,14 @@ struct virtual_channel {
int otherend_id;
int otherend_closed;
uint32_t session_id;
+
+ /*
+ * set when local close() is called explicitly. vchan could be
+ * used in hab-recv-msg() path (2) then close() is called (1).
+ * this is same case as close is not called and no msg path
+ */
+ int closed;
+ int forked; /* if fork is detected and assume only once */
};
/*
@@ -351,12 +378,15 @@ struct export_desc {
int readonly;
uint64_t import_index;
- struct virtual_channel *vchan;
+ struct virtual_channel *vchan; /* vchan could be freed earlier */
+ struct uhab_context *ctx;
+ struct physical_channel *pchan;
int32_t vcid_local;
int32_t vcid_remote;
int domid_local;
int domid_remote;
+ int flags;
struct list_head node;
void *kva;
@@ -365,7 +395,8 @@ struct export_desc {
} __packed;
int hab_vchan_open(struct uhab_context *ctx,
- unsigned int mmid, int32_t *vcid, uint32_t flags);
+ unsigned int mmid, int32_t *vcid,
+ int32_t timeout, uint32_t flags);
void hab_vchan_close(struct uhab_context *ctx,
int32_t vcid);
long hab_vchan_send(struct uhab_context *ctx,
@@ -401,13 +432,17 @@ int habmem_hyp_grant_user(unsigned long address,
int page_count,
int flags,
int remotedom,
- void *ppdata);
+ void *ppdata,
+ int *compressed,
+ int *compressed_size);
int habmem_hyp_grant(unsigned long address,
int page_count,
int flags,
int remotedom,
- void *ppdata);
+ void *ppdata,
+ int *compressed,
+ int *compressed_size);
int habmem_hyp_revoke(void *expdata, uint32_t count);
@@ -417,7 +452,7 @@ void habmem_imp_hyp_close(void *priv, int kernel);
int habmem_imp_hyp_map(void *imp_ctx, struct hab_import *param,
struct export_desc *exp, int kernel);
-int habmm_imp_hyp_unmap(void *imp_ctx, struct export_desc *exp);
+int habmm_imp_hyp_unmap(void *imp_ctx, struct export_desc *exp, int kernel);
int habmem_imp_hyp_mmap(struct file *flip, struct vm_area_struct *vma);
@@ -427,7 +462,7 @@ void hab_msg_free(struct hab_message *message);
int hab_msg_dequeue(struct virtual_channel *vchan,
struct hab_message **msg, int *rsize, unsigned int flags);
-void hab_msg_recv(struct physical_channel *pchan,
+int hab_msg_recv(struct physical_channel *pchan,
struct hab_header *header);
void hab_open_request_init(struct hab_open_request *request,
@@ -447,7 +482,7 @@ int hab_open_listen(struct uhab_context *ctx,
int ms_timeout);
struct virtual_channel *hab_vchan_alloc(struct uhab_context *ctx,
- struct physical_channel *pchan);
+ struct physical_channel *pchan, int openid);
struct virtual_channel *hab_vchan_get(struct physical_channel *pchan,
struct hab_header *header);
void hab_vchan_put(struct virtual_channel *vchan);
@@ -482,6 +517,7 @@ static inline void hab_ctx_put(struct uhab_context *ctx)
void hab_send_close_msg(struct virtual_channel *vchan);
int hab_hypervisor_register(void);
void hab_hypervisor_unregister(void);
+void hab_hypervisor_unregister_common(void);
int habhyp_commdev_alloc(void **commdev, int is_be, char *name,
int vmid_remote, struct hab_device *mmid_device);
int habhyp_commdev_dealloc(void *commdev);
@@ -496,7 +532,7 @@ int physical_channel_send(struct physical_channel *pchan,
void physical_channel_rx_dispatch(unsigned long physical_channel);
-int loopback_pchan_create(char *dev_name);
+int loopback_pchan_create(struct hab_device *dev, char *pchan_name);
int hab_parse(struct local_vmid *settings);
@@ -512,6 +548,21 @@ int hab_vchan_query(struct uhab_context *ctx, int32_t vcid, uint64_t *ids,
struct hab_device *find_hab_device(unsigned int mm_id);
+int get_refcnt(struct kref ref);
+
+int hab_open_pending_enter(struct uhab_context *ctx,
+ struct physical_channel *pchan,
+ struct hab_open_node *pending);
+
+int hab_open_pending_exit(struct uhab_context *ctx,
+ struct physical_channel *pchan,
+ struct hab_open_node *pending);
+
+int hab_open_cancel_notify(struct hab_open_request *request);
+
+int hab_open_receive_cancel(struct physical_channel *pchan,
+ size_t sizebytes);
+
/* Global singleton HAB instance */
extern struct hab_driver hab_driver;
diff --git a/drivers/soc/qcom/hab/hab_ghs.c b/drivers/soc/qcom/hab/hab_ghs.c
new file mode 100644
index 000000000000..e743d9b00a66
--- /dev/null
+++ b/drivers/soc/qcom/hab/hab_ghs.c
@@ -0,0 +1,214 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "hab.h"
+#include "hab_ghs.h"
+
+static const char * const dt_gipc_path_name[] = {
+ "testgipc1",
+ "testgipc2",
+ "testgipc3",
+ "testgipc4",
+ "testgipc5",
+ "testgipc6",
+ "testgipc7",
+ "testgipc8",
+ "testgipc9",
+ "testgipc10",
+ "testgipc11",
+ "testgipc12",
+ "testgipc13",
+ "testgipc14",
+ "testgipc15",
+ "testgipc16",
+ "testgipc17",
+ "testgipc18",
+ "testgipc19",
+ "testgipc20",
+ "testgipc21",
+ "testgipc22",
+};
+
+static struct ghs_vmm_plugin_info_s {
+ const char * const *dt_name;
+ int curr;
+ int probe_cnt;
+} ghs_vmm_plugin_info = {
+ dt_gipc_path_name,
+ 0,
+ ARRAY_SIZE(dt_gipc_path_name),
+};
+
+static void ghs_irq_handler(void *cookie)
+{
+ struct physical_channel *pchan = cookie;
+ struct ghs_vdev *dev =
+ (struct ghs_vdev *) (pchan ? pchan->hyp_data : NULL);
+
+ if (dev)
+ tasklet_schedule(&dev->task);
+}
+
+/* static struct physical_channel *habhyp_commdev_alloc(int id) */
+int habhyp_commdev_alloc(void **commdev, int is_be, char *name, int vmid_remote,
+ struct hab_device *mmid_device)
+{
+ struct ghs_vdev *dev = NULL;
+ struct physical_channel *pchan = NULL;
+ struct physical_channel **ppchan = (struct physical_channel **)commdev;
+ int ret = 0;
+
+ if (ghs_vmm_plugin_info.curr > ghs_vmm_plugin_info.probe_cnt) {
+ pr_err("too many commdev alloc %d, supported is %d\n",
+ ghs_vmm_plugin_info.curr,
+ ghs_vmm_plugin_info.probe_cnt);
+ ret = -ENOENT;
+ goto err;
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ pr_err("allocate struct ghs_vdev failed %zu bytes on pchan %s\n",
+ sizeof(*dev), name);
+ goto err;
+ }
+
+ memset(dev, 0, sizeof(*dev));
+ spin_lock_init(&dev->io_lock);
+
+ /*
+ * TODO: ExtractEndpoint is in ghs_comm.c because it blocks.
+ * Extrace and Request should be in roughly the same spot
+ */
+ if (is_be) {
+ /* role is backend */
+ dev->be = 1;
+ } else {
+ /* role is FE */
+ struct device_node *gvh_dn;
+
+ gvh_dn = of_find_node_by_path("/aliases");
+ if (gvh_dn) {
+ const char *ep_path = NULL;
+ struct device_node *ep_dn;
+
+ ret = of_property_read_string(gvh_dn,
+ ghs_vmm_plugin_info.dt_name[ghs_vmm_plugin_info.curr],
+ &ep_path);
+ if (ret)
+ pr_err("failed to read endpoint string ret %d\n",
+ ret);
+ of_node_put(gvh_dn);
+
+ ep_dn = of_find_node_by_path(ep_path);
+ if (ep_dn) {
+ dev->endpoint = kgipc_endpoint_alloc(ep_dn);
+ of_node_put(ep_dn);
+ if (IS_ERR(dev->endpoint)) {
+ ret = PTR_ERR(dev->endpoint);
+ pr_err("KGIPC alloc failed id: %d, ret: %d\n",
+ ghs_vmm_plugin_info.curr, ret);
+ goto err;
+ } else {
+ pr_debug("gipc ep found for %d\n",
+ ghs_vmm_plugin_info.curr);
+ }
+ } else {
+ pr_err("of_parse_phandle failed id: %d\n",
+ ghs_vmm_plugin_info.curr);
+ ret = -ENOENT;
+ goto err;
+ }
+ } else {
+ pr_err("of_find_compatible_node failed id: %d\n",
+ ghs_vmm_plugin_info.curr);
+ ret = -ENOENT;
+ goto err;
+ }
+ }
+ /* add pchan into the mmid_device list */
+ pchan = hab_pchan_alloc(mmid_device, vmid_remote);
+ if (!pchan) {
+ pr_err("hab_pchan_alloc failed for %s, cnt %d\n",
+ mmid_device->name, mmid_device->pchan_cnt);
+ ret = -ENOMEM;
+ goto err;
+ }
+ pchan->closed = 0;
+ pchan->hyp_data = (void *)dev;
+ pchan->is_be = is_be;
+ strlcpy(dev->name, name, sizeof(dev->name));
+ *ppchan = pchan;
+ dev->read_data = kmalloc(GIPC_RECV_BUFF_SIZE_BYTES, GFP_KERNEL);
+ if (!dev->read_data) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ tasklet_init(&dev->task, physical_channel_rx_dispatch,
+ (unsigned long) pchan);
+
+ ret = kgipc_endpoint_start_with_irq_callback(dev->endpoint,
+ ghs_irq_handler,
+ pchan);
+ if (ret) {
+ pr_err("irq alloc failed id: %d %s, ret: %d\n",
+ ghs_vmm_plugin_info.curr, name, ret);
+ goto err;
+ } else
+ pr_debug("ep irq handler started for %d %s, ret %d\n",
+ ghs_vmm_plugin_info.curr, name, ret);
+ /* this value could be more than devp total */
+ ghs_vmm_plugin_info.curr++;
+ return 0;
+err:
+ hab_pchan_put(pchan);
+ kfree(dev);
+ return ret;
+}
+
+int habhyp_commdev_dealloc(void *commdev)
+{
+ struct physical_channel *pchan = (struct physical_channel *)commdev;
+ struct ghs_vdev *dev = pchan->hyp_data;
+
+ kgipc_endpoint_free(dev->endpoint);
+ kfree(dev->read_data);
+ kfree(dev);
+
+ if (get_refcnt(pchan->refcount) > 1) {
+ pr_warn("potential leak pchan %s vchans %d refcnt %d\n",
+ pchan->name, pchan->vcnt, get_refcnt(pchan->refcount));
+ }
+ hab_pchan_put(pchan);
+ return 0;
+}
+
+void hab_hypervisor_unregister(void)
+{
+ pr_debug("total %d\n", hab_driver.ndevices);
+
+ hab_hypervisor_unregister_common();
+
+ ghs_vmm_plugin_info.curr = 0;
+}
+
+int hab_hypervisor_register(void)
+{
+ int ret = 0;
+
+ hab_driver.b_server_dom = 0;
+
+ return ret;
+}
diff --git a/drivers/soc/qcom/hab/hab_ghs.h b/drivers/soc/qcom/hab/hab_ghs.h
new file mode 100644
index 000000000000..54812480ebaa
--- /dev/null
+++ b/drivers/soc/qcom/hab/hab_ghs.h
@@ -0,0 +1,30 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __HAB_GHS_H
+#define __HAB_GHS_H
+
+#include <ghs_vmm/kgipc.h>
+#define GIPC_RECV_BUFF_SIZE_BYTES (32*1024)
+
+struct ghs_vdev {
+ int be;
+ void *read_data; /* buffer to receive from gipc */
+ size_t read_size;
+ int read_offset;
+ GIPC_Endpoint endpoint;
+ spinlock_t io_lock;
+ char name[32];
+ struct tasklet_struct task;
+};
+#endif /* __HAB_GHS_H */
diff --git a/drivers/soc/qcom/hab/hab_mem_linux.c b/drivers/soc/qcom/hab/hab_mem_linux.c
index a779067ee4c4..74ee88a037af 100644
--- a/drivers/soc/qcom/hab/hab_mem_linux.c
+++ b/drivers/soc/qcom/hab/hab_mem_linux.c
@@ -82,8 +82,6 @@ static int habmem_get_dma_pages_from_va(unsigned long address,
goto err;
}
- pr_debug("vma flags %lx\n", vma->vm_flags);
-
/* Look for the fd that matches this the vma file */
fd = iterate_fd(current->files, 0, match_file, vma->vm_file);
if (fd == 0) {
@@ -111,7 +109,6 @@ static int habmem_get_dma_pages_from_va(unsigned long address,
for_each_sg(sg_table->sgl, s, sg_table->nents, i) {
page = sg_page(s);
- pr_debug("sgl length %d\n", s->length);
for (j = page_offset; j < (s->length >> PAGE_SHIFT); j++) {
pages[rc] = nth_page(page, j);
@@ -205,7 +202,9 @@ int habmem_hyp_grant_user(unsigned long address,
int page_count,
int flags,
int remotedom,
- void *ppdata)
+ void *ppdata,
+ int *compressed,
+ int *compressed_size)
{
int i, ret = 0;
struct grantable *item = (struct grantable *)ppdata;
@@ -239,7 +238,8 @@ int habmem_hyp_grant_user(unsigned long address,
for (i = 0; i < page_count; i++)
item[i].pfn = page_to_pfn(pages[i]);
} else {
- pr_err("get %d user pages failed: %d\n", page_count, ret);
+ pr_err("get %d user pages failed %d flags %d\n",
+ page_count, ret, flags);
}
vfree(pages);
@@ -256,7 +256,9 @@ int habmem_hyp_grant(unsigned long address,
int page_count,
int flags,
int remotedom,
- void *ppdata)
+ void *ppdata,
+ int *compressed,
+ int *compressed_size)
{
int i;
struct grantable *item;
@@ -310,7 +312,7 @@ void habmem_imp_hyp_close(void *imp_ctx, int kernel)
list_del(&pglist->list);
priv->cnt--;
- vfree(pglist->pages);
+ kfree(pglist->pages);
kfree(pglist);
}
@@ -460,19 +462,19 @@ static int habmem_imp_hyp_map_fd(void *imp_ctx,
unsigned long pfn;
int i, j, k = 0;
pgprot_t prot = PAGE_KERNEL;
- int32_t fd;
+ int32_t fd, size;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
if (!pfn_table || !priv)
return -EINVAL;
-
- pages = vmalloc(exp->payload_count * sizeof(struct page *));
+ size = exp->payload_count * sizeof(struct page *);
+ pages = kmalloc(size, GFP_KERNEL);
if (!pages)
return -ENOMEM;
pglist = kzalloc(sizeof(*pglist), GFP_KERNEL);
if (!pglist) {
- vfree(pages);
+ kfree(pages);
return -ENOMEM;
}
@@ -503,7 +505,7 @@ static int habmem_imp_hyp_map_fd(void *imp_ctx,
exp_info.priv = pglist;
pglist->dmabuf = dma_buf_export(&exp_info);
if (IS_ERR(pglist->dmabuf)) {
- vfree(pages);
+ kfree(pages);
kfree(pglist);
return PTR_ERR(pglist->dmabuf);
}
@@ -511,7 +513,7 @@ static int habmem_imp_hyp_map_fd(void *imp_ctx,
fd = dma_buf_fd(pglist->dmabuf, O_CLOEXEC);
if (fd < 0) {
dma_buf_put(pglist->dmabuf);
- vfree(pages);
+ kfree(pages);
kfree(pglist);
return -EINVAL;
}
@@ -539,17 +541,18 @@ static int habmem_imp_hyp_map_kva(void *imp_ctx,
struct pages_list *pglist;
struct importer_context *priv = imp_ctx;
unsigned long pfn;
- int i, j, k = 0;
+ int i, j, k = 0, size;
pgprot_t prot = PAGE_KERNEL;
if (!pfn_table || !priv)
return -EINVAL;
- pages = vmalloc(exp->payload_count * sizeof(struct page *));
+ size = exp->payload_count * sizeof(struct page *);
+ pages = kmalloc(size, GFP_KERNEL);
if (!pages)
return -ENOMEM;
pglist = kzalloc(sizeof(*pglist), GFP_KERNEL);
if (!pglist) {
- vfree(pages);
+ kfree(pages);
return -ENOMEM;
}
@@ -575,7 +578,7 @@ static int habmem_imp_hyp_map_kva(void *imp_ctx,
pglist->kva = vmap(pglist->pages, pglist->npages, VM_MAP, prot);
if (pglist->kva == NULL) {
- vfree(pages);
+ kfree(pages);
kfree(pglist);
pr_err("%ld pages vmap failed\n", pglist->npages);
return -ENOMEM;
@@ -607,18 +610,18 @@ static int habmem_imp_hyp_map_uva(void *imp_ctx,
struct pages_list *pglist;
struct importer_context *priv = imp_ctx;
unsigned long pfn;
- int i, j, k = 0;
+ int i, j, k = 0, size;
if (!pfn_table || !priv)
return -EINVAL;
-
- pages = vmalloc(exp->payload_count * sizeof(struct page *));
+ size = exp->payload_count * sizeof(struct page *);
+ pages = kmalloc(size, GFP_KERNEL);
if (!pages)
return -ENOMEM;
pglist = kzalloc(sizeof(*pglist), GFP_KERNEL);
if (!pglist) {
- vfree(pages);
+ kfree(pages);
return -ENOMEM;
}
@@ -670,7 +673,7 @@ int habmem_imp_hyp_map(void *imp_ctx, struct hab_import *param,
return ret;
}
-int habmm_imp_hyp_unmap(void *imp_ctx, struct export_desc *exp)
+int habmm_imp_hyp_unmap(void *imp_ctx, struct export_desc *exp, int kernel)
{
struct importer_context *priv = imp_ctx;
struct pages_list *pglist, *tmp;
@@ -679,11 +682,8 @@ int habmm_imp_hyp_unmap(void *imp_ctx, struct export_desc *exp)
write_lock(&priv->implist_lock);
list_for_each_entry_safe(pglist, tmp, &priv->imp_list, list) {
if (pglist->export_id == exp->export_id &&
- pglist->vcid == exp->vcid_remote) {
+ pglist->vcid == exp->vcid_remote) {
found = 1;
- }
-
- if (found) {
list_del(&pglist->list);
priv->cnt--;
break;
@@ -705,7 +705,7 @@ int habmm_imp_hyp_unmap(void *imp_ctx, struct export_desc *exp)
if (pglist->dmabuf)
dma_buf_put(pglist->dmabuf);
- vfree(pglist->pages);
+ kfree(pglist->pages);
kfree(pglist);
return 0;
@@ -719,9 +719,6 @@ int habmem_imp_hyp_mmap(struct file *filp, struct vm_area_struct *vma)
struct pages_list *pglist;
int bfound = 0;
- pr_debug("mmap request start %lX, len %ld, index %lX\n",
- vma->vm_start, length, vma->vm_pgoff);
-
read_lock(&imp_ctx->implist_lock);
list_for_each_entry(pglist, &imp_ctx->imp_list, list) {
if (pglist->index == vma->vm_pgoff) {
diff --git a/drivers/soc/qcom/hab/hab_mimex.c b/drivers/soc/qcom/hab/hab_mimex.c
index 00fbeabed4bb..86d763f65657 100644
--- a/drivers/soc/qcom/hab/hab_mimex.c
+++ b/drivers/soc/qcom/hab/hab_mimex.c
@@ -28,7 +28,7 @@
static int hab_export_ack_find(struct uhab_context *ctx,
- struct hab_export_ack *expect_ack)
+ struct hab_export_ack *expect_ack, struct virtual_channel *vchan)
{
int ret = 0;
struct hab_export_ack_recvd *ack_recvd, *tmp;
@@ -36,9 +36,10 @@ static int hab_export_ack_find(struct uhab_context *ctx,
spin_lock_bh(&ctx->expq_lock);
list_for_each_entry_safe(ack_recvd, tmp, &ctx->exp_rxq, node) {
- if (ack_recvd->ack.export_id == expect_ack->export_id &&
+ if ((ack_recvd->ack.export_id == expect_ack->export_id &&
ack_recvd->ack.vcid_local == expect_ack->vcid_local &&
- ack_recvd->ack.vcid_remote == expect_ack->vcid_remote) {
+ ack_recvd->ack.vcid_remote == expect_ack->vcid_remote)
+ || vchan->otherend_closed) {
list_del(&ack_recvd->node);
kfree(ack_recvd);
ret = 1;
@@ -57,15 +58,17 @@ static int hab_export_ack_find(struct uhab_context *ctx,
}
static int hab_export_ack_wait(struct uhab_context *ctx,
- struct hab_export_ack *expect_ack)
+ struct hab_export_ack *expect_ack, struct virtual_channel *vchan)
{
int ret;
ret = wait_event_interruptible_timeout(ctx->exp_wq,
- hab_export_ack_find(ctx, expect_ack),
- HZ);
+ hab_export_ack_find(ctx, expect_ack, vchan),
+ HAB_HS_TIMEOUT);
if (!ret || (ret == -ERESTARTSYS))
ret = -EAGAIN;
+ else if (vchan->otherend_closed)
+ ret = -ENODEV;
else if (ret > 0)
ret = 0;
return ret;
@@ -86,7 +89,7 @@ static struct export_desc *habmem_add_export(struct virtual_channel *vchan,
if (!vchan || !sizebytes)
return NULL;
- exp = vmalloc(sizebytes);
+ exp = kzalloc(sizebytes, GFP_KERNEL);
if (!exp)
return NULL;
@@ -103,6 +106,8 @@ static struct export_desc *habmem_add_export(struct virtual_channel *vchan,
exp->vcid_remote = vchan->otherend_id;
exp->domid_local = -1; /* dom id, provided on the importer */
exp->domid_remote = vchan->pchan->dom_id;
+ exp->ctx = vchan->ctx;
+ exp->pchan = vchan->pchan;
ctx = vchan->ctx;
write_lock(&ctx->exp_lock);
@@ -118,19 +123,22 @@ void habmem_remove_export(struct export_desc *exp)
struct physical_channel *pchan;
struct uhab_context *ctx;
- if (!exp || !exp->vchan || !exp->vchan->ctx || !exp->vchan->pchan)
+ if (!exp || !exp->ctx || !exp->pchan) {
+ pr_err("failed to find valid info in exp %pK ctx %pK pchan %pK\n",
+ exp, exp->ctx, exp->pchan);
return;
+ }
- ctx = exp->vchan->ctx;
+ ctx = exp->ctx;
ctx->export_total--;
- pchan = exp->vchan->pchan;
+ pchan = exp->pchan;
spin_lock(&pchan->expid_lock);
idr_remove(&pchan->expid_idr, exp->export_id);
spin_unlock(&pchan->expid_lock);
- vfree(exp);
+ kfree(exp);
}
static int compress_pfns(void **pfns, int npages, unsigned int *data_size)
@@ -148,7 +156,7 @@ static int compress_pfns(void **pfns, int npages, unsigned int *data_size)
new_table->first_pfn = item[0].pfn;
for (i = 1; i < npages; i++) {
if (item[i].pfn-1 == item[i-1].pfn) {
- region_size++;
+ region_size++; /* continuous pfn */
} else {
new_table->region[j].size = region_size;
new_table->region[j].space = item[i].pfn -
@@ -208,7 +216,12 @@ static int habmem_export_vchan(struct uhab_context *ctx,
expected_ack.export_id = exp->export_id;
expected_ack.vcid_local = exp->vcid_local;
expected_ack.vcid_remote = exp->vcid_remote;
- ret = hab_export_ack_wait(ctx, &expected_ack);
+ ret = hab_export_ack_wait(ctx, &expected_ack, vchan);
+ if (ret != 0) {
+ pr_err("failed to receive remote export ack %d on vc %x\n",
+ ret, vchan->id);
+ return ret;
+ }
*export_id = exp->export_id;
@@ -225,12 +238,11 @@ int hab_mem_export(struct uhab_context *ctx,
uint32_t export_id = 0;
struct virtual_channel *vchan;
int page_count;
+ int compressed = 0;
- if (!ctx || !param || param->sizebytes > HAB_MAX_EXPORT_SIZE)
+ if (!ctx || !param)
return -EINVAL;
- pr_debug("vc %X, mem size %d\n", param->vcid, param->sizebytes);
-
vchan = hab_get_vchan_fromvcid(param->vcid, ctx);
if (!vchan || !vchan->pchan) {
ret = -ENODEV;
@@ -249,13 +261,17 @@ int hab_mem_export(struct uhab_context *ctx,
page_count,
param->flags,
vchan->pchan->dom_id,
- pdata_exp);
+ pdata_exp,
+ &compressed,
+ &pdata_size);
} else {
ret = habmem_hyp_grant_user((unsigned long)param->buffer,
page_count,
param->flags,
vchan->pchan->dom_id,
- pdata_exp);
+ pdata_exp,
+ &compressed,
+ &pdata_size);
}
if (ret < 0) {
pr_err("habmem_hyp_grant failed size=%d ret=%d\n",
@@ -263,7 +279,8 @@ int hab_mem_export(struct uhab_context *ctx,
goto err;
}
- compress_pfns(&pdata_exp, page_count, &pdata_size);
+ if (!compressed)
+ compress_pfns(&pdata_exp, page_count, &pdata_size);
ret = habmem_export_vchan(ctx,
vchan,
@@ -287,14 +304,23 @@ int hab_mem_unexport(struct uhab_context *ctx,
{
int ret = 0, found = 0;
struct export_desc *exp, *tmp;
+ struct virtual_channel *vchan;
if (!ctx || !param)
return -EINVAL;
+ /* refcnt on the access */
+ vchan = hab_get_vchan_fromvcid(param->vcid, ctx);
+ if (!vchan || !vchan->pchan) {
+ ret = -ENODEV;
+ goto err_novchan;
+ }
+
write_lock(&ctx->exp_lock);
list_for_each_entry_safe(exp, tmp, &ctx->exp_whse, node) {
- if ((param->exportid == exp->export_id) &&
- (param->vcid == exp->vcid_local)) {
+ if (param->exportid == exp->export_id &&
+ param->vcid == exp->vcid_local) {
+ /* same vchan guarantees the pchan for idr */
list_del(&exp->node);
found = 1;
break;
@@ -302,15 +328,22 @@ int hab_mem_unexport(struct uhab_context *ctx,
}
write_unlock(&ctx->exp_lock);
- if (!found)
- return -EINVAL;
+ if (!found) {
+ ret = -EINVAL;
+ goto err_novchan;
+ }
ret = habmem_hyp_revoke(exp->payload, exp->payload_count);
if (ret) {
pr_err("Error found in revoke grant with ret %d", ret);
- return ret;
+ goto err_novchan;
}
habmem_remove_export(exp);
+
+err_novchan:
+ if (vchan)
+ hab_vchan_put(vchan);
+
return ret;
}
@@ -320,14 +353,24 @@ int hab_mem_import(struct uhab_context *ctx,
{
int ret = 0, found = 0;
struct export_desc *exp = NULL;
+ struct virtual_channel *vchan;
if (!ctx || !param)
return -EINVAL;
+ vchan = hab_get_vchan_fromvcid(param->vcid, ctx);
+ if (!vchan || !vchan->pchan) {
+ ret = -ENODEV;
+ goto err_imp;
+ }
+
spin_lock_bh(&ctx->imp_lock);
list_for_each_entry(exp, &ctx->imp_whse, node) {
if ((exp->export_id == param->exportid) &&
(param->vcid == exp->vcid_remote)) {
+ /* only allow import on the vchan recevied from
+ * remote
+ */
found = 1;
break;
}
@@ -338,27 +381,24 @@ int hab_mem_import(struct uhab_context *ctx,
pr_err("Fail to get export descriptor from export id %d\n",
param->exportid);
ret = -ENODEV;
- return ret;
+ goto err_imp;
}
- pr_debug("call map id: %d pcnt %d remote_dom %d 1st_ref:0x%X\n",
- exp->export_id, exp->payload_count, exp->domid_local,
- *((uint32_t *)exp->payload));
-
ret = habmem_imp_hyp_map(ctx->import_ctx, param, exp, kernel);
if (ret) {
pr_err("Import fail ret:%d pcnt:%d rem:%d 1st_ref:0x%X\n",
ret, exp->payload_count,
exp->domid_local, *((uint32_t *)exp->payload));
- return ret;
+ goto err_imp;
}
exp->import_index = param->index;
exp->kva = kernel ? (void *)param->kva : NULL;
- pr_debug("import index %llx, kva or fd %llx, kernel %d\n",
- exp->import_index, param->kva, kernel);
+err_imp:
+ if (vchan)
+ hab_vchan_put(vchan);
return ret;
}
@@ -369,20 +409,26 @@ int hab_mem_unimport(struct uhab_context *ctx,
{
int ret = 0, found = 0;
struct export_desc *exp = NULL, *exp_tmp;
+ struct virtual_channel *vchan;
if (!ctx || !param)
return -EINVAL;
+ vchan = hab_get_vchan_fromvcid(param->vcid, ctx);
+ if (!vchan || !vchan->pchan) {
+ if (vchan)
+ hab_vchan_put(vchan);
+ return -ENODEV;
+ }
+
spin_lock_bh(&ctx->imp_lock);
list_for_each_entry_safe(exp, exp_tmp, &ctx->imp_whse, node) {
- if ((exp->export_id == param->exportid) &&
- (param->vcid == exp->vcid_remote)) {
+ if (exp->export_id == param->exportid &&
+ param->vcid == exp->vcid_remote) {
+ /* same vchan is expected here */
list_del(&exp->node);
ctx->import_total--;
found = 1;
-
- pr_debug("found id:%d payload cnt:%d kernel:%d\n",
- exp->export_id, exp->payload_count, kernel);
break;
}
}
@@ -391,7 +437,7 @@ int hab_mem_unimport(struct uhab_context *ctx,
if (!found)
ret = -EINVAL;
else {
- ret = habmm_imp_hyp_unmap(ctx->import_ctx, exp);
+ ret = habmm_imp_hyp_unmap(ctx->import_ctx, exp, kernel);
if (ret) {
pr_err("unmap fail id:%d pcnt:%d vcid:%d\n",
exp->export_id, exp->payload_count, exp->vcid_remote);
@@ -400,5 +446,8 @@ int hab_mem_unimport(struct uhab_context *ctx,
kfree(exp);
}
+ if (vchan)
+ hab_vchan_put(vchan);
+
return ret;
}
diff --git a/drivers/soc/qcom/hab/hab_msg.c b/drivers/soc/qcom/hab/hab_msg.c
index d904cdee838c..9d5ee134c94e 100644
--- a/drivers/soc/qcom/hab/hab_msg.c
+++ b/drivers/soc/qcom/hab/hab_msg.c
@@ -78,13 +78,14 @@ hab_msg_dequeue(struct virtual_channel *vchan, struct hab_message **msg,
} else {
pr_err("rcv buffer too small %d < %zd\n",
*rsize, message->sizebytes);
- *rsize = 0;
+ *rsize = message->sizebytes;
message = NULL;
- ret = -EINVAL;
+ ret = -EOVERFLOW; /* come back again */
}
}
spin_unlock_bh(&vchan->rx_lock);
} else
+ /* no message received, retain the original status */
*rsize = 0;
*msg = message;
@@ -142,7 +143,7 @@ static int hab_receive_create_export_ack(struct physical_channel *pchan,
return -ENOMEM;
if (sizeof(ack_recvd->ack) != sizebytes)
- pr_err("exp ack size %lu is not as arrived %zu\n",
+ pr_err("exp ack size %zu is not as arrived %zu\n",
sizeof(ack_recvd->ack), sizebytes);
if (physical_channel_read(pchan,
@@ -150,11 +151,6 @@ static int hab_receive_create_export_ack(struct physical_channel *pchan,
sizebytes) != sizebytes)
return -EIO;
- pr_debug("receive export id %d, local vc %X, vd remote %X\n",
- ack_recvd->ack.export_id,
- ack_recvd->ack.vcid_local,
- ack_recvd->ack.vcid_remote);
-
spin_lock_bh(&ctx->expq_lock);
list_add_tail(&ack_recvd->node, &ctx->exp_rxq);
spin_unlock_bh(&ctx->expq_lock);
@@ -162,10 +158,21 @@ static int hab_receive_create_export_ack(struct physical_channel *pchan,
return 0;
}
-void hab_msg_recv(struct physical_channel *pchan,
+static void hab_msg_drop(struct physical_channel *pchan, size_t sizebytes)
+{
+ uint8_t *data = NULL;
+
+ data = kmalloc(sizebytes, GFP_ATOMIC);
+ if (data == NULL)
+ return;
+ physical_channel_read(pchan, data, sizebytes);
+ kfree(data);
+}
+
+int hab_msg_recv(struct physical_channel *pchan,
struct hab_header *header)
{
- int ret;
+ int ret = 0;
struct hab_message *message;
struct hab_device *dev = pchan->habdev;
size_t sizebytes = HAB_HEADER_GET_SIZE(*header);
@@ -179,7 +186,8 @@ void hab_msg_recv(struct physical_channel *pchan,
/* get the local virtual channel if it isn't an open message */
if (payload_type != HAB_PAYLOAD_TYPE_INIT &&
payload_type != HAB_PAYLOAD_TYPE_INIT_ACK &&
- payload_type != HAB_PAYLOAD_TYPE_ACK) {
+ payload_type != HAB_PAYLOAD_TYPE_INIT_DONE &&
+ payload_type != HAB_PAYLOAD_TYPE_INIT_CANCEL) {
/* sanity check the received message */
if (payload_type >= HAB_PAYLOAD_TYPE_MAX ||
@@ -189,29 +197,42 @@ void hab_msg_recv(struct physical_channel *pchan,
payload_type, vchan_id, sizebytes, session_id);
}
+ /*
+ * need both vcid and session_id to be accurate.
+ * this is from pchan instead of ctx
+ */
vchan = hab_vchan_get(pchan, header);
if (!vchan) {
- pr_debug("vchan is not found, payload type %d, vchan id %x, sizebytes %zx, session %d\n",
+ pr_info("vchan is not found, payload type %d, vchan id %x, sizebytes %zx, session %d\n",
payload_type, vchan_id, sizebytes, session_id);
- if (sizebytes)
- pr_err("message is dropped\n");
-
- return;
+ if (sizebytes) {
+ hab_msg_drop(pchan, sizebytes);
+ pr_err("message %d dropped no vchan, session id %d\n",
+ payload_type, session_id);
+ }
+ return -EINVAL;
} else if (vchan->otherend_closed) {
hab_vchan_put(vchan);
- pr_debug("vchan remote is closed, payload type %d, vchan id %x, sizebytes %zx, session %d\n",
+ pr_info("vchan remote is closed payload type %d, vchan id %x, sizebytes %zx, session %d\n",
payload_type, vchan_id, sizebytes, session_id);
-
- if (sizebytes)
- pr_err("message is dropped\n");
-
- return;
+ if (sizebytes) {
+ hab_msg_drop(pchan, sizebytes);
+ pr_err("message %d dropped remote close, session id %d\n",
+ payload_type, session_id);
+ }
+ return -ENODEV;
}
} else {
if (sizebytes != sizeof(struct hab_open_send_data)) {
- pr_err("Invalid open request received, payload type %d, vchan id %x, sizebytes %zx, session %d\n",
+ pr_err("Invalid open request received type %d, vcid %x, szbytes %zx, session %d\n",
payload_type, vchan_id, sizebytes, session_id);
+ if (sizebytes) {
+ hab_msg_drop(pchan, sizebytes);
+ pr_err("message %d dropped unknown reason, session id %d\n",
+ payload_type, session_id);
+ }
+ return -ENODEV;
}
}
@@ -226,7 +247,7 @@ void hab_msg_recv(struct physical_channel *pchan,
case HAB_PAYLOAD_TYPE_INIT:
case HAB_PAYLOAD_TYPE_INIT_ACK:
- case HAB_PAYLOAD_TYPE_ACK:
+ case HAB_PAYLOAD_TYPE_INIT_DONE:
ret = hab_open_request_add(pchan, sizebytes, payload_type);
if (ret) {
pr_err("open request add failed, ret %d, payload type %d, sizebytes %zx\n",
@@ -236,6 +257,16 @@ void hab_msg_recv(struct physical_channel *pchan,
wake_up_interruptible(&dev->openq);
break;
+ case HAB_PAYLOAD_TYPE_INIT_CANCEL:
+ pr_info("remote open cancel header vcid %X session %d local %d remote %d\n",
+ vchan_id, session_id, pchan->vmid_local,
+ pchan->vmid_remote);
+ ret = hab_open_receive_cancel(pchan, sizebytes);
+ if (ret)
+ pr_err("open cancel handling failed ret %d vcid %X session %d\n",
+ ret, vchan_id, session_id);
+ break;
+
case HAB_PAYLOAD_TYPE_EXPORT:
exp_desc = kzalloc(sizebytes, GFP_ATOMIC);
if (!exp_desc)
@@ -243,7 +274,10 @@ void hab_msg_recv(struct physical_channel *pchan,
if (physical_channel_read(pchan, exp_desc, sizebytes) !=
sizebytes) {
- vfree(exp_desc);
+ pr_err("corrupted exp expect %zd bytes vcid %X remote %X open %d!\n",
+ sizebytes, vchan->id,
+ vchan->otherend_id, vchan->session_id);
+ kfree(exp_desc);
break;
}
@@ -265,36 +299,33 @@ void hab_msg_recv(struct physical_channel *pchan,
case HAB_PAYLOAD_TYPE_CLOSE:
/* remote request close */
- pr_debug("remote side request close\n");
- pr_debug(" vchan id %X, other end %X, session %d\n",
- vchan->id, vchan->otherend_id, session_id);
+ pr_info("remote request close vcid %pK %X other id %X session %d refcnt %d\n",
+ vchan, vchan->id, vchan->otherend_id,
+ session_id, get_refcnt(vchan->refcount));
hab_vchan_stop(vchan);
break;
case HAB_PAYLOAD_TYPE_PROFILE:
do_gettimeofday(&tv);
-
/* pull down the incoming data */
message = hab_msg_alloc(pchan, sizebytes);
- if (!message) {
- pr_err("msg alloc failed\n");
- break;
+ if (!message)
+ pr_err("failed to allocate msg Arrived msg will be lost\n");
+ else {
+ struct habmm_xing_vm_stat *pstat =
+ (struct habmm_xing_vm_stat *)message->data;
+ pstat->rx_sec = tv.tv_sec;
+ pstat->rx_usec = tv.tv_usec;
+ hab_msg_queue(vchan, message);
}
-
- ((uint64_t *)message->data)[2] = tv.tv_sec;
- ((uint64_t *)message->data)[3] = tv.tv_usec;
- hab_msg_queue(vchan, message);
break;
default:
- pr_err("unknown msg is received\n");
- pr_err("payload type %d, vchan id %x\n",
- payload_type, vchan_id);
- pr_err("sizebytes %zx, session %d\n",
- sizebytes, session_id);
-
+ pr_err("unknown msg received, payload type %d, vchan id %x, sizebytes %zx, session %d\n",
+ payload_type, vchan_id, sizebytes, session_id);
break;
}
if (vchan)
hab_vchan_put(vchan);
+ return ret;
}
diff --git a/drivers/soc/qcom/hab/hab_open.c b/drivers/soc/qcom/hab/hab_open.c
index 35f3281604e2..f740a43c1973 100644
--- a/drivers/soc/qcom/hab/hab_open.c
+++ b/drivers/soc/qcom/hab/hab_open.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,6 +12,8 @@
*/
#include "hab.h"
+#define HAB_OPEN_REQ_EXPIRE_TIME_S (3600*10)
+
void hab_open_request_init(struct hab_open_request *request,
int type,
struct physical_channel *pchan,
@@ -21,57 +23,55 @@ void hab_open_request_init(struct hab_open_request *request,
{
request->type = type;
request->pchan = pchan;
- request->vchan_id = vchan_id;
- request->sub_id = sub_id;
- request->open_id = open_id;
+ request->xdata.vchan_id = vchan_id;
+ request->xdata.sub_id = sub_id;
+ request->xdata.open_id = open_id;
}
int hab_open_request_send(struct hab_open_request *request)
{
struct hab_header header = HAB_HEADER_INITIALIZER;
- struct hab_open_send_data data;
HAB_HEADER_SET_SIZE(header, sizeof(struct hab_open_send_data));
HAB_HEADER_SET_TYPE(header, request->type);
- data.vchan_id = request->vchan_id;
- data.open_id = request->open_id;
- data.sub_id = request->sub_id;
-
- return physical_channel_send(request->pchan, &header, &data);
+ return physical_channel_send(request->pchan, &header, &request->xdata);
}
+/* called when remote sends in open-request */
int hab_open_request_add(struct physical_channel *pchan,
size_t sizebytes, int request_type)
{
struct hab_open_node *node;
struct hab_device *dev = pchan->habdev;
- struct hab_open_send_data data;
struct hab_open_request *request;
+ struct timeval tv;
node = kzalloc(sizeof(*node), GFP_ATOMIC);
if (!node)
return -ENOMEM;
- if (physical_channel_read(pchan, &data, sizebytes) != sizebytes)
+ request = &node->request;
+ if (physical_channel_read(pchan, &request->xdata, sizebytes)
+ != sizebytes)
return -EIO;
- request = &node->request;
- request->type = request_type;
- request->pchan = pchan;
- request->vchan_id = data.vchan_id;
- request->sub_id = data.sub_id;
- request->open_id = data.open_id;
- node->age = 0;
+ request->type = request_type;
+ request->pchan = pchan;
+
+ do_gettimeofday(&tv);
+ node->age = tv.tv_sec + HAB_OPEN_REQ_EXPIRE_TIME_S +
+ tv.tv_usec/1000000;
hab_pchan_get(pchan);
spin_lock_bh(&dev->openlock);
list_add_tail(&node->node, &dev->openq_list);
+ dev->openq_cnt++;
spin_unlock_bh(&dev->openlock);
-
return 0;
}
+/* local only */
static int hab_open_request_find(struct uhab_context *ctx,
struct hab_device *dev,
struct hab_open_request *listen,
@@ -79,6 +79,7 @@ static int hab_open_request_find(struct uhab_context *ctx,
{
struct hab_open_node *node, *tmp;
struct hab_open_request *request;
+ struct timeval tv;
int ret = 0;
if (ctx->closing ||
@@ -91,21 +92,27 @@ static int hab_open_request_find(struct uhab_context *ctx,
if (list_empty(&dev->openq_list))
goto done;
+ do_gettimeofday(&tv);
+
list_for_each_entry_safe(node, tmp, &dev->openq_list, node) {
request = (struct hab_open_request *)node;
- if (request->type == listen->type &&
- (request->sub_id == listen->sub_id) &&
- (!listen->open_id ||
- request->open_id == listen->open_id) &&
+ if ((request->type == listen->type ||
+ request->type == HAB_PAYLOAD_TYPE_INIT_CANCEL) &&
+ (request->xdata.sub_id == listen->xdata.sub_id) &&
+ (!listen->xdata.open_id ||
+ request->xdata.open_id == listen->xdata.open_id) &&
(!listen->pchan ||
request->pchan == listen->pchan)) {
list_del(&node->node);
+ dev->openq_cnt--;
*recv_request = request;
ret = 1;
break;
}
- node->age++;
- if (node->age > Q_AGE_THRESHOLD) {
+ if (node->age < (int64_t)tv.tv_sec + tv.tv_usec/1000000) {
+ pr_warn("open request type %d sub %d open %d\n",
+ request->type, request->xdata.sub_id,
+ request->xdata.sub_id);
list_del(&node->node);
hab_open_request_free(request);
}
@@ -121,7 +128,8 @@ void hab_open_request_free(struct hab_open_request *request)
if (request) {
hab_pchan_put(request->pchan);
kfree(request);
- }
+ } else
+ pr_err("empty request found\n");
}
int hab_open_listen(struct uhab_context *ctx,
@@ -132,22 +140,153 @@ int hab_open_listen(struct uhab_context *ctx,
{
int ret = 0;
- if (!ctx || !listen || !recv_request)
+ if (!ctx || !listen || !recv_request) {
+ pr_err("listen failed ctx %pK listen %pK request %pK\n",
+ ctx, listen, recv_request);
return -EINVAL;
+ }
*recv_request = NULL;
- if (ms_timeout > 0) {
+ if (ms_timeout > 0) { /* be case */
+ ms_timeout = msecs_to_jiffies(ms_timeout);
ret = wait_event_interruptible_timeout(dev->openq,
hab_open_request_find(ctx, dev, listen, recv_request),
ms_timeout);
- if (!ret || (-ERESTARTSYS == ret))
- ret = -EAGAIN;
- else if (ret > 0)
- ret = 0;
- } else {
+ if (!ret || (-ERESTARTSYS == ret)) {
+ pr_warn("something failed in open listen ret %d\n",
+ ret);
+ ret = -EAGAIN; /* condition not met */
+ } else if (ret > 0)
+ ret = 0; /* condition met */
+ } else { /* fe case */
ret = wait_event_interruptible(dev->openq,
hab_open_request_find(ctx, dev, listen, recv_request));
+ if (ctx->closing) {
+ pr_warn("local closing during open ret %d\n", ret);
+ ret = -ENODEV;
+ } else if (-ERESTARTSYS == ret) {
+ pr_warn("local interrupted ret %d\n", ret);
+ ret = -EINTR;
+ }
+ }
+
+ return ret;
+}
+
+/* called when receives remote's cancel init from FE or init-ack from BE */
+int hab_open_receive_cancel(struct physical_channel *pchan,
+ size_t sizebytes)
+{
+ struct hab_device *dev = pchan->habdev;
+ struct hab_open_send_data data;
+ struct hab_open_request *request;
+ struct hab_open_node *node, *tmp;
+ int bfound = 0;
+ struct timeval tv;
+
+ if (physical_channel_read(pchan, &data, sizebytes) != sizebytes)
+ return -EIO;
+
+ spin_lock_bh(&dev->openlock);
+ list_for_each_entry_safe(node, tmp, &dev->openq_list, node) {
+ request = &node->request;
+ /* check if open request has been serviced or not */
+ if ((request->type == HAB_PAYLOAD_TYPE_INIT ||
+ request->type == HAB_PAYLOAD_TYPE_INIT_ACK) &&
+ (request->xdata.sub_id == data.sub_id) &&
+ (request->xdata.open_id == data.open_id) &&
+ (request->xdata.vchan_id == data.vchan_id)) {
+ list_del(&node->node);
+ dev->openq_cnt--;
+ pr_info("open cancelled on pchan %s vcid %x subid %d openid %d\n",
+ pchan->name, data.vchan_id,
+ data.sub_id, data.open_id);
+ /* found un-serviced open request, delete it */
+ bfound = 1;
+ break;
+ }
+ }
+ spin_unlock_bh(&dev->openlock);
+
+ if (!bfound) {
+ pr_info("init waiting is in-flight. vcid %x sub %d open %d\n",
+ data.vchan_id, data.sub_id, data.open_id);
+ /* add cancel to the openq to let the waiting open bail out */
+ node = kzalloc(sizeof(*node), GFP_ATOMIC);
+ if (!node)
+ return -ENOMEM;
+
+ request = &node->request;
+ request->type = HAB_PAYLOAD_TYPE_INIT_CANCEL;
+ request->pchan = pchan;
+ request->xdata.vchan_id = data.vchan_id;
+ request->xdata.sub_id = data.sub_id;
+ request->xdata.open_id = data.open_id;
+
+ do_gettimeofday(&tv);
+ node->age = tv.tv_sec + HAB_OPEN_REQ_EXPIRE_TIME_S +
+ tv.tv_usec/1000000;
+ /* put when this node is handled in open path */
+ hab_pchan_get(pchan);
+
+ spin_lock_bh(&dev->openlock);
+ list_add_tail(&node->node, &dev->openq_list);
+ dev->openq_cnt++;
+ spin_unlock_bh(&dev->openlock);
+
+ wake_up_interruptible(&dev->openq);
+ }
+
+ return 0;
+}
+
+/* calls locally to send cancel pending open to remote */
+int hab_open_cancel_notify(struct hab_open_request *request)
+{
+ struct hab_header header = HAB_HEADER_INITIALIZER;
+
+ HAB_HEADER_SET_SIZE(header, sizeof(struct hab_open_send_data));
+ HAB_HEADER_SET_TYPE(header, HAB_PAYLOAD_TYPE_INIT_CANCEL);
+
+ return physical_channel_send(request->pchan, &header, &request->xdata);
+}
+
+int hab_open_pending_enter(struct uhab_context *ctx,
+ struct physical_channel *pchan,
+ struct hab_open_node *pending)
+{
+ write_lock(&ctx->ctx_lock);
+ list_add_tail(&pending->node, &ctx->pending_open);
+ ctx->pending_cnt++;
+ write_unlock(&ctx->ctx_lock);
+
+ return 0;
+}
+
+int hab_open_pending_exit(struct uhab_context *ctx,
+ struct physical_channel *pchan,
+ struct hab_open_node *pending)
+{
+ struct hab_open_node *node, *tmp;
+ int ret = -ENOENT;
+
+ write_lock(&ctx->ctx_lock);
+ list_for_each_entry_safe(node, tmp, &ctx->pending_open, node) {
+ if ((node->request.type == pending->request.type) &&
+ (node->request.pchan
+ == pending->request.pchan) &&
+ (node->request.xdata.vchan_id
+ == pending->request.xdata.vchan_id) &&
+ (node->request.xdata.sub_id
+ == pending->request.xdata.sub_id) &&
+ (node->request.xdata.open_id
+ == pending->request.xdata.open_id)) {
+ list_del(&node->node);
+ ctx->pending_cnt--;
+ ret = 0;
+ }
}
+ write_unlock(&ctx->ctx_lock);
return ret;
}
diff --git a/drivers/soc/qcom/hab/hab_parser.c b/drivers/soc/qcom/hab/hab_parser.c
index da0a4a3830a7..c332587e2b47 100644
--- a/drivers/soc/qcom/hab/hab_parser.c
+++ b/drivers/soc/qcom/hab/hab_parser.c
@@ -30,7 +30,7 @@ static int fill_vmid_mmid_tbl(struct vmid_mmid_desc *tbl, int32_t vm_start,
for (j = mmid_start; j < mmid_start + mmid_range; j++) {
/* sanity check */
if (tbl[i].mmid[j] != HABCFG_VMID_INVALID) {
- pr_err("overwrite previous setting, i %d, j %d, be %d\n",
+ pr_err("overwrite previous setting vmid %d, mmid %d, be %d\n",
i, j, tbl[i].is_listener[j]);
}
tbl[i].mmid[j] = j;
@@ -43,28 +43,23 @@ static int fill_vmid_mmid_tbl(struct vmid_mmid_desc *tbl, int32_t vm_start,
void dump_settings(struct local_vmid *settings)
{
- int i, j;
-
pr_debug("self vmid is %d\n", settings->self);
- for (i = 0; i < HABCFG_VMID_MAX; i++) {
- pr_debug("remote vmid %d\n",
- settings->vmid_mmid_list[i].vmid);
- for (j = 0; j <= HABCFG_MMID_AREA_MAX; j++) {
- pr_debug("mmid %d, is_be %d\n",
- settings->vmid_mmid_list[i].mmid[j],
- settings->vmid_mmid_list[i].is_listener[j]);
- }
- }
}
int fill_default_gvm_settings(struct local_vmid *settings, int vmid_local,
- int mmid_start, int mmid_end) {
+ int mmid_start, int mmid_end)
+{
+ int32_t be = HABCFG_BE_FALSE;
+ int32_t range = 1;
+ int32_t vmremote = 0; /* default to host[0] as local is guest[2] */
+
settings->self = vmid_local;
/* default gvm always talks to host as vm0 */
- return fill_vmid_mmid_tbl(settings->vmid_mmid_list, 0, 1,
- mmid_start/100, (mmid_end-mmid_start)/100+1, HABCFG_BE_FALSE);
+ return fill_vmid_mmid_tbl(settings->vmid_mmid_list, vmremote, range,
+ mmid_start/100, (mmid_end-mmid_start)/100+1, be);
}
+/* device tree based parser */
static int hab_parse_dt(struct local_vmid *settings)
{
int result, i;
@@ -151,6 +146,10 @@ static int hab_parse_dt(struct local_vmid *settings)
return 0;
}
+/*
+ * 0: successful
+ * negative: various failure core
+ */
int hab_parse(struct local_vmid *settings)
{
int ret;
diff --git a/drivers/soc/qcom/hab/hab_pchan.c b/drivers/soc/qcom/hab/hab_pchan.c
index 36bc29b7bd0c..8a9a6dfd1e0c 100644
--- a/drivers/soc/qcom/hab/hab_pchan.c
+++ b/drivers/soc/qcom/hab/hab_pchan.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -35,10 +35,10 @@ hab_pchan_alloc(struct hab_device *habdev, int otherend_id)
rwlock_init(&pchan->vchans_lock);
spin_lock_init(&pchan->rxbuf_lock);
- mutex_lock(&habdev->pchan_lock);
+ spin_lock_bh(&habdev->pchan_lock);
list_add_tail(&pchan->node, &habdev->pchannels);
habdev->pchan_cnt++;
- mutex_unlock(&habdev->pchan_lock);
+ spin_unlock_bh(&habdev->pchan_lock);
return pchan;
}
@@ -47,11 +47,26 @@ static void hab_pchan_free(struct kref *ref)
{
struct physical_channel *pchan =
container_of(ref, struct physical_channel, refcount);
+ struct virtual_channel *vchan;
- mutex_lock(&pchan->habdev->pchan_lock);
+ pr_debug("pchan %s refcnt %d\n", pchan->name,
+ get_refcnt(pchan->refcount));
+
+ spin_lock_bh(&pchan->habdev->pchan_lock);
list_del(&pchan->node);
pchan->habdev->pchan_cnt--;
- mutex_unlock(&pchan->habdev->pchan_lock);
+ spin_unlock_bh(&pchan->habdev->pchan_lock);
+
+ /* check vchan leaking */
+ read_lock(&pchan->vchans_lock);
+ list_for_each_entry(vchan, &pchan->vchannels, pnode) {
+ /* no logging on the owner. it might have been gone */
+ pr_warn("leaking vchan id %X remote %X refcnt %d\n",
+ vchan->id, vchan->otherend_id,
+ get_refcnt(vchan->refcount));
+ }
+ read_unlock(&pchan->vchans_lock);
+
kfree(pchan->hyp_data);
kfree(pchan);
}
@@ -61,7 +76,7 @@ hab_pchan_find_domid(struct hab_device *dev, int dom_id)
{
struct physical_channel *pchan;
- mutex_lock(&dev->pchan_lock);
+ spin_lock_bh(&dev->pchan_lock);
list_for_each_entry(pchan, &dev->pchannels, node)
if (pchan->dom_id == dom_id || dom_id == HABCFG_VMID_DONT_CARE)
break;
@@ -75,7 +90,7 @@ hab_pchan_find_domid(struct hab_device *dev, int dom_id)
if (pchan && !kref_get_unless_zero(&pchan->refcount))
pchan = NULL;
- mutex_unlock(&dev->pchan_lock);
+ spin_unlock_bh(&dev->pchan_lock);
return pchan;
}
diff --git a/drivers/soc/qcom/hab/hab_qvm.c b/drivers/soc/qcom/hab/hab_qvm.c
index 9aa41320a33f..129d1deeb2f0 100644
--- a/drivers/soc/qcom/hab/hab_qvm.c
+++ b/drivers/soc/qcom/hab/hab_qvm.c
@@ -71,14 +71,14 @@ static struct qvm_plugin_info {
static irqreturn_t shm_irq_handler(int irq, void *_pchan)
{
irqreturn_t rc = IRQ_NONE;
- struct physical_channel *pchan = _pchan;
+ struct physical_channel *pchan = (struct physical_channel *) _pchan;
struct qvm_channel *dev =
(struct qvm_channel *) (pchan ? pchan->hyp_data : NULL);
if (dev && dev->guest_ctrl) {
int status = dev->guest_ctrl->status;
- if (status & dev->idx) {
+ if (status & 0xffff) {/*source bitmask indicator*/
rc = IRQ_HANDLED;
tasklet_schedule(&dev->task);
}
@@ -95,13 +95,14 @@ static uint64_t get_guest_factory_paddr(struct qvm_channel *dev,
int i;
pr_debug("name = %s, factory paddr = 0x%lx, irq %d, pages %d\n",
- name, factory_addr, irq, pages);
+ name, factory_addr, irq, pages);
dev->guest_factory = (struct guest_shm_factory *)factory_addr;
if (dev->guest_factory->signature != GUEST_SHM_SIGNATURE) {
pr_err("signature error: %ld != %llu, factory addr %lx\n",
GUEST_SHM_SIGNATURE, dev->guest_factory->signature,
factory_addr);
+ iounmap(dev->guest_factory);
return 0;
}
@@ -120,6 +121,7 @@ static uint64_t get_guest_factory_paddr(struct qvm_channel *dev,
/* See if we successfully created/attached to the region. */
if (dev->guest_factory->status != GSS_OK) {
pr_err("create failed: %d\n", dev->guest_factory->status);
+ iounmap(dev->guest_factory);
return 0;
}
@@ -180,6 +182,7 @@ int habhyp_commdev_alloc(void **commdev, int is_be, char *name,
{
struct qvm_channel *dev = NULL;
struct qvm_plugin_info *qvm_priv = hab_driver.hyp_priv;
+ uint64_t paddr;
struct physical_channel **pchan = (struct physical_channel **)commdev;
int ret = 0, coid = 0, channel = 0;
char *shmdata;
@@ -187,7 +190,6 @@ int habhyp_commdev_alloc(void **commdev, int is_be, char *name,
hab_pipe_calc_required_bytes(PIPE_SHMEM_SIZE);
uint32_t pipe_alloc_pages =
(pipe_alloc_size + PAGE_SIZE - 1) / PAGE_SIZE;
- uint64_t paddr;
int temp;
int total_pages;
struct page **pages;
@@ -196,8 +198,10 @@ int habhyp_commdev_alloc(void **commdev, int is_be, char *name,
pipe_alloc_size);
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err;
+ }
spin_lock_init(&dev->io_lock);
@@ -208,7 +212,7 @@ int habhyp_commdev_alloc(void **commdev, int is_be, char *name,
pipe_alloc_pages);
qvm_priv->curr++;
if (qvm_priv->curr > qvm_priv->probe_cnt) {
- pr_err("factory setting %d overflow probed cnt %d\n",
+ pr_err("pchan guest factory setting %d overflow probed cnt %d\n",
qvm_priv->curr, qvm_priv->probe_cnt);
ret = -1;
goto err;
@@ -261,17 +265,18 @@ int habhyp_commdev_alloc(void **commdev, int is_be, char *name,
dev->coid = coid;
ret = create_dispatcher(*pchan);
- if (ret)
+ if (ret < 0)
goto err;
return ret;
err:
+ pr_err("habhyp_commdev_alloc failed\n");
+
kfree(dev);
if (*pchan)
hab_pchan_put(*pchan);
- pr_err("habhyp_commdev_alloc failed: %d\n", ret);
return ret;
}
@@ -280,6 +285,13 @@ int habhyp_commdev_dealloc(void *commdev)
struct physical_channel *pchan = (struct physical_channel *)commdev;
struct qvm_channel *dev = pchan->hyp_data;
+ dev->guest_ctrl->detach = 0;
+
+ if (get_refcnt(pchan->refcount) > 1) {
+ pr_warn("potential leak pchan %s vchans %d refcnt %d\n",
+ pchan->name, pchan->vcnt,
+ get_refcnt(pchan->refcount));
+ }
kfree(dev);
hab_pchan_put(pchan);
@@ -302,25 +314,13 @@ int hab_hypervisor_register(void)
void hab_hypervisor_unregister(void)
{
- int status, i;
-
- for (i = 0; i < hab_driver.ndevices; i++) {
- struct hab_device *dev = &hab_driver.devp[i];
- struct physical_channel *pchan;
-
- list_for_each_entry(pchan, &dev->pchannels, node) {
- status = habhyp_commdev_dealloc(pchan);
- if (status) {
- pr_err("failed to free pchan %pK, i %d, ret %d\n",
- pchan, i, status);
- }
- }
- }
+ hab_hypervisor_unregister_common();
qvm_priv_info.probe_cnt = 0;
qvm_priv_info.curr = 0;
}
+/* this happens before hypervisor register */
static int hab_shmem_probe(struct platform_device *pdev)
{
int irq = 0;
@@ -373,19 +373,6 @@ static int hab_shmem_remove(struct platform_device *pdev)
static void hab_shmem_shutdown(struct platform_device *pdev)
{
- int i;
- struct qvm_channel *dev;
- struct physical_channel *pchan;
- struct hab_device *hab_dev;
-
- for (i = 0; i < hab_driver.ndevices; i++) {
- hab_dev = &hab_driver.devp[i];
- pr_debug("detaching %s\n", hab_dev->name);
- list_for_each_entry(pchan, &hab_dev->pchannels, node) {
- dev = (struct qvm_channel *)pchan->hyp_data;
- dev->guest_ctrl->detach = 0;
- }
- }
}
static const struct of_device_id hab_shmem_match_table[] = {
diff --git a/drivers/soc/qcom/hab/hab_qvm.h b/drivers/soc/qcom/hab/hab_qvm.h
index b483f4c21331..fe7cb0bbda0a 100644
--- a/drivers/soc/qcom/hab/hab_qvm.h
+++ b/drivers/soc/qcom/hab/hab_qvm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -36,6 +36,7 @@ struct qvm_channel {
int channel;
int coid;
+ /* Guest VM */
unsigned int guest_intr;
unsigned int guest_iid;
unsigned int factory_addr;
diff --git a/drivers/soc/qcom/hab/hab_vchan.c b/drivers/soc/qcom/hab/hab_vchan.c
index 2db4db8f321b..d127bcca19f8 100644
--- a/drivers/soc/qcom/hab/hab_vchan.c
+++ b/drivers/soc/qcom/hab/hab_vchan.c
@@ -13,7 +13,8 @@
#include "hab.h"
struct virtual_channel *
-hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan)
+hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan,
+ int openid)
{
int id;
struct virtual_channel *vchan;
@@ -28,11 +29,13 @@ hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan)
/* This should be the first thing we do in this function */
idr_preload(GFP_KERNEL);
spin_lock_bh(&pchan->vid_lock);
- id = idr_alloc(&pchan->vchan_idr, vchan, 1, 256, GFP_NOWAIT);
+ id = idr_alloc(&pchan->vchan_idr, vchan, 1,
+ (HAB_VCID_ID_MASK >> HAB_VCID_ID_SHIFT) + 1, GFP_NOWAIT);
spin_unlock_bh(&pchan->vid_lock);
idr_preload_end();
- if (id < 0) {
+ if (id <= 0) {
+ pr_err("idr failed %d\n", id);
kfree(vchan);
return NULL;
}
@@ -40,8 +43,11 @@ hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan)
hab_pchan_get(pchan);
vchan->pchan = pchan;
+ /* vchan need both vcid and openid to be properly located */
+ vchan->session_id = openid;
write_lock(&pchan->vchans_lock);
list_add_tail(&vchan->pnode, &pchan->vchannels);
+ pchan->vcnt++;
write_unlock(&pchan->vchans_lock);
vchan->id = ((id << HAB_VCID_ID_SHIFT) & HAB_VCID_ID_MASK) |
((pchan->habdev->id << HAB_VCID_MMID_SHIFT) &
@@ -53,7 +59,7 @@ hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan)
init_waitqueue_head(&vchan->rx_queue);
kref_init(&vchan->refcount);
- kref_init(&vchan->usagecnt);
+
vchan->otherend_closed = pchan->closed;
hab_ctx_get(ctx);
@@ -65,11 +71,9 @@ hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan)
static void
hab_vchan_free(struct kref *ref)
{
- int found;
struct virtual_channel *vchan =
container_of(ref, struct virtual_channel, refcount);
struct hab_message *message, *msg_tmp;
- struct export_desc *exp, *exp_tmp;
struct physical_channel *pchan = vchan->pchan;
struct uhab_context *ctx = vchan->ctx;
struct virtual_channel *vc, *vc_tmp;
@@ -81,73 +85,84 @@ hab_vchan_free(struct kref *ref)
}
spin_unlock_bh(&vchan->rx_lock);
- do {
- found = 0;
- write_lock(&ctx->exp_lock);
- list_for_each_entry_safe(exp, exp_tmp, &ctx->exp_whse, node) {
- if (exp->vcid_local == vchan->id) {
- list_del(&exp->node);
- found = 1;
- break;
- }
- }
- write_unlock(&ctx->exp_lock);
- if (found) {
- habmem_hyp_revoke(exp->payload, exp->payload_count);
- habmem_remove_export(exp);
- }
- } while (found);
-
- do {
- found = 0;
- spin_lock_bh(&ctx->imp_lock);
- list_for_each_entry_safe(exp, exp_tmp, &ctx->imp_whse, node) {
- if (exp->vcid_remote == vchan->id) {
- list_del(&exp->node);
- found = 1;
- break;
- }
- }
- spin_unlock_bh(&ctx->imp_lock);
- if (found) {
- habmm_imp_hyp_unmap(ctx->import_ctx, exp);
- ctx->import_total--;
- kfree(exp);
- }
- } while (found);
-
- spin_lock_bh(&pchan->vid_lock);
- idr_remove(&pchan->vchan_idr, HAB_VCID_GET_ID(vchan->id));
- spin_unlock_bh(&pchan->vid_lock);
+ /* the release vchan from ctx was done earlier in vchan close() */
+ hab_ctx_put(ctx); /* now ctx is not needed from this vchan's view */
+ vchan->ctx = NULL;
+ /* release vchan from pchan. no more msg for this vchan */
write_lock(&pchan->vchans_lock);
list_for_each_entry_safe(vc, vc_tmp, &pchan->vchannels, pnode) {
if (vchan == vc) {
list_del(&vc->pnode);
+ /* the ref is held in case of pchan is freed */
+ pchan->vcnt--;
break;
}
}
write_unlock(&pchan->vchans_lock);
- hab_pchan_put(pchan);
- hab_ctx_put(ctx);
+ /* release idr at the last so same idr will not be used early */
+ spin_lock_bh(&pchan->vid_lock);
+ idr_remove(&pchan->vchan_idr, HAB_VCID_GET_ID(vchan->id));
+ spin_unlock_bh(&pchan->vid_lock);
+
+ hab_pchan_put(pchan); /* no more need for pchan from this vchan */
kfree(vchan);
}
+/*
+ * only for msg recv path to retrieve vchan from vcid and openid based on
+ * pchan's vchan list
+ */
struct virtual_channel*
hab_vchan_get(struct physical_channel *pchan, struct hab_header *header)
{
struct virtual_channel *vchan;
uint32_t vchan_id = HAB_HEADER_GET_ID(*header);
uint32_t session_id = HAB_HEADER_GET_SESSION_ID(*header);
+ size_t sizebytes = HAB_HEADER_GET_SIZE(*header);
+ uint32_t payload_type = HAB_HEADER_GET_TYPE(*header);
spin_lock_bh(&pchan->vid_lock);
vchan = idr_find(&pchan->vchan_idr, HAB_VCID_GET_ID(vchan_id));
- if (vchan)
- if ((vchan->session_id != session_id) ||
- (!kref_get_unless_zero(&vchan->refcount)))
+ if (vchan) {
+ if (vchan->session_id != session_id)
+ /*
+ * skipped if session is different even vcid
+ * is the same
+ */
+ vchan = NULL;
+ else if (!vchan->otherend_id /*&& !vchan->session_id*/) {
+ /*
+ * not paired vchan can be fetched right after it is
+ * alloc'ed. so it has to be skipped during search
+ * for remote msg
+ */
+ pr_warn("vcid %x is not paired yet session %d refcnt %d type %d sz %zd\n",
+ vchan->id, vchan->otherend_id,
+ get_refcnt(vchan->refcount),
+ payload_type, sizebytes);
+ vchan = NULL;
+ } else if (!kref_get_unless_zero(&vchan->refcount)) {
+ /*
+ * this happens when refcnt is already zero
+ * (put from other thread) or there is an actual error
+ */
+ pr_err("failed to inc vcid %pK %x remote %x session %d refcnt %d header %x session %d type %d sz %zd\n",
+ vchan, vchan->id, vchan->otherend_id,
+ vchan->session_id, get_refcnt(vchan->refcount),
+ vchan_id, session_id, payload_type, sizebytes);
+ vchan = NULL;
+ } else if (vchan->otherend_closed || vchan->closed) {
+ pr_err("closed already remote %d local %d vcid %x remote %x session %d refcnt %d header %x session %d type %d sz %zd\n",
+ vchan->otherend_closed, vchan->closed,
+ vchan->id, vchan->otherend_id,
+ vchan->session_id, get_refcnt(vchan->refcount),
+ vchan_id, session_id, payload_type, sizebytes);
vchan = NULL;
+ }
+ }
spin_unlock_bh(&pchan->vid_lock);
return vchan;
@@ -158,6 +173,7 @@ void hab_vchan_stop(struct virtual_channel *vchan)
if (vchan) {
vchan->otherend_closed = 1;
wake_up(&vchan->rx_queue);
+ wake_up_interruptible(&vchan->ctx->exp_wq);
}
}
@@ -184,23 +200,36 @@ int hab_vchan_find_domid(struct virtual_channel *vchan)
return vchan ? vchan->pchan->dom_id : -1;
}
-static void
-hab_vchan_free_deferred(struct work_struct *work)
-{
- struct virtual_channel *vchan =
- container_of(work, struct virtual_channel, work);
-
- hab_vchan_free(&vchan->refcount);
-}
-
-static void
-hab_vchan_schedule_free(struct kref *ref)
+/* this sould be only called once after refcnt is zero */
+static void hab_vchan_schedule_free(struct kref *ref)
{
- struct virtual_channel *vchan =
+ struct virtual_channel *vchanin =
container_of(ref, struct virtual_channel, refcount);
+ struct uhab_context *ctx = vchanin->ctx;
+ struct virtual_channel *vchan, *tmp;
+ int bnotify = 0;
+
+ /*
+ * similar logic is in ctx free. if ctx free runs first,
+ * this is skipped
+ */
+ write_lock(&ctx->ctx_lock);
+ list_for_each_entry_safe(vchan, tmp, &ctx->vchannels, node) {
+ if (vchan == vchanin) {
+ pr_debug("vchan free refcnt = %d\n",
+ get_refcnt(vchan->refcount));
+ ctx->vcnt--;
+ list_del(&vchan->node);
+ bnotify = 1;
+ break;
+ }
+ }
+ write_unlock(&ctx->ctx_lock);
- INIT_WORK(&vchan->work, hab_vchan_free_deferred);
- schedule_work(&vchan->work);
+ if (bnotify)
+ hab_vchan_stop_notify(vchan);
+
+ hab_vchan_free(ref);
}
void hab_vchan_put(struct virtual_channel *vchan)
@@ -210,17 +239,23 @@ void hab_vchan_put(struct virtual_channel *vchan)
}
int hab_vchan_query(struct uhab_context *ctx, int32_t vcid, uint64_t *ids,
- char *names, size_t name_size, uint32_t flags)
+ char *names, size_t name_size, uint32_t flags)
{
struct virtual_channel *vchan = hab_get_vchan_fromvcid(vcid, ctx);
+ if (!vchan)
+ return -EINVAL;
- if (!vchan || vchan->otherend_closed)
+ if (vchan->otherend_closed) {
+ hab_vchan_put(vchan);
return -ENODEV;
+ }
*ids = vchan->pchan->vmid_local |
((uint64_t)vchan->pchan->vmid_remote) << 32;
names[0] = 0;
names[name_size/2] = 0;
+ hab_vchan_put(vchan);
+
return 0;
}
diff --git a/drivers/soc/qcom/hab/khab.c b/drivers/soc/qcom/hab/khab.c
index ba77e5e9cca2..c4acf12fd553 100644
--- a/drivers/soc/qcom/hab/khab.c
+++ b/drivers/soc/qcom/hab/khab.c
@@ -10,13 +10,14 @@
* GNU General Public License for more details.
*
*/
-#include <linux/module.h>
#include "hab.h"
+#include <linux/module.h>
int32_t habmm_socket_open(int32_t *handle, uint32_t mm_ip_id,
uint32_t timeout, uint32_t flags)
{
- return hab_vchan_open(hab_driver.kctx, mm_ip_id, handle, flags);
+ return hab_vchan_open(hab_driver.kctx, mm_ip_id, handle,
+ timeout, flags);
}
EXPORT_SYMBOL(habmm_socket_open);
@@ -55,6 +56,9 @@ int32_t habmm_socket_recv(int32_t handle, void *dst_buff, uint32_t *size_bytes,
if (ret == 0 && msg)
memcpy(dst_buff, msg->data, msg->sizebytes);
+ else if (ret && msg)
+ pr_warn("vcid %X recv failed %d but msg is still received %zd bytes\n",
+ handle, ret, msg->sizebytes);
if (msg)
hab_msg_free(msg);
diff --git a/drivers/soc/qcom/hab/khab_test.c b/drivers/soc/qcom/hab/khab_test.c
index 3773211aeec7..7d6df8861421 100644
--- a/drivers/soc/qcom/hab/khab_test.c
+++ b/drivers/soc/qcom/hab/khab_test.c
@@ -13,7 +13,9 @@
#include "hab.h"
#include "khab_test.h"
#include "hab_pipe.h"
+#ifdef CONFIG_MSM_GVM_QUIN
#include "hab_qvm.h"
+#endif
#include <asm/cacheflush.h>
#include <linux/list.h>
@@ -30,8 +32,10 @@ enum hab_perf_test_type {
static int hab_shmm_throughput_test(void)
{
struct hab_device *habDev;
+#ifdef CONFIG_MSM_GVM_QUIN
struct qvm_channel *dev;
- struct hab_shared_buf *sh_buf;
+#endif
+ struct hab_shared_buf *sh_buf = NULL;
struct physical_channel *pchan;
struct timeval tv1, tv2;
int i, counter;
@@ -40,7 +44,7 @@ static int hab_shmm_throughput_test(void)
register int sum;
register int *pp, *lastone;
- int throughput[3][2] = {0};
+ int throughput[3][2] = { {0} };
int latency[6][PERF_TEST_ITERATION];
int ret = 0, tmp, size;
@@ -52,6 +56,7 @@ static int hab_shmm_throughput_test(void)
pchan = list_first_entry(&(habDev->pchannels),
struct physical_channel, node);
+#ifdef CONFIG_MSM_GVM_QUIN
dev = pchan->hyp_data;
if (!dev) {
ret = -EPERM;
@@ -59,6 +64,8 @@ static int hab_shmm_throughput_test(void)
}
sh_buf = dev->pipe_ep->tx_info.sh_buf;
+#endif
+
/* pChannel is of 128k, we use 64k to test */
size = 0x10000;
diff --git a/drivers/soc/qcom/hab/qvm_comm.c b/drivers/soc/qcom/hab/qvm_comm.c
index 41e34be9ac21..04381232b26a 100644
--- a/drivers/soc/qcom/hab/qvm_comm.c
+++ b/drivers/soc/qcom/hab/qvm_comm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -43,7 +43,6 @@ int physical_channel_send(struct physical_channel *pchan,
int sizebytes = HAB_HEADER_GET_SIZE(*header);
struct qvm_channel *dev = (struct qvm_channel *)pchan->hyp_data;
int total_size = sizeof(*header) + sizebytes;
- struct timeval tv;
if (total_size > dev->pipe_ep->tx_info.sh_buf->size)
return -EINVAL; /* too much data for ring */
@@ -67,9 +66,13 @@ int physical_channel_send(struct physical_channel *pchan,
}
if (HAB_HEADER_GET_TYPE(*header) == HAB_PAYLOAD_TYPE_PROFILE) {
+ struct timeval tv;
+ struct habmm_xing_vm_stat *pstat =
+ (struct habmm_xing_vm_stat *)payload;
+
do_gettimeofday(&tv);
- ((uint64_t *)payload)[0] = tv.tv_sec;
- ((uint64_t *)payload)[1] = tv.tv_usec;
+ pstat->tx_sec = tv.tv_sec;
+ pstat->tx_usec = tv.tv_usec;
}
if (sizebytes) {
@@ -102,7 +105,7 @@ void physical_channel_rx_dispatch(unsigned long data)
break; /* no data available */
if (header.signature != HAB_HEAD_SIGNATURE) {
- pr_err("HAB signature mismatch, expect %X, received %X, id_type_size %X, session %X, sequence %X\n",
+ pr_err("HAB signature mismatch expect %X received %X, id_type_size %X session %X sequence %X\n",
HAB_HEAD_SIGNATURE, header.signature,
header.id_type_size,
header.session_id,
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 7288c79de428..c41050adae5a 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c
@@ -1038,10 +1038,8 @@ static struct device *msm_bus_device_init(
bus_node = kzalloc(sizeof(struct msm_bus_node_device_type), GFP_KERNEL);
if (!bus_node) {
- MSM_BUS_ERR("%s:Bus node alloc failed\n", __func__);
- kfree(bus_dev);
- bus_dev = NULL;
- goto exit_device_init;
+ ret = -ENOMEM;
+ goto err_device_init;
}
bus_dev = &bus_node->dev;
device_initialize(bus_dev);
@@ -1049,47 +1047,37 @@ static struct device *msm_bus_device_init(
node_info = devm_kzalloc(bus_dev,
sizeof(struct msm_bus_node_info_type), GFP_KERNEL);
if (!node_info) {
- MSM_BUS_ERR("%s:Bus node info alloc failed\n", __func__);
- devm_kfree(bus_dev, bus_node);
- kfree(bus_dev);
- bus_dev = NULL;
- goto exit_device_init;
+ ret = -ENOMEM;
+ goto err_put_device;
}
bus_node->node_info = node_info;
bus_node->ap_owned = pdata->ap_owned;
bus_dev->of_node = pdata->of_node;
- if (msm_bus_copy_node_info(pdata, bus_dev) < 0) {
- devm_kfree(bus_dev, bus_node);
- devm_kfree(bus_dev, node_info);
- kfree(bus_dev);
- bus_dev = NULL;
- goto exit_device_init;
- }
+ ret = msm_bus_copy_node_info(pdata, bus_dev);
+ if (ret)
+ goto err_put_device;
bus_dev->bus = &msm_bus_type;
dev_set_name(bus_dev, bus_node->node_info->name);
ret = device_add(bus_dev);
- if (ret < 0) {
+ if (ret) {
MSM_BUS_ERR("%s: Error registering device %d",
__func__, pdata->node_info->id);
- devm_kfree(bus_dev, bus_node);
- devm_kfree(bus_dev, node_info->dev_connections);
- devm_kfree(bus_dev, node_info->connections);
- devm_kfree(bus_dev, node_info->black_connections);
- devm_kfree(bus_dev, node_info->black_listed_connections);
- devm_kfree(bus_dev, node_info);
- kfree(bus_dev);
- bus_dev = NULL;
- goto exit_device_init;
+ goto err_put_device;
}
device_create_file(bus_dev, &dev_attr_bw);
INIT_LIST_HEAD(&bus_node->devlist);
-
-exit_device_init:
return bus_dev;
+
+err_put_device:
+ put_device(bus_dev);
+ bus_dev = NULL;
+ kfree(bus_node);
+err_device_init:
+ return ERR_PTR(ret);
}
static int msm_bus_setup_dev_conn(struct device *bus_dev, void *data)
@@ -1284,10 +1272,10 @@ static int msm_bus_device_probe(struct platform_device *pdev)
node_dev = msm_bus_device_init(&pdata->info[i]);
- if (!node_dev) {
+ if (IS_ERR(node_dev)) {
MSM_BUS_ERR("%s: Error during dev init for %d",
__func__, pdata->info[i].node_info->id);
- ret = -ENXIO;
+ ret = PTR_ERR(node_dev);
goto exit_device_probe;
}
diff --git a/drivers/soc/qcom/msm_smem.c b/drivers/soc/qcom/msm_smem.c
index a94f741c2056..cf3e0e084ab4 100644
--- a/drivers/soc/qcom/msm_smem.c
+++ b/drivers/soc/qcom/msm_smem.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -26,6 +26,7 @@
#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/ramdump.h>
+#include <soc/qcom/scm.h>
#include <soc/qcom/smem.h>
@@ -1085,12 +1086,15 @@ static __init int modem_restart_late_init(void)
void *handle;
struct restart_notifier_block *nb;
- if (smem_dev)
- smem_ramdump_dev = create_ramdump_device("smem", smem_dev);
- if (IS_ERR_OR_NULL(smem_ramdump_dev)) {
- LOG_ERR("%s: Unable to create smem ramdump device.\n",
- __func__);
- smem_ramdump_dev = NULL;
+ if (scm_is_secure_device()) {
+ if (smem_dev)
+ smem_ramdump_dev = create_ramdump_device("smem",
+ smem_dev);
+ if (IS_ERR_OR_NULL(smem_ramdump_dev)) {
+ LOG_ERR("%s: Unable to create smem ramdump device.\n",
+ __func__);
+ smem_ramdump_dev = NULL;
+ }
}
for (i = 0; i < ARRAY_SIZE(restart_notifiers); i++) {
diff --git a/drivers/soc/qcom/qdsp6v2/apr.c b/drivers/soc/qcom/qdsp6v2/apr.c
index fefc348c0027..8cd86915be98 100644
--- a/drivers/soc/qcom/qdsp6v2/apr.c
+++ b/drivers/soc/qcom/qdsp6v2/apr.c
@@ -45,7 +45,8 @@ static void *apr_pkt_ctx;
static wait_queue_head_t dsp_wait;
static wait_queue_head_t modem_wait;
static bool is_modem_up;
-static bool is_initial_boot;
+static bool is_initial_modem_boot;
+static bool is_initial_adsp_boot;
/* Subsystem restart: QDSP6 data, functions */
static struct workqueue_struct *apr_reset_workqueue;
static void apr_reset_deregister(struct work_struct *work);
@@ -909,21 +910,28 @@ static int apr_notifier_service_cb(struct notifier_block *this,
* recovery notifications during initial boot
* up since everything is expected to be down.
*/
- if (is_initial_boot) {
- is_initial_boot = false;
- break;
- }
- if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN)
+ if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN) {
+ if (is_initial_modem_boot) {
+ is_initial_modem_boot = false;
+ break;
+ }
apr_modem_down(opcode);
- else
+ } else {
+ if (is_initial_adsp_boot) {
+ is_initial_adsp_boot = false;
+ break;
+ }
apr_adsp_down(opcode);
+ }
break;
case AUDIO_NOTIFIER_SERVICE_UP:
- is_initial_boot = false;
- if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN)
+ if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN) {
+ is_initial_modem_boot = false;
apr_modem_up();
- else
+ } else {
+ is_initial_adsp_boot = false;
apr_adsp_up();
+ }
break;
default:
break;
@@ -965,7 +973,8 @@ static int __init apr_init(void)
if (!apr_pkt_ctx)
pr_err("%s: Unable to create ipc log context\n", __func__);
- is_initial_boot = true;
+ is_initial_modem_boot = true;
+ is_initial_adsp_boot = true;
subsys_notif_register("apr_adsp", AUDIO_NOTIFIER_ADSP_DOMAIN,
&adsp_service_nb);
subsys_notif_register("apr_modem", AUDIO_NOTIFIER_MODEM_DOMAIN,
diff --git a/drivers/soc/qcom/scm_qcpe.c b/drivers/soc/qcom/scm_qcpe.c
index f50527e50a25..614670888aac 100644
--- a/drivers/soc/qcom/scm_qcpe.c
+++ b/drivers/soc/qcom/scm_qcpe.c
@@ -476,7 +476,8 @@ static int scm_call_qcpe(u32 fn_id, struct scm_desc *desc)
size_bytes = sizeof(smc_params);
memset(&smc_params, 0x0, sizeof(smc_params));
- ret = habmm_socket_recv(handle, &smc_params, &size_bytes, 0, 0);
+ ret = habmm_socket_recv(handle, &smc_params, &size_bytes, 0,
+ HABMM_SOCKET_RECV_FLAGS_UNINTERRUPTIBLE);
if (ret) {
pr_err("habmm_socket_recv failed, ret= 0x%x\n", ret);
goto err_ret;
diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c
index ae249f382339..ea94456ccef8 100644
--- a/drivers/soc/qcom/subsystem_restart.c
+++ b/drivers/soc/qcom/subsystem_restart.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1083,7 +1083,7 @@ int subsystem_restart_dev(struct subsys_device *dev)
{
const char *name;
- if (!get_device(&dev->dev))
+ if ((!dev) || !get_device(&dev->dev))
return -ENODEV;
if (!try_module_get(dev->owner)) {
@@ -1177,11 +1177,21 @@ EXPORT_SYMBOL(subsystem_crashed);
void subsys_set_crash_status(struct subsys_device *dev,
enum crash_status crashed)
{
+ if (!dev) {
+ pr_err("Invalid subsystem device\n");
+ return;
+ }
+
dev->crashed = crashed;
}
enum crash_status subsys_get_crash_status(struct subsys_device *dev)
{
+ if (!dev) {
+ pr_err("Invalid subsystem device\n");
+ return CRASH_STATUS_NO_CRASH;
+ }
+
return dev->crashed;
}
diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c
index 625030f1f256..3528cb08c78e 100644
--- a/drivers/soc/qcom/watchdog_v2.c
+++ b/drivers/soc/qcom/watchdog_v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -873,8 +873,7 @@ err:
}
static const struct dev_pm_ops msm_watchdog_dev_pm_ops = {
- .suspend_noirq = msm_watchdog_suspend,
- .resume_noirq = msm_watchdog_resume,
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(msm_watchdog_suspend, msm_watchdog_resume)
};
static struct platform_driver msm_watchdog_driver = {
diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c
index 2ad4cc7a4785..a2ead280ac4e 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-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2018, 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
@@ -754,8 +754,10 @@ static void ion_system_heap_destroy_pools(struct ion_page_pool **pools)
{
int i;
for (i = 0; i < num_orders; i++)
- if (pools[i])
+ if (pools[i]) {
ion_page_pool_destroy(pools[i]);
+ pools[i] = NULL;
+ }
}
/**
diff --git a/drivers/staging/rtl8192u/r8192U_core.c b/drivers/staging/rtl8192u/r8192U_core.c
index e06864f64beb..0f6bc6b8e4c6 100644
--- a/drivers/staging/rtl8192u/r8192U_core.c
+++ b/drivers/staging/rtl8192u/r8192U_core.c
@@ -1749,6 +1749,8 @@ static short rtl8192_usb_initendpoints(struct net_device *dev)
priv->rx_urb[16] = usb_alloc_urb(0, GFP_KERNEL);
priv->oldaddr = kmalloc(16, GFP_KERNEL);
+ if (!priv->oldaddr)
+ return -ENOMEM;
oldaddr = priv->oldaddr;
align = ((long)oldaddr) & 3;
if (align) {
diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c
index 3a1de5c87cb4..37959c8f42a9 100644
--- a/drivers/tty/serial/arc_uart.c
+++ b/drivers/tty/serial/arc_uart.c
@@ -596,6 +596,11 @@ static int arc_serial_probe(struct platform_device *pdev)
if (dev_id < 0)
dev_id = 0;
+ if (dev_id >= ARRAY_SIZE(arc_uart_ports)) {
+ dev_err(&pdev->dev, "serial%d out of range\n", dev_id);
+ return -EINVAL;
+ }
+
uart = &arc_uart_ports[dev_id];
port = &uart->port;
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 3d790033744e..01e2274b23f2 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1818,6 +1818,10 @@ static int lpuart_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
return ret;
}
+ if (ret >= ARRAY_SIZE(lpuart_ports)) {
+ dev_err(&pdev->dev, "serial%d out of range\n", ret);
+ return -EINVAL;
+ }
sport->port.line = ret;
sport->lpuart32 = of_device_is_compatible(np, "fsl,ls1021a-lpuart");
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 98176d12b3e1..07ede982b472 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -1923,6 +1923,12 @@ static int serial_imx_probe(struct platform_device *pdev)
else if (ret < 0)
return ret;
+ if (sport->port.line >= ARRAY_SIZE(imx_ports)) {
+ dev_err(&pdev->dev, "serial%d out of range\n",
+ sport->port.line);
+ return -EINVAL;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index cd0414bbe094..daa4a65ef6ff 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -1274,6 +1274,10 @@ static int mxs_auart_probe(struct platform_device *pdev)
s->port.line = pdev->id < 0 ? 0 : pdev->id;
else if (ret < 0)
return ret;
+ if (s->port.line >= ARRAY_SIZE(auart_port)) {
+ dev_err(&pdev->dev, "serial%d out of range\n", s->port.line);
+ return -EINVAL;
+ }
if (of_id) {
pdev->id_entry = of_id->data;
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index df642496f989..a20b51a4f860 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -860,15 +860,12 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p)
dma->rx_conf.direction = DMA_DEV_TO_MEM;
dma->rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma->rx_conf.src_addr = p->port.mapbase + S3C2410_URXH;
- dma->rx_conf.src_maxburst = 16;
+ dma->rx_conf.src_maxburst = 1;
dma->tx_conf.direction = DMA_MEM_TO_DEV;
dma->tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma->tx_conf.dst_addr = p->port.mapbase + S3C2410_UTXH;
- if (dma_get_cache_alignment() >= 16)
- dma->tx_conf.dst_maxburst = 16;
- else
- dma->tx_conf.dst_maxburst = 1;
+ dma->tx_conf.dst_maxburst = 1;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
@@ -1807,6 +1804,10 @@ static int s3c24xx_serial_probe(struct platform_device *pdev)
dbg("s3c24xx_serial_probe(%p) %d\n", pdev, index);
+ if (index >= ARRAY_SIZE(s3c24xx_serial_ports)) {
+ dev_err(&pdev->dev, "serial%d out of range\n", index);
+ return -EINVAL;
+ }
ourport = &s3c24xx_serial_ports[index];
ourport->drv_data = s3c24xx_get_driver_data(pdev);
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index 009e0dbc12d2..4f2f4aca8d2e 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -1026,7 +1026,7 @@ static struct uart_port *cdns_uart_get_port(int id)
struct uart_port *port;
/* Try the given port id if failed use default method */
- if (cdns_uart_port[id].mapbase != 0) {
+ if (id < CDNS_UART_NR_PORTS && cdns_uart_port[id].mapbase != 0) {
/* Find the next unused port */
for (id = 0; id < CDNS_UART_NR_PORTS; id++)
if (cdns_uart_port[id].mapbase == 0)
diff --git a/drivers/uio/msm_sharedmem/msm_sharedmem.c b/drivers/uio/msm_sharedmem/msm_sharedmem.c
index b10c40b3e1fc..84623c9b41d3 100644
--- a/drivers/uio/msm_sharedmem/msm_sharedmem.c
+++ b/drivers/uio/msm_sharedmem/msm_sharedmem.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -75,6 +75,24 @@ static int sharedmem_mmap(struct uio_info *info, struct vm_area_struct *vma)
return result;
}
+static void free_shared_ram_perms(u32 client_id, phys_addr_t addr, u32 size)
+{
+ int ret;
+ u32 source_vmlist[2] = {VMID_HLOS, VMID_MSS_MSA};
+ int dest_vmids[1] = {VMID_HLOS};
+ int dest_perms[1] = {PERM_READ|PERM_WRITE|PERM_EXEC};
+
+ if (client_id != MPSS_RMTS_CLIENT_ID)
+ return;
+
+ ret = hyp_assign_phys(addr, size, source_vmlist, 2, dest_vmids,
+ dest_perms, 1);
+ if (ret != 0) {
+ pr_err("hyp_assign_phys failed IPA=0x016%pa size=%u err=%d\n",
+ &addr, size, ret);
+ }
+}
+
/* Setup the shared ram permissions.
* This function currently supports the mpss client only.
*/
@@ -184,6 +202,17 @@ out:
return ret;
}
+static void msm_sharedmem_shutdown(struct platform_device *pdev)
+{
+ struct uio_info *info = dev_get_drvdata(&pdev->dev);
+
+ phys_addr_t shared_mem_addr = info->mem[0].addr;
+ u32 shared_mem_size = info->mem[0].size;
+
+ free_shared_ram_perms(MPSS_RMTS_CLIENT_ID, shared_mem_addr,
+ shared_mem_size);
+}
+
static int msm_sharedmem_remove(struct platform_device *pdev)
{
struct uio_info *info = dev_get_drvdata(&pdev->dev);
@@ -202,6 +231,7 @@ MODULE_DEVICE_TABLE(of, msm_sharedmem_of_match);
static struct platform_driver msm_sharedmem_driver = {
.probe = msm_sharedmem_probe,
.remove = msm_sharedmem_remove,
+ .shutdown = msm_sharedmem_shutdown,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index a738a68d2292..a899d47c2a7c 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -187,7 +187,7 @@ struct dwc2_hsotg_ep {
unsigned char dir_in;
unsigned char index;
unsigned char mc;
- unsigned char interval;
+ u16 interval;
unsigned int halted:1;
unsigned int periodic:1;
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 0abf73c91beb..98705b83d2dc 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -2424,12 +2424,6 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
dwc2_writel(dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
DXEPCTL_USBACTEP, hsotg->regs + DIEPCTL0);
- dwc2_hsotg_enqueue_setup(hsotg);
-
- dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
- dwc2_readl(hsotg->regs + DIEPCTL0),
- dwc2_readl(hsotg->regs + DOEPCTL0));
-
/* clear global NAKs */
val = DCTL_CGOUTNAK | DCTL_CGNPINNAK;
if (!is_usb_reset)
@@ -2440,6 +2434,12 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
mdelay(3);
hsotg->lx_state = DWC2_L0;
+
+ dwc2_hsotg_enqueue_setup(hsotg);
+
+ dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
+ dwc2_readl(hsotg->regs + DIEPCTL0),
+ dwc2_readl(hsotg->regs + DOEPCTL0));
}
static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 927c84ea4921..8f36f3df55eb 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -232,6 +232,8 @@
#define DWC3_GUSB3PIPECTL_ELASTIC_BUF_MODE (1 << 0)
/* Global TX Fifo Size Register */
+#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */
+#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index d65a10a80d56..ce6a44e136dc 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1465,7 +1465,7 @@ static int count_ext_compat(struct usb_configuration *c)
return res;
}
-static void fill_ext_compat(struct usb_configuration *c, u8 *buf)
+static int fill_ext_compat(struct usb_configuration *c, u8 *buf)
{
int i, count;
@@ -1492,10 +1492,12 @@ static void fill_ext_compat(struct usb_configuration *c, u8 *buf)
buf += 23;
}
count += 24;
- if (count >= 4096)
- return;
+ if (count + 24 >= USB_COMP_EP0_OS_DESC_BUFSIZ)
+ return count;
}
}
+
+ return count;
}
static int count_ext_prop(struct usb_configuration *c, int interface)
@@ -1540,25 +1542,20 @@ static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf)
struct usb_os_desc *d;
struct usb_os_desc_ext_prop *ext_prop;
int j, count, n, ret;
- u8 *start = buf;
f = c->interface[interface];
+ count = 10; /* header length */
for (j = 0; j < f->os_desc_n; ++j) {
if (interface != f->os_desc_table[j].if_id)
continue;
d = f->os_desc_table[j].os_desc;
if (d)
list_for_each_entry(ext_prop, &d->ext_prop, entry) {
- /* 4kB minus header length */
- n = buf - start;
- if (n >= 4086)
- return 0;
-
- count = ext_prop->data_len +
+ n = ext_prop->data_len +
ext_prop->name_len + 14;
- if (count > 4086 - n)
- return -EINVAL;
- usb_ext_prop_put_size(buf, count);
+ if (count + n >= USB_COMP_EP0_OS_DESC_BUFSIZ)
+ return count;
+ usb_ext_prop_put_size(buf, n);
usb_ext_prop_put_type(buf, ext_prop->type);
ret = usb_ext_prop_put_name(buf, ext_prop->name,
ext_prop->name_len);
@@ -1584,11 +1581,12 @@ static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf)
default:
return -EINVAL;
}
- buf += count;
+ buf += n;
+ count += n;
}
}
- return 0;
+ return count;
}
/*
@@ -1877,6 +1875,7 @@ unknown:
req->complete = composite_setup_complete;
buf = req->buf;
os_desc_cfg = cdev->os_desc_config;
+ w_length = min_t(u16, w_length, USB_COMP_EP0_OS_DESC_BUFSIZ);
memset(buf, 0, w_length);
buf[5] = 0x01;
switch (ctrl->bRequestType & USB_RECIP_MASK) {
@@ -1900,8 +1899,8 @@ unknown:
count += 16; /* header */
put_unaligned_le32(count, buf);
buf += 16;
- fill_ext_compat(os_desc_cfg, buf);
- value = w_length;
+ value = fill_ext_compat(os_desc_cfg, buf);
+ value = min_t(u16, w_length, value);
}
break;
case USB_RECIP_INTERFACE:
@@ -1930,8 +1929,7 @@ unknown:
interface, buf);
if (value < 0)
return value;
-
- value = w_length;
+ value = min_t(u16, w_length, value);
}
break;
}
@@ -2228,8 +2226,8 @@ int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
goto end;
}
- /* OS feature descriptor length <= 4kB */
- cdev->os_desc_req->buf = kmalloc(4096, GFP_KERNEL);
+ cdev->os_desc_req->buf = kmalloc(USB_COMP_EP0_OS_DESC_BUFSIZ,
+ GFP_KERNEL);
if (!cdev->os_desc_req->buf) {
ret = PTR_ERR(cdev->os_desc_req->buf);
kfree(cdev->os_desc_req);
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 1a2af68ca93d..a31322ed9447 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1259,9 +1259,9 @@ static void purge_configs_funcs(struct gadget_info *gi)
cfg = container_of(c, struct config_usb_cfg, c);
- list_for_each_entry_safe(f, tmp, &c->functions, list) {
+ list_for_each_entry_safe_reverse(f, tmp, &c->functions, list) {
- list_move_tail(&f->list, &cfg->func_list);
+ list_move(&f->list, &cfg->func_list);
if (f->unbind) {
dev_err(&gi->cdev.gadget->dev, "unbind function"
" '%s'/%pK\n", f->name, f);
diff --git a/drivers/usb/gadget/function/f_cdev.c b/drivers/usb/gadget/function/f_cdev.c
index 34ec15ab9010..233221fed424 100644
--- a/drivers/usb/gadget/function/f_cdev.c
+++ b/drivers/usb/gadget/function/f_cdev.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2013-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011, 2013-2018, The Linux Foundation. All rights reserved.
* Linux Foundation chooses to take subject only to the GPLv2 license terms,
* and distributes only under these terms.
*
@@ -1251,6 +1251,7 @@ ssize_t f_cdev_write(struct file *file,
ret = -EFAULT;
} else {
req->length = xfer_size;
+ req->zero = 1;
ret = usb_ep_queue(in, req, GFP_KERNEL);
if (ret) {
pr_err("EP QUEUE failed:%d\n", ret);
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 9edc01692142..4ec0dd4f0a8c 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -759,11 +759,15 @@ static void ffs_user_copy_worker(struct work_struct *work)
ffs_log("enter: ret %d", ret);
if (io_data->read && ret > 0) {
+ mm_segment_t oldfs = get_fs();
+
+ set_fs(USER_DS);
use_mm(io_data->mm);
ret = copy_to_iter(io_data->buf, ret, &io_data->data);
if (ret != io_data->req->actual && iov_iter_count(&io_data->data))
ret = -EFAULT;
unuse_mm(io_data->mm);
+ set_fs(oldfs);
}
io_data->kiocb->ki_complete(io_data->kiocb, ret, ret);
@@ -3561,7 +3565,7 @@ static int ffs_func_setup(struct usb_function *f,
ffs_log("exit");
- return 0;
+ return USB_GADGET_DELAYED_STATUS;
}
static void ffs_func_suspend(struct usb_function *f)
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 12064d3bddf6..b5dab103be38 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -1052,6 +1052,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
+ iad_desc.bFirstInterface = ret;
+
std_ac_if_desc.bInterfaceNumber = ret;
agdev->ac_intf = ret;
agdev->ac_alt = 0;
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index aac0ce8aeb0b..8991a4070792 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -1310,7 +1310,7 @@ static void udc_reset_ep_queue(struct fsl_udc *udc, u8 pipe)
{
struct fsl_ep *ep = get_ep_by_pipe(udc, pipe);
- if (ep->name)
+ if (ep->ep.name)
nuke(ep, -ESHUTDOWN);
}
@@ -1698,7 +1698,7 @@ static void dtd_complete_irq(struct fsl_udc *udc)
curr_ep = get_ep_by_pipe(udc, i);
/* If the ep is configured */
- if (curr_ep->name == NULL) {
+ if (!curr_ep->ep.name) {
WARNING("Invalid EP?");
continue;
}
diff --git a/drivers/usb/gadget/udc/goku_udc.h b/drivers/usb/gadget/udc/goku_udc.h
index 86d2adafe149..64eb0f2b5ea0 100644
--- a/drivers/usb/gadget/udc/goku_udc.h
+++ b/drivers/usb/gadget/udc/goku_udc.h
@@ -28,7 +28,7 @@ struct goku_udc_regs {
# define INT_EP1DATASET 0x00040
# define INT_EP2DATASET 0x00080
# define INT_EP3DATASET 0x00100
-#define INT_EPnNAK(n) (0x00100 < (n)) /* 0 < n < 4 */
+#define INT_EPnNAK(n) (0x00100 << (n)) /* 0 < n < 4 */
# define INT_EP1NAK 0x00200
# define INT_EP2NAK 0x00400
# define INT_EP3NAK 0x00800
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index c0ce3db6a7c0..be8c618e42db 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -444,7 +444,8 @@ static int ohci_init (struct ohci_hcd *ohci)
struct usb_hcd *hcd = ohci_to_hcd(ohci);
/* Accept arbitrarily long scatter-gather lists */
- hcd->self.sg_tablesize = ~0;
+ if (!(hcd->driver->flags & HCD_LOCAL_MEM))
+ hcd->self.sg_tablesize = ~0;
if (distrust_firmware)
ohci->flags |= OHCI_QUIRK_HUB_POWER;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 225d1de17c3e..ccff562d2f48 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -960,6 +960,8 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
if (dev->out_ctx)
xhci_free_container_ctx(xhci, dev->out_ctx);
+ if (dev->udev && dev->udev->slot_id)
+ dev->udev->slot_id = 0;
kfree(xhci->devs[slot_id]);
xhci->devs[slot_id] = NULL;
}
diff --git a/drivers/usb/misc/ks_bridge.c b/drivers/usb/misc/ks_bridge.c
index 35f652c281bb..4004ba0437d5 100644
--- a/drivers/usb/misc/ks_bridge.c
+++ b/drivers/usb/misc/ks_bridge.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014, 2017, Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, 2017-2018, Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -463,6 +463,8 @@ static const struct usb_device_id ksb_usb_ids[] = {
{ USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9091, 0), },
{ USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x901D, 0), },
{ USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x900E, 0), },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9900, 0), },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9901, 0), },
{ USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9048, 2),
.driver_info = (unsigned long)&ksb_efs_hsic_dev, },
{ USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x904C, 2),
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 06d83825923a..3a81b4c4d0dd 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -1775,6 +1775,7 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf)
int vbus;
u8 devctl;
+ pm_runtime_get_sync(dev);
spin_lock_irqsave(&musb->lock, flags);
val = musb->a_wait_bcon;
vbus = musb_platform_get_vbus_status(musb);
@@ -1788,6 +1789,7 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf)
vbus = 0;
}
spin_unlock_irqrestore(&musb->lock, flags);
+ pm_runtime_put_sync(dev);
return sprintf(buf, "Vbus %s, timeout %lu msec\n",
vbus ? "on" : "off", val);
@@ -2522,7 +2524,8 @@ static int musb_resume(struct device *dev)
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
- musb_start(musb);
+ musb_enable_interrupts(musb);
+ musb_platform_enable(musb);
return 0;
}
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 32cadca198b2..e7a051386b32 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -33,7 +33,7 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *);
static void cp210x_close(struct usb_serial_port *);
static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *);
static void cp210x_get_termios_port(struct usb_serial_port *port,
- unsigned int *cflagp, unsigned int *baudp);
+ tcflag_t *cflagp, unsigned int *baudp);
static void cp210x_change_speed(struct tty_struct *, struct usb_serial_port *,
struct ktermios *);
static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *,
@@ -515,7 +515,7 @@ static void cp210x_get_termios(struct tty_struct *tty,
&tty->termios.c_cflag, &baud);
tty_encode_baud_rate(tty, baud, baud);
} else {
- unsigned int cflag;
+ tcflag_t cflag;
cflag = 0;
cp210x_get_termios_port(port, &cflag, &baud);
}
@@ -526,10 +526,11 @@ static void cp210x_get_termios(struct tty_struct *tty,
* This is the heart of cp210x_get_termios which always uses a &usb_serial_port.
*/
static void cp210x_get_termios_port(struct usb_serial_port *port,
- unsigned int *cflagp, unsigned int *baudp)
+ tcflag_t *cflagp, unsigned int *baudp)
{
struct device *dev = &port->dev;
- unsigned int cflag, modem_ctl[4];
+ tcflag_t cflag;
+ unsigned int modem_ctl[4];
unsigned int baud;
unsigned int bits;
diff --git a/drivers/video/fbdev/sbuslib.c b/drivers/video/fbdev/sbuslib.c
index a350209ffbd3..31c301d6be62 100644
--- a/drivers/video/fbdev/sbuslib.c
+++ b/drivers/video/fbdev/sbuslib.c
@@ -121,7 +121,7 @@ int sbusfb_ioctl_helper(unsigned long cmd, unsigned long arg,
unsigned char __user *ured;
unsigned char __user *ugreen;
unsigned char __user *ublue;
- int index, count, i;
+ unsigned int index, count, i;
if (get_user(index, &c->index) ||
__get_user(count, &c->count) ||
@@ -160,7 +160,7 @@ int sbusfb_ioctl_helper(unsigned long cmd, unsigned long arg,
unsigned char __user *ugreen;
unsigned char __user *ublue;
struct fb_cmap *cmap = &info->cmap;
- int index, count, i;
+ unsigned int index, count, i;
u8 red, green, blue;
if (get_user(index, &c->index) ||
diff --git a/drivers/video/msm/ba/msm_ba.c b/drivers/video/msm/ba/msm_ba.c
index 42a737713465..d00f6169bdd9 100644
--- a/drivers/video/msm/ba/msm_ba.c
+++ b/drivers/video/msm/ba/msm_ba.c
@@ -347,6 +347,7 @@ int msm_ba_g_fmt(void *instance, struct v4l2_format *f)
} else {
f->fmt.pix.height = sd_fmt.format.height;
f->fmt.pix.width = sd_fmt.format.width;
+ f->fmt.pix.field = sd_fmt.format.field;
switch (sd_fmt.format.code) {
case MEDIA_BUS_FMT_YUYV8_2X8:
f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c
index aa93df5833dc..2048aad91add 100644
--- a/drivers/watchdog/f71808e_wdt.c
+++ b/drivers/watchdog/f71808e_wdt.c
@@ -520,7 +520,8 @@ static ssize_t watchdog_write(struct file *file, const char __user *buf,
char c;
if (get_user(c, buf + i))
return -EFAULT;
- expect_close = (c == 'V');
+ if (c == 'V')
+ expect_close = true;
}
/* Properly order writes across fork()ed processes */
diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h
index 2b28c00da0df..dfe20b81ced5 100644
--- a/drivers/watchdog/sp5100_tco.h
+++ b/drivers/watchdog/sp5100_tco.h
@@ -54,7 +54,7 @@
#define SB800_PM_WATCHDOG_CONFIG 0x4C
#define SB800_PCI_WATCHDOG_DECODE_EN (1 << 0)
-#define SB800_PM_WATCHDOG_DISABLE (1 << 2)
+#define SB800_PM_WATCHDOG_DISABLE (1 << 1)
#define SB800_PM_WATCHDOG_SECOND_RES (3 << 0)
#define SB800_ACPI_MMIO_DECODE_EN (1 << 0)
#define SB800_ACPI_MMIO_SEL (1 << 1)
diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c
index 83ec7b89d308..468961c59fa5 100644
--- a/drivers/xen/events/events_base.c
+++ b/drivers/xen/events/events_base.c
@@ -764,8 +764,8 @@ out:
mutex_unlock(&irq_mapping_update_lock);
return irq;
error_irq:
- for (; i >= 0; i--)
- __unbind_from_irq(irq + i);
+ while (nvec--)
+ __unbind_from_irq(irq + nvec);
mutex_unlock(&irq_mapping_update_lock);
return ret;
}
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index c49f79ed58c5..4b7ce442d8e5 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -328,7 +328,7 @@ static void gnttab_handle_deferred(unsigned long unused)
if (entry->page) {
pr_debug("freeing g.e. %#x (pfn %#lx)\n",
entry->ref, page_to_pfn(entry->page));
- __free_page(entry->page);
+ put_page(entry->page);
} else
pr_info("freeing g.e. %#x\n", entry->ref);
kfree(entry);
@@ -384,7 +384,7 @@ void gnttab_end_foreign_access(grant_ref_t ref, int readonly,
if (gnttab_end_foreign_access_ref(ref, readonly)) {
put_free_entry(ref);
if (page != 0)
- free_page(page);
+ put_page(virt_to_page(page));
} else
gnttab_add_deferred(ref, readonly,
page ? virt_to_page(page) : NULL);
diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c
index f7b19c25c3a4..1889e928a0da 100644
--- a/drivers/xen/swiotlb-xen.c
+++ b/drivers/xen/swiotlb-xen.c
@@ -359,7 +359,7 @@ xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr,
* physical address */
phys = xen_bus_to_phys(dev_addr);
- if (((dev_addr + size - 1 > dma_mask)) ||
+ if (((dev_addr + size - 1 <= dma_mask)) ||
range_straddles_page_boundary(phys, size))
xen_destroy_contiguous_region(phys, order);
diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c
index 2e319d0c395d..84cc98f3cabe 100644
--- a/drivers/xen/xen-acpi-processor.c
+++ b/drivers/xen/xen-acpi-processor.c
@@ -362,9 +362,9 @@ read_acpi_id(acpi_handle handle, u32 lvl, void *context, void **rv)
}
/* There are more ACPI Processor objects than in x2APIC or MADT.
* This can happen with incorrect ACPI SSDT declerations. */
- if (acpi_id > nr_acpi_bits) {
- pr_debug("We only have %u, trying to set %u\n",
- nr_acpi_bits, acpi_id);
+ if (acpi_id >= nr_acpi_bits) {
+ pr_debug("max acpi id %u, trying to set %u\n",
+ nr_acpi_bits - 1, acpi_id);
return AE_OK;
}
/* OK, There is a ACPI Processor object */
diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c
index 33a31cfef55d..c2d447687e33 100644
--- a/drivers/xen/xenbus/xenbus_probe.c
+++ b/drivers/xen/xenbus/xenbus_probe.c
@@ -470,8 +470,11 @@ int xenbus_probe_node(struct xen_bus_type *bus,
/* Register with generic device framework. */
err = device_register(&xendev->dev);
- if (err)
+ if (err) {
+ put_device(&xendev->dev);
+ xendev = NULL;
goto fail;
+ }
return 0;
fail:
diff --git a/drivers/zorro/zorro.c b/drivers/zorro/zorro.c
index d295d9878dff..8ec79385d3cc 100644
--- a/drivers/zorro/zorro.c
+++ b/drivers/zorro/zorro.c
@@ -16,6 +16,7 @@
#include <linux/bitops.h>
#include <linux/string.h>
#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <asm/byteorder.h>
@@ -185,6 +186,17 @@ static int __init amiga_zorro_probe(struct platform_device *pdev)
z->dev.parent = &bus->dev;
z->dev.bus = &zorro_bus_type;
z->dev.id = i;
+ switch (z->rom.er_Type & ERT_TYPEMASK) {
+ case ERT_ZORROIII:
+ z->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ break;
+
+ case ERT_ZORROII:
+ default:
+ z->dev.coherent_dma_mask = DMA_BIT_MASK(24);
+ break;
+ }
+ z->dev.dma_mask = &z->dev.coherent_dma_mask;
}
/* ... then register them */
diff --git a/fs/affs/namei.c b/fs/affs/namei.c
index 181e05b46e72..92448d0ad900 100644
--- a/fs/affs/namei.c
+++ b/fs/affs/namei.c
@@ -224,9 +224,10 @@ affs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
affs_lock_dir(dir);
bh = affs_find_entry(dir, dentry);
- affs_unlock_dir(dir);
- if (IS_ERR(bh))
+ if (IS_ERR(bh)) {
+ affs_unlock_dir(dir);
return ERR_CAST(bh);
+ }
if (bh) {
u32 ino = bh->b_blocknr;
@@ -240,10 +241,13 @@ affs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
}
affs_brelse(bh);
inode = affs_iget(sb, ino);
- if (IS_ERR(inode))
+ if (IS_ERR(inode)) {
+ affs_unlock_dir(dir);
return ERR_CAST(inode);
+ }
}
d_add(dentry, inode);
+ affs_unlock_dir(dir);
return NULL;
}
diff --git a/fs/aio.c b/fs/aio.c
index 54447d906b08..c4b508605bab 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -629,9 +629,8 @@ static void free_ioctx_users(struct percpu_ref *ref)
while (!list_empty(&ctx->active_reqs)) {
req = list_first_entry(&ctx->active_reqs,
struct aio_kiocb, ki_list);
-
- list_del_init(&req->ki_list);
kiocb_cancel(req);
+ list_del_init(&req->ki_list);
}
spin_unlock_irq(&ctx->ctx_lock);
@@ -1067,8 +1066,8 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id)
ctx = rcu_dereference(table->table[id]);
if (ctx && ctx->user_id == ctx_id) {
- percpu_ref_get(&ctx->users);
- ret = ctx;
+ if (percpu_ref_tryget_live(&ctx->users))
+ ret = ctx;
}
out:
rcu_read_unlock();
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index e2f5be261532..38ee08675468 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -2769,6 +2769,8 @@ again:
* contention with the cow code
*/
if (cow) {
+ bool last_level = (level == (BTRFS_MAX_LEVEL - 1));
+
/*
* if we don't really need to cow this block
* then we don't want to set the path blocking,
@@ -2793,9 +2795,13 @@ again:
}
btrfs_set_path_blocking(p);
- err = btrfs_cow_block(trans, root, b,
- p->nodes[level + 1],
- p->slots[level + 1], &b);
+ if (last_level)
+ err = btrfs_cow_block(trans, root, b, NULL, 0,
+ &b);
+ else
+ err = btrfs_cow_block(trans, root, b,
+ p->nodes[level + 1],
+ p->slots[level + 1], &b);
if (err) {
ret = err;
goto done;
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 85b207d19aa5..7efd70bfeaf7 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -923,7 +923,7 @@ static int check_async_write(struct inode *inode, unsigned long bio_flags)
if (bio_flags & EXTENT_BIO_TREE_LOG)
return 0;
#ifdef CONFIG_X86
- if (cpu_has_xmm4_2)
+ if (static_cpu_has_safe(X86_FEATURE_XMM4_2))
return 0;
#endif
return 1;
@@ -1196,7 +1196,7 @@ static struct btrfs_subvolume_writers *btrfs_alloc_subvolume_writers(void)
if (!writers)
return ERR_PTR(-ENOMEM);
- ret = percpu_counter_init(&writers->counter, 0, GFP_KERNEL);
+ ret = percpu_counter_init(&writers->counter, 0, GFP_NOFS);
if (ret < 0) {
kfree(writers);
return ERR_PTR(ret);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 260f94b019c9..982a9d509817 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -4392,6 +4392,7 @@ again:
if (wait_for_alloc) {
mutex_unlock(&fs_info->chunk_mutex);
wait_for_alloc = 0;
+ cond_resched();
goto again;
}
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index d4a6eef31854..052973620595 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1861,10 +1861,19 @@ int btrfs_release_file(struct inode *inode, struct file *filp)
static int start_ordered_ops(struct inode *inode, loff_t start, loff_t end)
{
int ret;
+ struct blk_plug plug;
+ /*
+ * This is only called in fsync, which would do synchronous writes, so
+ * a plug can merge adjacent IOs as much as possible. Esp. in case of
+ * multiple disks using raid profile, a large IO can be split to
+ * several segments of stripe length (currently 64K).
+ */
+ blk_start_plug(&plug);
atomic_inc(&BTRFS_I(inode)->sync_writers);
ret = btrfs_fdatawrite_range(inode, start, end);
atomic_dec(&BTRFS_I(inode)->sync_writers);
+ blk_finish_plug(&plug);
return ret;
}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 81b5a461d94e..1f01a8172308 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -6413,8 +6413,7 @@ static int btrfs_mknod(struct inode *dir, struct dentry *dentry,
goto out_unlock_inode;
} else {
btrfs_update_inode(trans, root, inode);
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
}
out_unlock:
@@ -6489,8 +6488,7 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
goto out_unlock_inode;
BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
out_unlock:
btrfs_end_transaction(trans, root);
@@ -6633,12 +6631,7 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
if (err)
goto out_fail_inode;
- d_instantiate(dentry, inode);
- /*
- * mkdir is special. We're unlocking after we call d_instantiate
- * to avoid a race with nfsd calling d_instantiate.
- */
- unlock_new_inode(inode);
+ d_instantiate_new(dentry, inode);
drop_on_err = 0;
out_fail:
@@ -9789,8 +9782,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
goto out_unlock_inode;
}
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
out_unlock:
btrfs_end_transaction(trans, root);
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index 1a33d3eb36de..b9fa99577bf7 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -2160,11 +2160,21 @@ int raid56_parity_recover(struct btrfs_root *root, struct bio *bio,
}
/*
- * reconstruct from the q stripe if they are
- * asking for mirror 3
+ * Loop retry:
+ * for 'mirror == 2', reconstruct from all other stripes.
+ * for 'mirror_num > 2', select a stripe to fail on every retry.
*/
- if (mirror_num == 3)
- rbio->failb = rbio->real_stripes - 2;
+ if (mirror_num > 2) {
+ /*
+ * 'mirror == 3' is to fail the p stripe and
+ * reconstruct from the q stripe. 'mirror > 3' is to
+ * fail a data stripe and reconstruct from p+q stripe.
+ */
+ rbio->failb = rbio->real_stripes - (mirror_num - 1);
+ ASSERT(rbio->failb > 0);
+ if (rbio->failb <= rbio->faila)
+ rbio->failb--;
+ }
ret = lock_stripe_add(rbio);
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 19b56873b797..83c73738165e 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -4674,6 +4674,9 @@ static int send_hole(struct send_ctx *sctx, u64 end)
u64 len;
int ret = 0;
+ if (sctx->flags & BTRFS_SEND_FLAG_NO_FILE_DATA)
+ return send_update_extent(sctx, offset, end - offset);
+
p = fs_path_alloc();
if (!p)
return -ENOMEM;
diff --git a/fs/btrfs/tests/qgroup-tests.c b/fs/btrfs/tests/qgroup-tests.c
index 846d277b1901..2b2978c04e80 100644
--- a/fs/btrfs/tests/qgroup-tests.c
+++ b/fs/btrfs/tests/qgroup-tests.c
@@ -70,7 +70,7 @@ static int insert_normal_tree_ref(struct btrfs_root *root, u64 bytenr,
btrfs_set_extent_generation(leaf, item, 1);
btrfs_set_extent_flags(leaf, item, BTRFS_EXTENT_FLAG_TREE_BLOCK);
block_info = (struct btrfs_tree_block_info *)(item + 1);
- btrfs_set_tree_block_level(leaf, block_info, 1);
+ btrfs_set_tree_block_level(leaf, block_info, 0);
iref = (struct btrfs_extent_inline_ref *)(block_info + 1);
if (parent > 0) {
btrfs_set_extent_inline_ref_type(leaf, iref,
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 6ba022ed4a52..738f5d6beb95 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2223,8 +2223,10 @@ again:
nritems = btrfs_header_nritems(path->nodes[0]);
if (path->slots[0] >= nritems) {
ret = btrfs_next_leaf(root, path);
- if (ret)
+ if (ret == 1)
break;
+ else if (ret < 0)
+ goto out;
}
btrfs_item_key_to_cpu(path->nodes[0], &found_key,
path->slots[0]);
@@ -3378,8 +3380,11 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
* from this directory and from this transaction
*/
ret = btrfs_next_leaf(root, path);
- if (ret == 1) {
- last_offset = (u64)-1;
+ if (ret) {
+ if (ret == 1)
+ last_offset = (u64)-1;
+ else
+ err = ret;
goto done;
}
btrfs_item_key_to_cpu(path->nodes[0], &tmp, path->slots[0]);
@@ -3830,6 +3835,7 @@ fill_holes:
ASSERT(ret == 0);
src = src_path->nodes[0];
i = 0;
+ need_find_last_extent = true;
}
btrfs_item_key_to_cpu(src, &key, i);
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index ed75d70b4bc2..b4d63a9842fa 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -5056,7 +5056,14 @@ int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len)
else if (map->type & BTRFS_BLOCK_GROUP_RAID5)
ret = 2;
else if (map->type & BTRFS_BLOCK_GROUP_RAID6)
- ret = 3;
+ /*
+ * There could be two corrupted data stripes, we need
+ * to loop retry in order to rebuild the correct data.
+ *
+ * Fail a stripe at a time on every retry except the
+ * stripe under reconstruction.
+ */
+ ret = map->num_stripes;
else
ret = 1;
free_extent_map(em);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 0c92af11f4f4..8632380d2b94 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -6421,9 +6421,7 @@ SetEARetry:
pSMB->InformationLevel =
cpu_to_le16(SMB_SET_FILE_EA);
- parm_data =
- (struct fealist *) (((char *) &pSMB->hdr.Protocol) +
- offset);
+ parm_data = (void *)pSMB + offsetof(struct smb_hdr, Protocol) + offset;
pSMB->ParameterOffset = cpu_to_le16(param_offset);
pSMB->DataOffset = cpu_to_le16(offset);
pSMB->SetupCount = 1;
diff --git a/fs/dcache.c b/fs/dcache.c
index 750ddc627855..7df640155468 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1897,6 +1897,28 @@ struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
EXPORT_SYMBOL(d_instantiate_unique);
+/*
+ * This should be equivalent to d_instantiate() + unlock_new_inode(),
+ * with lockdep-related part of unlock_new_inode() done before
+ * anything else. Use that instead of open-coding d_instantiate()/
+ * unlock_new_inode() combinations.
+ */
+void d_instantiate_new(struct dentry *entry, struct inode *inode)
+{
+ BUG_ON(!hlist_unhashed(&entry->d_u.d_alias));
+ BUG_ON(!inode);
+ lockdep_annotate_inode_mutex_key(inode);
+ security_d_instantiate(entry, inode);
+ spin_lock(&inode->i_lock);
+ __d_instantiate(entry, inode);
+ WARN_ON(!(inode->i_state & I_NEW));
+ inode->i_state &= ~I_NEW;
+ smp_mb();
+ wake_up_bit(&inode->i_state, __I_NEW);
+ spin_unlock(&inode->i_lock);
+}
+EXPORT_SYMBOL(d_instantiate_new);
+
/**
* d_instantiate_no_diralias - instantiate a non-aliased dentry
* @entry: dentry to complete
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index cb3ecf442d96..d108ed116858 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -291,7 +291,6 @@ ecryptfs_create(struct inode *directory_inode, struct dentry *ecryptfs_dentry,
iput(ecryptfs_inode);
goto out;
}
- unlock_new_inode(ecryptfs_inode);
crypt_stat = &ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat;
if (get_events() && get_events()->open_cb)
@@ -299,7 +298,7 @@ ecryptfs_create(struct inode *directory_inode, struct dentry *ecryptfs_dentry,
ecryptfs_inode_to_lower(ecryptfs_inode),
crypt_stat);
- d_instantiate(ecryptfs_dentry, ecryptfs_inode);
+ d_instantiate_new(ecryptfs_dentry, ecryptfs_inode);
out:
return rc;
}
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index 3267a80dbbe2..da3d40ef1668 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -40,8 +40,7 @@ static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode)
{
int err = ext2_add_link(dentry, inode);
if (!err) {
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
return 0;
}
inode_dec_link_count(inode);
@@ -267,8 +266,7 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, umode_t mode)
if (err)
goto out_fail;
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
out:
return err;
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index b9324d0ff218..cf09d5f71ad2 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2426,8 +2426,7 @@ static int ext4_add_nondir(handle_t *handle,
int err = ext4_add_entry(handle, dentry, inode);
if (!err) {
ext4_mark_inode_dirty(handle, inode);
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
return 0;
}
drop_nlink(inode);
@@ -2666,8 +2665,7 @@ out_clear_inode:
err = ext4_mark_inode_dirty(handle, dir);
if (err)
goto out_clear_inode;
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
if (IS_DIRSYNC(dir))
ext4_handle_sync(handle);
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index d0bfcfed35e2..97c17b3d984c 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -238,15 +238,12 @@ enum {
#define CP_DISCARD 0x00000010
#define CP_TRIMMED 0x00000020
-#define DEF_BATCHED_TRIM_SECTIONS 2048
-#define BATCHED_TRIM_SEGMENTS(sbi) \
- (GET_SEG_FROM_SEC(sbi, SM_I(sbi)->trim_sections))
-#define BATCHED_TRIM_BLOCKS(sbi) \
- (BATCHED_TRIM_SEGMENTS(sbi) << (sbi)->log_blocks_per_seg)
#define MAX_DISCARD_BLOCKS(sbi) BLKS_PER_SEC(sbi)
#define DEF_MAX_DISCARD_REQUEST 8 /* issue 8 discards per round */
+#define DEF_MAX_DISCARD_LEN 512 /* Max. 2MB per discard */
#define DEF_MIN_DISCARD_ISSUE_TIME 50 /* 50 ms, if exists */
#define DEF_MAX_DISCARD_ISSUE_TIME 60000 /* 60 s, if no candidates */
+#define DEF_DISCARD_URGENT_UTIL 80 /* do more discard over 80% */
#define DEF_CP_INTERVAL 60 /* 60 secs */
#define DEF_IDLE_INTERVAL 5 /* 5 secs */
@@ -753,7 +750,8 @@ static inline void set_extent_info(struct extent_info *ei, unsigned int fofs,
static inline bool __is_discard_mergeable(struct discard_info *back,
struct discard_info *front)
{
- return back->lstart + back->len == front->lstart;
+ return (back->lstart + back->len == front->lstart) &&
+ (back->len + front->len < DEF_MAX_DISCARD_LEN);
}
static inline bool __is_discard_back_mergeable(struct discard_info *cur,
@@ -1139,6 +1137,7 @@ enum {
enum fsync_mode {
FSYNC_MODE_POSIX, /* fsync follows posix semantics */
FSYNC_MODE_STRICT, /* fsync behaves in line with ext4 */
+ FSYNC_MODE_NOBARRIER, /* fsync behaves nobarrier based on posix */
};
#ifdef CONFIG_F2FS_FS_ENCRYPTION
@@ -2853,8 +2852,6 @@ int f2fs_flush_device_cache(struct f2fs_sb_info *sbi);
void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free);
void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr);
bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr);
-void init_discard_policy(struct discard_policy *dpolicy, int discard_type,
- unsigned int granularity);
void drop_discard_cmd(struct f2fs_sb_info *sbi);
void stop_discard_thread(struct f2fs_sb_info *sbi);
bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi);
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 7587758a285f..40d03d58b390 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -309,7 +309,7 @@ sync_nodes:
remove_ino_entry(sbi, ino, APPEND_INO);
clear_inode_flag(inode, FI_APPEND_WRITE);
flush_out:
- if (!atomic)
+ if (!atomic && F2FS_OPTION(sbi).fsync_mode != FSYNC_MODE_NOBARRIER)
ret = f2fs_issue_flush(sbi, inode->i_ino);
if (!ret) {
remove_ino_entry(sbi, ino, UPDATE_INO);
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index fecae8685d2a..0355891dbbf8 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -294,8 +294,7 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
alloc_nid_done(sbi, ino);
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
@@ -594,8 +593,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
err = page_symlink(inode, disk_link.name, disk_link.len);
err_out:
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
/*
* Let's flush symlink data in order to avoid broken symlink as much as
@@ -658,8 +656,7 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
alloc_nid_done(sbi, inode->i_ino);
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
@@ -710,8 +707,7 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
alloc_nid_done(sbi, inode->i_ino);
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 01bc94df9f00..a02d5c1a7ed2 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -996,6 +996,39 @@ static void __check_sit_bitmap(struct f2fs_sb_info *sbi,
#endif
}
+static void __init_discard_policy(struct f2fs_sb_info *sbi,
+ struct discard_policy *dpolicy,
+ int discard_type, unsigned int granularity)
+{
+ /* common policy */
+ dpolicy->type = discard_type;
+ dpolicy->sync = true;
+ dpolicy->granularity = granularity;
+
+ dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST;
+ dpolicy->io_aware_gran = MAX_PLIST_NUM;
+
+ if (discard_type == DPOLICY_BG) {
+ dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME;
+ dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME;
+ dpolicy->io_aware = true;
+ dpolicy->sync = false;
+ if (utilization(sbi) > DEF_DISCARD_URGENT_UTIL) {
+ dpolicy->granularity = 1;
+ dpolicy->max_interval = DEF_MIN_DISCARD_ISSUE_TIME;
+ }
+ } else if (discard_type == DPOLICY_FORCE) {
+ dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME;
+ dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME;
+ dpolicy->io_aware = false;
+ } else if (discard_type == DPOLICY_FSTRIM) {
+ dpolicy->io_aware = false;
+ } else if (discard_type == DPOLICY_UMOUNT) {
+ dpolicy->io_aware = false;
+ }
+}
+
+
/* this function is copied from blkdev_issue_discard from block/blk-lib.c */
static void __submit_discard_cmd(struct f2fs_sb_info *sbi,
struct discard_policy *dpolicy,
@@ -1210,68 +1243,6 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi,
return 0;
}
-static void __issue_discard_cmd_range(struct f2fs_sb_info *sbi,
- struct discard_policy *dpolicy,
- unsigned int start, unsigned int end)
-{
- struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
- struct discard_cmd *prev_dc = NULL, *next_dc = NULL;
- struct rb_node **insert_p = NULL, *insert_parent = NULL;
- struct discard_cmd *dc;
- struct blk_plug plug;
- int issued;
-
-next:
- issued = 0;
-
- mutex_lock(&dcc->cmd_lock);
- f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root));
-
- dc = (struct discard_cmd *)__lookup_rb_tree_ret(&dcc->root,
- NULL, start,
- (struct rb_entry **)&prev_dc,
- (struct rb_entry **)&next_dc,
- &insert_p, &insert_parent, true);
- if (!dc)
- dc = next_dc;
-
- blk_start_plug(&plug);
-
- while (dc && dc->lstart <= end) {
- struct rb_node *node;
-
- if (dc->len < dpolicy->granularity)
- goto skip;
-
- if (dc->state != D_PREP) {
- list_move_tail(&dc->list, &dcc->fstrim_list);
- goto skip;
- }
-
- __submit_discard_cmd(sbi, dpolicy, dc);
-
- if (++issued >= dpolicy->max_requests) {
- start = dc->lstart + dc->len;
-
- blk_finish_plug(&plug);
- mutex_unlock(&dcc->cmd_lock);
-
- schedule();
-
- goto next;
- }
-skip:
- node = rb_next(&dc->rb_node);
- dc = rb_entry_safe(node, struct discard_cmd, rb_node);
-
- if (fatal_signal_pending(current))
- break;
- }
-
- blk_finish_plug(&plug);
- mutex_unlock(&dcc->cmd_lock);
-}
-
static int __issue_discard_cmd(struct f2fs_sb_info *sbi,
struct discard_policy *dpolicy)
{
@@ -1412,7 +1383,18 @@ next:
static void __wait_all_discard_cmd(struct f2fs_sb_info *sbi,
struct discard_policy *dpolicy)
{
- __wait_discard_cmd_range(sbi, dpolicy, 0, UINT_MAX);
+ struct discard_policy dp;
+
+ if (dpolicy) {
+ __wait_discard_cmd_range(sbi, dpolicy, 0, UINT_MAX);
+ return;
+ }
+
+ /* wait all */
+ __init_discard_policy(sbi, &dp, DPOLICY_FSTRIM, 1);
+ __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX);
+ __init_discard_policy(sbi, &dp, DPOLICY_UMOUNT, 1);
+ __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX);
}
/* This should be covered by global mutex, &sit_i->sentry_lock */
@@ -1457,11 +1439,13 @@ bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi)
struct discard_policy dpolicy;
bool dropped;
- init_discard_policy(&dpolicy, DPOLICY_UMOUNT, dcc->discard_granularity);
+ __init_discard_policy(sbi, &dpolicy, DPOLICY_UMOUNT,
+ dcc->discard_granularity);
__issue_discard_cmd(sbi, &dpolicy);
dropped = __drop_discard_cmd(sbi);
- __wait_all_discard_cmd(sbi, &dpolicy);
+ /* just to make sure there is no pending discard commands */
+ __wait_all_discard_cmd(sbi, NULL);
return dropped;
}
@@ -1477,7 +1461,7 @@ static int issue_discard_thread(void *data)
set_freezable();
do {
- init_discard_policy(&dpolicy, DPOLICY_BG,
+ __init_discard_policy(sbi, &dpolicy, DPOLICY_BG,
dcc->discard_granularity);
wait_event_interruptible_timeout(*q,
@@ -1495,7 +1479,7 @@ static int issue_discard_thread(void *data)
dcc->discard_wake = 0;
if (sbi->gc_thread && sbi->gc_thread->gc_urgent)
- init_discard_policy(&dpolicy, DPOLICY_FORCE, 1);
+ __init_discard_policy(sbi, &dpolicy, DPOLICY_FORCE, 1);
sb_start_intwrite(sbi->sb);
@@ -1788,32 +1772,6 @@ skip:
wake_up_discard_thread(sbi, false);
}
-void init_discard_policy(struct discard_policy *dpolicy,
- int discard_type, unsigned int granularity)
-{
- /* common policy */
- dpolicy->type = discard_type;
- dpolicy->sync = true;
- dpolicy->granularity = granularity;
-
- dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST;
- dpolicy->io_aware_gran = MAX_PLIST_NUM;
-
- if (discard_type == DPOLICY_BG) {
- dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME;
- dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME;
- dpolicy->io_aware = true;
- } else if (discard_type == DPOLICY_FORCE) {
- dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME;
- dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME;
- dpolicy->io_aware = false;
- } else if (discard_type == DPOLICY_FSTRIM) {
- dpolicy->io_aware = false;
- } else if (discard_type == DPOLICY_UMOUNT) {
- dpolicy->io_aware = false;
- }
-}
-
static int create_discard_cmd_control(struct f2fs_sb_info *sbi)
{
dev_t dev = sbi->sb->s_bdev->bd_dev;
@@ -2453,11 +2411,72 @@ bool exist_trim_candidates(struct f2fs_sb_info *sbi, struct cp_control *cpc)
return has_candidate;
}
+static void __issue_discard_cmd_range(struct f2fs_sb_info *sbi,
+ struct discard_policy *dpolicy,
+ unsigned int start, unsigned int end)
+{
+ struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+ struct discard_cmd *prev_dc = NULL, *next_dc = NULL;
+ struct rb_node **insert_p = NULL, *insert_parent = NULL;
+ struct discard_cmd *dc;
+ struct blk_plug plug;
+ int issued;
+
+next:
+ issued = 0;
+
+ mutex_lock(&dcc->cmd_lock);
+ f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root));
+
+ dc = (struct discard_cmd *)__lookup_rb_tree_ret(&dcc->root,
+ NULL, start,
+ (struct rb_entry **)&prev_dc,
+ (struct rb_entry **)&next_dc,
+ &insert_p, &insert_parent, true);
+ if (!dc)
+ dc = next_dc;
+
+ blk_start_plug(&plug);
+
+ while (dc && dc->lstart <= end) {
+ struct rb_node *node;
+
+ if (dc->len < dpolicy->granularity)
+ goto skip;
+
+ if (dc->state != D_PREP) {
+ list_move_tail(&dc->list, &dcc->fstrim_list);
+ goto skip;
+ }
+
+ __submit_discard_cmd(sbi, dpolicy, dc);
+
+ if (++issued >= dpolicy->max_requests) {
+ start = dc->lstart + dc->len;
+
+ blk_finish_plug(&plug);
+ mutex_unlock(&dcc->cmd_lock);
+ __wait_all_discard_cmd(sbi, NULL);
+ congestion_wait(BLK_RW_ASYNC, HZ/50);
+ goto next;
+ }
+skip:
+ node = rb_next(&dc->rb_node);
+ dc = rb_entry_safe(node, struct discard_cmd, rb_node);
+
+ if (fatal_signal_pending(current))
+ break;
+ }
+
+ blk_finish_plug(&plug);
+ mutex_unlock(&dcc->cmd_lock);
+}
+
int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
{
__u64 start = F2FS_BYTES_TO_BLK(range->start);
__u64 end = start + F2FS_BYTES_TO_BLK(range->len) - 1;
- unsigned int start_segno, end_segno, cur_segno;
+ unsigned int start_segno, end_segno;
block_t start_block, end_block;
struct cp_control cpc;
struct discard_policy dpolicy;
@@ -2483,40 +2502,36 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
cpc.reason = CP_DISCARD;
cpc.trim_minlen = max_t(__u64, 1, F2FS_BYTES_TO_BLK(range->minlen));
+ cpc.trim_start = start_segno;
+ cpc.trim_end = end_segno;
- /* do checkpoint to issue discard commands safely */
- for (cur_segno = start_segno; cur_segno <= end_segno;
- cur_segno = cpc.trim_end + 1) {
- cpc.trim_start = cur_segno;
-
- if (sbi->discard_blks == 0)
- break;
- else if (sbi->discard_blks < BATCHED_TRIM_BLOCKS(sbi))
- cpc.trim_end = end_segno;
- else
- cpc.trim_end = min_t(unsigned int,
- rounddown(cur_segno +
- BATCHED_TRIM_SEGMENTS(sbi),
- sbi->segs_per_sec) - 1, end_segno);
-
- mutex_lock(&sbi->gc_mutex);
- err = write_checkpoint(sbi, &cpc);
- mutex_unlock(&sbi->gc_mutex);
- if (err)
- break;
+ if (sbi->discard_blks == 0)
+ goto out;
- schedule();
- }
+ mutex_lock(&sbi->gc_mutex);
+ err = write_checkpoint(sbi, &cpc);
+ mutex_unlock(&sbi->gc_mutex);
+ if (err)
+ goto out;
start_block = START_BLOCK(sbi, start_segno);
- end_block = START_BLOCK(sbi, min(cur_segno, end_segno) + 1);
+ end_block = START_BLOCK(sbi, end_segno + 1);
- init_discard_policy(&dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen);
+ __init_discard_policy(sbi, &dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen);
__issue_discard_cmd_range(sbi, &dpolicy, start_block, end_block);
- trimmed = __wait_discard_cmd_range(sbi, &dpolicy,
+
+ /*
+ * We filed discard candidates, but actually we don't need to wait for
+ * all of them, since they'll be issued in idle time along with runtime
+ * discard option. User configuration looks like using runtime discard
+ * or periodic fstrim instead of it.
+ */
+ if (!test_opt(sbi, DISCARD)) {
+ trimmed = __wait_discard_cmd_range(sbi, &dpolicy,
start_block, end_block);
+ range->len = F2FS_BLK_TO_BYTES(trimmed);
+ }
out:
- range->len = F2FS_BLK_TO_BYTES(trimmed);
return err;
}
@@ -3904,8 +3919,6 @@ int build_segment_manager(struct f2fs_sb_info *sbi)
sm_info->min_hot_blocks = DEF_MIN_HOT_BLOCKS;
sm_info->min_ssr_sections = reserved_sections(sbi);
- sm_info->trim_sections = DEF_BATCHED_TRIM_SECTIONS;
-
INIT_LIST_HEAD(&sm_info->sit_entry_set);
init_rwsem(&sm_info->curseg_lock);
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 55b2bad55671..cb57ad3ca32d 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -740,6 +740,10 @@ static int parse_options(struct super_block *sb, char *options)
} else if (strlen(name) == 6 &&
!strncmp(name, "strict", 6)) {
F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_STRICT;
+ } else if (strlen(name) == 9 &&
+ !strncmp(name, "nobarrier", 9)) {
+ F2FS_OPTION(sbi).fsync_mode =
+ FSYNC_MODE_NOBARRIER;
} else {
kfree(name);
return -EINVAL;
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index f33a56d6e6dd..2c53de9251be 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -245,6 +245,9 @@ out:
return count;
}
+ if (!strcmp(a->attr.name, "trim_sections"))
+ return -EINVAL;
+
*ui = t;
if (!strcmp(a->attr.name, "iostat_enable") && *ui == 0)
diff --git a/fs/fscache/page.c b/fs/fscache/page.c
index 6b35fc4860a0..1de16a5a5c4e 100644
--- a/fs/fscache/page.c
+++ b/fs/fscache/page.c
@@ -776,6 +776,7 @@ static void fscache_write_op(struct fscache_operation *_op)
_enter("{OP%x,%d}", op->op.debug_id, atomic_read(&op->op.usage));
+again:
spin_lock(&object->lock);
cookie = object->cookie;
@@ -816,10 +817,6 @@ static void fscache_write_op(struct fscache_operation *_op)
goto superseded;
page = results[0];
_debug("gang %d [%lx]", n, page->index);
- if (page->index >= op->store_limit) {
- fscache_stat(&fscache_n_store_pages_over_limit);
- goto superseded;
- }
radix_tree_tag_set(&cookie->stores, page->index,
FSCACHE_COOKIE_STORING_TAG);
@@ -829,6 +826,9 @@ static void fscache_write_op(struct fscache_operation *_op)
spin_unlock(&cookie->stores_lock);
spin_unlock(&object->lock);
+ if (page->index >= op->store_limit)
+ goto discard_page;
+
fscache_stat(&fscache_n_store_pages);
fscache_stat(&fscache_n_cop_write_page);
ret = object->cache->ops->write_page(op, page);
@@ -844,6 +844,11 @@ static void fscache_write_op(struct fscache_operation *_op)
_leave("");
return;
+discard_page:
+ fscache_stat(&fscache_n_store_pages_over_limit);
+ fscache_end_page_write(object, page);
+ goto again;
+
superseded:
/* this writer is going away and there aren't any more things to
* write */
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index 1543aa1b2a93..8744bd773823 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -806,7 +806,7 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_alloc_parms ap = { .aflags = 0, };
unsigned int data_blocks = 0, ind_blocks = 0, rblocks;
- loff_t bytes, max_bytes, max_blks = UINT_MAX;
+ loff_t bytes, max_bytes, max_blks;
int error;
const loff_t pos = offset;
const loff_t count = len;
@@ -858,7 +858,8 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t
return error;
/* ap.allowed tells us how many blocks quota will allow
* us to write. Check if this reduces max_blks */
- if (ap.allowed && ap.allowed < max_blks)
+ max_blks = UINT_MAX;
+ if (ap.allowed)
max_blks = ap.allowed;
error = gfs2_inplace_reserve(ip, &ap);
diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h
index ad04b3acae2b..a81ed38d8442 100644
--- a/fs/gfs2/quota.h
+++ b/fs/gfs2/quota.h
@@ -43,6 +43,8 @@ static inline int gfs2_quota_lock_check(struct gfs2_inode *ip,
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
int ret;
+
+ ap->allowed = UINT_MAX; /* Assume we are permitted a whole lot */
if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
return 0;
ret = gfs2_quota_lock(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE);
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
index 30c4c9ebb693..e27317169697 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -207,8 +207,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry,
__func__, inode->i_ino, inode->i_mode, inode->i_nlink,
f->inocache->pino_nlink, inode->i_mapping->nrpages);
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
return 0;
fail:
@@ -428,8 +427,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
mutex_unlock(&dir_f->sem);
jffs2_complete_reservation(c);
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
return 0;
fail:
@@ -573,8 +571,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, umode_t mode
mutex_unlock(&dir_f->sem);
jffs2_complete_reservation(c);
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
return 0;
fail:
@@ -745,8 +742,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, umode_t mode
mutex_unlock(&dir_f->sem);
jffs2_complete_reservation(c);
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
return 0;
fail:
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index 2caf1682036d..85e2594fe95c 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -361,7 +361,6 @@ error_io:
ret = -EIO;
error:
mutex_unlock(&f->sem);
- jffs2_do_clear_inode(c, f);
iget_failed(inode);
return ERR_PTR(ret);
}
diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c
index 9d7551f5c32a..f217ae750adb 100644
--- a/fs/jfs/namei.c
+++ b/fs/jfs/namei.c
@@ -178,8 +178,7 @@ static int jfs_create(struct inode *dip, struct dentry *dentry, umode_t mode,
unlock_new_inode(ip);
iput(ip);
} else {
- unlock_new_inode(ip);
- d_instantiate(dentry, ip);
+ d_instantiate_new(dentry, ip);
}
out2:
@@ -313,8 +312,7 @@ static int jfs_mkdir(struct inode *dip, struct dentry *dentry, umode_t mode)
unlock_new_inode(ip);
iput(ip);
} else {
- unlock_new_inode(ip);
- d_instantiate(dentry, ip);
+ d_instantiate_new(dentry, ip);
}
out2:
@@ -1058,8 +1056,7 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry,
unlock_new_inode(ip);
iput(ip);
} else {
- unlock_new_inode(ip);
- d_instantiate(dentry, ip);
+ d_instantiate_new(dentry, ip);
}
out2:
@@ -1443,8 +1440,7 @@ static int jfs_mknod(struct inode *dir, struct dentry *dentry,
unlock_new_inode(ip);
iput(ip);
} else {
- unlock_new_inode(ip);
- d_instantiate(dentry, ip);
+ d_instantiate_new(dentry, ip);
}
out1:
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 0f397e62de5a..41c8ddbc80dc 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -1780,7 +1780,7 @@ static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *sta
return ret;
}
-static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct nfs4_state *state, const nfs4_stateid *stateid, int err)
+static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct nfs4_state *state, const nfs4_stateid *stateid, struct file_lock *fl, int err)
{
switch (err) {
default:
@@ -1827,7 +1827,11 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
return -EAGAIN;
case -ENOMEM:
case -NFS4ERR_DENIED:
- /* kill_proc(fl->fl_pid, SIGLOST, 1); */
+ if (fl) {
+ struct nfs4_lock_state *lsp = fl->fl_u.nfs4_fl.owner;
+ if (lsp)
+ set_bit(NFS_LOCK_LOST, &lsp->ls_flags);
+ }
return 0;
}
return err;
@@ -1863,7 +1867,7 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx,
err = nfs4_open_recover_helper(opendata, FMODE_READ);
}
nfs4_opendata_put(opendata);
- return nfs4_handle_delegation_recall_error(server, state, stateid, err);
+ return nfs4_handle_delegation_recall_error(server, state, stateid, NULL, err);
}
static void nfs4_open_confirm_prepare(struct rpc_task *task, void *calldata)
@@ -6157,7 +6161,7 @@ int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state,
if (err != 0)
return err;
err = _nfs4_do_setlk(state, F_SETLK, fl, NFS_LOCK_NEW);
- return nfs4_handle_delegation_recall_error(server, state, stateid, err);
+ return nfs4_handle_delegation_recall_error(server, state, stateid, fl, err);
}
struct nfs_release_lockowner_data {
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 83fba40396ae..44f5cea49699 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -1386,6 +1386,7 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_
struct inode *inode = state->inode;
struct nfs_inode *nfsi = NFS_I(inode);
struct file_lock *fl;
+ struct nfs4_lock_state *lsp;
int status = 0;
struct file_lock_context *flctx = inode->i_flctx;
struct list_head *list;
@@ -1426,7 +1427,9 @@ restart:
case -NFS4ERR_DENIED:
case -NFS4ERR_RECLAIM_BAD:
case -NFS4ERR_RECLAIM_CONFLICT:
- /* kill_proc(fl->fl_pid, SIGLOST, 1); */
+ lsp = fl->fl_u.nfs4_fl.owner;
+ if (lsp)
+ set_bit(NFS_LOCK_LOST, &lsp->ls_flags);
status = 0;
}
spin_lock(&flctx->flc_lock);
diff --git a/fs/nfs/nfs4sysctl.c b/fs/nfs/nfs4sysctl.c
index 0fbd3ab1be22..44a7bbbf92f8 100644
--- a/fs/nfs/nfs4sysctl.c
+++ b/fs/nfs/nfs4sysctl.c
@@ -31,7 +31,7 @@ static struct ctl_table nfs4_cb_sysctls[] = {
.data = &nfs_idmap_cache_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec,
},
{ }
};
diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c
index c9a1a491aa91..cd7f5b0abe84 100644
--- a/fs/nilfs2/namei.c
+++ b/fs/nilfs2/namei.c
@@ -50,8 +50,7 @@ static inline int nilfs_add_nondir(struct dentry *dentry, struct inode *inode)
{
int err = nilfs_add_link(dentry, inode);
if (!err) {
- d_instantiate(dentry, inode);
- unlock_new_inode(inode);
+ d_instantiate_new(dentry, inode);
return 0;
}
inode_dec_link_count(inode);
@@ -246,8 +245,7 @@ static int nilfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
goto out_fail;
nilfs_mark_inode_dirty(inode);
- d_instantiate(dentry, inode);
- unlock_new_inode(inode);
+ d_instantiate_new(dentry, inode);
out:
if (!err)
err = nilfs_transaction_commit(dir->i_sb);
diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c
index 164307b99405..1e0d8da0d3cd 100644
--- a/fs/ocfs2/acl.c
+++ b/fs/ocfs2/acl.c
@@ -314,7 +314,9 @@ struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type)
return ERR_PTR(ret);
}
+ down_read(&OCFS2_I(inode)->ip_xattr_sem);
acl = ocfs2_get_acl_nolock(inode, type, di_bh);
+ up_read(&OCFS2_I(inode)->ip_xattr_sem);
ocfs2_inode_unlock(inode, 0);
brelse(di_bh);
@@ -333,7 +335,9 @@ int ocfs2_acl_chmod(struct inode *inode, struct buffer_head *bh)
if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
return 0;
+ down_read(&OCFS2_I(inode)->ip_xattr_sem);
acl = ocfs2_get_acl_nolock(inode, ACL_TYPE_ACCESS, bh);
+ up_read(&OCFS2_I(inode)->ip_xattr_sem);
if (IS_ERR(acl) || !acl)
return PTR_ERR(acl);
ret = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
@@ -364,8 +368,10 @@ int ocfs2_init_acl(handle_t *handle,
if (!S_ISLNK(inode->i_mode)) {
if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
+ down_read(&OCFS2_I(dir)->ip_xattr_sem);
acl = ocfs2_get_acl_nolock(dir, ACL_TYPE_DEFAULT,
dir_bh);
+ up_read(&OCFS2_I(dir)->ip_xattr_sem);
if (IS_ERR(acl))
return PTR_ERR(acl);
}
diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c
index 2ee7fe747cea..c55a9c47ac17 100644
--- a/fs/ocfs2/dlm/dlmdomain.c
+++ b/fs/ocfs2/dlm/dlmdomain.c
@@ -674,20 +674,6 @@ static void dlm_leave_domain(struct dlm_ctxt *dlm)
spin_unlock(&dlm->spinlock);
}
-int dlm_shutting_down(struct dlm_ctxt *dlm)
-{
- int ret = 0;
-
- spin_lock(&dlm_domain_lock);
-
- if (dlm->dlm_state == DLM_CTXT_IN_SHUTDOWN)
- ret = 1;
-
- spin_unlock(&dlm_domain_lock);
-
- return ret;
-}
-
void dlm_unregister_domain(struct dlm_ctxt *dlm)
{
int leave = 0;
diff --git a/fs/ocfs2/dlm/dlmdomain.h b/fs/ocfs2/dlm/dlmdomain.h
index fd6122a38dbd..8a9281411c18 100644
--- a/fs/ocfs2/dlm/dlmdomain.h
+++ b/fs/ocfs2/dlm/dlmdomain.h
@@ -28,7 +28,30 @@
extern spinlock_t dlm_domain_lock;
extern struct list_head dlm_domains;
-int dlm_shutting_down(struct dlm_ctxt *dlm);
+static inline int dlm_joined(struct dlm_ctxt *dlm)
+{
+ int ret = 0;
+
+ spin_lock(&dlm_domain_lock);
+ if (dlm->dlm_state == DLM_CTXT_JOINED)
+ ret = 1;
+ spin_unlock(&dlm_domain_lock);
+
+ return ret;
+}
+
+static inline int dlm_shutting_down(struct dlm_ctxt *dlm)
+{
+ int ret = 0;
+
+ spin_lock(&dlm_domain_lock);
+ if (dlm->dlm_state == DLM_CTXT_IN_SHUTDOWN)
+ ret = 1;
+ spin_unlock(&dlm_domain_lock);
+
+ return ret;
+}
+
void dlm_fire_domain_eviction_callbacks(struct dlm_ctxt *dlm,
int node_num);
diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c
index 4a338803e7e9..88149b4387c2 100644
--- a/fs/ocfs2/dlm/dlmrecovery.c
+++ b/fs/ocfs2/dlm/dlmrecovery.c
@@ -1377,6 +1377,15 @@ int dlm_mig_lockres_handler(struct o2net_msg *msg, u32 len, void *data,
if (!dlm_grab(dlm))
return -EINVAL;
+ if (!dlm_joined(dlm)) {
+ mlog(ML_ERROR, "Domain %s not joined! "
+ "lockres %.*s, master %u\n",
+ dlm->name, mres->lockname_len,
+ mres->lockname, mres->master);
+ dlm_put(dlm);
+ return -EINVAL;
+ }
+
BUG_ON(!(mres->flags & (DLM_MRES_RECOVERY|DLM_MRES_MIGRATION)));
real_master = mres->master;
diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index 13534f4fe5b5..722eb5bc9b8f 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -666,23 +666,24 @@ static int __ocfs2_journal_access(handle_t *handle,
/* we can safely remove this assertion after testing. */
if (!buffer_uptodate(bh)) {
mlog(ML_ERROR, "giving me a buffer that's not uptodate!\n");
- mlog(ML_ERROR, "b_blocknr=%llu\n",
- (unsigned long long)bh->b_blocknr);
+ mlog(ML_ERROR, "b_blocknr=%llu, b_state=0x%lx\n",
+ (unsigned long long)bh->b_blocknr, bh->b_state);
lock_buffer(bh);
/*
- * A previous attempt to write this buffer head failed.
- * Nothing we can do but to retry the write and hope for
- * the best.
+ * A previous transaction with a couple of buffer heads fail
+ * to checkpoint, so all the bhs are marked as BH_Write_EIO.
+ * For current transaction, the bh is just among those error
+ * bhs which previous transaction handle. We can't just clear
+ * its BH_Write_EIO and reuse directly, since other bhs are
+ * not written to disk yet and that will cause metadata
+ * inconsistency. So we should set fs read-only to avoid
+ * further damage.
*/
if (buffer_write_io_error(bh) && !buffer_uptodate(bh)) {
- clear_buffer_write_io_error(bh);
- set_buffer_uptodate(bh);
- }
-
- if (!buffer_uptodate(bh)) {
unlock_buffer(bh);
- return -EIO;
+ return ocfs2_error(osb->sb, "A previous attempt to "
+ "write this buffer head failed\n");
}
unlock_buffer(bh);
}
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 2de4c8a9340c..4f5141350af8 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -477,9 +477,8 @@ static int ocfs2_init_global_system_inodes(struct ocfs2_super *osb)
new = ocfs2_get_system_file_inode(osb, i, osb->slot_num);
if (!new) {
ocfs2_release_system_inodes(osb);
- status = -EINVAL;
+ status = ocfs2_is_soft_readonly(osb) ? -EROFS : -EINVAL;
mlog_errno(status);
- /* FIXME: Should ERROR_RO_FS */
mlog(ML_ERROR, "Unable to load system inode %d, "
"possibly corrupt fs?", i);
goto bail;
@@ -508,7 +507,7 @@ static int ocfs2_init_local_system_inodes(struct ocfs2_super *osb)
new = ocfs2_get_system_file_inode(osb, i, osb->slot_num);
if (!new) {
ocfs2_release_system_inodes(osb);
- status = -EINVAL;
+ status = ocfs2_is_soft_readonly(osb) ? -EROFS : -EINVAL;
mlog(ML_ERROR, "status=%d, sysfile=%d, slot=%d\n",
status, i, osb->slot_num);
goto bail;
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index 877830b05e12..4f0788232f2f 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -639,9 +639,11 @@ int ocfs2_calc_xattr_init(struct inode *dir,
si->value_len);
if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
+ down_read(&OCFS2_I(dir)->ip_xattr_sem);
acl_len = ocfs2_xattr_get_nolock(dir, dir_bh,
OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT,
"", NULL, 0);
+ up_read(&OCFS2_I(dir)->ip_xattr_sem);
if (acl_len > 0) {
a_size = ocfs2_xattr_entry_real_size(0, acl_len);
if (S_ISDIR(mode))
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 2b7fe41740f5..4c0989076151 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -95,6 +95,8 @@
#include "internal.h"
#include "fd.h"
+#include "../../lib/kstrtox.h"
+
/* NOTE:
* Implementing inode permission operations in /proc is almost
* certainly an error. Permission checks need to happen during
@@ -2040,8 +2042,33 @@ end_instantiate:
static int dname_to_vma_addr(struct dentry *dentry,
unsigned long *start, unsigned long *end)
{
- if (sscanf(dentry->d_name.name, "%lx-%lx", start, end) != 2)
+ const char *str = dentry->d_name.name;
+ unsigned long long sval, eval;
+ unsigned int len;
+
+ len = _parse_integer(str, 16, &sval);
+ if (len & KSTRTOX_OVERFLOW)
+ return -EINVAL;
+ if (sval != (unsigned long)sval)
return -EINVAL;
+ str += len;
+
+ if (*str != '-')
+ return -EINVAL;
+ str++;
+
+ len = _parse_integer(str, 16, &eval);
+ if (len & KSTRTOX_OVERFLOW)
+ return -EINVAL;
+ if (eval != (unsigned long)eval)
+ return -EINVAL;
+ str += len;
+
+ if (*str != '\0')
+ return -EINVAL;
+
+ *start = sval;
+ *end = eval;
return 0;
}
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 4dbe1e2daeca..5e1054f028af 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -654,7 +654,10 @@ static bool proc_sys_link_fill_cache(struct file *file,
struct ctl_table *table)
{
bool ret = true;
+
head = sysctl_head_grab(head);
+ if (IS_ERR(head))
+ return false;
if (S_ISLNK(table->mode)) {
/* It is not an error if we can not follow the link ignore it */
diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
index 3ebc70167e41..eb611bdd4725 100644
--- a/fs/reiserfs/namei.c
+++ b/fs/reiserfs/namei.c
@@ -687,8 +687,7 @@ static int reiserfs_create(struct inode *dir, struct dentry *dentry, umode_t mod
reiserfs_update_inode_transaction(inode);
reiserfs_update_inode_transaction(dir);
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
retval = journal_end(&th);
out_failed:
@@ -771,8 +770,7 @@ static int reiserfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode
goto out_failed;
}
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
retval = journal_end(&th);
out_failed:
@@ -871,8 +869,7 @@ static int reiserfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
/* the above add_entry did not update dir's stat data */
reiserfs_update_sd(&th, dir);
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
retval = journal_end(&th);
out_failed:
reiserfs_write_unlock(dir->i_sb);
@@ -1186,8 +1183,7 @@ static int reiserfs_symlink(struct inode *parent_dir,
goto out_failed;
}
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
retval = journal_end(&th);
out_failed:
reiserfs_write_unlock(parent_dir->i_sb);
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index c97b5a8d1e24..f34c545f4e54 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -611,8 +611,7 @@ static int udf_add_nondir(struct dentry *dentry, struct inode *inode)
if (fibh.sbh != fibh.ebh)
brelse(fibh.ebh);
brelse(fibh.sbh);
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
return 0;
}
@@ -722,8 +721,7 @@ static int udf_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
inc_nlink(dir);
dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb);
mark_inode_dirty(dir);
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
if (fibh.sbh != fibh.ebh)
brelse(fibh.ebh);
brelse(fibh.sbh);
diff --git a/fs/udf/super.c b/fs/udf/super.c
index ee09c97f3ab2..159977ec8e54 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -2073,8 +2073,9 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
bool lvid_open = false;
uopt.flags = (1 << UDF_FLAG_USE_AD_IN_ICB) | (1 << UDF_FLAG_STRICT);
- uopt.uid = INVALID_UID;
- uopt.gid = INVALID_GID;
+ /* By default we'll use overflow[ug]id when UDF inode [ug]id == -1 */
+ uopt.uid = make_kuid(current_user_ns(), overflowuid);
+ uopt.gid = make_kgid(current_user_ns(), overflowgid);
uopt.umask = 0;
uopt.fmode = UDF_INVALID_MODE;
uopt.dmode = UDF_INVALID_MODE;
diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c
index 47966554317c..2ec7689c25cf 100644
--- a/fs/ufs/namei.c
+++ b/fs/ufs/namei.c
@@ -38,8 +38,7 @@ static inline int ufs_add_nondir(struct dentry *dentry, struct inode *inode)
{
int err = ufs_add_link(dentry, inode);
if (!err) {
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
return 0;
}
inode_dec_link_count(inode);
@@ -191,8 +190,7 @@ static int ufs_mkdir(struct inode * dir, struct dentry * dentry, umode_t mode)
if (err)
goto out_fail;
- unlock_new_inode(inode);
- d_instantiate(dentry, inode);
+ d_instantiate_new(dentry, inode);
return 0;
out_fail:
diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index e1e7fe3b5424..b663b756f552 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -1924,6 +1924,93 @@ xfs_alloc_space_available(
}
/*
+ * Check the agfl fields of the agf for inconsistency or corruption. The purpose
+ * is to detect an agfl header padding mismatch between current and early v5
+ * kernels. This problem manifests as a 1-slot size difference between the
+ * on-disk flcount and the active [first, last] range of a wrapped agfl. This
+ * may also catch variants of agfl count corruption unrelated to padding. Either
+ * way, we'll reset the agfl and warn the user.
+ *
+ * Return true if a reset is required before the agfl can be used, false
+ * otherwise.
+ */
+static bool
+xfs_agfl_needs_reset(
+ struct xfs_mount *mp,
+ struct xfs_agf *agf)
+{
+ uint32_t f = be32_to_cpu(agf->agf_flfirst);
+ uint32_t l = be32_to_cpu(agf->agf_fllast);
+ uint32_t c = be32_to_cpu(agf->agf_flcount);
+ int agfl_size = XFS_AGFL_SIZE(mp);
+ int active;
+
+ /* no agfl header on v4 supers */
+ if (!xfs_sb_version_hascrc(&mp->m_sb))
+ return false;
+
+ /*
+ * The agf read verifier catches severe corruption of these fields.
+ * Repeat some sanity checks to cover a packed -> unpacked mismatch if
+ * the verifier allows it.
+ */
+ if (f >= agfl_size || l >= agfl_size)
+ return true;
+ if (c > agfl_size)
+ return true;
+
+ /*
+ * Check consistency between the on-disk count and the active range. An
+ * agfl padding mismatch manifests as an inconsistent flcount.
+ */
+ if (c && l >= f)
+ active = l - f + 1;
+ else if (c)
+ active = agfl_size - f + l + 1;
+ else
+ active = 0;
+
+ return active != c;
+}
+
+/*
+ * Reset the agfl to an empty state. Ignore/drop any existing blocks since the
+ * agfl content cannot be trusted. Warn the user that a repair is required to
+ * recover leaked blocks.
+ *
+ * The purpose of this mechanism is to handle filesystems affected by the agfl
+ * header padding mismatch problem. A reset keeps the filesystem online with a
+ * relatively minor free space accounting inconsistency rather than suffer the
+ * inevitable crash from use of an invalid agfl block.
+ */
+static void
+xfs_agfl_reset(
+ struct xfs_trans *tp,
+ struct xfs_buf *agbp,
+ struct xfs_perag *pag)
+{
+ struct xfs_mount *mp = tp->t_mountp;
+ struct xfs_agf *agf = XFS_BUF_TO_AGF(agbp);
+
+ ASSERT(pag->pagf_agflreset);
+ trace_xfs_agfl_reset(mp, agf, 0, _RET_IP_);
+
+ xfs_warn(mp,
+ "WARNING: Reset corrupted AGFL on AG %u. %d blocks leaked. "
+ "Please unmount and run xfs_repair.",
+ pag->pag_agno, pag->pagf_flcount);
+
+ agf->agf_flfirst = 0;
+ agf->agf_fllast = cpu_to_be32(XFS_AGFL_SIZE(mp) - 1);
+ agf->agf_flcount = 0;
+ xfs_alloc_log_agf(tp, agbp, XFS_AGF_FLFIRST | XFS_AGF_FLLAST |
+ XFS_AGF_FLCOUNT);
+
+ pag->pagf_flcount = 0;
+ pag->pagf_agflreset = false;
+}
+
+/*
* Decide whether to use this allocation group for this allocation.
* If so, fix up the btree freelist's size.
*/
@@ -1983,6 +2070,10 @@ xfs_alloc_fix_freelist(
}
}
+ /* reset a padding mismatched agfl before final free space check */
+ if (pag->pagf_agflreset)
+ xfs_agfl_reset(tp, agbp, pag);
+
/* If there isn't enough total space or single-extent, reject it. */
need = xfs_alloc_min_freelist(mp, pag);
if (!xfs_alloc_space_available(args, need, flags))
@@ -2121,6 +2212,7 @@ xfs_alloc_get_freelist(
agf->agf_flfirst = 0;
pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno));
+ ASSERT(!pag->pagf_agflreset);
be32_add_cpu(&agf->agf_flcount, -1);
xfs_trans_agflist_delta(tp, -1);
pag->pagf_flcount--;
@@ -2226,6 +2318,7 @@ xfs_alloc_put_freelist(
agf->agf_fllast = 0;
pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno));
+ ASSERT(!pag->pagf_agflreset);
be32_add_cpu(&agf->agf_flcount, 1);
xfs_trans_agflist_delta(tp, 1);
pag->pagf_flcount++;
@@ -2417,6 +2510,7 @@ xfs_alloc_read_agf(
pag->pagb_count = 0;
pag->pagb_tree = RB_ROOT;
pag->pagf_init = 1;
+ pag->pagf_agflreset = xfs_agfl_needs_reset(mp, agf);
}
#ifdef DEBUG
else if (!XFS_FORCED_SHUTDOWN(mp)) {
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index f949818fa1c7..fb9636cc927c 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -130,9 +130,6 @@ xfs_attr_get(
if (XFS_FORCED_SHUTDOWN(ip->i_mount))
return -EIO;
- if (!xfs_inode_hasattr(ip))
- return -ENOATTR;
-
error = xfs_attr_args_init(&args, ip, name, flags);
if (error)
return error;
@@ -417,9 +414,6 @@ xfs_attr_remove(
if (XFS_FORCED_SHUTDOWN(dp->i_mount))
return -EIO;
- if (!xfs_inode_hasattr(dp))
- return -ENOATTR;
-
error = xfs_attr_args_init(&args, dp, name, flags);
if (error)
return error;
diff --git a/fs/xfs/xfs_discard.c b/fs/xfs/xfs_discard.c
index e85a9519a5ae..64ad05cb831a 100644
--- a/fs/xfs/xfs_discard.c
+++ b/fs/xfs/xfs_discard.c
@@ -50,19 +50,19 @@ xfs_trim_extents(
pag = xfs_perag_get(mp, agno);
- error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
- if (error || !agbp)
- goto out_put_perag;
-
- cur = xfs_allocbt_init_cursor(mp, NULL, agbp, agno, XFS_BTNUM_CNT);
-
/*
* Force out the log. This means any transactions that might have freed
- * space before we took the AGF buffer lock are now on disk, and the
+ * space before we take the AGF buffer lock are now on disk, and the
* volatile disk cache is flushed.
*/
xfs_log_force(mp, XFS_LOG_SYNC);
+ error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
+ if (error || !agbp)
+ goto out_put_perag;
+
+ cur = xfs_allocbt_init_cursor(mp, NULL, agbp, agno, XFS_BTNUM_CNT);
+
/*
* Look up the longest btree in the AGF and start with it.
*/
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index f52c72a1a06f..73b725f965eb 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -3323,8 +3323,6 @@ maybe_sleep:
*/
if (iclog->ic_state & XLOG_STATE_IOERROR)
return -EIO;
- if (log_flushed)
- *log_flushed = 1;
} else {
no_sleep:
@@ -3432,8 +3430,6 @@ try_again:
xlog_wait(&iclog->ic_prev->ic_write_wait,
&log->l_icloglock);
- if (log_flushed)
- *log_flushed = 1;
already_slept = 1;
goto try_again;
}
@@ -3467,9 +3463,6 @@ try_again:
*/
if (iclog->ic_state & XLOG_STATE_IOERROR)
return -EIO;
-
- if (log_flushed)
- *log_flushed = 1;
} else { /* just return */
spin_unlock(&log->l_icloglock);
}
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index b57098481c10..ae3e52749f20 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -278,6 +278,7 @@ typedef struct xfs_perag {
char pagi_inodeok; /* The agi is ok for inodes */
__uint8_t pagf_levels[XFS_BTNUM_AGF];
/* # of levels in bno & cnt btree */
+ bool pagf_agflreset; /* agfl requires reset before use */
__uint32_t pagf_flcount; /* count of blocks in freelist */
xfs_extlen_t pagf_freeblks; /* total free blocks */
xfs_extlen_t pagf_longest; /* longest free space */
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 877079eb0f8f..cc6fa64821d2 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -1485,7 +1485,7 @@ TRACE_EVENT(xfs_trans_commit_lsn,
__entry->lsn)
);
-TRACE_EVENT(xfs_agf,
+DECLARE_EVENT_CLASS(xfs_agf_class,
TP_PROTO(struct xfs_mount *mp, struct xfs_agf *agf, int flags,
unsigned long caller_ip),
TP_ARGS(mp, agf, flags, caller_ip),
@@ -1541,6 +1541,13 @@ TRACE_EVENT(xfs_agf,
__entry->longest,
(void *)__entry->caller_ip)
);
+#define DEFINE_AGF_EVENT(name) \
+DEFINE_EVENT(xfs_agf_class, name, \
+ TP_PROTO(struct xfs_mount *mp, struct xfs_agf *agf, int flags, \
+ unsigned long caller_ip), \
+ TP_ARGS(mp, agf, flags, caller_ip))
+DEFINE_AGF_EVENT(xfs_agf);
+DEFINE_AGF_EVENT(xfs_agfl_reset);
TRACE_EVENT(xfs_free_extent,
TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agblock_t agbno,
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h
index 4814cf971048..25b793325b09 100644
--- a/include/asm-generic/pgtable.h
+++ b/include/asm-generic/pgtable.h
@@ -237,6 +237,21 @@ extern void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
extern pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp);
#endif
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+/*
+ * This is an implementation of pmdp_establish() that is only suitable for an
+ * architecture that doesn't have hardware dirty/accessed bits. In this case we
+ * can't race with CPU which sets these bits and non-atomic aproach is fine.
+ */
+static inline pmd_t generic_pmdp_establish(struct vm_area_struct *vma,
+ unsigned long address, pmd_t *pmdp, pmd_t pmd)
+{
+ pmd_t old_pmd = *pmdp;
+ set_pmd_at(vma->vm_mm, address, pmdp, pmd);
+ return old_pmd;
+}
+#endif
+
#ifndef __HAVE_ARCH_PMDP_INVALIDATE
extern void pmdp_invalidate(struct vm_area_struct *vma, unsigned long address,
pmd_t *pmdp);
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index c066f6b56e58..9df992b818a4 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -238,6 +238,7 @@ extern seqlock_t rename_lock;
* These are the low-level FS interfaces to the dcache..
*/
extern void d_instantiate(struct dentry *, struct inode *);
+extern void d_instantiate_new(struct dentry *, struct inode *);
extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *);
extern int d_instantiate_no_diralias(struct dentry *, struct inode *);
extern void __d_drop(struct dentry *dentry);
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index 2cd41e0cd394..b5ed9b114317 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -145,7 +145,7 @@ 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 26
-#define APPS_EVENT_LAST_ID 0x0C5B
+#define APPS_EVENT_LAST_ID 0xC7A
#define MSG_SSID_0 0
#define MSG_SSID_0_LAST 125
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 250f4d1ce9c5..f5f4e7871865 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -964,9 +964,9 @@ static inline struct file *get_file(struct file *f)
/* Page cache limit. The filesystems should put that into their s_maxbytes
limits, otherwise bad things can happen in VM. */
#if BITS_PER_LONG==32
-#define MAX_LFS_FILESIZE (((loff_t)PAGE_CACHE_SIZE << (BITS_PER_LONG-1))-1)
+#define MAX_LFS_FILESIZE ((loff_t)ULONG_MAX << PAGE_SHIFT)
#elif BITS_PER_LONG==64
-#define MAX_LFS_FILESIZE ((loff_t)0x7fffffffffffffffLL)
+#define MAX_LFS_FILESIZE ((loff_t)LLONG_MAX)
#endif
#define FL_POSIX 1
diff --git a/include/linux/habmm.h b/include/linux/habmm.h
index 842cd27fd372..cd4e2506f9ee 100644
--- a/include/linux/habmm.h
+++ b/include/linux/habmm.h
@@ -14,7 +14,7 @@
#ifndef HABMM_H
#define HABMM_H
-#include <uapi/linux/habmmid.h>
+#include "linux/habmmid.h"
#define HAB_API_VER_DEF(_MAJOR_, _MINOR_) \
((_MAJOR_&0xFF)<<16 | (_MINOR_&0xFFF))
diff --git a/include/linux/iio/imu/mpu.h b/include/linux/iio/imu/mpu.h
new file mode 100644
index 000000000000..4dbb86cad6d1
--- /dev/null
+++ b/include/linux/iio/imu/mpu.h
@@ -0,0 +1,124 @@
+/*
+* Copyright (C) 2012-2017 InvenSense, 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 __MPU_H_
+#define __MPU_H_
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#endif
+
+enum secondary_slave_type {
+ SECONDARY_SLAVE_TYPE_NONE,
+ SECONDARY_SLAVE_TYPE_ACCEL,
+ SECONDARY_SLAVE_TYPE_COMPASS,
+ SECONDARY_SLAVE_TYPE_PRESSURE,
+ SECONDARY_SLAVE_TYPE_ALS,
+
+ SECONDARY_SLAVE_TYPE_TYPES
+};
+
+enum ext_slave_id {
+ ID_INVALID = 0,
+ GYRO_ID_MPU3050,
+ GYRO_ID_MPU6050A2,
+ GYRO_ID_MPU6050B1,
+ GYRO_ID_MPU6050B1_NO_ACCEL,
+ GYRO_ID_ITG3500,
+
+ ACCEL_ID_LIS331,
+ ACCEL_ID_LSM303DLX,
+ ACCEL_ID_LIS3DH,
+ ACCEL_ID_KXSD9,
+ ACCEL_ID_KXTF9,
+ ACCEL_ID_BMA150,
+ ACCEL_ID_BMA222,
+ ACCEL_ID_BMA250,
+ ACCEL_ID_ADXL34X,
+ ACCEL_ID_MMA8450,
+ ACCEL_ID_MMA845X,
+ ACCEL_ID_MPU6050,
+
+ COMPASS_ID_AK8963,
+ COMPASS_ID_AK8975,
+ COMPASS_ID_AK8972,
+ COMPASS_ID_AMI30X,
+ COMPASS_ID_AMI306,
+ COMPASS_ID_YAS529,
+ COMPASS_ID_YAS530,
+ COMPASS_ID_HMC5883,
+ COMPASS_ID_LSM303DLH,
+ COMPASS_ID_LSM303DLM,
+ COMPASS_ID_MMC314X,
+ COMPASS_ID_HSCDTD002B,
+ COMPASS_ID_HSCDTD004A,
+ COMPASS_ID_MLX90399,
+ COMPASS_ID_AK09911,
+ COMPASS_ID_AK09912,
+ COMPASS_ID_AK09916,
+
+ PRESSURE_ID_BMP085,
+ PRESSURE_ID_BMP280,
+
+ ALS_ID_APDS_9900,
+ ALS_ID_APDS_9930,
+ ALS_ID_TSL_2772,
+};
+
+#define INV_PROD_KEY(ver, rev) (ver * 100 + rev)
+/**
+ * struct mpu_platform_data - Platform data for the mpu driver
+ * @int_config: Bits [7:3] of the int config register.
+ * @level_shifter: 0: VLogic, 1: VDD
+ * @orientation: Orientation matrix of the gyroscope
+ * @sec_slave_type: secondary slave device type, can be compass, accel, etc
+ * @sec_slave_id: id of the secondary slave device
+ * @secondary_i2c_address: secondary device's i2c address
+ * @secondary_orientation: secondary device's orientation matrix
+ * @aux_slave_type: auxiliary slave. Another slave device type
+ * @aux_slave_id: auxiliary slave ID.
+ * @aux_i2c_addr: auxiliary device I2C address.
+ * @read_only_slave_type: read only slave type.
+ * @read_only_slave_id: read only slave device ID.
+ * @read_only_i2c_addr: read only slave device address.
+ *
+ * Contains platform specific information on how to configure the MPU3050 to
+ * work on this platform. The orientation matricies are 3x3 rotation matricies
+ * that are applied to the data to rotate from the mounting orientation to the
+ * platform orientation. The values must be one of 0, 1, or -1 and each row and
+ * column should have exactly 1 non-zero value.
+ */
+struct mpu_platform_data {
+ __u8 int_config;
+ __u8 level_shifter;
+ __s8 orientation[9];
+ enum secondary_slave_type sec_slave_type;
+ enum ext_slave_id sec_slave_id;
+ __u16 secondary_i2c_addr;
+ __s8 secondary_orientation[9];
+ enum secondary_slave_type aux_slave_type;
+ enum ext_slave_id aux_slave_id;
+ __u16 aux_i2c_addr;
+ enum secondary_slave_type read_only_slave_type;
+ enum ext_slave_id read_only_slave_id;
+ __u16 read_only_i2c_addr;
+#ifdef CONFIG_OF
+ int (*power_on)(struct mpu_platform_data *);
+ int (*power_off)(struct mpu_platform_data *);
+ struct regulator *vdd_ana;
+ struct regulator *vdd_i2c;
+#endif
+};
+
+#endif /* __MPU_H_ */
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 64d0797cc3a7..6828063842df 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -260,6 +260,7 @@ enum power_supply_property {
POWER_SUPPLY_PROP_PD_VOLTAGE_MAX,
POWER_SUPPLY_PROP_PD_VOLTAGE_MIN,
POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE,
/* Local extensions of type int64_t */
POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
/* Properties of type `const char *' */
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index c59803dc68de..be1ab158ad1a 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -377,6 +377,8 @@ extern int swsusp_page_is_forbidden(struct page *);
extern void swsusp_set_page_free(struct page *);
extern void swsusp_unset_page_free(struct page *);
extern unsigned long get_safe_page(gfp_t gfp_mask);
+extern asmlinkage int swsusp_arch_suspend(void);
+extern asmlinkage int swsusp_arch_resume(void);
extern void hibernation_set_ops(const struct platform_hibernation_ops *ops);
extern int hibernate(void);
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 2260f92f1492..5b6df1a8dc74 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -324,7 +324,7 @@ struct tcp_sock {
/* Receiver queue space */
struct {
- int space;
+ u32 space;
u32 seq;
u32 time;
} rcvq_space;
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 4cde40dac778..dc35fa77b3d2 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -57,6 +57,9 @@
/* big enough to hold our biggest descriptor */
#define USB_COMP_EP0_BUFSIZ 4096
+/* OS feature descriptor length <= 4kB */
+#define USB_COMP_EP0_OS_DESC_BUFSIZ 4096
+
#define USB_MS_TO_HS_INTERVAL(x) (ilog2((x * 1000 / 125)) + 1)
struct usb_configuration;
diff --git a/include/net/cnss.h b/include/net/cnss.h
index d6f27759af17..ea0082dc1219 100644
--- a/include/net/cnss.h
+++ b/include/net/cnss.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -55,6 +55,7 @@ struct cnss_wlan_driver {
int (*suspend)(struct pci_dev *pdev, pm_message_t state);
int (*resume)(struct pci_dev *pdev);
void (*modem_status)(struct pci_dev *, int state);
+ void (*update_status)(struct pci_dev *pdev, uint32_t status);
struct cnss_wlan_runtime_ops *runtime_ops;
const struct pci_device_id *id_table;
};
@@ -93,11 +94,14 @@ struct cnss_platform_cap {
u32 cap_flag;
};
-/* WLAN driver status */
+/* WLAN driver status, keep it aligned with cnss2 */
enum cnss_driver_status {
CNSS_UNINITIALIZED,
CNSS_INITIALIZED,
- CNSS_LOAD_UNLOAD
+ CNSS_LOAD_UNLOAD,
+ CNSS_RECOVERY,
+ CNSS_FW_DOWN,
+ CNSS_SSR_FAIL,
};
enum cnss_runtime_request {
diff --git a/include/net/ip.h b/include/net/ip.h
index 17997b48102d..81c7408deb83 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -282,6 +282,13 @@ int ip_decrease_ttl(struct iphdr *iph)
return --iph->ttl;
}
+static inline int ip_mtu_locked(const struct dst_entry *dst)
+{
+ const struct rtable *rt = (const struct rtable *)dst;
+
+ return rt->rt_mtu_locked || dst_metric_locked(dst, RTAX_MTU);
+}
+
static inline
int ip_dont_fragment(const struct sock *sk, const struct dst_entry *dst)
{
@@ -289,7 +296,7 @@ int ip_dont_fragment(const struct sock *sk, const struct dst_entry *dst)
return pmtudisc == IP_PMTUDISC_DO ||
(pmtudisc == IP_PMTUDISC_WANT &&
- !(dst_metric_locked(dst, RTAX_MTU)));
+ !ip_mtu_locked(dst));
}
static inline bool ip_sk_accept_pmtu(const struct sock *sk)
@@ -315,7 +322,7 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
struct net *net = dev_net(dst->dev);
if (net->ipv4.sysctl_ip_fwd_use_pmtu ||
- dst_metric_locked(dst, RTAX_MTU) ||
+ ip_mtu_locked(dst) ||
!forwarding)
return dst_mtu(dst);
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index bda1721e9622..3afb7c4c7098 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -56,6 +56,7 @@ struct fib_nh_exception {
int fnhe_genid;
__be32 fnhe_daddr;
u32 fnhe_pmtu;
+ bool fnhe_mtu_locked;
__be32 fnhe_gw;
unsigned long fnhe_expires;
struct rtable __rcu *fnhe_rth_input;
diff --git a/include/net/llc_conn.h b/include/net/llc_conn.h
index ea985aa7a6c5..df528a623548 100644
--- a/include/net/llc_conn.h
+++ b/include/net/llc_conn.h
@@ -104,7 +104,7 @@ void llc_sk_reset(struct sock *sk);
/* Access to a connection */
int llc_conn_state_process(struct sock *sk, struct sk_buff *skb);
-void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb);
+int llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb);
void llc_conn_rtn_pdu(struct sock *sk, struct sk_buff *skb);
void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, u8 first_p_bit);
void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit);
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 4ca1c04a6f1f..c1744c2f8aca 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -3910,7 +3910,7 @@ static inline int ieee80211_sta_ps_transition_ni(struct ieee80211_sta *sta,
* The TX headroom reserved by mac80211 for its own tx_status functions.
* This is enough for the radiotap header.
*/
-#define IEEE80211_TX_STATUS_HEADROOM 14
+#define IEEE80211_TX_STATUS_HEADROOM ALIGN(14, 4)
/**
* ieee80211_sta_set_buffered - inform mac80211 about driver-buffered frames
diff --git a/include/net/regulatory.h b/include/net/regulatory.h
index ebc5a2ed8631..f83cacce3308 100644
--- a/include/net/regulatory.h
+++ b/include/net/regulatory.h
@@ -78,7 +78,7 @@ struct regulatory_request {
int wiphy_idx;
enum nl80211_reg_initiator initiator;
enum nl80211_user_reg_hint_type user_reg_hint_type;
- char alpha2[2];
+ char alpha2[3];
enum nl80211_dfs_regions dfs_region;
bool intersect;
bool processed;
diff --git a/include/net/route.h b/include/net/route.h
index 3adb9c724818..11dfd0df0e67 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -64,7 +64,8 @@ struct rtable {
__be32 rt_gateway;
/* Miscellaneous cached information */
- u32 rt_pmtu;
+ u32 rt_mtu_locked:1,
+ rt_pmtu:31;
u32 rt_table_id;
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index ee65bdae9971..48fe32252e8d 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -11587,4 +11587,9 @@ struct admx_sec_primary_mic_ch {
uint16_t reserved1;
} __packed;
+/*
+* ID of the DTMF Detection module.
+*/
+#define AUDPROC_MODULE_ID_DTMF_DETECTION 0x00010940
+
#endif /*_APR_AUDIO_V2_H_ */
diff --git a/include/sound/q6adm-v2.h b/include/sound/q6adm-v2.h
index 4545f2cd3826..f04daf310182 100644
--- a/include/sound/q6adm-v2.h
+++ b/include/sound/q6adm-v2.h
@@ -128,7 +128,7 @@ int adm_pack_and_set_one_pp_param(int port_id, int copp_idx,
int adm_open(int port, int path, int rate, int mode, int topology,
int perf_mode, uint16_t bits_per_sample,
- int app_type, int acdbdev_id);
+ int app_type, int acdbdev_id, u32 copp_token);
int adm_map_rtac_block(struct rtac_cal_block_data *cal_block);
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index 9df3e77da05b..3523fac586ce 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -309,6 +309,10 @@ int q6asm_open_write_v4(struct audio_client *ac, uint32_t format,
int q6asm_open_write_v5(struct audio_client *ac, uint32_t format,
uint16_t bits_per_sample);
+
+int q6asm_open_write_with_retry(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample);
+
int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format,
uint16_t bits_per_sample, int32_t stream_id,
bool is_gapless_mode);
diff --git a/include/trace/events/timer.h b/include/trace/events/timer.h
index 073b9ac245ba..e844556794dc 100644
--- a/include/trace/events/timer.h
+++ b/include/trace/events/timer.h
@@ -125,6 +125,20 @@ DEFINE_EVENT(timer_class, timer_cancel,
TP_ARGS(timer)
);
+#define decode_clockid(type) \
+ __print_symbolic(type, \
+ { CLOCK_REALTIME, "CLOCK_REALTIME" }, \
+ { CLOCK_MONOTONIC, "CLOCK_MONOTONIC" }, \
+ { CLOCK_BOOTTIME, "CLOCK_BOOTTIME" }, \
+ { CLOCK_TAI, "CLOCK_TAI" })
+
+#define decode_hrtimer_mode(mode) \
+ __print_symbolic(mode, \
+ { HRTIMER_MODE_ABS, "ABS" }, \
+ { HRTIMER_MODE_REL, "REL" }, \
+ { HRTIMER_MODE_ABS_PINNED, "ABS|PINNED" }, \
+ { HRTIMER_MODE_REL_PINNED, "REL|PINNED" })
+
/**
* hrtimer_init - called when the hrtimer is initialized
* @hrtimer: pointer to struct hrtimer
@@ -151,10 +165,8 @@ TRACE_EVENT(hrtimer_init,
),
TP_printk("hrtimer=%p clockid=%s mode=%s", __entry->hrtimer,
- __entry->clockid == CLOCK_REALTIME ?
- "CLOCK_REALTIME" : "CLOCK_MONOTONIC",
- __entry->mode == HRTIMER_MODE_ABS ?
- "HRTIMER_MODE_ABS" : "HRTIMER_MODE_REL")
+ decode_clockid(__entry->clockid),
+ decode_hrtimer_mode(__entry->mode))
);
/**
diff --git a/include/uapi/drm/msm_drm_pp.h b/include/uapi/drm/msm_drm_pp.h
index 9ed3a13953ef..5f5ca0345140 100644
--- a/include/uapi/drm/msm_drm_pp.h
+++ b/include/uapi/drm/msm_drm_pp.h
@@ -52,6 +52,34 @@ struct drm_msm_pa_vlut {
__u32 val[PA_VLUT_SIZE];
};
+#define PA_HSIC_HUE_ENABLE (1 << 0)
+#define PA_HSIC_SAT_ENABLE (1 << 1)
+#define PA_HSIC_VAL_ENABLE (1 << 2)
+#define PA_HSIC_CONT_ENABLE (1 << 3)
+#define PA_HSIC_LEFT_DISPLAY_ONLY (1 << 4)
+#define PA_HSIC_RIGHT_DISPLAY_ONLY (1 << 5)
+/**
+ * struct drm_msm_pa_hsic - pa hsic feature structure
+ * @flags: flags for the feature customization, values can be:
+ * - PA_HSIC_HUE_ENABLE: Enable hue adjustment
+ * - PA_HSIC_SAT_ENABLE: Enable saturation adjustment
+ * - PA_HSIC_VAL_ENABLE: Enable value adjustment
+ * - PA_HSIC_CONT_ENABLE: Enable contrast adjustment
+ *
+ * @hue: hue setting
+ * @saturation: saturation setting
+ * @value: value setting
+ * @contrast: contrast setting
+ */
+#define DRM_MSM_PA_HSIC
+struct drm_msm_pa_hsic {
+ __u64 flags;
+ __u32 hue;
+ __u32 saturation;
+ __u32 value;
+ __u32 contrast;
+};
+
/* struct drm_msm_memcol - Memory color feature strucuture.
* Skin, sky, foliage features are supported.
* @prot_flags: Bit mask for enabling protection feature.
@@ -79,4 +107,94 @@ struct drm_msm_memcol {
__u32 val_region;
};
+#define GAMUT_3D_MODE_17 1
+#define GAMUT_3D_MODE_5 2
+#define GAMUT_3D_MODE_13 3
+
+#define GAMUT_3D_MODE17_TBL_SZ 1229
+#define GAMUT_3D_MODE5_TBL_SZ 32
+#define GAMUT_3D_MODE13_TBL_SZ 550
+#define GAMUT_3D_SCALE_OFF_SZ 16
+#define GAMUT_3D_SCALEB_OFF_SZ 12
+#define GAMUT_3D_TBL_NUM 4
+#define GAMUT_3D_SCALE_OFF_TBL_NUM 3
+#define GAMUT_3D_MAP_EN (1 << 0)
+
+/**
+ * struct drm_msm_3d_col - 3d gamut color component structure
+ * @c0: Holds c0 value
+ * @c2_c1: Holds c2/c1 values
+ */
+struct drm_msm_3d_col {
+ __u32 c2_c1;
+ __u32 c0;
+};
+/**
+ * struct drm_msm_3d_gamut - 3d gamut feature structure
+ * @flags: flags for the feature values are:
+ * 0 - no map
+ * GAMUT_3D_MAP_EN - enable map
+ * @mode: lut mode can take following values:
+ * - GAMUT_3D_MODE_17
+ * - GAMUT_3D_MODE_5
+ * - GAMUT_3D_MODE_13
+ * @scale_off: Scale offset table
+ * @col: Color component tables
+ */
+struct drm_msm_3d_gamut {
+ __u64 flags;
+ __u32 mode;
+ __u32 scale_off[GAMUT_3D_SCALE_OFF_TBL_NUM][GAMUT_3D_SCALE_OFF_SZ];
+ struct drm_msm_3d_col col[GAMUT_3D_TBL_NUM][GAMUT_3D_MODE17_TBL_SZ];
+};
+
+#define PGC_TBL_LEN 512
+#define PGC_8B_ROUND (1 << 0)
+/**
+ * struct drm_msm_pgc_lut - pgc lut feature structure
+ * @flags: flags for the featue values can be:
+ * - PGC_8B_ROUND
+ * @c0: color0 component lut
+ * @c1: color1 component lut
+ * @c2: color2 component lut
+ */
+struct drm_msm_pgc_lut {
+ __u64 flags;
+ __u32 c0[PGC_TBL_LEN];
+ __u32 c1[PGC_TBL_LEN];
+ __u32 c2[PGC_TBL_LEN];
+};
+
+
+#define IGC_TBL_LEN 256
+#define IGC_DITHER_ENABLE (1 << 0)
+/**
+ * struct drm_msm_igc_lut - igc lut feature structure
+ * @flags: flags for the feature customization, values can be:
+ * - IGC_DITHER_ENABLE: Enable dither functionality
+ * @c0: color0 component lut
+ * @c1: color1 component lut
+ * @c2: color2 component lut
+ * @strength: dither strength, considered valid when IGC_DITHER_ENABLE
+ * is set in flags. Strength value based on source bit width.
+ */
+struct drm_msm_igc_lut {
+ __u64 flags;
+ __u32 c0[IGC_TBL_LEN];
+ __u32 c1[IGC_TBL_LEN];
+ __u32 c2[IGC_TBL_LEN];
+ __u32 strength;
+};
+
+#define HIST_V_SIZE 256
+/**
+ * struct drm_msm_hist - histogram feature structure
+ * @flags: for customizing operations
+ * @data: histogram data
+ */
+struct drm_msm_hist {
+ __u64 flags;
+ __u32 data[HIST_V_SIZE];
+};
+
#endif /* _MSM_DRM_PP_H_ */
diff --git a/include/uapi/drm/virtgpu_drm.h b/include/uapi/drm/virtgpu_drm.h
index fc9e2d6e5e2f..232367124712 100644
--- a/include/uapi/drm/virtgpu_drm.h
+++ b/include/uapi/drm/virtgpu_drm.h
@@ -60,6 +60,7 @@ struct drm_virtgpu_execbuffer {
};
#define VIRTGPU_PARAM_3D_FEATURES 1 /* do we have 3D features in the hw */
+#define VIRTGPU_PARAM_CAPSET_QUERY_FIX 2 /* do we have the capset fix */
struct drm_virtgpu_getparam {
uint64_t param;
diff --git a/include/uapi/linux/hab_ioctl.h b/include/uapi/linux/hab_ioctl.h
index 83b5da236888..70e16433044e 100644
--- a/include/uapi/linux/hab_ioctl.h
+++ b/include/uapi/linux/hab_ioctl.h
@@ -69,7 +69,6 @@ struct hab_info {
};
#define HAB_IOC_TYPE 0x0A
-#define HAB_MAX_MSG_SIZEBYTES 0x1000
#define IOCTL_HAB_SEND \
_IOW(HAB_IOC_TYPE, 0x2, struct hab_send)
diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
index a3e44bc9889b..602aca385a27 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -29,6 +29,7 @@
*/
#define ETH_ALEN 6 /* Octets in one ethernet addr */
+#define ETH_TLEN 2 /* Octets in ethernet type field */
#define ETH_HLEN 14 /* Total octets in header. */
#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */
#define ETH_DATA_LEN 1500 /* Max. octets in payload */
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index dd57d8bba233..03c9d5aef5c7 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2492,7 +2492,7 @@ enum nl80211_attrs {
#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS
#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS
-#define NL80211_WIPHY_NAME_MAXLEN 128
+#define NL80211_WIPHY_NAME_MAXLEN 64
#define NL80211_MAX_SUPP_RATES 32
#define NL80211_MAX_SUPP_HT_RATES 77
diff --git a/include/uapi/media/ais/msm_ais_isp.h b/include/uapi/media/ais/msm_ais_isp.h
index 55bc5290ce28..649173dd4404 100644
--- a/include/uapi/media/ais/msm_ais_isp.h
+++ b/include/uapi/media/ais/msm_ais_isp.h
@@ -619,6 +619,33 @@ struct msm_vfe_axi_src_state {
uint32_t src_frame_id;
};
+enum msm_vfe_cmd_ext_type_t {
+ VFE_GET_BUFQ_STATE,
+};
+
+enum msm_isp_buffer_state {
+ MSM_ISP_BUFFER_STATE_UNUSED, /* not used */
+ MSM_ISP_BUFFER_STATE_INITIALIZED, /* REQBUF done */
+ MSM_ISP_BUFFER_STATE_PREPARED, /* BUF mapped */
+ MSM_ISP_BUFFER_STATE_QUEUED, /* buf queued */
+ MSM_ISP_BUFFER_STATE_DEQUEUED, /* in use in VFE */
+ MSM_ISP_BUFFER_STATE_DIVERTED, /* Sent to other hardware*/
+ MSM_ISP_BUFFER_STATE_DISPATCHED, /* Sent to HAL*/
+};
+
+struct msm_vfe_bufq_state {
+ uint32_t handle;
+ uint32_t nbufs;
+ int32_t __user *buf_state;
+};
+
+struct msm_vfe_cmd_ext {
+ enum msm_vfe_cmd_ext_type_t type;
+ union {
+ struct msm_vfe_bufq_state bufq_state;
+ } data;
+};
+
enum msm_isp_event_mask_index {
ISP_EVENT_MASK_INDEX_STATS_NOTIFY = 0,
ISP_EVENT_MASK_INDEX_ERROR = 1,
@@ -909,6 +936,13 @@ struct msm_vfe_axi_output_plane_cfg {
uint32_t frame_increment;
};
+struct msm_vfe_axi_framedrop_update {
+ enum msm_vfe_axi_stream_src stream_src;
+
+ uint8_t framedrop_period;
+ uint32_t framedrop_pattern;
+};
+
struct msm_vfe_axi_output_path_cfg {
uint8_t enable;
@@ -975,6 +1009,8 @@ enum msm_isp_ioctl_cmd_code {
MSM_ISP_STOP,
MSM_ISP_SET_CLK_STATUS,
+ MSM_ISP_CMD_EXT,
+ MSM_ISP_FRAMEDROP_UPDATE,
};
@@ -1102,6 +1138,10 @@ enum msm_isp_ioctl_cmd_code {
_IOWR('V', MSM_ISP_AXI_OUTPUT_CFG, \
struct msm_vfe_axi_output_cfg)
+#define VIDIOC_MSM_ISP_FRAMEDROP_UPDATE \
+ _IOWR('V', MSM_ISP_FRAMEDROP_UPDATE, \
+ struct msm_vfe_axi_output_cfg)
+
#define VIDIOC_MSM_ISP_CAMIF_CFG \
_IOWR('V', MSM_ISP_CAMIF_CFG, \
struct msm_vfe_camif_cfg)
@@ -1110,4 +1150,8 @@ enum msm_isp_ioctl_cmd_code {
_IOWR('V', MSM_ISP_SET_CLK_STATUS, \
unsigned int)
+#define VIDIOC_MSM_ISP_CMD_EXT \
+ _IOWR('V', MSM_ISP_CMD_EXT, \
+ struct msm_vfe_cmd_ext)
+
#endif /* __UAPI_MSM_AIS_ISP__ */
diff --git a/include/uapi/media/ais/msm_ais_ispif.h b/include/uapi/media/ais/msm_ais_ispif.h
index b12175d787c2..a184e2983ab6 100644
--- a/include/uapi/media/ais/msm_ais_ispif.h
+++ b/include/uapi/media/ais/msm_ais_ispif.h
@@ -4,6 +4,7 @@
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/videodev2.h>
+#include <media/ais/msm_ais_mgr.h>
#define CSID_VERSION_V20 0x02000011
#define CSID_VERSION_V22 0x02001000
@@ -141,10 +142,13 @@ enum ispif_cfg_type_t {
ISPIF_STOP,
ISPIF_ENABLE_REG_DUMP,
ISPIF_SET_VFE_INFO,
- ISPIF_CFG2
+ ISPIF_CFG2,
+ ISPIF_READ_REG_LIST_CMD,
+ ISPIF_WRITE_REG_LIST_CMD,
};
+
struct ispif_cfg_data_ext {
enum ispif_cfg_type_t cfg_type;
void __user *data;
@@ -158,10 +162,12 @@ struct ispif_cfg_data {
uint32_t csid_version; /* ISPIF_INIT */
struct msm_ispif_vfe_info vfe_info; /* ISPIF_SET_VFE_INFO */
struct msm_ispif_param_data params; /* CFG, START, STOP */
+ struct msm_camera_reg_list_cmd *reg_list;
};
};
#define ISPIF_RDI_PACK_MODE_SUPPORT 1
+#define ISPIF_RW_REG_LIST_SUPPORT
#define VIDIOC_MSM_ISPIF_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE, struct ispif_cfg_data)
diff --git a/include/uapi/media/ais/msm_ais_mgr.h b/include/uapi/media/ais/msm_ais_mgr.h
index 43ae16df65f9..bfac1ac8296a 100644
--- a/include/uapi/media/ais/msm_ais_mgr.h
+++ b/include/uapi/media/ais/msm_ais_mgr.h
@@ -3,16 +3,87 @@
#include <media/ais/msm_ais.h>
+#define VREGNAME_SIZE 32
+#define CLKNAME_SIZE 32
+
+enum cam_ahb_clk_vote {
+ /* need to update the voting requests
+ * according to dtsi entries.
+ */
+ CAM_AHB_SUSPEND_VOTE = 0x0,
+ CAM_AHB_SVS_VOTE = 0x01,
+ CAM_AHB_NOMINAL_VOTE = 0x02,
+ CAM_AHB_TURBO_VOTE = 0x03,
+ CAM_AHB_DYNAMIC_VOTE = 0xFF,
+};
+
enum clk_mgr_cfg_type_t {
AIS_CLK_ENABLE,
AIS_CLK_DISABLE,
+ AIS_CLK_ENABLE_ALLCLK,
+ AIS_CLK_DISABLE_ALLCLK
+};
+
+enum ais_mgr_cfg_ext_type_t {
+ AIS_DIAG_GET_REGULATOR_INFO_LIST,
+ AIS_DIAG_GET_BUS_INFO_STATE,
+ AIS_DIAG_GET_CLK_INFO_LIST,
+ AIS_DIAG_GET_GPIO_LIST,
+ AIS_DIAG_SET_GPIO_LIST,
};
#define AIS_CLK_ENABLE AIS_CLK_ENABLE
#define AIS_CLK_DISABLE AIS_CLK_DISABLE
+
+struct msm_camera_reg_list_cmd {
+ void __user *value_list;
+ void __user *regaddr_list;
+ uint32_t reg_num;
+};
+
+struct msm_ais_diag_regulator_info_t {
+ int enable;
+ char regulatorname[VREGNAME_SIZE];
+};
+
+struct msm_ais_diag_regulator_info_list_t {
+ struct msm_ais_diag_regulator_info_t *infolist;
+ uint32_t regulator_num;
+};
+
+struct msm_ais_diag_bus_info_t {
+ enum cam_ahb_clk_vote ahb_clk_vote_state;
+ uint32_t isp_bus_vector_idx; /* 0 - init 1- ping 2 - pong */
+ uint64_t isp_ab;
+ uint64_t isp_ib;
+};
+
+struct msm_ais_diag_clk_info_t {
+ char clk_name[CLKNAME_SIZE];
+ long clk_rate;
+ uint8_t enable;
+};
+
+struct msm_ais_diag_clk_list_t {
+ void __user *clk_info;
+ uint32_t clk_num;
+};
+
+struct msm_ais_diag_gpio_list_t {
+ uint32_t __user *gpio_idx_list;
+ int32_t __user *gpio_val_list;
+ uint32_t gpio_num;
+};
+
struct clk_mgr_cfg_data_ext {
- enum clk_mgr_cfg_type_t cfg_type;
+ enum ais_mgr_cfg_ext_type_t cfg_type;
+ union {
+ struct msm_ais_diag_regulator_info_list_t vreg_infolist;
+ struct msm_ais_diag_bus_info_t bus_info;
+ struct msm_ais_diag_clk_list_t clk_infolist;
+ struct msm_ais_diag_gpio_list_t gpio_list;
+ } data;
};
struct clk_mgr_cfg_data {
diff --git a/include/uapi/media/ais/msm_ais_sensor.h b/include/uapi/media/ais/msm_ais_sensor.h
index ca9bcf96bcb0..633f3f227174 100644
--- a/include/uapi/media/ais/msm_ais_sensor.h
+++ b/include/uapi/media/ais/msm_ais_sensor.h
@@ -3,7 +3,7 @@
#include <linux/v4l2-mediabus.h>
#include <media/ais/msm_ais_sensor_sdk.h>
-
+#include <media/ais/msm_ais_mgr.h>
#include <linux/types.h>
#include <linux/i2c.h>
@@ -152,6 +152,8 @@ enum csid_cfg_type_t {
CSID_START,
CSID_STOP,
CSID_RELEASE,
+ CSID_READ_REG_LIST_CMD,
+ CSID_WRITE_REG_LIST_CMD,
};
enum csiphy_cfg_type_t {
@@ -160,6 +162,8 @@ enum csiphy_cfg_type_t {
CSIPHY_START,
CSIPHY_STOP,
CSIPHY_RELEASE,
+ CSIPHY_READ_REG_LIST_CMD,
+ CSIPHY_WRITE_REG_LIST_CMD,
};
enum camera_vreg_type {
@@ -291,6 +295,7 @@ struct csid_cfg_data {
struct msm_camera_csid_params *csid_params;
struct msm_camera_csid_testmode_parms *csid_testmode_params;
uint32_t csid_cidmask;
+ struct msm_camera_reg_list_cmd *csid_reg_list_cmd;
} cfg;
};
@@ -299,6 +304,7 @@ struct csiphy_cfg_data {
union {
struct msm_camera_csiphy_params *csiphy_params;
struct msm_camera_csi_lane_params *csi_lane_params;
+ struct msm_camera_reg_list_cmd *csiphy_reg_list_cmd;
} cfg;
};
@@ -613,6 +619,8 @@ struct sensor_init_cfg_data {
} cfg;
};
+#define CSI_RW_REG_LIST_SUPPORT
+
#define VIDIOC_MSM_SENSOR_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct sensorb_cfg_data)
diff --git a/init/Kconfig b/init/Kconfig
index cda05668350c..ec2342b3befe 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1426,6 +1426,17 @@ source "usr/Kconfig"
endif
+choice
+ prompt "Compiler optimization level"
+ default CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE
+
+config CC_OPTIMIZE_FOR_PERFORMANCE
+ bool "Optimize for performance"
+ help
+ This is the default optimization level for the kernel, building
+ with the "-O2" compiler flag for best performance and most
+ helpful compile-time warnings.
+
config CC_OPTIMIZE_FOR_SIZE
bool "Optimize for size"
help
@@ -1434,6 +1445,8 @@ config CC_OPTIMIZE_FOR_SIZE
If unsure, say N.
+endchoice
+
config SYSCTL
bool
diff --git a/ipc/shm.c b/ipc/shm.c
index 9fa852a6473b..5095eb3a6a6c 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -1113,14 +1113,17 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg,
goto out;
else if ((addr = (ulong)shmaddr)) {
if (addr & (shmlba - 1)) {
- /*
- * 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
+ if (shmflg & SHM_RND) {
+ addr &= ~(shmlba - 1); /* round down */
+
+ /*
+ * Ensure that the round-down is non-nil
+ * when remapping. This can happen for
+ * cases when addr < shmlba.
+ */
+ if (!addr && (shmflg & SHM_REMAP))
+ goto out;
+ } else
#ifndef __ARCH_FORCE_SHMLBA
if (addr & ~PAGE_MASK)
#endif
diff --git a/kernel/audit.c b/kernel/audit.c
index d8e9fba58cbc..d440c25cb3be 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -744,6 +744,8 @@ static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature
return;
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_FEATURE_CHANGE);
+ if (!ab)
+ return;
audit_log_task_info(ab, current);
audit_log_format(ab, " feature=%s old=%u new=%u old_lock=%u new_lock=%u res=%d",
audit_feature_names[which], !!old_feature, !!new_feature,
diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c
index 4121345498e0..ebc52c7bd8a6 100644
--- a/kernel/debug/kdb/kdb_main.c
+++ b/kernel/debug/kdb/kdb_main.c
@@ -1564,6 +1564,7 @@ static int kdb_md(int argc, const char **argv)
int symbolic = 0;
int valid = 0;
int phys = 0;
+ int raw = 0;
kdbgetintenv("MDCOUNT", &mdcount);
kdbgetintenv("RADIX", &radix);
@@ -1573,9 +1574,10 @@ static int kdb_md(int argc, const char **argv)
repeat = mdcount * 16 / bytesperword;
if (strcmp(argv[0], "mdr") == 0) {
- if (argc != 2)
+ if (argc == 2 || (argc == 0 && last_addr != 0))
+ valid = raw = 1;
+ else
return KDB_ARGCOUNT;
- valid = 1;
} else if (isdigit(argv[0][2])) {
bytesperword = (int)(argv[0][2] - '0');
if (bytesperword == 0) {
@@ -1611,7 +1613,10 @@ static int kdb_md(int argc, const char **argv)
radix = last_radix;
bytesperword = last_bytesperword;
repeat = last_repeat;
- mdcount = ((repeat * bytesperword) + 15) / 16;
+ if (raw)
+ mdcount = repeat;
+ else
+ mdcount = ((repeat * bytesperword) + 15) / 16;
}
if (argc) {
@@ -1628,7 +1633,10 @@ static int kdb_md(int argc, const char **argv)
diag = kdbgetularg(argv[nextarg], &val);
if (!diag) {
mdcount = (int) val;
- repeat = mdcount * 16 / bytesperword;
+ if (raw)
+ repeat = mdcount;
+ else
+ repeat = mdcount * 16 / bytesperword;
}
}
if (argc >= nextarg+1) {
@@ -1638,8 +1646,15 @@ static int kdb_md(int argc, const char **argv)
}
}
- if (strcmp(argv[0], "mdr") == 0)
- return kdb_mdr(addr, mdcount);
+ if (strcmp(argv[0], "mdr") == 0) {
+ int ret;
+ last_addr = addr;
+ ret = kdb_mdr(addr, mdcount);
+ last_addr += mdcount;
+ last_repeat = mdcount;
+ last_bytesperword = bytesperword; // to make REPEAT happy
+ return ret;
+ }
switch (radix) {
case 10:
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 1a73b31b5fe7..a8f984b4fcff 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -427,9 +427,15 @@ static inline void __update_cgrp_time(struct perf_cgroup *cgrp)
static inline void update_cgrp_time_from_cpuctx(struct perf_cpu_context *cpuctx)
{
- struct perf_cgroup *cgrp_out = cpuctx->cgrp;
- if (cgrp_out)
- __update_cgrp_time(cgrp_out);
+ struct perf_cgroup *cgrp = cpuctx->cgrp;
+ struct cgroup_subsys_state *css;
+
+ if (cgrp) {
+ for (css = &cgrp->css; css; css = css->parent) {
+ cgrp = container_of(css, struct perf_cgroup, css);
+ __update_cgrp_time(cgrp);
+ }
+ }
}
static inline void update_cgrp_time_from_event(struct perf_event *event)
@@ -457,6 +463,7 @@ perf_cgroup_set_timestamp(struct task_struct *task,
{
struct perf_cgroup *cgrp;
struct perf_cgroup_info *info;
+ struct cgroup_subsys_state *css;
/*
* ctx->lock held by caller
@@ -467,8 +474,12 @@ perf_cgroup_set_timestamp(struct task_struct *task,
return;
cgrp = perf_cgroup_from_task(task, ctx);
- info = this_cpu_ptr(cgrp->info);
- info->timestamp = ctx->timestamp;
+
+ for (css = &cgrp->css; css; css = css->parent) {
+ cgrp = container_of(css, struct perf_cgroup, css);
+ info = this_cpu_ptr(cgrp->info);
+ info->timestamp = ctx->timestamp;
+ }
}
#define PERF_CGROUP_SWOUT 0x1 /* cgroup switch out every event */
@@ -5404,7 +5415,8 @@ static void perf_output_read_group(struct perf_output_handle *handle,
if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
values[n++] = running;
- if (leader != event)
+ if ((leader != event) &&
+ (leader->state == PERF_EVENT_STATE_ACTIVE))
leader->pmu->read(leader);
values[n++] = perf_event_count(leader);
diff --git a/kernel/locking/qspinlock.c b/kernel/locking/qspinlock.c
index 8173bc7fec92..3b40c8809e52 100644
--- a/kernel/locking/qspinlock.c
+++ b/kernel/locking/qspinlock.c
@@ -423,6 +423,14 @@ queue:
tail = encode_tail(smp_processor_id(), idx);
node += idx;
+
+ /*
+ * Ensure that we increment the head node->count before initialising
+ * the actual node. If the compiler is kind enough to reorder these
+ * stores, then an IRQ could overwrite our assignments.
+ */
+ barrier();
+
node->locked = 0;
node->next = NULL;
pv_init_node(node);
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index 1535c46808f1..29771c69383b 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -30,6 +30,7 @@
#include <linux/genhd.h>
#include <linux/ktime.h>
#include <trace/events/power.h>
+#include <soc/qcom/boot_stats.h>
#include "power.h"
@@ -469,6 +470,7 @@ static int resume_target_kernel(bool platform_mode)
touch_softlockup_watchdog();
syscore_resume();
+ place_marker("PM: Image Restoration failed!");
Enable_irqs:
local_irq_enable();
@@ -705,6 +707,7 @@ int hibernate(void)
pm_restore_gfp_mask();
} else {
pr_debug("PM: Image restored successfully.\n");
+ place_marker("PM: Image restored!");
}
Free_bitmaps:
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 51f02ecaf125..2610516601ee 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -94,9 +94,6 @@ extern int in_suspend;
extern dev_t swsusp_resume_device;
extern sector_t swsusp_resume_block;
-extern asmlinkage int swsusp_arch_suspend(void);
-extern asmlinkage int swsusp_arch_resume(void);
-
extern int create_basic_memory_bitmaps(void);
extern void free_basic_memory_bitmaps(void);
extern int hibernate_preallocate_memory(void);
diff --git a/kernel/relay.c b/kernel/relay.c
index 0b4570cfacae..f6d5f08bdfaa 100644
--- a/kernel/relay.c
+++ b/kernel/relay.c
@@ -163,7 +163,7 @@ static struct rchan_buf *relay_create_buf(struct rchan *chan)
{
struct rchan_buf *buf;
- if (chan->n_subbufs > UINT_MAX / sizeof(size_t *))
+ if (chan->n_subbufs > KMALLOC_MAX_SIZE / sizeof(size_t *))
return NULL;
buf = kzalloc(sizeof(struct rchan_buf), GFP_KERNEL);
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index f962ab1eb046..621793d3ec6d 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -7130,7 +7130,7 @@ boosted_task_util(struct task_struct *task)
static unsigned long capacity_spare_wake(int cpu, struct task_struct *p)
{
- return capacity_orig_of(cpu) - cpu_util_wake(cpu, p);
+ return max_t(long, capacity_of(cpu) - cpu_util_wake(cpu, p), 0);
}
/*
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index fb7f36fd6e5b..af21389466b8 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -831,6 +831,8 @@ static int do_sched_rt_period_timer(struct rt_bandwidth *rt_b, int overrun)
struct rq *rq = rq_of_rt_rq(rt_rq);
raw_spin_lock(&rq->lock);
+ update_rq_clock(rq);
+
if (rt_rq->rt_time) {
u64 runtime;
diff --git a/kernel/signal.c b/kernel/signal.c
index 7d75bc2d042f..8bfbc47f0a23 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1392,6 +1392,10 @@ static int kill_something_info(int sig, struct siginfo *info, pid_t pid)
return ret;
}
+ /* -INT_MIN is undefined. Exclude this case to avoid a UBSAN warning */
+ if (pid == INT_MIN)
+ return -ESRCH;
+
read_lock(&tasklist_lock);
if (pid != -1) {
ret = __kill_pgrp_info(sig, info,
diff --git a/kernel/sys.c b/kernel/sys.c
index ba3ddb43dd9f..cf40663a54c2 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -55,6 +55,8 @@
#include <linux/uidgid.h>
#include <linux/cred.h>
+#include <linux/nospec.h>
+
#include <linux/kmsg_dump.h>
/* Move somewhere else to avoid recompiling? */
#include <generated/utsrelease.h>
@@ -1313,6 +1315,7 @@ SYSCALL_DEFINE2(old_getrlimit, unsigned int, resource,
if (resource >= RLIM_NLIMITS)
return -EINVAL;
+ resource = array_index_nospec(resource, RLIM_NLIMITS);
task_lock(current->group_leader);
x = current->signal->rlim[resource];
task_unlock(current->group_leader);
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 42a4009fd75a..2172dd61577e 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -469,9 +469,10 @@ clear_event_triggers(struct trace_array *tr)
struct trace_event_file *file;
list_for_each_entry(file, &tr->events, list) {
- struct event_trigger_data *data;
- list_for_each_entry_rcu(data, &file->triggers, list) {
+ struct event_trigger_data *data, *n;
+ list_for_each_entry_safe(data, n, &file->triggers, list) {
trace_event_trigger_enable_disable(file, 0);
+ list_del_rcu(&data->list);
if (data->ops->free)
data->ops->free(data->ops, data);
}
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index acccc6c7437f..89a0f1171f90 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -5223,7 +5223,7 @@ int workqueue_sysfs_register(struct workqueue_struct *wq)
ret = device_register(&wq_dev->dev);
if (ret) {
- kfree(wq_dev);
+ put_device(&wq_dev->dev);
wq->wq_dev = NULL;
return ret;
}
diff --git a/lib/test_bpf.c b/lib/test_bpf.c
index b7908d949a5f..b1495f586f29 100644
--- a/lib/test_bpf.c
+++ b/lib/test_bpf.c
@@ -83,6 +83,7 @@ struct bpf_test {
__u32 result;
} test[MAX_SUBTESTS];
int (*fill_helper)(struct bpf_test *self);
+ int expected_errcode; /* used when FLAG_EXPECTED_FAIL is set in the aux */
__u8 frag_data[MAX_DATA];
};
@@ -1780,7 +1781,9 @@ static struct bpf_test tests[] = {
},
CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
{ },
- { }
+ { },
+ .fill_helper = NULL,
+ .expected_errcode = -EINVAL,
},
{
"check: div_k_0",
@@ -1790,7 +1793,9 @@ static struct bpf_test tests[] = {
},
CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
{ },
- { }
+ { },
+ .fill_helper = NULL,
+ .expected_errcode = -EINVAL,
},
{
"check: unknown insn",
@@ -1801,7 +1806,9 @@ static struct bpf_test tests[] = {
},
CLASSIC | FLAG_EXPECTED_FAIL,
{ },
- { }
+ { },
+ .fill_helper = NULL,
+ .expected_errcode = -EINVAL,
},
{
"check: out of range spill/fill",
@@ -1811,7 +1818,9 @@ static struct bpf_test tests[] = {
},
CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
{ },
- { }
+ { },
+ .fill_helper = NULL,
+ .expected_errcode = -EINVAL,
},
{
"JUMPS + HOLES",
@@ -1903,6 +1912,8 @@ static struct bpf_test tests[] = {
CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
{ },
{ },
+ .fill_helper = NULL,
+ .expected_errcode = -EINVAL,
},
{
"check: LDX + RET X",
@@ -1913,6 +1924,8 @@ static struct bpf_test tests[] = {
CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
{ },
{ },
+ .fill_helper = NULL,
+ .expected_errcode = -EINVAL,
},
{ /* Mainly checking JIT here. */
"M[]: alt STX + LDX",
@@ -2087,6 +2100,8 @@ static struct bpf_test tests[] = {
CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
{ },
{ },
+ .fill_helper = NULL,
+ .expected_errcode = -EINVAL,
},
{ /* Passes checker but fails during runtime. */
"LD [SKF_AD_OFF-1]",
@@ -4462,6 +4477,7 @@ static struct bpf_test tests[] = {
{ },
{ },
.fill_helper = bpf_fill_maxinsns4,
+ .expected_errcode = -EINVAL,
},
{ /* Mainly checking JIT here. */
"BPF_MAXINSNS: Very long jump",
@@ -4517,10 +4533,15 @@ static struct bpf_test tests[] = {
{
"BPF_MAXINSNS: Jump, gap, jump, ...",
{ },
+#ifdef CONFIG_BPF_JIT_ALWAYS_ON
+ CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
+#else
CLASSIC | FLAG_NO_DATA,
+#endif
{ },
{ { 0, 0xababcbac } },
.fill_helper = bpf_fill_maxinsns11,
+ .expected_errcode = -ENOTSUPP,
},
{
"BPF_MAXINSNS: ld_abs+get_processor_id",
@@ -5290,7 +5311,7 @@ static struct bpf_prog *generate_filter(int which, int *err)
*err = bpf_prog_create(&fp, &fprog);
if (tests[which].aux & FLAG_EXPECTED_FAIL) {
- if (*err == -EINVAL) {
+ if (*err == tests[which].expected_errcode) {
pr_cont("PASS\n");
/* Verifier rejected filter as expected. */
*err = 0;
diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
index cfdbe1ce9ef8..eeaaac0fb24a 100644
--- a/mm/kasan/kasan.c
+++ b/mm/kasan/kasan.c
@@ -818,5 +818,5 @@ static int __init kasan_memhotplug_init(void)
return 0;
}
-module_init(kasan_memhotplug_init);
+core_initcall(kasan_memhotplug_init);
#endif
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index ddc6c93966d2..de479ce41690 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -1533,8 +1533,7 @@ static void start_scan_thread(void)
}
/*
- * Stop the automatic memory scanning thread. This function must be called
- * with the scan_mutex held.
+ * Stop the automatic memory scanning thread.
*/
static void stop_scan_thread(void)
{
@@ -1797,12 +1796,15 @@ static void kmemleak_do_cleanup(struct work_struct *work)
{
stop_scan_thread();
+ mutex_lock(&scan_mutex);
/*
- * Once the scan thread has stopped, it is safe to no longer track
- * object freeing. Ordering of the scan thread stopping and the memory
- * accesses below is guaranteed by the kthread_stop() function.
+ * Once it is made sure that kmemleak_scan has stopped, it is safe to no
+ * longer track object freeing. Ordering of the scan thread stopping and
+ * the memory accesses below is guaranteed by the kthread_stop()
+ * function.
*/
kmemleak_free_enabled = 0;
+ mutex_unlock(&scan_mutex);
if (!kmemleak_found_leaks)
__kmemleak_do_cleanup();
diff --git a/mm/ksm.c b/mm/ksm.c
index 25cc13c1fe20..33a790d85142 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -1512,8 +1512,22 @@ static void cmp_and_merge_page(struct page *page, struct rmap_item *rmap_item)
tree_rmap_item =
unstable_tree_search_insert(rmap_item, page, &tree_page);
if (tree_rmap_item) {
+ bool split;
+
kpage = try_to_merge_two_pages(rmap_item, page,
tree_rmap_item, tree_page);
+ /*
+ * If both pages we tried to merge belong to the same compound
+ * page, then we actually ended up increasing the reference
+ * count of the same compound page twice, and split_huge_page
+ * failed.
+ * Here we set a flag if that happened, and we use it later to
+ * try split_huge_page again. Since we call put_page right
+ * afterwards, the reference count will be correct and
+ * split_huge_page should succeed.
+ */
+ split = PageTransCompound(page)
+ && compound_head(page) == compound_head(tree_page);
put_page(tree_page);
if (kpage) {
/*
@@ -1538,6 +1552,20 @@ static void cmp_and_merge_page(struct page *page, struct rmap_item *rmap_item)
break_cow(tree_rmap_item);
break_cow(rmap_item);
}
+ } else if (split) {
+ /*
+ * We are here if we tried to merge two pages and
+ * failed because they both belonged to the same
+ * compound page. We will split the page now, but no
+ * merging will take place.
+ * We do not want to add the cost of a full lock; if
+ * the page is locked, it is better to skip it and
+ * perhaps try again later.
+ */
+ if (!trylock_page(page))
+ return;
+ split_huge_page(page);
+ unlock_page(page);
}
}
}
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 62e4af5b287f..b9b2e25342d4 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -1233,6 +1233,7 @@ static int get_nodes(nodemask_t *nodes, const unsigned long __user *nmask,
unsigned long maxnode)
{
unsigned long k;
+ unsigned long t;
unsigned long nlongs;
unsigned long endmask;
@@ -1249,13 +1250,19 @@ static int get_nodes(nodemask_t *nodes, const unsigned long __user *nmask,
else
endmask = (1UL << (maxnode % BITS_PER_LONG)) - 1;
- /* When the user specified more nodes than supported just check
- if the non supported part is all zero. */
+ /*
+ * When the user specified more nodes than supported just check
+ * if the non supported part is all zero.
+ *
+ * If maxnode have more longs than MAX_NUMNODES, check
+ * the bits in that area first. And then go through to
+ * check the rest bits which equal or bigger than MAX_NUMNODES.
+ * Otherwise, just check bits [MAX_NUMNODES, maxnode).
+ */
if (nlongs > BITS_TO_LONGS(MAX_NUMNODES)) {
if (nlongs > PAGE_SIZE/sizeof(long))
return -EINVAL;
for (k = BITS_TO_LONGS(MAX_NUMNODES); k < nlongs; k++) {
- unsigned long t;
if (get_user(t, nmask + k))
return -EFAULT;
if (k == nlongs - 1) {
@@ -1268,6 +1275,16 @@ static int get_nodes(nodemask_t *nodes, const unsigned long __user *nmask,
endmask = ~0UL;
}
+ if (maxnode > MAX_NUMNODES && MAX_NUMNODES % BITS_PER_LONG != 0) {
+ unsigned long valid_mask = endmask;
+
+ valid_mask &= ~((1UL << (MAX_NUMNODES % BITS_PER_LONG)) - 1);
+ if (get_user(t, nmask + nlongs - 1))
+ return -EFAULT;
+ if (t & valid_mask)
+ return -EINVAL;
+ }
+
if (copy_from_user(nodes_addr(*nodes), nmask, nlongs*sizeof(unsigned long)))
return -EFAULT;
nodes_addr(*nodes)[nlongs-1] &= endmask;
@@ -1394,10 +1411,14 @@ SYSCALL_DEFINE4(migrate_pages, pid_t, pid, unsigned long, maxnode,
goto out_put;
}
- if (!nodes_subset(*new, node_states[N_MEMORY])) {
- err = -EINVAL;
+ task_nodes = cpuset_mems_allowed(current);
+ nodes_and(*new, *new, task_nodes);
+ if (nodes_empty(*new))
+ goto out_put;
+
+ nodes_and(*new, *new, node_states[N_MEMORY]);
+ if (nodes_empty(*new))
goto out_put;
- }
err = security_task_movememory(task);
if (err)
@@ -2122,6 +2143,9 @@ bool __mpol_equal(struct mempolicy *a, struct mempolicy *b)
case MPOL_INTERLEAVE:
return !!nodes_equal(a->v.nodes, b->v.nodes);
case MPOL_PREFERRED:
+ /* a's ->flags is the same as b's */
+ if (a->flags & MPOL_F_LOCAL)
+ return true;
return a->v.preferred_node == b->v.preferred_node;
default:
BUG();
diff --git a/mm/mmap.c b/mm/mmap.c
index 5457c5f4935b..c9d2061257ac 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1307,6 +1307,35 @@ static inline int mlock_future_check(struct mm_struct *mm,
return 0;
}
+static inline u64 file_mmap_size_max(struct file *file, struct inode *inode)
+{
+ if (S_ISREG(inode->i_mode))
+ return MAX_LFS_FILESIZE;
+
+ if (S_ISBLK(inode->i_mode))
+ return MAX_LFS_FILESIZE;
+
+ /* Special "we do even unsigned file positions" case */
+ if (file->f_mode & FMODE_UNSIGNED_OFFSET)
+ return 0;
+
+ /* Yes, random drivers might want more. But I'm tired of buggy drivers */
+ return ULONG_MAX;
+}
+
+static inline bool file_mmap_ok(struct file *file, struct inode *inode,
+ unsigned long pgoff, unsigned long len)
+{
+ u64 maxsize = file_mmap_size_max(file, inode);
+
+ if (maxsize && len > maxsize)
+ return false;
+ maxsize -= len;
+ if (pgoff > maxsize >> PAGE_SHIFT)
+ return false;
+ return true;
+}
+
/*
* The caller must hold down_write(&current->mm->mmap_sem).
*/
@@ -1377,6 +1406,9 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
if (file) {
struct inode *inode = file_inode(file);
+ if (!file_mmap_ok(file, inode, pgoff, len))
+ return -EOVERFLOW;
+
switch (flags & MAP_TYPE) {
case MAP_SHARED:
if ((prot&PROT_WRITE) && !(file->f_mode&FMODE_WRITE))
diff --git a/mm/swapfile.c b/mm/swapfile.c
index fca04a53cf78..2968b505ca9a 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -2301,6 +2301,10 @@ static unsigned long read_swap_header(struct swap_info_struct *p,
maxpages = swp_offset(pte_to_swp_entry(
swp_entry_to_pte(swp_entry(0, ~0UL)))) + 1;
last_page = swap_header->info.last_page;
+ if (!last_page) {
+ pr_warn("Empty swap-file\n");
+ return 0;
+ }
if (last_page > maxpages) {
pr_warn("Truncating oversized swap area, only using %luk out of %luk\n",
maxpages << (PAGE_SHIFT - 10),
diff --git a/mm/vmscan.c b/mm/vmscan.c
index c7fe805048da..abeea66bc429 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1409,6 +1409,7 @@ int __isolate_lru_page(struct page *page, isolate_mode_t mode)
if (PageDirty(page)) {
struct address_space *mapping;
+ bool migrate_dirty;
/* ISOLATE_CLEAN means only clean pages */
if (mode & ISOLATE_CLEAN)
@@ -1417,10 +1418,19 @@ int __isolate_lru_page(struct page *page, isolate_mode_t mode)
/*
* Only pages without mappings or that have a
* ->migratepage callback are possible to migrate
- * without blocking
+ * without blocking. However, we can be racing with
+ * truncation so it's necessary to lock the page
+ * to stabilise the mapping as truncation holds
+ * the page lock until after the page is removed
+ * from the page cache.
*/
+ if (!trylock_page(page))
+ return ret;
+
mapping = page_mapping(page);
- if (mapping && !mapping->a_ops->migratepage)
+ migrate_dirty = !mapping || mapping->a_ops->migratepage;
+ unlock_page(page);
+ if (!migrate_dirty)
return ret;
}
}
@@ -3963,7 +3973,13 @@ int zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order)
*/
int page_evictable(struct page *page)
{
- return !mapping_unevictable(page_mapping(page)) && !PageMlocked(page);
+ int ret;
+
+ /* Prevent address_space of inode and swap cache from being freed */
+ rcu_read_lock();
+ ret = !mapping_unevictable(page_mapping(page)) && !PageMlocked(page);
+ rcu_read_unlock();
+ return ret;
}
#ifdef CONFIG_SHMEM
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
index 5f19133c5530..c2dff7c6e960 100644
--- a/net/batman-adv/distributed-arp-table.c
+++ b/net/batman-adv/distributed-arp-table.c
@@ -374,7 +374,7 @@ static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb,
batadv_arp_hw_src(skb, hdr_size), &ip_src,
batadv_arp_hw_dst(skb, hdr_size), &ip_dst);
- if (hdr_size == 0)
+ if (hdr_size < sizeof(struct batadv_unicast_packet))
return;
unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data;
diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
index 700c96c82a15..5d2f9d4879b2 100644
--- a/net/batman-adv/fragmentation.c
+++ b/net/batman-adv/fragmentation.c
@@ -278,7 +278,8 @@ batadv_frag_merge_packets(struct hlist_head *chain)
/* Move the existing MAC header to just before the payload. (Override
* the fragment header.)
*/
- skb_pull_rcsum(skb_out, hdr_size);
+ skb_pull(skb_out, hdr_size);
+ skb_out->ip_summed = CHECKSUM_NONE;
memmove(skb_out->data - ETH_HLEN, skb_mac_header(skb_out), ETH_HLEN);
skb_set_mac_header(skb_out, -ETH_HLEN);
skb_reset_network_header(skb_out);
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c
index e6c8382c79ba..6abfba1e227f 100644
--- a/net/batman-adv/gateway_client.c
+++ b/net/batman-adv/gateway_client.c
@@ -798,6 +798,9 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
vid = batadv_get_vid(skb, 0);
+ if (is_multicast_ether_addr(ethhdr->h_dest))
+ goto out;
+
orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source,
ethhdr->h_dest, vid);
if (!orig_dst_node)
diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
index eb76386f8d4b..8aa2d65df86f 100644
--- a/net/batman-adv/multicast.c
+++ b/net/batman-adv/multicast.c
@@ -428,8 +428,8 @@ static struct batadv_orig_node *
batadv_mcast_forw_tt_node_get(struct batadv_priv *bat_priv,
struct ethhdr *ethhdr)
{
- return batadv_transtable_search(bat_priv, ethhdr->h_source,
- ethhdr->h_dest, BATADV_NO_FLAGS);
+ return batadv_transtable_search(bat_priv, NULL, ethhdr->h_dest,
+ BATADV_NO_FLAGS);
}
/**
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index 720f1a5b81ac..9f1fe6169bef 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -430,13 +430,7 @@ void batadv_interface_rx(struct net_device *soft_iface,
/* skb->dev & skb->pkt_type are set here */
skb->protocol = eth_type_trans(skb, soft_iface);
-
- /* should not be necessary anymore as we use skb_pull_rcsum()
- * TODO: please verify this and remove this TODO
- * -- Dec 21st 2009, Simon Wunderlich
- */
-
- /* skb->ip_summed = CHECKSUM_UNNECESSARY; */
+ skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
batadv_inc_counter(bat_priv, BATADV_CNT_RX);
batadv_add_counter(bat_priv, BATADV_CNT_RX_BYTES,
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index 50b76011f470..51eab9b5baa1 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -1614,7 +1614,8 @@ static int compat_match_to_user(struct ebt_entry_match *m, void __user **dstptr,
int off = ebt_compat_match_offset(match, m->match_size);
compat_uint_t msize = m->match_size - off;
- BUG_ON(off >= m->match_size);
+ if (WARN_ON(off >= m->match_size))
+ return -EINVAL;
if (copy_to_user(cm->u.name, match->name,
strlen(match->name) + 1) || put_user(msize, &cm->match_size))
@@ -1641,7 +1642,8 @@ static int compat_target_to_user(struct ebt_entry_target *t,
int off = xt_compat_target_offset(target);
compat_uint_t tsize = t->target_size - off;
- BUG_ON(off >= t->target_size);
+ if (WARN_ON(off >= t->target_size))
+ return -EINVAL;
if (copy_to_user(cm->u.name, target->name,
strlen(target->name) + 1) || put_user(tsize, &cm->match_size))
@@ -1869,7 +1871,8 @@ static int ebt_buf_add(struct ebt_entries_buf_state *state,
if (state->buf_kern_start == NULL)
goto count_only;
- BUG_ON(state->buf_kern_offset + sz > state->buf_kern_len);
+ if (WARN_ON(state->buf_kern_offset + sz > state->buf_kern_len))
+ return -EINVAL;
memcpy(state->buf_kern_start + state->buf_kern_offset, data, sz);
@@ -1882,7 +1885,8 @@ static int ebt_buf_add_pad(struct ebt_entries_buf_state *state, unsigned int sz)
{
char *b = state->buf_kern_start;
- BUG_ON(b && state->buf_kern_offset > state->buf_kern_len);
+ if (WARN_ON(b && state->buf_kern_offset > state->buf_kern_len))
+ return -EINVAL;
if (b != NULL && sz > 0)
memset(b + state->buf_kern_offset, 0, sz);
@@ -1959,8 +1963,10 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt,
pad = XT_ALIGN(size_kern) - size_kern;
if (pad > 0 && dst) {
- BUG_ON(state->buf_kern_len <= pad);
- BUG_ON(state->buf_kern_offset - (match_size + off) + size_kern > state->buf_kern_len - pad);
+ if (WARN_ON(state->buf_kern_len <= pad))
+ return -EINVAL;
+ if (WARN_ON(state->buf_kern_offset - (match_size + off) + size_kern > state->buf_kern_len - pad))
+ return -EINVAL;
memset(dst + size_kern, 0, pad);
}
return off + match_size;
@@ -2011,7 +2017,8 @@ static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32,
if (ret < 0)
return ret;
- BUG_ON(ret < match32->match_size);
+ if (WARN_ON(ret < match32->match_size))
+ return -EINVAL;
growth += ret - match32->match_size;
growth += ebt_compat_entry_padsize();
@@ -2081,8 +2088,12 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base,
* offsets are relative to beginning of struct ebt_entry (i.e., 0).
*/
for (i = 0; i < 4 ; ++i) {
- if (offsets[i] >= *total)
+ if (offsets[i] > *total)
+ return -EINVAL;
+
+ if (i < 3 && offsets[i] == *total)
return -EINVAL;
+
if (i == 0)
continue;
if (offsets[i-1] > offsets[i])
@@ -2121,7 +2132,8 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base,
startoff = state->buf_user_offset - startoff;
- BUG_ON(*total < startoff);
+ if (WARN_ON(*total < startoff))
+ return -EINVAL;
*total -= startoff;
return 0;
}
@@ -2249,7 +2261,8 @@ static int compat_do_replace(struct net *net, void __user *user,
state.buf_kern_len = size64;
ret = compat_copy_entries(entries_tmp, tmp.entries_size, &state);
- BUG_ON(ret < 0); /* parses same data again */
+ if (WARN_ON(ret < 0))
+ goto out_unlock;
vfree(entries_tmp);
tmp.entries_size = size64;
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 5b3d611d8b5f..2017ffa5197a 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1691,6 +1691,10 @@ static int do_setlink(const struct sk_buff *skb,
const struct net_device_ops *ops = dev->netdev_ops;
int err;
+ err = validate_linkmsg(dev, tb);
+ if (err < 0)
+ return err;
+
if (tb[IFLA_NET_NS_PID] || tb[IFLA_NET_NS_FD]) {
struct net *net = rtnl_link_get_net(dev_net(dev), tb);
if (IS_ERR(net)) {
@@ -1982,10 +1986,6 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh)
goto errout;
}
- err = validate_linkmsg(dev, tb);
- if (err < 0)
- goto errout;
-
err = do_setlink(skb, dev, ifm, tb, ifname, 0);
errout:
return err;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 0d8383c8a117..03928b406d4c 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4321,13 +4321,18 @@ EXPORT_SYMBOL_GPL(skb_gso_transport_seglen);
static struct sk_buff *skb_reorder_vlan_header(struct sk_buff *skb)
{
+ int mac_len;
+
if (skb_cow(skb, skb_headroom(skb)) < 0) {
kfree_skb(skb);
return NULL;
}
- memmove(skb->data - ETH_HLEN, skb->data - skb->mac_len - VLAN_HLEN,
- 2 * ETH_ALEN);
+ mac_len = skb->data - skb_mac_header(skb);
+ if (likely(mac_len > VLAN_HLEN + ETH_TLEN)) {
+ memmove(skb_mac_header(skb) + VLAN_HLEN, skb_mac_header(skb),
+ mac_len - VLAN_HLEN - ETH_TLEN);
+ }
skb->mac_header += VLAN_HLEN;
return skb;
}
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index ff3b058cf58c..936dab12f99f 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -280,9 +280,7 @@ int dccp_disconnect(struct sock *sk, int flags)
dccp_clear_xmit_timers(sk);
ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
- ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
dp->dccps_hc_rx_ccid = NULL;
- dp->dccps_hc_tx_ccid = NULL;
__skb_queue_purge(&sk->sk_receive_queue);
__skb_queue_purge(&sk->sk_write_queue);
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 44abc52bae13..9d144cbd4e62 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -979,6 +979,8 @@ fib_convert_metrics(struct fib_info *fi, const struct fib_config *cfg)
if (val == TCP_CA_UNSPEC)
return -EINVAL;
} else {
+ if (nla_len(nla) != sizeof(u32))
+ return false;
val = nla_get_u32(nla);
}
if (type == RTAX_ADVMSS && val > 65535 - 40)
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 1b93ea766916..ce9a7fbb7c5f 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -493,8 +493,6 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
int err;
int copied;
- WARN_ON_ONCE(sk->sk_family == AF_INET6);
-
err = -EAGAIN;
skb = sock_dequeue_err_skb(sk);
if (!skb)
diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c
index a03f834f16d5..4b7c81f88abf 100644
--- a/net/ipv4/ip_vti.c
+++ b/net/ipv4/ip_vti.c
@@ -366,7 +366,6 @@ static int vti_tunnel_init(struct net_device *dev)
memcpy(dev->dev_addr, &iph->saddr, 4);
memcpy(dev->broadcast, &iph->daddr, 4);
- dev->hard_header_len = LL_MAX_HEADER + sizeof(struct iphdr);
dev->mtu = ETH_DATA_LEN;
dev->flags = IFF_NOARP;
dev->addr_len = 4;
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index ce4f10e9513a..75406603fa1e 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -615,6 +615,7 @@ static inline u32 fnhe_hashfun(__be32 daddr)
static void fill_route_from_fnhe(struct rtable *rt, struct fib_nh_exception *fnhe)
{
rt->rt_pmtu = fnhe->fnhe_pmtu;
+ rt->rt_mtu_locked = fnhe->fnhe_mtu_locked;
rt->dst.expires = fnhe->fnhe_expires;
if (fnhe->fnhe_gw) {
@@ -625,7 +626,7 @@ static void fill_route_from_fnhe(struct rtable *rt, struct fib_nh_exception *fnh
}
static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
- u32 pmtu, unsigned long expires)
+ u32 pmtu, bool lock, unsigned long expires)
{
struct fnhe_hash_bucket *hash;
struct fib_nh_exception *fnhe;
@@ -662,8 +663,10 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
fnhe->fnhe_genid = genid;
if (gw)
fnhe->fnhe_gw = gw;
- if (pmtu)
+ if (pmtu) {
fnhe->fnhe_pmtu = pmtu;
+ fnhe->fnhe_mtu_locked = lock;
+ }
fnhe->fnhe_expires = max(1UL, expires);
/* Update all cached dsts too */
rt = rcu_dereference(fnhe->fnhe_rth_input);
@@ -687,6 +690,7 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
fnhe->fnhe_daddr = daddr;
fnhe->fnhe_gw = gw;
fnhe->fnhe_pmtu = pmtu;
+ fnhe->fnhe_mtu_locked = lock;
fnhe->fnhe_expires = expires;
/* Exception created; mark the cached routes for the nexthop
@@ -768,7 +772,8 @@ static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flow
struct fib_nh *nh = &FIB_RES_NH(res);
update_or_create_fnhe(nh, fl4->daddr, new_gw,
- 0, jiffies + ip_rt_gc_timeout);
+ 0, false,
+ jiffies + ip_rt_gc_timeout);
}
if (kill_route)
rt->dst.obsolete = DST_OBSOLETE_KILL;
@@ -981,15 +986,18 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
{
struct dst_entry *dst = &rt->dst;
struct fib_result res;
+ bool lock = false;
- if (dst_metric_locked(dst, RTAX_MTU))
+ if (ip_mtu_locked(dst))
return;
if (ipv4_mtu(dst) < mtu)
return;
- if (mtu < ip_rt_min_pmtu)
+ if (mtu < ip_rt_min_pmtu) {
+ lock = true;
mtu = ip_rt_min_pmtu;
+ }
if (rt->rt_pmtu == mtu &&
time_before(jiffies, dst->expires - ip_rt_mtu_expires / 2))
@@ -999,7 +1007,7 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
if (fib_lookup(dev_net(dst->dev), fl4, &res, 0) == 0) {
struct fib_nh *nh = &FIB_RES_NH(res);
- update_or_create_fnhe(nh, fl4->daddr, 0, mtu,
+ update_or_create_fnhe(nh, fl4->daddr, 0, mtu, lock,
jiffies + ip_rt_mtu_expires);
}
rcu_read_unlock();
@@ -1256,7 +1264,7 @@ static unsigned int ipv4_mtu(const struct dst_entry *dst)
mtu = READ_ONCE(dst->dev->mtu);
- if (unlikely(dst_metric_locked(dst, RTAX_MTU))) {
+ if (unlikely(ip_mtu_locked(dst))) {
if (rt->rt_uses_gateway && mtu > 576)
mtu = 576;
}
@@ -1479,6 +1487,7 @@ static struct rtable *rt_dst_alloc(struct net_device *dev,
rt->rt_is_input = 0;
rt->rt_iif = 0;
rt->rt_pmtu = 0;
+ rt->rt_mtu_locked = 0;
rt->rt_gateway = 0;
rt->rt_uses_gateway = 0;
rt->rt_table_id = 0;
@@ -2399,6 +2408,7 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or
rt->rt_is_input = ort->rt_is_input;
rt->rt_iif = ort->rt_iif;
rt->rt_pmtu = ort->rt_pmtu;
+ rt->rt_mtu_locked = ort->rt_mtu_locked;
rt->rt_genid = rt_genid_ipv4(net);
rt->rt_flags = ort->rt_flags;
@@ -2501,6 +2511,8 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, u32 table_id,
memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics));
if (rt->rt_pmtu && expires)
metrics[RTAX_MTU - 1] = rt->rt_pmtu;
+ if (rt->rt_mtu_locked && expires)
+ metrics[RTAX_LOCK - 1] |= BIT(RTAX_MTU);
if (rtnetlink_put_metrics(skb, metrics) < 0)
goto nla_put_failure;
diff --git a/net/ipv4/tcp_illinois.c b/net/ipv4/tcp_illinois.c
index 2ab9bbb6faff..5ed6a89894fd 100644
--- a/net/ipv4/tcp_illinois.c
+++ b/net/ipv4/tcp_illinois.c
@@ -6,7 +6,7 @@
* The algorithm is described in:
* "TCP-Illinois: A Loss and Delay-Based Congestion Control Algorithm
* for High-Speed Networks"
- * http://www.ifp.illinois.edu/~srikant/Papers/liubassri06perf.pdf
+ * http://tamerbasar.csl.illinois.edu/LiuBasarSrikantPerfEvalArtJun2008.pdf
*
* Implemented from description in paper and ns-2 simulation.
* Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org>
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index b1cfce7f8e85..b4e95494b05b 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -558,8 +558,8 @@ static inline void tcp_rcv_rtt_measure_ts(struct sock *sk,
void tcp_rcv_space_adjust(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
+ u32 copied;
int time;
- int copied;
time = tcp_time_stamp - tp->rcvq_space.time;
if (time < (tp->rcv_rtt_est.rtt >> 3) || tp->rcv_rtt_est.rtt == 0)
@@ -581,12 +581,13 @@ void tcp_rcv_space_adjust(struct sock *sk)
if (sysctl_tcp_moderate_rcvbuf &&
!(sk->sk_userlocks & SOCK_RCVBUF_LOCK)) {
- int rcvwin, rcvmem, rcvbuf;
+ int rcvmem, rcvbuf;
+ u64 rcvwin;
/* minimal window to cope with packet losses, assuming
* steady state. Add some cushion because of small variations.
*/
- rcvwin = (copied << 1) + 16 * tp->advmss;
+ rcvwin = ((u64)copied << 1) + 16 * tp->advmss;
/* If rate increased by 25%,
* assume slow start, rcvwin = 3 * copied
@@ -606,7 +607,8 @@ void tcp_rcv_space_adjust(struct sock *sk)
while (tcp_win_from_space(rcvmem) < tp->advmss)
rcvmem += 128;
- rcvbuf = min(rcvwin / tp->advmss * rcvmem, sysctl_tcp_rmem[2]);
+ do_div(rcvwin, tp->advmss);
+ rcvbuf = min_t(u64, rcvwin * rcvmem, sysctl_tcp_rmem[2]);
if (rcvbuf > sk->sk_rcvbuf) {
sk->sk_rcvbuf = rcvbuf;
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index 39eebc7b2831..528f13718687 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -101,6 +101,7 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
xdst->u.rt.rt_gateway = rt->rt_gateway;
xdst->u.rt.rt_uses_gateway = rt->rt_uses_gateway;
xdst->u.rt.rt_pmtu = rt->rt_pmtu;
+ xdst->u.rt.rt_mtu_locked = rt->rt_mtu_locked;
xdst->u.rt.rt_table_id = rt->rt_table_id;
INIT_LIST_HEAD(&xdst->u.rt.rt_uncached);
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index e5846d1f9b55..9b92960f024d 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -1787,7 +1787,8 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns
ret = 0;
if (!ip6mr_new_table(net, v))
ret = -ENOMEM;
- raw6_sk(sk)->ip6mr_table = v;
+ else
+ raw6_sk(sk)->ip6mr_table = v;
rtnl_unlock();
return ret;
}
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 51f7c32f04d7..dec4e7bda5f3 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -1574,6 +1574,13 @@ static int ipip6_newlink(struct net *src_net, struct net_device *dev,
if (err < 0)
return err;
+ if (tb[IFLA_MTU]) {
+ u32 mtu = nla_get_u32(tb[IFLA_MTU]);
+
+ if (mtu >= IPV6_MIN_MTU && mtu <= 0xFFF8 - dev->hard_header_len)
+ dev->mtu = mtu;
+ }
+
#ifdef CONFIG_IPV6_SIT_6RD
if (ipip6_netlink_6rd_parms(data, &ip6rd))
err = ipip6_tunnel_update_6rd(nt, &ip6rd);
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 15150b412930..3ba903ff2bb0 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -437,6 +437,24 @@ static int verify_address_len(const void *p)
return 0;
}
+static inline int sadb_key_len(const struct sadb_key *key)
+{
+ int key_bytes = DIV_ROUND_UP(key->sadb_key_bits, 8);
+
+ return DIV_ROUND_UP(sizeof(struct sadb_key) + key_bytes,
+ sizeof(uint64_t));
+}
+
+static int verify_key_len(const void *p)
+{
+ const struct sadb_key *key = p;
+
+ if (sadb_key_len(key) > key->sadb_key_len)
+ return -EINVAL;
+
+ return 0;
+}
+
static inline int pfkey_sec_ctx_len(const struct sadb_x_sec_ctx *sec_ctx)
{
return DIV_ROUND_UP(sizeof(struct sadb_x_sec_ctx) +
@@ -533,16 +551,25 @@ static int parse_exthdrs(struct sk_buff *skb, const struct sadb_msg *hdr, void *
return -EINVAL;
if (ext_hdrs[ext_type-1] != NULL)
return -EINVAL;
- if (ext_type == SADB_EXT_ADDRESS_SRC ||
- ext_type == SADB_EXT_ADDRESS_DST ||
- ext_type == SADB_EXT_ADDRESS_PROXY ||
- ext_type == SADB_X_EXT_NAT_T_OA) {
+ switch (ext_type) {
+ case SADB_EXT_ADDRESS_SRC:
+ case SADB_EXT_ADDRESS_DST:
+ case SADB_EXT_ADDRESS_PROXY:
+ case SADB_X_EXT_NAT_T_OA:
if (verify_address_len(p))
return -EINVAL;
- }
- if (ext_type == SADB_X_EXT_SEC_CTX) {
+ break;
+ case SADB_X_EXT_SEC_CTX:
if (verify_sec_ctx_len(p))
return -EINVAL;
+ break;
+ case SADB_EXT_KEY_AUTH:
+ case SADB_EXT_KEY_ENCRYPT:
+ if (verify_key_len(p))
+ return -EINVAL;
+ break;
+ default:
+ break;
}
ext_hdrs[ext_type-1] = (void *) p;
}
@@ -1111,14 +1138,12 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,
key = ext_hdrs[SADB_EXT_KEY_AUTH - 1];
if (key != NULL &&
sa->sadb_sa_auth != SADB_X_AALG_NULL &&
- ((key->sadb_key_bits+7) / 8 == 0 ||
- (key->sadb_key_bits+7) / 8 > key->sadb_key_len * sizeof(uint64_t)))
+ key->sadb_key_bits == 0)
return ERR_PTR(-EINVAL);
key = ext_hdrs[SADB_EXT_KEY_ENCRYPT-1];
if (key != NULL &&
sa->sadb_sa_encrypt != SADB_EALG_NULL &&
- ((key->sadb_key_bits+7) / 8 == 0 ||
- (key->sadb_key_bits+7) / 8 > key->sadb_key_len * sizeof(uint64_t)))
+ key->sadb_key_bits == 0)
return ERR_PTR(-EINVAL);
x = xfrm_state_alloc(net);
diff --git a/net/llc/llc_c_ac.c b/net/llc/llc_c_ac.c
index f8d4ab8ca1a5..4b60f68cb492 100644
--- a/net/llc/llc_c_ac.c
+++ b/net/llc/llc_c_ac.c
@@ -389,7 +389,7 @@ static int llc_conn_ac_send_i_cmd_p_set_0(struct sock *sk, struct sk_buff *skb)
llc_pdu_init_as_i_cmd(skb, 0, llc->vS, llc->vR);
rc = llc_mac_hdr_init(skb, llc->dev->dev_addr, llc->daddr.mac);
if (likely(!rc)) {
- llc_conn_send_pdu(sk, skb);
+ rc = llc_conn_send_pdu(sk, skb);
llc_conn_ac_inc_vs_by_1(sk, skb);
}
return rc;
@@ -916,7 +916,7 @@ static int llc_conn_ac_send_i_rsp_f_set_ackpf(struct sock *sk,
llc_pdu_init_as_i_cmd(skb, llc->ack_pf, llc->vS, llc->vR);
rc = llc_mac_hdr_init(skb, llc->dev->dev_addr, llc->daddr.mac);
if (likely(!rc)) {
- llc_conn_send_pdu(sk, skb);
+ rc = llc_conn_send_pdu(sk, skb);
llc_conn_ac_inc_vs_by_1(sk, skb);
}
return rc;
@@ -935,14 +935,17 @@ static int llc_conn_ac_send_i_rsp_f_set_ackpf(struct sock *sk,
int llc_conn_ac_send_i_as_ack(struct sock *sk, struct sk_buff *skb)
{
struct llc_sock *llc = llc_sk(sk);
+ int ret;
if (llc->ack_must_be_send) {
- llc_conn_ac_send_i_rsp_f_set_ackpf(sk, skb);
+ ret = llc_conn_ac_send_i_rsp_f_set_ackpf(sk, skb);
llc->ack_must_be_send = 0 ;
llc->ack_pf = 0;
- } else
- llc_conn_ac_send_i_cmd_p_set_0(sk, skb);
- return 0;
+ } else {
+ ret = llc_conn_ac_send_i_cmd_p_set_0(sk, skb);
+ }
+
+ return ret;
}
/**
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c
index d861b74ad068..79c346fd859b 100644
--- a/net/llc/llc_conn.c
+++ b/net/llc/llc_conn.c
@@ -30,7 +30,7 @@
#endif
static int llc_find_offset(int state, int ev_type);
-static void llc_conn_send_pdus(struct sock *sk);
+static int llc_conn_send_pdus(struct sock *sk, struct sk_buff *skb);
static int llc_conn_service(struct sock *sk, struct sk_buff *skb);
static int llc_exec_conn_trans_actions(struct sock *sk,
struct llc_conn_state_trans *trans,
@@ -193,11 +193,11 @@ out_skb_put:
return rc;
}
-void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb)
+int llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb)
{
/* queue PDU to send to MAC layer */
skb_queue_tail(&sk->sk_write_queue, skb);
- llc_conn_send_pdus(sk);
+ return llc_conn_send_pdus(sk, skb);
}
/**
@@ -255,7 +255,7 @@ void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, u8 first_p_bit)
if (howmany_resend > 0)
llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO;
/* any PDUs to re-send are queued up; start sending to MAC */
- llc_conn_send_pdus(sk);
+ llc_conn_send_pdus(sk, NULL);
out:;
}
@@ -296,7 +296,7 @@ void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit)
if (howmany_resend > 0)
llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO;
/* any PDUs to re-send are queued up; start sending to MAC */
- llc_conn_send_pdus(sk);
+ llc_conn_send_pdus(sk, NULL);
out:;
}
@@ -340,12 +340,16 @@ out:
/**
* llc_conn_send_pdus - Sends queued PDUs
* @sk: active connection
+ * @hold_skb: the skb held by caller, or NULL if does not care
*
- * Sends queued pdus to MAC layer for transmission.
+ * Sends queued pdus to MAC layer for transmission. When @hold_skb is
+ * NULL, always return 0. Otherwise, return 0 if @hold_skb is sent
+ * successfully, or 1 for failure.
*/
-static void llc_conn_send_pdus(struct sock *sk)
+static int llc_conn_send_pdus(struct sock *sk, struct sk_buff *hold_skb)
{
struct sk_buff *skb;
+ int ret = 0;
while ((skb = skb_dequeue(&sk->sk_write_queue)) != NULL) {
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
@@ -357,10 +361,20 @@ static void llc_conn_send_pdus(struct sock *sk)
skb_queue_tail(&llc_sk(sk)->pdu_unack_q, skb);
if (!skb2)
break;
- skb = skb2;
+ dev_queue_xmit(skb2);
+ } else {
+ bool is_target = skb == hold_skb;
+ int rc;
+
+ if (is_target)
+ skb_get(skb);
+ rc = dev_queue_xmit(skb);
+ if (is_target)
+ ret = rc;
}
- dev_queue_xmit(skb);
}
+
+ return ret;
}
/**
diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c
index b0380927f05f..3f33ec44bd28 100644
--- a/net/netlabel/netlabel_unlabeled.c
+++ b/net/netlabel/netlabel_unlabeled.c
@@ -1469,6 +1469,16 @@ int netlbl_unlabel_getattr(const struct sk_buff *skb,
iface = rcu_dereference(netlbl_unlhsh_def);
if (iface == NULL || !iface->valid)
goto unlabel_getattr_nolabel;
+
+#if IS_ENABLED(CONFIG_IPV6)
+ /* When resolving a fallback label, check the sk_buff version as
+ * it is possible (e.g. SCTP) to have family = PF_INET6 while
+ * receiving ip_hdr(skb)->version = 4.
+ */
+ if (family == PF_INET6 && ip_hdr(skb)->version == 4)
+ family = PF_INET;
+#endif /* IPv6 */
+
switch (family) {
case PF_INET: {
struct iphdr *hdr4;
diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c
index 3621a902cb6e..d25212b135ea 100644
--- a/net/nfc/llcp_commands.c
+++ b/net/nfc/llcp_commands.c
@@ -149,6 +149,10 @@ struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri,
pr_debug("uri: %s, len: %zu\n", uri, uri_len);
+ /* sdreq->tlv_len is u8, takes uri_len, + 3 for header, + 1 for NULL */
+ if (WARN_ON_ONCE(uri_len > U8_MAX - 4))
+ return NULL;
+
sdreq = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL);
if (sdreq == NULL)
return NULL;
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index 12dfb457275d..32cb0c87e852 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -68,7 +68,8 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
};
static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = {
- [NFC_SDP_ATTR_URI] = { .type = NLA_STRING },
+ [NFC_SDP_ATTR_URI] = { .type = NLA_STRING,
+ .len = U8_MAX - 4 },
[NFC_SDP_ATTR_SAP] = { .type = NLA_U8 },
};
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 392d4e2c0a24..7814e5f744e6 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -2779,7 +2779,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
if (unlikely(offset < 0))
goto out_free;
} else if (reserve) {
- skb_push(skb, reserve);
+ skb_reserve(skb, -reserve);
}
/* Returns -EFAULT on error */
@@ -4156,6 +4156,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");
@@ -4198,7 +4199,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
goto out;
if (po->tp_version >= TPACKET_V3 &&
req->tp_block_size <=
- BLK_PLUS_PRIV((u64)req_u->req3.tp_sizeof_priv))
+ BLK_PLUS_PRIV((u64)req_u->req3.tp_sizeof_priv) + sizeof(struct tpacket3_hdr))
goto out;
if (unlikely(req->tp_frame_size < po->tp_hdrlen +
po->tp_reserve))
@@ -4291,6 +4292,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
if (pg_vec)
free_pg_vec(pg_vec, order, req->tp_block_nr);
out:
+ release_sock(sk);
return err;
}
diff --git a/net/rds/ib.c b/net/rds/ib.c
index f222885ac0c7..ed51ccc84b3a 100644
--- a/net/rds/ib.c
+++ b/net/rds/ib.c
@@ -336,7 +336,8 @@ static int rds_ib_laddr_check(struct net *net, __be32 addr)
/* Create a CMA ID and try to bind it. This catches both
* IB and iWARP capable NICs.
*/
- cm_id = rdma_create_id(&init_net, NULL, NULL, RDMA_PS_TCP, IB_QPT_RC);
+ cm_id = rdma_create_id(&init_net, rds_rdma_cm_event_handler,
+ NULL, RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(cm_id))
return PTR_ERR(cm_id);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index f08ed375bb91..4490dec28f50 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -13807,7 +13807,8 @@ void cfg80211_ft_event(struct net_device *netdev,
if (!ft_event->target_ap)
return;
- msg = nlmsg_new(100 + ft_event->ric_ies_len, GFP_KERNEL);
+ msg = nlmsg_new(100 + ft_event->ies_len + ft_event->ric_ies_len,
+ GFP_KERNEL);
if (!msg)
return;
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index dd243d2abd87..138d7f100f7e 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -743,7 +743,7 @@ int conf_write(const char *name)
struct menu *menu;
const char *basename;
const char *str;
- char dirname[PATH_MAX+1], tmpname[PATH_MAX+1], newname[PATH_MAX+1];
+ char dirname[PATH_MAX+1], tmpname[PATH_MAX+22], newname[PATH_MAX+8];
char *env;
dirname[0] = 0;
diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c
index cbf4996dd9c1..ed29bad1f03a 100644
--- a/scripts/kconfig/expr.c
+++ b/scripts/kconfig/expr.c
@@ -113,7 +113,7 @@ void expr_free(struct expr *e)
break;
case E_NOT:
expr_free(e->left.expr);
- return;
+ break;
case E_EQUAL:
case E_GEQ:
case E_GTH:
diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index b05cc3d4a9be..8360feaf51ce 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -364,6 +364,7 @@ void menu_finalize(struct menu *parent)
menu->parent = parent;
last_menu = menu;
}
+ expr_free(basedep);
if (last_menu) {
parent->list = parent->next;
parent->next = last_menu->next;
diff --git a/scripts/kconfig/zconf.y b/scripts/kconfig/zconf.y
index 71bf8bff696a..5122ed2d839a 100644
--- a/scripts/kconfig/zconf.y
+++ b/scripts/kconfig/zconf.y
@@ -107,7 +107,27 @@ static struct menu *current_menu, *current_entry;
%%
input: nl start | start;
-start: mainmenu_stmt stmt_list | stmt_list;
+start: mainmenu_stmt stmt_list | no_mainmenu_stmt stmt_list;
+
+/* mainmenu entry */
+
+mainmenu_stmt: T_MAINMENU prompt nl
+{
+ menu_add_prompt(P_MENU, $2, NULL);
+};
+
+/* Default main menu, if there's no mainmenu entry */
+
+no_mainmenu_stmt: /* empty */
+{
+ /*
+ * Hack: Keep the main menu title on the heap so we can safely free it
+ * later regardless of whether it comes from the 'prompt' in
+ * mainmenu_stmt or here
+ */
+ menu_add_prompt(P_MENU, strdup("Linux Kernel Configuration"), NULL);
+};
+
stmt_list:
/* empty */
@@ -344,13 +364,6 @@ if_block:
| if_block choice_stmt
;
-/* mainmenu entry */
-
-mainmenu_stmt: T_MAINMENU prompt nl
-{
- menu_add_prompt(P_MENU, $2, NULL);
-};
-
/* menu entry */
menu: T_MENU prompt T_EOL
@@ -495,6 +508,7 @@ word_opt: /* empty */ { $$ = NULL; }
void conf_parse(const char *name)
{
+ const char *tmp;
struct symbol *sym;
int i;
@@ -502,7 +516,6 @@ void conf_parse(const char *name)
sym_init();
_menu_init();
- rootmenu.prompt = menu_add_prompt(P_MENU, "Linux Kernel Configuration", NULL);
if (getenv("ZCONF_DEBUG"))
zconfdebug = 1;
@@ -512,8 +525,10 @@ void conf_parse(const char *name)
if (!modules_sym)
modules_sym = sym_find( "n" );
+ tmp = rootmenu.prompt->text;
rootmenu.prompt->text = _(rootmenu.prompt->text);
rootmenu.prompt->text = sym_expand_string_value(rootmenu.prompt->text);
+ free((char*)tmp);
menu_finalize(&rootmenu);
for_all_symbols(i, sym) {
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index df303346029b..648a0461f8ed 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -10,6 +10,7 @@ config IMA
select CRYPTO_HASH_INFO
select TCG_TPM if HAS_IOMEM && !UML
select TCG_TIS if TCG_TPM && X86
+ select TCG_CRB if TCG_TPM && ACPI
select TCG_IBMVTPM if TCG_TPM && PPC_PSERIES
help
The Trusted Computing Group(TCG) runtime Integrity
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 8da7c91b725d..c36b98b07d6b 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -383,14 +383,10 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
result = ima_protect_xattr(dentry, xattr_name, xattr_value,
xattr_value_len);
if (result == 1) {
- bool digsig;
-
if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST))
return -EINVAL;
- digsig = (xvalue->type == EVM_IMA_XATTR_DIGSIG);
- if (!digsig && (ima_appraise & IMA_APPRAISE_ENFORCE))
- return -EPERM;
- ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
+ ima_reset_appraise_flags(d_backing_inode(dentry),
+ (xvalue->type == EVM_IMA_XATTR_DIGSIG) ? 1 : 0);
result = 0;
}
return result;
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index 6eb62936c672..a29209fa5674 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -78,6 +78,8 @@ int __init ima_init_crypto(void)
hash_algo_name[ima_hash_algo], rc);
return rc;
}
+ pr_info("Allocated hash algorithm: %s\n",
+ hash_algo_name[ima_hash_algo]);
return 0;
}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 98289ba2a2e6..236dce30e517 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -16,6 +16,9 @@
* implements the IMA hooks: ima_bprm_check, ima_file_mmap,
* and ima_file_check.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/file.h>
#include <linux/binfmts.h>
@@ -353,6 +356,16 @@ static int __init init_ima(void)
hash_setup(CONFIG_IMA_DEFAULT_HASH);
error = ima_init();
+
+ if (error && strcmp(hash_algo_name[ima_hash_algo],
+ CONFIG_IMA_DEFAULT_HASH) != 0) {
+ pr_info("Allocating %s failed, going to use default hash algorithm %s\n",
+ hash_algo_name[ima_hash_algo], CONFIG_IMA_DEFAULT_HASH);
+ hash_setup_done = 0;
+ hash_setup(CONFIG_IMA_DEFAULT_HASH);
+ error = ima_init();
+ }
+
if (!error) {
ima_initialized = 1;
ima_update_policy_flag();
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 514380104944..180261da33c9 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -976,9 +976,9 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
struct snd_rawmidi_runtime *runtime = substream->runtime;
unsigned long appl_ptr;
- spin_lock_irqsave(&runtime->lock, flags);
if (userbuf)
mutex_lock(&runtime->realloc_mutex);
+ spin_lock_irqsave(&runtime->lock, flags);
while (count > 0 && runtime->avail) {
count1 = runtime->buffer_size - runtime->appl_ptr;
if (count1 > count)
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 0e51e5cd33fe..9b226aa83b3d 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -548,7 +548,7 @@ static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
}
timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
- SNDRV_TIMER_EVENT_CONTINUE);
+ SNDRV_TIMER_EVENT_PAUSE);
unlock:
spin_unlock_irqrestore(&timer->lock, flags);
return result;
@@ -570,7 +570,7 @@ static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop)
list_del_init(&timeri->ack_list);
list_del_init(&timeri->active_list);
snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
- SNDRV_TIMER_EVENT_CONTINUE);
+ SNDRV_TIMER_EVENT_PAUSE);
spin_unlock(&timeri->timer->lock);
}
spin_unlock_irqrestore(&slave_active_lock, flags);
diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c
index 6c58e6f73a01..7c6ef879c520 100644
--- a/sound/core/vmaster.c
+++ b/sound/core/vmaster.c
@@ -68,10 +68,13 @@ static int slave_update(struct link_slave *slave)
return -ENOMEM;
uctl->id = slave->slave.id;
err = slave->slave.get(&slave->slave, uctl);
+ if (err < 0)
+ goto error;
for (ch = 0; ch < slave->info.count; ch++)
slave->vals[ch] = uctl->value.integer.value[ch];
+ error:
kfree(uctl);
- return 0;
+ return err < 0 ? err : 0;
}
/* get the slave ctl info and save the initial values */
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index e94cfd5c69f7..ebec1a1ae543 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -84,7 +84,6 @@ config SND_HDA_PATCH_LOADER
config SND_HDA_CODEC_REALTEK
tristate "Build Realtek HD-audio codec support"
select SND_HDA_GENERIC
- select INPUT
help
Say Y or M here to include Realtek HD-audio codec support in
snd-hda-intel driver, such as ALC880.
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 6a789278970e..580b8943b965 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -3495,6 +3495,7 @@ static void alc280_fixup_hp_gpio4(struct hda_codec *codec,
}
}
+#if IS_REACHABLE(INPUT)
static void gpio2_mic_hotkey_event(struct hda_codec *codec,
struct hda_jack_callback *event)
{
@@ -3627,6 +3628,10 @@ static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec,
spec->kb_dev = NULL;
}
}
+#else /* INPUT */
+#define alc280_fixup_hp_gpio2_mic_hotkey NULL
+#define alc233_fixup_lenovo_line2_mic_hotkey NULL
+#endif /* INPUT */
static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c
index 29a97d52e8ad..66d6c52e7761 100644
--- a/sound/soc/au1x/ac97c.c
+++ b/sound/soc/au1x/ac97c.c
@@ -91,8 +91,8 @@ static unsigned short au1xac97c_ac97_read(struct snd_ac97 *ac97,
do {
mutex_lock(&ctx->lock);
- tmo = 5;
- while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--)
+ tmo = 6;
+ while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo)
udelay(21); /* wait an ac97 frame time */
if (!tmo) {
pr_debug("ac97rd timeout #1\n");
@@ -105,7 +105,7 @@ static unsigned short au1xac97c_ac97_read(struct snd_ac97 *ac97,
* poll, Forrest, poll...
*/
tmo = 0x10000;
- while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--)
+ while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo)
asm volatile ("nop");
data = RD(ctx, AC97_CMDRESP);
diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c
index 43f00dcff7af..a80ac1d18298 100644
--- a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c
+++ b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -86,6 +86,8 @@ static int msm_digcdc_clock_control(bool flag)
if (flag) {
mutex_lock(&pdata->cdc_int_mclk0_mutex);
if (atomic_read(&pdata->int_mclk0_enabled) == false) {
+ if (msm_dig_cdc->regmap->cache_only == true)
+ return ret;
if (pdata->native_clk_set)
pdata->digital_cdc_core_clk.clk_freq_in_hz =
NATIVE_MCLK_RATE;
@@ -103,8 +105,7 @@ static int msm_digcdc_clock_control(bool flag)
* Avoid access to lpass register
* as clock enable failed during SSR.
*/
- if (ret == -ENODEV)
- msm_dig_cdc->regmap->cache_only = true;
+ msm_dig_cdc->regmap->cache_only = true;
return ret;
}
pr_debug("enabled digital codec core clk\n");
@@ -2156,6 +2157,10 @@ static int msm_dig_suspend(struct device *dev)
pr_debug("%s:digcodec not initialized, return\n", __func__);
return 0;
}
+ if (!registered_digcodec->component.card) {
+ pr_debug("%s:component 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__);
diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c
index 1636a1eeb002..be1b69c63bdf 100644
--- a/sound/soc/intel/common/sst-firmware.c
+++ b/sound/soc/intel/common/sst-firmware.c
@@ -260,7 +260,6 @@ int sst_dma_new(struct sst_dsp *sst)
struct sst_pdata *sst_pdata = sst->pdata;
struct sst_dma *dma;
struct resource mem;
- const char *dma_dev_name;
int ret = 0;
if (sst->pdata->resindex_dma_base == -1)
@@ -271,7 +270,6 @@ int sst_dma_new(struct sst_dsp *sst)
* is attached to the ADSP IP. */
switch (sst->pdata->dma_engine) {
case SST_DMA_TYPE_DW:
- dma_dev_name = "dw_dmac";
break;
default:
dev_err(sst->dev, "error: invalid DMA engine %d\n",
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
index 9af5de2952d4..9420534d3c5f 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
@@ -570,16 +570,19 @@ static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
u64 fe_id = kcontrol->private_value;
int session_type = SESSION_TYPE_RX;
int be_id = ucontrol->value.integer.value[3];
- struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000, 0};
int ret = 0;
cfg_data.app_type = ucontrol->value.integer.value[0];
cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
if (ucontrol->value.integer.value[2] != 0)
cfg_data.sample_rate = ucontrol->value.integer.value[2];
- pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
+ if (ucontrol->value.integer.value[4] != 0)
+ cfg_data.copp_token = ucontrol->value.integer.value[4];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
be_id, &cfg_data);
if (ret < 0)
@@ -610,9 +613,12 @@ static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
ucontrol->value.integer.value[2] = cfg_data.sample_rate;
ucontrol->value.integer.value[3] = be_id;
- pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ ucontrol->value.integer.value[4] = cfg_data.copp_token;
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
+
done:
return ret;
}
@@ -623,16 +629,19 @@ static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
u64 fe_id = kcontrol->private_value;
int session_type = SESSION_TYPE_TX;
int be_id = ucontrol->value.integer.value[3];
- struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000, 0};
int ret = 0;
cfg_data.app_type = ucontrol->value.integer.value[0];
cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
if (ucontrol->value.integer.value[2] != 0)
cfg_data.sample_rate = ucontrol->value.integer.value[2];
- pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
+ if (ucontrol->value.integer.value[4] != 0)
+ cfg_data.copp_token = ucontrol->value.integer.value[4];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
be_id, &cfg_data);
if (ret < 0)
@@ -663,9 +672,11 @@ static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
ucontrol->value.integer.value[2] = cfg_data.sample_rate;
ucontrol->value.integer.value[3] = be_id;
- pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ ucontrol->value.integer.value[4] = cfg_data.copp_token;
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
done:
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 280c665dded4..1e69ddcc3464 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -385,16 +385,10 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
return -ENOMEM;
}
} else {
- if (q6asm_get_svc_version(APR_SVC_ASM) >=
- ADSP_ASM_API_VERSION_V2)
- ret = q6asm_open_write_v5(prtd->audio_client,
- fmt_type, bits_per_sample);
- else
- ret = q6asm_open_write_v4(prtd->audio_client,
+ ret = q6asm_open_write_with_retry(prtd->audio_client,
fmt_type, bits_per_sample);
-
if (ret < 0) {
- pr_err("%s: q6asm_open_write_v4 failed (%d)\n",
+ pr_err("%s: q6asm_open_write_with_retry failed (%d)\n",
__func__, ret);
q6asm_audio_client_free(prtd->audio_client);
prtd->audio_client = NULL;
@@ -666,6 +660,7 @@ static int msm_pcm_open(struct snd_pcm_substream *substream)
if (!prtd->audio_client) {
pr_info("%s: Could not allocate memory\n", __func__);
kfree(prtd);
+ prtd = NULL;
return -ENOMEM;
}
@@ -1141,6 +1136,12 @@ static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol,
}
prtd = substream->runtime->private_data;
+ if (prtd == NULL) {
+ pr_err("%s prtd is null.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
if (prtd->audio_client == NULL) {
pr_err("%s prtd is null.\n", __func__);
ret = -EINVAL;
@@ -1607,16 +1608,19 @@ static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
u64 fe_id = kcontrol->private_value;
int session_type = SESSION_TYPE_RX;
int be_id = ucontrol->value.integer.value[3];
- struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000, 0};
int ret = 0;
cfg_data.app_type = ucontrol->value.integer.value[0];
cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
if (ucontrol->value.integer.value[2] != 0)
cfg_data.sample_rate = ucontrol->value.integer.value[2];
- pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
+ if (ucontrol->value.integer.value[4] != 0)
+ cfg_data.copp_token = ucontrol->value.integer.value[4];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
be_id, &cfg_data);
if (ret < 0)
@@ -1647,9 +1651,11 @@ static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
ucontrol->value.integer.value[2] = cfg_data.sample_rate;
ucontrol->value.integer.value[3] = be_id;
- pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ ucontrol->value.integer.value[4] = cfg_data.copp_token;
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
done:
return ret;
}
@@ -2033,16 +2039,19 @@ static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
u64 fe_id = kcontrol->private_value;
int session_type = SESSION_TYPE_TX;
int be_id = ucontrol->value.integer.value[3];
- struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
+ struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000, 0};
int ret = 0;
cfg_data.app_type = ucontrol->value.integer.value[0];
cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
if (ucontrol->value.integer.value[2] != 0)
cfg_data.sample_rate = ucontrol->value.integer.value[2];
- pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
+ if (ucontrol->value.integer.value[4] != 0)
+ cfg_data.copp_token = ucontrol->value.integer.value[4];
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
be_id, &cfg_data);
if (ret < 0)
@@ -2073,9 +2082,11 @@ static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
ucontrol->value.integer.value[2] = cfg_data.sample_rate;
ucontrol->value.integer.value[3] = be_id;
- pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ ucontrol->value.integer.value[4] = cfg_data.copp_token;
+ pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d copp_token %d\n",
__func__, fe_id, session_type, be_id,
- cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
+ cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate,
+ cfg_data.copp_token);
done:
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 4e4970b7be33..d0dabcb4e337 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -116,6 +116,7 @@ enum {
#define QUAT_MI2S_TX_TEXT "QUAT_MI2S_TX"
#define ADM_LSM_TX_TEXT "ADM_LSM_TX"
#define INT3_MI2S_TX_TEXT "INT3_MI2S_TX"
+#define QUAT_TDM_TX_0_TEXT "QUAT_TDM_TX_0"
#define LSM_FUNCTION_TEXT "LSM Function"
static const char * const lsm_port_text[] = {
@@ -123,7 +124,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
+ INT3_MI2S_TX_TEXT, QUAT_TDM_TX_0_TEXT
};
struct msm_pcm_route_bdai_pp_params {
@@ -834,10 +835,10 @@ int msm_pcm_routing_reg_stream_app_type_cfg(
goto done;
}
- pr_debug("%s: fedai_id %d, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ pr_debug("%s: fedai_id %d, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d copp_token %d\n",
__func__, fedai_id, session_type, be_id,
cfg_data->app_type, cfg_data->acdb_dev_id,
- cfg_data->sample_rate);
+ cfg_data->sample_rate, cfg_data->copp_token);
if (!is_mm_lsm_fe_id(fedai_id)) {
pr_err("%s: Invalid machine driver ID %d\n",
@@ -920,10 +921,10 @@ int msm_pcm_routing_get_stream_app_type_cfg(
*bedai_id = be_id;
*cfg_data = fe_dai_app_type_cfg[fedai_id][session_type][be_id];
- pr_debug("%s: fedai_id %d, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+ pr_debug("%s: fedai_id %d, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d copp_token %d\n",
__func__, fedai_id, session_type, *bedai_id,
cfg_data->app_type, cfg_data->acdb_dev_id,
- cfg_data->sample_rate);
+ cfg_data->sample_rate, cfg_data->copp_token);
done:
return ret;
}
@@ -1141,6 +1142,7 @@ int msm_pcm_routing_reg_phy_compr_stream(int fe_id, int perf_mode,
u32 channels, sample_rate;
u16 bit_width = 16;
bool is_lsm;
+ u32 copp_token = 0;
pr_debug("%s:fe_id[%d] perf_mode[%d] id[%d] stream_type[%d] passt[%d]",
__func__, fe_id, perf_mode, dspst_id,
@@ -1224,6 +1226,8 @@ int msm_pcm_routing_reg_phy_compr_stream(int fe_id, int perf_mode,
fe_dai_app_type_cfg[fe_id][session_type][i].sample_rate;
bit_width =
app_type_cfg[app_type_idx].bit_width;
+ copp_token =
+ fe_dai_app_type_cfg[fe_id][session_type][i].copp_token;
} else {
sample_rate = msm_bedais[i].sample_rate;
}
@@ -1243,7 +1247,7 @@ int msm_pcm_routing_reg_phy_compr_stream(int fe_id, int perf_mode,
adm_open(msm_bedais[i].port_id,
path_type, sample_rate, channels,
topology, perf_mode, bit_width,
- app_type, acdb_dev_id);
+ app_type, acdb_dev_id, copp_token);
if ((copp_idx < 0) ||
(copp_idx >= MAX_COPPS_PER_PORT)) {
pr_err("%s:adm open failed coppid:%d\n",
@@ -1476,6 +1480,7 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
uint16_t bits_per_sample = 16;
uint32_t passthr_mode = LEGACY_PCM;
int ret = 0;
+ u32 copp_token = 0;
if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
/* bad ID assigned in machine driver */
@@ -1531,6 +1536,9 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
.sample_rate;
bits_per_sample =
app_type_cfg[app_type_idx].bit_width;
+ copp_token =
+ fe_dai_app_type_cfg[fedai_id][session_type][i]
+ .copp_token;
} else
sample_rate = msm_bedais[i].sample_rate;
@@ -1543,7 +1551,7 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
copp_idx = adm_open(msm_bedais[i].port_id, path_type,
sample_rate, channels, topology,
perf_mode, bits_per_sample,
- app_type, acdb_dev_id);
+ app_type, acdb_dev_id, copp_token);
if ((copp_idx < 0) ||
(copp_idx >= MAX_COPPS_PER_PORT)) {
pr_err("%s: adm open failed copp_idx:%d\n",
@@ -1716,6 +1724,7 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
struct msm_pcm_routing_fdai_data *fdai;
uint32_t passthr_mode;
bool is_lsm;
+ u32 copp_token = 0;
pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
@@ -1803,6 +1812,9 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
.sample_rate;
bits_per_sample =
app_type_cfg[app_type_idx].bit_width;
+ copp_token =
+ fe_dai_app_type_cfg[val][session_type][reg]
+ .copp_token;
} else
sample_rate = msm_bedais[reg].sample_rate;
@@ -1814,7 +1826,7 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
copp_idx = adm_open(msm_bedais[reg].port_id, path_type,
sample_rate, channels, topology,
fdai->perf_mode, bits_per_sample,
- app_type, acdb_dev_id);
+ app_type, acdb_dev_id, copp_token);
if ((copp_idx < 0) ||
(copp_idx >= MAX_COPPS_PER_PORT)) {
pr_err("%s: adm open failed\n", __func__);
@@ -2547,6 +2559,9 @@ static int msm_routing_lsm_port_put(struct snd_kcontrol *kcontrol,
case 10:
lsm_port = AFE_PORT_ID_INT3_MI2S_TX;
break;
+ case 11:
+ lsm_port = AFE_PORT_ID_QUATERNARY_TDM_TX;
+ break;
default:
pr_err("Default lsm port");
break;
@@ -2564,7 +2579,7 @@ static int msm_routing_lsm_func_get(struct snd_kcontrol *kcontrol,
u16 port_id;
enum afe_mad_type mad_type;
- pr_debug("%s: enter\n", __func__);
+ pr_debug("%s: id.name=%s\n", __func__, kcontrol->id.name);
for (i = 0; i < ARRAY_SIZE(lsm_port_text); i++)
if (!strnstr(kcontrol->id.name, lsm_port_text[i],
strlen(lsm_port_text[i])))
@@ -2590,6 +2605,10 @@ static int msm_routing_lsm_func_get(struct snd_kcontrol *kcontrol,
strlen(lsm_port_text[10])))
port_id = AFE_PORT_ID_INT3_MI2S_TX;
+ if (strnstr(kcontrol->id.name, lsm_port_text[11],
+ strlen(lsm_port_text[11])))
+ port_id = AFE_PORT_ID_QUATERNARY_TDM_TX;
+
mad_type = afe_port_get_mad_type(port_id);
pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
mad_type);
@@ -2623,7 +2642,7 @@ static int msm_routing_lsm_func_put(struct snd_kcontrol *kcontrol,
u16 port_id;
enum afe_mad_type mad_type;
- pr_debug("%s: enter\n", __func__);
+ pr_debug("%s: id.name=%s\n", __func__, kcontrol->id.name);
for (i = 0; i < ARRAY_SIZE(lsm_port_text); i++)
if (strnstr(kcontrol->id.name, lsm_port_text[i],
strlen(lsm_port_text[i])))
@@ -2669,6 +2688,10 @@ static int msm_routing_lsm_func_put(struct snd_kcontrol *kcontrol,
strlen(lsm_port_text[10])))
port_id = AFE_PORT_ID_INT3_MI2S_TX;
+ if (strnstr(kcontrol->id.name, lsm_port_text[11],
+ strlen(lsm_port_text[11])))
+ port_id = AFE_PORT_ID_QUATERNARY_TDM_TX;
+
pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
mad_type);
return afe_port_set_mad_type(port_id, mad_type);
@@ -11049,6 +11072,9 @@ static const struct snd_kcontrol_new lsm1_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new lsm2_mixer_controls[] = {
@@ -11076,6 +11102,9 @@ static const struct snd_kcontrol_new lsm2_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new lsm3_mixer_controls[] = {
@@ -11103,6 +11132,9 @@ static const struct snd_kcontrol_new lsm3_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new lsm4_mixer_controls[] = {
@@ -11130,6 +11162,9 @@ static const struct snd_kcontrol_new lsm4_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new lsm5_mixer_controls[] = {
@@ -11157,6 +11192,9 @@ static const struct snd_kcontrol_new lsm5_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new lsm6_mixer_controls[] = {
@@ -11184,6 +11222,9 @@ static const struct snd_kcontrol_new lsm6_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new lsm7_mixer_controls[] = {
@@ -11211,6 +11252,9 @@ static const struct snd_kcontrol_new lsm7_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new lsm8_mixer_controls[] = {
@@ -11238,6 +11282,9 @@ static const struct snd_kcontrol_new lsm8_mixer_controls[] = {
SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer,
msm_routing_put_listen_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer,
+ msm_routing_put_listen_mixer),
};
static const struct snd_kcontrol_new slim_fm_switch_mixer_controls =
@@ -11354,6 +11401,8 @@ static const struct snd_kcontrol_new lsm_controls[] = {
msm_routing_lsm_func_get, msm_routing_lsm_func_put),
SOC_ENUM_EXT(INT3_MI2S_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(QUAT_TDM_TX_0_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
/* kcontrol of lsm_port */
SOC_ENUM_EXT("LSM1 Port", lsm_port_enum,
msm_routing_lsm_port_get,
@@ -11379,6 +11428,9 @@ static const struct snd_kcontrol_new lsm_controls[] = {
SOC_ENUM_EXT("LSM8 Port", lsm_port_enum,
msm_routing_lsm_port_get,
msm_routing_lsm_port_put),
+ SOC_ENUM_EXT("LSM9 Port", lsm_port_enum,
+ msm_routing_lsm_port_get,
+ msm_routing_lsm_port_put),
};
static const char * const aanc_slim_0_rx_text[] = {
@@ -13725,6 +13777,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SEC_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
{"SEC_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
{"SEC_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"SEC_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
{"SEC_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
{"SEC_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
{"SEC_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
@@ -15200,6 +15253,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM1 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"LSM1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"LSM1 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"LSM1 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM1_UL_HL", NULL, "LSM1 Mixer"},
{"LSM2 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
@@ -15210,6 +15264,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM2 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"LSM2 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"LSM2 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"LSM2 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM2_UL_HL", NULL, "LSM2 Mixer"},
@@ -15221,6 +15276,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM3 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"LSM3 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"LSM3 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"LSM3 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM3_UL_HL", NULL, "LSM3 Mixer"},
@@ -15232,6 +15288,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM4 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"LSM4 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"LSM4 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"LSM4 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM4_UL_HL", NULL, "LSM4 Mixer"},
{"LSM5 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
@@ -15242,6 +15299,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM5 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"LSM5 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"LSM5 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+ {"LSM5 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM5_UL_HL", NULL, "LSM5 Mixer"},
{"LSM6 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
@@ -15250,6 +15308,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM6 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
{"LSM6 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
{"LSM6 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"LSM6 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM6_UL_HL", NULL, "LSM6 Mixer"},
{"LSM7 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
@@ -15258,6 +15317,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM7 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
{"LSM7 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
{"LSM7 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"LSM7 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM7_UL_HL", NULL, "LSM7 Mixer"},
{"LSM8 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
@@ -15266,6 +15326,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LSM8 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
{"LSM8 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
{"LSM8 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"LSM8 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
{"LSM8_UL_HL", NULL, "LSM8 Mixer"},
@@ -16127,6 +16188,7 @@ static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream)
u32 session_id;
struct media_format_info voc_be_media_format;
bool is_lsm;
+ u32 copp_token = 0;
pr_debug("%s: substream->pcm->id:%s\n",
__func__, substream->pcm->id);
@@ -16204,6 +16266,9 @@ static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream)
[be_id].sample_rate;
bits_per_sample =
app_type_cfg[app_type_idx].bit_width;
+ copp_token =
+ fe_dai_app_type_cfg[i][session_type]
+ [be_id].copp_token;
} else
sample_rate = bedai->sample_rate;
/*
@@ -16221,7 +16286,7 @@ static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream)
copp_idx = adm_open(bedai->port_id, path_type,
sample_rate, channels, topology,
fdai->perf_mode, bits_per_sample,
- app_type, acdb_dev_id);
+ app_type, acdb_dev_id, copp_token);
if ((copp_idx < 0) ||
(copp_idx >= MAX_COPPS_PER_PORT)) {
pr_err("%s: adm open failed\n", __func__);
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
index 63c90dc35c90..531f83d752b0 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
@@ -465,6 +465,7 @@ struct msm_pcm_stream_app_type_cfg {
int app_type;
int acdb_dev_id;
int sample_rate;
+ u32 copp_token;
};
/* dai_id: front-end ID,
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
index 76d8f8d9e33c..1b8150e5a30f 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -28,8 +28,15 @@
#include <linux/of_device.h>
#include "msm-pcm-voice-v2.h"
+#include "msm-qti-pp-config.h"
#include "q6voice.h"
+struct msm_dtmf_detected_event_data {
+ uint32_t event_type;
+ uint32_t payload_len;
+ struct vss_istream_evt_rx_dtmf_detected dtmf_payload;
+};
+
static struct msm_voice voice_info[VOICE_SESSION_INDEX_MAX];
static struct snd_pcm_hardware msm_pcm_hardware = {
@@ -52,6 +59,44 @@ static struct snd_pcm_hardware msm_pcm_hardware = {
.fifo_size = 0,
};
+
+static int get_idx_for_session(uint32_t session_id)
+{
+ int idx = 0;
+
+ switch (session_id) {
+ case VOICE_SESSION_VSID:
+ idx = VOICE_SESSION_INDEX;
+ break;
+ case VOICE2_SESSION_VSID:
+ idx = VOICE2_SESSION_INDEX;
+ break;
+ case VOLTE_SESSION_VSID:
+ idx = VOLTE_SESSION_INDEX;
+ break;
+ case QCHAT_SESSION_VSID:
+ idx = QCHAT_SESSION_INDEX;
+ break;
+ case VOWLAN_SESSION_VSID:
+ idx = VOWLAN_SESSION_INDEX;
+ break;
+ case VOICEMMODE1_VSID:
+ idx = VOICEMMODE1_INDEX;
+ break;
+ case VOICEMMODE2_VSID:
+ idx = VOICEMMODE2_INDEX;
+ break;
+ case ALL_SESSION_VSID:
+ idx = VOICE_SESSION_INDEX_MAX - 1;
+ break;
+ default:
+ pr_err("%s: Invalid session_id : %x\n", __func__, session_id);
+ break;
+ }
+
+ return idx;
+}
+
static bool is_volte(struct msm_voice *pvolte)
{
if (pvolte == &voice_info[VOLTE_SESSION_INDEX])
@@ -122,6 +167,38 @@ static uint32_t get_session_id(struct msm_voice *pvoc)
return session_id;
}
+static void dtmf_rx_detected_evt_hdlr(uint8_t *pkt,
+ char *session,
+ void *private_data)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *soc_prtd = private_data;
+ struct msm_dtmf_detected_event_data event_data = {ADSP_STREAM_PP_EVENT,
+ sizeof(struct vss_istream_evt_rx_dtmf_detected),
+ {0} };
+
+ if (!pkt) {
+ pr_err("%s: packet is NULL\n",
+ __func__);
+ return;
+ }
+
+ if (!private_data) {
+ pr_err("%s: private_data is NULL\n",
+ __func__);
+ return;
+ }
+
+ memcpy(&event_data.dtmf_payload, pkt,
+ sizeof(struct vss_istream_evt_rx_dtmf_detected));
+
+ ret = msm_adsp_inform_mixer_ctl(soc_prtd, (uint32_t *)&event_data);
+ if (ret) {
+ pr_err("%s: failed to inform mixer ctl. err = %d\n",
+ __func__, ret);
+ return;
+ }
+}
static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
{
@@ -151,6 +228,7 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
static int msm_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
struct msm_voice *voice;
if (!strncmp("VoLTE", substream->pcm->id, 5)) {
@@ -197,7 +275,7 @@ static int msm_pcm_open(struct snd_pcm_substream *substream)
runtime->private_data = voice;
mutex_unlock(&voice->lock);
-
+ msm_adsp_init_mixer_ctl_pp_event_queue(soc_prtd);
return 0;
}
static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
@@ -231,6 +309,7 @@ static int msm_pcm_close(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
struct msm_voice *prtd = runtime->private_data;
uint32_t session_id = 0;
int ret = 0;
@@ -250,6 +329,7 @@ static int msm_pcm_close(struct snd_pcm_substream *substream)
voc_end_voice_call(session_id);
}
mutex_unlock(&prtd->lock);
+ msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd);
return ret;
}
@@ -583,6 +663,41 @@ static int msm_voice_hd_voice_put(struct snd_kcontrol *kcontrol,
return ret;
}
+static int msm_dtmf_detect_rx_vsid_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint32_t session_id = ucontrol->value.integer.value[0];
+ uint32_t enable = ucontrol->value.integer.value[1];
+
+ if (enable)
+ enable = 1;
+
+ pr_debug("%s: sess_id=%d enable=%d\n", __func__, session_id, enable);
+
+ return voc_enable_dtmf_rx_detection(session_id, enable);
+}
+
+static int msm_dtmf_detect_rx_vsid_cb_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_voice *dtmf_voice_info = NULL;
+ uint32_t session_id = ucontrol->value.integer.value[0];
+ uint32_t enable = ucontrol->value.integer.value[1];
+
+ if (enable)
+ enable = 1;
+
+ pr_debug("%s: enable dtmf detect cb =%d for session_id=%d\n",
+ __func__, enable, session_id);
+
+ dtmf_voice_info = &voice_info[get_idx_for_session(session_id)];
+ voc_register_dtmf_rx_detection_cb
+ ((dtmf_rx_det_cb_fn) dtmf_rx_detected_evt_hdlr,
+ (void *) dtmf_voice_info->capture_substream->private_data);
+
+ return 0;
+}
+
static int msm_voice_topology_disable_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -636,6 +751,7 @@ static int msm_voice_cvd_version_get(struct snd_kcontrol *kcontrol,
return 0;
}
+
static struct snd_kcontrol_new msm_voice_controls[] = {
SOC_SINGLE_MULTI_EXT("Voice Rx Device Mute", SND_SOC_NOPM, 0, VSID_MAX,
0, 3, NULL, msm_voice_rx_device_mute_put),
@@ -654,6 +770,12 @@ static struct snd_kcontrol_new msm_voice_controls[] = {
msm_voice_topology_disable_put),
SOC_SINGLE_MULTI_EXT("HD Voice Enable", SND_SOC_NOPM, 0, VSID_MAX, 0, 2,
NULL, msm_voice_hd_voice_put),
+ SOC_SINGLE_MULTI_EXT("DTMF_Detect Rx VSID enable",
+ SND_SOC_NOPM, 0, VSID_MAX, 0, 2,
+ NULL, msm_dtmf_detect_rx_vsid_put),
+ SOC_SINGLE_MULTI_EXT("DTMF_Detect Rx Callback VSID enable",
+ SND_SOC_NOPM, 0, VSID_MAX, 0, 2,
+ NULL, msm_dtmf_detect_rx_vsid_cb_put),
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -666,6 +788,118 @@ static struct snd_kcontrol_new msm_voice_controls[] = {
};
+static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int msm_pcm_add_voice_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_voice_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__);
+ return -EINVAL;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+ if (!mixer_str)
+ return -ENOMEM;
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_voice_adsp_stream_cmd_config_control[0].name = mixer_str;
+ fe_voice_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_voice_adsp_stream_cmd_config_control,
+ ARRAY_SIZE(fe_voice_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);
+ return ret;
+}
+
+static int msm_pcm_add_voice_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_voice_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,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ return -EINVAL;
+ }
+
+ 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)
+ return -ENOMEM;
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
+ fe_voice_adsp_callback_config_control[0].name = mixer_str;
+ fe_voice_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_voice_adsp_callback_config_control,
+ ARRAY_SIZE(fe_voice_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);
+ return ret;
+}
+
static struct snd_pcm_ops msm_pcm_ops = {
.open = msm_pcm_open,
.hw_params = msm_pcm_hw_params,
@@ -684,6 +918,17 @@ static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ ret = msm_pcm_add_voice_adsp_stream_cmd_control(rtd);
+ if (ret)
+ pr_err("%s: Could not add pcm ADSP Stream Cmd Control\n",
+ __func__);
+
+ ret = msm_pcm_add_voice_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-qti-pp-config.c b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
index e890e6a71fb3..29a5fd18081a 100644
--- a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
+++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
@@ -172,6 +172,45 @@ static int msm_qti_pp_put_eq_band_count_audio_mixer(
return 0;
}
+static int msm_qti_pp_put_dtmf_module_enable
+ (struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 fe_id = 0;
+ struct msm_pcm_routing_fdai_data fe_dai;
+ struct audio_client *ac = NULL;
+ struct param_hdr_v3 param_hdr;
+ int ret = 0;
+ u32 flag = ucontrol->value.integer.value[0];
+
+ fe_id = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return -EINVAL;
+ }
+
+ msm_pcm_routing_get_fedai_info(fe_id, SESSION_TYPE_RX, &fe_dai);
+ ac = q6asm_get_audio_client(fe_dai.strm_id);
+
+ if (ac == NULL) {
+ pr_err("%s ac is null.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ param_hdr.module_id = AUDPROC_MODULE_ID_DTMF_DETECTION;
+ param_hdr.instance_id = INSTANCE_ID_0;
+ param_hdr.param_id = AUDPROC_PARAM_ID_ENABLE;
+ param_hdr.param_size = 4;
+
+ ret = q6asm_pack_and_set_pp_param_in_band(ac,
+ param_hdr, (u8 *)&flag);
+
+done:
+ return ret;
+}
+
static int msm_qti_pp_get_eq_band_audio_mixer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1364,6 +1403,18 @@ static const struct snd_kcontrol_new asphere_mixer_controls[] = {
0xFFFFFFFF, 0, 2, msm_qti_pp_asphere_get, msm_qti_pp_asphere_set),
};
+static const struct snd_kcontrol_new dtmf_detect_enable_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1 DTMF Detect Enable", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, NULL,
+ msm_qti_pp_put_dtmf_module_enable),
+ SOC_SINGLE_EXT("MultiMedia6 DTMF Detect Enable", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, NULL,
+ msm_qti_pp_put_dtmf_module_enable),
+ SOC_SINGLE_EXT("MultiMedia21 DTMF Detect Enable", SND_SOC_NOPM,
+ MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, NULL,
+ msm_qti_pp_put_dtmf_module_enable),
+};
+
#ifdef CONFIG_QTI_PP
void msm_qti_pp_add_controls(struct snd_soc_platform *platform)
{
@@ -1420,5 +1471,9 @@ void msm_qti_pp_add_controls(struct snd_soc_platform *platform)
snd_soc_add_platform_controls(platform, msm_multichannel_ec_controls,
ARRAY_SIZE(msm_multichannel_ec_controls));
+
+ snd_soc_add_platform_controls(platform,
+ dtmf_detect_enable_mixer_controls,
+ ARRAY_SIZE(dtmf_detect_enable_mixer_controls));
}
#endif /* CONFIG_QTI_PP */
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 24d4199ac30b..c6f3b5ba96ba 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -67,6 +67,7 @@ struct adm_copp {
atomic_t adm_delay_stat[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
uint32_t adm_delay[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
unsigned long adm_status[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+ atomic_t token[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
};
struct source_tracking_data {
@@ -102,6 +103,7 @@ struct adm_ctl {
int ec_ref_rx_sampling_rate;
int native_mode;
+ u32 copp_token;
};
static struct adm_ctl this_adm;
@@ -222,14 +224,45 @@ static int adm_get_copp_id(int port_idx, int copp_idx)
return atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
}
+static int adm_get_idx_if_single_copp_exists(int port_idx,
+ int topology, int mode,
+ int rate, int bit_width,
+ u32 copp_token)
+{
+ int idx;
+
+ pr_debug("%s: copp_token %d\n", __func__, copp_token);
+
+ for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++)
+ if ((topology ==
+ atomic_read(&this_adm.copp.topology[port_idx][idx])) &&
+ (mode ==
+ atomic_read(&this_adm.copp.mode[port_idx][idx])) &&
+ (rate ==
+ atomic_read(&this_adm.copp.rate[port_idx][idx])) &&
+ (bit_width ==
+ atomic_read(&this_adm.copp.bit_width[port_idx][idx])) &&
+ (copp_token ==
+ atomic_read(&this_adm.copp.token[port_idx][idx])))
+ return idx;
+ return -EINVAL;
+}
+
static int adm_get_idx_if_copp_exists(int port_idx, int topology, int mode,
- int rate, int bit_width, int app_type)
+ int rate, int bit_width, int app_type,
+ u32 copp_token)
{
int idx;
pr_debug("%s: port_idx-%d, topology-0x%x, mode-%d, rate-%d, bit_width-%d\n",
__func__, port_idx, topology, mode, rate, bit_width);
+ if (copp_token)
+ return adm_get_idx_if_single_copp_exists(port_idx,
+ topology, mode,
+ rate, bit_width,
+ copp_token);
+
for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++)
if ((topology ==
atomic_read(&this_adm.copp.topology[port_idx][idx])) &&
@@ -949,7 +982,7 @@ int adm_get_pp_params(int port_id, int copp_idx, uint32_t client_id,
copp_stat = &this_adm.copp.stat[port_idx][copp_idx];
atomic_set(copp_stat, -1);
ret = apr_send_pkt(this_adm.apr, (uint32_t *) &adm_get_params);
- if (ret) {
+ if (ret < 0) {
pr_err("%s: Get params APR send failed port = 0x%x ret %d\n",
__func__, port_id, ret);
ret = -EINVAL;
@@ -1064,7 +1097,7 @@ int adm_get_pp_topo_module_list_v2(int port_id, int copp_idx,
copp_stat = &this_adm.copp.stat[port_idx][copp_idx];
atomic_set(copp_stat, -1);
ret = apr_send_pkt(this_adm.apr, (uint32_t *) &adm_get_module_list);
- if (ret) {
+ if (ret < 0) {
pr_err("%s: APR send pkt failed for port_id: 0x%x failed ret %d\n",
__func__, port_id, ret);
ret = -EINVAL;
@@ -2804,7 +2837,8 @@ static int adm_open_v5_v6(int tmp_port, int port_idx, int copp_idx,
}
int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
- int perf_mode, uint16_t bit_width, int app_type, int acdb_id)
+ int perf_mode, uint16_t bit_width, int app_type, int acdb_id,
+ u32 copp_token)
{
int ret = 0;
int port_idx, flags;
@@ -2871,7 +2905,7 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
copp_idx = adm_get_idx_if_copp_exists(port_idx, topology,
perf_mode,
rate, bit_width,
- app_type);
+ app_type, copp_token);
if (copp_idx < 0) {
copp_idx = adm_get_next_available_copp(port_idx);
@@ -2895,6 +2929,8 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
app_type);
atomic_set(&this_adm.copp.acdb_id[port_idx][copp_idx],
acdb_id);
+ atomic_set(&this_adm.copp.token[port_idx][copp_idx],
+ copp_token);
set_bit(ADM_STATUS_CALIBRATION_REQUIRED,
(void *)&this_adm.copp.adm_status[port_idx][copp_idx]);
if ((path != ADM_PATH_COMPRESSED_RX) &&
@@ -3432,6 +3468,7 @@ int adm_close(int port_id, int perf_mode, int copp_idx)
atomic_set(&this_adm.copp.channels[port_idx][copp_idx], 0);
atomic_set(&this_adm.copp.bit_width[port_idx][copp_idx], 0);
atomic_set(&this_adm.copp.app_type[port_idx][copp_idx], 0);
+ atomic_set(&this_adm.copp.token[port_idx][copp_idx], 0);
clear_bit(ADM_STATUS_CALIBRATION_REQUIRED,
(void *)&this_adm.copp.adm_status[port_idx][copp_idx]);
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index 38dc3639a682..0524ca21feba 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -2374,7 +2374,8 @@ int afe_port_set_mad_type(u16 port_id, enum afe_mad_type mad_type)
int i;
if (port_id == AFE_PORT_ID_TERTIARY_MI2S_TX ||
- port_id == AFE_PORT_ID_INT3_MI2S_TX) {
+ port_id == AFE_PORT_ID_INT3_MI2S_TX ||
+ port_id == AFE_PORT_ID_QUATERNARY_TDM_TX) {
mad_type = MAD_SW_AUDIO;
return 0;
}
@@ -2393,7 +2394,8 @@ enum afe_mad_type afe_port_get_mad_type(u16 port_id)
int i;
if (port_id == AFE_PORT_ID_TERTIARY_MI2S_TX ||
- port_id == AFE_PORT_ID_INT3_MI2S_TX)
+ port_id == AFE_PORT_ID_INT3_MI2S_TX ||
+ port_id == AFE_PORT_ID_QUATERNARY_TDM_TX)
return MAD_SW_AUDIO;
i = port_id - SLIMBUS_0_RX;
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 567534e1599d..0ad15e90bfc6 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -98,6 +98,7 @@ static struct asm_mmap this_mmap;
struct audio_session {
struct audio_client *ac;
spinlock_t session_lock;
+ bool ignore;
};
/* session id: 0 reserved */
static struct audio_session session[ASM_ACTIVE_STREAMS_ALLOWED + 1];
@@ -597,6 +598,7 @@ int q6asm_mmap_apr_dereg(void)
static int q6asm_session_alloc(struct audio_client *ac)
{
int n;
+
for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) {
if (!(session[n].ac)) {
session[n].ac = ac;
@@ -607,6 +609,87 @@ static int q6asm_session_alloc(struct audio_client *ac)
return -ENOMEM;
}
+static void q6asm_session_set_ignore(int id)
+{
+ session[id].ignore = true;
+}
+
+static void q6asm_session_clean_ignore(void)
+{
+ int i;
+
+ for (i = 1; i <= ASM_ACTIVE_STREAMS_ALLOWED; i++)
+ session[i].ignore = false;
+}
+
+static void q6asm_session_deregister(struct audio_client *ac)
+{
+ rtac_set_asm_handle(ac->session, NULL);
+ apr_deregister(ac->apr2);
+ apr_deregister(ac->apr);
+ q6asm_mmap_apr_dereg();
+ ac->apr2 = NULL;
+ ac->apr = NULL;
+ ac->mmap_apr = NULL;
+}
+
+static int q6asm_session_register(struct audio_client *ac)
+{
+ ac->apr = apr_register("ADSP", "ASM",
+ (apr_fn)q6asm_callback,
+ ((ac->session) << 8 | 0x0001),
+ ac);
+ if (ac->apr == NULL) {
+ pr_err("%s: Registration with APR failed\n", __func__);
+ goto fail_apr1;
+ }
+
+ ac->apr2 = apr_register("ADSP", "ASM",
+ (apr_fn)q6asm_callback,
+ ((ac->session) << 8 | 0x0002),
+ ac);
+ if (ac->apr2 == NULL) {
+ pr_err("%s: Registration with APR-2 failed\n", __func__);
+ goto fail_apr2;
+ }
+
+ rtac_set_asm_handle(ac->session, ac->apr);
+
+ pr_debug("%s: Registering the common port with APR\n", __func__);
+ ac->mmap_apr = q6asm_mmap_apr_reg();
+ if (ac->mmap_apr == NULL)
+ goto fail_mmap;
+
+ return 0;
+
+fail_mmap:
+ apr_deregister(ac->apr2);
+fail_apr2:
+ apr_deregister(ac->apr);
+fail_apr1:
+ return -EINVAL;
+}
+
+static int q6asm_session_try_next(struct audio_client *ac)
+{
+ int n;
+ int s = ac->session;
+
+ for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) {
+ if (++s > ASM_ACTIVE_STREAMS_ALLOWED)
+ s -= ASM_ACTIVE_STREAMS_ALLOWED;
+ if (!session[s].ignore && !session[s].ac) {
+ q6asm_session_deregister(ac);
+ session[ac->session].ac = NULL;
+ session[s].ac = ac;
+ ac->session = s;
+ return q6asm_session_register(ac);
+ }
+ }
+ pr_debug("%s: session not available\n", __func__);
+ return -EINVAL;
+}
+
static int q6asm_get_session_id_from_audio_client(struct audio_client *ac)
{
int n;
@@ -1003,7 +1086,7 @@ int q6asm_unmap_rtac_block(uint32_t *mem_map_handle)
__func__, result2);
result = result2;
} else {
- mem_map_handle = 0;
+ *mem_map_handle = 0;
}
result2 = q6asm_mmap_apr_dereg();
@@ -1139,13 +1222,7 @@ void q6asm_audio_client_free(struct audio_client *ac)
}
}
- rtac_set_asm_handle(ac->session, NULL);
- apr_deregister(ac->apr2);
- apr_deregister(ac->apr);
- q6asm_mmap_apr_dereg();
- ac->apr2 = NULL;
- ac->apr = NULL;
- ac->mmap_apr = NULL;
+ q6asm_session_deregister(ac);
q6asm_session_free(ac);
pr_debug("%s: APR De-Register\n", __func__);
@@ -1307,34 +1384,11 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv)
ac->fptr_cache_ops = NULL;
/* DSP expects stream id from 1 */
ac->stream_id = 1;
- ac->apr = apr_register("ADSP", "ASM", \
- (apr_fn)q6asm_callback,\
- ((ac->session) << 8 | 0x0001),\
- ac);
-
- if (ac->apr == NULL) {
- pr_err("%s: Registration with APR failed\n", __func__);
- mutex_unlock(&session_lock);
- goto fail_apr1;
- }
- ac->apr2 = apr_register("ADSP", "ASM",
- (apr_fn)q6asm_callback,
- ((ac->session) << 8 | 0x0002),
- ac);
- if (ac->apr2 == NULL) {
- pr_err("%s: Registration with APR-2 failed\n", __func__);
- mutex_unlock(&session_lock);
- goto fail_apr2;
- }
-
- rtac_set_asm_handle(n, ac->apr);
-
- pr_debug("%s: Registering the common port with APR\n", __func__);
- ac->mmap_apr = q6asm_mmap_apr_reg();
- if (ac->mmap_apr == NULL) {
+ rc = q6asm_session_register(ac);
+ if (rc < 0) {
mutex_unlock(&session_lock);
- goto fail_mmap;
+ goto fail_register;
}
init_waitqueue_head(&ac->cmd_wait);
@@ -1357,7 +1411,7 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv)
rc = send_asm_custom_topology(ac);
if (rc < 0) {
mutex_unlock(&session_lock);
- goto fail_mmap;
+ goto fail_send;
}
pr_debug("%s: session[%d]\n", __func__, ac->session);
@@ -1365,11 +1419,9 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv)
mutex_unlock(&session_lock);
return ac;
-fail_mmap:
- apr_deregister(ac->apr2);
-fail_apr2:
- apr_deregister(ac->apr);
-fail_apr1:
+fail_send:
+ q6asm_session_deregister(ac);
+fail_register:
q6asm_session_free(ac);
fail_session:
return NULL;
@@ -3241,6 +3293,49 @@ int q6asm_open_write_v5(struct audio_client *ac, uint32_t format,
}
EXPORT_SYMBOL(q6asm_open_write_v5);
+static int q6asm_open_write_version_adaptor(struct audio_client *ac,
+ uint32_t format, uint16_t bits_per_sample)
+{
+ if (q6asm_get_svc_version(APR_SVC_ASM) >= ADSP_ASM_API_VERSION_V2)
+ return q6asm_open_write_v5(ac, format, bits_per_sample);
+ else
+ return q6asm_open_write_v4(ac, format, bits_per_sample);
+}
+
+/*
+ * q6asm_open_write_with_retry - Opens audio playback session, with retrying
+ * in case of session ID conflict
+ *
+ * @ac: Client session handle
+ * @format: decoder format
+ * @bits_per_sample: bit width of playback session
+ */
+int q6asm_open_write_with_retry(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample)
+{
+ int i, rc;
+
+ mutex_lock(&session_lock);
+ for (i = 0; i < ASM_ACTIVE_STREAMS_ALLOWED; i++) {
+ rc = q6asm_open_write_version_adaptor(ac, format,
+ bits_per_sample);
+ if (rc != -EALREADY)
+ break;
+
+ pr_debug("%s: session %d is occupied, try next\n",
+ __func__, ac->session);
+ q6asm_session_set_ignore(ac->session);
+ rc = q6asm_session_try_next(ac);
+ if (rc < 0)
+ break;
+ }
+ q6asm_session_clean_ignore();
+ mutex_unlock(&session_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(q6asm_open_write_with_retry);
+
int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format,
uint16_t bits_per_sample, int32_t stream_id,
bool is_gapless_mode)
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
index 17d78704709f..69189140c936 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.c
+++ b/sound/soc/msm/qdsp6v2/q6voice.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -6861,8 +6861,8 @@ static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv)
pr_debug("Recd VSS_ISTREAM_EVT_NOT_READY\n");
} else if (data->opcode == VSS_ISTREAM_EVT_READY) {
pr_debug("Recd VSS_ISTREAM_EVT_READY\n");
- } else if (data->opcode == VSS_ICOMMON_RSP_GET_PARAM ||
- VSS_ICOMMON_RSP_GET_PARAM_V3) {
+ } else if ((data->opcode == VSS_ICOMMON_RSP_GET_PARAM) ||
+ (data->opcode == VSS_ICOMMON_RSP_GET_PARAM_V3)) {
pr_debug("%s: VSS_ICOMMON_RSP_GET_PARAM\n", __func__);
ptr = data->payload;
if (ptr[0] != 0) {
@@ -6871,7 +6871,7 @@ static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv)
}
rtac_make_voice_callback(RTAC_CVS, data->payload,
data->payload_size);
- } else if (data->opcode == VSS_ISTREAM_EVT_RX_DTMF_DETECTED) {
+ } else if (data->opcode == VSS_ISTREAM_EVT_RX_DTMF_DETECTED) {
struct vss_istream_evt_rx_dtmf_detected *dtmf_rx_detected;
uint32_t *voc_pkt = data->payload;
uint32_t pkt_len = data->payload_size;
diff --git a/sound/soc/msm/qdsp6v2/rtac.c b/sound/soc/msm/qdsp6v2/rtac.c
index 5e33fb508455..82d954c646dc 100644
--- a/sound/soc/msm/qdsp6v2/rtac.c
+++ b/sound/soc/msm/qdsp6v2/rtac.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1349,7 +1349,7 @@ static int send_rtac_afe_apr(void __user *buf, uint32_t opcode)
if (copy_from_user(rtac_cal[AFE_RTAC_CAL].cal_data.kvaddr,
(void __user *) buf +
offsetof(struct rtac_afe_user_data,
- v3_get.param_hdr),
+ v3_set.param_hdr),
payload_size)) {
pr_err("%s: Could not copy payload from user buffer\n",
__func__);
diff --git a/sound/soc/msm/sdm660-internal.c b/sound/soc/msm/sdm660-internal.c
index 259763449456..4e2a40319b02 100644
--- a/sound/soc/msm/sdm660-internal.c
+++ b/sound/soc/msm/sdm660-internal.c
@@ -1379,6 +1379,8 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dapm_ignore_suspend(dapm, "Secondary Mic");
snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1");
snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4");
snd_soc_dapm_ignore_suspend(dapm, "EAR");
snd_soc_dapm_ignore_suspend(dapm, "HEADPHONE");
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index fd6e247d9fd8..91bad6731c9d 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -640,8 +640,12 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
tmp |= mod_slave;
break;
case SND_SOC_DAIFMT_CBS_CFS:
- /* Set default source clock in Master mode */
- if (i2s->rclk_srcrate == 0)
+ /*
+ * Set default source clock in Master mode, only when the
+ * CLK_I2S_RCLK_SRC clock is not exposed so we ensure any
+ * clock configuration assigned in DT is not overwritten.
+ */
+ if (i2s->rclk_srcrate == 0 && i2s->clk_data.clks == NULL)
i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0,
0, SND_SOC_CLOCK_IN);
break;
@@ -856,6 +860,11 @@ static int config_setup(struct i2s_dai *i2s)
return 0;
if (!(i2s->quirks & QUIRK_NO_MUXPSR)) {
+ struct clk *rclksrc = i2s->clk_table[CLK_I2S_RCLK_SRC];
+
+ if (i2s->rclk_srcrate == 0 && rclksrc && !IS_ERR(rclksrc))
+ i2s->rclk_srcrate = clk_get_rate(rclksrc);
+
psr = i2s->rclk_srcrate / i2s->frmclk / rfs;
writel(((psr - 1) << 8) | PSR_PSREN, i2s->addr + I2SPSR);
dev_dbg(&i2s->pdev->dev,
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index e3f34a86413c..c1e76feb3529 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -1188,6 +1188,9 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
kfree(sm);
continue;
}
+
+ /* create any TLV data */
+ soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr);
}
return kc;
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index e176bad19bcb..ca080a129b33 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -487,6 +487,24 @@ bpf_object__init_maps(struct bpf_object *obj, void *data,
return 0;
}
+static bool section_have_execinstr(struct bpf_object *obj, int idx)
+{
+ Elf_Scn *scn;
+ GElf_Shdr sh;
+
+ scn = elf_getscn(obj->efile.elf, idx);
+ if (!scn)
+ return false;
+
+ if (gelf_getshdr(scn, &sh) != &sh)
+ return false;
+
+ if (sh.sh_flags & SHF_EXECINSTR)
+ return true;
+
+ return false;
+}
+
static int bpf_object__elf_collect(struct bpf_object *obj)
{
Elf *elf = obj->efile.elf;
@@ -567,6 +585,14 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
} else if (sh.sh_type == SHT_REL) {
void *reloc = obj->efile.reloc;
int nr_reloc = obj->efile.nr_reloc + 1;
+ int sec = sh.sh_info; /* points to other section */
+
+ /* Only do relo for section with exec instructions */
+ if (!section_have_execinstr(obj, sec)) {
+ pr_debug("skip relo %s(%d) for section(%d)\n",
+ name, idx, sec);
+ continue;
+ }
reloc = realloc(reloc,
sizeof(*obj->efile.reloc) * nr_reloc);
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c
index 68276f35e323..6e4a10fe9dd0 100644
--- a/tools/lib/traceevent/event-parse.c
+++ b/tools/lib/traceevent/event-parse.c
@@ -4905,21 +4905,22 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
else
ls = 2;
- if (*(ptr+1) == 'F' || *(ptr+1) == 'f' ||
- *(ptr+1) == 'S' || *(ptr+1) == 's') {
+ if (isalnum(ptr[1]))
ptr++;
+
+ if (*ptr == 'F' || *ptr == 'f' ||
+ *ptr == 'S' || *ptr == 's') {
show_func = *ptr;
- } else if (*(ptr+1) == 'M' || *(ptr+1) == 'm') {
- print_mac_arg(s, *(ptr+1), data, size, event, arg);
- ptr++;
+ } else if (*ptr == 'M' || *ptr == 'm') {
+ print_mac_arg(s, *ptr, data, size, event, arg);
arg = arg->next;
break;
- } else if (*(ptr+1) == 'I' || *(ptr+1) == 'i') {
+ } else if (*ptr == 'I' || *ptr == 'i') {
int n;
- n = print_ip_arg(s, ptr+1, data, size, event, arg);
+ n = print_ip_arg(s, ptr, data, size, event, arg);
if (n > 0) {
- ptr += n;
+ ptr += n - 1;
arg = arg->next;
break;
}
diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c
index 88cccea3ca99..64309d73921b 100644
--- a/tools/lib/traceevent/parse-filter.c
+++ b/tools/lib/traceevent/parse-filter.c
@@ -1867,17 +1867,25 @@ static const char *get_field_str(struct filter_arg *arg, struct pevent_record *r
struct pevent *pevent;
unsigned long long addr;
const char *val = NULL;
+ unsigned int size;
char hex[64];
/* If the field is not a string convert it */
if (arg->str.field->flags & FIELD_IS_STRING) {
val = record->data + arg->str.field->offset;
+ size = arg->str.field->size;
+
+ if (arg->str.field->flags & FIELD_IS_DYNAMIC) {
+ addr = *(unsigned int *)val;
+ val = record->data + (addr & 0xffff);
+ size = addr >> 16;
+ }
/*
* We need to copy the data since we can't be sure the field
* is null terminated.
*/
- if (*(val + arg->str.field->size - 1)) {
+ if (*(val + size - 1)) {
/* copy it */
memcpy(arg->str.buffer, val, arg->str.field->size);
/* the buffer is already NULL terminated */
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c
index d677e018e504..bf907c50fcae 100644
--- a/tools/perf/tests/vmlinux-kallsyms.c
+++ b/tools/perf/tests/vmlinux-kallsyms.c
@@ -126,7 +126,7 @@ int test__vmlinux_matches_kallsyms(void)
if (pair && UM(pair->start) == mem_start) {
next_pair:
- if (strcmp(sym->name, pair->name) == 0) {
+ if (arch__compare_symbol_names(sym->name, pair->name) == 0) {
/*
* kallsyms don't have the symbol end, so we
* set that by using the next symbol start - 1,
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 6e0a16c7176a..8a84f82845f3 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -625,13 +625,13 @@ static void apply_config_terms(struct perf_evsel *evsel,
struct perf_evsel_config_term *term;
struct list_head *config_terms = &evsel->config_terms;
struct perf_event_attr *attr = &evsel->attr;
- struct callchain_param param;
+ /* callgraph default */
+ struct callchain_param param = {
+ .record_mode = callchain_param.record_mode,
+ };
u32 dump_size = 0;
char *callgraph_buf = NULL;
- /* callgraph default */
- param.record_mode = callchain_param.record_mode;
-
list_for_each_entry(term, config_terms, list) {
switch (term->type) {
case PERF_EVSEL__CONFIG_TERM_PERIOD:
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 4fd37d6708cb..f6720afa9f34 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -720,7 +720,7 @@ iter_prepare_cumulative_entry(struct hist_entry_iter *iter,
* cumulated only one time to prevent entries more than 100%
* overhead.
*/
- he_cache = malloc(sizeof(*he_cache) * (iter->max_stack + 1));
+ he_cache = malloc(sizeof(*he_cache) * (callchain_cursor.nr + 1));
if (he_cache == NULL)
return -ENOMEM;
@@ -881,8 +881,6 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
if (err)
return err;
- iter->max_stack = max_stack_depth;
-
err = iter->ops->prepare_entry(iter, al);
if (err)
goto out;
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index a48a2078d288..46b7591acd9c 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -91,7 +91,6 @@ struct hist_entry_iter {
int curr;
bool hide_unresolved;
- int max_stack;
struct perf_evsel *evsel;
struct perf_sample *sample;
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 24ebd3e3eb7d..5d2e479430d1 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -90,6 +90,7 @@ ifdef INSTALL_PATH
for TARGET in $(TARGETS); do \
echo "echo ; echo Running tests in $$TARGET" >> $(ALL_SCRIPT); \
echo "echo ========================================" >> $(ALL_SCRIPT); \
+ echo "[ -w /dev/kmsg ] && echo \"kselftest: Running tests in $$TARGET\" >> /dev/kmsg" >> $(ALL_SCRIPT); \
echo "cd $$TARGET" >> $(ALL_SCRIPT); \
make -s --no-print-directory -C $$TARGET emit_tests >> $(ALL_SCRIPT); \
echo "cd \$$ROOT" >> $(ALL_SCRIPT); \
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc
new file mode 100644
index 000000000000..5ba73035e1d9
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc
@@ -0,0 +1,46 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: Kprobe event string type argument
+
+[ -f kprobe_events ] || exit_unsupported # this is configurable
+
+echo 0 > events/enable
+echo > kprobe_events
+
+case `uname -m` in
+x86_64)
+ ARG2=%si
+ OFFS=8
+;;
+i[3456]86)
+ ARG2=%cx
+ OFFS=4
+;;
+aarch64)
+ ARG2=%x1
+ OFFS=8
+;;
+arm*)
+ ARG2=%r1
+ OFFS=4
+;;
+*)
+ echo "Please implement other architecture here"
+ exit_untested
+esac
+
+: "Test get argument (1)"
+echo "p:testprobe create_trace_kprobe arg1=+0(+0(${ARG2})):string" > kprobe_events
+echo 1 > events/kprobes/testprobe/enable
+! echo test >> kprobe_events
+tail -n 1 trace | grep -qe "testprobe.* arg1=\"test\""
+
+echo 0 > events/kprobes/testprobe/enable
+: "Test get argument (2)"
+echo "p:testprobe create_trace_kprobe arg1=+0(+0(${ARG2})):string arg2=+0(+${OFFS}(${ARG2})):string" > kprobe_events
+echo 1 > events/kprobes/testprobe/enable
+! echo test1 test2 >> kprobe_events
+tail -n 1 trace | grep -qe "testprobe.* arg1=\"test1\" arg2=\"test2\""
+
+echo 0 > events/enable
+echo > kprobe_events
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc
new file mode 100644
index 000000000000..231bcd2c4eb5
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc
@@ -0,0 +1,97 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: Kprobe event argument syntax
+
+[ -f kprobe_events ] || exit_unsupported # this is configurable
+
+grep "x8/16/32/64" README > /dev/null || exit_unsupported # version issue
+
+echo 0 > events/enable
+echo > kprobe_events
+
+PROBEFUNC="vfs_read"
+GOODREG=
+BADREG=
+GOODSYM="_sdata"
+if ! grep -qw ${GOODSYM} /proc/kallsyms ; then
+ GOODSYM=$PROBEFUNC
+fi
+BADSYM="deaqswdefr"
+SYMADDR=0x`grep -w ${GOODSYM} /proc/kallsyms | cut -f 1 -d " "`
+GOODTYPE="x16"
+BADTYPE="y16"
+
+case `uname -m` in
+x86_64|i[3456]86)
+ GOODREG=%ax
+ BADREG=%ex
+;;
+aarch64)
+ GOODREG=%x0
+ BADREG=%ax
+;;
+arm*)
+ GOODREG=%r0
+ BADREG=%ax
+;;
+esac
+
+test_goodarg() # Good-args
+{
+ while [ "$1" ]; do
+ echo "p ${PROBEFUNC} $1" > kprobe_events
+ shift 1
+ done;
+}
+
+test_badarg() # Bad-args
+{
+ while [ "$1" ]; do
+ ! echo "p ${PROBEFUNC} $1" > kprobe_events
+ shift 1
+ done;
+}
+
+echo > kprobe_events
+
+: "Register access"
+test_goodarg ${GOODREG}
+test_badarg ${BADREG}
+
+: "Symbol access"
+test_goodarg "@${GOODSYM}" "@${SYMADDR}" "@${GOODSYM}+10" "@${GOODSYM}-10"
+test_badarg "@" "@${BADSYM}" "@${GOODSYM}*10" "@${GOODSYM}/10" \
+ "@${GOODSYM}%10" "@${GOODSYM}&10" "@${GOODSYM}|10"
+
+: "Stack access"
+test_goodarg "\$stack" "\$stack0" "\$stack1"
+test_badarg "\$stackp" "\$stack0+10" "\$stack1-10"
+
+: "Retval access"
+echo "r ${PROBEFUNC} \$retval" > kprobe_events
+! echo "p ${PROBEFUNC} \$retval" > kprobe_events
+
+: "Comm access"
+test_goodarg "\$comm"
+
+: "Indirect memory access"
+test_goodarg "+0(${GOODREG})" "-0(${GOODREG})" "+10(\$stack)" \
+ "+0(\$stack1)" "+10(@${GOODSYM}-10)" "+0(+10(+20(\$stack)))"
+test_badarg "+(${GOODREG})" "(${GOODREG}+10)" "-(${GOODREG})" "(${GOODREG})" \
+ "+10(\$comm)" "+0(${GOODREG})+10"
+
+: "Name assignment"
+test_goodarg "varname=${GOODREG}"
+test_badarg "varname=varname2=${GOODREG}"
+
+: "Type syntax"
+test_goodarg "${GOODREG}:${GOODTYPE}"
+test_badarg "${GOODREG}::${GOODTYPE}" "${GOODREG}:${BADTYPE}" \
+ "${GOODTYPE}:${GOODREG}"
+
+: "Combination check"
+
+test_goodarg "\$comm:string" "+0(\$stack):string"
+test_badarg "\$comm:x64" "\$stack:string" "${GOODREG}:string"
+
+echo > kprobe_events
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/probepoint.tc b/tools/testing/selftests/ftrace/test.d/kprobe/probepoint.tc
new file mode 100644
index 000000000000..4fda01a08da4
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/probepoint.tc
@@ -0,0 +1,43 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: Kprobe events - probe points
+
+[ -f kprobe_events ] || exit_unsupported # this is configurable
+
+TARGET_FUNC=create_trace_kprobe
+
+dec_addr() { # hexaddr
+ printf "%d" "0x"`echo $1 | tail -c 8`
+}
+
+set_offs() { # prev target next
+ A1=`dec_addr $1`
+ A2=`dec_addr $2`
+ A3=`dec_addr $3`
+ TARGET="0x$2" # an address
+ PREV=`expr $A1 - $A2` # offset to previous symbol
+ NEXT=+`expr $A3 - $A2` # offset to next symbol
+ OVERFLOW=+`printf "0x%x" ${PREV}` # overflow offset to previous symbol
+}
+
+# We have to decode symbol addresses to get correct offsets.
+# If the offset is not an instruction boundary, it cause -EILSEQ.
+set_offs `grep -A1 -B1 ${TARGET_FUNC} /proc/kallsyms | cut -f 1 -d " " | xargs`
+
+UINT_TEST=no
+# printf "%x" -1 returns (unsigned long)-1.
+if [ `printf "%x" -1 | wc -c` != 9 ]; then
+ UINT_TEST=yes
+fi
+
+echo 0 > events/enable
+echo > kprobe_events
+echo "p:testprobe ${TARGET_FUNC}" > kprobe_events
+echo "p:testprobe ${TARGET}" > kprobe_events
+echo "p:testprobe ${TARGET_FUNC}${NEXT}" > kprobe_events
+! echo "p:testprobe ${TARGET_FUNC}${PREV}" > kprobe_events
+if [ "${UINT_TEST}" = yes ]; then
+! echo "p:testprobe ${TARGET_FUNC}${OVERFLOW}" > kprobe_events
+fi
+echo > kprobe_events
+clear_trace
diff --git a/tools/testing/selftests/memfd/config b/tools/testing/selftests/memfd/config
new file mode 100644
index 000000000000..835c7f4dadcd
--- /dev/null
+++ b/tools/testing/selftests/memfd/config
@@ -0,0 +1 @@
+CONFIG_FUSE_FS=m
diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c
index 412459369686..9b654a070e7d 100644
--- a/tools/testing/selftests/net/psock_fanout.c
+++ b/tools/testing/selftests/net/psock_fanout.c
@@ -97,6 +97,8 @@ static int sock_fanout_open(uint16_t typeflags, int num_packets)
static void sock_fanout_set_ebpf(int fd)
{
+ static char log_buf[65536];
+
const int len_off = __builtin_offsetof(struct __sk_buff, len);
struct bpf_insn prog[] = {
{ BPF_ALU64 | BPF_MOV | BPF_X, 6, 1, 0, 0 },
@@ -109,7 +111,6 @@ static void sock_fanout_set_ebpf(int fd)
{ BPF_ALU | BPF_MOV | BPF_K, 0, 0, 0, 0 },
{ BPF_JMP | BPF_EXIT, 0, 0, 0, 0 }
};
- char log_buf[512];
union bpf_attr attr;
int pfd;
diff --git a/tools/testing/selftests/powerpc/mm/subpage_prot.c b/tools/testing/selftests/powerpc/mm/subpage_prot.c
index 440180ff8089..ca29f5872817 100644
--- a/tools/testing/selftests/powerpc/mm/subpage_prot.c
+++ b/tools/testing/selftests/powerpc/mm/subpage_prot.c
@@ -135,6 +135,16 @@ static int run_test(void *addr, unsigned long size)
return 0;
}
+static int syscall_available(void)
+{
+ int rc;
+
+ errno = 0;
+ rc = syscall(__NR_subpage_prot, 0, 0, 0);
+
+ return rc == 0 || (errno != ENOENT && errno != ENOSYS);
+}
+
int test_anon(void)
{
unsigned long align;
@@ -145,6 +155,8 @@ int test_anon(void)
void *mallocblock;
unsigned long mallocsize;
+ SKIP_IF(!syscall_available());
+
if (getpagesize() != 0x10000) {
fprintf(stderr, "Kernel page size must be 64K!\n");
return 1;
@@ -180,6 +192,8 @@ int test_file(void)
off_t filesize;
int fd;
+ SKIP_IF(!syscall_available());
+
fd = open(file_name, O_RDWR);
if (fd == -1) {
perror("failed to open file");
diff --git a/tools/thermal/tmon/sysfs.c b/tools/thermal/tmon/sysfs.c
index 1c12536f2081..18f523557983 100644
--- a/tools/thermal/tmon/sysfs.c
+++ b/tools/thermal/tmon/sysfs.c
@@ -486,6 +486,7 @@ int zone_instance_to_index(int zone_inst)
int update_thermal_data()
{
int i;
+ int next_thermal_record = cur_thermal_record + 1;
char tz_name[256];
static unsigned long samples;
@@ -495,9 +496,9 @@ int update_thermal_data()
}
/* circular buffer for keeping historic data */
- if (cur_thermal_record >= NR_THERMAL_RECORDS)
- cur_thermal_record = 0;
- gettimeofday(&trec[cur_thermal_record].tv, NULL);
+ if (next_thermal_record >= NR_THERMAL_RECORDS)
+ next_thermal_record = 0;
+ gettimeofday(&trec[next_thermal_record].tv, NULL);
if (tmon_log) {
fprintf(tmon_log, "%lu ", ++samples);
fprintf(tmon_log, "%3.1f ", p_param.t_target);
@@ -507,11 +508,12 @@ int update_thermal_data()
snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, TZONE,
ptdata.tzi[i].instance);
sysfs_get_ulong(tz_name, "temp",
- &trec[cur_thermal_record].temp[i]);
+ &trec[next_thermal_record].temp[i]);
if (tmon_log)
fprintf(tmon_log, "%lu ",
- trec[cur_thermal_record].temp[i]/1000);
+ trec[next_thermal_record].temp[i] / 1000);
}
+ cur_thermal_record = next_thermal_record;
for (i = 0; i < ptdata.nr_cooling_dev; i++) {
char cdev_name[256];
unsigned long val;
diff --git a/tools/thermal/tmon/tmon.c b/tools/thermal/tmon/tmon.c
index 9aa19652e8e8..b43138f8b862 100644
--- a/tools/thermal/tmon/tmon.c
+++ b/tools/thermal/tmon/tmon.c
@@ -336,7 +336,6 @@ int main(int argc, char **argv)
show_data_w();
show_cooling_device();
}
- cur_thermal_record++;
time_elapsed += ticktime;
controller_handler(trec[0].temp[target_tz_index] / 1000,
&yk);