summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/acpi_processor.c52
-rw-r--r--drivers/acpi/acpica/dsmethod.c3
-rw-r--r--drivers/acpi/bus.c3
-rw-r--r--drivers/acpi/internal.h6
-rw-r--r--drivers/acpi/osl.c16
-rw-r--r--drivers/android/binder.c33
-rw-r--r--drivers/ata/ahci_platform.c3
-rw-r--r--drivers/ata/ahci_xgene.c4
-rw-r--r--drivers/ata/libahci.c1
-rw-r--r--drivers/ata/libata-eh.c2
-rw-r--r--drivers/base/core.c39
-rw-r--r--drivers/base/dd.c3
-rw-r--r--drivers/base/module.c8
-rw-r--r--drivers/base/power/domain.c2
-rw-r--r--drivers/base/power/main.c85
-rw-r--r--drivers/base/power/power.h3
-rw-r--r--drivers/base/power/runtime.c9
-rw-r--r--drivers/block/loop.c6
-rw-r--r--drivers/block/nbd.c4
-rw-r--r--drivers/block/paride/pd.c4
-rw-r--r--drivers/block/paride/pt.c4
-rw-r--r--drivers/bluetooth/bluetooth-power.c19
-rw-r--r--drivers/bluetooth/hci_vhci.c28
-rw-r--r--drivers/bus/imx-weim.c2
-rw-r--r--drivers/char/Kconfig4
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/adsprpc.c4
-rw-r--r--drivers/char/dcc_tty.c326
-rw-r--r--drivers/char/diag/diagchar.h1
-rw-r--r--drivers/char/diag/diagchar_core.c1
-rw-r--r--drivers/char/diag/diagfwd_peripheral.c3
-rw-r--r--drivers/char/diag/diagfwd_socket.c8
-rw-r--r--drivers/char/hw_random/exynos-rng.c10
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c8
-rw-r--r--drivers/clk/at91/clk-h32mx.c2
-rw-r--r--drivers/clk/bcm/clk-bcm2835.c23
-rw-r--r--drivers/clk/clk-composite.c2
-rw-r--r--drivers/clk/clk-divider.c13
-rw-r--r--drivers/clk/clk-fixed-factor.c2
-rw-r--r--drivers/clk/clk-fixed-rate.c2
-rw-r--r--drivers/clk/clk-fractional-divider.c2
-rw-r--r--drivers/clk/clk-gate.c2
-rw-r--r--drivers/clk/clk-mux.c2
-rw-r--r--drivers/clk/clk-pwm.c2
-rw-r--r--drivers/clk/clk.c268
-rw-r--r--drivers/clk/imx/clk-imx35.c4
-rw-r--r--drivers/clk/meson/clkc.c2
-rw-r--r--drivers/clk/msm/clock-debug.c8
-rw-r--r--drivers/clk/msm/clock-gcc-cobalt.c25
-rw-r--r--drivers/clk/msm/clock-gpu-cobalt.c3
-rw-r--r--drivers/clk/msm/clock-osm.c557
-rw-r--r--drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c4
-rw-r--r--drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c18
-rw-r--r--drivers/clk/nxp/clk-lpc18xx-ccu.c2
-rw-r--r--drivers/clk/qcom/clk-alpha-pll.c247
-rw-r--r--drivers/clk/qcom/clk-alpha-pll.h23
-rw-r--r--drivers/clk/qcom/clk-pll.h9
-rw-r--r--drivers/clk/qcom/clk-smd-rpm.c4
-rw-r--r--drivers/clk/qcom/gcc-msm8916.c2
-rw-r--r--drivers/clk/qcom/gcc-msm8960.c4
-rw-r--r--drivers/clk/qcom/gcc-msmfalcon.c15
-rw-r--r--drivers/clk/qcom/gpucc-msmfalcon.c14
-rw-r--r--drivers/clk/qcom/vdd-level-falcon.h42
-rw-r--r--drivers/clk/rockchip/clk.c13
-rw-r--r--drivers/clk/versatile/clk-sp810.c4
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c22
-rw-r--r--drivers/cpufreq/intel_pstate.c5
-rw-r--r--drivers/cpufreq/qcom-cpufreq.c32
-rw-r--r--drivers/cpuidle/cpuidle-arm.c2
-rw-r--r--drivers/cpuidle/cpuidle.c4
-rw-r--r--drivers/cpuidle/lpm-levels.c4
-rw-r--r--drivers/crypto/Kconfig10
-rw-r--r--drivers/crypto/caam/jr.c2
-rw-r--r--drivers/crypto/ccp/ccp-crypto-aes-cmac.c3
-rw-r--r--drivers/crypto/ccp/ccp-crypto-aes-xts.c17
-rw-r--r--drivers/crypto/ccp/ccp-crypto-sha.c3
-rw-r--r--drivers/crypto/msm/qce.c6
-rw-r--r--drivers/crypto/msm/qce50.c21
-rw-r--r--drivers/crypto/msm/qcedev.c68
-rw-r--r--drivers/crypto/qat/qat_common/Makefile1
-rw-r--r--drivers/crypto/qat/qat_common/adf_common_drv.h11
-rw-r--r--drivers/crypto/qat/qat_common/adf_ctl_drv.c6
-rw-r--r--drivers/crypto/qat/qat_common/adf_sriov.c26
-rw-r--r--drivers/crypto/sunxi-ss/sun4i-ss-cipher.c10
-rw-r--r--drivers/crypto/talitos.c151
-rw-r--r--drivers/crypto/ux500/hash/hash_core.c4
-rw-r--r--drivers/crypto/vmx/aes_cbc.c2
-rw-r--r--drivers/crypto/vmx/aes_ctr.c2
-rw-r--r--drivers/devfreq/devfreq.c13
-rw-r--r--drivers/dma/dw/core.c34
-rw-r--r--drivers/dma/hsu/hsu.c2
-rw-r--r--drivers/dma/hsu/hsu.h3
-rw-r--r--drivers/dma/pxa_dma.c39
-rw-r--r--drivers/edac/i7core_edac.c2
-rw-r--r--drivers/edac/sb_edac.c21
-rw-r--r--drivers/extcon/extcon-max77843.c2
-rw-r--r--drivers/firmware/efi/efi.c1
-rw-r--r--drivers/firmware/efi/libstub/Makefile7
-rw-r--r--drivers/firmware/efi/libstub/arm-stub.c40
-rw-r--r--drivers/firmware/efi/libstub/arm64-stub.c87
-rw-r--r--drivers/firmware/efi/libstub/efi-stub-helper.c7
-rw-r--r--drivers/firmware/efi/libstub/efistub.h7
-rw-r--r--drivers/firmware/efi/libstub/fdt.c14
-rw-r--r--drivers/firmware/efi/libstub/random.c135
-rw-r--r--drivers/firmware/efi/vars.c37
-rw-r--r--drivers/gpio/gpio-bcm-kona.c4
-rw-r--r--drivers/gpio/gpiolib-legacy.c8
-rw-r--r--drivers/gpio/gpiolib.c52
-rw-r--r--drivers/gpio/qpnp-pin.c12
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_object.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/atombios_encoders.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c4
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_dbgdev.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_process.c70
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c2
-rw-r--r--drivers/gpu/drm/drm_atomic.c5
-rw-r--r--drivers/gpu/drm/drm_crtc.c62
-rw-r--r--drivers/gpu/drm/drm_dp_mst_topology.c37
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c5
-rw-r--r--drivers/gpu/drm/drm_modes.c2
-rw-r--r--drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c2
-rw-r--r--drivers/gpu/drm/i915/i915_gem_shrinker.c2
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h11
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c8
-rw-r--r--drivers/gpu/drm/i915/intel_csr.c3
-rw-r--r--drivers/gpu/drm/i915/intel_ddi.c22
-rw-r--r--drivers/gpu/drm/i915/intel_display.c52
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c40
-rw-r--r--drivers/gpu/drm/i915/intel_dp_mst.c6
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h3
-rw-r--r--drivers/gpu/drm/i915/intel_fbdev.c6
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c12
-rw-r--r--drivers/gpu/drm/i915/intel_lrc.c6
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c8
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.c30
-rw-r--r--drivers/gpu/drm/i915/intel_uncore.c6
-rw-r--r--drivers/gpu/drm/imx/imx-drm-core.c8
-rw-r--r--drivers/gpu/drm/imx/ipuv3-crtc.c2
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_mode.c10
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.c2
-rw-r--r--drivers/gpu/drm/nouveau/nv04_fbcon.c7
-rw-r--r--drivers/gpu/drm/nouveau/nv50_fbcon.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fbcon.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/ramht.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c39
-rw-r--r--drivers/gpu/drm/qxl/qxl_display.c13
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.h2
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c10
-rw-r--r--drivers/gpu/drm/radeon/atombios_encoders.c4
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c154
-rw-r--r--drivers/gpu/drm/radeon/evergreen_reg.h46
-rw-r--r--drivers/gpu/drm/radeon/radeon_atpx_handler.c8
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c7
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c29
-rw-r--r--drivers/gpu/drm/radeon/radeon_dp_auxch.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_ttm.c2
-rw-r--r--drivers/gpu/drm/radeon/si_dpm.c1
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c7
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c25
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c12
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.h1
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c10
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fb.c53
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c10
-rw-r--r--drivers/gpu/ipu-v3/ipu-common.c12
-rw-r--r--drivers/gpu/msm/a5xx_reg.h2
-rw-r--r--drivers/gpu/msm/adreno-gpulist.h2
-rw-r--r--drivers/gpu/msm/adreno.c36
-rw-r--r--drivers/gpu/msm/adreno.h30
-rw-r--r--drivers/gpu/msm/adreno_a3xx.c45
-rw-r--r--drivers/gpu/msm/adreno_a4xx.c5
-rw-r--r--drivers/gpu/msm/adreno_a4xx_preempt.c2
-rw-r--r--drivers/gpu/msm/adreno_a5xx.c31
-rw-r--r--drivers/gpu/msm/adreno_perfcounter.c32
-rw-r--r--drivers/gpu/msm/kgsl.c49
-rw-r--r--drivers/gpu/msm/kgsl.h3
-rw-r--r--drivers/gpu/msm/kgsl_debugfs.c93
-rw-r--r--drivers/gpu/msm/kgsl_mmu.c20
-rw-r--r--drivers/gpu/msm/kgsl_pool.c132
-rw-r--r--drivers/gpu/msm/kgsl_pool.h2
-rw-r--r--drivers/gpu/msm/kgsl_sharedmem.c7
-rw-r--r--drivers/gpu/msm/kgsl_snapshot.c3
-rw-r--r--drivers/hid/hid-elo.c2
-rw-r--r--drivers/hid/hid-ids.h1
-rw-r--r--drivers/hid/hid-multitouch.c28
-rw-r--r--drivers/hid/hid-steelseries.c5
-rw-r--r--drivers/hid/usbhid/hid-quirks.c1
-rw-r--r--drivers/hid/usbhid/hiddev.c10
-rw-r--r--drivers/hid/wacom_wac.c5
-rw-r--r--drivers/hwmon/ads7828.c10
-rw-r--r--drivers/hwmon/dell-smm-hwmon.c80
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x.c4
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc.c24
-rw-r--r--drivers/hwtracing/coresight/coresight-tpiu.c3
-rw-r--r--drivers/hwtracing/stm/Kconfig16
-rw-r--r--drivers/hwtracing/stm/Makefile2
-rw-r--r--drivers/hwtracing/stm/core.c203
-rw-r--r--drivers/hwtracing/stm/dummy_stm.c67
-rw-r--r--drivers/hwtracing/stm/heartbeat.c126
-rw-r--r--drivers/hwtracing/stm/policy.c27
-rw-r--r--drivers/hwtracing/stm/stm.h2
-rw-r--r--drivers/i2c/busses/i2c-cpm.c4
-rw-r--r--drivers/i2c/busses/i2c-exynos5.c24
-rw-r--r--drivers/iio/accel/kxsd9.c4
-rw-r--r--drivers/iio/adc/ad7266.c7
-rw-r--r--drivers/iio/adc/qcom-rradc.c19
-rw-r--r--drivers/iio/adc/qcom-tadc.c2
-rw-r--r--drivers/iio/humidity/hdc100x.c20
-rw-r--r--drivers/iio/industrialio-trigger.c23
-rw-r--r--drivers/iio/light/apds9960.c1
-rw-r--r--drivers/iio/magnetometer/ak8975.c6
-rw-r--r--drivers/iio/pressure/st_pressure_core.c80
-rw-r--r--drivers/iio/proximity/as3935.c17
-rw-r--r--drivers/infiniband/core/cm.c4
-rw-r--r--drivers/infiniband/core/ucm.c4
-rw-r--r--drivers/infiniband/core/ucma.c3
-rw-r--r--drivers/infiniband/core/uverbs_main.c5
-rw-r--r--drivers/infiniband/hw/cxgb4/cq.c2
-rw-r--r--drivers/infiniband/hw/cxgb4/qp.c6
-rw-r--r--drivers/infiniband/hw/mlx4/ah.c2
-rw-r--r--drivers/infiniband/hw/mlx5/main.c6
-rw-r--r--drivers/infiniband/hw/qib/qib_file_ops.c5
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c2
-rw-r--r--drivers/input/misc/hbtp_input.c674
-rw-r--r--drivers/input/misc/max8997_haptic.c6
-rw-r--r--drivers/input/misc/pmic8xxx-pwrkey.c7
-rw-r--r--drivers/input/misc/pwm-beeper.c69
-rw-r--r--drivers/input/misc/uinput.c6
-rw-r--r--drivers/input/tablet/gtco.c10
-rw-r--r--drivers/input/touchscreen/gt9xx/goodix_tool.c45
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.c587
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.h17
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx_update.c10
-rw-r--r--drivers/input/touchscreen/zforce_ts.c4
-rw-r--r--drivers/iommu/amd_iommu.c87
-rw-r--r--drivers/iommu/amd_iommu_init.c14
-rw-r--r--drivers/iommu/arm-smmu-v3.c1
-rw-r--r--drivers/iommu/arm-smmu.c52
-rw-r--r--drivers/iommu/dma-iommu.c4
-rw-r--r--drivers/iommu/intel-iommu.c17
-rw-r--r--drivers/iommu/iommu-debug.c2
-rw-r--r--drivers/irqchip/irq-gic-v3.c19
-rw-r--r--drivers/irqchip/irq-gic.c8
-rw-r--r--drivers/irqchip/irq-mxs.c2
-rw-r--r--drivers/irqchip/irq-sunxi-nmi.c4
-rw-r--r--drivers/leds/leds-qpnp-flash-v2.c396
-rw-r--r--drivers/leds/leds-qpnp-wled.c570
-rw-r--r--drivers/mcb/mcb-parse.c2
-rw-r--r--drivers/md/Kconfig16
-rw-r--r--drivers/md/Makefile4
-rw-r--r--drivers/md/dm-android-verity.c925
-rw-r--r--drivers/md/dm-android-verity.h124
-rw-r--r--drivers/md/dm-cache-metadata.c64
-rw-r--r--drivers/md/dm-ioctl.c39
-rw-r--r--drivers/md/dm-linear.c24
-rw-r--r--drivers/md/dm-table.c1
-rw-r--r--drivers/md/dm-verity-fec.c19
-rw-r--r--drivers/md/dm-verity-fec.h4
-rw-r--r--drivers/md/dm-verity-target.c14
-rw-r--r--drivers/md/dm-verity.h10
-rw-r--r--drivers/md/md.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/common/cam_soc_api.c9
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp40.c27
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp44.c25
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp46.c30
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.c77
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c42
-rw-r--r--drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c7
-rw-r--r--drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c4
-rw-r--r--drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c68
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c2
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_core.c168
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_core.h10
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c285
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h11
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c34
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c4
-rw-r--r--drivers/media/platform/msm/vidc/hfi_packetization.c27
-rw-r--r--drivers/media/platform/msm/vidc/hfi_response_handler.c13
-rw-r--r--drivers/media/platform/msm/vidc/msm_v4l2_vidc.c20
-rw-r--r--drivers/media/platform/msm/vidc/msm_venc.c47
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc.c5
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_common.c69
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_debug.c6
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_internal.h1
-rw-r--r--drivers/media/platform/msm/vidc/venus_hfi.c18
-rw-r--r--drivers/media/platform/msm/vidc/venus_hfi.h3
-rw-r--r--drivers/media/platform/msm/vidc/vidc_hfi_api.h9
-rw-r--r--drivers/media/platform/msm/vidc/vidc_hfi_helper.h16
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c39
-rw-r--r--drivers/media/v4l2-core/v4l2-compat-ioctl32.c3
-rw-r--r--drivers/media/v4l2-core/videobuf2-core.c10
-rw-r--r--drivers/media/v4l2-core/videobuf2-memops.c2
-rw-r--r--drivers/memory/omap-gpmc.c2
-rw-r--r--drivers/mfd/intel-lpss.c20
-rw-r--r--drivers/mfd/intel_soc_pmic_core.c1
-rw-r--r--drivers/mfd/omap-usb-tll.c13
-rw-r--r--drivers/mfd/wcd934x-regmap.c4
-rw-r--r--drivers/mfd/wcd9xxx-utils.c23
-rw-r--r--drivers/misc/Kconfig2
-rw-r--r--drivers/misc/ad525x_dpot.c2
-rw-r--r--drivers/misc/cxl/irq.c1
-rw-r--r--drivers/misc/hdcp.c195
-rw-r--r--drivers/misc/mei/amthif.c4
-rw-r--r--drivers/misc/mei/bus.c15
-rw-r--r--drivers/misc/mei/client.c4
-rw-r--r--drivers/misc/mei/hbm.c3
-rw-r--r--drivers/misc/mei/interrupt.c6
-rw-r--r--drivers/misc/mei/mei_dev.h2
-rw-r--r--drivers/misc/mic/scif/scif_rma.c7
-rw-r--r--drivers/misc/qcom/qdsp6v2/amrwb_in.c2
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_aac.c10
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_alac.c2
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_amrnb.c6
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_amrwb.c6
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c6
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_ape.c6
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_evrc.c6
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c4
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_mp3.c6
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_qcelp.c6
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils.c4
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils_aio.c215
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_wma.c6
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_wmapro.c4
-rw-r--r--drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c20
-rw-r--r--drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c12
-rw-r--r--drivers/misc/qcom/qdsp6v2/ultrasound/usf.c66
-rw-r--r--drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c6
-rw-r--r--drivers/misc/qseecom.c378
-rw-r--r--drivers/misc/uid_stat.c153
-rw-r--r--drivers/mmc/card/block.c5
-rw-r--r--drivers/mmc/core/core.c2
-rw-r--r--drivers/mmc/core/mmc.c7
-rw-r--r--drivers/mmc/host/sdhci-acpi.c6
-rw-r--r--drivers/mmc/host/sdhci-pci-core.c5
-rw-r--r--drivers/mtd/nand/brcmnand/brcmnand.c34
-rw-r--r--drivers/mtd/nand/nand_base.c10
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c46
-rw-r--r--drivers/mtd/ubi/eba.c43
-rw-r--r--drivers/mtd/ubi/fastmap.c1
-rw-r--r--drivers/mtd/ubi/ubi.h2
-rw-r--r--drivers/net/can/dev.c56
-rw-r--r--drivers/net/can/m_can/m_can.c2
-rw-r--r--drivers/net/ethernet/atheros/alx/main.c7
-rw-r--r--drivers/net/ethernet/atheros/atlx/atl2.c2
-rw-r--r--drivers/net/ethernet/cadence/macb.c13
-rw-r--r--drivers/net/ethernet/cadence/macb.h2
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_queues.c4
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c10
-rw-r--r--drivers/net/ethernet/jme.c23
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_rx.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_tx.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_main.c15
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/port.c10
-rw-r--r--drivers/net/ethernet/rocker/rocker.c4
-rw-r--r--drivers/net/ethernet/sfc/ef10.c16
-rw-r--r--drivers/net/geneve.c41
-rw-r--r--drivers/net/team/team.c9
-rw-r--r--drivers/net/tun.c6
-rw-r--r--drivers/net/usb/asix_common.c2
-rw-r--r--drivers/net/usb/cdc_mbim.c9
-rw-r--r--drivers/net/usb/cdc_ncm.c7
-rw-r--r--drivers/net/vxlan.c60
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c8
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.c7
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c13
-rw-r--r--drivers/net/wireless/ath/ath5k/led.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/ar5008_phy.c8
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9002_phy.c5
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c7
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c10
-rw-r--r--drivers/net/wireless/ath/wil6210/interrupt.c4
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c30
-rw-r--r--drivers/net/wireless/ath/wil6210/pmc.c55
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h3
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c2
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c4
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c1
-rw-r--r--drivers/net/wireless/mwifiex/sta_ioctl.c1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/base.c4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c9
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c27
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c5
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/pci.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c5
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c3
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/wifi.h3
-rw-r--r--drivers/net/wireless/ti/wlcore/init.c5
-rw-r--r--drivers/nfc/nq-nci.c104
-rw-r--r--drivers/nfc/nq-nci.h9
-rw-r--r--drivers/nvmem/mxs-ocotp.c4
-rw-r--r--drivers/of/fdt.c19
-rw-r--r--drivers/of/irq.c19
-rw-r--r--drivers/pci/host/pci-msm.c37
-rw-r--r--drivers/pci/probe.c6
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-common.c14
-rw-r--r--drivers/pinctrl/pinctrl-at91-pio4.c2
-rw-r--r--drivers/pinctrl/pinctrl-single.c6
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msmfalcon.c229
-rw-r--r--drivers/pinctrl/samsung/pinctrl-exynos5440.c15
-rw-r--r--drivers/platform/msm/ipa/ipa_clients/ipa_usb.c499
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa.c6
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_dp.c32
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_flt.c36
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_i.h4
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_nat.c77
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_rt.c26
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c28
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa.c11
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_client.c57
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_dp.c154
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_i.h7
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_nat.c76
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_uc.c8
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_utils.c2
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c33
-rw-r--r--drivers/platform/msm/msm_11ad/msm_11ad.c48
-rw-r--r--drivers/platform/msm/qpnp-revid.c2
-rw-r--r--drivers/platform/msm/usb_bam.c11
-rw-r--r--drivers/platform/x86/dell-rbtn.c56
-rw-r--r--drivers/platform/x86/toshiba_acpi.c2
-rw-r--r--drivers/power/qcom-charger/fg-core.h44
-rw-r--r--drivers/power/qcom-charger/fg-memif.c193
-rw-r--r--drivers/power/qcom-charger/fg-reg.h25
-rw-r--r--drivers/power/qcom-charger/fg-util.c76
-rw-r--r--drivers/power/qcom-charger/pmic-voter.c1
-rw-r--r--drivers/power/qcom-charger/qpnp-fg-gen3.c516
-rw-r--r--drivers/power/qcom-charger/qpnp-smb2.c101
-rw-r--r--drivers/power/qcom-charger/smb-lib.c532
-rw-r--r--drivers/power/qcom-charger/smb-lib.h24
-rw-r--r--drivers/power/qcom-charger/smb-reg.h9
-rw-r--r--drivers/power/qcom-charger/smb138x-charger.c10
-rw-r--r--drivers/power/reset/Kconfig14
-rw-r--r--drivers/power/reset/Makefile2
-rw-r--r--drivers/power/reset/msm-poweroff.c1
-rw-r--r--drivers/power/reset/reboot-mode.c140
-rw-r--r--drivers/power/reset/reboot-mode.h14
-rw-r--r--drivers/power/reset/syscon-reboot-mode.c99
-rw-r--r--drivers/pwm/pwm-brcmstb.c4
-rw-r--r--drivers/regulator/axp20x-regulator.c4
-rw-r--r--drivers/regulator/cprh-kbss-regulator.c2
-rw-r--r--drivers/regulator/qpnp-labibb-regulator.c341
-rw-r--r--drivers/regulator/s2mps11.c28
-rw-r--r--drivers/regulator/s5m8767.c13
-rw-r--r--drivers/rtc/rtc-ds1685.c8
-rw-r--r--drivers/rtc/rtc-hym8563.c2
-rw-r--r--drivers/rtc/rtc-max77686.c2
-rw-r--r--drivers/rtc/rtc-rx8025.c1
-rw-r--r--drivers/rtc/rtc-vr41xx.c13
-rw-r--r--drivers/scsi/53c700.c4
-rw-r--r--drivers/scsi/aacraid/aacraid.h1
-rw-r--r--drivers/scsi/aacraid/comminit.c24
-rw-r--r--drivers/scsi/aacraid/commsup.c12
-rw-r--r--drivers/scsi/device_handler/Kconfig8
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c5
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_base.c3
-rw-r--r--drivers/scsi/qla1280.c2
-rw-r--r--drivers/scsi/scsi_devinfo.c1
-rw-r--r--drivers/scsi/scsi_error.c4
-rw-r--r--drivers/scsi/scsi_lib.c7
-rw-r--r--drivers/scsi/scsi_scan.c1
-rw-r--r--drivers/scsi/scsi_sysfs.c6
-rw-r--r--drivers/sensors/sensors_ssc.c13
-rw-r--r--drivers/soc/qcom/Kconfig9
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/glink.c1
-rw-r--r--drivers/soc/qcom/icnss.c65
-rw-r--r--drivers/soc/qcom/memshare/msm_memshare.c14
-rw-r--r--drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c4
-rw-r--r--drivers/soc/qcom/pil-q6v5-mss.c3
-rw-r--r--drivers/soc/qcom/pil-q6v5.c16
-rw-r--r--drivers/soc/qcom/pil-q6v5.h1
-rw-r--r--drivers/soc/qcom/qbt1000.c1263
-rw-r--r--drivers/soc/qcom/qdsp6v2/apr.c63
-rw-r--r--drivers/soc/qcom/qdsp6v2/apr_tal_glink.c51
-rw-r--r--drivers/soc/qcom/scm.c18
-rw-r--r--drivers/soc/qcom/secure_buffer.c4
-rw-r--r--drivers/soc/qcom/service-locator.c2
-rw-r--r--drivers/soc/qcom/service-notifier.c5
-rw-r--r--drivers/soc/qcom/sysmon.c6
-rw-r--r--drivers/soc/qcom/wcd-dsp-glink.c2
-rw-r--r--drivers/soc/rockchip/pm_domains.c1
-rw-r--r--drivers/spi/spi-pxa2xx.c2
-rw-r--r--drivers/spi/spi-rockchip.c7
-rw-r--r--drivers/spi/spi-ti-qspi.c45
-rw-r--r--drivers/spmi/spmi-pmic-arb.c57
-rw-r--r--drivers/staging/android/fiq_debugger/Kconfig9
-rw-r--r--drivers/staging/android/fiq_debugger/fiq_debugger.c36
-rw-r--r--drivers/staging/android/ion/ion_heap.c10
-rw-r--r--drivers/staging/comedi/drivers/das1800.c22
-rw-r--r--drivers/staging/iio/accel/sca3000_core.c2
-rw-r--r--drivers/staging/rdma/hfi1/TODO2
-rw-r--r--drivers/staging/rdma/hfi1/file_ops.c6
-rw-r--r--drivers/thermal/cpu_cooling.c16
-rw-r--r--drivers/thermal/rockchip_thermal.c11
-rw-r--r--drivers/thunderbolt/eeprom.c1
-rw-r--r--drivers/tty/n_gsm.c4
-rw-r--r--drivers/tty/n_hdlc.c4
-rw-r--r--drivers/tty/n_tty.c70
-rw-r--r--drivers/tty/pty.c4
-rw-r--r--drivers/tty/serial/8250/8250_mid.c35
-rw-r--r--drivers/tty/serial/8250/8250_pci.c3
-rw-r--r--drivers/tty/serial/atmel_serial.c14
-rw-r--r--drivers/tty/serial/samsung.c4
-rw-r--r--drivers/tty/serial/sh-sci.c39
-rw-r--r--drivers/tty/serial/ucc_uart.c3
-rw-r--r--drivers/tty/tty_buffer.c34
-rw-r--r--drivers/tty/vt/keyboard.c30
-rw-r--r--drivers/tty/vt/vt.c6
-rw-r--r--drivers/usb/common/usb-otg-fsm.c2
-rw-r--r--drivers/usb/core/driver.c40
-rw-r--r--drivers/usb/core/hcd-pci.c9
-rw-r--r--drivers/usb/core/hub.c11
-rw-r--r--drivers/usb/core/quirks.c23
-rw-r--r--drivers/usb/dwc2/core.h27
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c19
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c10
-rw-r--r--drivers/usb/dwc3/gadget.c19
-rw-r--r--drivers/usb/gadget/configfs.c9
-rw-r--r--drivers/usb/gadget/function/f_fs.c7
-rw-r--r--drivers/usb/gadget/function/f_gsi.c3
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c36
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.h2
-rw-r--r--drivers/usb/gadget/function/f_midi.c1
-rw-r--r--drivers/usb/gadget/function/f_mtp.c14
-rw-r--r--drivers/usb/gadget/function/u_ether.c21
-rw-r--r--drivers/usb/gadget/legacy/acm_ms.c4
-rw-r--r--drivers/usb/gadget/legacy/inode.c17
-rw-r--r--drivers/usb/gadget/legacy/mass_storage.c4
-rw-r--r--drivers/usb/gadget/legacy/multi.c12
-rw-r--r--drivers/usb/gadget/legacy/nokia.c7
-rw-r--r--drivers/usb/gadget/udc/udc-core.c2
-rw-r--r--drivers/usb/host/ehci-tegra.c2
-rw-r--r--drivers/usb/host/xhci-hub.c4
-rw-r--r--drivers/usb/host/xhci-mem.c36
-rw-r--r--drivers/usb/host/xhci-pci.c9
-rw-r--r--drivers/usb/host/xhci-plat.c11
-rw-r--r--drivers/usb/host/xhci-ring.c30
-rw-r--r--drivers/usb/host/xhci.c33
-rw-r--r--drivers/usb/misc/usbtest.c4
-rw-r--r--drivers/usb/musb/musb_core.c3
-rw-r--r--drivers/usb/musb/musb_host.c23
-rw-r--r--drivers/usb/pd/policy_engine.c311
-rw-r--r--drivers/usb/serial/cp210x.c4
-rw-r--r--drivers/usb/serial/io_edgeport.c56
-rw-r--r--drivers/usb/serial/keyspan.c4
-rw-r--r--drivers/usb/serial/mos7720.c1
-rw-r--r--drivers/usb/serial/mxuport.c10
-rw-r--r--drivers/usb/serial/option.c155
-rw-r--r--drivers/usb/serial/quatech2.c1
-rw-r--r--drivers/usb/storage/uas.c1
-rw-r--r--drivers/usb/usbip/usbip_common.c11
-rw-r--r--drivers/video/fbdev/Kconfig1
-rw-r--r--drivers/video/fbdev/da8xx-fb.c7
-rw-r--r--drivers/video/fbdev/msm/mdss.h13
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.c32
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c518
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h25
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c152
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c158
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.c162
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.h11
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c1
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_host.c5
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c133
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c17
-rw-r--r--drivers/video/fbdev/msm/mdss_hdcp.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_hdcp_1x.c32
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.c86
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c121
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h52
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c83
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_debug.c2
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c18
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_video.c48
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_layer.c347
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c342
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pipe.c22
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.c145
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_util.c31
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.c10
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.h90
-rw-r--r--drivers/video/fbdev/msm/mdss_smmu.c37
-rw-r--r--drivers/video/fbdev/msm/mdss_smmu.h34
-rw-r--r--drivers/video/fbdev/msm/msm_ext_display.c284
-rw-r--r--drivers/virtio/virtio_balloon.c20
-rw-r--r--drivers/xen/balloon.c40
-rw-r--r--drivers/xen/events/events_base.c6
-rw-r--r--drivers/xen/evtchn.c20
-rw-r--r--drivers/xen/xen-acpi-processor.c35
605 files changed, 16871 insertions, 5379 deletions
diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c
index 6979186dbd4b..9f77943653fb 100644
--- a/drivers/acpi/acpi_processor.c
+++ b/drivers/acpi/acpi_processor.c
@@ -491,6 +491,58 @@ static void acpi_processor_remove(struct acpi_device *device)
}
#endif /* CONFIG_ACPI_HOTPLUG_CPU */
+#ifdef CONFIG_X86
+static bool acpi_hwp_native_thermal_lvt_set;
+static acpi_status __init acpi_hwp_native_thermal_lvt_osc(acpi_handle handle,
+ u32 lvl,
+ void *context,
+ void **rv)
+{
+ u8 sb_uuid_str[] = "4077A616-290C-47BE-9EBD-D87058713953";
+ u32 capbuf[2];
+ struct acpi_osc_context osc_context = {
+ .uuid_str = sb_uuid_str,
+ .rev = 1,
+ .cap.length = 8,
+ .cap.pointer = capbuf,
+ };
+
+ if (acpi_hwp_native_thermal_lvt_set)
+ return AE_CTRL_TERMINATE;
+
+ capbuf[0] = 0x0000;
+ capbuf[1] = 0x1000; /* set bit 12 */
+
+ if (ACPI_SUCCESS(acpi_run_osc(handle, &osc_context))) {
+ if (osc_context.ret.pointer && osc_context.ret.length > 1) {
+ u32 *capbuf_ret = osc_context.ret.pointer;
+
+ if (capbuf_ret[1] & 0x1000) {
+ acpi_handle_info(handle,
+ "_OSC native thermal LVT Acked\n");
+ acpi_hwp_native_thermal_lvt_set = true;
+ }
+ }
+ kfree(osc_context.ret.pointer);
+ }
+
+ return AE_OK;
+}
+
+void __init acpi_early_processor_osc(void)
+{
+ if (boot_cpu_has(X86_FEATURE_HWP)) {
+ acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX,
+ acpi_hwp_native_thermal_lvt_osc,
+ NULL, NULL, NULL);
+ acpi_get_devices(ACPI_PROCESSOR_DEVICE_HID,
+ acpi_hwp_native_thermal_lvt_osc,
+ NULL, NULL);
+ }
+}
+#endif
+
/*
* The following ACPI IDs are known to be suitable for representing as
* processor devices.
diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c
index bc32f3194afe..28c50c6b5f45 100644
--- a/drivers/acpi/acpica/dsmethod.c
+++ b/drivers/acpi/acpica/dsmethod.c
@@ -417,6 +417,9 @@ acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node,
obj_desc->method.mutex->mutex.
original_sync_level =
obj_desc->method.mutex->mutex.sync_level;
+
+ obj_desc->method.mutex->mutex.thread_id =
+ acpi_os_get_thread_id();
}
}
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index a212cefae524..ca4f28432d87 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -1004,6 +1004,9 @@ static int __init acpi_bus_init(void)
goto error1;
}
+ /* Set capability bits for _OSC under processor scope */
+ acpi_early_processor_osc();
+
/*
* _OSC method may exist in module level code,
* so it must be run after ACPI_FULL_INITIALIZATION
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 11d87bf67e73..0f3f41c13b38 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -130,6 +130,12 @@ void acpi_early_processor_set_pdc(void);
static inline void acpi_early_processor_set_pdc(void) {}
#endif
+#ifdef CONFIG_X86
+void acpi_early_processor_osc(void);
+#else
+static inline void acpi_early_processor_osc(void) {}
+#endif
+
/* --------------------------------------------------------------------------
Embedded Controller
-------------------------------------------------------------------------- */
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 32d684af0ec7..a000ecb995e6 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -135,7 +135,7 @@ static struct osi_linux {
unsigned int enable:1;
unsigned int dmi:1;
unsigned int cmdline:1;
- unsigned int default_disabling:1;
+ u8 default_disabling;
} osi_linux = {0, 0, 0, 0};
static u32 acpi_osi_handler(acpi_string interface, u32 supported)
@@ -1444,10 +1444,13 @@ void __init acpi_osi_setup(char *str)
if (*str == '!') {
str++;
if (*str == '\0') {
- osi_linux.default_disabling = 1;
+ /* Do not override acpi_osi=!* */
+ if (!osi_linux.default_disabling)
+ osi_linux.default_disabling =
+ ACPI_DISABLE_ALL_VENDOR_STRINGS;
return;
} else if (*str == '*') {
- acpi_update_interfaces(ACPI_DISABLE_ALL_STRINGS);
+ osi_linux.default_disabling = ACPI_DISABLE_ALL_STRINGS;
for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
osi = &osi_setup_entries[i];
osi->enable = false;
@@ -1520,10 +1523,13 @@ static void __init acpi_osi_setup_late(void)
acpi_status status;
if (osi_linux.default_disabling) {
- status = acpi_update_interfaces(ACPI_DISABLE_ALL_VENDOR_STRINGS);
+ status = acpi_update_interfaces(osi_linux.default_disabling);
if (ACPI_SUCCESS(status))
- printk(KERN_INFO PREFIX "Disabled all _OSI OS vendors\n");
+ printk(KERN_INFO PREFIX "Disabled all _OSI OS vendors%s\n",
+ osi_linux.default_disabling ==
+ ACPI_DISABLE_ALL_STRINGS ?
+ " and feature groups" : "");
}
for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 20d17906fc9b..a3f458fd2238 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -1074,7 +1074,7 @@ static int binder_dec_node(struct binder_node *node, int strong, int internal)
static struct binder_ref *binder_get_ref(struct binder_proc *proc,
- uint32_t desc)
+ uint32_t desc, bool need_strong_ref)
{
struct rb_node *n = proc->refs_by_desc.rb_node;
struct binder_ref *ref;
@@ -1082,12 +1082,16 @@ static struct binder_ref *binder_get_ref(struct binder_proc *proc,
while (n) {
ref = rb_entry(n, struct binder_ref, rb_node_desc);
- if (desc < ref->desc)
+ if (desc < ref->desc) {
n = n->rb_left;
- else if (desc > ref->desc)
+ } else if (desc > ref->desc) {
n = n->rb_right;
- else
+ } else if (need_strong_ref && !ref->strong) {
+ binder_user_error("tried to use weak ref as strong ref\n");
+ return NULL;
+ } else {
return ref;
+ }
}
return NULL;
}
@@ -1357,7 +1361,8 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
- struct binder_ref *ref = binder_get_ref(proc, fp->handle);
+ struct binder_ref *ref = binder_get_ref(proc, fp->handle,
+ fp->type == BINDER_TYPE_HANDLE);
if (ref == NULL) {
pr_err("transaction release %d bad handle %d\n",
@@ -1452,7 +1457,7 @@ static void binder_transaction(struct binder_proc *proc,
if (tr->target.handle) {
struct binder_ref *ref;
- ref = binder_get_ref(proc, tr->target.handle);
+ ref = binder_get_ref(proc, tr->target.handle, true);
if (ref == NULL) {
binder_user_error("%d:%d got transaction to invalid handle\n",
proc->pid, thread->pid);
@@ -1649,7 +1654,9 @@ static void binder_transaction(struct binder_proc *proc,
fp->type = BINDER_TYPE_HANDLE;
else
fp->type = BINDER_TYPE_WEAK_HANDLE;
+ fp->binder = 0;
fp->handle = ref->desc;
+ fp->cookie = 0;
binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
&thread->todo);
@@ -1661,7 +1668,8 @@ static void binder_transaction(struct binder_proc *proc,
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
- struct binder_ref *ref = binder_get_ref(proc, fp->handle);
+ struct binder_ref *ref = binder_get_ref(proc, fp->handle,
+ fp->type == BINDER_TYPE_HANDLE);
if (ref == NULL) {
binder_user_error("%d:%d got transaction with invalid handle, %d\n",
@@ -1696,7 +1704,9 @@ static void binder_transaction(struct binder_proc *proc,
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
+ fp->binder = 0;
fp->handle = new_ref->desc;
+ fp->cookie = 0;
binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
trace_binder_transaction_ref_to_ref(t, ref,
new_ref);
@@ -1750,6 +1760,7 @@ static void binder_transaction(struct binder_proc *proc,
binder_debug(BINDER_DEBUG_TRANSACTION,
" fd %d -> %d\n", fp->handle, target_fd);
/* TODO: fput? */
+ fp->binder = 0;
fp->handle = target_fd;
} break;
@@ -1880,7 +1891,9 @@ static int binder_thread_write(struct binder_proc *proc,
ref->desc);
}
} else
- ref = binder_get_ref(proc, target);
+ ref = binder_get_ref(proc, target,
+ cmd == BC_ACQUIRE ||
+ cmd == BC_RELEASE);
if (ref == NULL) {
binder_user_error("%d:%d refcount change on invalid ref %d\n",
proc->pid, thread->pid, target);
@@ -2076,7 +2089,7 @@ static int binder_thread_write(struct binder_proc *proc,
if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
- ref = binder_get_ref(proc, target);
+ ref = binder_get_ref(proc, target, false);
if (ref == NULL) {
binder_user_error("%d:%d %s invalid ref %d\n",
proc->pid, thread->pid,
@@ -3444,7 +3457,7 @@ static void print_binder_node(struct seq_file *m, struct binder_node *node)
static void print_binder_ref(struct seq_file *m, struct binder_ref *ref)
{
- seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %p\n",
+ seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %pK\n",
ref->debug_id, ref->desc, ref->node->proc ? "" : "dead ",
ref->node->debug_id, ref->strong, ref->weak, ref->death);
}
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 04975b851c23..639adb1f8abd 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -51,6 +51,9 @@ static int ahci_probe(struct platform_device *pdev)
if (rc)
return rc;
+ of_property_read_u32(dev->of_node,
+ "ports-implemented", &hpriv->force_port_map);
+
if (of_device_is_compatible(dev->of_node, "hisilicon,hisi-ahci"))
hpriv->flags |= AHCI_HFLAG_NO_FBS | AHCI_HFLAG_NO_NCQ;
diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c
index e2c6d9e0c5ac..e916bff6cee8 100644
--- a/drivers/ata/ahci_xgene.c
+++ b/drivers/ata/ahci_xgene.c
@@ -739,9 +739,9 @@ static int xgene_ahci_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "%s: Error reading device info. Assume version1\n",
__func__);
version = XGENE_AHCI_V1;
- }
- if (info->valid & ACPI_VALID_CID)
+ } else if (info->valid & ACPI_VALID_CID) {
version = XGENE_AHCI_V2;
+ }
}
}
#endif
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 998c6a85ad89..9628fa131757 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -467,6 +467,7 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
dev_info(dev, "forcing port_map 0x%x -> 0x%x\n",
port_map, hpriv->force_port_map);
port_map = hpriv->force_port_map;
+ hpriv->saved_port_map = port_map;
}
if (hpriv->mask_port_map) {
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 961acc788f44..91a9e6af2ec4 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -606,7 +606,7 @@ void ata_scsi_error(struct Scsi_Host *host)
ata_scsi_port_error_handler(host, ap);
/* finish or retry handled scmd's and clean up */
- WARN_ON(host->host_failed || !list_empty(&eh_work_q));
+ WARN_ON(!list_empty(&eh_work_q));
DPRINTK("EXIT\n");
}
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 3ac683dff7de..bbe8e2efc677 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -841,11 +841,29 @@ static struct kobject *get_device_parent(struct device *dev,
return NULL;
}
+static inline bool live_in_glue_dir(struct kobject *kobj,
+ struct device *dev)
+{
+ if (!kobj || !dev->class ||
+ kobj->kset != &dev->class->p->glue_dirs)
+ return false;
+ return true;
+}
+
+static inline struct kobject *get_glue_dir(struct device *dev)
+{
+ return dev->kobj.parent;
+}
+
+/*
+ * make sure cleaning up dir as the last step, we need to make
+ * sure .release handler of kobject is run with holding the
+ * global lock
+ */
static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir)
{
/* see if we live in a "glue" directory */
- if (!glue_dir || !dev->class ||
- glue_dir->kset != &dev->class->p->glue_dirs)
+ if (!live_in_glue_dir(glue_dir, dev))
return;
mutex_lock(&gdp_mutex);
@@ -853,11 +871,6 @@ static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir)
mutex_unlock(&gdp_mutex);
}
-static void cleanup_device_parent(struct device *dev)
-{
- cleanup_glue_dir(dev, dev->kobj.parent);
-}
-
static int device_add_class_symlinks(struct device *dev)
{
struct device_node *of_node = dev_of_node(dev);
@@ -1033,6 +1046,7 @@ int device_add(struct device *dev)
struct kobject *kobj;
struct class_interface *class_intf;
int error = -EINVAL;
+ struct kobject *glue_dir = NULL;
dev = get_device(dev);
if (!dev)
@@ -1077,8 +1091,10 @@ int device_add(struct device *dev)
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
- if (error)
+ if (error) {
+ glue_dir = get_glue_dir(dev);
goto Error;
+ }
/* notify platform of device entry */
if (platform_notify)
@@ -1159,9 +1175,10 @@ done:
device_remove_file(dev, &dev_attr_uevent);
attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
+ glue_dir = get_glue_dir(dev);
kobject_del(&dev->kobj);
Error:
- cleanup_device_parent(dev);
+ cleanup_glue_dir(dev, glue_dir);
put_device(parent);
name_error:
kfree(dev->p);
@@ -1237,6 +1254,7 @@ EXPORT_SYMBOL_GPL(put_device);
void device_del(struct device *dev)
{
struct device *parent = dev->parent;
+ struct kobject *glue_dir = NULL;
struct class_interface *class_intf;
/* Notify clients of device removal. This call must come
@@ -1281,8 +1299,9 @@ void device_del(struct device *dev)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_REMOVED_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
- cleanup_device_parent(dev);
+ glue_dir = get_glue_dir(dev);
kobject_del(&dev->kobj);
+ cleanup_glue_dir(dev, glue_dir);
put_device(parent);
}
EXPORT_SYMBOL_GPL(device_del);
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 918ce439534b..0dd6379ac215 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -228,6 +228,8 @@ static void driver_bound(struct device *dev)
klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
+ device_pm_check_callbacks(dev);
+
/*
* Make sure the device is no longer in one of the deferred lists and
* kick off retrying all pending devices
@@ -720,6 +722,7 @@ static void __device_release_driver(struct device *dev)
dev->pm_domain->dismiss(dev);
klist_remove(&dev->p->knode_driver);
+ device_pm_check_callbacks(dev);
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_UNBOUND_DRIVER,
diff --git a/drivers/base/module.c b/drivers/base/module.c
index db930d3ee312..2a215780eda2 100644
--- a/drivers/base/module.c
+++ b/drivers/base/module.c
@@ -24,10 +24,12 @@ static char *make_driver_name(struct device_driver *drv)
static void module_create_drivers_dir(struct module_kobject *mk)
{
- if (!mk || mk->drivers_dir)
- return;
+ static DEFINE_MUTEX(drivers_dir_mutex);
- mk->drivers_dir = kobject_create_and_add("drivers", &mk->kobj);
+ mutex_lock(&drivers_dir_mutex);
+ if (mk && !mk->drivers_dir)
+ mk->drivers_dir = kobject_create_and_add("drivers", &mk->kobj);
+ mutex_unlock(&drivers_dir_mutex);
}
void module_add_driver(struct module *mod, struct device_driver *drv)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 65f50eccd49b..a48824deabc5 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1381,7 +1381,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
mutex_lock(&genpd->lock);
- if (!list_empty(&subdomain->slave_links) || subdomain->device_count) {
+ if (!list_empty(&subdomain->master_links) || subdomain->device_count) {
pr_warn("%s: unable to remove subdomain %s\n", genpd->name,
subdomain->name);
ret = -EBUSY;
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 6ed8b9326629..7eea95d490e6 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -35,8 +35,6 @@
#include <linux/timer.h>
#include <linux/wakeup_reason.h>
-#include <asm/current.h>
-
#include "../base.h"
#include "power.h"
@@ -62,12 +60,6 @@ struct suspend_stats suspend_stats;
static DEFINE_MUTEX(dpm_list_mtx);
static pm_message_t pm_transition;
-static void dpm_drv_timeout(unsigned long data);
-struct dpm_drv_wd_data {
- struct device *dev;
- struct task_struct *tsk;
-};
-
static int async_error;
static char *pm_verb(int event)
@@ -134,6 +126,7 @@ void device_pm_add(struct device *dev)
{
pr_debug("PM: Adding info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
+ device_pm_check_callbacks(dev);
mutex_lock(&dpm_list_mtx);
if (dev->parent && dev->parent->power.is_prepared)
dev_warn(dev, "parent %s should not be sleeping\n",
@@ -156,6 +149,7 @@ void device_pm_remove(struct device *dev)
mutex_unlock(&dpm_list_mtx);
device_wakeup_disable(dev);
pm_runtime_remove(dev);
+ device_pm_check_callbacks(dev);
}
/**
@@ -839,30 +833,6 @@ static void async_resume(void *data, async_cookie_t cookie)
}
/**
- * dpm_drv_timeout - Driver suspend / resume watchdog handler
- * @data: struct device which timed out
- *
- * Called when a driver has timed out suspending or resuming.
- * There's not much we can do here to recover so
- * BUG() out for a crash-dump
- *
- */
-static void dpm_drv_timeout(unsigned long data)
-{
- struct dpm_drv_wd_data *wd_data = (void *)data;
- struct device *dev = wd_data->dev;
- struct task_struct *tsk = wd_data->tsk;
-
- printk(KERN_EMERG "**** DPM device timeout: %s (%s)\n", dev_name(dev),
- (dev->driver ? dev->driver->name : "no driver"));
-
- printk(KERN_EMERG "dpm suspend stack:\n");
- show_stack(tsk, NULL);
-
- BUG();
-}
-
-/**
* dpm_resume - Execute "resume" callbacks for non-sysdev devices.
* @state: PM transition of the system being carried out.
*
@@ -1295,14 +1265,15 @@ int dpm_suspend_late(pm_message_t state)
error = device_suspend_late(dev);
mutex_lock(&dpm_list_mtx);
+ if (!list_empty(&dev->power.entry))
+ list_move(&dev->power.entry, &dpm_late_early_list);
+
if (error) {
pm_dev_err(dev, state, " late", error);
dpm_save_failed_dev(dev_name(dev));
put_device(dev);
break;
}
- if (!list_empty(&dev->power.entry))
- list_move(&dev->power.entry, &dpm_late_early_list);
put_device(dev);
if (async_error)
@@ -1380,8 +1351,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
pm_callback_t callback = NULL;
char *info = NULL;
int error = 0;
- struct timer_list timer;
- struct dpm_drv_wd_data data;
char suspend_abort[MAX_SUSPEND_ABORT_LEN];
DECLARE_DPM_WATCHDOG_ON_STACK(wd);
@@ -1412,14 +1381,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
if (dev->power.syscore)
goto Complete;
-
- data.dev = dev;
- data.tsk = current;
- init_timer_on_stack(&timer);
- timer.expires = jiffies + HZ * 12;
- timer.function = dpm_drv_timeout;
- timer.data = (unsigned long)&data;
- add_timer(&timer);
if (dev->power.direct_complete) {
if (pm_runtime_status_suspended(dev)) {
@@ -1500,9 +1461,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
device_unlock(dev);
dpm_watchdog_clear(&wd);
- del_timer_sync(&timer);
- destroy_timer_on_stack(&timer);
-
Complete:
complete_all(&dev->power.completion);
if (error)
@@ -1619,6 +1577,11 @@ static int device_prepare(struct device *dev, pm_message_t state)
dev->power.wakeup_path = device_may_wakeup(dev);
+ if (dev->power.no_pm_callbacks) {
+ ret = 1; /* Let device go direct_complete */
+ goto unlock;
+ }
+
if (dev->pm_domain) {
info = "preparing power domain ";
callback = dev->pm_domain->ops.prepare;
@@ -1641,6 +1604,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
if (callback)
ret = callback(dev);
+unlock:
device_unlock(dev);
if (ret < 0) {
@@ -1769,3 +1733,30 @@ void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *))
device_pm_unlock();
}
EXPORT_SYMBOL_GPL(dpm_for_each_dev);
+
+static bool pm_ops_is_empty(const struct dev_pm_ops *ops)
+{
+ if (!ops)
+ return true;
+
+ return !ops->prepare &&
+ !ops->suspend &&
+ !ops->suspend_late &&
+ !ops->suspend_noirq &&
+ !ops->resume_noirq &&
+ !ops->resume_early &&
+ !ops->resume &&
+ !ops->complete;
+}
+
+void device_pm_check_callbacks(struct device *dev)
+{
+ spin_lock_irq(&dev->power.lock);
+ dev->power.no_pm_callbacks =
+ (!dev->bus || pm_ops_is_empty(dev->bus->pm)) &&
+ (!dev->class || pm_ops_is_empty(dev->class->pm)) &&
+ (!dev->type || pm_ops_is_empty(dev->type->pm)) &&
+ (!dev->pm_domain || pm_ops_is_empty(&dev->pm_domain->ops)) &&
+ (!dev->driver || pm_ops_is_empty(dev->driver->pm));
+ spin_unlock_irq(&dev->power.lock);
+}
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 998fa6b23084..297beae64314 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -123,6 +123,7 @@ extern void device_pm_remove(struct device *);
extern void device_pm_move_before(struct device *, struct device *);
extern void device_pm_move_after(struct device *, struct device *);
extern void device_pm_move_last(struct device *);
+extern void device_pm_check_callbacks(struct device *dev);
#else /* !CONFIG_PM_SLEEP */
@@ -141,6 +142,8 @@ static inline void device_pm_move_after(struct device *deva,
struct device *devb) {}
static inline void device_pm_move_last(struct device *dev) {}
+static inline void device_pm_check_callbacks(struct device *dev) {}
+
#endif /* !CONFIG_PM_SLEEP */
static inline void device_pm_init(struct device *dev)
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index e1a10a03df8e..9796a1a15ef6 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -1468,11 +1468,16 @@ int pm_runtime_force_resume(struct device *dev)
goto out;
}
- ret = callback(dev);
+ ret = pm_runtime_set_active(dev);
if (ret)
goto out;
- pm_runtime_set_active(dev);
+ ret = callback(dev);
+ if (ret) {
+ pm_runtime_set_suspended(dev);
+ goto out;
+ }
+
pm_runtime_mark_last_busy(dev);
out:
pm_runtime_enable(dev);
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 423f4ca7d712..80cf8add46ff 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -488,6 +488,12 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
bvec = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter);
iov_iter_bvec(&iter, ITER_BVEC | rw, bvec,
bio_segments(bio), blk_rq_bytes(cmd->rq));
+ /*
+ * This bio may be started from the middle of the 'bvec'
+ * because of bio splitting, so offset from the bvec must
+ * be passed to iov iterator
+ */
+ iter.iov_offset = bio->bi_iter.bi_bvec_done;
cmd->iocb.ki_pos = pos;
cmd->iocb.ki_filp = file;
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 93b3f99b6865..8f1ce6d57a08 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -618,8 +618,8 @@ static void nbd_request_handler(struct request_queue *q)
req, req->cmd_type);
if (unlikely(!nbd->sock)) {
- dev_err(disk_to_dev(nbd->disk),
- "Attempted send on closed socket\n");
+ dev_err_ratelimited(disk_to_dev(nbd->disk),
+ "Attempted send on closed socket\n");
req->errors++;
nbd_end_request(nbd, req);
spin_lock_irq(q->queue_lock);
diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
index 562b5a4ca7b7..78a39f736c64 100644
--- a/drivers/block/paride/pd.c
+++ b/drivers/block/paride/pd.c
@@ -126,7 +126,7 @@
*/
#include <linux/types.h>
-static bool verbose = 0;
+static int verbose = 0;
static int major = PD_MAJOR;
static char *name = PD_NAME;
static int cluster = 64;
@@ -161,7 +161,7 @@ enum {D_PRT, D_PRO, D_UNI, D_MOD, D_GEO, D_SBY, D_DLY, D_SLV};
static DEFINE_MUTEX(pd_mutex);
static DEFINE_SPINLOCK(pd_lock);
-module_param(verbose, bool, 0);
+module_param(verbose, int, 0);
module_param(major, int, 0);
module_param(name, charp, 0);
module_param(cluster, int, 0);
diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c
index 1740d75e8a32..216a94fed5b4 100644
--- a/drivers/block/paride/pt.c
+++ b/drivers/block/paride/pt.c
@@ -117,7 +117,7 @@
*/
-static bool verbose = 0;
+static int verbose = 0;
static int major = PT_MAJOR;
static char *name = PT_NAME;
static int disable = 0;
@@ -152,7 +152,7 @@ static int (*drives[4])[6] = {&drive0, &drive1, &drive2, &drive3};
#include <asm/uaccess.h>
-module_param(verbose, bool, 0);
+module_param(verbose, int, 0);
module_param(major, int, 0);
module_param(name, charp, 0);
module_param_array(drive0, int, NULL, 0);
diff --git a/drivers/bluetooth/bluetooth-power.c b/drivers/bluetooth/bluetooth-power.c
index 1317ddaa3c23..b05b999fbbdc 100644
--- a/drivers/bluetooth/bluetooth-power.c
+++ b/drivers/bluetooth/bluetooth-power.c
@@ -46,6 +46,7 @@ static const struct of_device_id bt_power_match_table[] = {
static struct bluetooth_power_platform_data *bt_power_pdata;
static struct platform_device *btpdev;
static bool previous;
+static int pwr_state;
struct class *bt_class;
static int bt_major;
@@ -636,6 +637,7 @@ static int bt_power_probe(struct platform_device *pdev)
memcpy(bt_power_pdata, pdev->dev.platform_data,
sizeof(struct bluetooth_power_platform_data));
+ pwr_state = 0;
} else {
BT_PWR_ERR("Failed to get platform data");
goto free_pdata;
@@ -680,7 +682,7 @@ int bt_register_slimdev(struct device *dev)
static long bt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- int ret;
+ int ret, pwr_cntrl = 0;
switch (cmd) {
case BT_CMD_SLIM_TEST:
@@ -692,6 +694,18 @@ static long bt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
bt_power_pdata->slim_dev->platform_data
);
break;
+ case BT_CMD_PWR_CTRL:
+ pwr_cntrl = (int)arg;
+ BT_PWR_ERR("BT_CMD_PWR_CTRL pwr_cntrl:%d", pwr_cntrl);
+ if (pwr_state != pwr_cntrl) {
+ ret = bluetooth_power(pwr_cntrl);
+ if (!ret)
+ pwr_state = pwr_cntrl;
+ } else {
+ BT_PWR_ERR("BT chip state is already :%d no change d\n"
+ , pwr_state);
+ }
+ break;
default:
return -EINVAL;
}
@@ -711,6 +725,7 @@ static struct platform_driver bt_power_driver = {
static const struct file_operations bt_dev_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = bt_ioctl,
+ .compat_ioctl = bt_ioctl,
};
static int __init bluetooth_power_init(void)
@@ -733,7 +748,7 @@ static int __init bluetooth_power_init(void)
if (device_create(bt_class, NULL, MKDEV(bt_major, 0),
- NULL, "pintest") == NULL) {
+ NULL, "btpower") == NULL) {
BTFMSLIM_ERR("failed to allocate char dev\n");
goto chrdev_unreg;
}
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index ed888e302bc3..597b2d16b775 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -50,6 +50,7 @@ struct vhci_data {
wait_queue_head_t read_wait;
struct sk_buff_head readq;
+ struct mutex open_mutex;
struct delayed_work open_timeout;
};
@@ -87,12 +88,15 @@ static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
return 0;
}
-static int vhci_create_device(struct vhci_data *data, __u8 opcode)
+static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
{
struct hci_dev *hdev;
struct sk_buff *skb;
__u8 dev_type;
+ if (data->hdev)
+ return -EBADFD;
+
/* bits 0-1 are dev_type (BR/EDR or AMP) */
dev_type = opcode & 0x03;
@@ -151,6 +155,17 @@ static int vhci_create_device(struct vhci_data *data, __u8 opcode)
return 0;
}
+static int vhci_create_device(struct vhci_data *data, __u8 opcode)
+{
+ int err;
+
+ mutex_lock(&data->open_mutex);
+ err = __vhci_create_device(data, opcode);
+ mutex_unlock(&data->open_mutex);
+
+ return err;
+}
+
static inline ssize_t vhci_get_user(struct vhci_data *data,
struct iov_iter *from)
{
@@ -189,11 +204,6 @@ static inline ssize_t vhci_get_user(struct vhci_data *data,
break;
case HCI_VENDOR_PKT:
- if (data->hdev) {
- kfree_skb(skb);
- return -EBADFD;
- }
-
cancel_delayed_work_sync(&data->open_timeout);
opcode = *((__u8 *) skb->data);
@@ -320,6 +330,7 @@ static int vhci_open(struct inode *inode, struct file *file)
skb_queue_head_init(&data->readq);
init_waitqueue_head(&data->read_wait);
+ mutex_init(&data->open_mutex);
INIT_DELAYED_WORK(&data->open_timeout, vhci_open_timeout);
file->private_data = data;
@@ -333,15 +344,18 @@ static int vhci_open(struct inode *inode, struct file *file)
static int vhci_release(struct inode *inode, struct file *file)
{
struct vhci_data *data = file->private_data;
- struct hci_dev *hdev = data->hdev;
+ struct hci_dev *hdev;
cancel_delayed_work_sync(&data->open_timeout);
+ hdev = data->hdev;
+
if (hdev) {
hci_unregister_dev(hdev);
hci_free_dev(hdev);
}
+ skb_queue_purge(&data->readq);
file->private_data = NULL;
kfree(data);
diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c
index e98d15eaa799..1827fc4d15c1 100644
--- a/drivers/bus/imx-weim.c
+++ b/drivers/bus/imx-weim.c
@@ -150,7 +150,7 @@ static int __init weim_parse_dt(struct platform_device *pdev,
return ret;
}
- for_each_child_of_node(pdev->dev.of_node, child) {
+ for_each_available_child_of_node(pdev->dev.of_node, child) {
if (!child->name)
continue;
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index ca9a8684de94..1046c262b46b 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -590,10 +590,6 @@ config DEVPORT
depends on ISA || PCI
default y
-config DCC_TTY
- tristate "DCC tty driver"
- depends on ARM
-
source "drivers/s390/char/Kconfig"
config TILE_SROM
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index fe696f180841..e180562c725e 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -53,7 +53,6 @@ obj-$(CONFIG_PCMCIA) += pcmcia/
obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
obj-$(CONFIG_TCG_TPM) += tpm/
-obj-$(CONFIG_DCC_TTY) += dcc_tty.o
obj-$(CONFIG_PS3_FLASH) += ps3flash.o
obj-$(CONFIG_JS_RTC) += js-rtc.o
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 67c1207d35be..7767086df849 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -490,7 +490,7 @@ static void fastrpc_mmap_free(struct fastrpc_mmap *map)
ion_free(fl->apps->client, map->handle);
if (sess->smmu.enabled) {
if (map->size || map->phys)
- msm_dma_unmap_sg(fl->sctx->dev,
+ msm_dma_unmap_sg(sess->dev,
map->table->sgl,
map->table->nents, DMA_BIDIRECTIONAL,
map->buf);
@@ -1070,7 +1070,7 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx)
int idx = list[i].pgidx;
if (map->attr & FASTRPC_ATTR_NOVA) {
- offset = (uintptr_t)lpra[i].buf.pv;
+ offset = 0;
} else {
down_read(&current->mm->mmap_sem);
VERIFY(err, NULL != (vma = find_vma(current->mm,
diff --git a/drivers/char/dcc_tty.c b/drivers/char/dcc_tty.c
deleted file mode 100644
index 0a62d410286f..000000000000
--- a/drivers/char/dcc_tty.c
+++ /dev/null
@@ -1,326 +0,0 @@
-/* drivers/char/dcc_tty.c
- *
- * Copyright (C) 2007 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/console.h>
-#include <linux/hrtimer.h>
-#include <linux/tty.h>
-#include <linux/tty_driver.h>
-#include <linux/tty_flip.h>
-
-MODULE_DESCRIPTION("DCC TTY Driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("1.0");
-
-DEFINE_SPINLOCK(g_dcc_tty_lock);
-static struct hrtimer g_dcc_timer;
-static char g_dcc_buffer[16];
-static int g_dcc_buffer_head;
-static int g_dcc_buffer_count;
-static unsigned g_dcc_write_delay_usecs = 1;
-static struct tty_driver *g_dcc_tty_driver;
-static struct tty_struct *g_dcc_tty;
-static int g_dcc_tty_open_count;
-
-static void dcc_poll_locked(void)
-{
- char ch;
- int rch;
- int written;
-
- while (g_dcc_buffer_count) {
- ch = g_dcc_buffer[g_dcc_buffer_head];
- asm(
- "mrc 14, 0, r15, c0, c1, 0\n"
- "mcrcc 14, 0, %1, c0, c5, 0\n"
- "movcc %0, #1\n"
- "movcs %0, #0\n"
- : "=r" (written)
- : "r" (ch)
- );
- if (written) {
- if (ch == '\n')
- g_dcc_buffer[g_dcc_buffer_head] = '\r';
- else {
- g_dcc_buffer_head = (g_dcc_buffer_head + 1) % ARRAY_SIZE(g_dcc_buffer);
- g_dcc_buffer_count--;
- if (g_dcc_tty)
- tty_wakeup(g_dcc_tty);
- }
- g_dcc_write_delay_usecs = 1;
- } else {
- if (g_dcc_write_delay_usecs > 0x100)
- break;
- g_dcc_write_delay_usecs <<= 1;
- udelay(g_dcc_write_delay_usecs);
- }
- }
-
- if (g_dcc_tty && !test_bit(TTY_THROTTLED, &g_dcc_tty->flags)) {
- asm(
- "mrc 14, 0, %0, c0, c1, 0\n"
- "tst %0, #(1 << 30)\n"
- "moveq %0, #-1\n"
- "mrcne 14, 0, %0, c0, c5, 0\n"
- : "=r" (rch)
- );
- if (rch >= 0) {
- ch = rch;
- tty_insert_flip_string(g_dcc_tty->port, &ch, 1);
- tty_flip_buffer_push(g_dcc_tty->port);
- }
- }
-
-
- if (g_dcc_buffer_count)
- hrtimer_start(&g_dcc_timer, ktime_set(0, g_dcc_write_delay_usecs * NSEC_PER_USEC), HRTIMER_MODE_REL);
- else
- hrtimer_start(&g_dcc_timer, ktime_set(0, 20 * NSEC_PER_MSEC), HRTIMER_MODE_REL);
-}
-
-static int dcc_tty_open(struct tty_struct * tty, struct file * filp)
-{
- int ret;
- unsigned long irq_flags;
-
- spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
- if (g_dcc_tty == NULL || g_dcc_tty == tty) {
- g_dcc_tty = tty;
- g_dcc_tty_open_count++;
- ret = 0;
- } else
- ret = -EBUSY;
- spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
-
- printk("dcc_tty_open, tty %p, f_flags %x, returned %d\n", tty, filp->f_flags, ret);
-
- return ret;
-}
-
-static void dcc_tty_close(struct tty_struct * tty, struct file * filp)
-{
- printk("dcc_tty_close, tty %p, f_flags %x\n", tty, filp->f_flags);
- if (g_dcc_tty == tty) {
- if (--g_dcc_tty_open_count == 0)
- g_dcc_tty = NULL;
- }
-}
-
-static int dcc_write(const unsigned char *buf_start, int count)
-{
- const unsigned char *buf = buf_start;
- unsigned long irq_flags;
- int copy_len;
- int space_left;
- int tail;
-
- if (count < 1)
- return 0;
-
- spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
- do {
- tail = (g_dcc_buffer_head + g_dcc_buffer_count) % ARRAY_SIZE(g_dcc_buffer);
- copy_len = ARRAY_SIZE(g_dcc_buffer) - tail;
- space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count;
- if (copy_len > space_left)
- copy_len = space_left;
- if (copy_len > count)
- copy_len = count;
- memcpy(&g_dcc_buffer[tail], buf, copy_len);
- g_dcc_buffer_count += copy_len;
- buf += copy_len;
- count -= copy_len;
- if (copy_len < count && copy_len < space_left) {
- space_left -= copy_len;
- copy_len = count;
- if (copy_len > space_left) {
- copy_len = space_left;
- }
- memcpy(g_dcc_buffer, buf, copy_len);
- buf += copy_len;
- count -= copy_len;
- g_dcc_buffer_count += copy_len;
- }
- dcc_poll_locked();
- space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count;
- } while(count && space_left);
- spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
- return buf - buf_start;
-}
-
-static int dcc_tty_write(struct tty_struct * tty, const unsigned char *buf, int count)
-{
- int ret;
- /* printk("dcc_tty_write %p, %d\n", buf, count); */
- ret = dcc_write(buf, count);
- if (ret != count)
- printk("dcc_tty_write %p, %d, returned %d\n", buf, count, ret);
- return ret;
-}
-
-static int dcc_tty_write_room(struct tty_struct *tty)
-{
- int space_left;
- unsigned long irq_flags;
-
- spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
- space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count;
- spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
- return space_left;
-}
-
-static int dcc_tty_chars_in_buffer(struct tty_struct *tty)
-{
- int ret;
- asm(
- "mrc 14, 0, %0, c0, c1, 0\n"
- "mov %0, %0, LSR #30\n"
- "and %0, %0, #1\n"
- : "=r" (ret)
- );
- return ret;
-}
-
-static void dcc_tty_unthrottle(struct tty_struct * tty)
-{
- unsigned long irq_flags;
-
- spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
- dcc_poll_locked();
- spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
-}
-
-static enum hrtimer_restart dcc_tty_timer_func(struct hrtimer *timer)
-{
- unsigned long irq_flags;
-
- spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
- dcc_poll_locked();
- spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
- return HRTIMER_NORESTART;
-}
-
-void dcc_console_write(struct console *co, const char *b, unsigned count)
-{
-#if 1
- dcc_write(b, count);
-#else
- /* blocking printk */
- while (count > 0) {
- int written;
- written = dcc_write(b, count);
- if (written) {
- b += written;
- count -= written;
- }
- }
-#endif
-}
-
-static struct tty_driver *dcc_console_device(struct console *c, int *index)
-{
- *index = 0;
- return g_dcc_tty_driver;
-}
-
-static int __init dcc_console_setup(struct console *co, char *options)
-{
- if (co->index != 0)
- return -ENODEV;
- return 0;
-}
-
-
-static struct console dcc_console =
-{
- .name = "ttyDCC",
- .write = dcc_console_write,
- .device = dcc_console_device,
- .setup = dcc_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
-};
-
-static struct tty_operations dcc_tty_ops = {
- .open = dcc_tty_open,
- .close = dcc_tty_close,
- .write = dcc_tty_write,
- .write_room = dcc_tty_write_room,
- .chars_in_buffer = dcc_tty_chars_in_buffer,
- .unthrottle = dcc_tty_unthrottle,
-};
-
-static int __init dcc_tty_init(void)
-{
- int ret;
-
- hrtimer_init(&g_dcc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- g_dcc_timer.function = dcc_tty_timer_func;
-
- g_dcc_tty_driver = alloc_tty_driver(1);
- if (!g_dcc_tty_driver) {
- printk(KERN_ERR "dcc_tty_probe: alloc_tty_driver failed\n");
- ret = -ENOMEM;
- goto err_alloc_tty_driver_failed;
- }
- g_dcc_tty_driver->owner = THIS_MODULE;
- g_dcc_tty_driver->driver_name = "dcc";
- g_dcc_tty_driver->name = "ttyDCC";
- g_dcc_tty_driver->major = 0; // auto assign
- g_dcc_tty_driver->minor_start = 0;
- g_dcc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
- g_dcc_tty_driver->subtype = SERIAL_TYPE_NORMAL;
- g_dcc_tty_driver->init_termios = tty_std_termios;
- g_dcc_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
- tty_set_operations(g_dcc_tty_driver, &dcc_tty_ops);
- ret = tty_register_driver(g_dcc_tty_driver);
- if (ret) {
- printk(KERN_ERR "dcc_tty_probe: tty_register_driver failed, %d\n", ret);
- goto err_tty_register_driver_failed;
- }
- tty_register_device(g_dcc_tty_driver, 0, NULL);
-
- register_console(&dcc_console);
- hrtimer_start(&g_dcc_timer, ktime_set(0, 0), HRTIMER_MODE_REL);
-
- return 0;
-
-err_tty_register_driver_failed:
- put_tty_driver(g_dcc_tty_driver);
- g_dcc_tty_driver = NULL;
-err_alloc_tty_driver_failed:
- return ret;
-}
-
-static void __exit dcc_tty_exit(void)
-{
- int ret;
-
- tty_unregister_device(g_dcc_tty_driver, 0);
- ret = tty_unregister_driver(g_dcc_tty_driver);
- if (ret < 0) {
- printk(KERN_ERR "dcc_tty_remove: tty_unregister_driver failed, %d\n", ret);
- } else {
- put_tty_driver(g_dcc_tty_driver);
- }
- g_dcc_tty_driver = NULL;
-}
-
-module_init(dcc_tty_init);
-module_exit(dcc_tty_exit);
-
-
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 2aef98f4fe04..35cbe3b6b596 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -509,6 +509,7 @@ struct diagchar_dev {
struct list_head cmd_reg_list;
struct mutex cmd_reg_mutex;
uint32_t cmd_reg_count;
+ struct mutex diagfwd_channel_mutex;
/* Sizes that reflect memory pool sizes */
unsigned int poolsize;
unsigned int poolsize_hdlc;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 39be6ef3735e..a5781f6db269 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -3394,6 +3394,7 @@ static int __init diagchar_init(void)
mutex_init(&driver->diag_file_mutex);
mutex_init(&driver->delayed_rsp_mutex);
mutex_init(&apps_data_mutex);
+ mutex_init(&driver->diagfwd_channel_mutex);
init_waitqueue_head(&driver->wait_q);
INIT_WORK(&(driver->diag_drain_work), diag_drain_work_fn);
INIT_WORK(&(driver->update_user_clients),
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index 22b9e05086bd..40fdcbaaf31a 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -651,9 +651,9 @@ void diagfwd_close_transport(uint8_t transport, uint8_t peripheral)
break;
default:
return;
-
}
+ mutex_lock(&driver->diagfwd_channel_mutex);
fwd_info = &early_init_info[transport][peripheral];
if (fwd_info->p_ops && fwd_info->p_ops->close)
fwd_info->p_ops->close(fwd_info->ctxt);
@@ -677,6 +677,7 @@ void diagfwd_close_transport(uint8_t transport, uint8_t peripheral)
diagfwd_late_open(dest_info);
diagfwd_cntl_open(dest_info);
init_fn(peripheral);
+ mutex_unlock(&driver->diagfwd_channel_mutex);
diagfwd_queue_read(&peripheral_info[TYPE_DATA][peripheral]);
diagfwd_queue_read(&peripheral_info[TYPE_CMD][peripheral]);
}
diff --git a/drivers/char/diag/diagfwd_socket.c b/drivers/char/diag/diagfwd_socket.c
index fd927e931414..2f9ec51a17ba 100644
--- a/drivers/char/diag/diagfwd_socket.c
+++ b/drivers/char/diag/diagfwd_socket.c
@@ -959,7 +959,9 @@ static int diag_socket_read(void *ctxt, unsigned char *buf, int buf_len)
(info->data_ready > 0) || (!info->hdl) ||
(atomic_read(&info->diag_state) == 0));
if (err) {
+ mutex_lock(&driver->diagfwd_channel_mutex);
diagfwd_channel_read_done(info->fwd_ctxt, buf, 0);
+ mutex_unlock(&driver->diagfwd_channel_mutex);
return -ERESTARTSYS;
}
@@ -971,7 +973,9 @@ static int diag_socket_read(void *ctxt, unsigned char *buf, int buf_len)
DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
"%s closing read thread. diag state is closed\n",
info->name);
+ mutex_lock(&driver->diagfwd_channel_mutex);
diagfwd_channel_read_done(info->fwd_ctxt, buf, 0);
+ mutex_unlock(&driver->diagfwd_channel_mutex);
return 0;
}
@@ -1038,8 +1042,10 @@ static int diag_socket_read(void *ctxt, unsigned char *buf, int buf_len)
if (total_recd > 0) {
DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s read total bytes: %d\n",
info->name, total_recd);
+ mutex_lock(&driver->diagfwd_channel_mutex);
err = diagfwd_channel_read_done(info->fwd_ctxt,
buf, total_recd);
+ mutex_unlock(&driver->diagfwd_channel_mutex);
if (err)
goto fail;
} else {
@@ -1052,7 +1058,9 @@ static int diag_socket_read(void *ctxt, unsigned char *buf, int buf_len)
return 0;
fail:
+ mutex_lock(&driver->diagfwd_channel_mutex);
diagfwd_channel_read_done(info->fwd_ctxt, buf, 0);
+ mutex_unlock(&driver->diagfwd_channel_mutex);
return -EIO;
}
diff --git a/drivers/char/hw_random/exynos-rng.c b/drivers/char/hw_random/exynos-rng.c
index 30cf4623184f..aa30af5f0f2b 100644
--- a/drivers/char/hw_random/exynos-rng.c
+++ b/drivers/char/hw_random/exynos-rng.c
@@ -89,6 +89,7 @@ static int exynos_read(struct hwrng *rng, void *buf,
struct exynos_rng, rng);
u32 *data = buf;
int retry = 100;
+ int ret = 4;
pm_runtime_get_sync(exynos_rng->dev);
@@ -97,17 +98,20 @@ static int exynos_read(struct hwrng *rng, void *buf,
while (!(exynos_rng_readl(exynos_rng,
EXYNOS_PRNG_STATUS_OFFSET) & PRNG_DONE) && --retry)
cpu_relax();
- if (!retry)
- return -ETIMEDOUT;
+ if (!retry) {
+ ret = -ETIMEDOUT;
+ goto out;
+ }
exynos_rng_writel(exynos_rng, PRNG_DONE, EXYNOS_PRNG_STATUS_OFFSET);
*data = exynos_rng_readl(exynos_rng, EXYNOS_PRNG_OUT1_OFFSET);
+out:
pm_runtime_mark_last_busy(exynos_rng->dev);
pm_runtime_put_sync_autosuspend(exynos_rng->dev);
- return 4;
+ return ret;
}
static int exynos_rng_probe(struct platform_device *pdev)
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index e3536da05c88..a084a4751fa9 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -3819,6 +3819,7 @@ static void handle_new_recv_msgs(ipmi_smi_t intf)
while (!list_empty(&intf->waiting_rcv_msgs)) {
smi_msg = list_entry(intf->waiting_rcv_msgs.next,
struct ipmi_smi_msg, link);
+ list_del(&smi_msg->link);
if (!run_to_completion)
spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock,
flags);
@@ -3828,11 +3829,14 @@ static void handle_new_recv_msgs(ipmi_smi_t intf)
if (rv > 0) {
/*
* To preserve message order, quit if we
- * can't handle a message.
+ * can't handle a message. Add the message
+ * back at the head, this is safe because this
+ * tasklet is the only thing that pulls the
+ * messages.
*/
+ list_add(&smi_msg->link, &intf->waiting_rcv_msgs);
break;
} else {
- list_del(&smi_msg->link);
if (rv == 0)
/* Message handled */
ipmi_free_smi_msg(smi_msg);
diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c
index 61566bcefa53..a165230e7eda 100644
--- a/drivers/clk/at91/clk-h32mx.c
+++ b/drivers/clk/at91/clk-h32mx.c
@@ -116,7 +116,7 @@ void __init of_sama5d4_clk_h32mx_setup(struct device_node *np,
h32mxclk->pmc = pmc;
clk = clk_register(NULL, &h32mxclk->hw);
- if (!clk) {
+ if (IS_ERR(clk)) {
kfree(h32mxclk);
return;
}
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index 4f9830c1b121..6029313aa995 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -890,8 +890,14 @@ static void bcm2835_pll_off(struct clk_hw *hw)
struct bcm2835_cprman *cprman = pll->cprman;
const struct bcm2835_pll_data *data = pll->data;
- cprman_write(cprman, data->cm_ctrl_reg, CM_PLL_ANARST);
- cprman_write(cprman, data->a2w_ctrl_reg, A2W_PLL_CTRL_PWRDN);
+ spin_lock(&cprman->regs_lock);
+ cprman_write(cprman, data->cm_ctrl_reg,
+ cprman_read(cprman, data->cm_ctrl_reg) |
+ CM_PLL_ANARST);
+ cprman_write(cprman, data->a2w_ctrl_reg,
+ cprman_read(cprman, data->a2w_ctrl_reg) |
+ A2W_PLL_CTRL_PWRDN);
+ spin_unlock(&cprman->regs_lock);
}
static int bcm2835_pll_on(struct clk_hw *hw)
@@ -901,6 +907,10 @@ static int bcm2835_pll_on(struct clk_hw *hw)
const struct bcm2835_pll_data *data = pll->data;
ktime_t timeout;
+ cprman_write(cprman, data->a2w_ctrl_reg,
+ cprman_read(cprman, data->a2w_ctrl_reg) &
+ ~A2W_PLL_CTRL_PWRDN);
+
/* Take the PLL out of reset. */
cprman_write(cprman, data->cm_ctrl_reg,
cprman_read(cprman, data->cm_ctrl_reg) & ~CM_PLL_ANARST);
@@ -1068,10 +1078,12 @@ static void bcm2835_pll_divider_off(struct clk_hw *hw)
struct bcm2835_cprman *cprman = divider->cprman;
const struct bcm2835_pll_divider_data *data = divider->data;
+ spin_lock(&cprman->regs_lock);
cprman_write(cprman, data->cm_reg,
(cprman_read(cprman, data->cm_reg) &
~data->load_mask) | data->hold_mask);
cprman_write(cprman, data->a2w_reg, A2W_PLL_CHANNEL_DISABLE);
+ spin_unlock(&cprman->regs_lock);
}
static int bcm2835_pll_divider_on(struct clk_hw *hw)
@@ -1080,12 +1092,14 @@ static int bcm2835_pll_divider_on(struct clk_hw *hw)
struct bcm2835_cprman *cprman = divider->cprman;
const struct bcm2835_pll_divider_data *data = divider->data;
+ spin_lock(&cprman->regs_lock);
cprman_write(cprman, data->a2w_reg,
cprman_read(cprman, data->a2w_reg) &
~A2W_PLL_CHANNEL_DISABLE);
cprman_write(cprman, data->cm_reg,
cprman_read(cprman, data->cm_reg) & ~data->hold_mask);
+ spin_unlock(&cprman->regs_lock);
return 0;
}
@@ -1167,8 +1181,9 @@ static u32 bcm2835_clock_choose_div(struct clk_hw *hw,
div &= ~unused_frac_mask;
}
- /* Clamp to the limits. */
- div = max(div, unused_frac_mask + 1);
+ /* clamp to min divider of 1 */
+ div = max_t(u32, div, 1 << CM_DIV_FRAC_BITS);
+ /* clamp to the highest possible fractional divider */
div = min_t(u32, div, GENMASK(data->int_bits + CM_DIV_FRAC_BITS - 1,
CM_DIV_FRAC_BITS - data->frac_bits));
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index 4735de0660cc..8db56919e367 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -194,7 +194,7 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
unsigned long flags)
{
struct clk *clk;
- struct clk_init_data init;
+ struct clk_init_data init = {};
struct clk_composite *composite;
struct clk_ops *clk_composite_ops;
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 3ace102a2a0a..0c83ffc22dd2 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -422,6 +422,12 @@ const struct clk_ops clk_divider_ops = {
};
EXPORT_SYMBOL_GPL(clk_divider_ops);
+const struct clk_ops clk_divider_ro_ops = {
+ .recalc_rate = clk_divider_recalc_rate,
+ .round_rate = clk_divider_round_rate,
+};
+EXPORT_SYMBOL_GPL(clk_divider_ro_ops);
+
static struct clk *_register_divider(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
@@ -430,7 +436,7 @@ static struct clk *_register_divider(struct device *dev, const char *name,
{
struct clk_divider *div;
struct clk *clk;
- struct clk_init_data init;
+ struct clk_init_data init = {};
if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
if (width + shift > 16) {
@@ -445,7 +451,10 @@ static struct clk *_register_divider(struct device *dev, const char *name,
return ERR_PTR(-ENOMEM);
init.name = name;
- init.ops = &clk_divider_ops;
+ if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
+ init.ops = &clk_divider_ro_ops;
+ else
+ init.ops = &clk_divider_ops;
init.flags = flags | CLK_IS_BASIC;
init.parent_names = (parent_name ? &parent_name: NULL);
init.num_parents = (parent_name ? 1 : 0);
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c
index 83de57aeceea..57fbc94764ff 100644
--- a/drivers/clk/clk-fixed-factor.c
+++ b/drivers/clk/clk-fixed-factor.c
@@ -75,7 +75,7 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
unsigned int mult, unsigned int div)
{
struct clk_fixed_factor *fix;
- struct clk_init_data init;
+ struct clk_init_data init = {};
struct clk *clk;
fix = kmalloc(sizeof(*fix), GFP_KERNEL);
diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c
index f85ec8d1711f..2ca7d5a8826f 100644
--- a/drivers/clk/clk-fixed-rate.c
+++ b/drivers/clk/clk-fixed-rate.c
@@ -62,7 +62,7 @@ struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev,
{
struct clk_fixed_rate *fixed;
struct clk *clk;
- struct clk_init_data init;
+ struct clk_init_data init = {};
/* allocate fixed-rate clock */
fixed = kzalloc(sizeof(*fixed), GFP_KERNEL);
diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c
index 5c4955e33f7a..f50892a74b60 100644
--- a/drivers/clk/clk-fractional-divider.c
+++ b/drivers/clk/clk-fractional-divider.c
@@ -124,7 +124,7 @@ struct clk *clk_register_fractional_divider(struct device *dev,
u8 clk_divider_flags, spinlock_t *lock)
{
struct clk_fractional_divider *fd;
- struct clk_init_data init;
+ struct clk_init_data init = {};
struct clk *clk;
fd = kzalloc(sizeof(*fd), GFP_KERNEL);
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index de0b322f5f58..eeb142534016 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -129,7 +129,7 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
{
struct clk_gate *gate;
struct clk *clk;
- struct clk_init_data init;
+ struct clk_init_data init = {};
if (clk_gate_flags & CLK_GATE_HIWORD_MASK) {
if (bit_idx > 15) {
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
index 7129c86a79db..21cb9fc0e4c4 100644
--- a/drivers/clk/clk-mux.c
+++ b/drivers/clk/clk-mux.c
@@ -124,7 +124,7 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name,
{
struct clk_mux *mux;
struct clk *clk;
- struct clk_init_data init;
+ struct clk_init_data init = {};
u8 width = 0;
if (clk_mux_flags & CLK_MUX_HIWORD_MASK) {
diff --git a/drivers/clk/clk-pwm.c b/drivers/clk/clk-pwm.c
index 328fcfcefd8c..63505a323a08 100644
--- a/drivers/clk/clk-pwm.c
+++ b/drivers/clk/clk-pwm.c
@@ -56,7 +56,7 @@ static const struct clk_ops clk_pwm_ops = {
static int clk_pwm_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
- struct clk_init_data init;
+ struct clk_init_data init = {};
struct clk_pwm *clk_pwm;
struct pwm_device *pwm;
const char *clk_name;
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 97a604755053..1eb6e32e0d51 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
* Copyright (C) 2011-2012 Linaro Ltd <mturquette@linaro.org>
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -23,6 +24,7 @@
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/clkdev.h>
+#include <linux/regulator/consumer.h>
#include "clk.h"
@@ -41,6 +43,13 @@ static HLIST_HEAD(clk_root_list);
static HLIST_HEAD(clk_orphan_list);
static LIST_HEAD(clk_notifier_list);
+struct clk_handoff_vdd {
+ struct list_head list;
+ struct clk_vdd_class *vdd_class;
+};
+
+static LIST_HEAD(clk_handoff_vdd_list);
+
/*** private data structures ***/
struct clk_core {
@@ -75,6 +84,9 @@ struct clk_core {
struct hlist_node debug_node;
#endif
struct kref ref;
+ struct clk_vdd_class *vdd_class;
+ unsigned long *rate_max;
+ int num_rate_max;
};
#define CREATE_TRACE_POINTS
@@ -243,9 +255,12 @@ static int __init clk_ignore_unused_setup(char *__unused)
}
__setup("clk_ignore_unused", clk_ignore_unused_setup);
+static int clk_unvote_vdd_level(struct clk_vdd_class *vdd_class, int level);
+
static int clk_disable_unused(void)
{
struct clk_core *core;
+ struct clk_handoff_vdd *v, *v_temp;
if (clk_ignore_unused) {
pr_warn("clk: Not disabling unused clocks\n");
@@ -266,6 +281,13 @@ static int clk_disable_unused(void)
hlist_for_each_entry(core, &clk_orphan_list, child_node)
clk_unprepare_unused_subtree(core);
+ list_for_each_entry_safe(v, v_temp, &clk_handoff_vdd_list, list) {
+ clk_unvote_vdd_level(v->vdd_class,
+ v->vdd_class->num_levels - 1);
+ list_del(&v->list);
+ kfree(v);
+ };
+
clk_prepare_unlock();
return 0;
@@ -585,6 +607,212 @@ int __clk_mux_determine_rate_closest(struct clk_hw *hw,
}
EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest);
+/*
+ * Find the voltage level required for a given clock rate.
+ */
+static int clk_find_vdd_level(struct clk_core *clk, unsigned long rate)
+{
+ int level;
+
+ for (level = 0; level < clk->num_rate_max; level++)
+ if (rate <= clk->rate_max[level])
+ break;
+
+ if (level == clk->num_rate_max) {
+ pr_err("Rate %lu for %s is greater than highest Fmax\n", rate,
+ clk->name);
+ return -EINVAL;
+ }
+
+ return level;
+}
+
+/*
+ * Update voltage level given the current votes.
+ */
+static int clk_update_vdd(struct clk_vdd_class *vdd_class)
+{
+ int level, rc = 0, i, ignore;
+ struct regulator **r = vdd_class->regulator;
+ int *uv = vdd_class->vdd_uv;
+ int n_reg = vdd_class->num_regulators;
+ int cur_lvl = vdd_class->cur_level;
+ int max_lvl = vdd_class->num_levels - 1;
+ int cur_base = cur_lvl * n_reg;
+ int new_base;
+
+ /* aggregate votes */
+ for (level = max_lvl; level > 0; level--)
+ if (vdd_class->level_votes[level])
+ break;
+
+ if (level == cur_lvl)
+ return 0;
+
+ max_lvl = max_lvl * n_reg;
+ new_base = level * n_reg;
+
+ for (i = 0; i < vdd_class->num_regulators; i++) {
+ pr_debug("Set Voltage level Min %d, Max %d\n", uv[new_base + i],
+ uv[max_lvl + i]);
+ rc = regulator_set_voltage(r[i], uv[new_base + i],
+ uv[max_lvl + i]);
+ if (rc)
+ goto set_voltage_fail;
+
+ if (cur_lvl == 0 || cur_lvl == vdd_class->num_levels)
+ rc = regulator_enable(r[i]);
+ else if (level == 0)
+ rc = regulator_disable(r[i]);
+ if (rc)
+ goto enable_disable_fail;
+ }
+
+ if (vdd_class->set_vdd && !vdd_class->num_regulators)
+ rc = vdd_class->set_vdd(vdd_class, level);
+
+ if (!rc)
+ vdd_class->cur_level = level;
+
+ return rc;
+
+enable_disable_fail:
+ regulator_set_voltage(r[i], uv[cur_base + i], uv[max_lvl + i]);
+
+set_voltage_fail:
+ for (i--; i >= 0; i--) {
+ regulator_set_voltage(r[i], uv[cur_base + i], uv[max_lvl + i]);
+ if (cur_lvl == 0 || cur_lvl == vdd_class->num_levels)
+ regulator_disable(r[i]);
+ else if (level == 0)
+ ignore = regulator_enable(r[i]);
+ }
+
+ return rc;
+}
+
+/*
+ * Vote for a voltage level.
+ */
+static int clk_vote_vdd_level(struct clk_vdd_class *vdd_class, int level)
+{
+ int rc = 0;
+
+ if (level >= vdd_class->num_levels)
+ return -EINVAL;
+
+ mutex_lock(&vdd_class->lock);
+
+ vdd_class->level_votes[level]++;
+
+ rc = clk_update_vdd(vdd_class);
+ if (rc)
+ vdd_class->level_votes[level]--;
+
+ mutex_unlock(&vdd_class->lock);
+
+ return rc;
+}
+
+/*
+ * Remove vote for a voltage level.
+ */
+static int clk_unvote_vdd_level(struct clk_vdd_class *vdd_class, int level)
+{
+ int rc = 0;
+
+ if (level >= vdd_class->num_levels)
+ return -EINVAL;
+
+ mutex_lock(&vdd_class->lock);
+
+ if (WARN(!vdd_class->level_votes[level],
+ "Reference counts are incorrect for %s level %d\n",
+ vdd_class->class_name, level))
+ goto out;
+
+ vdd_class->level_votes[level]--;
+
+ rc = clk_update_vdd(vdd_class);
+ if (rc)
+ vdd_class->level_votes[level]++;
+
+out:
+ mutex_unlock(&vdd_class->lock);
+ return rc;
+}
+
+/*
+ * Vote for a voltage level corresponding to a clock's rate.
+ */
+static int clk_vote_rate_vdd(struct clk_core *core, unsigned long rate)
+{
+ int level;
+
+ if (!core->vdd_class)
+ return 0;
+
+ level = clk_find_vdd_level(core, rate);
+ if (level < 0)
+ return level;
+
+ return clk_vote_vdd_level(core->vdd_class, level);
+}
+
+/*
+ * Remove vote for a voltage level corresponding to a clock's rate.
+ */
+static void clk_unvote_rate_vdd(struct clk_core *core, unsigned long rate)
+{
+ int level;
+
+ if (!core->vdd_class)
+ return;
+
+ level = clk_find_vdd_level(core, rate);
+ if (level < 0)
+ return;
+
+ clk_unvote_vdd_level(core->vdd_class, level);
+}
+
+static bool clk_is_rate_level_valid(struct clk_core *core, unsigned long rate)
+{
+ int level;
+
+ if (!core->vdd_class)
+ return true;
+
+ level = clk_find_vdd_level(core, rate);
+
+ return level >= 0;
+}
+
+static int clk_vdd_class_init(struct clk_vdd_class *vdd)
+{
+ struct clk_handoff_vdd *v;
+
+ list_for_each_entry(v, &clk_handoff_vdd_list, list) {
+ if (v->vdd_class == vdd)
+ return 0;
+ }
+
+ pr_debug("voting for vdd_class %s\n", vdd->class_name);
+
+ if (clk_vote_vdd_level(vdd, vdd->num_levels - 1))
+ pr_err("failed to vote for %s\n", vdd->class_name);
+
+ v = kmalloc(sizeof(*v), GFP_KERNEL);
+ if (!v)
+ return -ENOMEM;
+
+ v->vdd_class = vdd;
+
+ list_add_tail(&v->list, &clk_handoff_vdd_list);
+
+ return 0;
+}
+
/*** clk api ***/
static void clk_core_unprepare(struct clk_core *core)
@@ -608,6 +836,9 @@ static void clk_core_unprepare(struct clk_core *core)
core->ops->unprepare(core->hw);
trace_clk_unprepare_complete(core);
+
+ clk_unvote_rate_vdd(core, core->rate);
+
clk_core_unprepare(core->parent);
}
@@ -649,12 +880,19 @@ static int clk_core_prepare(struct clk_core *core)
trace_clk_prepare(core);
+ ret = clk_vote_rate_vdd(core, core->rate);
+ if (ret) {
+ clk_core_unprepare(core->parent);
+ return ret;
+ }
+
if (core->ops->prepare)
ret = core->ops->prepare(core->hw);
trace_clk_prepare_complete(core);
if (ret) {
+ clk_unvote_rate_vdd(core, core->rate);
clk_core_unprepare(core->parent);
return ret;
}
@@ -1401,6 +1639,9 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core,
top = clk_calc_new_rates(parent, best_parent_rate);
out:
+ if (!clk_is_rate_level_valid(core, rate))
+ return NULL;
+
clk_calc_subtree(core, new_rate, parent, p_index);
return top;
@@ -1485,15 +1726,26 @@ static int clk_change_rate(struct clk_core *core)
trace_clk_set_rate(core, core->new_rate);
+ /* Enforce vdd requirements for new frequency. */
+ if (core->prepare_count) {
+ rc = clk_vote_rate_vdd(core, core->new_rate);
+ if (rc)
+ goto out;
+ }
+
if (!skip_set_rate && core->ops->set_rate) {
rc = core->ops->set_rate(core->hw, core->new_rate,
best_parent_rate);
if (rc)
- goto out;
+ goto err_set_rate;
}
trace_clk_set_rate_complete(core, core->new_rate);
+ /* Release vdd requirements for old frequency. */
+ if (core->prepare_count)
+ clk_unvote_rate_vdd(core, old_rate);
+
core->rate = clk_recalc(core, best_parent_rate);
if (core->notifier_count && old_rate != core->rate)
@@ -1519,6 +1771,9 @@ static int clk_change_rate(struct clk_core *core)
return rc;
+err_set_rate:
+ if (core->prepare_count)
+ clk_unvote_rate_vdd(core, core->new_rate);
out:
trace_clk_set_rate_complete(core, core->new_rate);
@@ -2597,8 +2852,19 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
core->num_parents = hw->init->num_parents;
core->min_rate = 0;
core->max_rate = ULONG_MAX;
+ core->vdd_class = hw->init->vdd_class;
+ core->rate_max = hw->init->rate_max;
+ core->num_rate_max = hw->init->num_rate_max;
hw->core = core;
+ if (core->vdd_class) {
+ ret = clk_vdd_class_init(core->vdd_class);
+ if (ret) {
+ pr_err("Failed to initialize vdd class\n");
+ goto fail_parent_names;
+ }
+ }
+
/* allocate local copy in case parent_names is __initdata */
core->parent_names = kcalloc(core->num_parents, sizeof(char *),
GFP_KERNEL);
diff --git a/drivers/clk/imx/clk-imx35.c b/drivers/clk/imx/clk-imx35.c
index a71d24cb4c06..b0978d3b83e2 100644
--- a/drivers/clk/imx/clk-imx35.c
+++ b/drivers/clk/imx/clk-imx35.c
@@ -66,7 +66,7 @@ static const char *std_sel[] = {"ppll", "arm"};
static const char *ipg_per_sel[] = {"ahb_per_div", "arm_per_div"};
enum mx35_clks {
- ckih, ckil, mpll, ppll, mpll_075, arm, hsp, hsp_div, hsp_sel, ahb, ipg,
+ ckih, mpll, ppll, mpll_075, arm, hsp, hsp_div, hsp_sel, ahb, ipg,
arm_per_div, ahb_per_div, ipg_per, uart_sel, uart_div, esdhc_sel,
esdhc1_div, esdhc2_div, esdhc3_div, spdif_sel, spdif_div_pre,
spdif_div_post, ssi_sel, ssi1_div_pre, ssi1_div_post, ssi2_div_pre,
@@ -79,7 +79,7 @@ enum mx35_clks {
rtc_gate, rtic_gate, scc_gate, sdma_gate, spba_gate, spdif_gate,
ssi1_gate, ssi2_gate, uart1_gate, uart2_gate, uart3_gate, usbotg_gate,
wdog_gate, max_gate, admux_gate, csi_gate, csi_div, csi_sel, iim_gate,
- gpu2d_gate, clk_max
+ gpu2d_gate, ckil, clk_max
};
static struct clk *clk[clk_max];
diff --git a/drivers/clk/meson/clkc.c b/drivers/clk/meson/clkc.c
index c83ae1367abc..d920d410b51d 100644
--- a/drivers/clk/meson/clkc.c
+++ b/drivers/clk/meson/clkc.c
@@ -198,7 +198,7 @@ meson_clk_register_fixed_rate(const struct clk_conf *clk_conf,
}
void __init meson_clk_register_clks(const struct clk_conf *clk_confs,
- size_t nr_confs,
+ unsigned int nr_confs,
void __iomem *clk_base)
{
unsigned int i;
diff --git a/drivers/clk/msm/clock-debug.c b/drivers/clk/msm/clock-debug.c
index d0ff821eb203..00a86ba55171 100644
--- a/drivers/clk/msm/clock-debug.c
+++ b/drivers/clk/msm/clock-debug.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2014, 2016, 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
@@ -355,8 +355,12 @@ static int trace_clocks_show(struct seq_file *m, void *unused)
return 1;
}
list_for_each_entry(c, &clk_list, list) {
+ int vlevel = 0;
+
+ if (c->num_fmax)
+ vlevel = find_vdd_level(c, c->rate);
trace_clock_state(c->dbg_name, c->prepare_count, c->count,
- c->rate);
+ c->rate, vlevel);
total_cnt++;
}
mutex_unlock(&clk_list_lock);
diff --git a/drivers/clk/msm/clock-gcc-cobalt.c b/drivers/clk/msm/clock-gcc-cobalt.c
index 46e791b3cb99..f2a3f7402f67 100644
--- a/drivers/clk/msm/clock-gcc-cobalt.c
+++ b/drivers/clk/msm/clock-gcc-cobalt.c
@@ -1707,17 +1707,6 @@ static struct branch_clk gcc_gpu_bimc_gfx_clk = {
},
};
-static struct branch_clk gcc_gpu_bimc_gfx_src_clk = {
- .cbcr_reg = GCC_GPU_BIMC_GFX_SRC_CBCR,
- .has_sibling = 1,
- .base = &virt_base,
- .c = {
- .dbg_name = "gcc_gpu_bimc_gfx_src_clk",
- .ops = &clk_ops_branch,
- CLK_INIT(gcc_gpu_bimc_gfx_src_clk.c),
- },
-};
-
static struct branch_clk gcc_gpu_cfg_ahb_clk = {
.cbcr_reg = GCC_GPU_CFG_AHB_CBCR,
.has_sibling = 1,
@@ -1731,17 +1720,6 @@ static struct branch_clk gcc_gpu_cfg_ahb_clk = {
},
};
-static struct branch_clk gcc_gpu_snoc_dvm_gfx_clk = {
- .cbcr_reg = GCC_GPU_SNOC_DVM_GFX_CBCR,
- .has_sibling = 1,
- .base = &virt_base,
- .c = {
- .dbg_name = "gcc_gpu_snoc_dvm_gfx_clk",
- .ops = &clk_ops_branch,
- CLK_INIT(gcc_gpu_snoc_dvm_gfx_clk.c),
- },
-};
-
static struct branch_clk gcc_gpu_iref_clk = {
.cbcr_reg = GCC_GPU_IREF_EN,
.has_sibling = 1,
@@ -2454,7 +2432,6 @@ static struct mux_clk gcc_debug_mux = {
{ &gcc_mss_mnoc_bimc_axi_clk.c, 0x0120 },
{ &gcc_mss_snoc_axi_clk.c, 0x0123 },
{ &gcc_gpu_cfg_ahb_clk.c, 0x013b },
- { &gcc_gpu_bimc_gfx_src_clk.c, 0x013e },
{ &gcc_gpu_bimc_gfx_clk.c, 0x013f },
{ &gcc_qspi_ahb_clk.c, 0x0156 },
{ &gcc_qspi_ref_clk.c, 0x0157 },
@@ -2649,9 +2626,7 @@ static struct clk_lookup msm_clocks_gcc_cobalt[] = {
CLK_LIST(gcc_gp2_clk),
CLK_LIST(gcc_gp3_clk),
CLK_LIST(gcc_gpu_bimc_gfx_clk),
- CLK_LIST(gcc_gpu_bimc_gfx_src_clk),
CLK_LIST(gcc_gpu_cfg_ahb_clk),
- CLK_LIST(gcc_gpu_snoc_dvm_gfx_clk),
CLK_LIST(gcc_gpu_iref_clk),
CLK_LIST(gcc_hmss_ahb_clk),
CLK_LIST(gcc_hmss_dvm_bus_clk),
diff --git a/drivers/clk/msm/clock-gpu-cobalt.c b/drivers/clk/msm/clock-gpu-cobalt.c
index 9d93351a083e..7cec9be1f42c 100644
--- a/drivers/clk/msm/clock-gpu-cobalt.c
+++ b/drivers/clk/msm/clock-gpu-cobalt.c
@@ -173,6 +173,7 @@ static struct clk_freq_tbl ftbl_gfx3d_clk_src_v2[] = {
F_SLEW( 515000000, 1030000000, gpu_pll0_pll_out_even, 1, 0, 0),
F_SLEW( 596000000, 1192000000, gpu_pll0_pll_out_even, 1, 0, 0),
F_SLEW( 670000000, 1340000000, gpu_pll0_pll_out_even, 1, 0, 0),
+ F_SLEW( 710000000, 1420000000, gpu_pll0_pll_out_even, 1, 0, 0),
F_END
};
@@ -611,7 +612,7 @@ static void msm_gfxcc_hamster_fixup(void)
static void msm_gfxcc_cobalt_v2_fixup(void)
{
- gpu_pll0_pll.c.fmax[VDD_DIG_MIN] = 1340000500;
+ gpu_pll0_pll.c.fmax[VDD_DIG_MIN] = 1420000500;
gfx3d_clk_src.freq_tbl = ftbl_gfx3d_clk_src_v2;
}
diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c
index a119c0b27321..3e45aee1c0f7 100644
--- a/drivers/clk/msm/clock-osm.c
+++ b/drivers/clk/msm/clock-osm.c
@@ -49,6 +49,7 @@ enum clk_osm_bases {
OSM_BASE,
PLL_BASE,
EFUSE_BASE,
+ ACD_BASE,
NUM_BASES,
};
@@ -80,6 +81,7 @@ enum clk_osm_trace_packet_id {
#define MEM_ACC_SEQ_REG_VAL_START(n) (SEQ_REG(60 + (n)))
#define SEQ_REG1_MSMCOBALT_V2 0x1048
#define VERSION_REG 0x0
+#define VERSION_1P1 0x00010100
#define OSM_TABLE_SIZE 40
#define MAX_CLUSTER_CNT 2
@@ -203,7 +205,6 @@ enum clk_osm_trace_packet_id {
#define TRACE_CTRL_ENABLE 1
#define TRACE_CTRL_DISABLE 0
#define TRACE_CTRL_ENABLE_WDOG_STATUS BIT(30)
-#define TRACE_CTRL_ENABLE_WDOG_STATUS_MASK BIT(30)
#define TRACE_CTRL_PACKET_TYPE_MASK BVAL(2, 1, 3)
#define TRACE_CTRL_PACKET_TYPE_SHIFT 1
#define TRACE_CTRL_PERIODIC_TRACE_EN_MASK BIT(3)
@@ -228,11 +229,43 @@ enum clk_osm_trace_packet_id {
#define MSMCOBALTV2_PWRCL_BOOT_RATE 1555200000
#define MSMCOBALTV2_PERFCL_BOOT_RATE 1728000000
+/* ACD registers */
+#define ACD_HW_VERSION 0x0
+#define ACDCR 0x4
+#define ACDTD 0x8
+#define ACDSSCR 0x28
+#define ACD_EXTINT_CFG 0x30
+#define ACD_DCVS_SW 0x34
+#define ACD_GFMUX_CFG 0x3c
+#define ACD_READOUT_CFG 0x48
+#define ACD_AUTOXFER_CFG 0x80
+#define ACD_AUTOXFER 0x84
+#define ACD_AUTOXFER_CTL 0x88
+#define ACD_AUTOXFER_STATUS 0x8c
+#define ACD_WRITE_CTL 0x90
+#define ACD_WRITE_STATUS 0x94
+#define ACD_READOUT 0x98
+
+#define ACD_MASTER_ONLY_REG_ADDR 0x80
+#define ACD_WRITE_CTL_UPDATE_EN BIT(0)
+#define ACD_WRITE_CTL_SELECT_SHIFT 1
+#define ACD_GFMUX_CFG_SELECT BIT(0)
+#define ACD_AUTOXFER_START_CLEAR 0
+#define ACD_AUTOXFER_START_SET BIT(0)
+#define AUTO_XFER_DONE_MASK BIT(0)
+#define ACD_DCVS_SW_DCVS_IN_PRGR_SET BIT(0)
+#define ACD_DCVS_SW_DCVS_IN_PRGR_CLEAR 0
+#define ACD_LOCAL_TRANSFER_TIMEOUT_NS 500
+
static void __iomem *virt_base;
static void __iomem *debug_base;
#define lmh_lite_clk_src_source_val 1
+#define ACD_REG_RELATIVE_ADDR(addr) (addr / 4)
+#define ACD_REG_RELATIVE_ADDR_BITMASK(addr) \
+ (1 << (ACD_REG_RELATIVE_ADDR(addr)))
+
#define FIXDIV(div) (div ? (2 * (div) - 1) : (0))
#define F(f, s, div, m, n) \
@@ -317,6 +350,7 @@ struct clk_osm {
unsigned long pbases[NUM_BASES];
spinlock_t lock;
+ u32 version;
u32 cpu_reg_mask;
u32 num_entries;
u32 cluster_num;
@@ -340,6 +374,14 @@ struct clk_osm {
u32 apm_ctrl_status;
u32 osm_clk_rate;
u32 xo_clk_rate;
+ u32 acd_td;
+ u32 acd_cr;
+ u32 acd_sscr;
+ u32 acd_extint0_cfg;
+ u32 acd_extint1_cfg;
+ u32 acd_autoxfer_ctl;
+ u32 acd_debugfs_addr;
+ bool acd_init;
bool secure_init;
bool red_fsm_en;
bool boost_fsm_en;
@@ -354,6 +396,7 @@ struct clk_osm {
struct notifier_block panic_notifier;
u32 trace_periodic_timer;
bool trace_en;
+ bool wdog_trace_en;
};
static bool msmcobalt_v1;
@@ -392,6 +435,161 @@ static inline int clk_osm_mb(struct clk_osm *c, int base)
return readl_relaxed_no_log((char *)c->vbases[base] + VERSION_REG);
}
+static inline int clk_osm_acd_mb(struct clk_osm *c)
+{
+ return readl_relaxed_no_log((char *)c->vbases[ACD_BASE] +
+ ACD_HW_VERSION);
+}
+
+static inline void clk_osm_acd_master_write_reg(struct clk_osm *c,
+ u32 val, u32 offset)
+{
+ writel_relaxed(val, (char *)c->vbases[ACD_BASE] + offset);
+}
+
+static int clk_osm_acd_local_read_reg(struct clk_osm *c, u32 offset)
+{
+ u32 reg = 0;
+ int timeout;
+
+ if (offset >= ACD_MASTER_ONLY_REG_ADDR) {
+ pr_err("ACD register at offset=0x%x not locally readable\n",
+ offset);
+ return -EINVAL;
+ }
+
+ /* Set select field in read control register */
+ writel_relaxed(ACD_REG_RELATIVE_ADDR(offset),
+ (char *)c->vbases[ACD_BASE] + ACD_READOUT_CFG);
+
+ /* Clear write control register */
+ writel_relaxed(reg, (char *)c->vbases[ACD_BASE] + ACD_WRITE_CTL);
+
+ /* Set select and update_en fields in write control register */
+ reg = (ACD_REG_RELATIVE_ADDR(ACD_READOUT_CFG)
+ << ACD_WRITE_CTL_SELECT_SHIFT)
+ | ACD_WRITE_CTL_UPDATE_EN;
+ writel_relaxed(reg, (char *)c->vbases[ACD_BASE] + ACD_WRITE_CTL);
+
+ /* Ensure writes complete before polling */
+ clk_osm_acd_mb(c);
+
+ /* Poll write status register */
+ for (timeout = ACD_LOCAL_TRANSFER_TIMEOUT_NS; timeout > 0;
+ timeout -= 100) {
+ reg = readl_relaxed((char *)c->vbases[ACD_BASE]
+ + ACD_WRITE_STATUS);
+ if ((reg & (ACD_REG_RELATIVE_ADDR_BITMASK(ACD_READOUT_CFG))))
+ break;
+ ndelay(100);
+ }
+
+ if (!timeout) {
+ pr_err("local read timed out, offset=0x%x status=0x%x\n",
+ offset, reg);
+ return -ETIMEDOUT;
+ }
+
+ reg = readl_relaxed((char *)c->vbases[ACD_BASE]
+ + ACD_READOUT);
+ return reg;
+}
+
+static int clk_osm_acd_local_write_reg(struct clk_osm *c, u32 val, u32 offset)
+{
+ u32 reg = 0;
+ int timeout;
+
+ if (offset >= ACD_MASTER_ONLY_REG_ADDR) {
+ pr_err("ACD register at offset=0x%x not transferrable\n",
+ offset);
+ return -EINVAL;
+ }
+
+ /* Clear write control register */
+ writel_relaxed(reg, (char *)c->vbases[ACD_BASE] + ACD_WRITE_CTL);
+
+ /* Set select and update_en fields in write control register */
+ reg = (ACD_REG_RELATIVE_ADDR(offset) << ACD_WRITE_CTL_SELECT_SHIFT)
+ | ACD_WRITE_CTL_UPDATE_EN;
+ writel_relaxed(reg, (char *)c->vbases[ACD_BASE] + ACD_WRITE_CTL);
+
+ /* Ensure writes complete before polling */
+ clk_osm_acd_mb(c);
+
+ /* Poll write status register */
+ for (timeout = ACD_LOCAL_TRANSFER_TIMEOUT_NS; timeout > 0;
+ timeout -= 100) {
+ reg = readl_relaxed((char *)c->vbases[ACD_BASE]
+ + ACD_WRITE_STATUS);
+ if ((reg & (ACD_REG_RELATIVE_ADDR_BITMASK(offset))))
+ break;
+ ndelay(100);
+ }
+
+ if (!timeout) {
+ pr_err("local write timed out, offset=0x%x val=0x%x status=0x%x\n",
+ offset, val, reg);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int clk_osm_acd_master_write_through_reg(struct clk_osm *c,
+ u32 val, u32 offset)
+{
+ writel_relaxed(val, (char *)c->vbases[ACD_BASE] + offset);
+
+ /* Ensure writes complete before transfer to local copy */
+ clk_osm_acd_mb(c);
+
+ return clk_osm_acd_local_write_reg(c, val, offset);
+}
+
+static int clk_osm_acd_auto_local_write_reg(struct clk_osm *c, u32 mask)
+{
+ u32 numregs, bitmask = mask;
+ u32 reg = 0;
+ int timeout;
+
+ /* count number of bits set in register mask */
+ for (numregs = 0; bitmask; numregs++)
+ bitmask &= bitmask - 1;
+
+ /* Program auto-transfter mask */
+ writel_relaxed(mask, (char *)c->vbases[ACD_BASE] + ACD_AUTOXFER_CFG);
+
+ /* Clear start field in auto-transfer register */
+ writel_relaxed(ACD_AUTOXFER_START_CLEAR,
+ (char *)c->vbases[ACD_BASE] + ACD_AUTOXFER);
+
+ /* Set start field in auto-transfer register */
+ writel_relaxed(ACD_AUTOXFER_START_SET,
+ (char *)c->vbases[ACD_BASE] + ACD_AUTOXFER);
+
+ /* Ensure writes complete before polling */
+ clk_osm_acd_mb(c);
+
+ /* Poll auto-transfer status register */
+ for (timeout = ACD_LOCAL_TRANSFER_TIMEOUT_NS * numregs;
+ timeout > 0; timeout -= 100) {
+ reg = readl_relaxed((char *)c->vbases[ACD_BASE]
+ + ACD_AUTOXFER_STATUS);
+ if (reg & AUTO_XFER_DONE_MASK)
+ break;
+ ndelay(100);
+ }
+
+ if (!timeout) {
+ pr_err("local register auto-transfer timed out, mask=0x%x registers=%d status=0x%x\n",
+ mask, numregs, reg);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
static inline int clk_osm_count_ns(struct clk_osm *c, u64 nsec)
{
u64 temp;
@@ -811,6 +1009,74 @@ static int clk_osm_parse_dt_configs(struct platform_device *pdev)
LLM_SW_OVERRIDE_CNT + i,
&perfcl_clk.llm_sw_overr[i]);
+ if (pwrcl_clk.acd_init || perfcl_clk.acd_init) {
+ rc = of_property_read_u32_array(of, "qcom,acdtd-val",
+ array, MAX_CLUSTER_CNT);
+ if (rc) {
+ dev_err(&pdev->dev, "unable to find qcom,acdtd-val property, rc=%d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ pwrcl_clk.acd_td = array[pwrcl_clk.cluster_num];
+ perfcl_clk.acd_td = array[perfcl_clk.cluster_num];
+
+ rc = of_property_read_u32_array(of, "qcom,acdcr-val",
+ array, MAX_CLUSTER_CNT);
+ if (rc) {
+ dev_err(&pdev->dev, "unable to find qcom,acdcr-val property, rc=%d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ pwrcl_clk.acd_cr = array[pwrcl_clk.cluster_num];
+ perfcl_clk.acd_cr = array[perfcl_clk.cluster_num];
+
+ rc = of_property_read_u32_array(of, "qcom,acdsscr-val",
+ array, MAX_CLUSTER_CNT);
+ if (rc) {
+ dev_err(&pdev->dev, "unable to find qcom,acdsscr-val property, rc=%d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ pwrcl_clk.acd_sscr = array[pwrcl_clk.cluster_num];
+ perfcl_clk.acd_sscr = array[perfcl_clk.cluster_num];
+
+ rc = of_property_read_u32_array(of, "qcom,acdextint0-val",
+ array, MAX_CLUSTER_CNT);
+ if (rc) {
+ dev_err(&pdev->dev, "unable to find qcom,acdextint0-val property, rc=%d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ pwrcl_clk.acd_extint0_cfg = array[pwrcl_clk.cluster_num];
+ perfcl_clk.acd_extint0_cfg = array[perfcl_clk.cluster_num];
+
+ rc = of_property_read_u32_array(of, "qcom,acdextint1-val",
+ array, MAX_CLUSTER_CNT);
+ if (rc) {
+ dev_err(&pdev->dev, "unable to find qcom,acdextint1-val property, rc=%d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ pwrcl_clk.acd_extint1_cfg = array[pwrcl_clk.cluster_num];
+ perfcl_clk.acd_extint1_cfg = array[perfcl_clk.cluster_num];
+
+ rc = of_property_read_u32_array(of, "qcom,acdautoxfer-val",
+ array, MAX_CLUSTER_CNT);
+ if (rc) {
+ dev_err(&pdev->dev, "unable to find qcom,acdautoxfer-val property, rc=%d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ pwrcl_clk.acd_autoxfer_ctl = array[pwrcl_clk.cluster_num];
+ perfcl_clk.acd_autoxfer_ctl = array[perfcl_clk.cluster_num];
+ }
+
rc = of_property_read_u32(of, "qcom,xo-clk-rate",
&pwrcl_clk.xo_clk_rate);
if (rc) {
@@ -1035,6 +1301,40 @@ static int clk_osm_resources_init(struct platform_device *pdev)
perfcl_clk.vbases[EFUSE_BASE] = vbase;
}
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "pwrcl_acd");
+ if (res) {
+ pbase = (unsigned long)res->start;
+ vbase = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!vbase) {
+ dev_err(&pdev->dev, "Unable to map in pwrcl_acd base\n");
+ return -ENOMEM;
+ }
+ pwrcl_clk.pbases[ACD_BASE] = pbase;
+ pwrcl_clk.vbases[ACD_BASE] = vbase;
+ pwrcl_clk.acd_init = true;
+ } else {
+ pwrcl_clk.acd_init = false;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "perfcl_acd");
+ if (res) {
+ pbase = (unsigned long)res->start;
+ vbase = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!vbase) {
+ dev_err(&pdev->dev, "Unable to map in perfcl_acd base\n");
+ return -ENOMEM;
+ }
+ perfcl_clk.pbases[ACD_BASE] = pbase;
+ perfcl_clk.vbases[ACD_BASE] = vbase;
+ perfcl_clk.acd_init = true;
+ } else {
+ perfcl_clk.acd_init = false;
+ }
+
vdd_pwrcl = devm_regulator_get(&pdev->dev, "vdd-pwrcl");
if (IS_ERR(vdd_pwrcl)) {
rc = PTR_ERR(vdd_pwrcl);
@@ -2145,6 +2445,36 @@ DEFINE_SIMPLE_ATTRIBUTE(debugfs_trace_enable_fops,
debugfs_set_trace_enable,
"%llu\n");
+static int debugfs_get_wdog_trace(void *data, u64 *val)
+{
+ struct clk_osm *c = data;
+
+ *val = c->wdog_trace_en;
+ return 0;
+}
+
+static int debugfs_set_wdog_trace(void *data, u64 val)
+{
+ struct clk_osm *c = data;
+ int regval;
+
+ if (c->version >= VERSION_1P1) {
+ regval = clk_osm_read_reg(c, TRACE_CTRL);
+ regval = val ? regval | TRACE_CTRL_ENABLE_WDOG_STATUS :
+ regval & ~TRACE_CTRL_ENABLE_WDOG_STATUS;
+ clk_osm_write_reg(c, regval, TRACE_CTRL);
+ c->wdog_trace_en = val ? true : false;
+ } else {
+ pr_info("wdog status registers enabled by default\n");
+ }
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(debugfs_trace_wdog_enable_fops,
+ debugfs_get_wdog_trace,
+ debugfs_set_wdog_trace,
+ "%llu\n");
+
#define MAX_DEBUG_BUF_LEN 15
static DEFINE_MUTEX(debug_buf_mutex);
@@ -2370,6 +2700,55 @@ DEFINE_SIMPLE_ATTRIBUTE(debugfs_perf_state_deviation_corrected_irq_fops,
debugfs_set_perf_state_deviation_corrected_irq,
"%llu\n");
+static int debugfs_get_debug_reg(void *data, u64 *val)
+{
+ struct clk_osm *c = data;
+
+ if (c->acd_debugfs_addr >= ACD_MASTER_ONLY_REG_ADDR)
+ *val = readl_relaxed((char *)c->vbases[ACD_BASE] +
+ c->acd_debugfs_addr);
+ else
+ *val = clk_osm_acd_local_read_reg(c, c->acd_debugfs_addr);
+ return 0;
+}
+
+static int debugfs_set_debug_reg(void *data, u64 val)
+{
+ struct clk_osm *c = data;
+
+ if (c->acd_debugfs_addr >= ACD_MASTER_ONLY_REG_ADDR)
+ clk_osm_acd_master_write_reg(c, val, c->acd_debugfs_addr);
+ else
+ clk_osm_acd_master_write_through_reg(c, val,
+ c->acd_debugfs_addr);
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(debugfs_acd_debug_reg_fops,
+ debugfs_get_debug_reg,
+ debugfs_set_debug_reg,
+ "0x%llx\n");
+
+static int debugfs_get_debug_reg_addr(void *data, u64 *val)
+{
+ struct clk_osm *c = data;
+
+ *val = c->acd_debugfs_addr;
+ return 0;
+}
+
+static int debugfs_set_debug_reg_addr(void *data, u64 val)
+{
+ struct clk_osm *c = data;
+
+ c->acd_debugfs_addr = val;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(debugfs_acd_debug_reg_addr_fops,
+ debugfs_get_debug_reg_addr,
+ debugfs_set_debug_reg_addr,
+ "%llu\n");
+
static void populate_debugfs_dir(struct clk_osm *c)
{
struct dentry *temp;
@@ -2416,6 +2795,15 @@ static void populate_debugfs_dir(struct clk_osm *c)
goto exit;
}
+ temp = debugfs_create_file("wdog_trace_enable",
+ S_IRUGO | S_IWUSR,
+ c->debugfs, c,
+ &debugfs_trace_wdog_enable_fops);
+ if (IS_ERR_OR_NULL(temp)) {
+ pr_err("debugfs_trace_wdog_enable_fops debugfs file creation failed\n");
+ goto exit;
+ }
+
temp = debugfs_create_file("trace_enable",
S_IRUGO | S_IWUSR,
c->debugfs, c,
@@ -2452,6 +2840,24 @@ static void populate_debugfs_dir(struct clk_osm *c)
goto exit;
}
+ temp = debugfs_create_file("acd_debug_reg",
+ S_IRUGO | S_IWUSR,
+ c->debugfs, c,
+ &debugfs_acd_debug_reg_fops);
+ if (IS_ERR_OR_NULL(temp)) {
+ pr_err("debugfs_acd_debug_reg_fops debugfs file creation failed\n");
+ goto exit;
+ }
+
+ temp = debugfs_create_file("acd_debug_reg_addr",
+ S_IRUGO | S_IWUSR,
+ c->debugfs, c,
+ &debugfs_acd_debug_reg_addr_fops);
+ if (IS_ERR_OR_NULL(temp)) {
+ pr_err("debugfs_acd_debug_reg_addr_fops debugfs file creation failed\n");
+ goto exit;
+ }
+
exit:
if (IS_ERR_OR_NULL(temp))
debugfs_remove_recursive(c->debugfs);
@@ -2496,6 +2902,81 @@ static int clk_osm_panic_callback(struct notifier_block *nfb,
return NOTIFY_OK;
}
+static int clk_osm_acd_init(struct clk_osm *c)
+{
+
+ int rc = 0;
+ u32 auto_xfer_mask = 0;
+
+ if (!c->acd_init)
+ return 0;
+
+ c->acd_debugfs_addr = ACD_HW_VERSION;
+
+ /* Program ACD tunable-length delay register */
+ clk_osm_acd_master_write_reg(c, c->acd_td, ACDTD);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDTD);
+
+ /* Program ACD control register */
+ clk_osm_acd_master_write_reg(c, c->acd_cr, ACDCR);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDCR);
+
+ /* Program ACD soft start control register */
+ clk_osm_acd_master_write_reg(c, c->acd_sscr, ACDSSCR);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDSSCR);
+
+ /* Program initial ACD external interface configuration register */
+ clk_osm_acd_master_write_reg(c, c->acd_extint0_cfg, ACD_EXTINT_CFG);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_EXTINT_CFG);
+
+ /* Program ACD auto-register transfer control register */
+ clk_osm_acd_master_write_reg(c, c->acd_autoxfer_ctl, ACD_AUTOXFER_CTL);
+
+ /* Ensure writes complete before transfers to local copy */
+ clk_osm_acd_mb(c);
+
+ /* Transfer master copies */
+ rc = clk_osm_acd_auto_local_write_reg(c, auto_xfer_mask);
+ if (rc)
+ return rc;
+
+ /* Switch CPUSS clock source to ACD clock */
+ rc = clk_osm_acd_master_write_through_reg(c, ACD_GFMUX_CFG_SELECT,
+ ACD_GFMUX_CFG);
+ if (rc)
+ return rc;
+
+ /* Program ACD_DCVS_SW */
+ rc = clk_osm_acd_master_write_through_reg(c,
+ ACD_DCVS_SW_DCVS_IN_PRGR_SET,
+ ACD_DCVS_SW);
+ if (rc)
+ return rc;
+
+ rc = clk_osm_acd_master_write_through_reg(c,
+ ACD_DCVS_SW_DCVS_IN_PRGR_CLEAR,
+ ACD_DCVS_SW);
+ if (rc)
+ return rc;
+
+ udelay(1);
+
+ /* Program final ACD external interface configuration register */
+ rc = clk_osm_acd_master_write_through_reg(c, c->acd_extint1_cfg,
+ ACD_EXTINT_CFG);
+ if (rc)
+ return rc;
+
+ /*
+ * ACDCR, ACDTD, ACDSSCR, ACD_EXTINT_CFG, ACD_GFMUX_CFG
+ * must be copied from master to local copy on PC exit.
+ */
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_GFMUX_CFG);
+ clk_osm_acd_master_write_reg(c, auto_xfer_mask, ACD_AUTOXFER_CFG);
+
+ return 0;
+}
+
static unsigned long init_rate = 300000000;
static unsigned long osm_clk_init_rate = 200000000;
@@ -2676,6 +3157,17 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev)
clk_osm_setup_cluster_pll(&perfcl_clk);
}
+ rc = clk_osm_acd_init(&pwrcl_clk);
+ if (rc) {
+ pr_err("failed to initialize ACD for pwrcl, rc=%d\n", rc);
+ return rc;
+ }
+ rc = clk_osm_acd_init(&perfcl_clk);
+ if (rc) {
+ pr_err("failed to initialize ACD for perfcl, rc=%d\n", rc);
+ return rc;
+ }
+
spin_lock_init(&pwrcl_clk.lock);
spin_lock_init(&perfcl_clk.lock);
@@ -2694,18 +3186,6 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev)
return rc;
}
- if (msmcobalt_v2) {
- /* Enable OSM WDOG registers */
- clk_osm_masked_write_reg(&pwrcl_clk,
- TRACE_CTRL_ENABLE_WDOG_STATUS,
- TRACE_CTRL,
- TRACE_CTRL_ENABLE_WDOG_STATUS_MASK);
- clk_osm_masked_write_reg(&perfcl_clk,
- TRACE_CTRL_ENABLE_WDOG_STATUS,
- TRACE_CTRL,
- TRACE_CTRL_ENABLE_WDOG_STATUS_MASK);
- }
-
/*
* The hmss_gpll0 clock runs at 300 MHz. Ensure it is at the correct
* frequency before enabling OSM. LUT index 0 is always sourced from
@@ -2719,33 +3199,26 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev)
}
clk_prepare_enable(&sys_apcsaux_clk_gcc.c);
- /* Set boot rate */
- rc = clk_set_rate(&pwrcl_clk.c, msmcobalt_v1 ?
- MSMCOBALTV1_PWRCL_BOOT_RATE :
- MSMCOBALTV2_PWRCL_BOOT_RATE);
+ rc = clk_set_rate(&osm_clk_src.c, osm_clk_init_rate);
if (rc) {
- dev_err(&pdev->dev, "Unable to set boot rate on pwr cluster, rc=%d\n",
+ dev_err(&pdev->dev, "Unable to set init rate on osm_clk, rc=%d\n",
rc);
- clk_disable_unprepare(&sys_apcsaux_clk_gcc.c);
- return rc;
+ goto exit2;
}
- rc = clk_set_rate(&perfcl_clk.c, msmcobalt_v1 ?
- MSMCOBALTV1_PERFCL_BOOT_RATE :
- MSMCOBALTV2_PERFCL_BOOT_RATE);
+ /* Make sure index zero is selected */
+ rc = clk_set_rate(&pwrcl_clk.c, init_rate);
if (rc) {
- dev_err(&pdev->dev, "Unable to set boot rate on perf cluster, rc=%d\n",
+ dev_err(&pdev->dev, "Unable to set init rate on pwr cluster, rc=%d\n",
rc);
- clk_disable_unprepare(&sys_apcsaux_clk_gcc.c);
- return rc;
+ goto exit2;
}
- rc = clk_set_rate(&osm_clk_src.c, osm_clk_init_rate);
+ rc = clk_set_rate(&perfcl_clk.c, init_rate);
if (rc) {
- dev_err(&pdev->dev, "Unable to set init rate on osm_clk, rc=%d\n",
+ dev_err(&pdev->dev, "Unable to set init rate on perf cluster, rc=%d\n",
rc);
- clk_disable_unprepare(&sys_apcsaux_clk_gcc.c);
- return rc;
+ goto exit2;
}
get_online_cpus();
@@ -2756,6 +3229,28 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev)
"Failed to enable clock for cpu %d\n", cpu);
}
+ /* Set final boot rate */
+ rc = clk_set_rate(&pwrcl_clk.c, msmcobalt_v1 ?
+ MSMCOBALTV1_PWRCL_BOOT_RATE :
+ MSMCOBALTV2_PWRCL_BOOT_RATE);
+ if (rc) {
+ dev_err(&pdev->dev, "Unable to set boot rate on pwr cluster, rc=%d\n",
+ rc);
+ goto exit2;
+ }
+
+ rc = clk_set_rate(&perfcl_clk.c, msmcobalt_v1 ?
+ MSMCOBALTV1_PERFCL_BOOT_RATE :
+ MSMCOBALTV2_PERFCL_BOOT_RATE);
+ if (rc) {
+ dev_err(&pdev->dev, "Unable to set boot rate on perf cluster, rc=%d\n",
+ rc);
+ goto exit2;
+ }
+
+ pwrcl_clk.version = clk_osm_read_reg(&pwrcl_clk, VERSION_REG);
+ perfcl_clk.version = clk_osm_read_reg(&perfcl_clk, VERSION_REG);
+
populate_opp_table(pdev);
populate_debugfs_dir(&pwrcl_clk);
populate_debugfs_dir(&perfcl_clk);
@@ -2769,6 +3264,8 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev)
return 0;
+exit2:
+ clk_disable_unprepare(&sys_apcsaux_clk_gcc.c);
exit:
dev_err(&pdev->dev, "OSM driver failed to initialize, rc=%d\n",
rc);
diff --git a/drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c b/drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c
index a574a9cd2b5a..93bbcf5d40f5 100644
--- a/drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c
+++ b/drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c
@@ -275,7 +275,7 @@ int dp_config_vco_rate(struct dp_pll_vco_clk *vco, unsigned long rate)
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00);
MDSS_PLL_REG_W(dp_res->pll_base,
- QSERDES_COM_DIV_FRAC_START3_MODE0, 0xa0);
+ QSERDES_COM_DIV_FRAC_START3_MODE0, 0x0a);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_CMN_CONFIG, 0x12);
MDSS_PLL_REG_W(dp_res->pll_base,
@@ -733,7 +733,7 @@ unsigned long dp_vco_get_rate(struct clk *c)
{
struct dp_pll_vco_clk *vco = mdss_dp_to_vco_clk(c);
int rc;
- u32 div, hsclk_div, link2xclk_div;
+ u32 div, hsclk_div, link2xclk_div = 0;
u64 vco_rate;
struct mdss_pll_resources *pll = vco->priv;
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c b/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c
index 4b2d8bba0940..299934c86d05 100644
--- a/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c
@@ -205,18 +205,34 @@ static void dsi_pll_calc_dec_frac(struct dsi_pll_cobalt *pll,
struct dsi_pll_regs *regs = &pll->reg_setup;
u64 target_freq;
u64 fref = rsc->vco_ref_clk_rate;
- u32 computed_output_div, div_log;
+ u32 computed_output_div, div_log = 0;
u64 pll_freq;
u64 divider;
u64 dec, dec_multiple;
u32 frac;
u64 multiplier;
+ u32 i;
target_freq = rsc->vco_current_rate;
pr_debug("target_freq = %llu\n", target_freq);
if (config->div_override) {
computed_output_div = config->output_div;
+
+ /*
+ * Computed_output_div = 2 ^ div_log
+ * To get div_log from output div just get the index of the
+ * 1 bit in the value.
+ * div_log ranges from 0-3. so check the 4 lsbs
+ */
+
+ for (i = 0; i < 4; i++) {
+ if (computed_output_div & (1 << i)) {
+ div_log = i;
+ break;
+ }
+ }
+
} else {
if (target_freq < MHZ_375) {
computed_output_div = 8;
diff --git a/drivers/clk/nxp/clk-lpc18xx-ccu.c b/drivers/clk/nxp/clk-lpc18xx-ccu.c
index 13aabbb3acbe..558da89555af 100644
--- a/drivers/clk/nxp/clk-lpc18xx-ccu.c
+++ b/drivers/clk/nxp/clk-lpc18xx-ccu.c
@@ -222,7 +222,7 @@ static void lpc18xx_ccu_register_branch_gate_div(struct lpc18xx_clk_branch *bran
div->width = 1;
div_hw = &div->hw;
- div_ops = &clk_divider_ops;
+ div_ops = &clk_divider_ro_ops;
}
branch->gate.reg = branch->offset + reg_base;
diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 4d46d65898d1..085b9acfb9d5 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-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
@@ -20,28 +20,34 @@
#include "clk-alpha-pll.h"
#define PLL_MODE 0x00
-# define PLL_OUTCTRL BIT(0)
-# define PLL_BYPASSNL BIT(1)
-# define PLL_RESET_N BIT(2)
-# define PLL_LOCK_COUNT_SHIFT 8
-# define PLL_LOCK_COUNT_MASK 0x3f
-# define PLL_BIAS_COUNT_SHIFT 14
-# define PLL_BIAS_COUNT_MASK 0x3f
-# define PLL_VOTE_FSM_ENA BIT(20)
-# define PLL_VOTE_FSM_RESET BIT(21)
-# define PLL_ACTIVE_FLAG BIT(30)
-# define PLL_LOCK_DET BIT(31)
+#define PLL_OUTCTRL BIT(0)
+#define PLL_BYPASSNL BIT(1)
+#define PLL_RESET_N BIT(2)
+#define PLL_LOCK_COUNT_SHIFT 8
+#define PLL_LOCK_COUNT_MASK 0x3f
+#define PLL_LOCK_COUNT_VAL 0x0
+#define PLL_BIAS_COUNT_SHIFT 14
+#define PLL_BIAS_COUNT_MASK 0x3f
+#define PLL_BIAS_COUNT_VAL 0x6
+#define PLL_LATCH_INTERFACE BIT(11)
+#define PLL_VOTE_FSM_ENA BIT(20)
+#define PLL_VOTE_FSM_RESET BIT(21)
+#define PLL_UPDATE BIT(22)
+#define PLL_HW_UPDATE_LOGIC_BYPASS BIT(23)
+#define PLL_ALPHA_EN BIT(24)
+#define PLL_ACTIVE_FLAG BIT(30)
+#define PLL_LOCK_DET BIT(31)
+#define PLL_ACK_LATCH BIT(29)
#define PLL_L_VAL 0x04
#define PLL_ALPHA_VAL 0x08
#define PLL_ALPHA_VAL_U 0x0c
#define PLL_USER_CTL 0x10
-# define PLL_POST_DIV_SHIFT 8
-# define PLL_POST_DIV_MASK 0xf
-# define PLL_ALPHA_EN BIT(24)
-# define PLL_VCO_SHIFT 20
-# define PLL_VCO_MASK 0x3
+#define PLL_POST_DIV_SHIFT 8
+#define PLL_POST_DIV_MASK 0xf
+#define PLL_VCO_SHIFT 20
+#define PLL_VCO_MASK 0x3
#define PLL_USER_CTL_U 0x14
@@ -79,7 +85,7 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse,
ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
if (ret)
return ret;
- if (inverse && (val & mask))
+ if (inverse && !(val & mask))
return 0;
else if ((val & mask) == mask)
return 0;
@@ -106,6 +112,10 @@ static int wait_for_pll_offline(struct clk_alpha_pll *pll, u32 mask)
return wait_for_pll(pll, mask, 0, "offline");
}
+static int wait_for_pll_latch_ack(struct clk_alpha_pll *pll, u32 mask)
+{
+ return wait_for_pll(pll, mask, 0, "latch_ack");
+}
/* alpha pll with hwfsm support */
@@ -114,29 +124,103 @@ static int wait_for_pll_offline(struct clk_alpha_pll *pll, u32 mask)
#define PLL_OFFLINE_ACK BIT(28)
#define PLL_ACTIVE_FLAG BIT(30)
+static void clk_alpha_set_fsm_mode(struct clk_alpha_pll *pll)
+{
+ u32 val;
+
+ regmap_read(pll->clkr.regmap, pll->offset + PLL_MODE, &val);
+
+ /* De-assert reset to FSM */
+ val &= ~PLL_VOTE_FSM_RESET;
+
+ /* Program bias count */
+ val &= ~(PLL_BIAS_COUNT_MASK << PLL_BIAS_COUNT_SHIFT);
+ val |= PLL_BIAS_COUNT_VAL << PLL_BIAS_COUNT_SHIFT;
+
+ /* Program lock count */
+ val &= ~(PLL_LOCK_COUNT_MASK << PLL_LOCK_COUNT_SHIFT);
+ val |= PLL_LOCK_COUNT_VAL << PLL_LOCK_COUNT_SHIFT;
+
+ /* Enable PLL FSM voting */
+ val |= PLL_VOTE_FSM_ENA;
+
+ regmap_write(pll->clkr.regmap, pll->offset + PLL_MODE, val);
+}
+
void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
- const struct pll_config *config)
+ const struct pll_config *config)
{
u32 val, mask;
- regmap_write(regmap, pll->offset + PLL_CONFIG_CTL,
- config->config_ctl_val);
+ if (config->l)
+ regmap_write(regmap, pll->offset + PLL_L_VAL,
+ config->l);
+ if (config->alpha)
+ regmap_write(regmap, pll->offset + PLL_ALPHA_VAL,
+ config->alpha);
+ if (config->alpha_u)
+ regmap_write(regmap, pll->offset + PLL_ALPHA_VAL_U,
+ config->alpha_u);
+ if (config->config_ctl_val)
+ regmap_write(regmap, pll->offset + PLL_CONFIG_CTL,
+ config->config_ctl_val);
+
+ if (config->main_output_mask || config->aux_output_mask ||
+ config->aux2_output_mask || config->early_output_mask ||
+ config->vco_val || config->alpha_en_mask) {
+
+ val = config->main_output_mask;
+ val |= config->aux_output_mask;
+ val |= config->aux2_output_mask;
+ val |= config->early_output_mask;
+ val |= config->vco_val;
+ val |= config->alpha_en_mask;
+
+ mask = config->main_output_mask;
+ mask |= config->aux_output_mask;
+ mask |= config->aux2_output_mask;
+ mask |= config->early_output_mask;
+ mask |= config->vco_mask;
+ mask |= config->alpha_en_mask;
+
+ regmap_update_bits(regmap, pll->offset + PLL_USER_CTL,
+ mask, val);
+ }
- val = config->main_output_mask;
- val |= config->aux_output_mask;
- val |= config->aux2_output_mask;
- val |= config->early_output_mask;
- val |= config->post_div_val;
+ if (config->post_div_mask) {
+ mask = config->post_div_mask;
+ val = config->post_div_val;
+ regmap_update_bits(regmap, pll->offset + PLL_USER_CTL,
+ mask, val);
+ }
- mask = config->main_output_mask;
- mask |= config->aux_output_mask;
- mask |= config->aux2_output_mask;
- mask |= config->early_output_mask;
- mask |= config->post_div_mask;
+ /* Do not bypass the latch interface */
+ if (pll->flags & SUPPORTS_SLEW)
+ regmap_update_bits(regmap, pll->offset + PLL_USER_CTL_U,
+ PLL_LATCH_INTERFACE, (u32)~PLL_LATCH_INTERFACE);
- regmap_update_bits(regmap, pll->offset + PLL_USER_CTL, mask, val);
+ if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) {
+ regmap_update_bits(regmap, pll->offset + PLL_MODE,
+ PLL_HW_UPDATE_LOGIC_BYPASS,
+ PLL_HW_UPDATE_LOGIC_BYPASS);
+ }
- return;
+ if (config->test_ctl_lo_mask) {
+ mask = config->test_ctl_lo_mask;
+ val = config->test_ctl_lo_val;
+ regmap_update_bits(regmap, pll->offset + PLL_TEST_CTL,
+ mask, val);
+ }
+
+ if (config->test_ctl_hi_mask) {
+ mask = config->test_ctl_hi_mask;
+ val = config->test_ctl_hi_val;
+ regmap_update_bits(regmap, pll->offset + PLL_TEST_CTL_U,
+ mask, val);
+ }
+
+ if (pll->flags & SUPPORTS_FSM_MODE)
+ clk_alpha_set_fsm_mode(pll);
}
static int clk_alpha_pll_hwfsm_enable(struct clk_hw *hw)
@@ -190,8 +274,9 @@ static void clk_alpha_pll_hwfsm_disable(struct clk_hw *hw)
PLL_FSM_ENA, 0);
if (ret)
return;
+
wait_for_pll_disable(pll, PLL_ACTIVE_FLAG);
- return;
+
}
static int clk_alpha_pll_enable(struct clk_hw *hw)
@@ -201,7 +286,6 @@ static int clk_alpha_pll_enable(struct clk_hw *hw)
u32 val, mask, off;
off = pll->offset;
-
mask = PLL_OUTCTRL | PLL_RESET_N | PLL_BYPASSNL;
ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
if (ret)
@@ -339,7 +423,53 @@ clk_alpha_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
a >>= ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH;
}
- return alpha_pll_calc_rate(prate, l, a);
+ ctl >>= PLL_POST_DIV_SHIFT;
+ ctl &= PLL_POST_DIV_MASK;
+
+ return alpha_pll_calc_rate(prate, l, a) >> fls(ctl);
+}
+
+static int clk_alpha_pll_dynamic_update(struct clk_alpha_pll *pll)
+{
+ int ret;
+
+ /* Latch the input to the PLL */
+ regmap_update_bits(pll->clkr.regmap, pll->offset + PLL_MODE,
+ PLL_UPDATE, PLL_UPDATE);
+
+ /* Wait for 2 reference cycle before checking ACK bit */
+ udelay(1);
+
+ ret = wait_for_pll_latch_ack(pll, PLL_ACK_LATCH);
+ if (ret)
+ return ret;
+
+ /* Return latch input to 0 */
+ regmap_update_bits(pll->clkr.regmap, pll->offset + PLL_MODE,
+ PLL_UPDATE, (u32)~PLL_UPDATE);
+
+ ret = wait_for_pll_enable(pll, PLL_LOCK_DET);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct pll_vco_data
+ *find_vco_data(const struct pll_vco_data *data,
+ unsigned long rate, size_t size)
+{
+ int i;
+
+ if (!data)
+ return NULL;
+
+ for (i = 0; i < size; i++) {
+ if (rate == data[i].freq)
+ return &data[i];
+ }
+
+ return &data[i - 1];
}
static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -347,16 +477,36 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
const struct pll_vco *vco;
+ const struct pll_vco_data *data;
+ bool is_enabled;
u32 l, off = pll->offset;
u64 a;
+ unsigned long rrate;
+
+ rrate = alpha_pll_round_rate(rate, prate, &l, &a);
- rate = alpha_pll_round_rate(rate, prate, &l, &a);
- vco = alpha_pll_find_vco(pll, rate);
+ if (rrate != rate) {
+ pr_err("alpha_pll: Call clk_set_rate with rounded rates!\n");
+ return -EINVAL;
+ }
+
+ vco = alpha_pll_find_vco(pll, rrate);
if (!vco) {
pr_err("alpha pll not in a valid vco range\n");
return -EINVAL;
}
+ is_enabled = clk_hw_is_enabled(hw);
+
+ /*
+ * For PLLs that do not support dynamic programming (dynamic_update
+ * is not set), ensure PLL is off before changing rate. For
+ * optimization reasons, assume no downstream clock is actively
+ * using it.
+ */
+ if (is_enabled && !(pll->flags & SUPPORTS_DYNAMIC_UPDATE))
+ hw->init->ops->disable(hw);
+
a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);
regmap_write(pll->clkr.regmap, off + PLL_L_VAL, l);
@@ -367,9 +517,27 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
PLL_VCO_MASK << PLL_VCO_SHIFT,
vco->val << PLL_VCO_SHIFT);
+ data = find_vco_data(pll->vco_data, rate, pll->num_vco_data);
+ if (data) {
+ if (data->freq == rate)
+ regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL,
+ PLL_POST_DIV_MASK << PLL_POST_DIV_SHIFT,
+ data->post_div_val << PLL_POST_DIV_SHIFT);
+ else
+ regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL,
+ PLL_POST_DIV_MASK << PLL_POST_DIV_SHIFT,
+ 0x0 << PLL_VCO_SHIFT);
+ }
+
regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL, PLL_ALPHA_EN,
PLL_ALPHA_EN);
+ if (is_enabled && (pll->flags & SUPPORTS_DYNAMIC_UPDATE))
+ clk_alpha_pll_dynamic_update(pll);
+
+ if (is_enabled && !(pll->flags & SUPPORTS_DYNAMIC_UPDATE))
+ hw->init->ops->enable(hw);
+
return 0;
}
@@ -381,6 +549,9 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
u64 a;
unsigned long min_freq, max_freq;
+ if (rate < pll->min_supported_freq)
+ return pll->min_supported_freq;
+
rate = alpha_pll_round_rate(rate, *prate, &l, &a);
if (alpha_pll_find_vco(pll, rate))
return rate;
diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h
index 7718bd5beefc..9b1d3ee61cac 100644
--- a/drivers/clk/qcom/clk-alpha-pll.h
+++ b/drivers/clk/qcom/clk-alpha-pll.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-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
@@ -18,6 +18,11 @@
#include "clk-regmap.h"
#include "clk-pll.h"
+struct pll_vco_data {
+ unsigned long freq;
+ u8 post_div_val;
+};
+
struct pll_vco {
unsigned long min_freq;
unsigned long max_freq;
@@ -28,21 +33,35 @@ struct pll_vco {
* struct clk_alpha_pll - phase locked loop (PLL)
* @offset: base address of registers
* @vco_table: array of VCO settings
+ * @vco_data: array of VCO data settings like post div
* @clkr: regmap clock handle
*/
struct clk_alpha_pll {
u32 offset;
+ struct pll_config *config;
const struct pll_vco *vco_table;
size_t num_vco;
+ const struct pll_vco_data *vco_data;
+ size_t num_vco_data;
+
+ u8 flags;
+#define SUPPORTS_FSM_MODE BIT(0)
+ /* some PLLs support dynamically updating their rate
+ * without disabling the PLL first. Set this flag
+ * to enable this support.
+ */
+#define SUPPORTS_DYNAMIC_UPDATE BIT(1)
+#define SUPPORTS_SLEW BIT(2)
+
struct clk_regmap clkr;
- u32 config_ctl_val;
#define PLLOUT_MAIN BIT(0)
#define PLLOUT_AUX BIT(1)
#define PLLOUT_AUX2 BIT(2)
#define PLLOUT_EARLY BIT(3)
u32 pllout_flags;
+ unsigned long min_supported_freq;
};
/**
diff --git a/drivers/clk/qcom/clk-pll.h b/drivers/clk/qcom/clk-pll.h
index 1e64fe9326d6..4f779fda785b 100644
--- a/drivers/clk/qcom/clk-pll.h
+++ b/drivers/clk/qcom/clk-pll.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013, 2016 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
@@ -70,6 +70,8 @@ struct pll_config {
u16 l;
u32 m;
u32 n;
+ u32 alpha;
+ u32 alpha_u;
u32 vco_val;
u32 vco_mask;
u32 pre_div_val;
@@ -77,11 +79,16 @@ struct pll_config {
u32 post_div_val;
u32 post_div_mask;
u32 mn_ena_mask;
+ u32 alpha_en_mask;
u32 main_output_mask;
u32 aux_output_mask;
u32 aux2_output_mask;
u32 early_output_mask;
u32 config_ctl_val;
+ u32 test_ctl_lo_val;
+ u32 test_ctl_lo_mask;
+ u32 test_ctl_hi_val;
+ u32 test_ctl_hi_mask;
};
void clk_pll_configure_sr(struct clk_pll *pll, struct regmap *regmap,
diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c
index 612e7b37a8d0..bc982c9bfa71 100644
--- a/drivers/clk/qcom/clk-smd-rpm.c
+++ b/drivers/clk/qcom/clk-smd-rpm.c
@@ -662,8 +662,6 @@ static struct clk_hw *msmfalcon_clks[] = {
[RPM_AGGR2_NOC_A_CLK] = &msmfalcon_aggre2_noc_a_clk.hw,
[RPM_CNOC_CLK] = &msmfalcon_cnoc_clk.hw,
[RPM_CNOC_A_CLK] = &msmfalcon_cnoc_a_clk.hw,
- [RPM_MMAXI_CLK] = &msmfalcon_mmssnoc_axi_clk.hw,
- [RPM_MMAXI_A_CLK] = &msmfalcon_mmssnoc_axi_a_clk.hw,
[RPM_IPA_CLK] = &msmfalcon_ipa_clk.hw,
[RPM_IPA_A_CLK] = &msmfalcon_ipa_a_clk.hw,
[RPM_CE1_CLK] = &msmfalcon_ce1_clk.hw,
@@ -684,6 +682,8 @@ static struct clk_hw *msmfalcon_clks[] = {
[RPM_LN_BB_CLK3_PIN_AO] = &msmfalcon_ln_bb_clk3_pin_ao.hw,
[RPM_CNOC_PERIPH_CLK] = &msmfalcon_cnoc_periph_clk.hw,
[RPM_CNOC_PERIPH_A_CLK] = &msmfalcon_cnoc_periph_a_clk.hw,
+ [MMSSNOC_AXI_CLK] = &msmfalcon_mmssnoc_axi_clk.hw,
+ [MMSSNOC_AXI_A_CLK] = &msmfalcon_mmssnoc_axi_a_clk.hw,
/* Voter Clocks */
[BIMC_MSMBUS_CLK] = &bimc_msmbus_clk.hw,
diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c
index d0a0313d6bef..2e7f03d50f4e 100644
--- a/drivers/clk/qcom/gcc-msm8916.c
+++ b/drivers/clk/qcom/gcc-msm8916.c
@@ -2346,6 +2346,7 @@ static struct clk_branch gcc_crypto_ahb_clk = {
"pcnoc_bfdcd_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2381,6 +2382,7 @@ static struct clk_branch gcc_crypto_clk = {
"crypto_clk_src",
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c
index 6c899fb5b856..8b9c388bc43c 100644
--- a/drivers/clk/qcom/gcc-msm8960.c
+++ b/drivers/clk/qcom/gcc-msm8960.c
@@ -2722,7 +2722,7 @@ static struct clk_rcg ce3_src = {
},
.freq_tbl = clk_tbl_ce3,
.clkr = {
- .enable_reg = 0x2c08,
+ .enable_reg = 0x36c0,
.enable_mask = BIT(7),
.hw.init = &(struct clk_init_data){
.name = "ce3_src",
@@ -2738,7 +2738,7 @@ static struct clk_branch ce3_core_clk = {
.halt_reg = 0x2fdc,
.halt_bit = 5,
.clkr = {
- .enable_reg = 0x36c4,
+ .enable_reg = 0x36cc,
.enable_mask = BIT(4),
.hw.init = &(struct clk_init_data){
.name = "ce3_core_clk",
diff --git a/drivers/clk/qcom/gcc-msmfalcon.c b/drivers/clk/qcom/gcc-msmfalcon.c
index 42b91d70aa54..b5f7e18cf495 100644
--- a/drivers/clk/qcom/gcc-msmfalcon.c
+++ b/drivers/clk/qcom/gcc-msmfalcon.c
@@ -35,8 +35,8 @@
#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
-static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL);
-static DEFINE_VDD_REGULATORS(vdd_dig_ao, VDD_DIG_NUM, 1, vdd_corner, NULL);
+static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner);
+static DEFINE_VDD_REGULATORS(vdd_dig_ao, VDD_DIG_NUM, 1, vdd_corner);
enum {
P_CORE_BI_PLL_TEST_SE,
@@ -2201,7 +2201,7 @@ static struct clk_branch gcc_ufs_axi_hw_ctl_clk = {
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_branch2_hw_ctl_ops,
+ .ops = &clk_branch2_ops,
},
},
};
@@ -2249,7 +2249,7 @@ static struct clk_branch gcc_ufs_ice_core_hw_ctl_clk = {
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_branch2_hw_ctl_ops,
+ .ops = &clk_branch2_ops,
},
},
};
@@ -2284,7 +2284,7 @@ static struct clk_branch gcc_ufs_phy_aux_hw_ctl_clk = {
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_branch2_hw_ctl_ops,
+ .ops = &clk_branch2_ops,
},
},
};
@@ -2355,7 +2355,7 @@ static struct clk_branch gcc_ufs_unipro_core_hw_ctl_clk = {
},
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_branch2_hw_ctl_ops,
+ .ops = &clk_branch2_ops,
},
},
};
@@ -2753,9 +2753,8 @@ MODULE_DEVICE_TABLE(of, gcc_falcon_match_table);
static int gcc_falcon_probe(struct platform_device *pdev)
{
- int ret = 0, i;
+ int ret = 0;
struct regmap *regmap;
- struct clk *clk;
regmap = qcom_cc_map(pdev, &gcc_falcon_desc);
if (IS_ERR(regmap))
diff --git a/drivers/clk/qcom/gpucc-msmfalcon.c b/drivers/clk/qcom/gpucc-msmfalcon.c
index f194abb471cd..fe7cff443250 100644
--- a/drivers/clk/qcom/gpucc-msmfalcon.c
+++ b/drivers/clk/qcom/gpucc-msmfalcon.c
@@ -35,8 +35,8 @@
#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
#define F_GFX(f, s, h, m, n, sf) { (f), (s), (2 * (h) - 1), (m), (n), (sf) }
-static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL);
-static DEFINE_VDD_REGULATORS(vdd_mx, VDD_DIG_NUM, 1, vdd_corner, NULL);
+static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner);
+static DEFINE_VDD_REGULATORS(vdd_mx, VDD_DIG_NUM, 1, vdd_corner);
static DEFINE_VDD_REGS_INIT(vdd_gfx, 1);
enum {
@@ -181,6 +181,7 @@ static struct clk_init_data gpu_clks_init[] = {
* | 465000000 | 930000000 | 1 | 2 |
* | 588000000 | 1176000000 | 1 | 2 |
* | 647000000 | 1294000000 | 1 | 2 |
+ * | 700000000 | 1400000000 | 1 | 2 |
* | 750000000 | 1500000000 | 1 | 2 |
* ====================================================
*/
@@ -193,6 +194,7 @@ static const struct freq_tbl ftbl_gfx3d_clk_src[] = {
F_GFX(465000000, 0, 2, 0, 0, 930000000),
F_GFX(588000000, 0, 2, 0, 0, 1176000000),
F_GFX(647000000, 0, 2, 0, 0, 1294000000),
+ F_GFX(700000000, 0, 2, 0, 0, 1400000000),
F_GFX(750000000, 0, 2, 0, 0, 1500000000),
{ }
};
@@ -376,9 +378,9 @@ static int of_get_fmax_vdd_class(struct platform_device *pdev,
if (!vdd->vdd_uv)
return -ENOMEM;
- gpu_clks_init[index].fmax = devm_kzalloc(&pdev->dev, prop_len *
+ gpu_clks_init[index].rate_max = devm_kzalloc(&pdev->dev, prop_len *
sizeof(unsigned long), GFP_KERNEL);
- if (!gpu_clks_init[index].fmax)
+ if (!gpu_clks_init[index].rate_max)
return -ENOMEM;
array = devm_kzalloc(&pdev->dev, prop_len * sizeof(u32) * num,
@@ -388,7 +390,7 @@ static int of_get_fmax_vdd_class(struct platform_device *pdev,
of_property_read_u32_array(of, prop_name, array, prop_len * num);
for (i = 0; i < prop_len; i++) {
- gpu_clks_init[index].fmax[i] = array[num * i];
+ gpu_clks_init[index].rate_max[i] = array[num * i];
for (j = 1; j < num; j++) {
vdd->vdd_uv[(num - 1) * i + (j - 1)] =
array[num * i + j];
@@ -398,7 +400,7 @@ static int of_get_fmax_vdd_class(struct platform_device *pdev,
devm_kfree(&pdev->dev, array);
vdd->num_levels = prop_len;
vdd->cur_level = prop_len;
- gpu_clks_init[index].num_fmax = prop_len;
+ gpu_clks_init[index].num_rate_max = prop_len;
return 0;
}
diff --git a/drivers/clk/qcom/vdd-level-falcon.h b/drivers/clk/qcom/vdd-level-falcon.h
index e8699358cf91..d54e801ecc67 100644
--- a/drivers/clk/qcom/vdd-level-falcon.h
+++ b/drivers/clk/qcom/vdd-level-falcon.h
@@ -19,50 +19,52 @@
#define VDD_DIG_FMAX_MAP1(l1, f1) \
.vdd_class = &vdd_dig, \
- .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ .rate_max = (unsigned long[VDD_DIG_NUM]) { \
[VDD_DIG_##l1] = (f1), \
}, \
- .num_fmax = VDD_DIG_NUM
+ .num_rate_max = VDD_DIG_NUM
+
#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
.vdd_class = &vdd_dig, \
- .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ .rate_max = (unsigned long[VDD_DIG_NUM]) { \
[VDD_DIG_##l1] = (f1), \
[VDD_DIG_##l2] = (f2), \
}, \
- .num_fmax = VDD_DIG_NUM
+ .num_rate_max = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
.vdd_class = &vdd_dig, \
- .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ .rate_max = (unsigned long[VDD_DIG_NUM]) { \
[VDD_DIG_##l1] = (f1), \
[VDD_DIG_##l2] = (f2), \
[VDD_DIG_##l3] = (f3), \
}, \
- .num_fmax = VDD_DIG_NUM
+ .num_rate_max = VDD_DIG_NUM
+
#define VDD_DIG_FMAX_MAP4(l1, f1, l2, f2, l3, f3, l4, f4) \
.vdd_class = &vdd_dig, \
- .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ .rate_max = (unsigned long[VDD_DIG_NUM]) { \
[VDD_DIG_##l1] = (f1), \
[VDD_DIG_##l2] = (f2), \
[VDD_DIG_##l3] = (f3), \
[VDD_DIG_##l4] = (f4), \
}, \
- .num_fmax = VDD_DIG_NUM
+ .num_rate_max = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP5(l1, f1, l2, f2, l3, f3, l4, f4, l5, f5) \
.vdd_class = &vdd_dig, \
- .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ .rate_max = (unsigned long[VDD_DIG_NUM]) { \
[VDD_DIG_##l1] = (f1), \
[VDD_DIG_##l2] = (f2), \
[VDD_DIG_##l3] = (f3), \
[VDD_DIG_##l4] = (f4), \
[VDD_DIG_##l5] = (f5), \
}, \
- .num_fmax = VDD_DIG_NUM
+ .num_rate_max = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP6(l1, f1, l2, f2, l3, f3, l4, f4, l5, f5, l6, f6) \
.vdd_class = &vdd_dig, \
- .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ .rate_max = (unsigned long[VDD_DIG_NUM]) { \
[VDD_DIG_##l1] = (f1), \
[VDD_DIG_##l2] = (f2), \
[VDD_DIG_##l3] = (f3), \
@@ -70,12 +72,12 @@
[VDD_DIG_##l5] = (f5), \
[VDD_DIG_##l6] = (f6), \
}, \
- .num_fmax = VDD_DIG_NUM
+ .num_rate_max = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP7(l1, f1, l2, f2, l3, f3, l4, f4, l5, f5, l6, f6, \
l7, f7) \
.vdd_class = &vdd_dig, \
- .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ .rate_max = (unsigned long[VDD_DIG_NUM]) { \
[VDD_DIG_##l1] = (f1), \
[VDD_DIG_##l2] = (f2), \
[VDD_DIG_##l3] = (f3), \
@@ -84,27 +86,27 @@
[VDD_DIG_##l6] = (f6), \
[VDD_DIG_##l7] = (f7), \
}, \
- .num_fmax = VDD_DIG_NUM
+ .num_rate_max = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP1_AO(l1, f1) \
.vdd_class = &vdd_dig_ao, \
- .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ .rate_max = (unsigned long[VDD_DIG_NUM]) { \
[VDD_DIG_##l1] = (f1), \
}, \
- .num_fmax = VDD_DIG_NUM
+ .num_rate_max = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP3_AO(l1, f1, l2, f2, l3, f3) \
.vdd_class = &vdd_dig_ao, \
- .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ .rate_max = (unsigned long[VDD_DIG_NUM]) { \
[VDD_DIG_##l1] = (f1), \
[VDD_DIG_##l2] = (f2), \
[VDD_DIG_##l3] = (f3), \
}, \
- .num_fmax = VDD_DIG_NUM
+ .num_rate_max = VDD_DIG_NUM
#define VDD_GPU_PLL_FMAX_MAP6(l1, f1, l2, f2, l3, f3, l4, f4, l5, f5, l6, f6) \
.vdd_class = &vdd_mx, \
- .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ .rate_max = (unsigned long[VDD_DIG_NUM]) { \
[VDD_DIG_##l1] = (f1), \
[VDD_DIG_##l2] = (f2), \
[VDD_DIG_##l3] = (f3), \
@@ -112,7 +114,7 @@
[VDD_DIG_##l5] = (f5), \
[VDD_DIG_##l6] = (f6), \
}, \
- .num_fmax = VDD_DIG_NUM
+ .num_rate_max = VDD_DIG_NUM
enum vdd_dig_levels {
VDD_DIG_NONE,
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index be6c7fd8315d..9b6c8188efac 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -70,7 +70,7 @@ static struct clk *rockchip_clk_register_branch(const char *name,
if (gate_offset >= 0) {
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate)
- return ERR_PTR(-ENOMEM);
+ goto err_gate;
gate->flags = gate_flags;
gate->reg = base + gate_offset;
@@ -82,7 +82,7 @@ static struct clk *rockchip_clk_register_branch(const char *name,
if (div_width > 0) {
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
- return ERR_PTR(-ENOMEM);
+ goto err_div;
div->flags = div_flags;
div->reg = base + muxdiv_offset;
@@ -90,7 +90,9 @@ static struct clk *rockchip_clk_register_branch(const char *name,
div->width = div_width;
div->lock = lock;
div->table = div_table;
- div_ops = &clk_divider_ops;
+ div_ops = (div_flags & CLK_DIVIDER_READ_ONLY)
+ ? &clk_divider_ro_ops
+ : &clk_divider_ops;
}
clk = clk_register_composite(NULL, name, parent_names, num_parents,
@@ -100,6 +102,11 @@ static struct clk *rockchip_clk_register_branch(const char *name,
flags);
return clk;
+err_div:
+ kfree(gate);
+err_gate:
+ kfree(mux);
+ return ERR_PTR(-ENOMEM);
}
static struct clk *rockchip_clk_register_frac_branch(const char *name,
diff --git a/drivers/clk/versatile/clk-sp810.c b/drivers/clk/versatile/clk-sp810.c
index a1cdef6b0f90..897c36c1754a 100644
--- a/drivers/clk/versatile/clk-sp810.c
+++ b/drivers/clk/versatile/clk-sp810.c
@@ -92,6 +92,7 @@ static void __init clk_sp810_of_setup(struct device_node *node)
int num = ARRAY_SIZE(parent_names);
char name[12];
struct clk_init_data init;
+ static int instance;
int i;
bool deprecated;
@@ -118,7 +119,7 @@ static void __init clk_sp810_of_setup(struct device_node *node)
deprecated = !of_find_property(node, "assigned-clock-parents", NULL);
for (i = 0; i < ARRAY_SIZE(sp810->timerclken); i++) {
- snprintf(name, ARRAY_SIZE(name), "timerclken%d", i);
+ snprintf(name, sizeof(name), "sp810_%d_%d", instance, i);
sp810->timerclken[i].sp810 = sp810;
sp810->timerclken[i].channel = i;
@@ -139,5 +140,6 @@ static void __init clk_sp810_of_setup(struct device_node *node)
}
of_clk_add_provider(node, clk_sp810_timerclken_of_get, sp810);
+ instance++;
}
CLK_OF_DECLARE(sp810, "arm,sp810", clk_sp810_of_setup);
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index 90638f325d0b..30e1a2138002 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -162,28 +162,6 @@ struct cpufreq_interactive_tunables {
bool enable_prediction;
};
-/*
- * HACK: FIXME: Bring back cpufreq_{get,put}_global_kobject()
- * definition removed by upstream commit 8eec1020f0c0 "cpufreq:
- * create cpu/cpufreq at boot time" to fix build failures.
- */
-static int cpufreq_global_kobject_usage;
-
-int cpufreq_get_global_kobject(void)
-{
- if (!cpufreq_global_kobject_usage++)
- return kobject_add(cpufreq_global_kobject,
- &cpu_subsys.dev_root->kobj, "%s", "cpufreq");
-
- return 0;
-}
-
-void cpufreq_put_global_kobject(void)
-{
- if (!--cpufreq_global_kobject_usage)
- kobject_del(cpufreq_global_kobject);
-}
-
/* For cases where we have single governor instance for system */
static struct cpufreq_interactive_tunables *common_tunables;
static struct cpufreq_interactive_tunables *cached_common_tunables;
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 98fb8821382d..f53b02a6bc05 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -667,6 +667,11 @@ static int core_get_max_pstate(void)
if (err)
goto skip_tar;
+ /* For level 1 and 2, bits[23:16] contain the ratio */
+ if (tdp_ctrl)
+ tdp_ratio >>= 16;
+
+ tdp_ratio &= 0xff; /* ratios are only 8 bits long */
if (tdp_ratio - 1 == tar) {
max_pstate = tar;
pr_debug("max_pstate=TAC %x\n", max_pstate);
diff --git a/drivers/cpufreq/qcom-cpufreq.c b/drivers/cpufreq/qcom-cpufreq.c
index 82861b1dbe46..2aa7b783f276 100644
--- a/drivers/cpufreq/qcom-cpufreq.c
+++ b/drivers/cpufreq/qcom-cpufreq.c
@@ -3,7 +3,7 @@
* MSM architecture cpufreq driver
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2016, The Linux Foundation. All rights reserved.
* Author: Mike A. Chan <mikechan@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -320,7 +320,7 @@ static struct cpufreq_driver msm_cpufreq_driver = {
static struct cpufreq_frequency_table *cpufreq_parse_dt(struct device *dev,
char *tbl_name, int cpu)
{
- int ret, nf, i;
+ int ret, nf, i, j;
u32 *data;
struct cpufreq_frequency_table *ftbl;
@@ -344,6 +344,7 @@ static struct cpufreq_frequency_table *cpufreq_parse_dt(struct device *dev,
if (!ftbl)
return ERR_PTR(-ENOMEM);
+ j = 0;
for (i = 0; i < nf; i++) {
unsigned long f;
@@ -353,29 +354,20 @@ static struct cpufreq_frequency_table *cpufreq_parse_dt(struct device *dev,
f /= 1000;
/*
- * Check if this is the last feasible frequency in the table.
+ * Don't repeat frequencies if they round up to the same clock
+ * frequency.
*
- * The table listing frequencies higher than what the HW can
- * support is not an error since the table might be shared
- * across CPUs in different speed bins. It's also not
- * sufficient to check if the rounded rate is lower than the
- * requested rate as it doesn't cover the following example:
- *
- * Table lists: 2.2 GHz and 2.5 GHz.
- * Rounded rate returns: 2.2 GHz and 2.3 GHz.
- *
- * In this case, we can CPUfreq to use 2.2 GHz and 2.3 GHz
- * instead of rejecting the 2.5 GHz table entry.
*/
- if (i > 0 && f <= ftbl[i-1].frequency)
- break;
+ if (j > 0 && f <= ftbl[j - 1].frequency)
+ continue;
- ftbl[i].driver_data = i;
- ftbl[i].frequency = f;
+ ftbl[j].driver_data = j;
+ ftbl[j].frequency = f;
+ j++;
}
- ftbl[i].driver_data = i;
- ftbl[i].frequency = CPUFREQ_TABLE_END;
+ ftbl[j].driver_data = j;
+ ftbl[j].frequency = CPUFREQ_TABLE_END;
devm_kfree(dev, data);
diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c
index 545069d5fdfb..e342565e8715 100644
--- a/drivers/cpuidle/cpuidle-arm.c
+++ b/drivers/cpuidle/cpuidle-arm.c
@@ -50,7 +50,7 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,
* call the CPU ops suspend protocol with idle index as a
* parameter.
*/
- arm_cpuidle_suspend(idx);
+ ret = arm_cpuidle_suspend(idx);
cpu_pm_exit();
}
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index a045b9a940e8..7f437bc4431b 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -214,7 +214,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
tick_broadcast_exit();
}
- if (!cpuidle_state_is_coupled(drv, entered_state))
+ if (!cpuidle_state_is_coupled(drv, index))
local_irq_enable();
diff = ktime_to_us(ktime_sub(time_end, time_start));
@@ -433,6 +433,8 @@ static void __cpuidle_unregister_device(struct cpuidle_device *dev)
list_del(&dev->device_list);
per_cpu(cpuidle_devices, dev->cpu) = NULL;
module_put(drv->owner);
+
+ dev->registered = 0;
}
static void __cpuidle_device_init(struct cpuidle_device *dev)
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 37e504381313..de033cc37a15 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -85,7 +85,9 @@ struct lpm_debug {
struct lpm_cluster *lpm_root_node;
-static bool lpm_prediction;
+#define MAXSAMPLES 5
+
+static bool lpm_prediction = true;
module_param_named(lpm_prediction,
lpm_prediction, bool, S_IRUGO | S_IWUSR | S_IWGRP);
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 386c85fc714b..77aea1f41714 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -372,18 +372,18 @@ config CRYPTO_DEV_QCRYPTO
config CRYPTO_DEV_QCOM_MSM_QCE
tristate "Qualcomm Crypto Engine (QCE) module"
- select CRYPTO_DEV_QCE50 if ARCH_APQ8084 || ARCH_MSM8916 || ARCH_MSM8994 || ARCH_MSM8996 || ARCH_MSM8992 || ARCH_MSMTITANIUM || ARCH_MSM8909 || ARCH_MSMCOBALT || ARCH_MSMFALCON
+ select CRYPTO_DEV_QCE50 if ARCH_APQ8084 || ARCH_MSM8916 || ARCH_MSM8994 || ARCH_MSM8996 || ARCH_MSM8992 || ARCH_MSMTITANIUM || ARCH_MSM8909 || ARCH_MSMCOBALT || ARCH_MSMFALCON || ARCH_MSMTRITON
default n
help
This driver supports Qualcomm Crypto Engine in MSM7x30, MSM8660
MSM8x55, MSM8960, MSM9615, MSM8916, MSM8994, MSM8996, FSM9900,
- MSMTITANINUM, APQ8084, MSMCOBALT and MSMFALCON.
+ MSMTITANINUM, APQ8084, MSMCOBALT, MSMFALCON and MSMTRITON.
To compile this driver as a module, choose M here: the
For MSM7x30 MSM8660 and MSM8x55 the module is called qce
For MSM8960, APQ8064 and MSM9615 the module is called qce40
For MSM8974, MSM8916, MSM8994, MSM8996, MSM8992, MSMTITANIUM,
- APQ8084, MSMCOBALT and MSMFALCON the module is called qce50.
+ APQ8084, MSMCOBALT, MSMFALCON and MSMTRITON the module is called qce50.
config CRYPTO_DEV_QCEDEV
tristate "QCEDEV Interface to CE module"
@@ -391,8 +391,8 @@ config CRYPTO_DEV_QCEDEV
help
This driver supports Qualcomm QCEDEV Crypto in MSM7x30, MSM8660,
MSM8960, MSM9615, APQ8064, MSM8974, MSM8916, MSM8994, MSM8996,
- APQ8084, MSMCOBALT, MSMFALCON. This exposes the interface to the QCE hardware
- accelerator via IOCTLs.
+ APQ8084, MSMCOBALT, MSMFALCON, MSMTRITON. This exposes the
+ interface to the QCE hardware accelerator via IOCTLs.
To compile this driver as a module, choose M here: the
module will be called qcedev.
diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c
index f7e0d8d4c3da..8f50a02ff68d 100644
--- a/drivers/crypto/caam/jr.c
+++ b/drivers/crypto/caam/jr.c
@@ -248,7 +248,7 @@ static void caam_jr_dequeue(unsigned long devarg)
struct device *caam_jr_alloc(void)
{
struct caam_drv_private_jr *jrpriv, *min_jrpriv = NULL;
- struct device *dev = NULL;
+ struct device *dev = ERR_PTR(-ENODEV);
int min_tfm_cnt = INT_MAX;
int tfm_cnt;
diff --git a/drivers/crypto/ccp/ccp-crypto-aes-cmac.c b/drivers/crypto/ccp/ccp-crypto-aes-cmac.c
index 3d9acc53d247..60fc0fa26fd3 100644
--- a/drivers/crypto/ccp/ccp-crypto-aes-cmac.c
+++ b/drivers/crypto/ccp/ccp-crypto-aes-cmac.c
@@ -225,6 +225,9 @@ static int ccp_aes_cmac_export(struct ahash_request *req, void *out)
struct ccp_aes_cmac_req_ctx *rctx = ahash_request_ctx(req);
struct ccp_aes_cmac_exp_ctx state;
+ /* Don't let anything leak to 'out' */
+ memset(&state, 0, sizeof(state));
+
state.null_msg = rctx->null_msg;
memcpy(state.iv, rctx->iv, sizeof(state.iv));
state.buf_count = rctx->buf_count;
diff --git a/drivers/crypto/ccp/ccp-crypto-aes-xts.c b/drivers/crypto/ccp/ccp-crypto-aes-xts.c
index 52c7395cb8d8..0d0d4529ee36 100644
--- a/drivers/crypto/ccp/ccp-crypto-aes-xts.c
+++ b/drivers/crypto/ccp/ccp-crypto-aes-xts.c
@@ -122,6 +122,7 @@ static int ccp_aes_xts_crypt(struct ablkcipher_request *req,
struct ccp_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
struct ccp_aes_req_ctx *rctx = ablkcipher_request_ctx(req);
unsigned int unit;
+ u32 unit_size;
int ret;
if (!ctx->u.aes.key_len)
@@ -133,11 +134,17 @@ static int ccp_aes_xts_crypt(struct ablkcipher_request *req,
if (!req->info)
return -EINVAL;
- for (unit = 0; unit < ARRAY_SIZE(unit_size_map); unit++)
- if (!(req->nbytes & (unit_size_map[unit].size - 1)))
- break;
+ unit_size = CCP_XTS_AES_UNIT_SIZE__LAST;
+ if (req->nbytes <= unit_size_map[0].size) {
+ for (unit = 0; unit < ARRAY_SIZE(unit_size_map); unit++) {
+ if (!(req->nbytes & (unit_size_map[unit].size - 1))) {
+ unit_size = unit_size_map[unit].value;
+ break;
+ }
+ }
+ }
- if ((unit_size_map[unit].value == CCP_XTS_AES_UNIT_SIZE__LAST) ||
+ if ((unit_size == CCP_XTS_AES_UNIT_SIZE__LAST) ||
(ctx->u.aes.key_len != AES_KEYSIZE_128)) {
/* Use the fallback to process the request for any
* unsupported unit sizes or key sizes
@@ -158,7 +165,7 @@ static int ccp_aes_xts_crypt(struct ablkcipher_request *req,
rctx->cmd.engine = CCP_ENGINE_XTS_AES_128;
rctx->cmd.u.xts.action = (encrypt) ? CCP_AES_ACTION_ENCRYPT
: CCP_AES_ACTION_DECRYPT;
- rctx->cmd.u.xts.unit_size = unit_size_map[unit].value;
+ rctx->cmd.u.xts.unit_size = unit_size;
rctx->cmd.u.xts.key = &ctx->u.aes.key_sg;
rctx->cmd.u.xts.key_len = ctx->u.aes.key_len;
rctx->cmd.u.xts.iv = &rctx->iv_sg;
diff --git a/drivers/crypto/ccp/ccp-crypto-sha.c b/drivers/crypto/ccp/ccp-crypto-sha.c
index 8ef06fad8b14..ab9945f2cb7a 100644
--- a/drivers/crypto/ccp/ccp-crypto-sha.c
+++ b/drivers/crypto/ccp/ccp-crypto-sha.c
@@ -212,6 +212,9 @@ static int ccp_sha_export(struct ahash_request *req, void *out)
struct ccp_sha_req_ctx *rctx = ahash_request_ctx(req);
struct ccp_sha_exp_ctx state;
+ /* Don't let anything leak to 'out' */
+ memset(&state, 0, sizeof(state));
+
state.type = rctx->type;
state.msg_bits = rctx->msg_bits;
state.first = rctx->first;
diff --git a/drivers/crypto/msm/qce.c b/drivers/crypto/msm/qce.c
index 7ddbb1938400..4cf95b90a2df 100644
--- a/drivers/crypto/msm/qce.c
+++ b/drivers/crypto/msm/qce.c
@@ -1,6 +1,6 @@
/* Qualcomm Crypto Engine driver.
*
- * Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-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
@@ -1962,8 +1962,8 @@ int qce_aead_req(void *handle, struct qce_req *q_req)
else
q_req->cryptlen = areq->cryptlen - authsize;
- if ((q_req->cryptlen > ULONG_MAX - ivsize) ||
- (q_req->cryptlen + ivsize > ULONG_MAX - areq->assoclen)) {
+ if ((q_req->cryptlen > UINT_MAX - ivsize) ||
+ (q_req->cryptlen + ivsize > UINT_MAX - areq->assoclen)) {
pr_err("Integer overflow on total aead req length.\n");
return -EINVAL;
}
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 3562de7fc967..55c043b44cea 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -3023,6 +3023,7 @@ static void qce_multireq_timeout(unsigned long data)
struct qce_device *pce_dev = (struct qce_device *)data;
int ret = 0;
int last_seq;
+ unsigned long flags;
last_seq = atomic_read(&pce_dev->bunch_cmd_seq);
if (last_seq == 0 ||
@@ -3032,21 +3033,33 @@ static void qce_multireq_timeout(unsigned long data)
return;
}
/* last bunch mode command time out */
+
+ /*
+ * From here to dummy request finish sps request and set owner back
+ * to none, we disable interrupt.
+ * So it won't get preempted or interrupted. If bam inerrupts happen
+ * between, and completion callback gets called from BAM, a new
+ * request may be issued by the client driver. Deadlock may happen.
+ */
+ local_irq_save(flags);
if (cmpxchg(&pce_dev->owner, QCE_OWNER_NONE, QCE_OWNER_TIMEOUT)
!= QCE_OWNER_NONE) {
+ local_irq_restore(flags);
mod_timer(&(pce_dev->timer), (jiffies + DELAY_IN_JIFFIES));
return;
}
- del_timer(&(pce_dev->timer));
- pce_dev->mode = IN_INTERRUPT_MODE;
- pce_dev->qce_stats.no_of_timeouts++;
- pr_debug("pcedev %d mode switch to INTR\n", pce_dev->dev_no);
ret = qce_dummy_req(pce_dev);
if (ret)
pr_warn("pcedev %d: Failed to insert dummy req\n",
pce_dev->dev_no);
cmpxchg(&pce_dev->owner, QCE_OWNER_TIMEOUT, QCE_OWNER_NONE);
+ pce_dev->mode = IN_INTERRUPT_MODE;
+ local_irq_restore(flags);
+
+ del_timer(&(pce_dev->timer));
+ pce_dev->qce_stats.no_of_timeouts++;
+ pr_debug("pcedev %d mode switch to INTR\n", pce_dev->dev_no);
}
void qce_get_driver_stats(void *handle)
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index e63f061175ad..e2099c4e7877 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -1234,44 +1234,6 @@ static int qcedev_vbuf_ablk_cipher(struct qcedev_async_req *areq,
struct qcedev_cipher_op_req *saved_req;
struct qcedev_cipher_op_req *creq = &areq->cipher_op_req;
- /* Verify Source Address's */
- for (i = 0; i < areq->cipher_op_req.entries; i++)
- if (!access_ok(VERIFY_READ,
- (void __user *)areq->cipher_op_req.vbuf.src[i].vaddr,
- areq->cipher_op_req.vbuf.src[i].len))
- return -EFAULT;
-
- /* Verify Destination Address's */
- if (creq->in_place_op != 1) {
- for (i = 0, total = 0; i < QCEDEV_MAX_BUFFERS; i++) {
- if ((areq->cipher_op_req.vbuf.dst[i].vaddr != 0) &&
- (total < creq->data_len)) {
- if (!access_ok(VERIFY_WRITE,
- (void __user *)creq->vbuf.dst[i].vaddr,
- creq->vbuf.dst[i].len)) {
- pr_err("%s:DST WR_VERIFY err %d=0x%lx\n",
- __func__, i, (uintptr_t)
- creq->vbuf.dst[i].vaddr);
- return -EFAULT;
- }
- total += creq->vbuf.dst[i].len;
- }
- }
- } else {
- for (i = 0, total = 0; i < creq->entries; i++) {
- if (total < creq->data_len) {
- if (!access_ok(VERIFY_WRITE,
- (void __user *)creq->vbuf.src[i].vaddr,
- creq->vbuf.src[i].len)) {
- pr_err("%s:SRC WR_VERIFY err %d=0x%lx\n",
- __func__, i, (uintptr_t)
- creq->vbuf.src[i].vaddr);
- return -EFAULT;
- }
- total += creq->vbuf.src[i].len;
- }
- }
- }
total = 0;
if (areq->cipher_op_req.mode == QCEDEV_AES_MODE_CTR)
@@ -1569,6 +1531,36 @@ static int qcedev_check_cipher_params(struct qcedev_cipher_op_req *req,
__func__, total, req->data_len);
goto error;
}
+ /* Verify Source Address's */
+ for (i = 0, total = 0; i < req->entries; i++) {
+ if (total < req->data_len) {
+ if (!access_ok(VERIFY_READ,
+ (void __user *)req->vbuf.src[i].vaddr,
+ req->vbuf.src[i].len)) {
+ pr_err("%s:SRC RD_VERIFY err %d=0x%lx\n",
+ __func__, i, (uintptr_t)
+ req->vbuf.src[i].vaddr);
+ goto error;
+ }
+ total += req->vbuf.src[i].len;
+ }
+ }
+
+ /* Verify Destination Address's */
+ for (i = 0, total = 0; i < QCEDEV_MAX_BUFFERS; i++) {
+ if ((req->vbuf.dst[i].vaddr != 0) &&
+ (total < req->data_len)) {
+ if (!access_ok(VERIFY_WRITE,
+ (void __user *)req->vbuf.dst[i].vaddr,
+ req->vbuf.dst[i].len)) {
+ pr_err("%s:DST WR_VERIFY err %d=0x%lx\n",
+ __func__, i, (uintptr_t)
+ req->vbuf.dst[i].vaddr);
+ goto error;
+ }
+ total += req->vbuf.dst[i].len;
+ }
+ }
return 0;
error:
return -EINVAL;
diff --git a/drivers/crypto/qat/qat_common/Makefile b/drivers/crypto/qat/qat_common/Makefile
index 9e9e196c6d51..45b5adaafa6f 100644
--- a/drivers/crypto/qat/qat_common/Makefile
+++ b/drivers/crypto/qat/qat_common/Makefile
@@ -2,6 +2,7 @@ $(obj)/qat_rsapubkey-asn1.o: $(obj)/qat_rsapubkey-asn1.c \
$(obj)/qat_rsapubkey-asn1.h
$(obj)/qat_rsaprivkey-asn1.o: $(obj)/qat_rsaprivkey-asn1.c \
$(obj)/qat_rsaprivkey-asn1.h
+$(obj)/qat_asym_algs.o: $(obj)/qat_rsapubkey-asn1.h $(obj)/qat_rsaprivkey-asn1.h
clean-files += qat_rsapubkey-asn1.c qat_rsapubkey-asn1.h
clean-files += qat_rsaprivkey-asn1.c qat_rsapvivkey-asn1.h
diff --git a/drivers/crypto/qat/qat_common/adf_common_drv.h b/drivers/crypto/qat/qat_common/adf_common_drv.h
index 3f76bd495bcb..aa1dbeaa9b49 100644
--- a/drivers/crypto/qat/qat_common/adf_common_drv.h
+++ b/drivers/crypto/qat/qat_common/adf_common_drv.h
@@ -227,6 +227,8 @@ void adf_disable_vf2pf_interrupts(struct adf_accel_dev *accel_dev,
uint32_t vf_mask);
void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev,
uint32_t vf_mask);
+int adf_init_pf_wq(void);
+void adf_exit_pf_wq(void);
#else
static inline int adf_sriov_configure(struct pci_dev *pdev, int numvfs)
{
@@ -236,5 +238,14 @@ static inline int adf_sriov_configure(struct pci_dev *pdev, int numvfs)
static inline void adf_disable_sriov(struct adf_accel_dev *accel_dev)
{
}
+
+static inline int adf_init_pf_wq(void)
+{
+ return 0;
+}
+
+static inline void adf_exit_pf_wq(void)
+{
+}
#endif
#endif
diff --git a/drivers/crypto/qat/qat_common/adf_ctl_drv.c b/drivers/crypto/qat/qat_common/adf_ctl_drv.c
index 473d36d91644..e7480f373532 100644
--- a/drivers/crypto/qat/qat_common/adf_ctl_drv.c
+++ b/drivers/crypto/qat/qat_common/adf_ctl_drv.c
@@ -469,12 +469,17 @@ static int __init adf_register_ctl_device_driver(void)
if (adf_init_aer())
goto err_aer;
+ if (adf_init_pf_wq())
+ goto err_pf_wq;
+
if (qat_crypto_register())
goto err_crypto_register;
return 0;
err_crypto_register:
+ adf_exit_pf_wq();
+err_pf_wq:
adf_exit_aer();
err_aer:
adf_chr_drv_destroy();
@@ -487,6 +492,7 @@ static void __exit adf_unregister_ctl_device_driver(void)
{
adf_chr_drv_destroy();
adf_exit_aer();
+ adf_exit_pf_wq();
qat_crypto_unregister();
adf_clean_vf_map(false);
mutex_destroy(&adf_ctl_lock);
diff --git a/drivers/crypto/qat/qat_common/adf_sriov.c b/drivers/crypto/qat/qat_common/adf_sriov.c
index 1117a8b58280..38a0415e767d 100644
--- a/drivers/crypto/qat/qat_common/adf_sriov.c
+++ b/drivers/crypto/qat/qat_common/adf_sriov.c
@@ -119,11 +119,6 @@ static int adf_enable_sriov(struct adf_accel_dev *accel_dev)
int i;
u32 reg;
- /* Workqueue for PF2VF responses */
- pf2vf_resp_wq = create_workqueue("qat_pf2vf_resp_wq");
- if (!pf2vf_resp_wq)
- return -ENOMEM;
-
for (i = 0, vf_info = accel_dev->pf.vf_info; i < totalvfs;
i++, vf_info++) {
/* This ptr will be populated when VFs will be created */
@@ -216,11 +211,6 @@ void adf_disable_sriov(struct adf_accel_dev *accel_dev)
kfree(accel_dev->pf.vf_info);
accel_dev->pf.vf_info = NULL;
-
- if (pf2vf_resp_wq) {
- destroy_workqueue(pf2vf_resp_wq);
- pf2vf_resp_wq = NULL;
- }
}
EXPORT_SYMBOL_GPL(adf_disable_sriov);
@@ -304,3 +294,19 @@ int adf_sriov_configure(struct pci_dev *pdev, int numvfs)
return numvfs;
}
EXPORT_SYMBOL_GPL(adf_sriov_configure);
+
+int __init adf_init_pf_wq(void)
+{
+ /* Workqueue for PF2VF responses */
+ pf2vf_resp_wq = create_workqueue("qat_pf2vf_resp_wq");
+
+ return !pf2vf_resp_wq ? -ENOMEM : 0;
+}
+
+void adf_exit_pf_wq(void)
+{
+ if (pf2vf_resp_wq) {
+ destroy_workqueue(pf2vf_resp_wq);
+ pf2vf_resp_wq = NULL;
+ }
+}
diff --git a/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c b/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c
index a19ee127edca..e72fea737a0d 100644
--- a/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c
+++ b/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c
@@ -35,6 +35,7 @@ static int sun4i_ss_opti_poll(struct ablkcipher_request *areq)
unsigned int todo;
struct sg_mapping_iter mi, mo;
unsigned int oi, oo; /* offset for in and out */
+ unsigned long flags;
if (areq->nbytes == 0)
return 0;
@@ -49,7 +50,7 @@ static int sun4i_ss_opti_poll(struct ablkcipher_request *areq)
return -EINVAL;
}
- spin_lock_bh(&ss->slock);
+ spin_lock_irqsave(&ss->slock, flags);
for (i = 0; i < op->keylen; i += 4)
writel(*(op->key + i / 4), ss->base + SS_KEY0 + i);
@@ -117,7 +118,7 @@ release_ss:
sg_miter_stop(&mi);
sg_miter_stop(&mo);
writel(0, ss->base + SS_CTL);
- spin_unlock_bh(&ss->slock);
+ spin_unlock_irqrestore(&ss->slock, flags);
return err;
}
@@ -149,6 +150,7 @@ static int sun4i_ss_cipher_poll(struct ablkcipher_request *areq)
unsigned int ob = 0; /* offset in buf */
unsigned int obo = 0; /* offset in bufo*/
unsigned int obl = 0; /* length of data in bufo */
+ unsigned long flags;
if (areq->nbytes == 0)
return 0;
@@ -181,7 +183,7 @@ static int sun4i_ss_cipher_poll(struct ablkcipher_request *areq)
if (no_chunk == 1)
return sun4i_ss_opti_poll(areq);
- spin_lock_bh(&ss->slock);
+ spin_lock_irqsave(&ss->slock, flags);
for (i = 0; i < op->keylen; i += 4)
writel(*(op->key + i / 4), ss->base + SS_KEY0 + i);
@@ -308,7 +310,7 @@ release_ss:
sg_miter_stop(&mi);
sg_miter_stop(&mo);
writel(0, ss->base + SS_CTL);
- spin_unlock_bh(&ss->slock);
+ spin_unlock_irqrestore(&ss->slock, flags);
return err;
}
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index b6f9f42e2985..9a8a18aafd5c 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -63,6 +63,14 @@ static void to_talitos_ptr(struct talitos_ptr *ptr, dma_addr_t dma_addr,
ptr->eptr = upper_32_bits(dma_addr);
}
+static void copy_talitos_ptr(struct talitos_ptr *dst_ptr,
+ struct talitos_ptr *src_ptr, bool is_sec1)
+{
+ dst_ptr->ptr = src_ptr->ptr;
+ if (!is_sec1)
+ dst_ptr->eptr = src_ptr->eptr;
+}
+
static void to_talitos_ptr_len(struct talitos_ptr *ptr, unsigned int len,
bool is_sec1)
{
@@ -827,6 +835,16 @@ struct talitos_ahash_req_ctx {
struct scatterlist *psrc;
};
+struct talitos_export_state {
+ u32 hw_context[TALITOS_MDEU_MAX_CONTEXT_SIZE / sizeof(u32)];
+ u8 buf[HASH_MAX_BLOCK_SIZE];
+ unsigned int swinit;
+ unsigned int first;
+ unsigned int last;
+ unsigned int to_hash_later;
+ unsigned int nbuf;
+};
+
static int aead_setkey(struct crypto_aead *authenc,
const u8 *key, unsigned int keylen)
{
@@ -1083,21 +1101,20 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
sg_count = dma_map_sg(dev, areq->src, edesc->src_nents ?: 1,
(areq->src == areq->dst) ? DMA_BIDIRECTIONAL
: DMA_TO_DEVICE);
-
/* hmac data */
desc->ptr[1].len = cpu_to_be16(areq->assoclen);
if (sg_count > 1 &&
(ret = sg_to_link_tbl_offset(areq->src, sg_count, 0,
areq->assoclen,
&edesc->link_tbl[tbl_off])) > 1) {
- tbl_off += ret;
-
to_talitos_ptr(&desc->ptr[1], edesc->dma_link_tbl + tbl_off *
sizeof(struct talitos_ptr), 0);
desc->ptr[1].j_extent = DESC_PTR_LNKTBL_JUMP;
dma_sync_single_for_device(dev, edesc->dma_link_tbl,
edesc->dma_len, DMA_BIDIRECTIONAL);
+
+ tbl_off += ret;
} else {
to_talitos_ptr(&desc->ptr[1], sg_dma_address(areq->src), 0);
desc->ptr[1].j_extent = 0;
@@ -1126,11 +1143,13 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
if (edesc->desc.hdr & DESC_HDR_MODE1_MDEU_CICV)
sg_link_tbl_len += authsize;
- if (sg_count > 1 &&
- (ret = sg_to_link_tbl_offset(areq->src, sg_count, areq->assoclen,
- sg_link_tbl_len,
- &edesc->link_tbl[tbl_off])) > 1) {
- tbl_off += ret;
+ if (sg_count == 1) {
+ to_talitos_ptr(&desc->ptr[4], sg_dma_address(areq->src) +
+ areq->assoclen, 0);
+ } else if ((ret = sg_to_link_tbl_offset(areq->src, sg_count,
+ areq->assoclen, sg_link_tbl_len,
+ &edesc->link_tbl[tbl_off])) >
+ 1) {
desc->ptr[4].j_extent |= DESC_PTR_LNKTBL_JUMP;
to_talitos_ptr(&desc->ptr[4], edesc->dma_link_tbl +
tbl_off *
@@ -1138,8 +1157,10 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
dma_sync_single_for_device(dev, edesc->dma_link_tbl,
edesc->dma_len,
DMA_BIDIRECTIONAL);
- } else
- to_talitos_ptr(&desc->ptr[4], sg_dma_address(areq->src), 0);
+ tbl_off += ret;
+ } else {
+ copy_talitos_ptr(&desc->ptr[4], &edesc->link_tbl[tbl_off], 0);
+ }
/* cipher out */
desc->ptr[5].len = cpu_to_be16(cryptlen);
@@ -1151,11 +1172,13 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
edesc->icv_ool = false;
- if (sg_count > 1 &&
- (sg_count = sg_to_link_tbl_offset(areq->dst, sg_count,
+ if (sg_count == 1) {
+ to_talitos_ptr(&desc->ptr[5], sg_dma_address(areq->dst) +
+ areq->assoclen, 0);
+ } else if ((sg_count =
+ sg_to_link_tbl_offset(areq->dst, sg_count,
areq->assoclen, cryptlen,
- &edesc->link_tbl[tbl_off])) >
- 1) {
+ &edesc->link_tbl[tbl_off])) > 1) {
struct talitos_ptr *tbl_ptr = &edesc->link_tbl[tbl_off];
to_talitos_ptr(&desc->ptr[5], edesc->dma_link_tbl +
@@ -1178,8 +1201,9 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
edesc->dma_len, DMA_BIDIRECTIONAL);
edesc->icv_ool = true;
- } else
- to_talitos_ptr(&desc->ptr[5], sg_dma_address(areq->dst), 0);
+ } else {
+ copy_talitos_ptr(&desc->ptr[5], &edesc->link_tbl[tbl_off], 0);
+ }
/* iv out */
map_single_talitos_ptr(dev, &desc->ptr[6], ivsize, ctx->iv,
@@ -1940,6 +1964,46 @@ static int ahash_digest(struct ahash_request *areq)
return ahash_process_req(areq, areq->nbytes);
}
+static int ahash_export(struct ahash_request *areq, void *out)
+{
+ struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
+ struct talitos_export_state *export = out;
+
+ memcpy(export->hw_context, req_ctx->hw_context,
+ req_ctx->hw_context_size);
+ memcpy(export->buf, req_ctx->buf, req_ctx->nbuf);
+ export->swinit = req_ctx->swinit;
+ export->first = req_ctx->first;
+ export->last = req_ctx->last;
+ export->to_hash_later = req_ctx->to_hash_later;
+ export->nbuf = req_ctx->nbuf;
+
+ return 0;
+}
+
+static int ahash_import(struct ahash_request *areq, const void *in)
+{
+ struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
+ const struct talitos_export_state *export = in;
+
+ memset(req_ctx, 0, sizeof(*req_ctx));
+ req_ctx->hw_context_size =
+ (crypto_ahash_digestsize(tfm) <= SHA256_DIGEST_SIZE)
+ ? TALITOS_MDEU_CONTEXT_SIZE_MD5_SHA1_SHA256
+ : TALITOS_MDEU_CONTEXT_SIZE_SHA384_SHA512;
+ memcpy(req_ctx->hw_context, export->hw_context,
+ req_ctx->hw_context_size);
+ memcpy(req_ctx->buf, export->buf, export->nbuf);
+ req_ctx->swinit = export->swinit;
+ req_ctx->first = export->first;
+ req_ctx->last = export->last;
+ req_ctx->to_hash_later = export->to_hash_later;
+ req_ctx->nbuf = export->nbuf;
+
+ return 0;
+}
+
struct keyhash_result {
struct completion completion;
int err;
@@ -2334,6 +2398,7 @@ static struct talitos_alg_template driver_algs[] = {
{ .type = CRYPTO_ALG_TYPE_AHASH,
.alg.hash = {
.halg.digestsize = MD5_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct talitos_export_state),
.halg.base = {
.cra_name = "md5",
.cra_driver_name = "md5-talitos",
@@ -2349,6 +2414,7 @@ static struct talitos_alg_template driver_algs[] = {
{ .type = CRYPTO_ALG_TYPE_AHASH,
.alg.hash = {
.halg.digestsize = SHA1_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct talitos_export_state),
.halg.base = {
.cra_name = "sha1",
.cra_driver_name = "sha1-talitos",
@@ -2364,6 +2430,7 @@ static struct talitos_alg_template driver_algs[] = {
{ .type = CRYPTO_ALG_TYPE_AHASH,
.alg.hash = {
.halg.digestsize = SHA224_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct talitos_export_state),
.halg.base = {
.cra_name = "sha224",
.cra_driver_name = "sha224-talitos",
@@ -2379,6 +2446,7 @@ static struct talitos_alg_template driver_algs[] = {
{ .type = CRYPTO_ALG_TYPE_AHASH,
.alg.hash = {
.halg.digestsize = SHA256_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct talitos_export_state),
.halg.base = {
.cra_name = "sha256",
.cra_driver_name = "sha256-talitos",
@@ -2394,6 +2462,7 @@ static struct talitos_alg_template driver_algs[] = {
{ .type = CRYPTO_ALG_TYPE_AHASH,
.alg.hash = {
.halg.digestsize = SHA384_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct talitos_export_state),
.halg.base = {
.cra_name = "sha384",
.cra_driver_name = "sha384-talitos",
@@ -2409,6 +2478,7 @@ static struct talitos_alg_template driver_algs[] = {
{ .type = CRYPTO_ALG_TYPE_AHASH,
.alg.hash = {
.halg.digestsize = SHA512_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct talitos_export_state),
.halg.base = {
.cra_name = "sha512",
.cra_driver_name = "sha512-talitos",
@@ -2424,6 +2494,7 @@ static struct talitos_alg_template driver_algs[] = {
{ .type = CRYPTO_ALG_TYPE_AHASH,
.alg.hash = {
.halg.digestsize = MD5_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct talitos_export_state),
.halg.base = {
.cra_name = "hmac(md5)",
.cra_driver_name = "hmac-md5-talitos",
@@ -2439,6 +2510,7 @@ static struct talitos_alg_template driver_algs[] = {
{ .type = CRYPTO_ALG_TYPE_AHASH,
.alg.hash = {
.halg.digestsize = SHA1_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct talitos_export_state),
.halg.base = {
.cra_name = "hmac(sha1)",
.cra_driver_name = "hmac-sha1-talitos",
@@ -2454,6 +2526,7 @@ static struct talitos_alg_template driver_algs[] = {
{ .type = CRYPTO_ALG_TYPE_AHASH,
.alg.hash = {
.halg.digestsize = SHA224_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct talitos_export_state),
.halg.base = {
.cra_name = "hmac(sha224)",
.cra_driver_name = "hmac-sha224-talitos",
@@ -2469,6 +2542,7 @@ static struct talitos_alg_template driver_algs[] = {
{ .type = CRYPTO_ALG_TYPE_AHASH,
.alg.hash = {
.halg.digestsize = SHA256_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct talitos_export_state),
.halg.base = {
.cra_name = "hmac(sha256)",
.cra_driver_name = "hmac-sha256-talitos",
@@ -2484,6 +2558,7 @@ static struct talitos_alg_template driver_algs[] = {
{ .type = CRYPTO_ALG_TYPE_AHASH,
.alg.hash = {
.halg.digestsize = SHA384_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct talitos_export_state),
.halg.base = {
.cra_name = "hmac(sha384)",
.cra_driver_name = "hmac-sha384-talitos",
@@ -2499,6 +2574,7 @@ static struct talitos_alg_template driver_algs[] = {
{ .type = CRYPTO_ALG_TYPE_AHASH,
.alg.hash = {
.halg.digestsize = SHA512_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct talitos_export_state),
.halg.base = {
.cra_name = "hmac(sha512)",
.cra_driver_name = "hmac-sha512-talitos",
@@ -2519,21 +2595,11 @@ struct talitos_crypto_alg {
struct talitos_alg_template algt;
};
-static int talitos_cra_init(struct crypto_tfm *tfm)
+static int talitos_init_common(struct talitos_ctx *ctx,
+ struct talitos_crypto_alg *talitos_alg)
{
- struct crypto_alg *alg = tfm->__crt_alg;
- struct talitos_crypto_alg *talitos_alg;
- struct talitos_ctx *ctx = crypto_tfm_ctx(tfm);
struct talitos_private *priv;
- if ((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_AHASH)
- talitos_alg = container_of(__crypto_ahash_alg(alg),
- struct talitos_crypto_alg,
- algt.alg.hash);
- else
- talitos_alg = container_of(alg, struct talitos_crypto_alg,
- algt.alg.crypto);
-
/* update context with ptr to dev */
ctx->dev = talitos_alg->dev;
@@ -2551,10 +2617,33 @@ static int talitos_cra_init(struct crypto_tfm *tfm)
return 0;
}
+static int talitos_cra_init(struct crypto_tfm *tfm)
+{
+ struct crypto_alg *alg = tfm->__crt_alg;
+ struct talitos_crypto_alg *talitos_alg;
+ struct talitos_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ if ((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_AHASH)
+ talitos_alg = container_of(__crypto_ahash_alg(alg),
+ struct talitos_crypto_alg,
+ algt.alg.hash);
+ else
+ talitos_alg = container_of(alg, struct talitos_crypto_alg,
+ algt.alg.crypto);
+
+ return talitos_init_common(ctx, talitos_alg);
+}
+
static int talitos_cra_init_aead(struct crypto_aead *tfm)
{
- talitos_cra_init(crypto_aead_tfm(tfm));
- return 0;
+ struct aead_alg *alg = crypto_aead_alg(tfm);
+ struct talitos_crypto_alg *talitos_alg;
+ struct talitos_ctx *ctx = crypto_aead_ctx(tfm);
+
+ talitos_alg = container_of(alg, struct talitos_crypto_alg,
+ algt.alg.aead);
+
+ return talitos_init_common(ctx, talitos_alg);
}
static int talitos_cra_init_ahash(struct crypto_tfm *tfm)
@@ -2677,6 +2766,8 @@ static struct talitos_crypto_alg *talitos_alg_alloc(struct device *dev,
t_alg->algt.alg.hash.finup = ahash_finup;
t_alg->algt.alg.hash.digest = ahash_digest;
t_alg->algt.alg.hash.setkey = ahash_setkey;
+ t_alg->algt.alg.hash.import = ahash_import;
+ t_alg->algt.alg.hash.export = ahash_export;
if (!(priv->features & TALITOS_FTR_HMAC_OK) &&
!strncmp(alg->cra_name, "hmac", 4)) {
diff --git a/drivers/crypto/ux500/hash/hash_core.c b/drivers/crypto/ux500/hash/hash_core.c
index 66b1c3313e2e..cd4398498495 100644
--- a/drivers/crypto/ux500/hash/hash_core.c
+++ b/drivers/crypto/ux500/hash/hash_core.c
@@ -797,7 +797,7 @@ static int hash_process_data(struct hash_device_data *device_data,
&device_data->state);
memmove(req_ctx->state.buffer,
device_data->state.buffer,
- HASH_BLOCK_SIZE / sizeof(u32));
+ HASH_BLOCK_SIZE);
if (ret) {
dev_err(device_data->dev,
"%s: hash_resume_state() failed!\n",
@@ -848,7 +848,7 @@ static int hash_process_data(struct hash_device_data *device_data,
memmove(device_data->state.buffer,
req_ctx->state.buffer,
- HASH_BLOCK_SIZE / sizeof(u32));
+ HASH_BLOCK_SIZE);
if (ret) {
dev_err(device_data->dev, "%s: hash_save_state() failed!\n",
__func__);
diff --git a/drivers/crypto/vmx/aes_cbc.c b/drivers/crypto/vmx/aes_cbc.c
index 0b8fe2ec5315..f3801b983f42 100644
--- a/drivers/crypto/vmx/aes_cbc.c
+++ b/drivers/crypto/vmx/aes_cbc.c
@@ -182,7 +182,7 @@ struct crypto_alg p8_aes_cbc_alg = {
.cra_name = "cbc(aes)",
.cra_driver_name = "p8_aes_cbc",
.cra_module = THIS_MODULE,
- .cra_priority = 1000,
+ .cra_priority = 2000,
.cra_type = &crypto_blkcipher_type,
.cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_NEED_FALLBACK,
.cra_alignmask = 0,
diff --git a/drivers/crypto/vmx/aes_ctr.c b/drivers/crypto/vmx/aes_ctr.c
index ee1306cd8f59..404a1b69a3ab 100644
--- a/drivers/crypto/vmx/aes_ctr.c
+++ b/drivers/crypto/vmx/aes_ctr.c
@@ -166,7 +166,7 @@ struct crypto_alg p8_aes_ctr_alg = {
.cra_name = "ctr(aes)",
.cra_driver_name = "p8_aes_ctr",
.cra_module = THIS_MODULE,
- .cra_priority = 1000,
+ .cra_priority = 2000,
.cra_type = &crypto_blkcipher_type,
.cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_NEED_FALLBACK,
.cra_alignmask = 0,
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index bc1c5f6dd4bd..844a8ad666a9 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -806,7 +806,7 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
struct devfreq *df = to_devfreq(dev);
int ret;
char str_governor[DEVFREQ_NAME_LEN + 1];
- struct devfreq_governor *governor;
+ const struct devfreq_governor *governor, *prev_gov;
ret = sscanf(buf, "%" __stringify(DEVFREQ_NAME_LEN) "s", str_governor);
if (ret != 1)
@@ -831,12 +831,21 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
goto out;
}
}
+ prev_gov = df->governor;
df->governor = governor;
strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN);
ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
- if (ret)
+ if (ret) {
dev_warn(dev, "%s: Governor %s not started(%d)\n",
__func__, df->governor->name, ret);
+ if (prev_gov) {
+ df->governor = prev_gov;
+ strncpy(df->governor_name, prev_gov->name,
+ DEVFREQ_NAME_LEN);
+ df->governor->event_handler(df, DEVFREQ_GOV_START,
+ NULL);
+ }
+ }
out:
mutex_unlock(&devfreq_list_lock);
diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c
index 4f099ea29f83..c66133b5e852 100644
--- a/drivers/dma/dw/core.c
+++ b/drivers/dma/dw/core.c
@@ -130,26 +130,14 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc)
static void dwc_initialize(struct dw_dma_chan *dwc)
{
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
- struct dw_dma_slave *dws = dwc->chan.private;
u32 cfghi = DWC_CFGH_FIFO_MODE;
u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority);
if (dwc->initialized == true)
return;
- if (dws) {
- /*
- * We need controller-specific data to set up slave
- * transfers.
- */
- BUG_ON(!dws->dma_dev || dws->dma_dev != dw->dma.dev);
-
- cfghi |= DWC_CFGH_DST_PER(dws->dst_id);
- cfghi |= DWC_CFGH_SRC_PER(dws->src_id);
- } else {
- cfghi |= DWC_CFGH_DST_PER(dwc->dst_id);
- cfghi |= DWC_CFGH_SRC_PER(dwc->src_id);
- }
+ cfghi |= DWC_CFGH_DST_PER(dwc->dst_id);
+ cfghi |= DWC_CFGH_SRC_PER(dwc->src_id);
channel_writel(dwc, CFG_LO, cfglo);
channel_writel(dwc, CFG_HI, cfghi);
@@ -936,7 +924,7 @@ bool dw_dma_filter(struct dma_chan *chan, void *param)
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma_slave *dws = param;
- if (!dws || dws->dma_dev != chan->device->dev)
+ if (dws->dma_dev != chan->device->dev)
return false;
/* We have to copy data since dws can be temporary storage */
@@ -1160,6 +1148,14 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
* doesn't mean what you think it means), and status writeback.
*/
+ /*
+ * We need controller-specific data to set up slave transfers.
+ */
+ if (chan->private && !dw_dma_filter(chan, chan->private)) {
+ dev_warn(chan2dev(chan), "Wrong controller-specific data\n");
+ return -EINVAL;
+ }
+
/* Enable controller here if needed */
if (!dw->in_use)
dw_dma_on(dw);
@@ -1221,6 +1217,14 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
spin_lock_irqsave(&dwc->lock, flags);
list_splice_init(&dwc->free_list, &list);
dwc->descs_allocated = 0;
+
+ /* Clear custom channel configuration */
+ dwc->src_id = 0;
+ dwc->dst_id = 0;
+
+ dwc->src_master = 0;
+ dwc->dst_master = 0;
+
dwc->initialized = false;
/* Disable interrupts */
diff --git a/drivers/dma/hsu/hsu.c b/drivers/dma/hsu/hsu.c
index 823ad728aecf..efc02b98e6ba 100644
--- a/drivers/dma/hsu/hsu.c
+++ b/drivers/dma/hsu/hsu.c
@@ -135,7 +135,7 @@ static u32 hsu_dma_chan_get_sr(struct hsu_dma_chan *hsuc)
sr = hsu_chan_readl(hsuc, HSU_CH_SR);
spin_unlock_irqrestore(&hsuc->vchan.lock, flags);
- return sr;
+ return sr & ~(HSU_CH_SR_DESCE_ANY | HSU_CH_SR_CDESC_ANY);
}
irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short nr)
diff --git a/drivers/dma/hsu/hsu.h b/drivers/dma/hsu/hsu.h
index f06579c6d548..26da2865b025 100644
--- a/drivers/dma/hsu/hsu.h
+++ b/drivers/dma/hsu/hsu.h
@@ -41,6 +41,9 @@
#define HSU_CH_SR_DESCTO(x) BIT(8 + (x))
#define HSU_CH_SR_DESCTO_ANY (BIT(11) | BIT(10) | BIT(9) | BIT(8))
#define HSU_CH_SR_CHE BIT(15)
+#define HSU_CH_SR_DESCE(x) BIT(16 + (x))
+#define HSU_CH_SR_DESCE_ANY (BIT(19) | BIT(18) | BIT(17) | BIT(16))
+#define HSU_CH_SR_CDESC_ANY (BIT(31) | BIT(30))
/* Bits in HSU_CH_CR */
#define HSU_CH_CR_CHA BIT(0)
diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c
index a59061e4221a..55f5d33f6dc7 100644
--- a/drivers/dma/pxa_dma.c
+++ b/drivers/dma/pxa_dma.c
@@ -122,6 +122,7 @@ struct pxad_chan {
struct pxad_device {
struct dma_device slave;
int nr_chans;
+ int nr_requestors;
void __iomem *base;
struct pxad_phy *phys;
spinlock_t phy_lock; /* Phy association */
@@ -473,7 +474,7 @@ static void pxad_free_phy(struct pxad_chan *chan)
return;
/* clear the channel mapping in DRCMR */
- if (chan->drcmr <= DRCMR_CHLNUM) {
+ if (chan->drcmr <= pdev->nr_requestors) {
reg = pxad_drcmr(chan->drcmr);
writel_relaxed(0, chan->phy->base + reg);
}
@@ -509,6 +510,7 @@ static bool is_running_chan_misaligned(struct pxad_chan *chan)
static void phy_enable(struct pxad_phy *phy, bool misaligned)
{
+ struct pxad_device *pdev;
u32 reg, dalgn;
if (!phy->vchan)
@@ -518,7 +520,8 @@ static void phy_enable(struct pxad_phy *phy, bool misaligned)
"%s(); phy=%p(%d) misaligned=%d\n", __func__,
phy, phy->idx, misaligned);
- if (phy->vchan->drcmr <= DRCMR_CHLNUM) {
+ pdev = to_pxad_dev(phy->vchan->vc.chan.device);
+ if (phy->vchan->drcmr <= pdev->nr_requestors) {
reg = pxad_drcmr(phy->vchan->drcmr);
writel_relaxed(DRCMR_MAPVLD | phy->idx, phy->base + reg);
}
@@ -914,6 +917,7 @@ static void pxad_get_config(struct pxad_chan *chan,
{
u32 maxburst = 0, dev_addr = 0;
enum dma_slave_buswidth width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
+ struct pxad_device *pdev = to_pxad_dev(chan->vc.chan.device);
*dcmd = 0;
if (dir == DMA_DEV_TO_MEM) {
@@ -922,7 +926,7 @@ static void pxad_get_config(struct pxad_chan *chan,
dev_addr = chan->cfg.src_addr;
*dev_src = dev_addr;
*dcmd |= PXA_DCMD_INCTRGADDR;
- if (chan->drcmr <= DRCMR_CHLNUM)
+ if (chan->drcmr <= pdev->nr_requestors)
*dcmd |= PXA_DCMD_FLOWSRC;
}
if (dir == DMA_MEM_TO_DEV) {
@@ -931,7 +935,7 @@ static void pxad_get_config(struct pxad_chan *chan,
dev_addr = chan->cfg.dst_addr;
*dev_dst = dev_addr;
*dcmd |= PXA_DCMD_INCSRCADDR;
- if (chan->drcmr <= DRCMR_CHLNUM)
+ if (chan->drcmr <= pdev->nr_requestors)
*dcmd |= PXA_DCMD_FLOWTRG;
}
if (dir == DMA_MEM_TO_MEM)
@@ -1341,13 +1345,15 @@ static struct dma_chan *pxad_dma_xlate(struct of_phandle_args *dma_spec,
static int pxad_init_dmadev(struct platform_device *op,
struct pxad_device *pdev,
- unsigned int nr_phy_chans)
+ unsigned int nr_phy_chans,
+ unsigned int nr_requestors)
{
int ret;
unsigned int i;
struct pxad_chan *c;
pdev->nr_chans = nr_phy_chans;
+ pdev->nr_requestors = nr_requestors;
INIT_LIST_HEAD(&pdev->slave.channels);
pdev->slave.device_alloc_chan_resources = pxad_alloc_chan_resources;
pdev->slave.device_free_chan_resources = pxad_free_chan_resources;
@@ -1382,7 +1388,7 @@ static int pxad_probe(struct platform_device *op)
const struct of_device_id *of_id;
struct mmp_dma_platdata *pdata = dev_get_platdata(&op->dev);
struct resource *iores;
- int ret, dma_channels = 0;
+ int ret, dma_channels = 0, nb_requestors = 0;
const enum dma_slave_buswidth widths =
DMA_SLAVE_BUSWIDTH_1_BYTE | DMA_SLAVE_BUSWIDTH_2_BYTES |
DMA_SLAVE_BUSWIDTH_4_BYTES;
@@ -1399,13 +1405,23 @@ static int pxad_probe(struct platform_device *op)
return PTR_ERR(pdev->base);
of_id = of_match_device(pxad_dt_ids, &op->dev);
- if (of_id)
+ if (of_id) {
of_property_read_u32(op->dev.of_node, "#dma-channels",
&dma_channels);
- else if (pdata && pdata->dma_channels)
+ ret = of_property_read_u32(op->dev.of_node, "#dma-requests",
+ &nb_requestors);
+ if (ret) {
+ dev_warn(pdev->slave.dev,
+ "#dma-requests set to default 32 as missing in OF: %d",
+ ret);
+ nb_requestors = 32;
+ };
+ } else if (pdata && pdata->dma_channels) {
dma_channels = pdata->dma_channels;
- else
+ nb_requestors = pdata->nb_requestors;
+ } else {
dma_channels = 32; /* default 32 channel */
+ }
dma_cap_set(DMA_SLAVE, pdev->slave.cap_mask);
dma_cap_set(DMA_MEMCPY, pdev->slave.cap_mask);
@@ -1422,7 +1438,7 @@ static int pxad_probe(struct platform_device *op)
pdev->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
pdev->slave.dev = &op->dev;
- ret = pxad_init_dmadev(op, pdev, dma_channels);
+ ret = pxad_init_dmadev(op, pdev, dma_channels, nb_requestors);
if (ret) {
dev_err(pdev->slave.dev, "unable to register\n");
return ret;
@@ -1441,7 +1457,8 @@ static int pxad_probe(struct platform_device *op)
platform_set_drvdata(op, pdev);
pxad_init_debugfs(pdev);
- dev_info(pdev->slave.dev, "initialized %d channels\n", dma_channels);
+ dev_info(pdev->slave.dev, "initialized %d channels on %d requestors\n",
+ dma_channels, nb_requestors);
return 0;
}
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index 01087a38da22..792bdae2b91d 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -1866,7 +1866,7 @@ static int i7core_mce_check_error(struct notifier_block *nb, unsigned long val,
i7_dev = get_i7core_dev(mce->socketid);
if (!i7_dev)
- return NOTIFY_BAD;
+ return NOTIFY_DONE;
mci = i7_dev->mci;
pvt = mci->pvt_info;
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index cbee3179ec08..ca64b174f8a3 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -218,8 +218,11 @@ static const u32 rir_offset[MAX_RIR_RANGES][MAX_RIR_WAY] = {
{ 0x1a0, 0x1a4, 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc },
};
-#define RIR_RNK_TGT(reg) GET_BITFIELD(reg, 16, 19)
-#define RIR_OFFSET(reg) GET_BITFIELD(reg, 2, 14)
+#define RIR_RNK_TGT(type, reg) (((type) == BROADWELL) ? \
+ GET_BITFIELD(reg, 20, 23) : GET_BITFIELD(reg, 16, 19))
+
+#define RIR_OFFSET(type, reg) (((type) == HASWELL || (type) == BROADWELL) ? \
+ GET_BITFIELD(reg, 2, 15) : GET_BITFIELD(reg, 2, 14))
/* Device 16, functions 2-7 */
@@ -1175,14 +1178,14 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
pci_read_config_dword(pvt->pci_tad[i],
rir_offset[j][k],
&reg);
- tmp_mb = RIR_OFFSET(reg) << 6;
+ tmp_mb = RIR_OFFSET(pvt->info.type, reg) << 6;
gb = div_u64_rem(tmp_mb, 1024, &mb);
edac_dbg(0, "CH#%d RIR#%d INTL#%d, offset %u.%03u GB (0x%016Lx), tgt: %d, reg=0x%08x\n",
i, j, k,
gb, (mb*1000)/1024,
((u64)tmp_mb) << 20L,
- (u32)RIR_RNK_TGT(reg),
+ (u32)RIR_RNK_TGT(pvt->info.type, reg),
reg);
}
}
@@ -1396,7 +1399,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
}
ch_way = TAD_CH(reg) + 1;
- sck_way = 1 << TAD_SOCK(reg);
+ sck_way = TAD_SOCK(reg);
if (ch_way == 3)
idx = addr >> 6;
@@ -1435,7 +1438,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
switch(ch_way) {
case 2:
case 4:
- sck_xch = 1 << sck_way * (ch_way >> 1);
+ sck_xch = (1 << sck_way) * (ch_way >> 1);
break;
default:
sprintf(msg, "Invalid mirror set. Can't decode addr");
@@ -1471,7 +1474,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
ch_addr = addr - offset;
ch_addr >>= (6 + shiftup);
- ch_addr /= ch_way * sck_way;
+ ch_addr /= sck_xch;
ch_addr <<= (6 + shiftup);
ch_addr |= addr & ((1 << (6 + shiftup)) - 1);
@@ -1512,7 +1515,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
pci_read_config_dword(pvt->pci_tad[ch_add + base_ch],
rir_offset[n_rir][idx],
&reg);
- *rank = RIR_RNK_TGT(reg);
+ *rank = RIR_RNK_TGT(pvt->info.type, reg);
edac_dbg(0, "RIR#%d: channel address 0x%08Lx < 0x%08Lx, RIR interleave %d, index %d\n",
n_rir,
@@ -2254,7 +2257,7 @@ static int sbridge_mce_check_error(struct notifier_block *nb, unsigned long val,
mci = get_mci_for_node_id(mce->socketid);
if (!mci)
- return NOTIFY_BAD;
+ return NOTIFY_DONE;
pvt = mci->pvt_info;
/*
diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c
index 9f9ea334399c..b6cb30d207be 100644
--- a/drivers/extcon/extcon-max77843.c
+++ b/drivers/extcon/extcon-max77843.c
@@ -803,7 +803,7 @@ static int max77843_muic_probe(struct platform_device *pdev)
/* Clear IRQ bits before request IRQs */
ret = regmap_bulk_read(max77843->regmap_muic,
MAX77843_MUIC_REG_INT1, info->status,
- MAX77843_MUIC_IRQ_NUM);
+ MAX77843_MUIC_STATUS_NUM);
if (ret) {
dev_err(&pdev->dev, "Failed to Clear IRQ bits\n");
goto err_muic_irq;
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 027ca212179f..3b52677f459a 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -180,6 +180,7 @@ static int generic_ops_register(void)
{
generic_ops.get_variable = efi.get_variable;
generic_ops.set_variable = efi.set_variable;
+ generic_ops.set_variable_nonblocking = efi.set_variable_nonblocking;
generic_ops.get_next_variable = efi.get_next_variable;
generic_ops.query_variable_store = efi_query_variable_store;
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index b2a172d93a08..d775e2bfc017 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -8,7 +8,7 @@ cflags-$(CONFIG_X86_32) := -march=i386
cflags-$(CONFIG_X86_64) := -mcmodel=small
cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2 \
-fPIC -fno-strict-aliasing -mno-red-zone \
- -mno-mmx -mno-sse -DDISABLE_BRANCH_PROFILING
+ -mno-mmx -mno-sse
cflags-$(CONFIG_ARM64) := $(subst -pg,,$(KBUILD_CFLAGS))
cflags-$(CONFIG_ARM) := $(subst -pg,,$(KBUILD_CFLAGS)) \
@@ -16,7 +16,7 @@ cflags-$(CONFIG_ARM) := $(subst -pg,,$(KBUILD_CFLAGS)) \
cflags-$(CONFIG_EFI_ARMSTUB) += -I$(srctree)/scripts/dtc/libfdt
-KBUILD_CFLAGS := $(cflags-y) \
+KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
$(call cc-option,-ffreestanding) \
$(call cc-option,-fno-stack-protector)
@@ -35,7 +35,8 @@ $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o string.o \
$(patsubst %.c,lib-%.o,$(arm-deps))
-lib-$(CONFIG_ARM64) += arm64-stub.o
+lib-$(CONFIG_ARM) += arm32-stub.o
+lib-$(CONFIG_ARM64) += arm64-stub.o random.o
CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
#
diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c
index 950c87f5d279..d5aa1d16154f 100644
--- a/drivers/firmware/efi/libstub/arm-stub.c
+++ b/drivers/firmware/efi/libstub/arm-stub.c
@@ -18,6 +18,8 @@
#include "efistub.h"
+bool __nokaslr;
+
static int efi_secureboot_enabled(efi_system_table_t *sys_table_arg)
{
static efi_guid_t const var_guid = EFI_GLOBAL_VARIABLE_GUID;
@@ -207,14 +209,6 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
pr_efi_err(sys_table, "Failed to find DRAM base\n");
goto fail;
}
- status = handle_kernel_image(sys_table, image_addr, &image_size,
- &reserve_addr,
- &reserve_size,
- dram_base, image);
- if (status != EFI_SUCCESS) {
- pr_efi_err(sys_table, "Failed to relocate kernel\n");
- goto fail;
- }
/*
* Get the command line from EFI, using the LOADED_IMAGE
@@ -224,7 +218,28 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
cmdline_ptr = efi_convert_cmdline(sys_table, image, &cmdline_size);
if (!cmdline_ptr) {
pr_efi_err(sys_table, "getting command line via LOADED_IMAGE_PROTOCOL\n");
- goto fail_free_image;
+ goto fail;
+ }
+
+ /* check whether 'nokaslr' was passed on the command line */
+ if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
+ static const u8 default_cmdline[] = CONFIG_CMDLINE;
+ const u8 *str, *cmdline = cmdline_ptr;
+
+ if (IS_ENABLED(CONFIG_CMDLINE_FORCE))
+ cmdline = default_cmdline;
+ str = strstr(cmdline, "nokaslr");
+ if (str == cmdline || (str > cmdline && *(str - 1) == ' '))
+ __nokaslr = true;
+ }
+
+ status = handle_kernel_image(sys_table, image_addr, &image_size,
+ &reserve_addr,
+ &reserve_size,
+ dram_base, image);
+ if (status != EFI_SUCCESS) {
+ pr_efi_err(sys_table, "Failed to relocate kernel\n");
+ goto fail_free_cmdline;
}
status = efi_parse_options(cmdline_ptr);
@@ -244,7 +259,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
if (status != EFI_SUCCESS) {
pr_efi_err(sys_table, "Failed to load device tree!\n");
- goto fail_free_cmdline;
+ goto fail_free_image;
}
}
@@ -286,12 +301,11 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
efi_free(sys_table, initrd_size, initrd_addr);
efi_free(sys_table, fdt_size, fdt_addr);
-fail_free_cmdline:
- efi_free(sys_table, cmdline_size, (unsigned long)cmdline_ptr);
-
fail_free_image:
efi_free(sys_table, image_size, *image_addr);
efi_free(sys_table, reserve_size, reserve_addr);
+fail_free_cmdline:
+ efi_free(sys_table, cmdline_size, (unsigned long)cmdline_ptr);
fail:
return EFI_ERROR;
}
diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c
index 78dfbd34b6bf..377d935a3380 100644
--- a/drivers/firmware/efi/libstub/arm64-stub.c
+++ b/drivers/firmware/efi/libstub/arm64-stub.c
@@ -13,6 +13,10 @@
#include <asm/efi.h>
#include <asm/sections.h>
+#include "efistub.h"
+
+extern bool __nokaslr;
+
efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg,
unsigned long *image_addr,
unsigned long *image_size,
@@ -23,26 +27,61 @@ efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg,
{
efi_status_t status;
unsigned long kernel_size, kernel_memsize = 0;
- unsigned long nr_pages;
void *old_image_addr = (void *)*image_addr;
unsigned long preferred_offset;
+ u64 phys_seed = 0;
+
+ if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
+ if (!__nokaslr) {
+ status = efi_get_random_bytes(sys_table_arg,
+ sizeof(phys_seed),
+ (u8 *)&phys_seed);
+ if (status == EFI_NOT_FOUND) {
+ pr_efi(sys_table_arg, "EFI_RNG_PROTOCOL unavailable, no randomness supplied\n");
+ } else if (status != EFI_SUCCESS) {
+ pr_efi_err(sys_table_arg, "efi_get_random_bytes() failed\n");
+ return status;
+ }
+ } else {
+ pr_efi(sys_table_arg, "KASLR disabled on kernel command line\n");
+ }
+ }
/*
* The preferred offset of the kernel Image is TEXT_OFFSET bytes beyond
* a 2 MB aligned base, which itself may be lower than dram_base, as
* long as the resulting offset equals or exceeds it.
*/
- preferred_offset = round_down(dram_base, SZ_2M) + TEXT_OFFSET;
+ preferred_offset = round_down(dram_base, MIN_KIMG_ALIGN) + TEXT_OFFSET;
if (preferred_offset < dram_base)
- preferred_offset += SZ_2M;
+ preferred_offset += MIN_KIMG_ALIGN;
- /* Relocate the image, if required. */
kernel_size = _edata - _text;
- if (*image_addr != preferred_offset) {
- kernel_memsize = kernel_size + (_end - _edata);
+ kernel_memsize = kernel_size + (_end - _edata);
+
+ if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) {
+ /*
+ * If CONFIG_DEBUG_ALIGN_RODATA is not set, produce a
+ * displacement in the interval [0, MIN_KIMG_ALIGN) that
+ * is a multiple of the minimal segment alignment (SZ_64K)
+ */
+ u32 mask = (MIN_KIMG_ALIGN - 1) & ~(SZ_64K - 1);
+ u32 offset = !IS_ENABLED(CONFIG_DEBUG_ALIGN_RODATA) ?
+ (phys_seed >> 32) & mask : TEXT_OFFSET;
+
+ /*
+ * If KASLR is enabled, and we have some randomness available,
+ * locate the kernel at a randomized offset in physical memory.
+ */
+ *reserve_size = kernel_memsize + offset;
+ status = efi_random_alloc(sys_table_arg, *reserve_size,
+ MIN_KIMG_ALIGN, reserve_addr,
+ (u32)phys_seed);
+ *image_addr = *reserve_addr + offset;
+ } else {
/*
- * First, try a straight allocation at the preferred offset.
+ * Else, try a straight allocation at the preferred offset.
* This will work around the issue where, if dram_base == 0x0,
* efi_low_alloc() refuses to allocate at 0x0 (to prevent the
* address of the allocation to be mistaken for a FAIL return
@@ -52,27 +91,31 @@ efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg,
* Mustang), we can still place the kernel at the address
* 'dram_base + TEXT_OFFSET'.
*/
+ if (*image_addr == preferred_offset)
+ return EFI_SUCCESS;
+
*image_addr = *reserve_addr = preferred_offset;
- nr_pages = round_up(kernel_memsize, EFI_ALLOC_ALIGN) /
- EFI_PAGE_SIZE;
+ *reserve_size = round_up(kernel_memsize, EFI_ALLOC_ALIGN);
+
status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS,
- EFI_LOADER_DATA, nr_pages,
+ EFI_LOADER_DATA,
+ *reserve_size / EFI_PAGE_SIZE,
(efi_physical_addr_t *)reserve_addr);
- if (status != EFI_SUCCESS) {
- kernel_memsize += TEXT_OFFSET;
- status = efi_low_alloc(sys_table_arg, kernel_memsize,
- SZ_2M, reserve_addr);
+ }
- if (status != EFI_SUCCESS) {
- pr_efi_err(sys_table_arg, "Failed to relocate kernel\n");
- return status;
- }
- *image_addr = *reserve_addr + TEXT_OFFSET;
+ if (status != EFI_SUCCESS) {
+ *reserve_size = kernel_memsize + TEXT_OFFSET;
+ status = efi_low_alloc(sys_table_arg, *reserve_size,
+ MIN_KIMG_ALIGN, reserve_addr);
+
+ if (status != EFI_SUCCESS) {
+ pr_efi_err(sys_table_arg, "Failed to relocate kernel\n");
+ *reserve_size = 0;
+ return status;
}
- memcpy((void *)*image_addr, old_image_addr, kernel_size);
- *reserve_size = kernel_memsize;
+ *image_addr = *reserve_addr + TEXT_OFFSET;
}
-
+ memcpy((void *)*image_addr, old_image_addr, kernel_size);
return EFI_SUCCESS;
}
diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c
index f07d4a67fa76..29ed2f9b218c 100644
--- a/drivers/firmware/efi/libstub/efi-stub-helper.c
+++ b/drivers/firmware/efi/libstub/efi-stub-helper.c
@@ -649,6 +649,10 @@ static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n)
return dst;
}
+#ifndef MAX_CMDLINE_ADDRESS
+#define MAX_CMDLINE_ADDRESS ULONG_MAX
+#endif
+
/*
* Convert the unicode UEFI command line to ASCII to pass to kernel.
* Size of memory allocated return in *cmd_line_len.
@@ -684,7 +688,8 @@ char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
options_bytes++; /* NUL termination */
- status = efi_low_alloc(sys_table_arg, options_bytes, 0, &cmdline_addr);
+ status = efi_high_alloc(sys_table_arg, options_bytes, 0,
+ &cmdline_addr, MAX_CMDLINE_ADDRESS);
if (status != EFI_SUCCESS)
return NULL;
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index 6b6548fda089..5ed3d3f38166 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -43,4 +43,11 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
unsigned long desc_size, efi_memory_desc_t *runtime_map,
int *count);
+efi_status_t efi_get_random_bytes(efi_system_table_t *sys_table,
+ unsigned long size, u8 *out);
+
+efi_status_t efi_random_alloc(efi_system_table_t *sys_table_arg,
+ unsigned long size, unsigned long align,
+ unsigned long *addr, unsigned long random_seed);
+
#endif
diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c
index b62e2f5dcab3..b1c22cf18f7d 100644
--- a/drivers/firmware/efi/libstub/fdt.c
+++ b/drivers/firmware/efi/libstub/fdt.c
@@ -147,6 +147,20 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
if (status)
goto fdt_set_fail;
+ if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
+ efi_status_t efi_status;
+
+ efi_status = efi_get_random_bytes(sys_table, sizeof(fdt_val64),
+ (u8 *)&fdt_val64);
+ if (efi_status == EFI_SUCCESS) {
+ status = fdt_setprop(fdt, node, "kaslr-seed",
+ &fdt_val64, sizeof(fdt_val64));
+ if (status)
+ goto fdt_set_fail;
+ } else if (efi_status != EFI_NOT_FOUND) {
+ return efi_status;
+ }
+ }
return EFI_SUCCESS;
fdt_set_fail:
diff --git a/drivers/firmware/efi/libstub/random.c b/drivers/firmware/efi/libstub/random.c
new file mode 100644
index 000000000000..53f6d3fe6d86
--- /dev/null
+++ b/drivers/firmware/efi/libstub/random.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 Linaro Ltd; <ard.biesheuvel@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/efi.h>
+#include <asm/efi.h>
+
+#include "efistub.h"
+
+struct efi_rng_protocol {
+ efi_status_t (*get_info)(struct efi_rng_protocol *,
+ unsigned long *, efi_guid_t *);
+ efi_status_t (*get_rng)(struct efi_rng_protocol *,
+ efi_guid_t *, unsigned long, u8 *out);
+};
+
+efi_status_t efi_get_random_bytes(efi_system_table_t *sys_table_arg,
+ unsigned long size, u8 *out)
+{
+ efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID;
+ efi_status_t status;
+ struct efi_rng_protocol *rng;
+
+ status = efi_call_early(locate_protocol, &rng_proto, NULL,
+ (void **)&rng);
+ if (status != EFI_SUCCESS)
+ return status;
+
+ return rng->get_rng(rng, NULL, size, out);
+}
+
+/*
+ * Return the number of slots covered by this entry, i.e., the number of
+ * addresses it covers that are suitably aligned and supply enough room
+ * for the allocation.
+ */
+static unsigned long get_entry_num_slots(efi_memory_desc_t *md,
+ unsigned long size,
+ unsigned long align)
+{
+ u64 start, end;
+
+ if (md->type != EFI_CONVENTIONAL_MEMORY)
+ return 0;
+
+ start = round_up(md->phys_addr, align);
+ end = round_down(md->phys_addr + md->num_pages * EFI_PAGE_SIZE - size,
+ align);
+
+ if (start > end)
+ return 0;
+
+ return (end - start + 1) / align;
+}
+
+/*
+ * The UEFI memory descriptors have a virtual address field that is only used
+ * when installing the virtual mapping using SetVirtualAddressMap(). Since it
+ * is unused here, we can reuse it to keep track of each descriptor's slot
+ * count.
+ */
+#define MD_NUM_SLOTS(md) ((md)->virt_addr)
+
+efi_status_t efi_random_alloc(efi_system_table_t *sys_table_arg,
+ unsigned long size,
+ unsigned long align,
+ unsigned long *addr,
+ unsigned long random_seed)
+{
+ unsigned long map_size, desc_size, total_slots = 0, target_slot;
+ efi_status_t status;
+ efi_memory_desc_t *memory_map;
+ int map_offset;
+
+ status = efi_get_memory_map(sys_table_arg, &memory_map, &map_size,
+ &desc_size, NULL, NULL);
+ if (status != EFI_SUCCESS)
+ return status;
+
+ if (align < EFI_ALLOC_ALIGN)
+ align = EFI_ALLOC_ALIGN;
+
+ /* count the suitable slots in each memory map entry */
+ for (map_offset = 0; map_offset < map_size; map_offset += desc_size) {
+ efi_memory_desc_t *md = (void *)memory_map + map_offset;
+ unsigned long slots;
+
+ slots = get_entry_num_slots(md, size, align);
+ MD_NUM_SLOTS(md) = slots;
+ total_slots += slots;
+ }
+
+ /* find a random number between 0 and total_slots */
+ target_slot = (total_slots * (u16)random_seed) >> 16;
+
+ /*
+ * target_slot is now a value in the range [0, total_slots), and so
+ * it corresponds with exactly one of the suitable slots we recorded
+ * when iterating over the memory map the first time around.
+ *
+ * So iterate over the memory map again, subtracting the number of
+ * slots of each entry at each iteration, until we have found the entry
+ * that covers our chosen slot. Use the residual value of target_slot
+ * to calculate the randomly chosen address, and allocate it directly
+ * using EFI_ALLOCATE_ADDRESS.
+ */
+ for (map_offset = 0; map_offset < map_size; map_offset += desc_size) {
+ efi_memory_desc_t *md = (void *)memory_map + map_offset;
+ efi_physical_addr_t target;
+ unsigned long pages;
+
+ if (target_slot >= MD_NUM_SLOTS(md)) {
+ target_slot -= MD_NUM_SLOTS(md);
+ continue;
+ }
+
+ target = round_up(md->phys_addr, align) + target_slot * align;
+ pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+
+ status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS,
+ EFI_LOADER_DATA, pages, &target);
+ if (status == EFI_SUCCESS)
+ *addr = target;
+ break;
+ }
+
+ efi_call_early(free_pool, memory_map);
+
+ return status;
+}
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
index 7f2ea21c730d..6f182fd91a6d 100644
--- a/drivers/firmware/efi/vars.c
+++ b/drivers/firmware/efi/vars.c
@@ -202,29 +202,44 @@ static const struct variable_validate variable_validate[] = {
{ NULL_GUID, "", NULL },
};
+/*
+ * Check if @var_name matches the pattern given in @match_name.
+ *
+ * @var_name: an array of @len non-NUL characters.
+ * @match_name: a NUL-terminated pattern string, optionally ending in "*". A
+ * final "*" character matches any trailing characters @var_name,
+ * including the case when there are none left in @var_name.
+ * @match: on output, the number of non-wildcard characters in @match_name
+ * that @var_name matches, regardless of the return value.
+ * @return: whether @var_name fully matches @match_name.
+ */
static bool
variable_matches(const char *var_name, size_t len, const char *match_name,
int *match)
{
for (*match = 0; ; (*match)++) {
char c = match_name[*match];
- char u = var_name[*match];
- /* Wildcard in the matching name means we've matched */
- if (c == '*')
+ switch (c) {
+ case '*':
+ /* Wildcard in @match_name means we've matched. */
return true;
- /* Case sensitive match */
- if (!c && *match == len)
- return true;
+ case '\0':
+ /* @match_name has ended. Has @var_name too? */
+ return (*match == len);
- if (c != u)
+ default:
+ /*
+ * We've reached a non-wildcard char in @match_name.
+ * Continue only if there's an identical character in
+ * @var_name.
+ */
+ if (*match < len && c == var_name[*match])
+ continue;
return false;
-
- if (!c)
- return true;
+ }
}
- return true;
}
bool
diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c
index 33a1f9779b86..4ea71d505bce 100644
--- a/drivers/gpio/gpio-bcm-kona.c
+++ b/drivers/gpio/gpio-bcm-kona.c
@@ -551,11 +551,11 @@ static void bcm_kona_gpio_reset(struct bcm_kona_gpio *kona_gpio)
/* disable interrupts and clear status */
for (i = 0; i < kona_gpio->num_bank; i++) {
/* Unlock the entire bank first */
- bcm_kona_gpio_write_lock_regs(kona_gpio, i, UNLOCK_CODE);
+ bcm_kona_gpio_write_lock_regs(reg_base, i, UNLOCK_CODE);
writel(0xffffffff, reg_base + GPIO_INT_MASK(i));
writel(0xffffffff, reg_base + GPIO_INT_STATUS(i));
/* Now re-lock the bank */
- bcm_kona_gpio_write_lock_regs(kona_gpio, i, LOCK_CODE);
+ bcm_kona_gpio_write_lock_regs(reg_base, i, LOCK_CODE);
}
}
diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c
index 3a5c7011ad3b..8b830996fe02 100644
--- a/drivers/gpio/gpiolib-legacy.c
+++ b/drivers/gpio/gpiolib-legacy.c
@@ -28,6 +28,10 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
if (!desc && gpio_is_valid(gpio))
return -EPROBE_DEFER;
+ err = gpiod_request(desc, label);
+ if (err)
+ return err;
+
if (flags & GPIOF_OPEN_DRAIN)
set_bit(FLAG_OPEN_DRAIN, &desc->flags);
@@ -37,10 +41,6 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
if (flags & GPIOF_ACTIVE_LOW)
set_bit(FLAG_ACTIVE_LOW, &desc->flags);
- err = gpiod_request(desc, label);
- if (err)
- return err;
-
if (flags & GPIOF_DIR_IN)
err = gpiod_direction_input(desc);
else
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 4e4c3083ae56..06d345b087f8 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -927,14 +927,6 @@ static int __gpiod_request(struct gpio_desc *desc, const char *label)
spin_lock_irqsave(&gpio_lock, flags);
}
done:
- if (status < 0) {
- /* Clear flags that might have been set by the caller before
- * requesting the GPIO.
- */
- clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
- clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
- clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
- }
spin_unlock_irqrestore(&gpio_lock, flags);
return status;
}
@@ -2062,28 +2054,13 @@ struct gpio_desc *__must_check gpiod_get_optional(struct device *dev,
}
EXPORT_SYMBOL_GPL(gpiod_get_optional);
-/**
- * gpiod_parse_flags - helper function to parse GPIO lookup flags
- * @desc: gpio to be setup
- * @lflags: gpio_lookup_flags - returned from of_find_gpio() or
- * of_get_gpio_hog()
- *
- * Set the GPIO descriptor flags based on the given GPIO lookup flags.
- */
-static void gpiod_parse_flags(struct gpio_desc *desc, unsigned long lflags)
-{
- if (lflags & GPIO_ACTIVE_LOW)
- set_bit(FLAG_ACTIVE_LOW, &desc->flags);
- if (lflags & GPIO_OPEN_DRAIN)
- set_bit(FLAG_OPEN_DRAIN, &desc->flags);
- if (lflags & GPIO_OPEN_SOURCE)
- set_bit(FLAG_OPEN_SOURCE, &desc->flags);
-}
/**
* gpiod_configure_flags - helper function to configure a given GPIO
* @desc: gpio whose value will be assigned
* @con_id: function within the GPIO consumer
+ * @lflags: gpio_lookup_flags - returned from of_find_gpio() or
+ * of_get_gpio_hog()
* @dflags: gpiod_flags - optional GPIO initialization flags
*
* Return 0 on success, -ENOENT if no GPIO has been assigned to the
@@ -2091,10 +2068,17 @@ static void gpiod_parse_flags(struct gpio_desc *desc, unsigned long lflags)
* occurred while trying to acquire the GPIO.
*/
static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
- enum gpiod_flags dflags)
+ unsigned long lflags, enum gpiod_flags dflags)
{
int status;
+ if (lflags & GPIO_ACTIVE_LOW)
+ set_bit(FLAG_ACTIVE_LOW, &desc->flags);
+ if (lflags & GPIO_OPEN_DRAIN)
+ set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+ if (lflags & GPIO_OPEN_SOURCE)
+ set_bit(FLAG_OPEN_SOURCE, &desc->flags);
+
/* No particular flag request, return here... */
if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) {
pr_debug("no flags found for %s\n", con_id);
@@ -2161,13 +2145,11 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
return desc;
}
- gpiod_parse_flags(desc, lookupflags);
-
status = gpiod_request(desc, con_id);
if (status < 0)
return ERR_PTR(status);
- status = gpiod_configure_flags(desc, con_id, flags);
+ status = gpiod_configure_flags(desc, con_id, lookupflags, flags);
if (status < 0) {
dev_dbg(dev, "setup of GPIO %s failed\n", con_id);
gpiod_put(desc);
@@ -2223,6 +2205,10 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
if (IS_ERR(desc))
return desc;
+ ret = gpiod_request(desc, NULL);
+ if (ret)
+ return ERR_PTR(ret);
+
if (active_low)
set_bit(FLAG_ACTIVE_LOW, &desc->flags);
@@ -2233,10 +2219,6 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
set_bit(FLAG_OPEN_SOURCE, &desc->flags);
}
- ret = gpiod_request(desc, NULL);
- if (ret)
- return ERR_PTR(ret);
-
return desc;
}
EXPORT_SYMBOL_GPL(fwnode_get_named_gpiod);
@@ -2289,8 +2271,6 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
chip = gpiod_to_chip(desc);
hwnum = gpio_chip_hwgpio(desc);
- gpiod_parse_flags(desc, lflags);
-
local_desc = gpiochip_request_own_desc(chip, hwnum, name);
if (IS_ERR(local_desc)) {
pr_err("requesting hog GPIO %s (chip %s, offset %d) failed\n",
@@ -2298,7 +2278,7 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
return PTR_ERR(local_desc);
}
- status = gpiod_configure_flags(desc, name, dflags);
+ status = gpiod_configure_flags(desc, name, lflags, dflags);
if (status < 0) {
pr_err("setup of hog GPIO %s (chip %s, offset %d) failed\n",
name, chip->label, hwnum);
diff --git a/drivers/gpio/qpnp-pin.c b/drivers/gpio/qpnp-pin.c
index 182c6074985e..483bb9338ac3 100644
--- a/drivers/gpio/qpnp-pin.c
+++ b/drivers/gpio/qpnp-pin.c
@@ -827,9 +827,17 @@ static int qpnp_pin_get(struct gpio_chip *gpio_chip, unsigned offset)
if (WARN_ON(!q_spec))
return -ENODEV;
+ if (is_gpio_lv_mv(q_spec)) {
+ mask = Q_REG_LV_MV_MODE_SEL_MASK;
+ shift = Q_REG_LV_MV_MODE_SEL_SHIFT;
+ } else {
+ mask = Q_REG_MODE_SEL_MASK;
+ shift = Q_REG_MODE_SEL_SHIFT;
+ }
+
/* gpio val is from RT status iff input is enabled */
- if ((q_spec->regs[Q_REG_I_MODE_CTL] & Q_REG_MODE_SEL_MASK)
- == QPNP_PIN_MODE_DIG_IN) {
+ if (q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL], shift, mask)
+ == QPNP_PIN_MODE_DIG_IN) {
rc = regmap_read(q_chip->regmap,
Q_REG_ADDR(q_spec, Q_REG_STATUS1), &val);
buf[0] = (u8)val;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index bb1099c549df..053fc2f465df 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -1673,6 +1673,7 @@ struct amdgpu_uvd {
struct amdgpu_bo *vcpu_bo;
void *cpu_addr;
uint64_t gpu_addr;
+ unsigned fw_version;
atomic_t handles[AMDGPU_MAX_UVD_HANDLES];
struct drm_file *filp[AMDGPU_MAX_UVD_HANDLES];
struct delayed_work idle_work;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
index 8ac49812a716..5a8fbadbd27b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
@@ -63,10 +63,6 @@ bool amdgpu_has_atpx(void) {
return amdgpu_atpx_priv.atpx_detected;
}
-bool amdgpu_has_atpx_dgpu_power_cntl(void) {
- return amdgpu_atpx_priv.atpx.functions.power_cntl;
-}
-
/**
* amdgpu_atpx_call - call an ATPX method
*
@@ -146,6 +142,10 @@ static void amdgpu_atpx_parse_functions(struct amdgpu_atpx_functions *f, u32 mas
*/
static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx)
{
+ /* make sure required functions are enabled */
+ /* dGPU power control is required */
+ atpx->functions.power_cntl = true;
+
if (atpx->functions.px_params) {
union acpi_object *info;
struct atpx_px_params output;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
index 119cdc2c43e7..7ef2c13921b4 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
@@ -194,12 +194,12 @@ int amdgpu_connector_get_monitor_bpc(struct drm_connector *connector)
bpc = 8;
DRM_DEBUG("%s: HDMI deep color 10 bpc exceeds max tmds clock. Using %d bpc.\n",
connector->name, bpc);
- } else if (bpc > 8) {
- /* max_tmds_clock missing, but hdmi spec mandates it for deep color. */
- DRM_DEBUG("%s: Required max tmds clock for HDMI deep color missing. Using 8 bpc.\n",
- connector->name);
- bpc = 8;
}
+ } else if (bpc > 8) {
+ /* max_tmds_clock missing, but hdmi spec mandates it for deep color. */
+ DRM_DEBUG("%s: Required max tmds clock for HDMI deep color missing. Using 8 bpc.\n",
+ connector->name);
+ bpc = 8;
}
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 9d88023df836..c961fe093e12 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -61,12 +61,6 @@ static const char *amdgpu_asic_name[] = {
"LAST",
};
-#if defined(CONFIG_VGA_SWITCHEROO)
-bool amdgpu_has_atpx_dgpu_power_cntl(void);
-#else
-static inline bool amdgpu_has_atpx_dgpu_power_cntl(void) { return false; }
-#endif
-
bool amdgpu_device_is_px(struct drm_device *dev)
{
struct amdgpu_device *adev = dev->dev_private;
@@ -1475,7 +1469,7 @@ int amdgpu_device_init(struct amdgpu_device *adev,
if (amdgpu_runtime_pm == 1)
runtime = true;
- if (amdgpu_device_is_px(ddev) && amdgpu_has_atpx_dgpu_power_cntl())
+ if (amdgpu_device_is_px(ddev))
runtime = true;
vga_switcheroo_register_client(adev->pdev, &amdgpu_switcheroo_ops, runtime);
if (runtime)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c
index 7b7f4aba60c0..fe36caf1b7d7 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c
@@ -150,7 +150,7 @@ u32 amdgpu_dpm_get_vrefresh(struct amdgpu_device *adev)
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
amdgpu_crtc = to_amdgpu_crtc(crtc);
if (crtc->enabled && amdgpu_crtc->enabled && amdgpu_crtc->hw_mode.clock) {
- vrefresh = amdgpu_crtc->hw_mode.vrefresh;
+ vrefresh = drm_mode_vrefresh(&amdgpu_crtc->hw_mode);
break;
}
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
index e23843f4d877..4488e82f87b0 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
@@ -303,7 +303,7 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
fw_info.feature = adev->vce.fb_version;
break;
case AMDGPU_INFO_FW_UVD:
- fw_info.ver = 0;
+ fw_info.ver = adev->uvd.fw_version;
fw_info.feature = 0;
break;
case AMDGPU_INFO_FW_GMC:
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
index 064ebb347074..89df7871653d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
@@ -52,7 +52,7 @@ struct amdgpu_hpd;
#define AMDGPU_MAX_HPD_PINS 6
#define AMDGPU_MAX_CRTCS 6
-#define AMDGPU_MAX_AFMT_BLOCKS 7
+#define AMDGPU_MAX_AFMT_BLOCKS 9
enum amdgpu_rmx_type {
RMX_OFF,
@@ -308,8 +308,8 @@ struct amdgpu_mode_info {
struct atom_context *atom_context;
struct card_info *atom_card_info;
bool mode_config_initialized;
- struct amdgpu_crtc *crtcs[6];
- struct amdgpu_afmt *afmt[7];
+ struct amdgpu_crtc *crtcs[AMDGPU_MAX_CRTCS];
+ struct amdgpu_afmt *afmt[AMDGPU_MAX_AFMT_BLOCKS];
/* DVI-I properties */
struct drm_property *coherent_mode_property;
/* DAC enable load detect */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
index b8fbbd7699e4..73628c7599e7 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
@@ -540,6 +540,7 @@ int amdgpu_bo_set_metadata (struct amdgpu_bo *bo, void *metadata,
if (!metadata_size) {
if (bo->metadata_size) {
kfree(bo->metadata);
+ bo->metadata = NULL;
bo->metadata_size = 0;
}
return 0;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
index 53f987aeeacf..3b35ad83867c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
@@ -156,6 +156,9 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev)
DRM_INFO("Found UVD firmware Version: %hu.%hu Family ID: %hu\n",
version_major, version_minor, family_id);
+ adev->uvd.fw_version = ((version_major << 24) | (version_minor << 16) |
+ (family_id << 8));
+
bo_size = AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8)
+ AMDGPU_UVD_STACK_SIZE + AMDGPU_UVD_HEAP_SIZE;
r = amdgpu_bo_create(adev, bo_size, PAGE_SIZE, true,
@@ -273,6 +276,8 @@ int amdgpu_uvd_resume(struct amdgpu_device *adev)
memcpy(adev->uvd.cpu_addr, (adev->uvd.fw->data) + offset,
(adev->uvd.fw->size) - offset);
+ cancel_delayed_work_sync(&adev->uvd.idle_work);
+
size = amdgpu_bo_size(adev->uvd.vcpu_bo);
size -= le32_to_cpu(hdr->ucode_size_bytes);
ptr = adev->uvd.cpu_addr;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
index a745eeeb5d82..bb0da76051a1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
@@ -220,6 +220,7 @@ int amdgpu_vce_suspend(struct amdgpu_device *adev)
if (i == AMDGPU_MAX_VCE_HANDLES)
return 0;
+ cancel_delayed_work_sync(&adev->vce.idle_work);
/* TODO: suspending running encoding sessions isn't supported */
return -EINVAL;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c b/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c
index 1e0bba29e167..1cd6de575305 100644
--- a/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c
+++ b/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c
@@ -298,6 +298,10 @@ bool amdgpu_atombios_encoder_mode_fixup(struct drm_encoder *encoder,
&& (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2)))
adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2;
+ /* vertical FP must be at least 1 */
+ if (mode->crtc_vsync_start == mode->crtc_vdisplay)
+ adjusted_mode->crtc_vsync_start++;
+
/* get the native mode for scaling */
if (amdgpu_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT))
amdgpu_panel_mode_fixup(encoder, adjusted_mode);
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
index aa491540ba85..b57fffc2d4af 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
@@ -3628,7 +3628,7 @@ static void gfx_v7_0_ring_emit_vm_flush(struct amdgpu_ring *ring,
unsigned vm_id, uint64_t pd_addr)
{
int usepfp = (ring->type == AMDGPU_RING_TYPE_GFX);
- uint32_t seq = ring->fence_drv.sync_seq;
+ uint32_t seq = ring->fence_drv.sync_seq[ring->idx];
uint64_t addr = ring->fence_drv.gpu_addr;
amdgpu_ring_write(ring, PACKET3(PACKET3_WAIT_REG_MEM, 5));
@@ -5463,7 +5463,7 @@ static int gfx_v7_0_eop_irq(struct amdgpu_device *adev,
case 2:
for (i = 0; i < adev->gfx.num_compute_rings; i++) {
ring = &adev->gfx.compute_ring[i];
- if ((ring->me == me_id) & (ring->pipe == pipe_id))
+ if ((ring->me == me_id) && (ring->pipe == pipe_id))
amdgpu_fence_process(ring);
}
break;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_dbgdev.c b/drivers/gpu/drm/amd/amdkfd/kfd_dbgdev.c
index c34c393e9aea..d5e19b5fbbfb 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_dbgdev.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_dbgdev.c
@@ -513,7 +513,7 @@ static int dbgdev_wave_control_set_registers(
union SQ_CMD_BITS *in_reg_sq_cmd,
union GRBM_GFX_INDEX_BITS *in_reg_gfx_index)
{
- int status;
+ int status = 0;
union SQ_CMD_BITS reg_sq_cmd;
union GRBM_GFX_INDEX_BITS reg_gfx_index;
struct HsaDbgWaveMsgAMDGen2 *pMsg;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
index 9be007081b72..eb1da83c9902 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
@@ -242,13 +242,19 @@ static void kfd_process_notifier_release(struct mmu_notifier *mn,
pqm_uninit(&p->pqm);
/* Iterate over all process device data structure and check
- * if we should reset all wavefronts */
- list_for_each_entry(pdd, &p->per_device_data, per_device_list)
+ * if we should delete debug managers and reset all wavefronts
+ */
+ list_for_each_entry(pdd, &p->per_device_data, per_device_list) {
+ if ((pdd->dev->dbgmgr) &&
+ (pdd->dev->dbgmgr->pasid == p->pasid))
+ kfd_dbgmgr_destroy(pdd->dev->dbgmgr);
+
if (pdd->reset_wavefronts) {
pr_warn("amdkfd: Resetting all wave fronts\n");
dbgdev_wave_reset_wavefronts(pdd->dev, p);
pdd->reset_wavefronts = false;
}
+ }
mutex_unlock(&p->mutex);
@@ -404,42 +410,52 @@ void kfd_unbind_process_from_device(struct kfd_dev *dev, unsigned int pasid)
idx = srcu_read_lock(&kfd_processes_srcu);
+ /*
+ * Look for the process that matches the pasid. If there is no such
+ * process, we either released it in amdkfd's own notifier, or there
+ * is a bug. Unfortunately, there is no way to tell...
+ */
hash_for_each_rcu(kfd_processes_table, i, p, kfd_processes)
- if (p->pasid == pasid)
- break;
+ if (p->pasid == pasid) {
- srcu_read_unlock(&kfd_processes_srcu, idx);
+ srcu_read_unlock(&kfd_processes_srcu, idx);
- BUG_ON(p->pasid != pasid);
+ pr_debug("Unbinding process %d from IOMMU\n", pasid);
- mutex_lock(&p->mutex);
+ mutex_lock(&p->mutex);
- if ((dev->dbgmgr) && (dev->dbgmgr->pasid == p->pasid))
- kfd_dbgmgr_destroy(dev->dbgmgr);
+ if ((dev->dbgmgr) && (dev->dbgmgr->pasid == p->pasid))
+ kfd_dbgmgr_destroy(dev->dbgmgr);
- pqm_uninit(&p->pqm);
+ pqm_uninit(&p->pqm);
- pdd = kfd_get_process_device_data(dev, p);
+ pdd = kfd_get_process_device_data(dev, p);
- if (!pdd) {
- mutex_unlock(&p->mutex);
- return;
- }
+ if (!pdd) {
+ mutex_unlock(&p->mutex);
+ return;
+ }
- if (pdd->reset_wavefronts) {
- dbgdev_wave_reset_wavefronts(pdd->dev, p);
- pdd->reset_wavefronts = false;
- }
+ if (pdd->reset_wavefronts) {
+ dbgdev_wave_reset_wavefronts(pdd->dev, p);
+ pdd->reset_wavefronts = false;
+ }
- /*
- * Just mark pdd as unbound, because we still need it to call
- * amd_iommu_unbind_pasid() in when the process exits.
- * We don't call amd_iommu_unbind_pasid() here
- * because the IOMMU called us.
- */
- pdd->bound = false;
+ /*
+ * Just mark pdd as unbound, because we still need it
+ * to call amd_iommu_unbind_pasid() in when the
+ * process exits.
+ * We don't call amd_iommu_unbind_pasid() here
+ * because the IOMMU called us.
+ */
+ pdd->bound = false;
- mutex_unlock(&p->mutex);
+ mutex_unlock(&p->mutex);
+
+ return;
+ }
+
+ srcu_read_unlock(&kfd_processes_srcu, idx);
}
struct kfd_process_device *kfd_get_first_process_device_data(struct kfd_process *p)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
index d0299aed517e..59d1269626b1 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
@@ -335,6 +335,8 @@ atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff,
factor_reg);
+ } else {
+ atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff, 0);
}
}
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index aeee083c7f95..6253775b8d9c 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -150,7 +150,7 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
for (i = 0; i < state->num_connector; i++) {
struct drm_connector *connector = state->connectors[i];
- if (!connector)
+ if (!connector || !connector->funcs)
continue;
/*
@@ -367,6 +367,8 @@ int drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state,
drm_property_unreference_blob(state->mode_blob);
state->mode_blob = NULL;
+ memset(&state->mode, 0, sizeof(state->mode));
+
if (blob) {
if (blob->length != sizeof(struct drm_mode_modeinfo) ||
drm_mode_convert_umode(&state->mode,
@@ -379,7 +381,6 @@ int drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state,
DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n",
state->mode.name, state);
} else {
- memset(&state->mode, 0, sizeof(state->mode));
state->enable = false;
DRM_DEBUG_ATOMIC("Set [NOMODE] for CRTC state %p\n",
state);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 24c5434abd1c..dc84003f694e 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -2682,8 +2682,6 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
goto out;
}
- drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
-
/*
* Check whether the primary plane supports the fb pixel format.
* Drivers not implementing the universal planes API use a
@@ -3316,6 +3314,24 @@ int drm_mode_addfb2(struct drm_device *dev,
return 0;
}
+struct drm_mode_rmfb_work {
+ struct work_struct work;
+ struct list_head fbs;
+};
+
+static void drm_mode_rmfb_work_fn(struct work_struct *w)
+{
+ struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work);
+
+ while (!list_empty(&arg->fbs)) {
+ struct drm_framebuffer *fb =
+ list_first_entry(&arg->fbs, typeof(*fb), filp_head);
+
+ list_del_init(&fb->filp_head);
+ drm_framebuffer_remove(fb);
+ }
+}
+
/**
* drm_mode_rmfb - remove an FB from the configuration
* @dev: drm device for the ioctl
@@ -3356,7 +3372,25 @@ int drm_mode_rmfb(struct drm_device *dev,
mutex_unlock(&dev->mode_config.fb_lock);
mutex_unlock(&file_priv->fbs_lock);
- drm_framebuffer_unreference(fb);
+ /*
+ * we now own the reference that was stored in the fbs list
+ *
+ * drm_framebuffer_remove may fail with -EINTR on pending signals,
+ * so run this in a separate stack as there's no way to correctly
+ * handle this after the fb is already removed from the lookup table.
+ */
+ if (atomic_read(&fb->refcount.refcount) > 1) {
+ struct drm_mode_rmfb_work arg;
+
+ INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
+ INIT_LIST_HEAD(&arg.fbs);
+ list_add_tail(&fb->filp_head, &arg.fbs);
+
+ schedule_work(&arg.work);
+ flush_work(&arg.work);
+ destroy_work_on_stack(&arg.work);
+ } else
+ drm_framebuffer_unreference(fb);
return 0;
@@ -3509,7 +3543,6 @@ out_err1:
return ret;
}
-
/**
* drm_fb_release - remove and free the FBs on this file
* @priv: drm file for the ioctl
@@ -3524,6 +3557,9 @@ out_err1:
void drm_fb_release(struct drm_file *priv)
{
struct drm_framebuffer *fb, *tfb;
+ struct drm_mode_rmfb_work arg;
+
+ INIT_LIST_HEAD(&arg.fbs);
/*
* When the file gets released that means no one else can access the fb
@@ -3536,10 +3572,22 @@ void drm_fb_release(struct drm_file *priv)
* at it any more.
*/
list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
- list_del_init(&fb->filp_head);
+ if (atomic_read(&fb->refcount.refcount) > 1) {
+ list_move_tail(&fb->filp_head, &arg.fbs);
+ } else {
+ list_del_init(&fb->filp_head);
- /* This drops the fpriv->fbs reference. */
- drm_framebuffer_unreference(fb);
+ /* This drops the fpriv->fbs reference. */
+ drm_framebuffer_unreference(fb);
+ }
+ }
+
+ if (!list_empty(&arg.fbs)) {
+ INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
+
+ schedule_work(&arg.work);
+ flush_work(&arg.work);
+ destroy_work_on_stack(&arg.work);
}
}
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 39d7e2e15c11..2485fb652716 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -1665,13 +1665,19 @@ static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_branch *mstb;
int len, ret, port_num;
+ port = drm_dp_get_validated_port_ref(mgr, port);
+ if (!port)
+ return -EINVAL;
+
port_num = port->port_num;
mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
if (!mstb) {
mstb = drm_dp_get_last_connected_port_and_mstb(mgr, port->parent, &port_num);
- if (!mstb)
+ if (!mstb) {
+ drm_dp_put_port(port);
return -EINVAL;
+ }
}
txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
@@ -1697,6 +1703,7 @@ static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
kfree(txmsg);
fail_put:
drm_dp_put_mst_branch_device(mstb);
+ drm_dp_put_port(port);
return ret;
}
@@ -1779,6 +1786,11 @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr)
req_payload.start_slot = cur_slots;
if (mgr->proposed_vcpis[i]) {
port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
+ port = drm_dp_get_validated_port_ref(mgr, port);
+ if (!port) {
+ mutex_unlock(&mgr->payload_lock);
+ return -EINVAL;
+ }
req_payload.num_slots = mgr->proposed_vcpis[i]->num_slots;
} else {
port = NULL;
@@ -1804,6 +1816,9 @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr)
mgr->payloads[i].payload_state = req_payload.payload_state;
}
cur_slots += req_payload.num_slots;
+
+ if (port)
+ drm_dp_put_port(port);
}
for (i = 0; i < mgr->max_payloads; i++) {
@@ -2109,6 +2124,8 @@ int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr)
if (mgr->mst_primary) {
int sret;
+ u8 guid[16];
+
sret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE);
if (sret != DP_RECEIVER_CAP_SIZE) {
DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
@@ -2123,6 +2140,16 @@ int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr)
ret = -1;
goto out_unlock;
}
+
+ /* Some hubs forget their guids after they resume */
+ sret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
+ if (sret != 16) {
+ DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
+ ret = -1;
+ goto out_unlock;
+ }
+ drm_dp_check_mstb_guid(mgr->mst_primary, guid);
+
ret = 0;
} else
ret = -1;
@@ -2847,11 +2874,9 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)
drm_dp_port_teardown_pdt(port, port->pdt);
if (!port->input && port->vcpi.vcpi > 0) {
- if (mgr->mst_state) {
- drm_dp_mst_reset_vcpi_slots(mgr, port);
- drm_dp_update_payload_part1(mgr);
- drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
- }
+ drm_dp_mst_reset_vcpi_slots(mgr, port);
+ drm_dp_update_payload_part1(mgr);
+ drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
}
kref_put(&port->kref, drm_dp_free_mst_port);
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 69cbab5e5c81..5ad036741b99 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1899,7 +1899,6 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
int n, int width, int height)
{
int c, o;
- struct drm_device *dev = fb_helper->dev;
struct drm_connector *connector;
const struct drm_connector_helper_funcs *connector_funcs;
struct drm_encoder *encoder;
@@ -1918,7 +1917,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
if (modes[n] == NULL)
return best_score;
- crtcs = kzalloc(dev->mode_config.num_connector *
+ crtcs = kzalloc(fb_helper->connector_count *
sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
if (!crtcs)
return best_score;
@@ -1964,7 +1963,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
if (score > best_score) {
best_score = score;
memcpy(best_crtcs, crtcs,
- dev->mode_config.num_connector *
+ fb_helper->connector_count *
sizeof(struct drm_fb_helper_crtc *));
}
}
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index cd74a0953f42..39e30abddf08 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -1487,6 +1487,8 @@ int drm_mode_convert_umode(struct drm_display_mode *out,
if (out->status != MODE_OK)
goto out;
+ drm_mode_set_crtcinfo(out, CRTC_INTERLACE_HALVE_V);
+
ret = 0;
out:
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c
index 6b43ae3ffd73..1616af209bfc 100644
--- a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c
+++ b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c
@@ -72,7 +72,7 @@ static const char *const dsi_errors[] = {
"RX Prot Violation",
"HS Generic Write FIFO Full",
"LP Generic Write FIFO Full",
- "Generic Read Data Avail"
+ "Generic Read Data Avail",
"Special Packet Sent",
"Tearing Effect",
};
diff --git a/drivers/gpu/drm/i915/i915_gem_shrinker.c b/drivers/gpu/drm/i915/i915_gem_shrinker.c
index f7df54a8ee2b..c0a96f1ee18e 100644
--- a/drivers/gpu/drm/i915/i915_gem_shrinker.c
+++ b/drivers/gpu/drm/i915/i915_gem_shrinker.c
@@ -39,7 +39,7 @@ static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task)
if (!mutex_is_locked(mutex))
return false;
-#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_MUTEXES)
+#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_MUTEX_SPIN_ON_OWNER)
return mutex->owner == task;
#else
/* Since UP may be pre-empted, we cannot assume that we own the lock */
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index bc7b8faba84d..9ed9f6dde86f 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -2838,7 +2838,14 @@ enum skl_disp_power_wells {
#define GEN6_RP_STATE_CAP (MCHBAR_MIRROR_BASE_SNB + 0x5998)
#define BXT_RP_STATE_CAP 0x138170
-#define INTERVAL_1_28_US(us) (((us) * 100) >> 7)
+/*
+ * Make these a multiple of magic 25 to avoid SNB (eg. Dell XPS
+ * 8300) freezing up around GPU hangs. Looks as if even
+ * scheduling/timer interrupts start misbehaving if the RPS
+ * EI/thresholds are "bad", leading to a very sluggish or even
+ * frozen machine.
+ */
+#define INTERVAL_1_28_US(us) roundup(((us) * 100) >> 7, 25)
#define INTERVAL_1_33_US(us) (((us) * 3) >> 2)
#define INTERVAL_0_833_US(us) (((us) * 6) / 5)
#define GT_INTERVAL_FROM_US(dev_priv, us) (IS_GEN9(dev_priv) ? \
@@ -7350,6 +7357,8 @@ enum skl_disp_power_wells {
#define TRANS_CLK_SEL_DISABLED (0x0<<29)
#define TRANS_CLK_SEL_PORT(x) (((x)+1)<<29)
+#define CDCLK_FREQ 0x46200
+
#define TRANSA_MSA_MISC 0x60410
#define TRANSB_MSA_MISC 0x61410
#define TRANSC_MSA_MISC 0x62410
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 6a2c76e367a5..97d1ed20418b 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -248,8 +248,14 @@ static bool intel_crt_compute_config(struct intel_encoder *encoder,
pipe_config->has_pch_encoder = true;
/* LPT FDI RX only supports 8bpc. */
- if (HAS_PCH_LPT(dev))
+ if (HAS_PCH_LPT(dev)) {
+ if (pipe_config->bw_constrained && pipe_config->pipe_bpp < 24) {
+ DRM_DEBUG_KMS("LPT only supports 24bpp\n");
+ return false;
+ }
+
pipe_config->pipe_bpp = 24;
+ }
/* FDI must always be 2.7 GHz */
if (HAS_DDI(dev)) {
diff --git a/drivers/gpu/drm/i915/intel_csr.c b/drivers/gpu/drm/i915/intel_csr.c
index 9e530a739354..fc28c512ece3 100644
--- a/drivers/gpu/drm/i915/intel_csr.c
+++ b/drivers/gpu/drm/i915/intel_csr.c
@@ -180,7 +180,8 @@ struct stepping_info {
static const struct stepping_info skl_stepping_info[] = {
{'A', '0'}, {'B', '0'}, {'C', '0'},
{'D', '0'}, {'E', '0'}, {'F', '0'},
- {'G', '0'}, {'H', '0'}, {'I', '0'}
+ {'G', '0'}, {'H', '0'}, {'I', '0'},
+ {'J', '0'}, {'K', '0'}
};
static struct stepping_info bxt_stepping_info[] = {
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index 7e6158b889da..3c6b07683bd9 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -464,9 +464,17 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port,
} else if (IS_BROADWELL(dev)) {
ddi_translations_fdi = bdw_ddi_translations_fdi;
ddi_translations_dp = bdw_ddi_translations_dp;
- ddi_translations_edp = bdw_ddi_translations_edp;
+
+ if (dev_priv->edp_low_vswing) {
+ ddi_translations_edp = bdw_ddi_translations_edp;
+ n_edp_entries = ARRAY_SIZE(bdw_ddi_translations_edp);
+ } else {
+ ddi_translations_edp = bdw_ddi_translations_dp;
+ n_edp_entries = ARRAY_SIZE(bdw_ddi_translations_dp);
+ }
+
ddi_translations_hdmi = bdw_ddi_translations_hdmi;
- n_edp_entries = ARRAY_SIZE(bdw_ddi_translations_edp);
+
n_dp_entries = ARRAY_SIZE(bdw_ddi_translations_dp);
n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi);
hdmi_default_entry = 7;
@@ -3188,12 +3196,6 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
intel_ddi_clock_get(encoder, pipe_config);
}
-static void intel_ddi_destroy(struct drm_encoder *encoder)
-{
- /* HDMI has nothing special to destroy, so we can go with this. */
- intel_dp_encoder_destroy(encoder);
-}
-
static bool intel_ddi_compute_config(struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config)
{
@@ -3212,7 +3214,8 @@ static bool intel_ddi_compute_config(struct intel_encoder *encoder,
}
static const struct drm_encoder_funcs intel_ddi_funcs = {
- .destroy = intel_ddi_destroy,
+ .reset = intel_dp_encoder_reset,
+ .destroy = intel_dp_encoder_destroy,
};
static struct intel_connector *
@@ -3284,6 +3287,7 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
intel_encoder->post_disable = intel_ddi_post_disable;
intel_encoder->get_hw_state = intel_ddi_get_hw_state;
intel_encoder->get_config = intel_ddi_get_config;
+ intel_encoder->suspend = intel_dp_encoder_suspend;
intel_dig_port->port = port;
intel_dig_port->saved_port_bits = I915_READ(DDI_BUF_CTL(port)) &
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index f859a5b87ed4..c41bc42b6fa7 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -4447,7 +4447,7 @@ int skl_update_scaler_crtc(struct intel_crtc_state *state)
intel_crtc->base.base.id, intel_crtc->pipe, SKL_CRTC_INDEX);
return skl_update_scaler(state, !state->base.active, SKL_CRTC_INDEX,
- &state->scaler_state.scaler_id, DRM_ROTATE_0,
+ &state->scaler_state.scaler_id, BIT(DRM_ROTATE_0),
state->pipe_src_w, state->pipe_src_h,
adjusted_mode->crtc_hdisplay, adjusted_mode->crtc_vdisplay);
}
@@ -8228,12 +8228,14 @@ static void ironlake_init_pch_refclk(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_encoder *encoder;
+ int i;
u32 val, final;
bool has_lvds = false;
bool has_cpu_edp = false;
bool has_panel = false;
bool has_ck505 = false;
bool can_ssc = false;
+ bool using_ssc_source = false;
/* We need to take the global config into account */
for_each_intel_encoder(dev, encoder) {
@@ -8260,8 +8262,22 @@ static void ironlake_init_pch_refclk(struct drm_device *dev)
can_ssc = true;
}
- DRM_DEBUG_KMS("has_panel %d has_lvds %d has_ck505 %d\n",
- has_panel, has_lvds, has_ck505);
+ /* Check if any DPLLs are using the SSC source */
+ for (i = 0; i < dev_priv->num_shared_dpll; i++) {
+ u32 temp = I915_READ(PCH_DPLL(i));
+
+ if (!(temp & DPLL_VCO_ENABLE))
+ continue;
+
+ if ((temp & PLL_REF_INPUT_MASK) ==
+ PLLB_REF_INPUT_SPREADSPECTRUMIN) {
+ using_ssc_source = true;
+ break;
+ }
+ }
+
+ DRM_DEBUG_KMS("has_panel %d has_lvds %d has_ck505 %d using_ssc_source %d\n",
+ has_panel, has_lvds, has_ck505, using_ssc_source);
/* Ironlake: try to setup display ref clock before DPLL
* enabling. This is only under driver's control after
@@ -8298,9 +8314,9 @@ static void ironlake_init_pch_refclk(struct drm_device *dev)
final |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
} else
final |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
- } else {
- final |= DREF_SSC_SOURCE_DISABLE;
- final |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
+ } else if (using_ssc_source) {
+ final |= DREF_SSC_SOURCE_ENABLE;
+ final |= DREF_SSC1_ENABLE;
}
if (final == val)
@@ -8346,7 +8362,7 @@ static void ironlake_init_pch_refclk(struct drm_device *dev)
POSTING_READ(PCH_DREF_CONTROL);
udelay(200);
} else {
- DRM_DEBUG_KMS("Disabling SSC entirely\n");
+ DRM_DEBUG_KMS("Disabling CPU source output\n");
val &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
@@ -8357,16 +8373,20 @@ static void ironlake_init_pch_refclk(struct drm_device *dev)
POSTING_READ(PCH_DREF_CONTROL);
udelay(200);
- /* Turn off the SSC source */
- val &= ~DREF_SSC_SOURCE_MASK;
- val |= DREF_SSC_SOURCE_DISABLE;
+ if (!using_ssc_source) {
+ DRM_DEBUG_KMS("Disabling SSC source\n");
- /* Turn off SSC1 */
- val &= ~DREF_SSC1_ENABLE;
+ /* Turn off the SSC source */
+ val &= ~DREF_SSC_SOURCE_MASK;
+ val |= DREF_SSC_SOURCE_DISABLE;
- I915_WRITE(PCH_DREF_CONTROL, val);
- POSTING_READ(PCH_DREF_CONTROL);
- udelay(200);
+ /* Turn off SSC1 */
+ val &= ~DREF_SSC1_ENABLE;
+
+ I915_WRITE(PCH_DREF_CONTROL, val);
+ POSTING_READ(PCH_DREF_CONTROL);
+ udelay(200);
+ }
}
BUG_ON(val != final);
@@ -9669,6 +9689,8 @@ static void broadwell_set_cdclk(struct drm_device *dev, int cdclk)
sandybridge_pcode_write(dev_priv, HSW_PCODE_DE_WRITE_FREQ_REQ, data);
mutex_unlock(&dev_priv->rps.hw_lock);
+ I915_WRITE(CDCLK_FREQ, DIV_ROUND_CLOSEST(cdclk, 1000) - 1);
+
intel_update_cdclk(dev);
WARN(cdclk != dev_priv->cdclk_freq,
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 78b8ec84d576..8e1d6d74c203 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -3628,8 +3628,7 @@ static bool
intel_dp_reset_link_train(struct intel_dp *intel_dp, uint32_t *DP,
uint8_t dp_train_pat)
{
- if (!intel_dp->train_set_valid)
- memset(intel_dp->train_set, 0, sizeof(intel_dp->train_set));
+ memset(intel_dp->train_set, 0, sizeof(intel_dp->train_set));
intel_dp_set_signal_levels(intel_dp, DP);
return intel_dp_set_link_train(intel_dp, DP, dp_train_pat);
}
@@ -3746,22 +3745,6 @@ intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
break;
}
- /*
- * if we used previously trained voltage and pre-emphasis values
- * and we don't get clock recovery, reset link training values
- */
- if (intel_dp->train_set_valid) {
- DRM_DEBUG_KMS("clock recovery not ok, reset");
- /* clear the flag as we are not reusing train set */
- intel_dp->train_set_valid = false;
- if (!intel_dp_reset_link_train(intel_dp, &DP,
- DP_TRAINING_PATTERN_1 |
- DP_LINK_SCRAMBLING_DISABLE)) {
- DRM_ERROR("failed to enable link training\n");
- return;
- }
- continue;
- }
/* Check to see if we've tried the max voltage */
for (i = 0; i < intel_dp->lane_count; i++)
@@ -3854,7 +3837,6 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
/* Make sure clock is still ok */
if (!drm_dp_clock_recovery_ok(link_status,
intel_dp->lane_count)) {
- intel_dp->train_set_valid = false;
intel_dp_link_training_clock_recovery(intel_dp);
intel_dp_set_link_train(intel_dp, &DP,
training_pattern |
@@ -3871,7 +3853,6 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
/* Try 5 times, then try clock recovery if that fails */
if (tries > 5) {
- intel_dp->train_set_valid = false;
intel_dp_link_training_clock_recovery(intel_dp);
intel_dp_set_link_train(intel_dp, &DP,
training_pattern |
@@ -3893,10 +3874,8 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
intel_dp->DP = DP;
- if (channel_eq) {
- intel_dp->train_set_valid = true;
+ if (channel_eq)
DRM_DEBUG_KMS("Channel EQ done. DP Training successful\n");
- }
}
void intel_dp_stop_link_train(struct intel_dp *intel_dp)
@@ -5035,7 +5014,7 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
kfree(intel_dig_port);
}
-static void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder)
+void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
@@ -5077,15 +5056,17 @@ static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp)
edp_panel_vdd_schedule_off(intel_dp);
}
-static void intel_dp_encoder_reset(struct drm_encoder *encoder)
+void intel_dp_encoder_reset(struct drm_encoder *encoder)
{
- struct intel_dp *intel_dp;
+ struct drm_i915_private *dev_priv = to_i915(encoder->dev);
+ struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+
+ if (!HAS_DDI(dev_priv))
+ intel_dp->DP = I915_READ(intel_dp->output_reg);
if (to_intel_encoder(encoder)->type != INTEL_OUTPUT_EDP)
return;
- intel_dp = enc_to_intel_dp(encoder);
-
pps_lock(intel_dp);
/*
@@ -5157,9 +5138,6 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd)
intel_display_power_get(dev_priv, power_domain);
if (long_hpd) {
- /* indicate that we need to restart link training */
- intel_dp->train_set_valid = false;
-
if (!intel_digital_port_connected(dev_priv, intel_dig_port))
goto mst_fail;
diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c
index 0639275fc471..06bd9257acdc 100644
--- a/drivers/gpu/drm/i915/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/intel_dp_mst.c
@@ -477,6 +477,8 @@ static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
struct intel_connector *intel_connector = to_intel_connector(connector);
struct drm_device *dev = connector->dev;
+ intel_connector->unregister(intel_connector);
+
/* need to nuke the connector */
drm_modeset_lock_all(dev);
if (connector->state->crtc) {
@@ -490,11 +492,7 @@ static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
WARN(ret, "Disabling mst crtc failed with %i\n", ret);
}
- drm_modeset_unlock_all(dev);
- intel_connector->unregister(intel_connector);
-
- drm_modeset_lock_all(dev);
intel_connector_remove_from_fbdev(intel_connector);
drm_connector_cleanup(connector);
drm_modeset_unlock_all(dev);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 0d00f07b7163..c5f11e0c5d5b 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -783,7 +783,6 @@ struct intel_dp {
bool has_aux_irq,
int send_bytes,
uint32_t aux_clock_divider);
- bool train_set_valid;
/* Displayport compliance testing */
unsigned long compliance_test_type;
@@ -1204,6 +1203,8 @@ void intel_dp_set_link_params(struct intel_dp *intel_dp,
void intel_dp_start_link_train(struct intel_dp *intel_dp);
void intel_dp_stop_link_train(struct intel_dp *intel_dp);
void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode);
+void intel_dp_encoder_reset(struct drm_encoder *encoder);
+void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder);
void intel_dp_encoder_destroy(struct drm_encoder *encoder);
int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc);
bool intel_dp_compute_config(struct intel_encoder *encoder,
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index 4fd5fdfef6bd..c0c094d5b822 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -362,12 +362,12 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
uint64_t conn_configured = 0, mask;
int pass = 0;
- save_enabled = kcalloc(dev->mode_config.num_connector, sizeof(bool),
+ save_enabled = kcalloc(fb_helper->connector_count, sizeof(bool),
GFP_KERNEL);
if (!save_enabled)
return false;
- memcpy(save_enabled, enabled, dev->mode_config.num_connector);
+ memcpy(save_enabled, enabled, fb_helper->connector_count);
mask = (1 << fb_helper->connector_count) - 1;
retry:
for (i = 0; i < fb_helper->connector_count; i++) {
@@ -501,7 +501,7 @@ retry:
if (fallback) {
bail:
DRM_DEBUG_KMS("Not using firmware configuration\n");
- memcpy(enabled, save_enabled, dev->mode_config.num_connector);
+ memcpy(enabled, save_enabled, fb_helper->connector_count);
kfree(save_enabled);
return false;
}
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index e6c035b0fc1c..4b8ed9f2dabc 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1388,8 +1388,16 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
hdmi_to_dig_port(intel_hdmi));
}
- if (!live_status)
- DRM_DEBUG_KMS("Live status not up!");
+ if (!live_status) {
+ DRM_DEBUG_KMS("HDMI live status down\n");
+ /*
+ * Live status register is not reliable on all intel platforms.
+ * So consider live_status only for certain platforms, for
+ * others, read EDID to determine presence of sink.
+ */
+ if (INTEL_INFO(dev_priv)->gen < 7 || IS_IVYBRIDGE(dev_priv))
+ live_status = true;
+ }
intel_hdmi_unset_edid(connector);
diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
index d69547a65dbb..7058f75c7b42 100644
--- a/drivers/gpu/drm/i915/intel_lrc.c
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -776,11 +776,11 @@ static int logical_ring_prepare(struct drm_i915_gem_request *req, int bytes)
if (unlikely(total_bytes > remain_usable)) {
/*
* The base request will fit but the reserved space
- * falls off the end. So only need to to wait for the
- * reserved size after flushing out the remainder.
+ * falls off the end. So don't need an immediate wrap
+ * and only need to effectively wait for the reserved
+ * size space from the start of ringbuffer.
*/
wait_bytes = remain_actual + ringbuf->reserved_size;
- need_wrap = true;
} else if (total_bytes > ringbuf->space) {
/* No wrapping required, just waiting. */
wait_bytes = total_bytes;
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index f091ad12d694..62284e45d531 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -3880,6 +3880,8 @@ static void ilk_pipe_wm_get_hw_state(struct drm_crtc *crtc)
if (IS_HASWELL(dev) || IS_BROADWELL(dev))
hw->wm_linetime[pipe] = I915_READ(PIPE_WM_LINETIME(pipe));
+ memset(active, 0, sizeof(*active));
+
active->pipe_enabled = intel_crtc->active;
if (active->pipe_enabled) {
@@ -6620,6 +6622,12 @@ static void broadwell_init_clock_gating(struct drm_device *dev)
misccpctl = I915_READ(GEN7_MISCCPCTL);
I915_WRITE(GEN7_MISCCPCTL, misccpctl & ~GEN7_DOP_CLOCK_GATE_ENABLE);
I915_WRITE(GEN8_L3SQCREG1, BDW_WA_L3SQCREG1_DEFAULT);
+ /*
+ * Wait at least 100 clocks before re-enabling clock gating. See
+ * the definition of L3SQCREG1 in BSpec.
+ */
+ POSTING_READ(GEN8_L3SQCREG1);
+ udelay(1);
I915_WRITE(GEN7_MISCCPCTL, misccpctl);
/*
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index f6b2a814e629..9d48443bca2e 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -1922,6 +1922,17 @@ i915_dispatch_execbuffer(struct drm_i915_gem_request *req,
return 0;
}
+static void cleanup_phys_status_page(struct intel_engine_cs *ring)
+{
+ struct drm_i915_private *dev_priv = to_i915(ring->dev);
+
+ if (!dev_priv->status_page_dmah)
+ return;
+
+ drm_pci_free(ring->dev, dev_priv->status_page_dmah);
+ ring->status_page.page_addr = NULL;
+}
+
static void cleanup_status_page(struct intel_engine_cs *ring)
{
struct drm_i915_gem_object *obj;
@@ -1938,9 +1949,9 @@ static void cleanup_status_page(struct intel_engine_cs *ring)
static int init_status_page(struct intel_engine_cs *ring)
{
- struct drm_i915_gem_object *obj;
+ struct drm_i915_gem_object *obj = ring->status_page.obj;
- if ((obj = ring->status_page.obj) == NULL) {
+ if (obj == NULL) {
unsigned flags;
int ret;
@@ -2134,7 +2145,7 @@ static int intel_init_ring_buffer(struct drm_device *dev,
if (ret)
goto error;
} else {
- BUG_ON(ring->id != RCS);
+ WARN_ON(ring->id != RCS);
ret = init_phys_status_page(ring);
if (ret)
goto error;
@@ -2179,7 +2190,12 @@ void intel_cleanup_ring_buffer(struct intel_engine_cs *ring)
if (ring->cleanup)
ring->cleanup(ring);
- cleanup_status_page(ring);
+ if (I915_NEED_GFX_HWS(ring->dev)) {
+ cleanup_status_page(ring);
+ } else {
+ WARN_ON(ring->id != RCS);
+ cleanup_phys_status_page(ring);
+ }
i915_cmd_parser_fini_ring(ring);
i915_gem_batch_pool_fini(&ring->batch_pool);
@@ -2341,11 +2357,11 @@ static int __intel_ring_prepare(struct intel_engine_cs *ring, int bytes)
if (unlikely(total_bytes > remain_usable)) {
/*
* The base request will fit but the reserved space
- * falls off the end. So only need to to wait for the
- * reserved size after flushing out the remainder.
+ * falls off the end. So don't need an immediate wrap
+ * and only need to effectively wait for the reserved
+ * size space from the start of ringbuffer.
*/
wait_bytes = remain_actual + ringbuf->reserved_size;
- need_wrap = true;
} else if (total_bytes > ringbuf->space) {
/* No wrapping required, just waiting. */
wait_bytes = total_bytes;
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index 43cba129a0c0..cc91ae832ffb 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -1132,7 +1132,11 @@ static void intel_uncore_fw_domains_init(struct drm_device *dev)
} else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
dev_priv->uncore.funcs.force_wake_get =
fw_domains_get_with_thread_status;
- dev_priv->uncore.funcs.force_wake_put = fw_domains_put;
+ if (IS_HASWELL(dev))
+ dev_priv->uncore.funcs.force_wake_put =
+ fw_domains_put_with_fifo;
+ else
+ dev_priv->uncore.funcs.force_wake_put = fw_domains_put;
fw_domain_init(dev_priv, FW_DOMAIN_ID_RENDER,
FORCEWAKE_MT, FORCEWAKE_ACK_HSW);
} else if (IS_IVYBRIDGE(dev)) {
diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c
index 7b990b4e96d2..5378bdc3bbf9 100644
--- a/drivers/gpu/drm/imx/imx-drm-core.c
+++ b/drivers/gpu/drm/imx/imx-drm-core.c
@@ -26,6 +26,7 @@
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_of.h>
+#include <video/imx-ipu-v3.h>
#include "imx-drm.h"
@@ -504,6 +505,13 @@ static int compare_of(struct device *dev, void *data)
{
struct device_node *np = data;
+ /* Special case for DI, dev->of_node may not be set yet */
+ if (strcmp(dev->driver->name, "imx-ipuv3-crtc") == 0) {
+ struct ipu_client_platformdata *pdata = dev->platform_data;
+
+ return pdata->of_node == np;
+ }
+
/* Special case for LDB, one device for two channels */
if (of_node_cmp(np->name, "lvds-channel") == 0) {
np = of_get_parent(np);
diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c
index 4ab841eebee1..9b0abd44b751 100644
--- a/drivers/gpu/drm/imx/ipuv3-crtc.c
+++ b/drivers/gpu/drm/imx/ipuv3-crtc.c
@@ -369,7 +369,7 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
ret = imx_drm_add_crtc(drm, &ipu_crtc->base, &ipu_crtc->imx_crtc,
&ipu_crtc->plane[0]->base, &ipu_crtc_helper_funcs,
- ipu_crtc->dev->of_node);
+ pdata->of_node);
if (ret) {
dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret);
goto err_put_resources;
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index c99d3fe12881..e5bb40e58020 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -194,7 +194,7 @@ static int mga_g200se_set_plls(struct mga_device *mdev, long clock)
}
}
- fvv = pllreffreq * testn / testm;
+ fvv = pllreffreq * (n + 1) / (m + 1);
fvv = (fvv - 800000) / 50000;
if (fvv > 15)
@@ -214,6 +214,14 @@ static int mga_g200se_set_plls(struct mga_device *mdev, long clock)
WREG_DAC(MGA1064_PIX_PLLC_M, m);
WREG_DAC(MGA1064_PIX_PLLC_N, n);
WREG_DAC(MGA1064_PIX_PLLC_P, p);
+
+ if (mdev->unique_rev_id >= 0x04) {
+ WREG_DAC(0x1a, 0x09);
+ msleep(20);
+ WREG_DAC(0x1a, 0x01);
+
+ }
+
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 59f27e774acb..e40a1b07a014 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -557,6 +557,8 @@ nouveau_fbcon_init(struct drm_device *dev)
if (ret)
goto fini;
+ if (fbcon->helper.fbdev)
+ fbcon->helper.fbdev->pixmap.buf_align = 4;
return 0;
fini:
diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c
index 789dc2993b0d..8f715feadf56 100644
--- a/drivers/gpu/drm/nouveau/nv04_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c
@@ -82,7 +82,6 @@ nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
uint32_t fg;
uint32_t bg;
uint32_t dsize;
- uint32_t width;
uint32_t *data = (uint32_t *)image->data;
int ret;
@@ -93,9 +92,6 @@ nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
if (ret)
return ret;
- width = ALIGN(image->width, 8);
- dsize = ALIGN(width * image->height, 32) >> 5;
-
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
fg = ((uint32_t *) info->pseudo_palette)[image->fg_color];
@@ -111,10 +107,11 @@ nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
((image->dx + image->width) & 0xffff));
OUT_RING(chan, bg);
OUT_RING(chan, fg);
- OUT_RING(chan, (image->height << 16) | width);
+ OUT_RING(chan, (image->height << 16) | image->width);
OUT_RING(chan, (image->height << 16) | image->width);
OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff));
+ dsize = ALIGN(image->width * image->height, 32) >> 5;
while (dsize) {
int iter_len = dsize > 128 ? 128 : dsize;
diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c
index e05499d6ed83..a4e259a00430 100644
--- a/drivers/gpu/drm/nouveau/nv50_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c
@@ -95,7 +95,7 @@ nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
struct nouveau_fbdev *nfbdev = info->par;
struct nouveau_drm *drm = nouveau_drm(nfbdev->dev);
struct nouveau_channel *chan = drm->channel;
- uint32_t width, dwords, *data = (uint32_t *)image->data;
+ uint32_t dwords, *data = (uint32_t *)image->data;
uint32_t mask = ~(~0 >> (32 - info->var.bits_per_pixel));
uint32_t *palette = info->pseudo_palette;
int ret;
@@ -107,9 +107,6 @@ nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
if (ret)
return ret;
- width = ALIGN(image->width, 32);
- dwords = (width * image->height) >> 5;
-
BEGIN_NV04(chan, NvSub2D, 0x0814, 2);
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
@@ -128,6 +125,7 @@ nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
OUT_RING(chan, 0);
OUT_RING(chan, image->dy);
+ dwords = ALIGN(image->width * image->height, 32) >> 5;
while (dwords) {
int push = dwords > 2047 ? 2047 : dwords;
diff --git a/drivers/gpu/drm/nouveau/nvc0_fbcon.c b/drivers/gpu/drm/nouveau/nvc0_fbcon.c
index c97395b4a312..f28315e865a5 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fbcon.c
@@ -95,7 +95,7 @@ nvc0_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
struct nouveau_fbdev *nfbdev = info->par;
struct nouveau_drm *drm = nouveau_drm(nfbdev->dev);
struct nouveau_channel *chan = drm->channel;
- uint32_t width, dwords, *data = (uint32_t *)image->data;
+ uint32_t dwords, *data = (uint32_t *)image->data;
uint32_t mask = ~(~0 >> (32 - info->var.bits_per_pixel));
uint32_t *palette = info->pseudo_palette;
int ret;
@@ -107,9 +107,6 @@ nvc0_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
if (ret)
return ret;
- width = ALIGN(image->width, 32);
- dwords = (width * image->height) >> 5;
-
BEGIN_NVC0(chan, NvSub2D, 0x0814, 2);
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
@@ -128,6 +125,7 @@ nvc0_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
OUT_RING (chan, 0);
OUT_RING (chan, image->dy);
+ dwords = ALIGN(image->width * image->height, 32) >> 5;
while (dwords) {
int push = dwords > 2047 ? 2047 : dwords;
diff --git a/drivers/gpu/drm/nouveau/nvkm/core/ramht.c b/drivers/gpu/drm/nouveau/nvkm/core/ramht.c
index 3216e157a8a0..89da47234016 100644
--- a/drivers/gpu/drm/nouveau/nvkm/core/ramht.c
+++ b/drivers/gpu/drm/nouveau/nvkm/core/ramht.c
@@ -131,7 +131,7 @@ nvkm_ramht_del(struct nvkm_ramht **pramht)
struct nvkm_ramht *ramht = *pramht;
if (ramht) {
nvkm_gpuobj_del(&ramht->gpuobj);
- kfree(*pramht);
+ vfree(*pramht);
*pramht = NULL;
}
}
@@ -143,8 +143,8 @@ nvkm_ramht_new(struct nvkm_device *device, u32 size, u32 align,
struct nvkm_ramht *ramht;
int ret, i;
- if (!(ramht = *pramht = kzalloc(sizeof(*ramht) + (size >> 3) *
- sizeof(*ramht->data), GFP_KERNEL)))
+ if (!(ramht = *pramht = vzalloc(sizeof(*ramht) +
+ (size >> 3) * sizeof(*ramht->data))))
return -ENOMEM;
ramht->device = device;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c
index b4b41b135643..2aaf0dd19a55 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c
@@ -40,8 +40,8 @@ static int
gf119_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
{
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
- const u32 loff = gf119_sor_loff(outp);
- nvkm_mask(device, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern);
+ const u32 soff = gf119_sor_soff(outp);
+ nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, 0x01010101 * pattern);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
index 9f5dfc85147a..eeeea1c2ca23 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
@@ -874,22 +874,41 @@ gf100_gr_trap_gpc_rop(struct gf100_gr *gr, int gpc)
}
static const struct nvkm_enum gf100_mp_warp_error[] = {
- { 0x00, "NO_ERROR" },
- { 0x01, "STACK_MISMATCH" },
+ { 0x01, "STACK_ERROR" },
+ { 0x02, "API_STACK_ERROR" },
+ { 0x03, "RET_EMPTY_STACK_ERROR" },
+ { 0x04, "PC_WRAP" },
{ 0x05, "MISALIGNED_PC" },
- { 0x08, "MISALIGNED_GPR" },
- { 0x09, "INVALID_OPCODE" },
- { 0x0d, "GPR_OUT_OF_BOUNDS" },
- { 0x0e, "MEM_OUT_OF_BOUNDS" },
- { 0x0f, "UNALIGNED_MEM_ACCESS" },
+ { 0x06, "PC_OVERFLOW" },
+ { 0x07, "MISALIGNED_IMMC_ADDR" },
+ { 0x08, "MISALIGNED_REG" },
+ { 0x09, "ILLEGAL_INSTR_ENCODING" },
+ { 0x0a, "ILLEGAL_SPH_INSTR_COMBO" },
+ { 0x0b, "ILLEGAL_INSTR_PARAM" },
+ { 0x0c, "INVALID_CONST_ADDR" },
+ { 0x0d, "OOR_REG" },
+ { 0x0e, "OOR_ADDR" },
+ { 0x0f, "MISALIGNED_ADDR" },
{ 0x10, "INVALID_ADDR_SPACE" },
- { 0x11, "INVALID_PARAM" },
+ { 0x11, "ILLEGAL_INSTR_PARAM2" },
+ { 0x12, "INVALID_CONST_ADDR_LDC" },
+ { 0x13, "GEOMETRY_SM_ERROR" },
+ { 0x14, "DIVERGENT" },
+ { 0x15, "WARP_EXIT" },
{}
};
static const struct nvkm_bitfield gf100_mp_global_error[] = {
+ { 0x00000001, "SM_TO_SM_FAULT" },
+ { 0x00000002, "L1_ERROR" },
{ 0x00000004, "MULTIPLE_WARP_ERRORS" },
- { 0x00000008, "OUT_OF_STACK_SPACE" },
+ { 0x00000008, "PHYSICAL_STACK_OVERFLOW" },
+ { 0x00000010, "BPT_INT" },
+ { 0x00000020, "BPT_PAUSE" },
+ { 0x00000040, "SINGLE_STEP_COMPLETE" },
+ { 0x20000000, "ECC_SEC_ERROR" },
+ { 0x40000000, "ECC_DED_ERROR" },
+ { 0x80000000, "TIMEOUT" },
{}
};
@@ -1717,6 +1736,8 @@ gf100_gr_init(struct gf100_gr *gr)
gf100_gr_mmio(gr, gr->func->mmio);
+ nvkm_mask(device, TPC_UNIT(0, 0, 0x05c), 0x00000001, 0x00000001);
+
memcpy(tpcnr, gr->tpc_nr, sizeof(gr->tpc_nr));
for (i = 0, gpc = -1; i < gr->tpc_total; i++) {
do {
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
index 183aea1abebc..5edebf495c07 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -375,10 +375,15 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
qxl_bo_kunmap(user_bo);
+ qcrtc->cur_x += qcrtc->hot_spot_x - hot_x;
+ qcrtc->cur_y += qcrtc->hot_spot_y - hot_y;
+ qcrtc->hot_spot_x = hot_x;
+ qcrtc->hot_spot_y = hot_y;
+
cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
cmd->type = QXL_CURSOR_SET;
- cmd->u.set.position.x = qcrtc->cur_x;
- cmd->u.set.position.y = qcrtc->cur_y;
+ cmd->u.set.position.x = qcrtc->cur_x + qcrtc->hot_spot_x;
+ cmd->u.set.position.y = qcrtc->cur_y + qcrtc->hot_spot_y;
cmd->u.set.shape = qxl_bo_physical_address(qdev, cursor_bo, 0);
@@ -441,8 +446,8 @@ static int qxl_crtc_cursor_move(struct drm_crtc *crtc,
cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
cmd->type = QXL_CURSOR_MOVE;
- cmd->u.position.x = qcrtc->cur_x;
- cmd->u.position.y = qcrtc->cur_y;
+ cmd->u.position.x = qcrtc->cur_x + qcrtc->hot_spot_x;
+ cmd->u.position.y = qcrtc->cur_y + qcrtc->hot_spot_y;
qxl_release_unmap(qdev, release, &cmd->release_info);
qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h
index 01a86948eb8c..3ab90179e9ab 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.h
+++ b/drivers/gpu/drm/qxl/qxl_drv.h
@@ -135,6 +135,8 @@ struct qxl_crtc {
int index;
int cur_x;
int cur_y;
+ int hot_spot_x;
+ int hot_spot_y;
};
struct qxl_output {
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index dac78ad24b31..79bab6fd76bb 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -1739,6 +1739,7 @@ static u32 radeon_get_pll_use_mask(struct drm_crtc *crtc)
static int radeon_get_shared_dp_ppll(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
struct drm_crtc *test_crtc;
struct radeon_crtc *test_radeon_crtc;
@@ -1748,6 +1749,10 @@ static int radeon_get_shared_dp_ppll(struct drm_crtc *crtc)
test_radeon_crtc = to_radeon_crtc(test_crtc);
if (test_radeon_crtc->encoder &&
ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_radeon_crtc->encoder))) {
+ /* PPLL2 is exclusive to UNIPHYA on DCE61 */
+ if (ASIC_IS_DCE61(rdev) && !ASIC_IS_DCE8(rdev) &&
+ test_radeon_crtc->pll_id == ATOM_PPLL2)
+ continue;
/* for DP use the same PLL for all */
if (test_radeon_crtc->pll_id != ATOM_PPLL_INVALID)
return test_radeon_crtc->pll_id;
@@ -1769,6 +1774,7 @@ static int radeon_get_shared_nondp_ppll(struct drm_crtc *crtc)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
struct drm_crtc *test_crtc;
struct radeon_crtc *test_radeon_crtc;
u32 adjusted_clock, test_adjusted_clock;
@@ -1784,6 +1790,10 @@ static int radeon_get_shared_nondp_ppll(struct drm_crtc *crtc)
test_radeon_crtc = to_radeon_crtc(test_crtc);
if (test_radeon_crtc->encoder &&
!ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_radeon_crtc->encoder))) {
+ /* PPLL2 is exclusive to UNIPHYA on DCE61 */
+ if (ASIC_IS_DCE61(rdev) && !ASIC_IS_DCE8(rdev) &&
+ test_radeon_crtc->pll_id == ATOM_PPLL2)
+ continue;
/* check if we are already driving this connector with another crtc */
if (test_radeon_crtc->connector == radeon_crtc->connector) {
/* if we are, return that pll */
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c
index adf74f4366bb..0b04b9282f56 100644
--- a/drivers/gpu/drm/radeon/atombios_encoders.c
+++ b/drivers/gpu/drm/radeon/atombios_encoders.c
@@ -310,6 +310,10 @@ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
&& (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2)))
adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2;
+ /* vertical FP must be at least 1 */
+ if (mode->crtc_vsync_start == mode->crtc_vdisplay)
+ adjusted_mode->crtc_vsync_start++;
+
/* get the native mode for scaling */
if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
radeon_panel_mode_fixup(encoder, adjusted_mode);
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 2ad462896896..32491355a1d4 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -2608,10 +2608,152 @@ static void evergreen_agp_enable(struct radeon_device *rdev)
WREG32(VM_CONTEXT1_CNTL, 0);
}
+static const unsigned ni_dig_offsets[] =
+{
+ NI_DIG0_REGISTER_OFFSET,
+ NI_DIG1_REGISTER_OFFSET,
+ NI_DIG2_REGISTER_OFFSET,
+ NI_DIG3_REGISTER_OFFSET,
+ NI_DIG4_REGISTER_OFFSET,
+ NI_DIG5_REGISTER_OFFSET
+};
+
+static const unsigned ni_tx_offsets[] =
+{
+ NI_DCIO_UNIPHY0_UNIPHY_TX_CONTROL1,
+ NI_DCIO_UNIPHY1_UNIPHY_TX_CONTROL1,
+ NI_DCIO_UNIPHY2_UNIPHY_TX_CONTROL1,
+ NI_DCIO_UNIPHY3_UNIPHY_TX_CONTROL1,
+ NI_DCIO_UNIPHY4_UNIPHY_TX_CONTROL1,
+ NI_DCIO_UNIPHY5_UNIPHY_TX_CONTROL1
+};
+
+static const unsigned evergreen_dp_offsets[] =
+{
+ EVERGREEN_DP0_REGISTER_OFFSET,
+ EVERGREEN_DP1_REGISTER_OFFSET,
+ EVERGREEN_DP2_REGISTER_OFFSET,
+ EVERGREEN_DP3_REGISTER_OFFSET,
+ EVERGREEN_DP4_REGISTER_OFFSET,
+ EVERGREEN_DP5_REGISTER_OFFSET
+};
+
+
+/*
+ * Assumption is that EVERGREEN_CRTC_MASTER_EN enable for requested crtc
+ * We go from crtc to connector and it is not relible since it
+ * should be an opposite direction .If crtc is enable then
+ * find the dig_fe which selects this crtc and insure that it enable.
+ * if such dig_fe is found then find dig_be which selects found dig_be and
+ * insure that it enable and in DP_SST mode.
+ * if UNIPHY_PLL_CONTROL1.enable then we should disconnect timing
+ * from dp symbols clocks .
+ */
+static bool evergreen_is_dp_sst_stream_enabled(struct radeon_device *rdev,
+ unsigned crtc_id, unsigned *ret_dig_fe)
+{
+ unsigned i;
+ unsigned dig_fe;
+ unsigned dig_be;
+ unsigned dig_en_be;
+ unsigned uniphy_pll;
+ unsigned digs_fe_selected;
+ unsigned dig_be_mode;
+ unsigned dig_fe_mask;
+ bool is_enabled = false;
+ bool found_crtc = false;
+
+ /* loop through all running dig_fe to find selected crtc */
+ for (i = 0; i < ARRAY_SIZE(ni_dig_offsets); i++) {
+ dig_fe = RREG32(NI_DIG_FE_CNTL + ni_dig_offsets[i]);
+ if (dig_fe & NI_DIG_FE_CNTL_SYMCLK_FE_ON &&
+ crtc_id == NI_DIG_FE_CNTL_SOURCE_SELECT(dig_fe)) {
+ /* found running pipe */
+ found_crtc = true;
+ dig_fe_mask = 1 << i;
+ dig_fe = i;
+ break;
+ }
+ }
+
+ if (found_crtc) {
+ /* loop through all running dig_be to find selected dig_fe */
+ for (i = 0; i < ARRAY_SIZE(ni_dig_offsets); i++) {
+ dig_be = RREG32(NI_DIG_BE_CNTL + ni_dig_offsets[i]);
+ /* if dig_fe_selected by dig_be? */
+ digs_fe_selected = NI_DIG_BE_CNTL_FE_SOURCE_SELECT(dig_be);
+ dig_be_mode = NI_DIG_FE_CNTL_MODE(dig_be);
+ if (dig_fe_mask & digs_fe_selected &&
+ /* if dig_be in sst mode? */
+ dig_be_mode == NI_DIG_BE_DPSST) {
+ dig_en_be = RREG32(NI_DIG_BE_EN_CNTL +
+ ni_dig_offsets[i]);
+ uniphy_pll = RREG32(NI_DCIO_UNIPHY0_PLL_CONTROL1 +
+ ni_tx_offsets[i]);
+ /* dig_be enable and tx is running */
+ if (dig_en_be & NI_DIG_BE_EN_CNTL_ENABLE &&
+ dig_en_be & NI_DIG_BE_EN_CNTL_SYMBCLK_ON &&
+ uniphy_pll & NI_DCIO_UNIPHY0_PLL_CONTROL1_ENABLE) {
+ is_enabled = true;
+ *ret_dig_fe = dig_fe;
+ break;
+ }
+ }
+ }
+ }
+
+ return is_enabled;
+}
+
+/*
+ * Blank dig when in dp sst mode
+ * Dig ignores crtc timing
+ */
+static void evergreen_blank_dp_output(struct radeon_device *rdev,
+ unsigned dig_fe)
+{
+ unsigned stream_ctrl;
+ unsigned fifo_ctrl;
+ unsigned counter = 0;
+
+ if (dig_fe >= ARRAY_SIZE(evergreen_dp_offsets)) {
+ DRM_ERROR("invalid dig_fe %d\n", dig_fe);
+ return;
+ }
+
+ stream_ctrl = RREG32(EVERGREEN_DP_VID_STREAM_CNTL +
+ evergreen_dp_offsets[dig_fe]);
+ if (!(stream_ctrl & EVERGREEN_DP_VID_STREAM_CNTL_ENABLE)) {
+ DRM_ERROR("dig %d , should be enable\n", dig_fe);
+ return;
+ }
+
+ stream_ctrl &=~EVERGREEN_DP_VID_STREAM_CNTL_ENABLE;
+ WREG32(EVERGREEN_DP_VID_STREAM_CNTL +
+ evergreen_dp_offsets[dig_fe], stream_ctrl);
+
+ stream_ctrl = RREG32(EVERGREEN_DP_VID_STREAM_CNTL +
+ evergreen_dp_offsets[dig_fe]);
+ while (counter < 32 && stream_ctrl & EVERGREEN_DP_VID_STREAM_STATUS) {
+ msleep(1);
+ counter++;
+ stream_ctrl = RREG32(EVERGREEN_DP_VID_STREAM_CNTL +
+ evergreen_dp_offsets[dig_fe]);
+ }
+ if (counter >= 32 )
+ DRM_ERROR("counter exceeds %d\n", counter);
+
+ fifo_ctrl = RREG32(EVERGREEN_DP_STEER_FIFO + evergreen_dp_offsets[dig_fe]);
+ fifo_ctrl |= EVERGREEN_DP_STEER_FIFO_RESET;
+ WREG32(EVERGREEN_DP_STEER_FIFO + evergreen_dp_offsets[dig_fe], fifo_ctrl);
+
+}
+
void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save)
{
u32 crtc_enabled, tmp, frame_count, blackout;
int i, j;
+ unsigned dig_fe;
if (!ASIC_IS_NODCE(rdev)) {
save->vga_render_control = RREG32(VGA_RENDER_CONTROL);
@@ -2651,7 +2793,17 @@ void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *sav
break;
udelay(1);
}
-
+ /*we should disable dig if it drives dp sst*/
+ /*but we are in radeon_device_init and the topology is unknown*/
+ /*and it is available after radeon_modeset_init*/
+ /*the following method radeon_atom_encoder_dpms_dig*/
+ /*does the job if we initialize it properly*/
+ /*for now we do it this manually*/
+ /**/
+ if (ASIC_IS_DCE5(rdev) &&
+ evergreen_is_dp_sst_stream_enabled(rdev, i ,&dig_fe))
+ evergreen_blank_dp_output(rdev, dig_fe);
+ /*we could remove 6 lines below*/
/* XXX this is a hack to avoid strange behavior with EFI on certain systems */
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1);
tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]);
diff --git a/drivers/gpu/drm/radeon/evergreen_reg.h b/drivers/gpu/drm/radeon/evergreen_reg.h
index aa939dfed3a3..b436badf9efa 100644
--- a/drivers/gpu/drm/radeon/evergreen_reg.h
+++ b/drivers/gpu/drm/radeon/evergreen_reg.h
@@ -250,8 +250,43 @@
/* HDMI blocks at 0x7030, 0x7c30, 0x10830, 0x11430, 0x12030, 0x12c30 */
#define EVERGREEN_HDMI_BASE 0x7030
+/*DIG block*/
+#define NI_DIG0_REGISTER_OFFSET (0x7000 - 0x7000)
+#define NI_DIG1_REGISTER_OFFSET (0x7C00 - 0x7000)
+#define NI_DIG2_REGISTER_OFFSET (0x10800 - 0x7000)
+#define NI_DIG3_REGISTER_OFFSET (0x11400 - 0x7000)
+#define NI_DIG4_REGISTER_OFFSET (0x12000 - 0x7000)
+#define NI_DIG5_REGISTER_OFFSET (0x12C00 - 0x7000)
+
+
+#define NI_DIG_FE_CNTL 0x7000
+# define NI_DIG_FE_CNTL_SOURCE_SELECT(x) ((x) & 0x3)
+# define NI_DIG_FE_CNTL_SYMCLK_FE_ON (1<<24)
+
+
+#define NI_DIG_BE_CNTL 0x7140
+# define NI_DIG_BE_CNTL_FE_SOURCE_SELECT(x) (((x) >> 8 ) & 0x3F)
+# define NI_DIG_FE_CNTL_MODE(x) (((x) >> 16) & 0x7 )
+
+#define NI_DIG_BE_EN_CNTL 0x7144
+# define NI_DIG_BE_EN_CNTL_ENABLE (1 << 0)
+# define NI_DIG_BE_EN_CNTL_SYMBCLK_ON (1 << 8)
+# define NI_DIG_BE_DPSST 0
/* Display Port block */
+#define EVERGREEN_DP0_REGISTER_OFFSET (0x730C - 0x730C)
+#define EVERGREEN_DP1_REGISTER_OFFSET (0x7F0C - 0x730C)
+#define EVERGREEN_DP2_REGISTER_OFFSET (0x10B0C - 0x730C)
+#define EVERGREEN_DP3_REGISTER_OFFSET (0x1170C - 0x730C)
+#define EVERGREEN_DP4_REGISTER_OFFSET (0x1230C - 0x730C)
+#define EVERGREEN_DP5_REGISTER_OFFSET (0x12F0C - 0x730C)
+
+
+#define EVERGREEN_DP_VID_STREAM_CNTL 0x730C
+# define EVERGREEN_DP_VID_STREAM_CNTL_ENABLE (1 << 0)
+# define EVERGREEN_DP_VID_STREAM_STATUS (1 <<16)
+#define EVERGREEN_DP_STEER_FIFO 0x7310
+# define EVERGREEN_DP_STEER_FIFO_RESET (1 << 0)
#define EVERGREEN_DP_SEC_CNTL 0x7280
# define EVERGREEN_DP_SEC_STREAM_ENABLE (1 << 0)
# define EVERGREEN_DP_SEC_ASP_ENABLE (1 << 4)
@@ -266,4 +301,15 @@
# define EVERGREEN_DP_SEC_N_BASE_MULTIPLE(x) (((x) & 0xf) << 24)
# define EVERGREEN_DP_SEC_SS_EN (1 << 28)
+/*DCIO_UNIPHY block*/
+#define NI_DCIO_UNIPHY0_UNIPHY_TX_CONTROL1 (0x6600 -0x6600)
+#define NI_DCIO_UNIPHY1_UNIPHY_TX_CONTROL1 (0x6640 -0x6600)
+#define NI_DCIO_UNIPHY2_UNIPHY_TX_CONTROL1 (0x6680 - 0x6600)
+#define NI_DCIO_UNIPHY3_UNIPHY_TX_CONTROL1 (0x66C0 - 0x6600)
+#define NI_DCIO_UNIPHY4_UNIPHY_TX_CONTROL1 (0x6700 - 0x6600)
+#define NI_DCIO_UNIPHY5_UNIPHY_TX_CONTROL1 (0x6740 - 0x6600)
+
+#define NI_DCIO_UNIPHY0_PLL_CONTROL1 0x6618
+# define NI_DCIO_UNIPHY0_PLL_CONTROL1_ENABLE (1 << 0)
+
#endif
diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
index 9bc408c9f9f6..c4b4f298a283 100644
--- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
+++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
@@ -62,10 +62,6 @@ bool radeon_has_atpx(void) {
return radeon_atpx_priv.atpx_detected;
}
-bool radeon_has_atpx_dgpu_power_cntl(void) {
- return radeon_atpx_priv.atpx.functions.power_cntl;
-}
-
/**
* radeon_atpx_call - call an ATPX method
*
@@ -145,6 +141,10 @@ static void radeon_atpx_parse_functions(struct radeon_atpx_functions *f, u32 mas
*/
static int radeon_atpx_validate(struct radeon_atpx *atpx)
{
+ /* make sure required functions are enabled */
+ /* dGPU power control is required */
+ atpx->functions.power_cntl = true;
+
if (atpx->functions.px_params) {
union acpi_object *info;
struct atpx_px_params output;
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 340f3f549f29..9cfc1c3e1965 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -1996,10 +1996,12 @@ radeon_add_atom_connector(struct drm_device *dev,
rdev->mode_info.dither_property,
RADEON_FMT_DITHER_DISABLE);
- if (radeon_audio != 0)
+ if (radeon_audio != 0) {
drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.audio_property,
RADEON_AUDIO_AUTO);
+ radeon_connector->audio = RADEON_AUDIO_AUTO;
+ }
if (ASIC_IS_DCE5(rdev))
drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.output_csc_property,
@@ -2124,6 +2126,7 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.audio_property,
RADEON_AUDIO_AUTO);
+ radeon_connector->audio = RADEON_AUDIO_AUTO;
}
if (connector_type == DRM_MODE_CONNECTOR_DVII) {
radeon_connector->dac_load_detect = true;
@@ -2179,6 +2182,7 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.audio_property,
RADEON_AUDIO_AUTO);
+ radeon_connector->audio = RADEON_AUDIO_AUTO;
}
if (ASIC_IS_DCE5(rdev))
drm_object_attach_property(&radeon_connector->base.base,
@@ -2231,6 +2235,7 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.audio_property,
RADEON_AUDIO_AUTO);
+ radeon_connector->audio = RADEON_AUDIO_AUTO;
}
if (ASIC_IS_DCE5(rdev))
drm_object_attach_property(&radeon_connector->base.base,
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index f78f111e68de..e2dd5d19c32c 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -103,12 +103,6 @@ static const char radeon_family_name[][16] = {
"LAST",
};
-#if defined(CONFIG_VGA_SWITCHEROO)
-bool radeon_has_atpx_dgpu_power_cntl(void);
-#else
-static inline bool radeon_has_atpx_dgpu_power_cntl(void) { return false; }
-#endif
-
#define RADEON_PX_QUIRK_DISABLE_PX (1 << 0)
#define RADEON_PX_QUIRK_LONG_WAKEUP (1 << 1)
@@ -636,6 +630,23 @@ void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc)
/*
* GPU helpers function.
*/
+
+/**
+ * radeon_device_is_virtual - check if we are running is a virtual environment
+ *
+ * Check if the asic has been passed through to a VM (all asics).
+ * Used at driver startup.
+ * Returns true if virtual or false if not.
+ */
+static bool radeon_device_is_virtual(void)
+{
+#ifdef CONFIG_X86
+ return boot_cpu_has(X86_FEATURE_HYPERVISOR);
+#else
+ return false;
+#endif
+}
+
/**
* radeon_card_posted - check if the hw has already been initialized
*
@@ -649,6 +660,10 @@ bool radeon_card_posted(struct radeon_device *rdev)
{
uint32_t reg;
+ /* for pass through, always force asic_init */
+ if (radeon_device_is_virtual())
+ return false;
+
/* required for EFI mode on macbook2,1 which uses an r5xx asic */
if (efi_enabled(EFI_BOOT) &&
(rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE) &&
@@ -1439,7 +1454,7 @@ int radeon_device_init(struct radeon_device *rdev,
* ignore it */
vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode);
- if ((rdev->flags & RADEON_IS_PX) && radeon_has_atpx_dgpu_power_cntl())
+ if (rdev->flags & RADEON_IS_PX)
runtime = true;
vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops, runtime);
if (runtime)
diff --git a/drivers/gpu/drm/radeon/radeon_dp_auxch.c b/drivers/gpu/drm/radeon/radeon_dp_auxch.c
index 3b0c229d7dcd..db64e0062689 100644
--- a/drivers/gpu/drm/radeon/radeon_dp_auxch.c
+++ b/drivers/gpu/drm/radeon/radeon_dp_auxch.c
@@ -105,7 +105,7 @@ radeon_dp_aux_transfer_native(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg
tmp &= AUX_HPD_SEL(0x7);
tmp |= AUX_HPD_SEL(chan->rec.hpd);
- tmp |= AUX_EN | AUX_LS_READ_EN;
+ tmp |= AUX_EN | AUX_LS_READ_EN | AUX_HPD_DISCON(0x1);
WREG32(AUX_CONTROL + aux_offset[instance], tmp);
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index e06ac546a90f..f342aad79cc6 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -235,6 +235,8 @@ static int radeon_verify_access(struct ttm_buffer_object *bo, struct file *filp)
{
struct radeon_bo *rbo = container_of(bo, struct radeon_bo, tbo);
+ if (radeon_ttm_tt_has_userptr(bo->ttm))
+ return -EPERM;
return drm_vma_node_verify_access(&rbo->gem_base.vma_node, filp);
}
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index 7285adb27099..caa73de584a5 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -2931,6 +2931,7 @@ static struct si_dpm_quirk si_dpm_quirk_list[] = {
{ PCI_VENDOR_ID_ATI, 0x6811, 0x1462, 0x2015, 0, 120000 },
{ PCI_VENDOR_ID_ATI, 0x6811, 0x1043, 0x2015, 0, 120000 },
{ PCI_VENDOR_ID_ATI, 0x6811, 0x148c, 0x2015, 0, 120000 },
+ { PCI_VENDOR_ID_ATI, 0x6810, 0x1682, 0x9275, 0, 120000 },
{ 0, 0, 0, 0 },
};
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 745e996d2dbc..4ae8b56b1847 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -1004,9 +1004,9 @@ out_unlock:
return ret;
}
-static bool ttm_bo_mem_compat(struct ttm_placement *placement,
- struct ttm_mem_reg *mem,
- uint32_t *new_flags)
+bool ttm_bo_mem_compat(struct ttm_placement *placement,
+ struct ttm_mem_reg *mem,
+ uint32_t *new_flags)
{
int i;
@@ -1038,6 +1038,7 @@ static bool ttm_bo_mem_compat(struct ttm_placement *placement,
return false;
}
+EXPORT_SYMBOL(ttm_bo_mem_compat);
int ttm_bo_validate(struct ttm_buffer_object *bo,
struct ttm_placement *placement,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
index 299925a1f6c6..eadc981ee79a 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
@@ -49,6 +49,7 @@ int vmw_dmabuf_pin_in_placement(struct vmw_private *dev_priv,
{
struct ttm_buffer_object *bo = &buf->base;
int ret;
+ uint32_t new_flags;
ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible);
if (unlikely(ret != 0))
@@ -60,7 +61,12 @@ int vmw_dmabuf_pin_in_placement(struct vmw_private *dev_priv,
if (unlikely(ret != 0))
goto err;
- ret = ttm_bo_validate(bo, placement, interruptible, false);
+ if (buf->pin_count > 0)
+ ret = ttm_bo_mem_compat(placement, &bo->mem,
+ &new_flags) == true ? 0 : -EINVAL;
+ else
+ ret = ttm_bo_validate(bo, placement, interruptible, false);
+
if (!ret)
vmw_bo_pin_reserved(buf, true);
@@ -91,6 +97,7 @@ int vmw_dmabuf_pin_in_vram_or_gmr(struct vmw_private *dev_priv,
{
struct ttm_buffer_object *bo = &buf->base;
int ret;
+ uint32_t new_flags;
ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible);
if (unlikely(ret != 0))
@@ -102,6 +109,12 @@ int vmw_dmabuf_pin_in_vram_or_gmr(struct vmw_private *dev_priv,
if (unlikely(ret != 0))
goto err;
+ if (buf->pin_count > 0) {
+ ret = ttm_bo_mem_compat(&vmw_vram_gmr_placement, &bo->mem,
+ &new_flags) == true ? 0 : -EINVAL;
+ goto out_unreserve;
+ }
+
ret = ttm_bo_validate(bo, &vmw_vram_gmr_placement, interruptible,
false);
if (likely(ret == 0) || ret == -ERESTARTSYS)
@@ -161,6 +174,7 @@ int vmw_dmabuf_pin_in_start_of_vram(struct vmw_private *dev_priv,
struct ttm_placement placement;
struct ttm_place place;
int ret = 0;
+ uint32_t new_flags;
place = vmw_vram_placement.placement[0];
place.lpfn = bo->num_pages;
@@ -185,10 +199,15 @@ int vmw_dmabuf_pin_in_start_of_vram(struct vmw_private *dev_priv,
*/
if (bo->mem.mem_type == TTM_PL_VRAM &&
bo->mem.start < bo->num_pages &&
- bo->mem.start > 0)
+ bo->mem.start > 0 &&
+ buf->pin_count == 0)
(void) ttm_bo_validate(bo, &vmw_sys_placement, false, false);
- ret = ttm_bo_validate(bo, &placement, interruptible, false);
+ if (buf->pin_count > 0)
+ ret = ttm_bo_mem_compat(&placement, &bo->mem,
+ &new_flags) == true ? 0 : -EINVAL;
+ else
+ ret = ttm_bo_validate(bo, &placement, interruptible, false);
/* For some reason we didn't end up at the start of vram */
WARN_ON(ret == 0 && bo->offset != 0);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 24fb348a44e1..f3f31f995878 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -227,6 +227,7 @@ static int vmw_force_iommu;
static int vmw_restrict_iommu;
static int vmw_force_coherent;
static int vmw_restrict_dma_mask;
+static int vmw_assume_16bpp;
static int vmw_probe(struct pci_dev *, const struct pci_device_id *);
static void vmw_master_init(struct vmw_master *);
@@ -243,6 +244,8 @@ MODULE_PARM_DESC(force_coherent, "Force coherent TTM pages");
module_param_named(force_coherent, vmw_force_coherent, int, 0600);
MODULE_PARM_DESC(restrict_dma_mask, "Restrict DMA mask to 44 bits with IOMMU");
module_param_named(restrict_dma_mask, vmw_restrict_dma_mask, int, 0600);
+MODULE_PARM_DESC(assume_16bpp, "Assume 16-bpp when filtering modes");
+module_param_named(assume_16bpp, vmw_assume_16bpp, int, 0600);
static void vmw_print_capabilities(uint32_t capabilities)
@@ -652,6 +655,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
dev_priv->vram_start = pci_resource_start(dev->pdev, 1);
dev_priv->mmio_start = pci_resource_start(dev->pdev, 2);
+ dev_priv->assume_16bpp = !!vmw_assume_16bpp;
+
dev_priv->enable_fb = enable_fbdev;
vmw_write(dev_priv, SVGA_REG_ID, SVGA_ID_2);
@@ -698,6 +703,13 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
vmw_read(dev_priv,
SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB);
+ /*
+ * Workaround for low memory 2D VMs to compensate for the
+ * allocation taken by fbdev
+ */
+ if (!(dev_priv->capabilities & SVGA_CAP_3D))
+ mem_size *= 2;
+
dev_priv->max_mob_pages = mem_size * 1024 / PAGE_SIZE;
dev_priv->prim_bb_mem =
vmw_read(dev_priv,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
index 469cdd520615..2e94fe27b3f6 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
@@ -387,6 +387,7 @@ struct vmw_private {
spinlock_t hw_lock;
spinlock_t cap_lock;
bool has_dx;
+ bool assume_16bpp;
/*
* VGA registers.
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index 5da5de0cb522..4948c1529836 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -3273,19 +3273,19 @@ static const struct vmw_cmd_entry vmw_cmd_entries[SVGA_3D_CMD_MAX] = {
&vmw_cmd_dx_cid_check, true, false, true),
VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_QUERY, &vmw_cmd_dx_define_query,
true, false, true),
- VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_QUERY, &vmw_cmd_ok,
+ VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_QUERY, &vmw_cmd_dx_cid_check,
true, false, true),
VMW_CMD_DEF(SVGA_3D_CMD_DX_BIND_QUERY, &vmw_cmd_dx_bind_query,
true, false, true),
VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_QUERY_OFFSET,
- &vmw_cmd_ok, true, false, true),
- VMW_CMD_DEF(SVGA_3D_CMD_DX_BEGIN_QUERY, &vmw_cmd_ok,
+ &vmw_cmd_dx_cid_check, true, false, true),
+ VMW_CMD_DEF(SVGA_3D_CMD_DX_BEGIN_QUERY, &vmw_cmd_dx_cid_check,
true, false, true),
- VMW_CMD_DEF(SVGA_3D_CMD_DX_END_QUERY, &vmw_cmd_ok,
+ VMW_CMD_DEF(SVGA_3D_CMD_DX_END_QUERY, &vmw_cmd_dx_cid_check,
true, false, true),
VMW_CMD_DEF(SVGA_3D_CMD_DX_READBACK_QUERY, &vmw_cmd_invalid,
true, false, true),
- VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_PREDICATION, &vmw_cmd_invalid,
+ VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_PREDICATION, &vmw_cmd_dx_cid_check,
true, false, true),
VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_VIEWPORTS, &vmw_cmd_dx_cid_check,
true, false, true),
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index 3b1faf7862a5..d2d93959b119 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -517,28 +517,6 @@ static int vmw_fb_kms_framebuffer(struct fb_info *info)
par->set_fb = &vfb->base;
- if (!par->bo_ptr) {
- /*
- * Pin before mapping. Since we don't know in what placement
- * to pin, call into KMS to do it for us.
- */
- ret = vfb->pin(vfb);
- if (ret) {
- DRM_ERROR("Could not pin the fbdev framebuffer.\n");
- return ret;
- }
-
- ret = ttm_bo_kmap(&par->vmw_bo->base, 0,
- par->vmw_bo->base.num_pages, &par->map);
- if (ret) {
- vfb->unpin(vfb);
- DRM_ERROR("Could not map the fbdev framebuffer.\n");
- return ret;
- }
-
- par->bo_ptr = ttm_kmap_obj_virtual(&par->map, &par->bo_iowrite);
- }
-
return 0;
}
@@ -573,9 +551,9 @@ static int vmw_fb_set_par(struct fb_info *info)
mode = old_mode;
old_mode = NULL;
} else if (!vmw_kms_validate_mode_vram(vmw_priv,
- mode->hdisplay *
- (var->bits_per_pixel + 7) / 8,
- mode->vdisplay)) {
+ mode->hdisplay *
+ DIV_ROUND_UP(var->bits_per_pixel, 8),
+ mode->vdisplay)) {
drm_mode_destroy(vmw_priv->dev, mode);
return -EINVAL;
}
@@ -601,6 +579,31 @@ static int vmw_fb_set_par(struct fb_info *info)
if (ret)
goto out_unlock;
+ if (!par->bo_ptr) {
+ struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(set.fb);
+
+ /*
+ * Pin before mapping. Since we don't know in what placement
+ * to pin, call into KMS to do it for us.
+ */
+ ret = vfb->pin(vfb);
+ if (ret) {
+ DRM_ERROR("Could not pin the fbdev framebuffer.\n");
+ goto out_unlock;
+ }
+
+ ret = ttm_bo_kmap(&par->vmw_bo->base, 0,
+ par->vmw_bo->base.num_pages, &par->map);
+ if (ret) {
+ vfb->unpin(vfb);
+ DRM_ERROR("Could not map the fbdev framebuffer.\n");
+ goto out_unlock;
+ }
+
+ par->bo_ptr = ttm_kmap_obj_virtual(&par->map, &par->bo_iowrite);
+ }
+
+
vmw_fb_dirty_mark(par, par->fb_x, par->fb_y,
par->set_fb->width, par->set_fb->height);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index 7c2e118a77b0..060e5c6f4446 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -1538,14 +1538,10 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC)
};
int i;
- u32 assumed_bpp = 2;
+ u32 assumed_bpp = 4;
- /*
- * If using screen objects, then assume 32-bpp because that's what the
- * SVGA device is assuming
- */
- if (dev_priv->active_display_unit == vmw_du_screen_object)
- assumed_bpp = 4;
+ if (dev_priv->assume_16bpp)
+ assumed_bpp = 2;
if (dev_priv->active_display_unit == vmw_du_screen_target) {
max_width = min(max_width, dev_priv->stdu_max_width);
diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index a0e28f3a278d..5030cba4a581 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -997,7 +997,7 @@ struct ipu_platform_reg {
};
/* These must be in the order of the corresponding device tree port nodes */
-static const struct ipu_platform_reg client_reg[] = {
+static struct ipu_platform_reg client_reg[] = {
{
.pdata = {
.csi = 0,
@@ -1048,7 +1048,7 @@ static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
mutex_unlock(&ipu_client_id_mutex);
for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
- const struct ipu_platform_reg *reg = &client_reg[i];
+ struct ipu_platform_reg *reg = &client_reg[i];
struct platform_device *pdev;
struct device_node *of_node;
@@ -1068,9 +1068,9 @@ static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
goto err_register;
}
- pdev->dev.of_node = of_node;
pdev->dev.parent = dev;
+ reg->pdata.of_node = of_node;
ret = platform_device_add_data(pdev, &reg->pdata,
sizeof(reg->pdata));
if (!ret)
@@ -1079,6 +1079,12 @@ static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
platform_device_put(pdev);
goto err_register;
}
+
+ /*
+ * Set of_node only after calling platform_device_add. Otherwise
+ * the platform:imx-ipuv3-crtc modalias won't be used.
+ */
+ pdev->dev.of_node = of_node;
}
return 0;
diff --git a/drivers/gpu/msm/a5xx_reg.h b/drivers/gpu/msm/a5xx_reg.h
index f3b4e6622043..436b6949c414 100644
--- a/drivers/gpu/msm/a5xx_reg.h
+++ b/drivers/gpu/msm/a5xx_reg.h
@@ -608,6 +608,7 @@
#define A5XX_PC_PERFCTR_PC_SEL_7 0xD17
/* HLSQ registers */
+#define A5XX_HLSQ_DBG_ECO_CNTL 0xE04
#define A5XX_HLSQ_ADDR_MODE_CNTL 0xE05
#define A5XX_HLSQ_PERFCTR_HLSQ_SEL_0 0xE10
#define A5XX_HLSQ_PERFCTR_HLSQ_SEL_1 0xE11
@@ -632,6 +633,7 @@
#define A5XX_VFD_PERFCTR_VFD_SEL_7 0xE57
/* VPC registers */
+#define A5XX_VPC_DBG_ECO_CNTL 0xE60
#define A5XX_VPC_ADDR_MODE_CNTL 0xE61
#define A5XX_VPC_PERFCTR_VPC_SEL_0 0xE64
#define A5XX_VPC_PERFCTR_VPC_SEL_1 0xE65
diff --git a/drivers/gpu/msm/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h
index 3615be45b6d9..a02ed40ba9d5 100644
--- a/drivers/gpu/msm/adreno-gpulist.h
+++ b/drivers/gpu/msm/adreno-gpulist.h
@@ -269,7 +269,7 @@ static const struct adreno_gpu_core adreno_gpulist[] = {
.patchid = ANY_ID,
.features = ADRENO_PREEMPTION | ADRENO_64BIT |
ADRENO_CONTENT_PROTECTION |
- ADRENO_GPMU | ADRENO_SPTP_PC,
+ ADRENO_GPMU | ADRENO_SPTP_PC | ADRENO_LM,
.pm4fw_name = "a530_pm4.fw",
.pfpfw_name = "a530_pfp.fw",
.zap_name = "a540_zap",
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 9940f7a7c2b7..11226472d801 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -589,11 +589,21 @@ static irqreturn_t adreno_irq_handler(struct kgsl_device *device)
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
struct adreno_irq *irq_params = gpudev->irq;
irqreturn_t ret = IRQ_NONE;
- unsigned int status = 0, tmp;
+ unsigned int status = 0, tmp, int_bit;
int i;
adreno_readreg(adreno_dev, ADRENO_REG_RBBM_INT_0_STATUS, &status);
+ /*
+ * Clear all the interrupt bits but ADRENO_INT_RBBM_AHB_ERROR. Because
+ * even if we clear it here, it will stay high until it is cleared
+ * in its respective handler. Otherwise, the interrupt handler will
+ * fire again.
+ */
+ int_bit = ADRENO_INT_BIT(adreno_dev, ADRENO_INT_RBBM_AHB_ERROR);
+ adreno_writereg(adreno_dev, ADRENO_REG_RBBM_INT_CLEAR_CMD,
+ status & ~int_bit);
+
/* Loop through all set interrupts and call respective handlers */
for (tmp = status; tmp != 0;) {
i = fls(tmp) - 1;
@@ -612,9 +622,14 @@ static irqreturn_t adreno_irq_handler(struct kgsl_device *device)
gpudev->irq_trace(adreno_dev, status);
- if (status)
+ /*
+ * Clear ADRENO_INT_RBBM_AHB_ERROR bit after this interrupt has been
+ * cleared in its respective handler
+ */
+ if (status & int_bit)
adreno_writereg(adreno_dev, ADRENO_REG_RBBM_INT_CLEAR_CMD,
- status);
+ int_bit);
+
return ret;
}
@@ -828,6 +843,8 @@ static struct {
{ ADRENO_QUIRK_FAULT_DETECT_MASK, "qcom,gpu-quirk-fault-detect-mask" },
{ ADRENO_QUIRK_DISABLE_RB_DP2CLOCKGATING,
"qcom,gpu-quirk-dp2clockgating-disable" },
+ { ADRENO_QUIRK_DISABLE_LMLOADKILL,
+ "qcom,gpu-quirk-lmloadkill-disable" },
};
static int adreno_of_get_power(struct adreno_device *adreno_dev,
@@ -2094,8 +2111,6 @@ static int adreno_soft_reset(struct kgsl_device *device)
adreno_support_64bit(adreno_dev))
gpudev->enable_64bit(adreno_dev);
- /* Restore physical performance counter values after soft reset */
- adreno_perfcounter_restore(adreno_dev);
/* Reinitialize the GPU */
gpudev->start(adreno_dev);
@@ -2122,6 +2137,9 @@ static int adreno_soft_reset(struct kgsl_device *device)
set_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv);
}
+ /* Restore physical performance counter values after soft reset */
+ adreno_perfcounter_restore(adreno_dev);
+
return ret;
}
@@ -2276,9 +2294,9 @@ static void adreno_read(struct kgsl_device *device, void __iomem *base,
unsigned int mem_len)
{
- unsigned int __iomem *reg;
+ void __iomem *reg;
BUG_ON(offsetwords*sizeof(uint32_t) >= mem_len);
- reg = (unsigned int __iomem *)(base + (offsetwords << 2));
+ reg = (base + (offsetwords << 2));
if (!in_interrupt())
kgsl_pre_hwaccess(device);
@@ -2318,7 +2336,7 @@ static void adreno_regwrite(struct kgsl_device *device,
unsigned int offsetwords,
unsigned int value)
{
- unsigned int __iomem *reg;
+ void __iomem *reg;
BUG_ON(offsetwords*sizeof(uint32_t) >= device->reg_len);
@@ -2328,7 +2346,7 @@ static void adreno_regwrite(struct kgsl_device *device,
trace_kgsl_regwrite(device, offsetwords, value);
kgsl_cffdump_regwrite(device, offsetwords << 2, value);
- reg = (unsigned int __iomem *)(device->reg_virt + (offsetwords << 2));
+ reg = (device->reg_virt + (offsetwords << 2));
/*ensure previous writes post before this one,
* i.e. act like normal writel() */
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 0f3403cb0095..d81142db5b58 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -123,6 +123,8 @@
#define ADRENO_QUIRK_FAULT_DETECT_MASK BIT(3)
/* Disable RB sampler datapath clock gating optimization */
#define ADRENO_QUIRK_DISABLE_RB_DP2CLOCKGATING BIT(4)
+/* Disable local memory(LM) feature to avoid corner case error */
+#define ADRENO_QUIRK_DISABLE_LMLOADKILL BIT(5)
/* Flags to control command packet settings */
#define KGSL_CMD_FLAGS_NONE 0
@@ -198,6 +200,10 @@ struct adreno_gpudev;
/* Time to allow preemption to complete (in ms) */
#define ADRENO_PREEMPT_TIMEOUT 10000
+#define ADRENO_INT_BIT(a, _bit) (((a)->gpucore->gpudev->int_bits) ? \
+ (adreno_get_int(a, _bit) < 0 ? 0 : \
+ BIT(adreno_get_int(a, _bit))) : 0)
+
/**
* enum adreno_preempt_states
* ADRENO_PREEMPT_NONE: No preemption is scheduled
@@ -574,6 +580,11 @@ enum adreno_regs {
ADRENO_REG_REGISTER_MAX,
};
+enum adreno_int_bits {
+ ADRENO_INT_RBBM_AHB_ERROR,
+ ADRENO_INT_BITS_MAX,
+};
+
/**
* adreno_reg_offsets: Holds array of register offsets
* @offsets: Offset array of size defined by enum adreno_regs
@@ -589,6 +600,7 @@ struct adreno_reg_offsets {
#define ADRENO_REG_UNUSED 0xFFFFFFFF
#define ADRENO_REG_SKIP 0xFFFFFFFE
#define ADRENO_REG_DEFINE(_offset, _reg) [_offset] = _reg
+#define ADRENO_INT_DEFINE(_offset, _val) ADRENO_REG_DEFINE(_offset, _val)
/*
* struct adreno_vbif_data - Describes vbif register value pair
@@ -726,6 +738,7 @@ struct adreno_gpudev {
* so define them in the structure and use them as variables.
*/
const struct adreno_reg_offsets *reg_offsets;
+ unsigned int *const int_bits;
const struct adreno_ft_perf_counters *ft_perf_counters;
unsigned int ft_perf_counters_count;
@@ -1101,6 +1114,23 @@ static inline unsigned int adreno_getreg(struct adreno_device *adreno_dev,
return gpudev->reg_offsets->offsets[offset_name];
}
+/*
+ * adreno_get_int() - Returns the offset value of an interrupt bit from
+ * the interrupt bit array in the gpudev node
+ * @adreno_dev: Pointer to the the adreno device
+ * @bit_name: The interrupt bit enum whose bit is returned
+ */
+static inline unsigned int adreno_get_int(struct adreno_device *adreno_dev,
+ enum adreno_int_bits bit_name)
+{
+ struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
+
+ if (bit_name >= ADRENO_INT_BITS_MAX)
+ return -ERANGE;
+
+ return gpudev->int_bits[bit_name];
+}
+
/**
* adreno_gpu_fault() - Return the current state of the GPU
* @adreno_dev: A pointer to the adreno_device to query
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 97e71464c2df..423071811b43 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -151,6 +151,43 @@ static const unsigned int _a3xx_pwron_fixup_fs_instructions[] = {
0x00000000, 0x03000000, 0x00000000, 0x00000000,
};
+static void a3xx_efuse_speed_bin(struct adreno_device *adreno_dev)
+{
+ unsigned int val;
+ unsigned int speed_bin[3];
+ struct kgsl_device *device = &adreno_dev->dev;
+
+ if (of_property_read_u32_array(device->pdev->dev.of_node,
+ "qcom,gpu-speed-bin", speed_bin, 3))
+ return;
+
+ adreno_efuse_read_u32(adreno_dev, speed_bin[0], &val);
+
+ adreno_dev->speed_bin = (val & speed_bin[1]) >> speed_bin[2];
+}
+
+static const struct {
+ int (*check)(struct adreno_device *adreno_dev);
+ void (*func)(struct adreno_device *adreno_dev);
+} a3xx_efuse_funcs[] = {
+ { adreno_is_a306a, a3xx_efuse_speed_bin },
+};
+
+static void a3xx_check_features(struct adreno_device *adreno_dev)
+{
+ unsigned int i;
+
+ if (adreno_efuse_map(adreno_dev))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(a3xx_efuse_funcs); i++) {
+ if (a3xx_efuse_funcs[i].check(adreno_dev))
+ a3xx_efuse_funcs[i].func(adreno_dev);
+ }
+
+ adreno_efuse_unmap(adreno_dev);
+}
+
/**
* _a3xx_pwron_fixup() - Initialize a special command buffer to run a
* post-power collapse shader workaround
@@ -604,6 +641,9 @@ static void a3xx_platform_setup(struct adreno_device *adreno_dev)
gpudev->vbif_xin_halt_ctrl0_mask =
A30X_VBIF_XIN_HALT_CTRL0_MASK;
}
+
+ /* Check efuse bits for various capabilties */
+ a3xx_check_features(adreno_dev);
}
static int a3xx_send_me_init(struct adreno_device *adreno_dev,
@@ -1425,6 +1465,10 @@ static struct adreno_coresight a3xx_coresight = {
.groups = a3xx_coresight_groups,
};
+static unsigned int a3xx_int_bits[ADRENO_INT_BITS_MAX] = {
+ ADRENO_INT_DEFINE(ADRENO_INT_RBBM_AHB_ERROR, A3XX_INT_RBBM_AHB_ERROR),
+};
+
/* Register offset defines for A3XX */
static unsigned int a3xx_register_offsets[ADRENO_REG_REGISTER_MAX] = {
ADRENO_REG_DEFINE(ADRENO_REG_CP_ME_RAM_WADDR, A3XX_CP_ME_RAM_WADDR),
@@ -1853,6 +1897,7 @@ int a3xx_microcode_load(struct adreno_device *adreno_dev,
struct adreno_gpudev adreno_a3xx_gpudev = {
.reg_offsets = &a3xx_reg_offsets,
+ .int_bits = a3xx_int_bits,
.ft_perf_counters = a3xx_ft_perf_counters,
.ft_perf_counters_count = ARRAY_SIZE(a3xx_ft_perf_counters),
.perfcounters = &a3xx_perfcounters,
diff --git a/drivers/gpu/msm/adreno_a4xx.c b/drivers/gpu/msm/adreno_a4xx.c
index bfbdb0e7ac1f..5ca04e522270 100644
--- a/drivers/gpu/msm/adreno_a4xx.c
+++ b/drivers/gpu/msm/adreno_a4xx.c
@@ -739,6 +739,10 @@ static void a4xx_err_callback(struct adreno_device *adreno_dev, int bit)
}
}
+static unsigned int a4xx_int_bits[ADRENO_INT_BITS_MAX] = {
+ ADRENO_INT_DEFINE(ADRENO_INT_RBBM_AHB_ERROR, A4XX_INT_RBBM_AHB_ERROR),
+};
+
/* Register offset defines for A4XX, in order of enum adreno_regs */
static unsigned int a4xx_register_offsets[ADRENO_REG_REGISTER_MAX] = {
ADRENO_REG_DEFINE(ADRENO_REG_CP_ME_RAM_WADDR, A4XX_CP_ME_RAM_WADDR),
@@ -1765,6 +1769,7 @@ static struct adreno_snapshot_data a4xx_snapshot_data = {
struct adreno_gpudev adreno_a4xx_gpudev = {
.reg_offsets = &a4xx_reg_offsets,
+ .int_bits = a4xx_int_bits,
.ft_perf_counters = a4xx_ft_perf_counters,
.ft_perf_counters_count = ARRAY_SIZE(a4xx_ft_perf_counters),
.perfcounters = &a4xx_perfcounters,
diff --git a/drivers/gpu/msm/adreno_a4xx_preempt.c b/drivers/gpu/msm/adreno_a4xx_preempt.c
index 4087ac60c89e..ef837dc4b7ea 100644
--- a/drivers/gpu/msm/adreno_a4xx_preempt.c
+++ b/drivers/gpu/msm/adreno_a4xx_preempt.c
@@ -146,6 +146,8 @@ static int a4xx_submit_preempt_token(struct adreno_ringbuffer *rb,
&ptname, PT_INFO_OFFSET(current_rb_ptname));
pt = kgsl_mmu_get_pt_from_ptname(&(device->mmu),
ptname);
+ if (IS_ERR_OR_NULL(pt))
+ return (pt == NULL) ? -ENOENT : PTR_ERR(pt);
/* set the ringbuffer for incoming RB */
pt_switch_sizedwords =
adreno_iommu_set_pt_generate_cmds(incoming_rb,
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index 2891940b8f5b..f652e955b07c 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -196,6 +196,8 @@ static void a5xx_platform_setup(struct adreno_device *adreno_dev)
/* A510 has 3 XIN ports in VBIF */
gpudev->vbif_xin_halt_ctrl0_mask =
A510_VBIF_XIN_HALT_CTRL0_MASK;
+ } else if (adreno_is_a540(adreno_dev)) {
+ gpudev->snapshot_data->sect_sizes->cp_merciu = 1024;
}
/* Calculate SP local and private mem addresses */
@@ -1534,12 +1536,12 @@ static void a5xx_clk_set_options(struct adreno_device *adreno_dev,
const char *name, struct clk *clk)
{
if (adreno_is_a540(adreno_dev)) {
- if (!strcmp(name, "mem_iface_clk"))
- clk_set_flags(clk, CLKFLAG_NORETAIN_PERIPH);
- clk_set_flags(clk, CLKFLAG_NORETAIN_MEM);
- if (!strcmp(name, "core_clk")) {
+ if (!strcmp(name, "mem_iface_clk")) {
clk_set_flags(clk, CLKFLAG_NORETAIN_PERIPH);
clk_set_flags(clk, CLKFLAG_NORETAIN_MEM);
+ } else if (!strcmp(name, "core_clk")) {
+ clk_set_flags(clk, CLKFLAG_RETAIN_PERIPH);
+ clk_set_flags(clk, CLKFLAG_RETAIN_MEM);
}
}
}
@@ -1781,11 +1783,11 @@ static void a5xx_start(struct adreno_device *adreno_dev)
set_bit(ADRENO_DEVICE_HANG_INTR, &adreno_dev->priv);
gpudev->irq->mask |= (1 << A5XX_INT_MISC_HANG_DETECT);
/*
- * Set hang detection threshold to 1 million cycles
- * (0xFFFF*16)
+ * Set hang detection threshold to 4 million cycles
+ * (0x3FFFF*16)
*/
kgsl_regwrite(device, A5XX_RBBM_INTERFACE_HANG_INT_CNTL,
- (1 << 30) | 0xFFFF);
+ (1 << 30) | 0x3FFFF);
}
@@ -1944,6 +1946,16 @@ static void a5xx_start(struct adreno_device *adreno_dev)
}
+ /*
+ * VPC corner case with local memory load kill leads to corrupt
+ * internal state. Normal Disable does not work for all a5x chips.
+ * So do the following setting to disable it.
+ */
+ if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_DISABLE_LMLOADKILL)) {
+ kgsl_regrmw(device, A5XX_VPC_DBG_ECO_CNTL, 0, 0x1 << 23);
+ kgsl_regrmw(device, A5XX_HLSQ_DBG_ECO_CNTL, 0x1 << 18, 0);
+ }
+
a5xx_preemption_start(adreno_dev);
a5xx_protect_init(adreno_dev);
}
@@ -2872,6 +2884,10 @@ static struct adreno_ft_perf_counters a5xx_ft_perf_counters[] = {
{KGSL_PERFCOUNTER_GROUP_TSE, A5XX_TSE_INPUT_PRIM_NUM},
};
+static unsigned int a5xx_int_bits[ADRENO_INT_BITS_MAX] = {
+ ADRENO_INT_DEFINE(ADRENO_INT_RBBM_AHB_ERROR, A5XX_INT_RBBM_AHB_ERROR),
+};
+
/* Register offset defines for A5XX, in order of enum adreno_regs */
static unsigned int a5xx_register_offsets[ADRENO_REG_REGISTER_MAX] = {
ADRENO_REG_DEFINE(ADRENO_REG_CP_WFI_PEND_CTR, A5XX_CP_WFI_PEND_CTR),
@@ -3504,6 +3520,7 @@ static struct adreno_coresight a5xx_coresight = {
struct adreno_gpudev adreno_a5xx_gpudev = {
.reg_offsets = &a5xx_reg_offsets,
+ .int_bits = a5xx_int_bits,
.ft_perf_counters = a5xx_ft_perf_counters,
.ft_perf_counters_count = ARRAY_SIZE(a5xx_ft_perf_counters),
.coresight = &a5xx_coresight,
diff --git a/drivers/gpu/msm/adreno_perfcounter.c b/drivers/gpu/msm/adreno_perfcounter.c
index 42f8119ad8b4..f5f99c3ebb4a 100644
--- a/drivers/gpu/msm/adreno_perfcounter.c
+++ b/drivers/gpu/msm/adreno_perfcounter.c
@@ -522,12 +522,18 @@ int adreno_perfcounter_get(struct adreno_device *adreno_dev,
if (empty == -1)
return -EBUSY;
+ /* initialize the new counter */
+ group->regs[empty].countable = countable;
+
/* enable the new counter */
ret = adreno_perfcounter_enable(adreno_dev, groupid, empty, countable);
- if (ret)
+ if (ret) {
+ /* Put back the perfcounter */
+ if (!(group->flags & ADRENO_PERFCOUNTER_GROUP_FIXED))
+ group->regs[empty].countable =
+ KGSL_PERFCOUNTER_NOT_USED;
return ret;
- /* initialize the new counter */
- group->regs[empty].countable = countable;
+ }
/* set initial kernel and user count */
if (flags & PERFCOUNTER_FLAG_KERNEL) {
@@ -720,10 +726,22 @@ static int _perfcounter_enable_default(struct adreno_device *adreno_dev,
/* wait for the above commands submitted to complete */
ret = adreno_ringbuffer_waittimestamp(rb, rb->timestamp,
ADRENO_IDLE_TIMEOUT);
- if (ret)
- KGSL_DRV_ERR(device,
- "Perfcounter %u/%u/%u start via commands failed %d\n",
- group, counter, countable, ret);
+ if (ret) {
+ /*
+ * If we were woken up because of cancelling rb events
+ * either due to soft reset or adreno_stop, ignore the
+ * error and return 0 here. The perfcounter is already
+ * set up in software and it will be programmed in
+ * hardware when we wake up or come up after soft reset,
+ * by adreno_perfcounter_restore.
+ */
+ if (ret == -EAGAIN)
+ ret = 0;
+ else
+ KGSL_DRV_ERR(device,
+ "Perfcounter %u/%u/%u start via commands failed %d\n",
+ group, counter, countable, ret);
+ }
} else {
/* Select the desired perfcounter */
kgsl_regwrite(device, reg->select, countable);
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index add4590bbb90..554eb2dffae4 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -2507,6 +2507,8 @@ static int kgsl_setup_dma_buf(struct kgsl_device *device,
meta->dmabuf = dmabuf;
meta->attach = attach;
+ attach->priv = entry;
+
entry->priv_data = meta;
entry->memdesc.pagetable = pagetable;
entry->memdesc.size = 0;
@@ -2557,6 +2559,45 @@ out:
}
#endif
+#ifdef CONFIG_DMA_SHARED_BUFFER
+void kgsl_get_egl_counts(struct kgsl_mem_entry *entry,
+ int *egl_surface_count, int *egl_image_count)
+{
+ struct kgsl_dma_buf_meta *meta = entry->priv_data;
+ struct dma_buf *dmabuf = meta->dmabuf;
+ struct dma_buf_attachment *mem_entry_buf_attachment = meta->attach;
+ struct device *buf_attachment_dev = mem_entry_buf_attachment->dev;
+ struct dma_buf_attachment *attachment = NULL;
+
+ mutex_lock(&dmabuf->lock);
+ list_for_each_entry(attachment, &dmabuf->attachments, node) {
+ struct kgsl_mem_entry *scan_mem_entry = NULL;
+
+ if (attachment->dev != buf_attachment_dev)
+ continue;
+
+ scan_mem_entry = attachment->priv;
+ if (!scan_mem_entry)
+ continue;
+
+ switch (kgsl_memdesc_get_memtype(&scan_mem_entry->memdesc)) {
+ case KGSL_MEMTYPE_EGL_SURFACE:
+ (*egl_surface_count)++;
+ break;
+ case KGSL_MEMTYPE_EGL_IMAGE:
+ (*egl_image_count)++;
+ break;
+ }
+ }
+ mutex_unlock(&dmabuf->lock);
+}
+#else
+void kgsl_get_egl_counts(struct kgsl_mem_entry *entry,
+ int *egl_surface_count, int *egl_image_count)
+{
+}
+#endif
+
long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
@@ -3891,7 +3932,7 @@ kgsl_mmap_memstore(struct kgsl_device *device, struct vm_area_struct *vma)
return -EINVAL;
}
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
result = remap_pfn_range(vma, vma->vm_start,
device->memstore.physaddr >> PAGE_SHIFT,
@@ -4527,6 +4568,9 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
if (status)
goto error_close_mmu;
+ /* Initialize the memory pools */
+ kgsl_init_page_pools(device->pdev);
+
status = kgsl_allocate_global(device, &device->memstore,
KGSL_MEMSTORE_SIZE, 0, KGSL_MEMDESC_CONTIG, "memstore");
@@ -4581,9 +4625,6 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
/* Initialize common sysfs entries */
kgsl_pwrctrl_init_sysfs(device);
- /* Initialize the memory pools */
- kgsl_init_page_pools();
-
return 0;
error_free_memstore:
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index 826c4edb3582..fbf9197b6d1b 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -435,6 +435,9 @@ long kgsl_ioctl_sparse_unbind(struct kgsl_device_private *dev_priv,
void kgsl_mem_entry_destroy(struct kref *kref);
+void kgsl_get_egl_counts(struct kgsl_mem_entry *entry,
+ int *egl_surface_count, int *egl_image_count);
+
struct kgsl_mem_entry * __must_check
kgsl_sharedmem_find(struct kgsl_process_private *private, uint64_t gpuaddr);
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index 2f293e4da398..7758fc956055 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -125,13 +125,15 @@ static char get_cacheflag(const struct kgsl_memdesc *m)
}
-static int print_mem_entry(int id, void *ptr, void *data)
+static int print_mem_entry(void *data, void *ptr)
{
struct seq_file *s = data;
struct kgsl_mem_entry *entry = ptr;
char flags[10];
char usage[16];
struct kgsl_memdesc *m = &entry->memdesc;
+ unsigned int usermem_type = kgsl_memdesc_usermem_type(m);
+ int egl_surface_count = 0, egl_image_count = 0;
if (m->flags & KGSL_MEMFLAGS_SPARSE_VIRT)
return 0;
@@ -149,12 +151,17 @@ static int print_mem_entry(int id, void *ptr, void *data)
kgsl_get_memory_usage(usage, sizeof(usage), m->flags);
- seq_printf(s, "%pK %pK %16llu %5d %9s %10s %16s %5d %16llu",
+ if (usermem_type == KGSL_MEM_ENTRY_ION)
+ kgsl_get_egl_counts(entry, &egl_surface_count,
+ &egl_image_count);
+
+ seq_printf(s, "%pK %pK %16llu %5d %9s %10s %16s %5d %16llu %6d %6d",
(uint64_t *)(uintptr_t) m->gpuaddr,
(unsigned long *) m->useraddr,
m->size, entry->id, flags,
- memtype_str(kgsl_memdesc_usermem_type(m)),
- usage, (m->sgt ? m->sgt->nents : 0), m->mapsize);
+ memtype_str(usermem_type),
+ usage, (m->sgt ? m->sgt->nents : 0), m->mapsize,
+ egl_surface_count, egl_image_count);
if (entry->metadata[0] != 0)
seq_printf(s, " %s", entry->metadata);
@@ -164,25 +171,83 @@ static int print_mem_entry(int id, void *ptr, void *data)
return 0;
}
-static int process_mem_print(struct seq_file *s, void *unused)
+static struct kgsl_mem_entry *process_mem_seq_find(struct seq_file *s,
+ void *ptr, loff_t pos)
{
+ struct kgsl_mem_entry *entry = ptr;
struct kgsl_process_private *private = s->private;
+ int id = 0;
+ loff_t temp_pos = 1;
- seq_printf(s, "%16s %16s %16s %5s %9s %10s %16s %5s %16s\n",
- "gpuaddr", "useraddr", "size", "id", "flags", "type",
- "usage", "sglen", "mapsize");
+ if (entry != SEQ_START_TOKEN)
+ id = entry->id + 1;
spin_lock(&private->mem_lock);
- idr_for_each(&private->mem_idr, print_mem_entry, s);
+ for (entry = idr_get_next(&private->mem_idr, &id); entry;
+ id++, entry = idr_get_next(&private->mem_idr, &id),
+ temp_pos++) {
+ if (temp_pos == pos && kgsl_mem_entry_get(entry)) {
+ spin_unlock(&private->mem_lock);
+ goto found;
+ }
+ }
spin_unlock(&private->mem_lock);
- return 0;
+ entry = NULL;
+found:
+ if (ptr != SEQ_START_TOKEN)
+ kgsl_mem_entry_put(ptr);
+
+ return entry;
+}
+
+static void *process_mem_seq_start(struct seq_file *s, loff_t *pos)
+{
+ loff_t seq_file_offset = *pos;
+
+ if (seq_file_offset == 0)
+ return SEQ_START_TOKEN;
+ else
+ return process_mem_seq_find(s, SEQ_START_TOKEN,
+ seq_file_offset);
+}
+
+static void process_mem_seq_stop(struct seq_file *s, void *ptr)
+{
+ if (ptr && ptr != SEQ_START_TOKEN)
+ kgsl_mem_entry_put(ptr);
}
+static void *process_mem_seq_next(struct seq_file *s, void *ptr,
+ loff_t *pos)
+{
+ ++*pos;
+ return process_mem_seq_find(s, ptr, 1);
+}
+
+static int process_mem_seq_show(struct seq_file *s, void *ptr)
+{
+ if (ptr == SEQ_START_TOKEN) {
+ seq_printf(s, "%16s %16s %16s %5s %9s %10s %16s %5s %16s %6s %6s\n",
+ "gpuaddr", "useraddr", "size", "id", "flags", "type",
+ "usage", "sglen", "mapsize", "eglsrf", "eglimg");
+ return 0;
+ } else
+ return print_mem_entry(s, ptr);
+}
+
+static const struct seq_operations process_mem_seq_fops = {
+ .start = process_mem_seq_start,
+ .stop = process_mem_seq_stop,
+ .next = process_mem_seq_next,
+ .show = process_mem_seq_show,
+};
+
static int process_mem_open(struct inode *inode, struct file *file)
{
int ret;
pid_t pid = (pid_t) (unsigned long) inode->i_private;
+ struct seq_file *s = NULL;
struct kgsl_process_private *private = NULL;
private = kgsl_process_private_find(pid);
@@ -190,9 +255,13 @@ static int process_mem_open(struct inode *inode, struct file *file)
if (!private)
return -ENODEV;
- ret = single_open(file, process_mem_print, private);
+ ret = seq_open(file, &process_mem_seq_fops);
if (ret)
kgsl_process_private_put(private);
+ else {
+ s = file->private_data;
+ s->private = private;
+ }
return ret;
}
@@ -205,7 +274,7 @@ static int process_mem_release(struct inode *inode, struct file *file)
if (private)
kgsl_process_private_put(private);
- return single_release(inode, file);
+ return seq_release(inode, file);
}
static const struct file_operations process_mem_fops = {
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index ba564b2851f9..f516b7cd245a 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -390,6 +390,13 @@ kgsl_mmu_map(struct kgsl_pagetable *pagetable,
if (!memdesc->gpuaddr)
return -EINVAL;
+ if (!(memdesc->flags & (KGSL_MEMFLAGS_SPARSE_VIRT |
+ KGSL_MEMFLAGS_SPARSE_PHYS))) {
+ /* Only global mappings should be mapped multiple times */
+ if (!kgsl_memdesc_is_global(memdesc) &&
+ (KGSL_MEMDESC_MAPPED & memdesc->priv))
+ return -EINVAL;
+ }
size = kgsl_memdesc_footprint(memdesc);
@@ -403,6 +410,9 @@ kgsl_mmu_map(struct kgsl_pagetable *pagetable,
atomic_inc(&pagetable->stats.entries);
KGSL_STATS_ADD(size, &pagetable->stats.mapped,
&pagetable->stats.max_mapped);
+
+ /* This is needed for non-sparse mappings */
+ memdesc->priv |= KGSL_MEMDESC_MAPPED;
}
return 0;
@@ -455,6 +465,13 @@ kgsl_mmu_unmap(struct kgsl_pagetable *pagetable,
if (memdesc->size == 0)
return -EINVAL;
+ if (!(memdesc->flags & (KGSL_MEMFLAGS_SPARSE_VIRT |
+ KGSL_MEMFLAGS_SPARSE_PHYS))) {
+ /* Only global mappings should be mapped multiple times */
+ if (!(KGSL_MEMDESC_MAPPED & memdesc->priv))
+ return -EINVAL;
+ }
+
if (PT_OP_VALID(pagetable, mmu_unmap)) {
uint64_t size;
@@ -464,6 +481,9 @@ kgsl_mmu_unmap(struct kgsl_pagetable *pagetable,
atomic_dec(&pagetable->stats.entries);
atomic_long_sub(size, &pagetable->stats.mapped);
+
+ if (!kgsl_memdesc_is_global(memdesc))
+ memdesc->priv &= ~KGSL_MEMDESC_MAPPED;
}
return ret;
diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c
index f5402fdc7e57..6ecbab466c7c 100644
--- a/drivers/gpu/msm/kgsl_pool.c
+++ b/drivers/gpu/msm/kgsl_pool.c
@@ -21,6 +21,10 @@
#include "kgsl_device.h"
#include "kgsl_pool.h"
+#define KGSL_MAX_POOLS 4
+#define KGSL_MAX_POOL_ORDER 8
+#define KGSL_MAX_RESERVED_PAGES 4096
+
/**
* struct kgsl_page_pool - Structure to hold information for the pool
* @pool_order: Page order describing the size of the page
@@ -40,41 +44,10 @@ struct kgsl_page_pool {
struct list_head page_list;
};
-static struct kgsl_page_pool kgsl_pools[] = {
- {
- .pool_order = 0,
- .reserved_pages = 2048,
- .allocation_allowed = true,
- .list_lock = __SPIN_LOCK_UNLOCKED(kgsl_pools[0].list_lock),
- .page_list = LIST_HEAD_INIT(kgsl_pools[0].page_list),
- },
-#ifndef CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS
- {
- .pool_order = 1,
- .reserved_pages = 1024,
- .allocation_allowed = true,
- .list_lock = __SPIN_LOCK_UNLOCKED(kgsl_pools[1].list_lock),
- .page_list = LIST_HEAD_INIT(kgsl_pools[1].page_list),
- },
- {
- .pool_order = 4,
- .reserved_pages = 256,
- .allocation_allowed = false,
- .list_lock = __SPIN_LOCK_UNLOCKED(kgsl_pools[2].list_lock),
- .page_list = LIST_HEAD_INIT(kgsl_pools[2].page_list),
- },
- {
- .pool_order = 8,
- .reserved_pages = 32,
- .allocation_allowed = false,
- .list_lock = __SPIN_LOCK_UNLOCKED(kgsl_pools[3].list_lock),
- .page_list = LIST_HEAD_INIT(kgsl_pools[3].page_list),
- },
+static struct kgsl_page_pool kgsl_pools[KGSL_MAX_POOLS];
+static int kgsl_num_pools;
+static int kgsl_pool_max_pages;
-#endif
-};
-
-#define KGSL_NUM_POOLS ARRAY_SIZE(kgsl_pools)
/* Returns KGSL pool corresponding to input page order*/
static struct kgsl_page_pool *
@@ -82,7 +55,7 @@ _kgsl_get_pool_from_order(unsigned int order)
{
int i;
- for (i = 0; i < KGSL_NUM_POOLS; i++) {
+ for (i = 0; i < kgsl_num_pools; i++) {
if (kgsl_pools[i].pool_order == order)
return &kgsl_pools[i];
}
@@ -154,7 +127,7 @@ static int kgsl_pool_size_total(void)
int i;
int total = 0;
- for (i = 0; i < KGSL_NUM_POOLS; i++)
+ for (i = 0; i < kgsl_num_pools; i++)
total += kgsl_pool_size(&kgsl_pools[i]);
return total;
}
@@ -207,7 +180,7 @@ kgsl_pool_reduce(unsigned int target_pages, bool exit)
total_pages = kgsl_pool_size_total();
- for (i = (KGSL_NUM_POOLS - 1); i >= 0; i--) {
+ for (i = (kgsl_num_pools - 1); i >= 0; i--) {
pool = &kgsl_pools[i];
/*
@@ -300,7 +273,7 @@ static int kgsl_pool_idx_lookup(unsigned int order)
{
int i;
- for (i = 0; i < KGSL_NUM_POOLS; i++)
+ for (i = 0; i < kgsl_num_pools; i++)
if (order == kgsl_pools[i].pool_order)
return i;
@@ -384,10 +357,13 @@ void kgsl_pool_free_page(struct page *page)
page_order = compound_order(page);
- pool = _kgsl_get_pool_from_order(page_order);
- if (pool != NULL) {
- _kgsl_pool_add_page(pool, page);
- return;
+ if (!kgsl_pool_max_pages ||
+ (kgsl_pool_size_total() < kgsl_pool_max_pages)) {
+ pool = _kgsl_get_pool_from_order(page_order);
+ if (pool != NULL) {
+ _kgsl_pool_add_page(pool, page);
+ return;
+ }
}
/* Give back to system as not added to pool */
@@ -398,7 +374,7 @@ static void kgsl_pool_reserve_pages(void)
{
int i, j;
- for (i = 0; i < KGSL_NUM_POOLS; i++) {
+ for (i = 0; i < kgsl_num_pools; i++) {
struct page *page;
for (j = 0; j < kgsl_pools[i].reserved_pages; j++) {
@@ -445,8 +421,76 @@ static struct shrinker kgsl_pool_shrinker = {
.batch = 0,
};
-void kgsl_init_page_pools(void)
+static void kgsl_pool_config(unsigned int order, unsigned int reserved_pages,
+ bool allocation_allowed)
{
+#ifdef CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS
+ if (order > 0) {
+ pr_info("%s: Pool order:%d not supprted.!!\n", __func__, order);
+ return;
+ }
+#endif
+ if ((order > KGSL_MAX_POOL_ORDER) ||
+ (reserved_pages > KGSL_MAX_RESERVED_PAGES))
+ return;
+
+ kgsl_pools[kgsl_num_pools].pool_order = order;
+ kgsl_pools[kgsl_num_pools].reserved_pages = reserved_pages;
+ kgsl_pools[kgsl_num_pools].allocation_allowed = allocation_allowed;
+ spin_lock_init(&kgsl_pools[kgsl_num_pools].list_lock);
+ INIT_LIST_HEAD(&kgsl_pools[kgsl_num_pools].page_list);
+ kgsl_num_pools++;
+}
+
+static void kgsl_of_parse_mempools(struct device_node *node)
+{
+ struct device_node *child;
+ unsigned int page_size, reserved_pages = 0;
+ bool allocation_allowed;
+
+ for_each_child_of_node(node, child) {
+ unsigned int index;
+
+ if (of_property_read_u32(child, "reg", &index))
+ return;
+
+ if (index >= KGSL_MAX_POOLS)
+ continue;
+
+ if (of_property_read_u32(child, "qcom,mempool-page-size",
+ &page_size))
+ return;
+
+ of_property_read_u32(child, "qcom,mempool-reserved",
+ &reserved_pages);
+
+ allocation_allowed = of_property_read_bool(child,
+ "qcom,mempool-allocate");
+
+ kgsl_pool_config(ilog2(page_size >> PAGE_SHIFT), reserved_pages,
+ allocation_allowed);
+ }
+}
+
+static void kgsl_of_get_mempools(struct device_node *parent)
+{
+ struct device_node *node;
+
+ node = of_find_compatible_node(parent, NULL, "qcom,gpu-mempools");
+ if (node != NULL) {
+ /* Get Max pages limit for mempool */
+ of_property_read_u32(node, "qcom,mempool-max-pages",
+ &kgsl_pool_max_pages);
+ kgsl_of_parse_mempools(node);
+ }
+}
+
+void kgsl_init_page_pools(struct platform_device *pdev)
+{
+
+ /* Get GPU mempools data and configure pools */
+ kgsl_of_get_mempools(pdev->dev.of_node);
+
/* Reserve the appropriate number of pages for each pool */
kgsl_pool_reserve_pages();
diff --git a/drivers/gpu/msm/kgsl_pool.h b/drivers/gpu/msm/kgsl_pool.h
index efbfa96f1498..d55e1ada123b 100644
--- a/drivers/gpu/msm/kgsl_pool.h
+++ b/drivers/gpu/msm/kgsl_pool.h
@@ -35,7 +35,7 @@ kgsl_gfp_mask(unsigned int page_order)
void kgsl_pool_free_sgt(struct sg_table *sgt);
void kgsl_pool_free_pages(struct page **pages, unsigned int page_count);
-void kgsl_init_page_pools(void);
+void kgsl_init_page_pools(struct platform_device *pdev);
void kgsl_exit_page_pools(void);
int kgsl_pool_alloc_page(int *page_size, struct page **pages,
unsigned int pages_len, unsigned int *align);
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index 72895c18119f..618e9e9a33a3 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -574,12 +574,11 @@ int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset,
void *addr = (memdesc->hostptr) ?
memdesc->hostptr : (void *) memdesc->useraddr;
- /* Make sure that size is non-zero */
- if (!size)
+ if (size == 0 || size > UINT_MAX)
return -EINVAL;
- /* Make sure that the offset + size isn't bigger than we can handle */
- if ((offset + size) > ULONG_MAX)
+ /* Make sure that the offset + size does not overflow */
+ if ((offset + size < offset) || (offset + size < size))
return -ERANGE;
/* Make sure the offset + size do not overflow the address */
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index a2e4a909062f..13dc3017072d 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -1067,6 +1067,9 @@ void kgsl_snapshot_save_frozen_objs(struct work_struct *work)
size_t size = 0;
void *ptr;
+ if (IS_ERR_OR_NULL(device))
+ return;
+
kgsl_snapshot_process_ib_obj_list(snapshot);
list_for_each_entry(obj, &snapshot->obj_list, node) {
diff --git a/drivers/hid/hid-elo.c b/drivers/hid/hid-elo.c
index aad8c162a825..0cd4f7216239 100644
--- a/drivers/hid/hid-elo.c
+++ b/drivers/hid/hid-elo.c
@@ -261,7 +261,7 @@ static void elo_remove(struct hid_device *hdev)
struct elo_priv *priv = hid_get_drvdata(hdev);
hid_hw_stop(hdev);
- flush_workqueue(wq);
+ cancel_delayed_work_sync(&priv->work);
kfree(priv);
}
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 8b78a7f1f779..909ab0176ef2 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -255,6 +255,7 @@
#define USB_DEVICE_ID_CORSAIR_K90 0x1b02
#define USB_VENDOR_ID_CREATIVELABS 0x041e
+#define USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51 0x322c
#define USB_DEVICE_ID_PRODIKEYS_PCMIDI 0x2801
#define USB_VENDOR_ID_CVTOUCH 0x1ff7
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 7ecd96bdf834..f62a9d6601cc 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -61,6 +61,7 @@ MODULE_LICENSE("GPL");
#define MT_QUIRK_ALWAYS_VALID (1 << 4)
#define MT_QUIRK_VALID_IS_INRANGE (1 << 5)
#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6)
+#define MT_QUIRK_CONFIDENCE (1 << 7)
#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8)
#define MT_QUIRK_NO_AREA (1 << 9)
#define MT_QUIRK_IGNORE_DUPLICATES (1 << 10)
@@ -78,6 +79,7 @@ struct mt_slot {
__s32 contactid; /* the device ContactID assigned to this slot */
bool touch_state; /* is the touch valid? */
bool inrange_state; /* is the finger in proximity of the sensor? */
+ bool confidence_state; /* is the touch made by a finger? */
};
struct mt_class {
@@ -450,16 +452,6 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)
td->buttons_count++;
- /* Only map fields from TouchScreen or TouchPad collections.
- * We need to ignore fields that belong to other collections
- * such as Mouse that might have the same GenericDesktop usages. */
- if (field->application == HID_DG_TOUCHSCREEN)
- set_bit(INPUT_PROP_DIRECT, hi->input->propbit);
- else if (field->application == HID_DG_TOUCHPAD)
- set_bit(INPUT_PROP_POINTER, hi->input->propbit);
- else
- return 0;
-
if (usage->usage_index)
prev_usage = &field->usage[usage->usage_index - 1];
@@ -512,6 +504,9 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
mt_store_field(usage, td, hi);
return 1;
case HID_DG_CONFIDENCE:
+ if (cls->name == MT_CLS_WIN_8 &&
+ field->application == HID_DG_TOUCHPAD)
+ cls->quirks |= MT_QUIRK_CONFIDENCE;
mt_store_field(usage, td, hi);
return 1;
case HID_DG_TIPSWITCH:
@@ -624,6 +619,7 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
return;
if (td->curvalid || (td->mtclass.quirks & MT_QUIRK_ALWAYS_VALID)) {
+ int active;
int slotnum = mt_compute_slot(td, input);
struct mt_slot *s = &td->curdata;
struct input_mt *mt = input->mt;
@@ -638,10 +634,14 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
return;
}
+ if (!(td->mtclass.quirks & MT_QUIRK_CONFIDENCE))
+ s->confidence_state = 1;
+ active = (s->touch_state || s->inrange_state) &&
+ s->confidence_state;
+
input_mt_slot(input, slotnum);
- input_mt_report_slot_state(input, MT_TOOL_FINGER,
- s->touch_state || s->inrange_state);
- if (s->touch_state || s->inrange_state) {
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, active);
+ if (active) {
/* this finger is in proximity of the sensor */
int wide = (s->w > s->h);
/* divided by two to match visual scale of touch */
@@ -706,6 +706,8 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field,
td->curdata.touch_state = value;
break;
case HID_DG_CONFIDENCE:
+ if (quirks & MT_QUIRK_CONFIDENCE)
+ td->curdata.confidence_state = value;
if (quirks & MT_QUIRK_VALID_IS_CONFIDENCE)
td->curvalid = value;
break;
diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c
index 93ddc1c65b4c..3edd4ac36494 100644
--- a/drivers/hid/hid-steelseries.c
+++ b/drivers/hid/hid-steelseries.c
@@ -253,11 +253,6 @@ static int steelseries_srws1_probe(struct hid_device *hdev,
goto err_free;
}
- if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 16)) {
- ret = -ENODEV;
- goto err_free;
- }
-
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
hid_err(hdev, "hw start failed\n");
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 7dd0953cd70f..dc8e6adf95a4 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -70,6 +70,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
+ { USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_ELAN, HID_ANY_ID, HID_QUIRK_ALWAYS_POLL },
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index 2f1ddca6f2e0..700145b15088 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -516,13 +516,13 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd,
goto inval;
} else if (uref->usage_index >= field->report_count)
goto inval;
-
- else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) &&
- (uref_multi->num_values > HID_MAX_MULTI_USAGES ||
- uref->usage_index + uref_multi->num_values > field->report_count))
- goto inval;
}
+ if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) &&
+ (uref_multi->num_values > HID_MAX_MULTI_USAGES ||
+ uref->usage_index + uref_multi->num_values > field->report_count))
+ goto inval;
+
switch (cmd) {
case HIDIOCGUSAGE:
uref->value = field->value[uref->usage_index];
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 3c0f47ac8e53..5c02d7bbc7f2 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -3449,6 +3449,10 @@ static const struct wacom_features wacom_features_0x33E =
{ "Wacom Intuos PT M 2", 21600, 13500, 2047, 63,
INTUOSHT2, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16,
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
+static const struct wacom_features wacom_features_0x343 =
+ { "Wacom DTK1651", 34616, 19559, 1023, 0,
+ DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4,
+ WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
static const struct wacom_features wacom_features_HID_ANY_ID =
{ "Wacom HID", .type = HID_GENERIC };
@@ -3614,6 +3618,7 @@ const struct hid_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0x33C) },
{ USB_DEVICE_WACOM(0x33D) },
{ USB_DEVICE_WACOM(0x33E) },
+ { USB_DEVICE_WACOM(0x343) },
{ USB_DEVICE_WACOM(0x4001) },
{ USB_DEVICE_WACOM(0x4004) },
{ USB_DEVICE_WACOM(0x5000) },
diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c
index 6c99ee7bafa3..ee396ff167d9 100644
--- a/drivers/hwmon/ads7828.c
+++ b/drivers/hwmon/ads7828.c
@@ -120,6 +120,7 @@ static int ads7828_probe(struct i2c_client *client,
unsigned int vref_mv = ADS7828_INT_VREF_MV;
bool diff_input = false;
bool ext_vref = false;
+ unsigned int regval;
data = devm_kzalloc(dev, sizeof(struct ads7828_data), GFP_KERNEL);
if (!data)
@@ -154,6 +155,15 @@ static int ads7828_probe(struct i2c_client *client,
if (!diff_input)
data->cmd_byte |= ADS7828_CMD_SD_SE;
+ /*
+ * Datasheet specifies internal reference voltage is disabled by
+ * default. The internal reference voltage needs to be enabled and
+ * voltage needs to settle before getting valid ADC data. So perform a
+ * dummy read to enable the internal reference voltage.
+ */
+ if (!ext_vref)
+ regmap_read(data->regmap, data->cmd_byte, &regval);
+
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
data,
ads7828_groups);
diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c
index c43318d3416e..a9356a3dea92 100644
--- a/drivers/hwmon/dell-smm-hwmon.c
+++ b/drivers/hwmon/dell-smm-hwmon.c
@@ -66,11 +66,13 @@
static DEFINE_MUTEX(i8k_mutex);
static char bios_version[4];
+static char bios_machineid[16];
static struct device *i8k_hwmon_dev;
static u32 i8k_hwmon_flags;
static uint i8k_fan_mult = I8K_FAN_MULT;
static uint i8k_pwm_mult;
static uint i8k_fan_max = I8K_FAN_HIGH;
+static bool disallow_fan_type_call;
#define I8K_HWMON_HAVE_TEMP1 (1 << 0)
#define I8K_HWMON_HAVE_TEMP2 (1 << 1)
@@ -94,13 +96,13 @@ module_param(ignore_dmi, bool, 0);
MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match");
#if IS_ENABLED(CONFIG_I8K)
-static bool restricted;
+static bool restricted = true;
module_param(restricted, bool, 0);
-MODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set");
+MODULE_PARM_DESC(restricted, "Restrict fan control and serial number to CAP_SYS_ADMIN (default: 1)");
static bool power_status;
module_param(power_status, bool, 0600);
-MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k");
+MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k (default: 0)");
#endif
static uint fan_mult;
@@ -235,14 +237,28 @@ static int i8k_get_fan_speed(int fan)
/*
* Read the fan type.
*/
-static int i8k_get_fan_type(int fan)
+static int _i8k_get_fan_type(int fan)
{
struct smm_regs regs = { .eax = I8K_SMM_GET_FAN_TYPE, };
+ if (disallow_fan_type_call)
+ return -EINVAL;
+
regs.ebx = fan & 0xff;
return i8k_smm(&regs) ? : regs.eax & 0xff;
}
+static int i8k_get_fan_type(int fan)
+{
+ /* I8K_SMM_GET_FAN_TYPE SMM call is expensive, so cache values */
+ static int types[2] = { INT_MIN, INT_MIN };
+
+ if (types[fan] == INT_MIN)
+ types[fan] = _i8k_get_fan_type(fan);
+
+ return types[fan];
+}
+
/*
* Read the fan nominal rpm for specific fan speed.
*/
@@ -392,9 +408,11 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
break;
case I8K_MACHINE_ID:
- memset(buff, 0, 16);
- strlcpy(buff, i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
- sizeof(buff));
+ if (restricted && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ memset(buff, 0, sizeof(buff));
+ strlcpy(buff, bios_machineid, sizeof(buff));
break;
case I8K_FN_STATUS:
@@ -511,7 +529,7 @@ static int i8k_proc_show(struct seq_file *seq, void *offset)
seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n",
I8K_PROC_FMT,
bios_version,
- i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
+ (restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : bios_machineid,
cpu_temp,
left_fan, right_fan, left_speed, right_speed,
ac_power, fn_key);
@@ -718,6 +736,9 @@ static struct attribute *i8k_attrs[] = {
static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr,
int index)
{
+ if (disallow_fan_type_call &&
+ (index == 9 || index == 12))
+ return 0;
if (index >= 0 && index <= 1 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1))
return 0;
@@ -767,13 +788,17 @@ static int __init i8k_init_hwmon(void)
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4;
- /* First fan attributes, if fan type is OK */
- err = i8k_get_fan_type(0);
+ /* First fan attributes, if fan status or type is OK */
+ err = i8k_get_fan_status(0);
+ if (err < 0)
+ err = i8k_get_fan_type(0);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN1;
- /* Second fan attributes, if fan type is OK */
- err = i8k_get_fan_type(1);
+ /* Second fan attributes, if fan status or type is OK */
+ err = i8k_get_fan_status(1);
+ if (err < 0)
+ err = i8k_get_fan_type(1);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2;
@@ -929,12 +954,14 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = {
MODULE_DEVICE_TABLE(dmi, i8k_dmi_table);
-static struct dmi_system_id i8k_blacklist_dmi_table[] __initdata = {
+/*
+ * On some machines once I8K_SMM_GET_FAN_TYPE is issued then CPU fan speed
+ * randomly going up and down due to bug in Dell SMM or BIOS. Here is blacklist
+ * of affected Dell machines for which we disallow I8K_SMM_GET_FAN_TYPE call.
+ * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=100121
+ */
+static struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initdata = {
{
- /*
- * CPU fan speed going up and down on Dell Studio XPS 8000
- * for unknown reasons.
- */
.ident = "Dell Studio XPS 8000",
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
@@ -942,16 +969,19 @@ static struct dmi_system_id i8k_blacklist_dmi_table[] __initdata = {
},
},
{
- /*
- * CPU fan speed going up and down on Dell Studio XPS 8100
- * for unknown reasons.
- */
.ident = "Dell Studio XPS 8100",
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8100"),
},
},
+ {
+ .ident = "Dell Inspiron 580",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 580 "),
+ },
+ },
{ }
};
@@ -966,8 +996,7 @@ static int __init i8k_probe(void)
/*
* Get DMI information
*/
- if (!dmi_check_system(i8k_dmi_table) ||
- dmi_check_system(i8k_blacklist_dmi_table)) {
+ if (!dmi_check_system(i8k_dmi_table)) {
if (!ignore_dmi && !force)
return -ENODEV;
@@ -978,8 +1007,13 @@ static int __init i8k_probe(void)
i8k_get_dmi_data(DMI_BIOS_VERSION));
}
+ if (dmi_check_system(i8k_blacklist_fan_type_dmi_table))
+ disallow_fan_type_call = true;
+
strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION),
sizeof(bios_version));
+ strlcpy(bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
+ sizeof(bios_machineid));
/*
* Get SMM Dell signature
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 5697ad3b1d13..a37b5ce9a6b2 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -2801,6 +2801,10 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
put_online_cpus();
+ ret = clk_set_rate(adev->pclk, CORESIGHT_CLK_RATE_TRACE);
+ if (ret)
+ return ret;
+
pm_runtime_put(&adev->dev);
mutex_lock(&drvdata->mutex);
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 766b052ade1d..cc8d957e0581 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -792,11 +792,14 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
drvdata->out_mode == TMC_ETR_OUT_MODE_USB) {
drvdata->usbch = usb_qdss_open("qdss", drvdata,
usb_notifier);
- if (IS_ERR(drvdata->usbch)) {
+ if (IS_ERR_OR_NULL(drvdata->usbch)) {
dev_err(drvdata->dev, "usb_qdss_open failed\n");
ret = PTR_ERR(drvdata->usbch);
pm_runtime_put(drvdata->dev);
mutex_unlock(&drvdata->mem_lock);
+ if (!ret)
+ ret = -ENODEV;
+
return ret;
}
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETB ||
@@ -1542,8 +1545,8 @@ static ssize_t mem_size_store(struct device *dev,
mutex_lock(&drvdata->mem_lock);
if (kstrtoul(buf, 16, &val)) {
- return -EINVAL;
mutex_unlock(&drvdata->mem_lock);
+ return -EINVAL;
}
drvdata->mem_size = val;
@@ -1846,12 +1849,13 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
struct device_node *np = adev->dev.of_node;
struct coresight_cti_data *ctidata;
- if (np) {
- pdata = of_get_coresight_platform_data(dev, np);
- if (IS_ERR(pdata))
- return PTR_ERR(pdata);
- adev->dev.platform_data = pdata;
- }
+ if (!np)
+ return -ENODEV;
+
+ pdata = of_get_coresight_platform_data(dev, np);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ adev->dev.platform_data = pdata;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
@@ -1896,6 +1900,10 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4;
}
+ ret = clk_set_rate(adev->pclk, CORESIGHT_CLK_RATE_TRACE);
+ if (ret)
+ return ret;
+
pm_runtime_put(&adev->dev);
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c
index 7baa1e750a23..3fd080b94069 100644
--- a/drivers/hwtracing/coresight/coresight-tpiu.c
+++ b/drivers/hwtracing/coresight/coresight-tpiu.c
@@ -162,6 +162,9 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
/* Disable tpiu to support older devices */
tpiu_disable_hw(drvdata);
+ ret = clk_set_rate(adev->pclk, CORESIGHT_CLK_RATE_TRACE);
+ if (ret)
+ return ret;
pm_runtime_put(&adev->dev);
diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig
index 83e9f591a54b..847a39b35307 100644
--- a/drivers/hwtracing/stm/Kconfig
+++ b/drivers/hwtracing/stm/Kconfig
@@ -1,6 +1,7 @@
config STM
tristate "System Trace Module devices"
select CONFIGFS_FS
+ select SRCU
help
A System Trace Module (STM) is a device exporting data in System
Trace Protocol (STP) format as defined by MIPI STP standards.
@@ -8,6 +9,8 @@ config STM
Say Y here to enable System Trace Module device support.
+if STM
+
config STM_DUMMY
tristate "Dummy STM driver"
help
@@ -24,3 +27,16 @@ config STM_SOURCE_CONSOLE
If you want to send kernel console messages over STM devices,
say Y.
+
+config STM_SOURCE_HEARTBEAT
+ tristate "Heartbeat over STM devices"
+ help
+ This is a kernel space trace source that sends periodic
+ heartbeat messages to trace hosts over STM devices. It is
+ also useful for testing stm class drivers and the stm class
+ framework itself.
+
+ If you want to send heartbeat messages over STM devices,
+ say Y.
+
+endif
diff --git a/drivers/hwtracing/stm/Makefile b/drivers/hwtracing/stm/Makefile
index f9312c38dd7a..a9ce3d487e57 100644
--- a/drivers/hwtracing/stm/Makefile
+++ b/drivers/hwtracing/stm/Makefile
@@ -5,5 +5,7 @@ stm_core-y := core.o policy.o
obj-$(CONFIG_STM_DUMMY) += dummy_stm.o
obj-$(CONFIG_STM_SOURCE_CONSOLE) += stm_console.o
+obj-$(CONFIG_STM_SOURCE_HEARTBEAT) += stm_heartbeat.o
stm_console-y := console.o
+stm_heartbeat-y := heartbeat.o
diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c
index b6445d9e5453..02095410cb33 100644
--- a/drivers/hwtracing/stm/core.c
+++ b/drivers/hwtracing/stm/core.c
@@ -67,9 +67,24 @@ static ssize_t channels_show(struct device *dev,
static DEVICE_ATTR_RO(channels);
+static ssize_t hw_override_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct stm_device *stm = to_stm_device(dev);
+ int ret;
+
+ ret = sprintf(buf, "%u\n", stm->data->hw_override);
+
+ return ret;
+}
+
+static DEVICE_ATTR_RO(hw_override);
+
static struct attribute *stm_attrs[] = {
&dev_attr_masters.attr,
&dev_attr_channels.attr,
+ &dev_attr_hw_override.attr,
NULL,
};
@@ -113,6 +128,7 @@ struct stm_device *stm_find_device(const char *buf)
stm = to_stm_device(dev);
if (!try_module_get(stm->owner)) {
+ /* matches class_find_device() above */
put_device(dev);
return NULL;
}
@@ -125,7 +141,7 @@ struct stm_device *stm_find_device(const char *buf)
* @stm: stm device, previously acquired by stm_find_device()
*
* This drops the module reference and device reference taken by
- * stm_find_device().
+ * stm_find_device() or stm_char_open().
*/
void stm_put_device(struct stm_device *stm)
{
@@ -185,6 +201,9 @@ static void stm_output_claim(struct stm_device *stm, struct stm_output *output)
{
struct stp_master *master = stm_master(stm, output->master);
+ lockdep_assert_held(&stm->mc_lock);
+ lockdep_assert_held(&output->lock);
+
if (WARN_ON_ONCE(master->nr_free < output->nr_chans))
return;
@@ -199,6 +218,9 @@ stm_output_disclaim(struct stm_device *stm, struct stm_output *output)
{
struct stp_master *master = stm_master(stm, output->master);
+ lockdep_assert_held(&stm->mc_lock);
+ lockdep_assert_held(&output->lock);
+
bitmap_release_region(&master->chan_map[0], output->channel,
ilog2(output->nr_chans));
@@ -288,6 +310,7 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width,
}
spin_lock(&stm->mc_lock);
+ spin_lock(&output->lock);
/* output is already assigned -- shouldn't happen */
if (WARN_ON_ONCE(output->nr_chans))
goto unlock;
@@ -304,6 +327,7 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width,
ret = 0;
unlock:
+ spin_unlock(&output->lock);
spin_unlock(&stm->mc_lock);
return ret;
@@ -312,11 +336,18 @@ unlock:
static void stm_output_free(struct stm_device *stm, struct stm_output *output)
{
spin_lock(&stm->mc_lock);
+ spin_lock(&output->lock);
if (output->nr_chans)
stm_output_disclaim(stm, output);
+ spin_unlock(&output->lock);
spin_unlock(&stm->mc_lock);
}
+static void stm_output_init(struct stm_output *output)
+{
+ spin_lock_init(&output->lock);
+}
+
static int major_match(struct device *dev, const void *data)
{
unsigned int major = *(unsigned int *)data;
@@ -339,6 +370,7 @@ static int stm_char_open(struct inode *inode, struct file *file)
if (!stmf)
return -ENOMEM;
+ stm_output_init(&stmf->output);
stmf->stm = to_stm_device(dev);
if (!try_module_get(stmf->stm->owner))
@@ -349,6 +381,8 @@ static int stm_char_open(struct inode *inode, struct file *file)
return nonseekable_open(inode, file);
err_free:
+ /* matches class_find_device() above */
+ put_device(dev);
kfree(stmf);
return err;
@@ -357,9 +391,19 @@ err_free:
static int stm_char_release(struct inode *inode, struct file *file)
{
struct stm_file *stmf = file->private_data;
+ struct stm_device *stm = stmf->stm;
+
+ if (stm->data->unlink)
+ stm->data->unlink(stm->data, stmf->output.master,
+ stmf->output.channel);
+
+ stm_output_free(stm, &stmf->output);
- stm_output_free(stmf->stm, &stmf->output);
- stm_put_device(stmf->stm);
+ /*
+ * matches the stm_char_open()'s
+ * class_find_device() + try_module_get()
+ */
+ stm_put_device(stm);
kfree(stmf);
return 0;
@@ -380,8 +424,8 @@ static int stm_file_assign(struct stm_file *stmf, char *id, unsigned int width)
return ret;
}
-static void stm_write(struct stm_data *data, unsigned int master,
- unsigned int channel, const char *buf, size_t count)
+static ssize_t stm_write(struct stm_data *data, unsigned int master,
+ unsigned int channel, const char *buf, size_t count)
{
unsigned int flags = STP_PACKET_TIMESTAMPED;
const unsigned char *p = buf, nil = 0;
@@ -393,9 +437,14 @@ static void stm_write(struct stm_data *data, unsigned int master,
sz = data->packet(data, master, channel, STP_PACKET_DATA, flags,
sz, p);
flags = 0;
+
+ if (sz < 0)
+ break;
}
data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, &nil);
+
+ return pos;
}
static ssize_t stm_char_write(struct file *file, const char __user *buf,
@@ -406,6 +455,9 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf,
char *kbuf;
int err;
+ if (count + 1 > PAGE_SIZE)
+ count = PAGE_SIZE - 1;
+
/*
* if no m/c have been assigned to this writer up to this
* point, use "default" policy entry
@@ -430,8 +482,8 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf,
return -EFAULT;
}
- stm_write(stm->data, stmf->output.master, stmf->output.channel, kbuf,
- count);
+ count = stm_write(stm->data, stmf->output.master, stmf->output.channel,
+ kbuf, count);
kfree(kbuf);
@@ -509,16 +561,12 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg)
if (ret)
goto err_free;
- ret = 0;
-
if (stm->data->link)
ret = stm->data->link(stm->data, stmf->output.master,
stmf->output.channel);
- if (ret) {
+ if (ret)
stm_output_free(stmf->stm, &stmf->output);
- stm_put_device(stmf->stm);
- }
err_free:
kfree(id);
@@ -633,17 +681,11 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data,
stm->dev.parent = parent;
stm->dev.release = stm_device_release;
- err = kobject_set_name(&stm->dev.kobj, "%s", stm_data->name);
- if (err)
- goto err_device;
-
- err = device_add(&stm->dev);
- if (err)
- goto err_device;
-
+ mutex_init(&stm->link_mutex);
spin_lock_init(&stm->link_lock);
INIT_LIST_HEAD(&stm->link_list);
+ /* initialize the object before it is accessible via sysfs */
spin_lock_init(&stm->mc_lock);
mutex_init(&stm->policy_mutex);
stm->sw_nmasters = nmasters;
@@ -651,9 +693,20 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data,
stm->data = stm_data;
stm_data->stm = stm;
+ err = kobject_set_name(&stm->dev.kobj, "%s", stm_data->name);
+ if (err)
+ goto err_device;
+
+ err = device_add(&stm->dev);
+ if (err)
+ goto err_device;
+
return 0;
err_device:
+ unregister_chrdev(stm->major, stm_data->name);
+
+ /* matches device_initialize() above */
put_device(&stm->dev);
err_free:
kfree(stm);
@@ -662,20 +715,28 @@ err_free:
}
EXPORT_SYMBOL_GPL(stm_register_device);
-static void __stm_source_link_drop(struct stm_source_device *src,
- struct stm_device *stm);
+static int __stm_source_link_drop(struct stm_source_device *src,
+ struct stm_device *stm);
void stm_unregister_device(struct stm_data *stm_data)
{
struct stm_device *stm = stm_data->stm;
struct stm_source_device *src, *iter;
- int i;
+ int i, ret;
- spin_lock(&stm->link_lock);
+ mutex_lock(&stm->link_mutex);
list_for_each_entry_safe(src, iter, &stm->link_list, link_entry) {
- __stm_source_link_drop(src, stm);
+ ret = __stm_source_link_drop(src, stm);
+ /*
+ * src <-> stm link must not change under the same
+ * stm::link_mutex, so complain loudly if it has;
+ * also in this situation ret!=0 means this src is
+ * not connected to this stm and it should be otherwise
+ * safe to proceed with the tear-down of stm.
+ */
+ WARN_ON_ONCE(ret);
}
- spin_unlock(&stm->link_lock);
+ mutex_unlock(&stm->link_mutex);
synchronize_srcu(&stm_source_srcu);
@@ -694,6 +755,17 @@ void stm_unregister_device(struct stm_data *stm_data)
}
EXPORT_SYMBOL_GPL(stm_unregister_device);
+/*
+ * stm::link_list access serialization uses a spinlock and a mutex; holding
+ * either of them guarantees that the list is stable; modification requires
+ * holding both of them.
+ *
+ * Lock ordering is as follows:
+ * stm::link_mutex
+ * stm::link_lock
+ * src::link_lock
+ */
+
/**
* stm_source_link_add() - connect an stm_source device to an stm device
* @src: stm_source device
@@ -710,6 +782,7 @@ static int stm_source_link_add(struct stm_source_device *src,
char *id;
int err;
+ mutex_lock(&stm->link_mutex);
spin_lock(&stm->link_lock);
spin_lock(&src->link_lock);
@@ -719,6 +792,7 @@ static int stm_source_link_add(struct stm_source_device *src,
spin_unlock(&src->link_lock);
spin_unlock(&stm->link_lock);
+ mutex_unlock(&stm->link_mutex);
id = kstrdup(src->data->name, GFP_KERNEL);
if (id) {
@@ -753,9 +827,9 @@ static int stm_source_link_add(struct stm_source_device *src,
fail_free_output:
stm_output_free(stm, &src->output);
- stm_put_device(stm);
fail_detach:
+ mutex_lock(&stm->link_mutex);
spin_lock(&stm->link_lock);
spin_lock(&src->link_lock);
@@ -764,6 +838,7 @@ fail_detach:
spin_unlock(&src->link_lock);
spin_unlock(&stm->link_lock);
+ mutex_unlock(&stm->link_mutex);
return err;
}
@@ -776,28 +851,55 @@ fail_detach:
* If @stm is @src::link, disconnect them from one another and put the
* reference on the @stm device.
*
- * Caller must hold stm::link_lock.
+ * Caller must hold stm::link_mutex.
*/
-static void __stm_source_link_drop(struct stm_source_device *src,
- struct stm_device *stm)
+static int __stm_source_link_drop(struct stm_source_device *src,
+ struct stm_device *stm)
{
struct stm_device *link;
+ int ret = 0;
+
+ lockdep_assert_held(&stm->link_mutex);
+ /* for stm::link_list modification, we hold both mutex and spinlock */
+ spin_lock(&stm->link_lock);
spin_lock(&src->link_lock);
link = srcu_dereference_check(src->link, &stm_source_srcu, 1);
- if (WARN_ON_ONCE(link != stm)) {
- spin_unlock(&src->link_lock);
- return;
+
+ /*
+ * The linked device may have changed since we last looked, because
+ * we weren't holding the src::link_lock back then; if this is the
+ * case, tell the caller to retry.
+ */
+ if (link != stm) {
+ ret = -EAGAIN;
+ goto unlock;
}
stm_output_free(link, &src->output);
- /* caller must hold stm::link_lock */
list_del_init(&src->link_entry);
/* matches stm_find_device() from stm_source_link_store() */
stm_put_device(link);
rcu_assign_pointer(src->link, NULL);
+unlock:
spin_unlock(&src->link_lock);
+ spin_unlock(&stm->link_lock);
+
+ /*
+ * Call the unlink callbacks for both source and stm, when we know
+ * that we have actually performed the unlinking.
+ */
+ if (!ret) {
+ if (src->data->unlink)
+ src->data->unlink(src->data);
+
+ if (stm->data->unlink)
+ stm->data->unlink(stm->data, src->output.master,
+ src->output.channel);
+ }
+
+ return ret;
}
/**
@@ -813,21 +915,29 @@ static void __stm_source_link_drop(struct stm_source_device *src,
static void stm_source_link_drop(struct stm_source_device *src)
{
struct stm_device *stm;
- int idx;
+ int idx, ret;
+retry:
idx = srcu_read_lock(&stm_source_srcu);
+ /*
+ * The stm device will be valid for the duration of this
+ * read section, but the link may change before we grab
+ * the src::link_lock in __stm_source_link_drop().
+ */
stm = srcu_dereference(src->link, &stm_source_srcu);
+ ret = 0;
if (stm) {
- if (src->data->unlink)
- src->data->unlink(src->data);
-
- spin_lock(&stm->link_lock);
- __stm_source_link_drop(src, stm);
- spin_unlock(&stm->link_lock);
+ mutex_lock(&stm->link_mutex);
+ ret = __stm_source_link_drop(src, stm);
+ mutex_unlock(&stm->link_mutex);
}
srcu_read_unlock(&stm_source_srcu, idx);
+
+ /* if it did change, retry */
+ if (ret == -EAGAIN)
+ goto retry;
}
static ssize_t stm_source_link_show(struct device *dev,
@@ -862,8 +972,10 @@ static ssize_t stm_source_link_store(struct device *dev,
return -EINVAL;
err = stm_source_link_add(src, link);
- if (err)
+ if (err) {
+ /* matches the stm_find_device() above */
stm_put_device(link);
+ }
return err ? : count;
}
@@ -925,6 +1037,7 @@ int stm_source_register_device(struct device *parent,
if (err)
goto err;
+ stm_output_init(&src->output);
spin_lock_init(&src->link_lock);
INIT_LIST_HEAD(&src->link_entry);
src->data = data;
@@ -973,9 +1086,9 @@ int stm_source_write(struct stm_source_data *data, unsigned int chan,
stm = srcu_dereference(src->link, &stm_source_srcu);
if (stm)
- stm_write(stm->data, src->output.master,
- src->output.channel + chan,
- buf, count);
+ count = stm_write(stm->data, src->output.master,
+ src->output.channel + chan,
+ buf, count);
else
count = -ENODEV;
diff --git a/drivers/hwtracing/stm/dummy_stm.c b/drivers/hwtracing/stm/dummy_stm.c
index 3709bef0b21f..a86612d989f9 100644
--- a/drivers/hwtracing/stm/dummy_stm.c
+++ b/drivers/hwtracing/stm/dummy_stm.c
@@ -40,22 +40,71 @@ dummy_stm_packet(struct stm_data *stm_data, unsigned int master,
return size;
}
-static struct stm_data dummy_stm = {
- .name = "dummy_stm",
- .sw_start = 0x0000,
- .sw_end = 0xffff,
- .sw_nchannels = 0xffff,
- .packet = dummy_stm_packet,
-};
+#define DUMMY_STM_MAX 32
+
+static struct stm_data dummy_stm[DUMMY_STM_MAX];
+
+static int nr_dummies = 4;
+
+module_param(nr_dummies, int, 0400);
+
+static unsigned int fail_mode;
+
+module_param(fail_mode, int, 0600);
+
+static int dummy_stm_link(struct stm_data *data, unsigned int master,
+ unsigned int channel)
+{
+ if (fail_mode && (channel & fail_mode))
+ return -EINVAL;
+
+ return 0;
+}
static int dummy_stm_init(void)
{
- return stm_register_device(NULL, &dummy_stm, THIS_MODULE);
+ int i, ret = -ENOMEM;
+
+ if (nr_dummies < 0 || nr_dummies > DUMMY_STM_MAX)
+ return -EINVAL;
+
+ for (i = 0; i < nr_dummies; i++) {
+ dummy_stm[i].name = kasprintf(GFP_KERNEL, "dummy_stm.%d", i);
+ if (!dummy_stm[i].name)
+ goto fail_unregister;
+
+ dummy_stm[i].sw_start = 0x0000;
+ dummy_stm[i].sw_end = 0xffff;
+ dummy_stm[i].sw_nchannels = 0xffff;
+ dummy_stm[i].packet = dummy_stm_packet;
+ dummy_stm[i].link = dummy_stm_link;
+
+ ret = stm_register_device(NULL, &dummy_stm[i], THIS_MODULE);
+ if (ret)
+ goto fail_free;
+ }
+
+ return 0;
+
+fail_unregister:
+ for (i--; i >= 0; i--) {
+ stm_unregister_device(&dummy_stm[i]);
+fail_free:
+ kfree(dummy_stm[i].name);
+ }
+
+ return ret;
+
}
static void dummy_stm_exit(void)
{
- stm_unregister_device(&dummy_stm);
+ int i;
+
+ for (i = 0; i < nr_dummies; i++) {
+ stm_unregister_device(&dummy_stm[i]);
+ kfree(dummy_stm[i].name);
+ }
}
module_init(dummy_stm_init);
diff --git a/drivers/hwtracing/stm/heartbeat.c b/drivers/hwtracing/stm/heartbeat.c
new file mode 100644
index 000000000000..3da7b673aab2
--- /dev/null
+++ b/drivers/hwtracing/stm/heartbeat.c
@@ -0,0 +1,126 @@
+/*
+ * Simple heartbeat STM source driver
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * Heartbeat STM source will send repetitive messages over STM devices to a
+ * trace host.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/slab.h>
+#include <linux/stm.h>
+
+#define STM_HEARTBEAT_MAX 32
+
+static int nr_devs = 4;
+static int interval_ms = 10;
+
+module_param(nr_devs, int, 0400);
+module_param(interval_ms, int, 0600);
+
+static struct stm_heartbeat {
+ struct stm_source_data data;
+ struct hrtimer hrtimer;
+ unsigned int active;
+} stm_heartbeat[STM_HEARTBEAT_MAX];
+
+static const char str[] = "heartbeat stm source driver is here to serve you";
+
+static enum hrtimer_restart stm_heartbeat_hrtimer_handler(struct hrtimer *hr)
+{
+ struct stm_heartbeat *heartbeat = container_of(hr, struct stm_heartbeat,
+ hrtimer);
+
+ stm_source_write(&heartbeat->data, 0, str, sizeof str);
+ if (heartbeat->active)
+ hrtimer_forward_now(hr, ms_to_ktime(interval_ms));
+
+ return heartbeat->active ? HRTIMER_RESTART : HRTIMER_NORESTART;
+}
+
+static int stm_heartbeat_link(struct stm_source_data *data)
+{
+ struct stm_heartbeat *heartbeat =
+ container_of(data, struct stm_heartbeat, data);
+
+ heartbeat->active = 1;
+ hrtimer_start(&heartbeat->hrtimer, ms_to_ktime(interval_ms),
+ HRTIMER_MODE_ABS);
+
+ return 0;
+}
+
+static void stm_heartbeat_unlink(struct stm_source_data *data)
+{
+ struct stm_heartbeat *heartbeat =
+ container_of(data, struct stm_heartbeat, data);
+
+ heartbeat->active = 0;
+ hrtimer_cancel(&heartbeat->hrtimer);
+}
+
+static int stm_heartbeat_init(void)
+{
+ int i, ret = -ENOMEM;
+
+ if (nr_devs < 0 || nr_devs > STM_HEARTBEAT_MAX)
+ return -EINVAL;
+
+ for (i = 0; i < nr_devs; i++) {
+ stm_heartbeat[i].data.name =
+ kasprintf(GFP_KERNEL, "heartbeat.%d", i);
+ if (!stm_heartbeat[i].data.name)
+ goto fail_unregister;
+
+ stm_heartbeat[i].data.nr_chans = 1;
+ stm_heartbeat[i].data.link = stm_heartbeat_link;
+ stm_heartbeat[i].data.unlink = stm_heartbeat_unlink;
+ hrtimer_init(&stm_heartbeat[i].hrtimer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_ABS);
+ stm_heartbeat[i].hrtimer.function =
+ stm_heartbeat_hrtimer_handler;
+
+ ret = stm_source_register_device(NULL, &stm_heartbeat[i].data);
+ if (ret)
+ goto fail_free;
+ }
+
+ return 0;
+
+fail_unregister:
+ for (i--; i >= 0; i--) {
+ stm_source_unregister_device(&stm_heartbeat[i].data);
+fail_free:
+ kfree(stm_heartbeat[i].data.name);
+ }
+
+ return ret;
+}
+
+static void stm_heartbeat_exit(void)
+{
+ int i;
+
+ for (i = 0; i < nr_devs; i++) {
+ stm_source_unregister_device(&stm_heartbeat[i].data);
+ kfree(stm_heartbeat[i].data.name);
+ }
+}
+
+module_init(stm_heartbeat_init);
+module_exit(stm_heartbeat_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("stm_heartbeat driver");
+MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c
index 11ab6d01adf6..1c061cb9bff0 100644
--- a/drivers/hwtracing/stm/policy.c
+++ b/drivers/hwtracing/stm/policy.c
@@ -272,13 +272,17 @@ void stp_policy_unbind(struct stp_policy *policy)
{
struct stm_device *stm = policy->stm;
+ /*
+ * stp_policy_release() will not call here if the policy is already
+ * unbound; other users should not either, as no link exists between
+ * this policy and anything else in that case
+ */
if (WARN_ON_ONCE(!policy->stm))
return;
- mutex_lock(&stm->policy_mutex);
- stm->policy = NULL;
- mutex_unlock(&stm->policy_mutex);
+ lockdep_assert_held(&stm->policy_mutex);
+ stm->policy = NULL;
policy->stm = NULL;
stm_put_device(stm);
@@ -287,8 +291,16 @@ void stp_policy_unbind(struct stp_policy *policy)
static void stp_policy_release(struct config_item *item)
{
struct stp_policy *policy = to_stp_policy(item);
+ struct stm_device *stm = policy->stm;
+ /* a policy *can* be unbound and still exist in configfs tree */
+ if (!stm)
+ return;
+
+ mutex_lock(&stm->policy_mutex);
stp_policy_unbind(policy);
+ mutex_unlock(&stm->policy_mutex);
+
kfree(policy);
}
@@ -320,16 +332,17 @@ stp_policies_make(struct config_group *group, const char *name)
/*
* node must look like <device_name>.<policy_name>, where
- * <device_name> is the name of an existing stm device and
- * <policy_name> is an arbitrary string
+ * <device_name> is the name of an existing stm device; may
+ * contain dots;
+ * <policy_name> is an arbitrary string; may not contain dots
*/
- p = strchr(devname, '.');
+ p = strrchr(devname, '.');
if (!p) {
kfree(devname);
return ERR_PTR(-EINVAL);
}
- *p++ = '\0';
+ *p = '\0';
stm = stm_find_device(devname);
kfree(devname);
diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h
index 95ece0292c99..4e8c6926260f 100644
--- a/drivers/hwtracing/stm/stm.h
+++ b/drivers/hwtracing/stm/stm.h
@@ -45,6 +45,7 @@ struct stm_device {
int major;
unsigned int sw_nmasters;
struct stm_data *data;
+ struct mutex link_mutex;
spinlock_t link_lock;
struct list_head link_list;
/* master allocation */
@@ -56,6 +57,7 @@ struct stm_device {
container_of((_d), struct stm_device, dev)
struct stm_output {
+ spinlock_t lock;
unsigned int master;
unsigned int channel;
unsigned int nr_chans;
diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c
index 714bdc837769..b167ab25310a 100644
--- a/drivers/i2c/busses/i2c-cpm.c
+++ b/drivers/i2c/busses/i2c-cpm.c
@@ -116,8 +116,8 @@ struct cpm_i2c {
cbd_t __iomem *rbase;
u_char *txbuf[CPM_MAXBD];
u_char *rxbuf[CPM_MAXBD];
- u32 txdma[CPM_MAXBD];
- u32 rxdma[CPM_MAXBD];
+ dma_addr_t txdma[CPM_MAXBD];
+ dma_addr_t rxdma[CPM_MAXBD];
};
static irqreturn_t cpm_i2c_interrupt(int irq, void *dev_id)
diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
index b29c7500461a..f54ece8fce78 100644
--- a/drivers/i2c/busses/i2c-exynos5.c
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -671,7 +671,9 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap,
return -EIO;
}
- clk_prepare_enable(i2c->clk);
+ ret = clk_enable(i2c->clk);
+ if (ret)
+ return ret;
for (i = 0; i < num; i++, msgs++) {
stop = (i == num - 1);
@@ -695,7 +697,7 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap,
}
out:
- clk_disable_unprepare(i2c->clk);
+ clk_disable(i2c->clk);
return ret;
}
@@ -747,7 +749,9 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
return -ENOENT;
}
- clk_prepare_enable(i2c->clk);
+ ret = clk_prepare_enable(i2c->clk);
+ if (ret)
+ return ret;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
@@ -799,6 +803,10 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, i2c);
+ clk_disable(i2c->clk);
+
+ return 0;
+
err_clk:
clk_disable_unprepare(i2c->clk);
return ret;
@@ -810,6 +818,8 @@ static int exynos5_i2c_remove(struct platform_device *pdev)
i2c_del_adapter(&i2c->adap);
+ clk_unprepare(i2c->clk);
+
return 0;
}
@@ -821,6 +831,8 @@ static int exynos5_i2c_suspend_noirq(struct device *dev)
i2c->suspended = 1;
+ clk_unprepare(i2c->clk);
+
return 0;
}
@@ -830,7 +842,9 @@ static int exynos5_i2c_resume_noirq(struct device *dev)
struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
int ret = 0;
- clk_prepare_enable(i2c->clk);
+ ret = clk_prepare_enable(i2c->clk);
+ if (ret)
+ return ret;
ret = exynos5_hsi2c_clock_setup(i2c);
if (ret) {
@@ -839,7 +853,7 @@ static int exynos5_i2c_resume_noirq(struct device *dev)
}
exynos5_i2c_init(i2c);
- clk_disable_unprepare(i2c->clk);
+ clk_disable(i2c->clk);
i2c->suspended = 0;
return 0;
diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c
index 923f56598d4b..3a9f106787d2 100644
--- a/drivers/iio/accel/kxsd9.c
+++ b/drivers/iio/accel/kxsd9.c
@@ -81,7 +81,7 @@ static int kxsd9_write_scale(struct iio_dev *indio_dev, int micro)
mutex_lock(&st->buf_lock);
ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
- if (ret)
+ if (ret < 0)
goto error_ret;
st->tx[0] = KXSD9_WRITE(KXSD9_REG_CTRL_C);
st->tx[1] = (ret & ~KXSD9_FS_MASK) | i;
@@ -163,7 +163,7 @@ static int kxsd9_read_raw(struct iio_dev *indio_dev,
break;
case IIO_CHAN_INFO_SCALE:
ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
- if (ret)
+ if (ret < 0)
goto error_ret;
*val2 = kxsd9_micro_scales[ret & KXSD9_FS_MASK];
ret = IIO_VAL_INT_PLUS_MICRO;
diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c
index 21e19b60e2b9..2123f0ac2e2a 100644
--- a/drivers/iio/adc/ad7266.c
+++ b/drivers/iio/adc/ad7266.c
@@ -396,8 +396,8 @@ static int ad7266_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
- st->reg = devm_regulator_get(&spi->dev, "vref");
- if (!IS_ERR_OR_NULL(st->reg)) {
+ st->reg = devm_regulator_get_optional(&spi->dev, "vref");
+ if (!IS_ERR(st->reg)) {
ret = regulator_enable(st->reg);
if (ret)
return ret;
@@ -408,6 +408,9 @@ static int ad7266_probe(struct spi_device *spi)
st->vref_mv = ret / 1000;
} else {
+ /* Any other error indicates that the regulator does exist */
+ if (PTR_ERR(st->reg) != -ENODEV)
+ return PTR_ERR(st->reg);
/* Use internal reference */
st->vref_mv = 2500;
}
diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c
index ebb49230d4d7..e08de7a808eb 100644
--- a/drivers/iio/adc/qcom-rradc.c
+++ b/drivers/iio/adc/qcom-rradc.c
@@ -165,7 +165,8 @@
#define FG_ADC_RR_CHG_THRESHOLD_SCALE 4
#define FG_ADC_RR_VOLT_INPUT_FACTOR 8
-#define FG_ADC_RR_CURR_INPUT_FACTOR 2
+#define FG_ADC_RR_CURR_INPUT_FACTOR 2000
+#define FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL 1886
#define FG_ADC_SCALE_MILLI_FACTOR 1000
#define FG_ADC_KELVINMIL_CELSIUSMIL 273150
@@ -323,12 +324,20 @@ static int rradc_post_process_curr(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 adc_code,
int *result_ua)
{
- int64_t ua = 0;
+ int64_t ua = 0, scale = 0;
- /* 0.5 V/A; 2.5V ADC full scale */
- ua = ((int64_t)adc_code * FG_ADC_RR_CURR_INPUT_FACTOR);
+ if (!prop)
+ return -EINVAL;
+
+ if (prop->channel == RR_ADC_USBIN_I)
+ scale = FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL;
+ else
+ scale = FG_ADC_RR_CURR_INPUT_FACTOR;
+
+ /* scale * V/A; 2.5V ADC full scale */
+ ua = ((int64_t)adc_code * scale);
ua *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR);
- ua = div64_s64(ua, FG_MAX_ADC_READINGS);
+ ua = div64_s64(ua, (FG_MAX_ADC_READINGS * 1000));
*result_ua = ua;
return 0;
diff --git a/drivers/iio/adc/qcom-tadc.c b/drivers/iio/adc/qcom-tadc.c
index 3cc2694f9a03..4a56847a43e7 100644
--- a/drivers/iio/adc/qcom-tadc.c
+++ b/drivers/iio/adc/qcom-tadc.c
@@ -398,7 +398,7 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc)
}
for (i = 0; i < TADC_NUM_CH; i++)
- adc[i] = val[i * 2] | val[i * 2 + 1] << BITS_PER_BYTE;
+ adc[i] = (s16)(val[i * 2] | (u16)val[i * 2 + 1] << 8);
return jiffies_to_msecs(timeout - timeleft);
}
diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c
index a7f61e881a49..dc5e7e70f951 100644
--- a/drivers/iio/humidity/hdc100x.c
+++ b/drivers/iio/humidity/hdc100x.c
@@ -55,7 +55,7 @@ static const struct {
},
{ /* IIO_HUMIDITYRELATIVE channel */
.shift = 8,
- .mask = 2,
+ .mask = 3,
},
};
@@ -164,14 +164,14 @@ static int hdc100x_get_measurement(struct hdc100x_data *data,
dev_err(&client->dev, "cannot read high byte measurement");
return ret;
}
- val = ret << 6;
+ val = ret << 8;
ret = i2c_smbus_read_byte(client);
if (ret < 0) {
dev_err(&client->dev, "cannot read low byte measurement");
return ret;
}
- val |= ret >> 2;
+ val |= ret;
return val;
}
@@ -211,18 +211,18 @@ static int hdc100x_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_TEMP) {
- *val = 165;
- *val2 = 65536 >> 2;
+ *val = 165000;
+ *val2 = 65536;
return IIO_VAL_FRACTIONAL;
} else {
- *val = 0;
- *val2 = 10000;
- return IIO_VAL_INT_PLUS_MICRO;
+ *val = 100;
+ *val2 = 65536;
+ return IIO_VAL_FRACTIONAL;
}
break;
case IIO_CHAN_INFO_OFFSET:
- *val = -3971;
- *val2 = 879096;
+ *val = -15887;
+ *val2 = 515151;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
index ae2806aafb72..0c52dfe64977 100644
--- a/drivers/iio/industrialio-trigger.c
+++ b/drivers/iio/industrialio-trigger.c
@@ -210,22 +210,35 @@ static int iio_trigger_attach_poll_func(struct iio_trigger *trig,
/* Prevent the module from being removed whilst attached to a trigger */
__module_get(pf->indio_dev->info->driver_module);
+
+ /* Get irq number */
pf->irq = iio_trigger_get_irq(trig);
+ if (pf->irq < 0)
+ goto out_put_module;
+
+ /* Request irq */
ret = request_threaded_irq(pf->irq, pf->h, pf->thread,
pf->type, pf->name,
pf);
- if (ret < 0) {
- module_put(pf->indio_dev->info->driver_module);
- return ret;
- }
+ if (ret < 0)
+ goto out_put_irq;
+ /* Enable trigger in driver */
if (trig->ops && trig->ops->set_trigger_state && notinuse) {
ret = trig->ops->set_trigger_state(trig, true);
if (ret < 0)
- module_put(pf->indio_dev->info->driver_module);
+ goto out_free_irq;
}
return ret;
+
+out_free_irq:
+ free_irq(pf->irq, pf);
+out_put_irq:
+ iio_trigger_put_irq(trig, pf->irq);
+out_put_module:
+ module_put(pf->indio_dev->info->driver_module);
+ return ret;
}
static int iio_trigger_detach_poll_func(struct iio_trigger *trig,
diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c
index f6a07dc32ae4..4a6d9670e4cd 100644
--- a/drivers/iio/light/apds9960.c
+++ b/drivers/iio/light/apds9960.c
@@ -1005,6 +1005,7 @@ static int apds9960_probe(struct i2c_client *client,
iio_device_attach_buffer(indio_dev, buffer);
+ indio_dev->dev.parent = &client->dev;
indio_dev->info = &apds9960_info;
indio_dev->name = APDS9960_DRV_NAME;
indio_dev->channels = apds9960_channels;
diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c
index b13936dacc78..f2a7f72f7aa6 100644
--- a/drivers/iio/magnetometer/ak8975.c
+++ b/drivers/iio/magnetometer/ak8975.c
@@ -462,6 +462,8 @@ static int ak8975_setup_irq(struct ak8975_data *data)
int rc;
int irq;
+ init_waitqueue_head(&data->data_ready_queue);
+ clear_bit(0, &data->flags);
if (client->irq)
irq = client->irq;
else
@@ -477,8 +479,6 @@ static int ak8975_setup_irq(struct ak8975_data *data)
return rc;
}
- init_waitqueue_head(&data->data_ready_queue);
- clear_bit(0, &data->flags);
data->eoc_irq = irq;
return rc;
@@ -732,7 +732,7 @@ static int ak8975_probe(struct i2c_client *client,
int eoc_gpio;
int err;
const char *name = NULL;
- enum asahi_compass_chipset chipset;
+ enum asahi_compass_chipset chipset = AK_MAX_TYPE;
/* Grab and set up the supplied GPIO. */
if (client->dev.platform_data)
diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c
index b39a2fb0671c..5056bd68573f 100644
--- a/drivers/iio/pressure/st_pressure_core.c
+++ b/drivers/iio/pressure/st_pressure_core.c
@@ -28,15 +28,21 @@
#include <linux/iio/common/st_sensors.h>
#include "st_pressure.h"
+#define MCELSIUS_PER_CELSIUS 1000
+
+/* Default pressure sensitivity */
#define ST_PRESS_LSB_PER_MBAR 4096UL
#define ST_PRESS_KPASCAL_NANO_SCALE (100000000UL / \
ST_PRESS_LSB_PER_MBAR)
+
+/* Default temperature sensitivity */
#define ST_PRESS_LSB_PER_CELSIUS 480UL
-#define ST_PRESS_CELSIUS_NANO_SCALE (1000000000UL / \
- ST_PRESS_LSB_PER_CELSIUS)
+#define ST_PRESS_MILLI_CELSIUS_OFFSET 42500UL
+
#define ST_PRESS_NUMBER_DATA_CHANNELS 1
/* FULLSCALE */
+#define ST_PRESS_FS_AVL_1100MB 1100
#define ST_PRESS_FS_AVL_1260MB 1260
#define ST_PRESS_1_OUT_XL_ADDR 0x28
@@ -54,18 +60,20 @@
#define ST_PRESS_LPS331AP_PW_MASK 0x80
#define ST_PRESS_LPS331AP_FS_ADDR 0x23
#define ST_PRESS_LPS331AP_FS_MASK 0x30
-#define ST_PRESS_LPS331AP_FS_AVL_1260_VAL 0x00
-#define ST_PRESS_LPS331AP_FS_AVL_1260_GAIN ST_PRESS_KPASCAL_NANO_SCALE
-#define ST_PRESS_LPS331AP_FS_AVL_TEMP_GAIN ST_PRESS_CELSIUS_NANO_SCALE
#define ST_PRESS_LPS331AP_BDU_ADDR 0x20
#define ST_PRESS_LPS331AP_BDU_MASK 0x04
#define ST_PRESS_LPS331AP_DRDY_IRQ_ADDR 0x22
#define ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK 0x04
#define ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK 0x20
#define ST_PRESS_LPS331AP_MULTIREAD_BIT true
-#define ST_PRESS_LPS331AP_TEMP_OFFSET 42500
/* CUSTOM VALUES FOR LPS001WP SENSOR */
+
+/* LPS001WP pressure resolution */
+#define ST_PRESS_LPS001WP_LSB_PER_MBAR 16UL
+/* LPS001WP temperature resolution */
+#define ST_PRESS_LPS001WP_LSB_PER_CELSIUS 64UL
+
#define ST_PRESS_LPS001WP_WAI_EXP 0xba
#define ST_PRESS_LPS001WP_ODR_ADDR 0x20
#define ST_PRESS_LPS001WP_ODR_MASK 0x30
@@ -74,6 +82,8 @@
#define ST_PRESS_LPS001WP_ODR_AVL_13HZ_VAL 0x03
#define ST_PRESS_LPS001WP_PW_ADDR 0x20
#define ST_PRESS_LPS001WP_PW_MASK 0x40
+#define ST_PRESS_LPS001WP_FS_AVL_PRESS_GAIN \
+ (100000000UL / ST_PRESS_LPS001WP_LSB_PER_MBAR)
#define ST_PRESS_LPS001WP_BDU_ADDR 0x20
#define ST_PRESS_LPS001WP_BDU_MASK 0x04
#define ST_PRESS_LPS001WP_MULTIREAD_BIT true
@@ -90,18 +100,12 @@
#define ST_PRESS_LPS25H_ODR_AVL_25HZ_VAL 0x04
#define ST_PRESS_LPS25H_PW_ADDR 0x20
#define ST_PRESS_LPS25H_PW_MASK 0x80
-#define ST_PRESS_LPS25H_FS_ADDR 0x00
-#define ST_PRESS_LPS25H_FS_MASK 0x00
-#define ST_PRESS_LPS25H_FS_AVL_1260_VAL 0x00
-#define ST_PRESS_LPS25H_FS_AVL_1260_GAIN ST_PRESS_KPASCAL_NANO_SCALE
-#define ST_PRESS_LPS25H_FS_AVL_TEMP_GAIN ST_PRESS_CELSIUS_NANO_SCALE
#define ST_PRESS_LPS25H_BDU_ADDR 0x20
#define ST_PRESS_LPS25H_BDU_MASK 0x04
#define ST_PRESS_LPS25H_DRDY_IRQ_ADDR 0x23
#define ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK 0x01
#define ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK 0x10
#define ST_PRESS_LPS25H_MULTIREAD_BIT true
-#define ST_PRESS_LPS25H_TEMP_OFFSET 42500
#define ST_PRESS_LPS25H_OUT_XL_ADDR 0x28
#define ST_TEMP_LPS25H_OUT_L_ADDR 0x2b
@@ -153,7 +157,9 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = {
.storagebits = 16,
.endianness = IIO_LE,
},
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
.modified = 0,
},
{
@@ -169,7 +175,7 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = {
},
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) |
- BIT(IIO_CHAN_INFO_OFFSET),
+ BIT(IIO_CHAN_INFO_SCALE),
.modified = 0,
},
IIO_CHAN_SOFT_TIMESTAMP(1)
@@ -204,11 +210,14 @@ static const struct st_sensor_settings st_press_sensors_settings[] = {
.addr = ST_PRESS_LPS331AP_FS_ADDR,
.mask = ST_PRESS_LPS331AP_FS_MASK,
.fs_avl = {
+ /*
+ * Pressure and temperature sensitivity values
+ * as defined in table 3 of LPS331AP datasheet.
+ */
[0] = {
.num = ST_PRESS_FS_AVL_1260MB,
- .value = ST_PRESS_LPS331AP_FS_AVL_1260_VAL,
- .gain = ST_PRESS_LPS331AP_FS_AVL_1260_GAIN,
- .gain2 = ST_PRESS_LPS331AP_FS_AVL_TEMP_GAIN,
+ .gain = ST_PRESS_KPASCAL_NANO_SCALE,
+ .gain2 = ST_PRESS_LSB_PER_CELSIUS,
},
},
},
@@ -248,7 +257,17 @@ static const struct st_sensor_settings st_press_sensors_settings[] = {
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.fs = {
- .addr = 0,
+ .fs_avl = {
+ /*
+ * Pressure and temperature resolution values
+ * as defined in table 3 of LPS001WP datasheet.
+ */
+ [0] = {
+ .num = ST_PRESS_FS_AVL_1100MB,
+ .gain = ST_PRESS_LPS001WP_FS_AVL_PRESS_GAIN,
+ .gain2 = ST_PRESS_LPS001WP_LSB_PER_CELSIUS,
+ },
+ },
},
.bdu = {
.addr = ST_PRESS_LPS001WP_BDU_ADDR,
@@ -285,14 +304,15 @@ static const struct st_sensor_settings st_press_sensors_settings[] = {
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.fs = {
- .addr = ST_PRESS_LPS25H_FS_ADDR,
- .mask = ST_PRESS_LPS25H_FS_MASK,
.fs_avl = {
+ /*
+ * Pressure and temperature sensitivity values
+ * as defined in table 3 of LPS25H datasheet.
+ */
[0] = {
.num = ST_PRESS_FS_AVL_1260MB,
- .value = ST_PRESS_LPS25H_FS_AVL_1260_VAL,
- .gain = ST_PRESS_LPS25H_FS_AVL_1260_GAIN,
- .gain2 = ST_PRESS_LPS25H_FS_AVL_TEMP_GAIN,
+ .gain = ST_PRESS_KPASCAL_NANO_SCALE,
+ .gain2 = ST_PRESS_LSB_PER_CELSIUS,
},
},
},
@@ -346,26 +366,26 @@ static int st_press_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- *val = 0;
-
switch (ch->type) {
case IIO_PRESSURE:
+ *val = 0;
*val2 = press_data->current_fullscale->gain;
- break;
+ return IIO_VAL_INT_PLUS_NANO;
case IIO_TEMP:
+ *val = MCELSIUS_PER_CELSIUS;
*val2 = press_data->current_fullscale->gain2;
- break;
+ return IIO_VAL_FRACTIONAL;
default:
err = -EINVAL;
goto read_error;
}
- return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_OFFSET:
switch (ch->type) {
case IIO_TEMP:
- *val = 425;
- *val2 = 10;
+ *val = ST_PRESS_MILLI_CELSIUS_OFFSET *
+ press_data->current_fullscale->gain2;
+ *val2 = MCELSIUS_PER_CELSIUS;
break;
default:
err = -EINVAL;
diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c
index f4d29d5dbd5f..e2f926cdcad2 100644
--- a/drivers/iio/proximity/as3935.c
+++ b/drivers/iio/proximity/as3935.c
@@ -64,6 +64,7 @@ struct as3935_state {
struct delayed_work work;
u32 tune_cap;
+ u8 buffer[16]; /* 8-bit data + 56-bit padding + 64-bit timestamp */
u8 buf[2] ____cacheline_aligned;
};
@@ -72,7 +73,8 @@ static const struct iio_chan_spec as3935_channels[] = {
.type = IIO_PROXIMITY,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) |
- BIT(IIO_CHAN_INFO_PROCESSED),
+ BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_SCALE),
.scan_index = 0,
.scan_type = {
.sign = 'u',
@@ -181,7 +183,12 @@ static int as3935_read_raw(struct iio_dev *indio_dev,
/* storm out of range */
if (*val == AS3935_DATA_MASK)
return -EINVAL;
- *val *= 1000;
+
+ if (m == IIO_CHAN_INFO_PROCESSED)
+ *val *= 1000;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 1000;
break;
default:
return -EINVAL;
@@ -206,10 +213,10 @@ static irqreturn_t as3935_trigger_handler(int irq, void *private)
ret = as3935_read(st, AS3935_DATA, &val);
if (ret)
goto err_read;
- val &= AS3935_DATA_MASK;
- val *= 1000;
- iio_push_to_buffers_with_timestamp(indio_dev, &val, pf->timestamp);
+ st->buffer[0] = val & AS3935_DATA_MASK;
+ iio_push_to_buffers_with_timestamp(indio_dev, &st->buffer,
+ pf->timestamp);
err_read:
iio_trigger_notify_done(indio_dev->trig);
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index d6d2b3582910..4d8e7f18a9af 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -3430,14 +3430,14 @@ static int cm_establish(struct ib_cm_id *cm_id)
work->cm_event.event = IB_CM_USER_ESTABLISHED;
/* Check if the device started its remove_one */
- spin_lock_irq(&cm.lock);
+ spin_lock_irqsave(&cm.lock, flags);
if (!cm_dev->going_down) {
queue_delayed_work(cm.wq, &work->work, 0);
} else {
kfree(work);
ret = -ENODEV;
}
- spin_unlock_irq(&cm.lock);
+ spin_unlock_irqrestore(&cm.lock, flags);
out:
return ret;
diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c
index 6b4e8a008bc0..564adf3116e8 100644
--- a/drivers/infiniband/core/ucm.c
+++ b/drivers/infiniband/core/ucm.c
@@ -48,6 +48,7 @@
#include <asm/uaccess.h>
+#include <rdma/ib.h>
#include <rdma/ib_cm.h>
#include <rdma/ib_user_cm.h>
#include <rdma/ib_marshall.h>
@@ -1103,6 +1104,9 @@ static ssize_t ib_ucm_write(struct file *filp, const char __user *buf,
struct ib_ucm_cmd_hdr hdr;
ssize_t result;
+ if (WARN_ON_ONCE(!ib_safe_file_access(filp)))
+ return -EACCES;
+
if (len < sizeof(hdr))
return -EINVAL;
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index 8b5a934e1133..886f61ea6cc7 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -1574,6 +1574,9 @@ static ssize_t ucma_write(struct file *filp, const char __user *buf,
struct rdma_ucm_cmd_hdr hdr;
ssize_t ret;
+ if (WARN_ON_ONCE(!ib_safe_file_access(filp)))
+ return -EACCES;
+
if (len < sizeof(hdr))
return -EINVAL;
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index e3ef28861be6..24f3ca2c4ad7 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -48,6 +48,8 @@
#include <asm/uaccess.h>
+#include <rdma/ib.h>
+
#include "uverbs.h"
MODULE_AUTHOR("Roland Dreier");
@@ -682,6 +684,9 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
int srcu_key;
ssize_t ret;
+ if (WARN_ON_ONCE(!ib_safe_file_access(filp)))
+ return -EACCES;
+
if (count < sizeof hdr)
return -EINVAL;
diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c
index de9cd6901752..bc147582bed9 100644
--- a/drivers/infiniband/hw/cxgb4/cq.c
+++ b/drivers/infiniband/hw/cxgb4/cq.c
@@ -162,7 +162,7 @@ static int create_cq(struct c4iw_rdev *rdev, struct t4_cq *cq,
cq->bar2_va = c4iw_bar2_addrs(rdev, cq->cqid, T4_BAR2_QTYPE_INGRESS,
&cq->bar2_qid,
user ? &cq->bar2_pa : NULL);
- if (user && !cq->bar2_va) {
+ if (user && !cq->bar2_pa) {
pr_warn(MOD "%s: cqid %u not in BAR2 range.\n",
pci_name(rdev->lldi.pdev), cq->cqid);
ret = -EINVAL;
diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c
index aa515afee724..53aa7515f542 100644
--- a/drivers/infiniband/hw/cxgb4/qp.c
+++ b/drivers/infiniband/hw/cxgb4/qp.c
@@ -185,6 +185,10 @@ void __iomem *c4iw_bar2_addrs(struct c4iw_rdev *rdev, unsigned int qid,
if (pbar2_pa)
*pbar2_pa = (rdev->bar2_pa + bar2_qoffset) & PAGE_MASK;
+
+ if (is_t4(rdev->lldi.adapter_type))
+ return NULL;
+
return rdev->bar2_kva + bar2_qoffset;
}
@@ -270,7 +274,7 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
/*
* User mode must have bar2 access.
*/
- if (user && (!wq->sq.bar2_va || !wq->rq.bar2_va)) {
+ if (user && (!wq->sq.bar2_pa || !wq->rq.bar2_pa)) {
pr_warn(MOD "%s: sqid %u or rqid %u not in BAR2 range.\n",
pci_name(rdev->lldi.pdev), wq->sq.qid, wq->rq.qid);
goto free_dma;
diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c
index 86af71351d9a..06da56bda201 100644
--- a/drivers/infiniband/hw/mlx4/ah.c
+++ b/drivers/infiniband/hw/mlx4/ah.c
@@ -47,6 +47,7 @@ static struct ib_ah *create_ib_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr,
ah->av.ib.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24));
ah->av.ib.g_slid = ah_attr->src_path_bits;
+ ah->av.ib.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28);
if (ah_attr->ah_flags & IB_AH_GRH) {
ah->av.ib.g_slid |= 0x80;
ah->av.ib.gid_index = ah_attr->grh.sgid_index;
@@ -64,7 +65,6 @@ static struct ib_ah *create_ib_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr,
!(1 << ah->av.ib.stat_rate & dev->caps.stat_rate_support))
--ah->av.ib.stat_rate;
}
- ah->av.ib.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28);
return &ah->ibah;
}
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index c4e091528390..fd17443aeacd 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -273,7 +273,7 @@ static int mlx5_ib_query_device(struct ib_device *ibdev,
sizeof(struct mlx5_wqe_ctrl_seg)) /
sizeof(struct mlx5_wqe_data_seg);
props->max_sge = min(max_rq_sg, max_sq_sg);
- props->max_sge_rd = props->max_sge;
+ props->max_sge_rd = MLX5_MAX_SGE_RD;
props->max_cq = 1 << MLX5_CAP_GEN(mdev, log_max_cq);
props->max_cqe = (1 << MLX5_CAP_GEN(mdev, log_max_cq_sz)) - 1;
props->max_mr = 1 << MLX5_CAP_GEN(mdev, log_max_mkey);
@@ -405,8 +405,8 @@ static int mlx5_query_hca_port(struct ib_device *ibdev, u8 port,
struct mlx5_ib_dev *dev = to_mdev(ibdev);
struct mlx5_core_dev *mdev = dev->mdev;
struct mlx5_hca_vport_context *rep;
- int max_mtu;
- int oper_mtu;
+ u16 max_mtu;
+ u16 oper_mtu;
int err;
u8 ib_link_width_oper;
u8 vl_hw_cap;
diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c
index e449e394963f..24f4a782e0f4 100644
--- a/drivers/infiniband/hw/qib/qib_file_ops.c
+++ b/drivers/infiniband/hw/qib/qib_file_ops.c
@@ -45,6 +45,8 @@
#include <linux/export.h>
#include <linux/uio.h>
+#include <rdma/ib.h>
+
#include "qib.h"
#include "qib_common.h"
#include "qib_user_sdma.h"
@@ -2067,6 +2069,9 @@ static ssize_t qib_write(struct file *fp, const char __user *data,
ssize_t ret = 0;
void *dest;
+ if (WARN_ON_ONCE(!ib_safe_file_access(fp)))
+ return -EACCES;
+
if (count < sizeof(cmd.type)) {
ret = -EINVAL;
goto bail;
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 3db9a659719b..5f0f4fc58f43 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -1519,7 +1519,7 @@ static int srp_map_idb(struct srp_rdma_ch *ch, struct srp_request *req,
if (dev->use_fast_reg) {
state.sg = idb_sg;
- sg_set_buf(idb_sg, req->indirect_desc, idb_len);
+ sg_init_one(idb_sg, req->indirect_desc, idb_len);
idb_sg->dma_address = req->indirect_dma_addr; /* hack! */
#ifdef CONFIG_NEED_SG_DMA_LENGTH
idb_sg->dma_length = idb_sg->length; /* hack^2 */
diff --git a/drivers/input/misc/hbtp_input.c b/drivers/input/misc/hbtp_input.c
index 519e3db4d73e..f94ecf02d9cb 100644
--- a/drivers/input/misc/hbtp_input.c
+++ b/drivers/input/misc/hbtp_input.c
@@ -23,6 +23,12 @@
#include <linux/regulator/consumer.h>
#include <uapi/linux/hbtp_input.h>
#include "../input-compat.h"
+#include <linux/ktime.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
#if defined(CONFIG_FB)
#include <linux/notifier.h>
@@ -32,6 +38,10 @@
#define HBTP_INPUT_NAME "hbtp_input"
#define DISP_COORDS_SIZE 2
+#define HBTP_PINCTRL_VALID_STATE_CNT (2)
+#define HBTP_HOLD_DURATION_US (10)
+#define HBTP_PINCTRL_DDIC_SEQ_NUM (4)
+
struct hbtp_data {
struct platform_device *pdev;
struct input_dev *input_dev;
@@ -41,6 +51,20 @@ struct hbtp_data {
#if defined(CONFIG_FB)
struct notifier_block fb_notif;
#endif
+ struct pinctrl *ts_pinctrl;
+ struct pinctrl_state *gpio_state_active;
+ struct pinctrl_state *gpio_state_suspend;
+ struct pinctrl_state *ddic_rst_state_active;
+ struct pinctrl_state *ddic_rst_state_suspend;
+ u32 ts_pinctrl_seq_delay;
+ u32 ddic_pinctrl_seq_delay[HBTP_PINCTRL_DDIC_SEQ_NUM];
+ u32 fb_resume_seq_delay;
+ bool lcd_on;
+ bool power_suspended;
+ bool power_sync_enabled;
+ bool power_sig_enabled;
+ struct completion power_resume_sig;
+ struct completion power_suspend_sig;
struct regulator *vcc_ana;
struct regulator *vcc_dig;
int afe_load_ua;
@@ -59,31 +83,68 @@ struct hbtp_data {
bool override_disp_coords;
bool manage_afe_power_ana;
bool manage_power_dig;
+ u32 power_on_delay;
+ u32 power_off_delay;
+ bool manage_pin_ctrl;
};
static struct hbtp_data *hbtp;
#if defined(CONFIG_FB)
+static int hbtp_fb_suspend(struct hbtp_data *ts);
+static int hbtp_fb_early_resume(struct hbtp_data *ts);
+static int hbtp_fb_resume(struct hbtp_data *ts);
+#endif
+
+#if defined(CONFIG_FB)
static int fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
int blank;
struct fb_event *evdata = data;
struct hbtp_data *hbtp_data =
- container_of(self, struct hbtp_data, fb_notif);
- char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL};
+ container_of(self, struct hbtp_data, fb_notif);
- if (evdata && evdata->data && event == FB_EVENT_BLANK &&
- hbtp_data && hbtp_data->input_dev) {
+ if (evdata && evdata->data && hbtp_data &&
+ (event == FB_EARLY_EVENT_BLANK ||
+ event == FB_R_EARLY_EVENT_BLANK)) {
blank = *(int *)(evdata->data);
- if (blank == FB_BLANK_UNBLANK)
- kobject_uevent_env(&hbtp_data->input_dev->dev.kobj,
- KOBJ_ONLINE, envp);
- else if (blank == FB_BLANK_POWERDOWN)
- kobject_uevent_env(&hbtp_data->input_dev->dev.kobj,
- KOBJ_OFFLINE, envp);
+ if (event == FB_EARLY_EVENT_BLANK) {
+ if (blank == FB_BLANK_UNBLANK) {
+ pr_debug("%s: receives EARLY_BLANK:UNBLANK\n",
+ __func__);
+ hbtp_data->lcd_on = true;
+ hbtp_fb_early_resume(hbtp_data);
+ } else if (blank == FB_BLANK_POWERDOWN) {
+ pr_debug("%s: receives EARLY_BLANK:POWERDOWN\n",
+ __func__);
+ hbtp_data->lcd_on = false;
+ }
+ } else if (event == FB_R_EARLY_EVENT_BLANK) {
+ if (blank == FB_BLANK_UNBLANK) {
+ pr_debug("%s: receives R_EARLY_BALNK:UNBLANK\n",
+ __func__);
+ hbtp_data->lcd_on = false;
+ hbtp_fb_suspend(hbtp_data);
+ } else if (blank == FB_BLANK_POWERDOWN) {
+ pr_debug("%s: receives R_EARLY_BALNK:POWERDOWN\n",
+ __func__);
+ hbtp_data->lcd_on = true;
+ }
+ }
}
+ if (evdata && evdata->data && hbtp_data &&
+ event == FB_EVENT_BLANK) {
+ blank = *(int *)(evdata->data);
+ if (blank == FB_BLANK_POWERDOWN) {
+ pr_debug("%s: receives BLANK:POWERDOWN\n", __func__);
+ hbtp_fb_suspend(hbtp_data);
+ } else if (blank == FB_BLANK_UNBLANK) {
+ pr_debug("%s: receives BLANK:UNBLANK\n", __func__);
+ hbtp_fb_resume(hbtp_data);
+ }
+ }
return 0;
}
#endif
@@ -111,6 +172,8 @@ static int hbtp_input_release(struct inode *inode, struct file *file)
return -ENOTTY;
}
hbtp->count--;
+ if (hbtp->power_sig_enabled)
+ hbtp->power_sig_enabled = false;
mutex_unlock(&hbtp->mutex);
return 0;
@@ -278,6 +341,14 @@ static int hbtp_pdev_power_on(struct hbtp_data *hbtp, bool on)
return ret;
}
}
+
+ if (hbtp->power_on_delay) {
+ pr_debug("%s: power-on-delay = %u\n", __func__,
+ hbtp->power_on_delay);
+ usleep_range(hbtp->power_on_delay,
+ hbtp->power_on_delay + HBTP_HOLD_DURATION_US);
+ }
+
if (hbtp->vcc_dig) {
ret = reg_set_load_check(hbtp->vcc_dig,
hbtp->dig_load_ua);
@@ -299,17 +370,171 @@ static int hbtp_pdev_power_on(struct hbtp_data *hbtp, bool on)
return 0;
reg_off:
- if (hbtp->vcc_ana) {
- reg_set_load_check(hbtp->vcc_ana, 0);
- regulator_disable(hbtp->vcc_ana);
- }
if (hbtp->vcc_dig) {
reg_set_load_check(hbtp->vcc_dig, 0);
regulator_disable(hbtp->vcc_dig);
}
+
+ if (hbtp->power_off_delay) {
+ pr_debug("%s: power-off-delay = %u\n", __func__,
+ hbtp->power_off_delay);
+ usleep_range(hbtp->power_off_delay,
+ hbtp->power_off_delay + HBTP_HOLD_DURATION_US);
+ }
+
+ if (hbtp->vcc_ana) {
+ reg_set_load_check(hbtp->vcc_ana, 0);
+ regulator_disable(hbtp->vcc_ana);
+ }
return 0;
}
+static int hbtp_gpio_select(struct hbtp_data *data, bool on)
+{
+ struct pinctrl_state *pins_state;
+ int ret = 0;
+
+ pins_state = on ? data->gpio_state_active : data->gpio_state_suspend;
+ if (!IS_ERR_OR_NULL(pins_state)) {
+ ret = pinctrl_select_state(data->ts_pinctrl, pins_state);
+ if (ret) {
+ dev_err(&data->pdev->dev,
+ "can not set %s pins\n",
+ on ? "ts_active" : "ts_suspend");
+ return ret;
+ }
+
+ if (on) {
+ if (data->ts_pinctrl_seq_delay) {
+ usleep_range(data->ts_pinctrl_seq_delay,
+ data->ts_pinctrl_seq_delay +
+ HBTP_HOLD_DURATION_US);
+ dev_dbg(&data->pdev->dev, "ts_pinctrl_seq_delay = %u\n",
+ data->ts_pinctrl_seq_delay);
+ }
+ }
+ } else {
+ dev_warn(&data->pdev->dev,
+ "not a valid '%s' pinstate\n",
+ on ? "ts_active" : "ts_suspend");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int hbtp_ddic_rst_select(struct hbtp_data *data, bool on)
+{
+ struct pinctrl_state *active, *suspend;
+ int ret = 0;
+
+ active = data->ddic_rst_state_active;
+ if (IS_ERR_OR_NULL(active)) {
+ dev_warn(&data->pdev->dev,
+ "not a valid ddic_rst_active pinstate\n");
+ return ret;
+ }
+
+ suspend = data->ddic_rst_state_suspend;
+ if (IS_ERR_OR_NULL(suspend)) {
+ dev_warn(&data->pdev->dev,
+ "not a valid ddic_rst_suspend pinstate\n");
+ return ret;
+ }
+
+ if (on) {
+ if (data->ddic_pinctrl_seq_delay[0]) {
+ usleep_range(data->ddic_pinctrl_seq_delay[0],
+ data->ddic_pinctrl_seq_delay[0] +
+ HBTP_HOLD_DURATION_US);
+ dev_dbg(&data->pdev->dev, "ddic_seq_delay[0] = %u\n",
+ data->ddic_pinctrl_seq_delay[0]);
+ }
+
+ ret = pinctrl_select_state(data->ts_pinctrl, active);
+ if (ret) {
+ dev_err(&data->pdev->dev,
+ "can not set ddic_rst_active pins\n");
+ return ret;
+ }
+ if (data->ddic_pinctrl_seq_delay[1]) {
+ usleep_range(data->ddic_pinctrl_seq_delay[1],
+ data->ddic_pinctrl_seq_delay[1] +
+ HBTP_HOLD_DURATION_US);
+ dev_dbg(&data->pdev->dev, "ddic_seq_delay[1] = %u\n",
+ data->ddic_pinctrl_seq_delay[1]);
+ }
+ ret = pinctrl_select_state(data->ts_pinctrl, suspend);
+ if (ret) {
+ dev_err(&data->pdev->dev,
+ "can not set ddic_rst_suspend pins\n");
+ return ret;
+ }
+
+ if (data->ddic_pinctrl_seq_delay[2]) {
+ usleep_range(data->ddic_pinctrl_seq_delay[2],
+ data->ddic_pinctrl_seq_delay[2] +
+ HBTP_HOLD_DURATION_US);
+ dev_dbg(&data->pdev->dev, "ddic_seq_delay[2] = %u\n",
+ data->ddic_pinctrl_seq_delay[2]);
+ }
+
+ ret = pinctrl_select_state(data->ts_pinctrl, active);
+ if (ret) {
+ dev_err(&data->pdev->dev,
+ "can not set ddic_rst_active pins\n");
+ return ret;
+ }
+
+ if (data->ddic_pinctrl_seq_delay[3]) {
+ usleep_range(data->ddic_pinctrl_seq_delay[3],
+ data->ddic_pinctrl_seq_delay[3] +
+ HBTP_HOLD_DURATION_US);
+ dev_dbg(&data->pdev->dev, "ddic_seq_delay[3] = %u\n",
+ data->ddic_pinctrl_seq_delay[3]);
+ }
+ } else {
+ ret = pinctrl_select_state(data->ts_pinctrl, suspend);
+ if (ret) {
+ dev_err(&data->pdev->dev,
+ "can not set ddic_rst_suspend pins\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int hbtp_pinctrl_enable(struct hbtp_data *ts, bool on)
+{
+ int rc = 0;
+
+ if (!ts->manage_pin_ctrl) {
+ pr_info("%s: pinctrl info is not available\n", __func__);
+ return 0;
+ }
+
+ if (!on)
+ goto pinctrl_suspend;
+
+ rc = hbtp_gpio_select(ts, true);
+ if (rc < 0)
+ return -EINVAL;
+
+ rc = hbtp_ddic_rst_select(ts, true);
+ if (rc < 0)
+ goto err_ddic_rst_pinctrl_enable;
+
+ return rc;
+
+pinctrl_suspend:
+ if (ts->ddic_rst_state_suspend)
+ hbtp_ddic_rst_select(ts, true);
+err_ddic_rst_pinctrl_enable:
+ hbtp_gpio_select(ts, false);
+ return rc;
+}
+
static long hbtp_input_ioctl_handler(struct file *file, unsigned int cmd,
unsigned long arg, void __user *p)
{
@@ -318,6 +543,8 @@ static long hbtp_input_ioctl_handler(struct file *file, unsigned int cmd,
struct hbtp_input_absinfo absinfo[ABS_MT_LAST - ABS_MT_FIRST + 1];
struct hbtp_input_key key_data;
enum hbtp_afe_power_cmd power_cmd;
+ enum hbtp_afe_signal afe_signal;
+ enum hbtp_afe_power_ctrl afe_power_ctrl;
switch (cmd) {
case HBTP_SET_ABSPARAM:
@@ -408,6 +635,112 @@ static long hbtp_input_ioctl_handler(struct file *file, unsigned int cmd,
input_sync(hbtp->input_dev);
break;
+ case HBTP_SET_SYNCSIGNAL:
+ if (!hbtp || !hbtp->input_dev) {
+ pr_err("%s: The input device hasn't been created\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ if (!hbtp->power_sig_enabled) {
+ pr_err("%s: power_signal is not enabled", __func__);
+ return -EPERM;
+ }
+
+ if (copy_from_user(&afe_signal, (void *)arg,
+ sizeof(enum hbtp_afe_signal))) {
+ pr_err("%s: Error copying data\n", __func__);
+ return -EFAULT;
+ }
+
+ pr_debug("%s: receives %d signal\n", __func__, afe_signal);
+
+ switch (afe_signal) {
+ case HBTP_AFE_SIGNAL_ON_RESUME:
+ mutex_lock(&hbtp->mutex);
+ if (!hbtp->power_suspended) {
+ complete(&hbtp->power_resume_sig);
+ } else {
+ pr_err("%s: resume signal in wrong state\n",
+ __func__);
+ }
+ mutex_unlock(&hbtp->mutex);
+ break;
+ case HBTP_AFE_SIGNAL_ON_SUSPEND:
+ mutex_lock(&hbtp->mutex);
+ if (hbtp->power_suspended) {
+ complete(&hbtp->power_suspend_sig);
+ } else {
+ pr_err("%s: suspend signal in wrong state\n",
+ __func__);
+ }
+ mutex_unlock(&hbtp->mutex);
+ break;
+ default:
+ pr_err("%s: Unsupported command for afe signal, %d\n",
+ __func__, afe_signal);
+ return -EINVAL;
+ }
+ break;
+ case HBTP_SET_POWER_CTRL:
+ if (!hbtp || !hbtp->input_dev) {
+ pr_err("%s: The input device hasn't been created\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&afe_power_ctrl, (void *)arg,
+ sizeof(enum hbtp_afe_power_ctrl))) {
+ pr_err("%s: Error copying data\n", __func__);
+ return -EFAULT;
+ }
+ switch (afe_power_ctrl) {
+ case HBTP_AFE_POWER_ENABLE_SYNC:
+ pr_debug("%s: power_sync is enabled\n", __func__);
+ if (!hbtp->manage_pin_ctrl || !hbtp->manage_power_dig ||
+ !hbtp->manage_afe_power_ana) {
+ pr_err("%s: power/pin is not available\n",
+ __func__);
+ return -EFAULT;
+ }
+ mutex_lock(&hbtp->mutex);
+ error = hbtp_pdev_power_on(hbtp, true);
+ if (error) {
+ mutex_unlock(&hbtp->mutex);
+ pr_err("%s: failed to power on\n", __func__);
+ return error;
+ }
+ error = hbtp_pinctrl_enable(hbtp, true);
+ if (error) {
+ mutex_unlock(&hbtp->mutex);
+ pr_err("%s: failed to enable pins\n", __func__);
+ hbtp_pdev_power_on(hbtp, false);
+ return error;
+ }
+ hbtp->power_sync_enabled = true;
+ mutex_unlock(&hbtp->mutex);
+ pr_debug("%s: power_sync option is enabled\n",
+ __func__);
+ break;
+ case HBTP_AFE_POWER_ENABLE_SYNC_SIGNAL:
+ if (!hbtp->power_sync_enabled) {
+ pr_err("%s: power_sync is not enabled\n",
+ __func__);
+ return -EFAULT;
+ }
+ mutex_lock(&hbtp->mutex);
+ init_completion(&hbtp->power_resume_sig);
+ init_completion(&hbtp->power_suspend_sig);
+ hbtp->power_sig_enabled = true;
+ mutex_unlock(&hbtp->mutex);
+ pr_err("%s: sync_signal option is enabled\n", __func__);
+ break;
+ default:
+ pr_err("%s: unsupported power ctrl, %d\n",
+ __func__, afe_power_ctrl);
+ return -EINVAL;
+ }
+ break;
default:
pr_err("%s: Unsupported ioctl command %u\n", __func__, cmd);
error = -EINVAL;
@@ -514,6 +847,25 @@ static int hbtp_parse_dt(struct device *dev)
}
}
+ if (hbtp->manage_power_dig && hbtp->manage_afe_power_ana) {
+ rc = of_property_read_u32(np,
+ "qcom,afe-power-on-delay-us", &temp_val);
+ if (!rc)
+ hbtp->power_on_delay = (u32)temp_val;
+ else
+ dev_info(dev, "Power-On Delay is not specified\n");
+
+ rc = of_property_read_u32(np,
+ "qcom,afe-power-off-delay-us", &temp_val);
+ if (!rc)
+ hbtp->power_off_delay = (u32)temp_val;
+ else
+ dev_info(dev, "Power-Off Delay is not specified\n");
+
+ dev_dbg(dev, "power-on-delay = %u, power-off-delay = %u\n",
+ hbtp->power_on_delay, hbtp->power_off_delay);
+ }
+
prop = of_find_property(np, "qcom,display-resolution", NULL);
if (prop != NULL) {
if (!prop->value)
@@ -605,11 +957,292 @@ static int hbtp_parse_dt(struct device *dev)
}
#endif
+static int hbtp_pinctrl_init(struct hbtp_data *data)
+{
+ const char *statename;
+ int rc;
+ int state_cnt, i;
+ struct device_node *np = data->pdev->dev.of_node;
+ bool pinctrl_state_act_found = false;
+ bool pinctrl_state_sus_found = false;
+ bool pinctrl_ddic_act_found = false;
+ bool pinctrl_ddic_sus_found = false;
+ int count = 0;
+
+ data->ts_pinctrl = devm_pinctrl_get(&(data->pdev->dev));
+ if (IS_ERR_OR_NULL(data->ts_pinctrl)) {
+ dev_err(&data->pdev->dev,
+ "Target does not use pinctrl\n");
+ rc = PTR_ERR(data->ts_pinctrl);
+ data->ts_pinctrl = NULL;
+ return rc;
+ }
+
+ state_cnt = of_property_count_strings(np, "pinctrl-names");
+ if (state_cnt < HBTP_PINCTRL_VALID_STATE_CNT) {
+ /*
+ *if pinctrl names are not available then,
+ *power_sync can't be enabled
+ */
+ dev_info(&data->pdev->dev,
+ "pinctrl names are not available\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ for (i = 0; i < state_cnt; i++) {
+ rc = of_property_read_string_index(np,
+ "pinctrl-names", i, &statename);
+ if (rc) {
+ dev_err(&data->pdev->dev,
+ "failed to read pinctrl states by index\n");
+ goto error;
+ }
+
+ if (!strcmp(statename, "pmx_ts_active")) {
+ data->gpio_state_active
+ = pinctrl_lookup_state(data->ts_pinctrl,
+ statename);
+ if (IS_ERR_OR_NULL(data->gpio_state_active)) {
+ dev_err(&data->pdev->dev,
+ "Can not get ts default state\n");
+ rc = PTR_ERR(data->gpio_state_active);
+ goto error;
+ }
+ pinctrl_state_act_found = true;
+ } else if (!strcmp(statename, "pmx_ts_suspend")) {
+ data->gpio_state_suspend
+ = pinctrl_lookup_state(data->ts_pinctrl,
+ statename);
+ if (IS_ERR_OR_NULL(data->gpio_state_suspend)) {
+ dev_err(&data->pdev->dev,
+ "Can not get ts sleep state\n");
+ rc = PTR_ERR(data->gpio_state_suspend);
+ goto error;
+ }
+ pinctrl_state_sus_found = true;
+ } else if (!strcmp(statename, "ddic_rst_active")) {
+ data->ddic_rst_state_active
+ = pinctrl_lookup_state(data->ts_pinctrl,
+ statename);
+ if (IS_ERR(data->ddic_rst_state_active)) {
+ dev_err(&data->pdev->dev,
+ "Can not get DDIC rst act state\n");
+ rc = PTR_ERR(data->ddic_rst_state_active);
+ goto error;
+ }
+ pinctrl_ddic_act_found = true;
+ } else if (!strcmp(statename, "ddic_rst_suspend")) {
+ data->ddic_rst_state_suspend
+ = pinctrl_lookup_state(data->ts_pinctrl,
+ statename);
+ if (IS_ERR(data->ddic_rst_state_suspend)) {
+ dev_err(&data->pdev->dev,
+ "Can not get DDIC rst sleep state\n");
+ rc = PTR_ERR(data->ddic_rst_state_suspend);
+ goto error;
+ }
+ pinctrl_ddic_sus_found = true;
+ } else {
+ dev_err(&data->pdev->dev, "invalid pinctrl state\n");
+ rc = -EINVAL;
+ goto error;
+ }
+ }
+
+ if (!pinctrl_state_act_found || !pinctrl_state_sus_found) {
+ dev_err(&data->pdev->dev,
+ "missing required pinctrl states\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (of_property_read_u32(np, "qcom,pmx-ts-on-seq-delay-us",
+ &data->ts_pinctrl_seq_delay)) {
+ dev_warn(&data->pdev->dev, "Can not find ts seq delay\n");
+ }
+
+ if (of_property_read_u32(np, "qcom,fb-resume-delay-us",
+ &data->fb_resume_seq_delay)) {
+ dev_warn(&data->pdev->dev, "Can not find fb resume seq delay\n");
+ }
+
+ if (pinctrl_ddic_act_found && pinctrl_ddic_sus_found) {
+ count = of_property_count_u32_elems(np,
+ "qcom,ddic-rst-on-seq-delay-us");
+ if (count == HBTP_PINCTRL_DDIC_SEQ_NUM) {
+ of_property_read_u32_array(np,
+ "qcom,ddic-rst-on-seq-delay-us",
+ data->ddic_pinctrl_seq_delay, count);
+ } else {
+ dev_err(&data->pdev->dev, "count(%u) is not same as %u\n",
+ (u32)count, HBTP_PINCTRL_DDIC_SEQ_NUM);
+ }
+ } else {
+ dev_err(&data->pdev->dev, "ddic pinctrl act/sus not found\n");
+ }
+
+ data->manage_pin_ctrl = true;
+ return 0;
+
+error:
+ devm_pinctrl_put(data->ts_pinctrl);
+ data->ts_pinctrl = NULL;
+ return rc;
+}
+
+static int hbtp_fb_suspend(struct hbtp_data *ts)
+{
+ int rc;
+ char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL};
+
+ mutex_lock(&hbtp->mutex);
+ if (ts->pdev && ts->power_sync_enabled) {
+ pr_debug("%s: power_sync is enabled\n", __func__);
+ if (ts->power_suspended) {
+ mutex_unlock(&hbtp->mutex);
+ pr_err("%s: power is not resumed\n", __func__);
+ return 0;
+ }
+ rc = hbtp_pinctrl_enable(ts, false);
+ if (rc) {
+ pr_err("%s: failed to disable GPIO pins\n", __func__);
+ goto err_pin_disable;
+ }
+
+ rc = hbtp_pdev_power_on(ts, false);
+ if (rc) {
+ pr_err("%s: failed to disable power\n", __func__);
+ goto err_power_disable;
+ }
+ ts->power_suspended = true;
+ }
+
+ if (ts->input_dev) {
+ kobject_uevent_env(&ts->input_dev->dev.kobj,
+ KOBJ_OFFLINE, envp);
+
+ if (ts->power_sig_enabled) {
+ pr_debug("%s: power_sig is enabled, wait for signal\n",
+ __func__);
+ mutex_unlock(&hbtp->mutex);
+ rc = wait_for_completion_interruptible(
+ &hbtp->power_suspend_sig);
+ if (rc != 0) {
+ pr_err("%s: wait for suspend is interrupted\n",
+ __func__);
+ }
+ mutex_lock(&hbtp->mutex);
+ pr_debug("%s: Wait is done for suspend\n", __func__);
+ } else {
+ pr_debug("%s: power_sig is NOT enabled", __func__);
+ }
+ }
+
+ mutex_unlock(&hbtp->mutex);
+ return 0;
+err_power_disable:
+ hbtp_pinctrl_enable(ts, true);
+err_pin_disable:
+ mutex_unlock(&hbtp->mutex);
+ return rc;
+}
+
+static int hbtp_fb_early_resume(struct hbtp_data *ts)
+{
+ char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL};
+ int rc;
+
+ mutex_lock(&hbtp->mutex);
+
+ pr_debug("%s: hbtp_fb_early_resume\n", __func__);
+
+ if (ts->pdev && ts->power_sync_enabled) {
+ pr_debug("%s: power_sync is enabled\n", __func__);
+ if (!ts->power_suspended) {
+ pr_err("%s: power is not suspended\n", __func__);
+ mutex_unlock(&hbtp->mutex);
+ return 0;
+ }
+ rc = hbtp_pdev_power_on(ts, true);
+ if (rc) {
+ pr_err("%s: failed to enable panel power\n", __func__);
+ goto err_power_on;
+ }
+
+ rc = hbtp_pinctrl_enable(ts, true);
+
+ if (rc) {
+ pr_err("%s: failed to enable pin\n", __func__);
+ goto err_pin_enable;
+ }
+
+ ts->power_suspended = false;
+
+ if (ts->input_dev) {
+
+ kobject_uevent_env(&ts->input_dev->dev.kobj,
+ KOBJ_ONLINE, envp);
+
+ if (ts->power_sig_enabled) {
+ pr_err("%s: power_sig is enabled, wait for signal\n",
+ __func__);
+ mutex_unlock(&hbtp->mutex);
+ rc = wait_for_completion_interruptible(
+ &hbtp->power_resume_sig);
+ if (rc != 0) {
+ pr_err("%s: wait for resume is interrupted\n",
+ __func__);
+ }
+ mutex_lock(&hbtp->mutex);
+ pr_debug("%s: wait is done\n", __func__);
+ } else {
+ pr_debug("%s: power_sig is NOT enabled\n",
+ __func__);
+ }
+
+ if (ts->fb_resume_seq_delay) {
+ usleep_range(ts->fb_resume_seq_delay,
+ ts->fb_resume_seq_delay +
+ HBTP_HOLD_DURATION_US);
+ pr_err("%s: fb_resume_seq_delay = %u\n",
+ __func__, ts->fb_resume_seq_delay);
+ }
+ }
+ }
+ mutex_unlock(&hbtp->mutex);
+ return 0;
+
+err_pin_enable:
+ hbtp_pdev_power_on(ts, false);
+err_power_on:
+ mutex_unlock(&hbtp->mutex);
+ return rc;
+}
+
+static int hbtp_fb_resume(struct hbtp_data *ts)
+{
+ char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL};
+
+ mutex_lock(&hbtp->mutex);
+ if (!ts->power_sync_enabled) {
+ pr_debug("%s: power_sync is disabled, send uevent\n", __func__);
+ if (ts->input_dev) {
+ kobject_uevent_env(&ts->input_dev->dev.kobj,
+ KOBJ_ONLINE, envp);
+ }
+ }
+ mutex_unlock(&hbtp->mutex);
+ return 0;
+}
+
static int hbtp_pdev_probe(struct platform_device *pdev)
{
int error;
struct regulator *vcc_ana, *vcc_dig;
+ hbtp->pdev = pdev;
+
if (pdev->dev.of_node) {
error = hbtp_parse_dt(&pdev->dev);
if (error) {
@@ -618,6 +1251,14 @@ static int hbtp_pdev_probe(struct platform_device *pdev)
}
}
+ platform_set_drvdata(pdev, hbtp);
+
+ error = hbtp_pinctrl_init(hbtp);
+ if (error) {
+ pr_info("%s: pinctrl isn't available, rc=%d\n", __func__,
+ error);
+ }
+
if (hbtp->manage_afe_power_ana) {
vcc_ana = regulator_get(&pdev->dev, "vcc_ana");
if (IS_ERR(vcc_ana)) {
@@ -662,8 +1303,6 @@ static int hbtp_pdev_probe(struct platform_device *pdev)
hbtp->vcc_dig = vcc_dig;
}
- hbtp->pdev = pdev;
-
return 0;
}
@@ -677,6 +1316,9 @@ static int hbtp_pdev_remove(struct platform_device *pdev)
regulator_put(hbtp->vcc_dig);
}
+ if (hbtp->ts_pinctrl)
+ devm_pinctrl_put(hbtp->ts_pinctrl);
+
return 0;
}
diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c
index a806ba3818f7..8d6326d7e7be 100644
--- a/drivers/input/misc/max8997_haptic.c
+++ b/drivers/input/misc/max8997_haptic.c
@@ -255,12 +255,14 @@ static int max8997_haptic_probe(struct platform_device *pdev)
struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent);
const struct max8997_platform_data *pdata =
dev_get_platdata(iodev->dev);
- const struct max8997_haptic_platform_data *haptic_pdata =
- pdata->haptic_pdata;
+ const struct max8997_haptic_platform_data *haptic_pdata = NULL;
struct max8997_haptic *chip;
struct input_dev *input_dev;
int error;
+ if (pdata)
+ haptic_pdata = pdata->haptic_pdata;
+
if (!haptic_pdata) {
dev_err(&pdev->dev, "no haptic platform data\n");
return -EINVAL;
diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
index 3f02e0e03d12..67aab86048ad 100644
--- a/drivers/input/misc/pmic8xxx-pwrkey.c
+++ b/drivers/input/misc/pmic8xxx-pwrkey.c
@@ -353,7 +353,8 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
if (of_property_read_u32(pdev->dev.of_node, "debounce", &kpd_delay))
kpd_delay = 15625;
- if (kpd_delay > 62500 || kpd_delay == 0) {
+ /* Valid range of pwr key trigger delay is 1/64 sec to 2 seconds. */
+ if (kpd_delay > USEC_PER_SEC * 2 || kpd_delay < USEC_PER_SEC / 64) {
dev_err(&pdev->dev, "invalid power key trigger delay\n");
return -EINVAL;
}
@@ -385,8 +386,8 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
pwr->name = "pmic8xxx_pwrkey";
pwr->phys = "pmic8xxx_pwrkey/input0";
- delay = (kpd_delay << 10) / USEC_PER_SEC;
- delay = 1 + ilog2(delay);
+ delay = (kpd_delay << 6) / USEC_PER_SEC;
+ delay = ilog2(delay);
err = regmap_read(regmap, PON_CNTL_1, &pon_cntl);
if (err < 0) {
diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c
index f2261ab54701..18663d4edae5 100644
--- a/drivers/input/misc/pwm-beeper.c
+++ b/drivers/input/misc/pwm-beeper.c
@@ -20,21 +20,40 @@
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
struct pwm_beeper {
struct input_dev *input;
struct pwm_device *pwm;
+ struct work_struct work;
unsigned long period;
};
#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
+static void __pwm_beeper_set(struct pwm_beeper *beeper)
+{
+ unsigned long period = beeper->period;
+
+ if (period) {
+ pwm_config(beeper->pwm, period / 2, period);
+ pwm_enable(beeper->pwm);
+ } else
+ pwm_disable(beeper->pwm);
+}
+
+static void pwm_beeper_work(struct work_struct *work)
+{
+ struct pwm_beeper *beeper =
+ container_of(work, struct pwm_beeper, work);
+
+ __pwm_beeper_set(beeper);
+}
+
static int pwm_beeper_event(struct input_dev *input,
unsigned int type, unsigned int code, int value)
{
- int ret = 0;
struct pwm_beeper *beeper = input_get_drvdata(input);
- unsigned long period;
if (type != EV_SND || value < 0)
return -EINVAL;
@@ -49,22 +68,31 @@ static int pwm_beeper_event(struct input_dev *input,
return -EINVAL;
}
- if (value == 0) {
- pwm_disable(beeper->pwm);
- } else {
- period = HZ_TO_NANOSECONDS(value);
- ret = pwm_config(beeper->pwm, period / 2, period);
- if (ret)
- return ret;
- ret = pwm_enable(beeper->pwm);
- if (ret)
- return ret;
- beeper->period = period;
- }
+ if (value == 0)
+ beeper->period = 0;
+ else
+ beeper->period = HZ_TO_NANOSECONDS(value);
+
+ schedule_work(&beeper->work);
return 0;
}
+static void pwm_beeper_stop(struct pwm_beeper *beeper)
+{
+ cancel_work_sync(&beeper->work);
+
+ if (beeper->period)
+ pwm_disable(beeper->pwm);
+}
+
+static void pwm_beeper_close(struct input_dev *input)
+{
+ struct pwm_beeper *beeper = input_get_drvdata(input);
+
+ pwm_beeper_stop(beeper);
+}
+
static int pwm_beeper_probe(struct platform_device *pdev)
{
unsigned long pwm_id = (unsigned long)dev_get_platdata(&pdev->dev);
@@ -87,6 +115,8 @@ static int pwm_beeper_probe(struct platform_device *pdev)
goto err_free;
}
+ INIT_WORK(&beeper->work, pwm_beeper_work);
+
beeper->input = input_allocate_device();
if (!beeper->input) {
dev_err(&pdev->dev, "Failed to allocate input device\n");
@@ -106,6 +136,7 @@ static int pwm_beeper_probe(struct platform_device *pdev)
beeper->input->sndbit[0] = BIT(SND_TONE) | BIT(SND_BELL);
beeper->input->event = pwm_beeper_event;
+ beeper->input->close = pwm_beeper_close;
input_set_drvdata(beeper->input, beeper);
@@ -135,7 +166,6 @@ static int pwm_beeper_remove(struct platform_device *pdev)
input_unregister_device(beeper->input);
- pwm_disable(beeper->pwm);
pwm_free(beeper->pwm);
kfree(beeper);
@@ -147,8 +177,7 @@ static int __maybe_unused pwm_beeper_suspend(struct device *dev)
{
struct pwm_beeper *beeper = dev_get_drvdata(dev);
- if (beeper->period)
- pwm_disable(beeper->pwm);
+ pwm_beeper_stop(beeper);
return 0;
}
@@ -157,10 +186,8 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev)
{
struct pwm_beeper *beeper = dev_get_drvdata(dev);
- if (beeper->period) {
- pwm_config(beeper->pwm, beeper->period / 2, beeper->period);
- pwm_enable(beeper->pwm);
- }
+ if (beeper->period)
+ __pwm_beeper_set(beeper);
return 0;
}
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 5adbcedcb81c..2bb4c8633d3b 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -893,9 +893,15 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
#ifdef CONFIG_COMPAT
+
+#define UI_SET_PHYS_COMPAT _IOW(UINPUT_IOCTL_BASE, 108, compat_uptr_t)
+
static long uinput_compat_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
+ if (cmd == UI_SET_PHYS_COMPAT)
+ cmd = UI_SET_PHYS;
+
return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg));
}
#endif
diff --git a/drivers/input/tablet/gtco.c b/drivers/input/tablet/gtco.c
index 3a7f3a4a4396..7c18249d6c8e 100644
--- a/drivers/input/tablet/gtco.c
+++ b/drivers/input/tablet/gtco.c
@@ -858,6 +858,14 @@ static int gtco_probe(struct usb_interface *usbinterface,
goto err_free_buf;
}
+ /* Sanity check that a device has an endpoint */
+ if (usbinterface->altsetting[0].desc.bNumEndpoints < 1) {
+ dev_err(&usbinterface->dev,
+ "Invalid number of endpoints\n");
+ error = -EINVAL;
+ goto err_free_urb;
+ }
+
/*
* The endpoint is always altsetting 0, we know this since we know
* this device only has one interrupt endpoint
@@ -879,7 +887,7 @@ static int gtco_probe(struct usb_interface *usbinterface,
* HID report descriptor
*/
if (usb_get_extra_descriptor(usbinterface->cur_altsetting,
- HID_DEVICE_TYPE, &hid_desc) != 0){
+ HID_DEVICE_TYPE, &hid_desc) != 0) {
dev_err(&usbinterface->dev,
"Can't retrieve exta USB descriptor to get hid report descriptor length\n");
error = -EIO;
diff --git a/drivers/input/touchscreen/gt9xx/goodix_tool.c b/drivers/input/touchscreen/gt9xx/goodix_tool.c
index 99a29401b36f..1657f56558ce 100644
--- a/drivers/input/touchscreen/gt9xx/goodix_tool.c
+++ b/drivers/input/touchscreen/gt9xx/goodix_tool.c
@@ -236,14 +236,13 @@ static u8 relation(u8 src, u8 dst, u8 rlt)
return ret;
}
-/*******************************************************
+/*
* Function:
- * Comfirm function.
+ * Comfirm function.
* Input:
- * None.
+ * None.
* Output:
* Return write length.
- *******************************************************
*/
static u8 comfirm(void)
{
@@ -301,11 +300,11 @@ static s32 fill_update_info(char __user *user_buf,
* Function:
* Goodix tool write function.
* Input:
- * standard proc write function param.
+ * standard proc write function param.
* Output:
* Return write length.
*/
-static s32 goodix_tool_write(struct file *filp, const char __user *userbuf,
+static ssize_t goodix_tool_write(struct file *filp, const char __user *userbuf,
size_t count, loff_t *ppos)
{
s32 ret = 0;
@@ -318,20 +317,22 @@ static s32 goodix_tool_write(struct file *filp, const char __user *userbuf,
goto exit;
}
- dev_dbg(&gt_client->dev, "wr:0x%02x, flag:0x%02x, flag addr:0x%02x%02x,
- flag val:0x%02x, flag rel:0x%02x,", cmd_headd.wr,
- cmd_head.flag, cmd_head.flag_addr[0],
- cmd_head.flag_addr[1], cmd_head.flag_val,
- cmd_head.flag_relation);
- dev_dbg(&gt_client->dev, "circle:%d, times:%d, retry:%d, delay:%d,
- data len:%d, addr len:%d, addr:0x%02x%02x, write len: %d",
- (s32)cmd_head.circle, (s32)cmd_head.times, (s32)cmd_head.retry,
- (s32)cmd_head.delay, (s32)cmd_head.data_len,
- (s32)cmd_head.addr_len, cmd_head.addr[0], cmd_head.addr[1],
- (s32)count);
+ dev_dbg(&gt_client->dev,
+ "wr: 0x%02x, flag:0x%02x, flag addr:0x%02x%02x\n", cmd_head.wr,
+ cmd_head.flag, cmd_head.flag_addr[0], cmd_head.flag_addr[1]);
+ dev_dbg(&gt_client->dev,
+ "flag val:0x%02x, flag rel:0x%02x,\n", cmd_head.flag_val,
+ cmd_head.flag_relation);
+ dev_dbg(&gt_client->dev, "circle:%u, times:%u, retry:%u, delay:%u\n",
+ (s32) cmd_head.circle, (s32) cmd_head.times,
+ (s32) cmd_head.retry, (s32)cmd_head.delay);
+ dev_dbg(&gt_client->dev,
+ "data len:%u, addr len:%u, addr:0x%02x%02x, write len: %u\n",
+ (s32)cmd_head.data_len, (s32)cmd_head.addr_len,
+ cmd_head.addr[0], cmd_head.addr[1], (s32)count);
if (cmd_head.data_len > (data_length - GTP_ADDR_LENGTH)) {
- dev_err(&gt_client->dev, "data len %d > data buff %d, rejected\n",
+ dev_err(&gt_client->dev, "data len %u > data buff %d, rejected\n",
cmd_head.data_len, (data_length - GTP_ADDR_LENGTH));
ret = -EINVAL;
goto exit;
@@ -383,7 +384,7 @@ static s32 goodix_tool_write(struct file *filp, const char __user *userbuf,
if (cmd_head.data_len > sizeof(ic_type)) {
dev_err(&gt_client->dev,
- "data len %d > data buff %d, rejected\n",
+ "data len %u > data buff %zu, rejected\n",
cmd_head.data_len, sizeof(ic_type));
ret = -EINVAL;
goto exit;
@@ -445,7 +446,7 @@ static s32 goodix_tool_write(struct file *filp, const char __user *userbuf,
show_len = 0;
total_len = 0;
if (cmd_head.data_len + 1 > data_length) {
- dev_err(&gt_client->dev, "data len %d > data buff %d, rejected\n",
+ dev_err(&gt_client->dev, "data len %u > data buff %d, rejected\n",
cmd_head.data_len + 1, data_length);
ret = -EINVAL;
goto exit;
@@ -470,11 +471,11 @@ exit:
* Function:
* Goodix tool read function.
* Input:
- * standard proc read function param.
+ * standard proc read function param.
* Output:
* Return read length.
*/
-static s32 goodix_tool_read(struct file *file, char __user *user_buf,
+static ssize_t goodix_tool_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
u16 data_len = 0;
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.c b/drivers/input/touchscreen/gt9xx/gt9xx.c
index 3b19a45922c4..ead935120624 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx.c
+++ b/drivers/input/touchscreen/gt9xx/gt9xx.c
@@ -48,6 +48,7 @@
#include <linux/module.h>
#include <linux/input/mt.h>
#include <linux/debugfs.h>
+#include <linux/interrupt.h>
#define GOODIX_DEV_NAME "Goodix-CTP"
#define CFG_MAX_TOUCH_POINTS 5
@@ -65,6 +66,8 @@
#define RESET_DELAY_T3_US 200 /* T3: > 100us */
#define RESET_DELAY_T4 20 /* T4: > 5ms */
+#define SLEEP_DELAY_US 5000
+#define WAKE_UP_DELAY_US 5000
#define PHY_BUF_SIZE 32
#define PROP_NAME_SIZE 24
@@ -72,11 +75,6 @@
#define GTP_MAX_TOUCH 5
#define GTP_ESD_CHECK_CIRCLE_MS 2000
-#if GTP_HAVE_TOUCH_KEY
-static const u16 touch_key_array[] = {KEY_MENU, KEY_HOMEPAGE, KEY_BACK};
-
-#endif
-
static void gtp_int_sync(struct goodix_ts_data *ts, int ms);
static int gtp_i2c_test(struct i2c_client *client);
static int goodix_power_off(struct goodix_ts_data *ts);
@@ -99,15 +97,14 @@ static void gtp_esd_check_func(struct work_struct *work);
static int gtp_init_ext_watchdog(struct i2c_client *client);
#endif
-#if GTP_SLIDE_WAKEUP
-enum doze_status {
+enum doze {
DOZE_DISABLED = 0,
DOZE_ENABLED = 1,
DOZE_WAKEUP = 2,
};
-static enum doze_status = DOZE_DISABLED;
+static enum doze doze_status = DOZE_DISABLED;
static s8 gtp_enter_doze(struct goodix_ts_data *ts);
-#endif
+
bool init_done;
static u8 chip_gt9xxs; /* true if ic is gt9xxs, like gt915s */
u8 grp_cfg_version;
@@ -157,11 +154,11 @@ int gtp_i2c_read(struct i2c_client *client, u8 *buf, int len)
dev_err(&client->dev, "I2C retry: %d\n", retries + 1);
}
if (retries == GTP_I2C_RETRY_5) {
-#if GTP_SLIDE_WAKEUP
- /* reset chip would quit doze mode */
- if (doze_status == DOZE_ENABLED)
- return ret;
-#endif
+ if (ts->pdata->slide_wakeup)
+ /* reset chip would quit doze mode */
+ if (doze_status == DOZE_ENABLED)
+ return ret;
+
if (init_done)
gtp_reset_guitar(ts, 10);
else
@@ -203,10 +200,10 @@ int gtp_i2c_write(struct i2c_client *client, u8 *buf, int len)
dev_err(&client->dev, "I2C retry: %d\n", retries + 1);
}
if (retries == GTP_I2C_RETRY_5) {
-#if GTP_SLIDE_WAKEUP
- if (doze_status == DOZE_ENABLED)
- return ret;
-#endif
+ if (ts->pdata->slide_wakeup)
+ if (doze_status == DOZE_ENABLED)
+ return ret;
+
if (init_done)
gtp_reset_guitar(ts, 10);
else
@@ -270,24 +267,25 @@ Output:
*********************************************************/
int gtp_send_cfg(struct goodix_ts_data *ts)
{
- int ret;
-#if GTP_DRIVER_SEND_CFG
- int retry = 0;
+ int ret = 0;
+ int retry;
- if (ts->fixed_cfg) {
- dev_dbg(&ts->client->dev,
- "Ic fixed config, no config sent!");
- ret = 2;
- } else {
- for (retry = 0; retry < GTP_I2C_RETRY_5; retry++) {
- ret = gtp_i2c_write(ts->client,
- ts->config_data,
- GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH);
- if (ret > 0)
- break;
+ if (ts->pdata->driver_send_cfg) {
+ if (ts->fixed_cfg) {
+ dev_dbg(&ts->client->dev,
+ "Ic fixed config, no config sent!");
+ ret = 2;
+ } else {
+ for (retry = 0; retry < GTP_I2C_RETRY_5; retry++) {
+ ret = gtp_i2c_write(ts->client,
+ ts->config_data,
+ GTP_CONFIG_MAX_LENGTH +
+ GTP_ADDR_LENGTH);
+ if (ret > 0)
+ break;
+ }
}
}
-#endif
return ret;
}
@@ -347,9 +345,8 @@ Output:
static void gtp_touch_down(struct goodix_ts_data *ts, int id, int x, int y,
int w)
{
-#if GTP_CHANGE_X2Y
- swap(x, y);
-#endif
+ if (ts->pdata->change_x2y)
+ swap(x, y);
input_mt_slot(ts->input_dev, id);
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
@@ -394,9 +391,7 @@ static void goodix_ts_work_func(struct work_struct *work)
u8 finger = 0;
static u16 pre_touch;
static u8 pre_key;
-#if GTP_WITH_PEN
static u8 pre_pen;
-#endif
u8 key_value = 0;
u8 *coor_data = NULL;
s32 input_x = 0;
@@ -406,10 +401,7 @@ static void goodix_ts_work_func(struct work_struct *work)
s32 i = 0;
int ret = -1;
struct goodix_ts_data *ts = NULL;
-
-#if GTP_SLIDE_WAKEUP
u8 doze_buf[3] = {0x81, 0x4B};
-#endif
ts = container_of(work, struct goodix_ts_data, work);
#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
@@ -417,55 +409,59 @@ static void goodix_ts_work_func(struct work_struct *work)
return;
#endif
-#if GTP_SLIDE_WAKEUP
- if (doze_status == DOZE_ENABLED) {
- ret = gtp_i2c_read(ts->client, doze_buf, 3);
- if (ret > 0) {
- if (doze_buf[2] == 0xAA) {
- dev_dbg(&ts->client->dev,
- "Slide(0xAA) To Light up the screen!");
- doze_status = DOZE_WAKEUP;
- input_report_key(
- ts->input_dev, KEY_POWER, 1);
- input_sync(ts->input_dev);
- input_report_key(
- ts->input_dev, KEY_POWER, 0);
- input_sync(ts->input_dev);
- /* clear 0x814B */
- doze_buf[2] = 0x00;
- gtp_i2c_write(ts->client, doze_buf, 3);
- } else if (doze_buf[2] == 0xBB) {
- dev_dbg(&ts->client->dev,
- "Slide(0xBB) To Light up the screen!");
- doze_status = DOZE_WAKEUP;
- input_report_key(ts->input_dev, KEY_POWER, 1);
- input_sync(ts->input_dev);
- input_report_key(ts->input_dev, KEY_POWER, 0);
- input_sync(ts->input_dev);
- /* clear 0x814B*/
- doze_buf[2] = 0x00;
- gtp_i2c_write(ts->client, doze_buf, 3);
- } else if (0xC0 == (doze_buf[2] & 0xC0)) {
- dev_dbg(&ts->client->dev,
- "double click to light up the screen!");
- doze_status = DOZE_WAKEUP;
- input_report_key(ts->input_dev, KEY_POWER, 1);
- input_sync(ts->input_dev);
- input_report_key(ts->input_dev, KEY_POWER, 0);
- input_sync(ts->input_dev);
- /* clear 0x814B */
- doze_buf[2] = 0x00;
- gtp_i2c_write(ts->client, doze_buf, 3);
- } else {
- gtp_enter_doze(ts);
+ if (ts->pdata->slide_wakeup) {
+ if (doze_status == DOZE_ENABLED) {
+ ret = gtp_i2c_read(ts->client, doze_buf, 3);
+ if (ret > 0) {
+ if (doze_buf[2] == 0xAA) {
+ dev_dbg(&ts->client->dev,
+ "Slide(0xAA) To Light up the screen!");
+ doze_status = DOZE_WAKEUP;
+ input_report_key(
+ ts->input_dev, KEY_POWER, 1);
+ input_sync(ts->input_dev);
+ input_report_key(
+ ts->input_dev, KEY_POWER, 0);
+ input_sync(ts->input_dev);
+ /* clear 0x814B */
+ doze_buf[2] = 0x00;
+ gtp_i2c_write(ts->client, doze_buf, 3);
+ } else if (doze_buf[2] == 0xBB) {
+ dev_dbg(&ts->client->dev,
+ "Slide(0xBB) To Light up the screen!");
+ doze_status = DOZE_WAKEUP;
+ input_report_key(ts->input_dev,
+ KEY_POWER, 1);
+ input_sync(ts->input_dev);
+ input_report_key(ts->input_dev,
+ KEY_POWER, 0);
+ input_sync(ts->input_dev);
+ /* clear 0x814B*/
+ doze_buf[2] = 0x00;
+ gtp_i2c_write(ts->client, doze_buf, 3);
+ } else if (0xC0 == (doze_buf[2] & 0xC0)) {
+ dev_dbg(&ts->client->dev,
+ "double click to light up the screen!");
+ doze_status = DOZE_WAKEUP;
+ input_report_key(ts->input_dev,
+ KEY_POWER, 1);
+ input_sync(ts->input_dev);
+ input_report_key(ts->input_dev,
+ KEY_POWER, 0);
+ input_sync(ts->input_dev);
+ /* clear 0x814B */
+ doze_buf[2] = 0x00;
+ gtp_i2c_write(ts->client, doze_buf, 3);
+ } else {
+ gtp_enter_doze(ts);
+ }
}
- }
- if (ts->use_irq)
- gtp_irq_enable(ts);
+ if (ts->use_irq)
+ gtp_irq_enable(ts);
- return;
+ return;
+ }
}
-#endif
ret = gtp_i2c_read(ts->client, point_data, 12);
if (ret < 0) {
@@ -506,15 +502,16 @@ static void goodix_ts_work_func(struct work_struct *work)
pre_key = key_value;
-#if GTP_WITH_PEN
- if (pre_pen && (touch_num == 0)) {
- dev_dbg(&ts->client->dev, "Pen touch UP(Slot)!");
- input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
- input_mt_slot(ts->input_dev, 5);
- input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
- pre_pen = 0;
+ if (ts->pdata->with_pen) {
+ if (pre_pen && (touch_num == 0)) {
+ dev_dbg(&ts->client->dev, "Pen touch UP(Slot)!");
+ input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
+ input_mt_slot(ts->input_dev, 5);
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
+ pre_pen = 0;
+ }
}
-#endif
+
if (pre_touch || touch_num) {
s32 pos = 0;
u16 touch_index = 0;
@@ -522,45 +519,45 @@ static void goodix_ts_work_func(struct work_struct *work)
coor_data = &point_data[3];
if (touch_num) {
id = coor_data[pos] & 0x0F;
-#if GTP_WITH_PEN
- id = coor_data[pos];
- if (id == 128) {
- dev_dbg(&ts->client->dev,
- "Pen touch DOWN(Slot)!");
- input_x = coor_data[pos + 1]
- | (coor_data[pos + 2] << 8);
- input_y = coor_data[pos + 3]
- | (coor_data[pos + 4] << 8);
- input_w = coor_data[pos + 5]
- | (coor_data[pos + 6] << 8);
-
- input_report_key(ts->input_dev,
- BTN_TOOL_PEN, 1);
- input_mt_slot(ts->input_dev, 5);
- input_report_abs(ts->input_dev,
- ABS_MT_TRACKING_ID, 5);
- input_report_abs(ts->input_dev,
- ABS_MT_POSITION_X, input_x);
- input_report_abs(ts->input_dev,
- ABS_MT_POSITION_Y, input_y);
- input_report_abs(ts->input_dev,
- ABS_MT_TOUCH_MAJOR, input_w);
- dev_dbg(&ts->client->dev,
- "Pen/Stylus: (%d, %d)[%d]",
- input_x, input_y, input_w);
- pre_pen = 1;
- pre_touch = 0;
+ if (ts->pdata->with_pen) {
+ id = coor_data[pos];
+ if (id == 128) {
+ dev_dbg(&ts->client->dev,
+ "Pen touch DOWN(Slot)!");
+ input_x = coor_data[pos + 1]
+ | (coor_data[pos + 2] << 8);
+ input_y = coor_data[pos + 3]
+ | (coor_data[pos + 4] << 8);
+ input_w = coor_data[pos + 5]
+ | (coor_data[pos + 6] << 8);
+
+ input_report_key(ts->input_dev,
+ BTN_TOOL_PEN, 1);
+ input_mt_slot(ts->input_dev, 5);
+ input_report_abs(ts->input_dev,
+ ABS_MT_TRACKING_ID, 5);
+ input_report_abs(ts->input_dev,
+ ABS_MT_POSITION_X, input_x);
+ input_report_abs(ts->input_dev,
+ ABS_MT_POSITION_Y, input_y);
+ input_report_abs(ts->input_dev,
+ ABS_MT_TOUCH_MAJOR, input_w);
+ dev_dbg(&ts->client->dev,
+ "Pen/Stylus: (%d, %d)[%d]",
+ input_x, input_y, input_w);
+ pre_pen = 1;
+ pre_touch = 0;
+ }
}
-#endif
touch_index |= (0x01<<id);
}
for (i = 0; i < GTP_MAX_TOUCH; i++) {
-#if GTP_WITH_PEN
- if (pre_pen == 1)
- break;
-#endif
+ if (ts->pdata->with_pen)
+ if (pre_pen == 1)
+ break;
+
if (touch_index & (0x01<<i)) {
input_x = coor_data[pos + 1] |
coor_data[pos + 2] << 8;
@@ -651,7 +648,7 @@ void gtp_reset_guitar(struct goodix_ts_data *ts, int ms)
else
gpio_direction_output(ts->pdata->irq_gpio, 0);
- usleep(RESET_DELAY_T3_US);
+ usleep_range(RESET_DELAY_T3_US, RESET_DELAY_T3_US + 1);
gpio_direction_output(ts->pdata->reset_gpio, 1);
msleep(RESET_DELAY_T4);
@@ -665,7 +662,6 @@ void gtp_reset_guitar(struct goodix_ts_data *ts, int ms)
}
#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_FB)
-#if GTP_SLIDE_WAKEUP
/*******************************************************
Function:
Enter doze mode for sliding wakeup.
@@ -682,9 +678,9 @@ static s8 gtp_enter_doze(struct goodix_ts_data *ts)
(u8)(GTP_REG_SLEEP >> 8),
(u8)GTP_REG_SLEEP, 8};
-#if GTP_DBL_CLK_WAKEUP
- i2c_control_buf[2] = 0x09;
-#endif
+ if (ts->pdata->dbl_clk_wakeup)
+ i2c_control_buf[2] = 0x09;
+
gtp_irq_disable(ts);
while (retry++ < GTP_I2C_RETRY_3) {
@@ -713,7 +709,6 @@ static s8 gtp_enter_doze(struct goodix_ts_data *ts)
gtp_irq_enable(ts);
return ret;
}
-#else
/**
* gtp_enter_sleep - Enter sleep mode
* @ts: driver private data
@@ -746,7 +741,7 @@ static u8 gtp_enter_sleep(struct goodix_ts_data *ts)
}
return 0;
}
- usleep(5000);
+ usleep_range(SLEEP_DELAY_US, SLEEP_DELAY_US + 1);
while (retry++ < GTP_I2C_RETRY_5) {
ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
if (ret == 1) {
@@ -758,7 +753,6 @@ static u8 gtp_enter_sleep(struct goodix_ts_data *ts)
dev_err(&ts->client->dev, "GTP send sleep cmd failed.\n");
return ret;
}
-#endif /* !GTP_SLIDE_WAKEUP */
/*******************************************************
Function:
@@ -804,33 +798,34 @@ static s8 gtp_wakeup_sleep(struct goodix_ts_data *ts)
"Wakeup sleep send config success.");
} else {
err_retry:
-#if GTP_SLIDE_WAKEUP
- /* wakeup not by slide */
- if (doze_status != DOZE_WAKEUP)
- gtp_reset_guitar(ts, 10);
- else
- /* wakeup by slide */
- doze_status = DOZE_DISABLED;
-#else
- if (chip_gt9xxs == 1) {
- gtp_reset_guitar(ts, 10);
+ if (ts->pdata->slide_wakeup) { /* wakeup not by slide */
+ if (doze_status != DOZE_WAKEUP)
+ gtp_reset_guitar(ts, 10);
+ else
+ /* wakeup by slide */
+ doze_status = DOZE_DISABLED;
} else {
- ret = gpio_direction_output(ts->pdata->irq_gpio, 1);
- usleep(5000);
+ if (chip_gt9xxs == 1) {
+ gtp_reset_guitar(ts, 10);
+ } else {
+ ret = gpio_direction_output(
+ ts->pdata->irq_gpio, 1);
+ usleep_range(WAKE_UP_DELAY_US,
+ WAKE_UP_DELAY_US + 1);
+ }
}
-#endif
ret = gtp_i2c_test(ts->client);
if (ret == 2) {
dev_dbg(&ts->client->dev, "GTP wakeup sleep.");
-#if (!GTP_SLIDE_WAKEUP)
- if (chip_gt9xxs == 0) {
- gtp_int_sync(ts, 25);
- msleep(20);
+ if (!ts->pdata->slide_wakeup) {
+ if (chip_gt9xxs == 0) {
+ gtp_int_sync(ts, 25);
+ msleep(20);
#if GTP_ESD_PROTECT
- gtp_init_ext_watchdog(ts->client);
+ gtp_init_ext_watchdog(ts->client);
#endif
+ }
}
-#endif
return ret;
}
gtp_reset_guitar(ts, 20);
@@ -854,123 +849,126 @@ Output:
static int gtp_init_panel(struct goodix_ts_data *ts)
{
struct i2c_client *client = ts->client;
- unsigned char *config_data;
+ unsigned char *config_data = NULL;
int ret = -EIO;
-
-#if GTP_DRIVER_SEND_CFG
int i;
u8 check_sum = 0;
u8 opr_buf[16];
u8 sensor_id = 0;
- for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++)
- dev_dbg(&client->dev, "Config Groups(%d) Lengths: %d",
- i, ts->pdata->config_data_len[i]);
-
- ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1);
- if (ret == SUCCESS) {
- if (opr_buf[0] != 0xBE) {
- ts->fw_error = 1;
- dev_err(&client->dev,
- "Firmware error, no config sent!");
- return -EINVAL;
- }
- }
+ if (ts->pdata->driver_send_cfg) {
+ for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++)
+ dev_dbg(&client->dev, "Config Groups(%d) Lengths: %zu",
+ i, ts->pdata->config_data_len[i]);
- for (i = 1; i < GOODIX_MAX_CFG_GROUP; i++) {
- if (ts->pdata->config_data_len[i])
- break;
- }
- if (i == GOODIX_MAX_CFG_GROUP) {
- sensor_id = 0;
- } else {
- ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID,
- &sensor_id, 1);
+ ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1);
if (ret == SUCCESS) {
- if (sensor_id >= GOODIX_MAX_CFG_GROUP) {
+ if (opr_buf[0] != 0xBE) {
+ ts->fw_error = 1;
dev_err(&client->dev,
- "Invalid sensor_id(0x%02X), No Config Sent!",
- sensor_id);
+ "Firmware error, no config sent!");
return -EINVAL;
}
+ }
+
+ for (i = 1; i < GOODIX_MAX_CFG_GROUP; i++) {
+ if (ts->pdata->config_data_len[i])
+ break;
+ }
+
+ if (i == GOODIX_MAX_CFG_GROUP) {
+ sensor_id = 0;
} else {
- dev_err(&client->dev,
- "Failed to get sensor_id, No config sent!");
- return -EINVAL;
+ ret = gtp_i2c_read_dbl_check(ts->client,
+ GTP_REG_SENSOR_ID, &sensor_id, 1);
+ if (ret == SUCCESS) {
+ if (sensor_id >= GOODIX_MAX_CFG_GROUP) {
+ dev_err(&client->dev,
+ "Invalid sensor_id(0x%02X), No Config Sent!",
+ sensor_id);
+ return -EINVAL;
+ }
+ } else {
+ dev_err(&client->dev,
+ "Failed to get sensor_id, No config sent!");
+ return -EINVAL;
+ }
}
- }
- dev_dbg(&client->dev, "Sensor ID selected: %d", sensor_id);
+ dev_info(&client->dev, "Sensor ID selected: %d", sensor_id);
- if (ts->pdata->config_data_len[sensor_id] < GTP_CONFIG_MIN_LENGTH ||
- !ts->pdata->config_data[sensor_id]) {
- dev_err(&client->dev,
- "Sensor_ID(%d) matches with NULL or invalid config group!\n",
- sensor_id);
- return -EINVAL;
- }
+ if (ts->pdata->config_data_len[sensor_id] <
+ GTP_CONFIG_MIN_LENGTH ||
+ !ts->pdata->config_data[sensor_id]) {
+ dev_err(&client->dev,
+ "Sensor_ID(%d) matches with NULL or invalid config group!\n",
+ sensor_id);
+ return -EINVAL;
+ }
- ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA,
- &opr_buf[0], 1);
- if (ret == SUCCESS) {
- if (opr_buf[0] < 90) {
- /* backup group config version */
- grp_cfg_version =
- ts->pdata->config_data[sensor_id][GTP_ADDR_LENGTH];
- ts->pdata->config_data[sensor_id][GTP_ADDR_LENGTH] =
- 0x00;
- ts->fixed_cfg = 0;
+ ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA,
+ &opr_buf[0], 1);
+ if (ret == SUCCESS) {
+ if (opr_buf[0] < 90) {
+ /* backup group config version */
+ grp_cfg_version =
+ ts->pdata->
+ config_data[sensor_id][GTP_ADDR_LENGTH];
+ ts->pdata->
+ config_data[sensor_id][GTP_ADDR_LENGTH]
+ = 0x00;
+ ts->fixed_cfg = 0;
+ } else {
+ /* treated as fixed config, not send config */
+ dev_warn(&client->dev,
+ "Ic fixed config with config version(%d, 0x%02X)",
+ opr_buf[0], opr_buf[0]);
+ ts->fixed_cfg = 1;
+ }
} else {
- /* treated as fixed config, not send config */
- dev_warn(&client->dev,
- "Ic fixed config with config version(%d, 0x%02X)",
- opr_buf[0], opr_buf[0]);
- ts->fixed_cfg = 1;
+ dev_err(&client->dev,
+ "Failed to get ic config version!No config sent!");
+ return -EINVAL;
}
- } else {
- dev_err(&client->dev,
- "Failed to get ic config version!No config sent!");
- return -EINVAL;
- }
- config_data = ts->pdata->config_data[sensor_id];
- ts->config_data = ts->pdata->config_data[sensor_id];
- ts->gtp_cfg_len = ts->pdata->config_data_len[sensor_id];
+ config_data = ts->pdata->config_data[sensor_id];
+ ts->config_data = ts->pdata->config_data[sensor_id];
+ ts->gtp_cfg_len = ts->pdata->config_data_len[sensor_id];
#if GTP_CUSTOM_CFG
- config_data[RESOLUTION_LOC] =
- (unsigned char)(GTP_MAX_WIDTH && 0xFF);
- config_data[RESOLUTION_LOC + 1] =
- (unsigned char)(GTP_MAX_WIDTH >> 8);
- config_data[RESOLUTION_LOC + 2] =
- (unsigned char)(GTP_MAX_HEIGHT && 0xFF);
- config_data[RESOLUTION_LOC + 3] =
- (unsigned char)(GTP_MAX_HEIGHT >> 8);
-
- if (GTP_INT_TRIGGER == 0)
- config_data[TRIGGER_LOC] &= 0xfe;
- else if (GTP_INT_TRIGGER == 1)
- config_data[TRIGGER_LOC] |= 0x01;
+ config_data[RESOLUTION_LOC] =
+ (unsigned char)(GTP_MAX_WIDTH && 0xFF);
+ config_data[RESOLUTION_LOC + 1] =
+ (unsigned char)(GTP_MAX_WIDTH >> 8);
+ config_data[RESOLUTION_LOC + 2] =
+ (unsigned char)(GTP_MAX_HEIGHT && 0xFF);
+ config_data[RESOLUTION_LOC + 3] =
+ (unsigned char)(GTP_MAX_HEIGHT >> 8);
+
+ if (GTP_INT_TRIGGER == 0)
+ config_data[TRIGGER_LOC] &= 0xfe;
+ else if (GTP_INT_TRIGGER == 1)
+ config_data[TRIGGER_LOC] |= 0x01;
#endif /* !GTP_CUSTOM_CFG */
- check_sum = 0;
- for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++)
- check_sum += config_data[i];
+ check_sum = 0;
+ for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++)
+ check_sum += config_data[i];
- config_data[ts->gtp_cfg_len] = (~check_sum) + 1;
+ config_data[ts->gtp_cfg_len] = (~check_sum) + 1;
-#else /* DRIVER NOT SEND CONFIG */
- ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH;
- ret = gtp_i2c_read(ts->client, config_data,
+ } else { /* DRIVER NOT SEND CONFIG */
+ ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH;
+ ret = gtp_i2c_read(ts->client, config_data,
ts->gtp_cfg_len + GTP_ADDR_LENGTH);
- if (ret < 0) {
- dev_err(&client->dev,
+ if (ret < 0) {
+ dev_err(&client->dev,
"Read Config Failed, Using DEFAULT Resolution & INT Trigger!\n");
- ts->abs_x_max = GTP_MAX_WIDTH;
- ts->abs_y_max = GTP_MAX_HEIGHT;
- ts->int_trigger_type = GTP_INT_TRIGGER;
- }
-#endif /* !DRIVER NOT SEND CONFIG */
+ ts->abs_x_max = GTP_MAX_WIDTH;
+ ts->abs_y_max = GTP_MAX_HEIGHT;
+ ts->int_trigger_type = GTP_INT_TRIGGER;
+ }
+ } /* !DRIVER NOT SEND CONFIG */
if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0)) {
ts->abs_x_max = (config_data[RESOLUTION_LOC + 1] << 8)
@@ -1177,7 +1175,8 @@ static int gtp_request_irq(struct goodix_ts_data *ts)
int ret;
const u8 irq_table[] = GTP_IRQ_TAB;
- GTP_DEBUG("INT trigger type:%x, irq=%d", ts->int_trigger_type,
+ dev_dbg(&ts->client->dev, "INT trigger type:%x, irq=%d",
+ ts->int_trigger_type,
ts->client->irq);
ret = request_threaded_irq(ts->client->irq, NULL,
@@ -1206,9 +1205,7 @@ static int gtp_request_input_dev(struct goodix_ts_data *ts)
{
int ret;
char phys[PHY_BUF_SIZE];
-#if GTP_HAVE_TOUCH_KEY
int index = 0;
-#endif
ts->input_dev = input_allocate_device();
if (ts->input_dev == NULL) {
@@ -1224,26 +1221,24 @@ static int gtp_request_input_dev(struct goodix_ts_data *ts)
/* in case of "out of memory" */
input_mt_init_slots(ts->input_dev, 10, 0);
- for (index = 0; index < ts->pdata->num_button; index++) {
- input_set_capability(ts->input_dev,
+ if (ts->pdata->have_touch_key) {
+ for (index = 0; index < ts->pdata->num_button; index++) {
+ input_set_capability(ts->input_dev,
EV_KEY, ts->pdata->button_map[index]);
+ }
}
+ if (ts->pdata->slide_wakeup)
+ input_set_capability(ts->input_dev, EV_KEY, KEY_POWER);
-#if GTP_SLIDE_WAKEUP
- input_set_capability(ts->input_dev, EV_KEY, KEY_POWER);
-#endif
-
-#if GTP_WITH_PEN
- /* pen support */
- __set_bit(BTN_TOOL_PEN, ts->input_dev->keybit);
- __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
- __set_bit(INPUT_PROP_POINTER, ts->input_dev->propbit);
-#endif
+ if (ts->pdata->with_pen) { /* pen support */
+ __set_bit(BTN_TOOL_PEN, ts->input_dev->keybit);
+ __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
+ __set_bit(INPUT_PROP_POINTER, ts->input_dev->propbit);
+ }
-#if GTP_CHANGE_X2Y
- swap(ts->abs_x_max, ts->abs_y_max);
-#endif
+ if (ts->pdata->change_x2y)
+ swap(ts->abs_x_max, ts->abs_y_max);
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X,
0, ts->abs_x_max, 0, 0);
@@ -1283,7 +1278,7 @@ exit_free_inputdev:
static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA)
{
return (regulator_count_voltages(reg) > 0) ?
- regulator_set_optimum_mode(reg, load_uA) : 0;
+ regulator_set_load(reg, load_uA) : 0;
}
/**
@@ -1828,7 +1823,7 @@ static int goodix_parse_dt(struct device *dev,
u32 temp_val, num_buttons;
u32 button_map[MAX_BUTTONS];
char prop_name[PROP_NAME_SIZE];
- int i, read_cfg_num;
+ int i, read_cfg_num, temp;
rc = goodix_ts_get_dt_coords(dev, "goodix,panel-coords", pdata);
if (rc && (rc != -EINVAL))
@@ -1846,6 +1841,25 @@ static int goodix_parse_dt(struct device *dev,
pdata->enable_power_off = of_property_read_bool(np,
"goodix,enable-power-off");
+
+ pdata->have_touch_key = of_property_read_bool(np,
+ "goodix,have-touch-key");
+
+ pdata->driver_send_cfg = of_property_read_bool(np,
+ "goodix,driver-send-cfg");
+
+ pdata->change_x2y = of_property_read_bool(np,
+ "goodix,change-x2y");
+
+ pdata->with_pen = of_property_read_bool(np,
+ "goodix,with-pen");
+
+ pdata->slide_wakeup = of_property_read_bool(np,
+ "goodix,slide-wakeup");
+
+ pdata->dbl_clk_wakeup = of_property_read_bool(np,
+ "goodix,dbl_clk_wakeup");
+
/* reset, irq gpio info */
pdata->reset_gpio = of_get_named_gpio_flags(np, "reset-gpios",
0, &pdata->reset_gpio_flags);
@@ -1891,14 +1905,15 @@ static int goodix_parse_dt(struct device *dev,
read_cfg_num = 0;
for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++) {
+ temp = 0;
snprintf(prop_name, sizeof(prop_name), "goodix,cfg-data%d", i);
- prop = of_find_property(np, prop_name,
- &pdata->config_data_len[i]);
+ prop = of_find_property(np, prop_name, &temp);
if (!prop || !prop->value) {
pdata->config_data_len[i] = 0;
pdata->config_data[i] = NULL;
continue;
}
+ pdata->config_data_len[i] = temp;
pdata->config_data[i] = devm_kzalloc(dev,
GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH,
GFP_KERNEL);
@@ -2101,8 +2116,6 @@ exit_free_irq:
#endif
if (ts->use_irq)
free_irq(client->irq, ts);
- else
- hrtimer_cancel(&ts->timer);
cancel_work_sync(&ts->work);
flush_workqueue(ts->goodix_wq);
destroy_workqueue(ts->goodix_wq);
@@ -2166,8 +2179,6 @@ static int goodix_ts_remove(struct i2c_client *client)
if (ts) {
if (ts->use_irq)
free_irq(client->irq, ts);
- else
- hrtimer_cancel(&ts->timer);
cancel_work_sync(&ts->work);
flush_workqueue(ts->goodix_wq);
@@ -2225,23 +2236,21 @@ static int goodix_ts_suspend(struct device *dev)
gtp_esd_switch(ts->client, SWITCH_OFF);
#endif
-#if GTP_SLIDE_WAKEUP
- ret = gtp_enter_doze(ts);
-#else
- if (ts->use_irq)
- gtp_irq_disable(ts);
- else
- hrtimer_cancel(&ts->timer);
+ if (ts->pdata->slide_wakeup) {
+ ret = gtp_enter_doze(ts);
+ } else {
+ if (ts->use_irq)
+ gtp_irq_disable(ts);
- for (i = 0; i < GTP_MAX_TOUCH; i++)
- gtp_touch_up(ts, i);
+ for (i = 0; i < GTP_MAX_TOUCH; i++)
+ gtp_touch_up(ts, i);
- input_sync(ts->input_dev);
+ input_sync(ts->input_dev);
- ret = gtp_enter_sleep(ts);
-#endif
- if (ret < 0)
- dev_err(&ts->client->dev, "GTP early suspend failed\n");
+ ret = gtp_enter_sleep(ts);
+ if (ret < 0)
+ dev_err(&ts->client->dev, "GTP early suspend failed.\n");
+ }
/* to avoid waking up while not sleeping,
* delay 48 + 10ms to ensure reliability
*/
@@ -2273,18 +2282,14 @@ static int goodix_ts_resume(struct device *dev)
mutex_lock(&ts->lock);
ret = gtp_wakeup_sleep(ts);
-#if GTP_SLIDE_WAKEUP
- doze_status = DOZE_DISABLED;
-#endif
+ if (ts->pdata->slide_wakeup)
+ doze_status = DOZE_DISABLED;
if (ret <= 0)
dev_err(&ts->client->dev, "GTP resume failed\n");
if (ts->use_irq)
gtp_irq_enable(ts);
- else
- hrtimer_start(&ts->timer,
- ktime_set(1, 0), HRTIMER_MODE_REL);
#if GTP_ESD_PROTECT
gtp_esd_switch(ts->client, SWITCH_ON);
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.h b/drivers/input/touchscreen/gt9xx/gt9xx.h
index 779a0ddd93f8..1e85e2fce276 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx.h
+++ b/drivers/input/touchscreen/gt9xx/gt9xx.h
@@ -60,6 +60,12 @@ struct goodix_ts_platform_data {
u8 *config_data[GOODIX_MAX_CFG_GROUP];
u32 button_map[MAX_BUTTONS];
u8 num_button;
+ bool have_touch_key;
+ bool driver_send_cfg;
+ bool change_x2y;
+ bool with_pen;
+ bool slide_wakeup;
+ bool dbl_clk_wakeup;
};
struct goodix_ts_data {
spinlock_t irq_lock;
@@ -69,6 +75,7 @@ struct goodix_ts_data {
struct hrtimer timer;
struct workqueue_struct *goodix_wq;
struct work_struct work;
+ char fw_name[GTP_FW_NAME_MAXSIZE];
struct delayed_work goodix_update_work;
s32 irq_is_disabled;
s32 use_irq;
@@ -107,17 +114,7 @@ extern u16 total_len;
/***************************PART1:ON/OFF define*******************************/
#define GTP_CUSTOM_CFG 0
-#define GTP_CHANGE_X2Y 0
-#define GTP_DRIVER_SEND_CFG 1
-#define GTP_HAVE_TOUCH_KEY 1
-
#define GTP_ESD_PROTECT 0
-#define GTP_WITH_PEN 0
-
-/* This cannot work when enable-power-off is on */
-#define GTP_SLIDE_WAKEUP 0
-/* double-click wakeup, function together with GTP_SLIDE_WAKEUP */
-#define GTP_DBL_CLK_WAKEUP 0
#define GTP_IRQ_TAB {\
IRQ_TYPE_EDGE_RISING,\
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_update.c b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
index a91256c576e3..6bc243492272 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx_update.c
+++ b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
@@ -67,6 +67,8 @@
#define FAIL 0
#define SUCCESS 1
+#define RESET_DELAY_US 20000
+
struct st_fw_head {
u8 hw_info[4]; /* hardware info */
u8 pid[8]; /* product id */
@@ -390,7 +392,7 @@ s32 gup_enter_update_mode(struct i2c_client *client)
/* step1:RST output low last at least 2ms */
gpio_direction_output(ts->pdata->reset_gpio, 0);
- usleep(20000);
+ usleep_range(RESET_DELAY_US, RESET_DELAY_US + 1);
/* step2:select I2C slave addr,INT:0--0xBA;1--0x28. */
gpio_direction_output(ts->pdata->irq_gpio,
@@ -565,8 +567,8 @@ static s8 gup_update_config(struct i2c_client *client,
!memcmp(&pid[GTP_ADDR_LENGTH], "960", 3)) {
chip_cfg_len = 228;
}
- pr_debug("config file ASCII len:%d", cfg->size);
- pr_debug("need config binary len:%d", chip_cfg_len);
+ pr_debug("config file ASCII len: %zu", cfg->size);
+ pr_debug("need config binary len: %u", chip_cfg_len);
if ((cfg->size + 5) < chip_cfg_len * 5) {
pr_err("Config length error");
return -EINVAL;
@@ -643,7 +645,7 @@ static s32 gup_get_firmware_file(struct i2c_client *client,
return -EEXIST;
}
- dev_dbg(&client->dev, "Config File: %s size=%d", path, fw->size);
+ dev_dbg(&client->dev, "Config File: %s size: %zu", path, fw->size);
msg->fw_data =
devm_kzalloc(&client->dev, fw->size, GFP_KERNEL);
if (!msg->fw_data) {
diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c
index 9bbadaaf6bc3..7b3845aa5983 100644
--- a/drivers/input/touchscreen/zforce_ts.c
+++ b/drivers/input/touchscreen/zforce_ts.c
@@ -370,8 +370,8 @@ static int zforce_touch_event(struct zforce_ts *ts, u8 *payload)
point.coord_x = point.coord_y = 0;
}
- point.state = payload[9 * i + 5] & 0x03;
- point.id = (payload[9 * i + 5] & 0xfc) >> 2;
+ point.state = payload[9 * i + 5] & 0x0f;
+ point.id = (payload[9 * i + 5] & 0xf0) >> 4;
/* determine touch major, minor and orientation */
point.area_major = max(payload[9 * i + 6],
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index fc836f523afa..b9319b76a8a1 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -91,6 +91,7 @@ struct iommu_dev_data {
struct list_head dev_data_list; /* For global dev_data_list */
struct protection_domain *domain; /* Domain the device is bound to */
u16 devid; /* PCI Device ID */
+ u16 alias; /* Alias Device ID */
bool iommu_v2; /* Device can make use of IOMMUv2 */
bool passthrough; /* Device is identity mapped */
struct {
@@ -125,6 +126,13 @@ static struct protection_domain *to_pdomain(struct iommu_domain *dom)
return container_of(dom, struct protection_domain, domain);
}
+static inline u16 get_device_id(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ return PCI_DEVID(pdev->bus->number, pdev->devfn);
+}
+
static struct iommu_dev_data *alloc_dev_data(u16 devid)
{
struct iommu_dev_data *dev_data;
@@ -162,6 +170,68 @@ out_unlock:
return dev_data;
}
+static int __last_alias(struct pci_dev *pdev, u16 alias, void *data)
+{
+ *(u16 *)data = alias;
+ return 0;
+}
+
+static u16 get_alias(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u16 devid, ivrs_alias, pci_alias;
+
+ devid = get_device_id(dev);
+ ivrs_alias = amd_iommu_alias_table[devid];
+ pci_for_each_dma_alias(pdev, __last_alias, &pci_alias);
+
+ if (ivrs_alias == pci_alias)
+ return ivrs_alias;
+
+ /*
+ * DMA alias showdown
+ *
+ * The IVRS is fairly reliable in telling us about aliases, but it
+ * can't know about every screwy device. If we don't have an IVRS
+ * reported alias, use the PCI reported alias. In that case we may
+ * still need to initialize the rlookup and dev_table entries if the
+ * alias is to a non-existent device.
+ */
+ if (ivrs_alias == devid) {
+ if (!amd_iommu_rlookup_table[pci_alias]) {
+ amd_iommu_rlookup_table[pci_alias] =
+ amd_iommu_rlookup_table[devid];
+ memcpy(amd_iommu_dev_table[pci_alias].data,
+ amd_iommu_dev_table[devid].data,
+ sizeof(amd_iommu_dev_table[pci_alias].data));
+ }
+
+ return pci_alias;
+ }
+
+ pr_info("AMD-Vi: Using IVRS reported alias %02x:%02x.%d "
+ "for device %s[%04x:%04x], kernel reported alias "
+ "%02x:%02x.%d\n", PCI_BUS_NUM(ivrs_alias), PCI_SLOT(ivrs_alias),
+ PCI_FUNC(ivrs_alias), dev_name(dev), pdev->vendor, pdev->device,
+ PCI_BUS_NUM(pci_alias), PCI_SLOT(pci_alias),
+ PCI_FUNC(pci_alias));
+
+ /*
+ * If we don't have a PCI DMA alias and the IVRS alias is on the same
+ * bus, then the IVRS table may know about a quirk that we don't.
+ */
+ if (pci_alias == devid &&
+ PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) {
+ pdev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
+ pdev->dma_alias_devfn = ivrs_alias & 0xff;
+ pr_info("AMD-Vi: Added PCI DMA alias %02x.%d for %s\n",
+ PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias),
+ dev_name(dev));
+ }
+
+ return ivrs_alias;
+}
+
static struct iommu_dev_data *find_dev_data(u16 devid)
{
struct iommu_dev_data *dev_data;
@@ -174,13 +244,6 @@ static struct iommu_dev_data *find_dev_data(u16 devid)
return dev_data;
}
-static inline u16 get_device_id(struct device *dev)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
-
- return PCI_DEVID(pdev->bus->number, pdev->devfn);
-}
-
static struct iommu_dev_data *get_dev_data(struct device *dev)
{
return dev->archdata.iommu;
@@ -308,6 +371,8 @@ static int iommu_init_device(struct device *dev)
if (!dev_data)
return -ENOMEM;
+ dev_data->alias = get_alias(dev);
+
if (pci_iommuv2_capable(pdev)) {
struct amd_iommu *iommu;
@@ -328,7 +393,7 @@ static void iommu_ignore_device(struct device *dev)
u16 devid, alias;
devid = get_device_id(dev);
- alias = amd_iommu_alias_table[devid];
+ alias = get_alias(dev);
memset(&amd_iommu_dev_table[devid], 0, sizeof(struct dev_table_entry));
memset(&amd_iommu_dev_table[alias], 0, sizeof(struct dev_table_entry));
@@ -1017,7 +1082,7 @@ static int device_flush_dte(struct iommu_dev_data *dev_data)
int ret;
iommu = amd_iommu_rlookup_table[dev_data->devid];
- alias = amd_iommu_alias_table[dev_data->devid];
+ alias = dev_data->alias;
ret = iommu_flush_dte(iommu, dev_data->devid);
if (!ret && alias != dev_data->devid)
@@ -1891,7 +1956,7 @@ static void do_attach(struct iommu_dev_data *dev_data,
bool ats;
iommu = amd_iommu_rlookup_table[dev_data->devid];
- alias = amd_iommu_alias_table[dev_data->devid];
+ alias = dev_data->alias;
ats = dev_data->ats.enabled;
/* Update data structures */
@@ -1925,7 +1990,7 @@ static void do_detach(struct iommu_dev_data *dev_data)
return;
iommu = amd_iommu_rlookup_table[dev_data->devid];
- alias = amd_iommu_alias_table[dev_data->devid];
+ alias = dev_data->alias;
/* decrease reference counters */
dev_data->domain->dev_iommu[iommu->index] -= 1;
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index bf4959f4225b..94f1bf772ec9 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -1363,13 +1363,23 @@ static int __init amd_iommu_init_pci(void)
break;
}
+ /*
+ * Order is important here to make sure any unity map requirements are
+ * fulfilled. The unity mappings are created and written to the device
+ * table during the amd_iommu_init_api() call.
+ *
+ * After that we call init_device_table_dma() to make sure any
+ * uninitialized DTE will block DMA, and in the end we flush the caches
+ * of all IOMMUs to make sure the changes to the device table are
+ * active.
+ */
+ ret = amd_iommu_init_api();
+
init_device_table_dma();
for_each_iommu(iommu)
iommu_flush_all_caches(iommu);
- ret = amd_iommu_init_api();
-
if (!ret)
print_iommu_info();
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 4e5118a4cd30..8487987458a1 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -1919,6 +1919,7 @@ static struct iommu_ops arm_smmu_ops = {
.detach_dev = arm_smmu_detach_dev,
.map = arm_smmu_map,
.unmap = arm_smmu_unmap,
+ .map_sg = default_iommu_map_sg,
.iova_to_phys = arm_smmu_iova_to_phys,
.add_device = arm_smmu_add_device,
.remove_device = arm_smmu_remove_device,
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index fda10abae58a..eac6d07e6097 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -528,6 +528,8 @@ static bool arm_smmu_is_static_cb(struct arm_smmu_device *smmu);
static bool arm_smmu_is_slave_side_secure(struct arm_smmu_domain *smmu_domain);
static bool arm_smmu_has_secure_vmid(struct arm_smmu_domain *smmu_domain);
+static int arm_smmu_enable_s1_translations(struct arm_smmu_domain *smmu_domain);
+
static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
{
return container_of(dom, struct arm_smmu_domain, domain);
@@ -1604,7 +1606,8 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
/* SCTLR */
reg = SCTLR_CFCFG | SCTLR_CFIE | SCTLR_CFRE | SCTLR_EAE_SBOP;
- if (!(smmu_domain->attributes & (1 << DOMAIN_ATTR_S1_BYPASS)) ||
+ if ((!(smmu_domain->attributes & (1 << DOMAIN_ATTR_S1_BYPASS)) &&
+ !(smmu_domain->attributes & (1 << DOMAIN_ATTR_EARLY_MAP))) ||
!stage1)
reg |= SCTLR_M;
if (stage1)
@@ -1651,7 +1654,7 @@ static void arm_smmu_secure_domain_unlock(struct arm_smmu_domain *smmu_domain)
static unsigned long arm_smmu_pgtbl_lock(struct arm_smmu_domain *smmu_domain)
{
- unsigned long flags;
+ unsigned long flags = 0;
if (arm_smmu_is_slave_side_secure(smmu_domain))
mutex_lock(&smmu_domain->pgtbl_mutex_lock);
@@ -3043,6 +3046,11 @@ static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
ret = 0;
break;
}
+ case DOMAIN_ATTR_EARLY_MAP:
+ *((int *)data) = !!(smmu_domain->attributes
+ & (1 << DOMAIN_ATTR_EARLY_MAP));
+ ret = 0;
+ break;
default:
ret = -ENODEV;
break;
@@ -3148,6 +3156,24 @@ static int arm_smmu_domain_set_attr(struct iommu_domain *domain,
smmu_domain->attributes |= 1 << DOMAIN_ATTR_FAST;
ret = 0;
break;
+ case DOMAIN_ATTR_EARLY_MAP: {
+ int early_map = *((int *)data);
+
+ ret = 0;
+ if (early_map) {
+ smmu_domain->attributes |=
+ 1 << DOMAIN_ATTR_EARLY_MAP;
+ } else {
+ if (smmu_domain->smmu)
+ ret = arm_smmu_enable_s1_translations(
+ smmu_domain);
+
+ if (!ret)
+ smmu_domain->attributes &=
+ ~(1 << DOMAIN_ATTR_EARLY_MAP);
+ }
+ break;
+ }
default:
ret = -ENODEV;
break;
@@ -3158,6 +3184,28 @@ out_unlock:
return ret;
}
+
+static int arm_smmu_enable_s1_translations(struct arm_smmu_domain *smmu_domain)
+{
+ struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
+ struct arm_smmu_device *smmu = smmu_domain->smmu;
+ void __iomem *cb_base;
+ u32 reg;
+ int ret;
+
+ cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
+ ret = arm_smmu_enable_clocks(smmu);
+ if (ret)
+ return ret;
+
+ reg = readl_relaxed(cb_base + ARM_SMMU_CB_SCTLR);
+ reg |= SCTLR_M;
+
+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_SCTLR);
+ arm_smmu_disable_clocks(smmu);
+ return ret;
+}
+
static int arm_smmu_dma_supported(struct iommu_domain *domain,
struct device *dev, u64 mask)
{
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 72d6182666cb..58f2fe687a24 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -403,7 +403,7 @@ static int __finalise_sg(struct device *dev, struct scatterlist *sg, int nents,
unsigned int s_length = sg_dma_len(s);
unsigned int s_dma_len = s->length;
- s->offset = s_offset;
+ s->offset += s_offset;
s->length = s_length;
sg_dma_address(s) = dma_addr + s_offset;
dma_addr += s_dma_len;
@@ -422,7 +422,7 @@ static void __invalidate_sg(struct scatterlist *sg, int nents)
for_each_sg(sg, s, nents, i) {
if (sg_dma_address(s) != DMA_ERROR_CODE)
- s->offset = sg_dma_address(s);
+ s->offset += sg_dma_address(s);
if (sg_dma_len(s))
s->length = sg_dma_len(s);
sg_dma_address(s) = DMA_ERROR_CODE;
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index a2e1b7f14df2..6763a4dfed94 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -3169,11 +3169,6 @@ static int __init init_dmars(void)
}
}
- iommu_flush_write_buffer(iommu);
- iommu_set_root_entry(iommu);
- iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL);
- iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
-
if (!ecap_pass_through(iommu->ecap))
hw_pass_through = 0;
#ifdef CONFIG_INTEL_IOMMU_SVM
@@ -3182,6 +3177,18 @@ static int __init init_dmars(void)
#endif
}
+ /*
+ * Now that qi is enabled on all iommus, set the root entry and flush
+ * caches. This is required on some Intel X58 chipsets, otherwise the
+ * flush_context function will loop forever and the boot hangs.
+ */
+ for_each_active_iommu(iommu, drhd) {
+ iommu_flush_write_buffer(iommu);
+ iommu_set_root_entry(iommu);
+ iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL);
+ iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
+ }
+
if (iommu_pass_through)
iommu_identity_mapping |= IDENTMAP_ALL;
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index 3b54fd4a77e6..7b0c1ae5a48d 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -70,6 +70,8 @@ static const char *iommu_debug_attr_to_string(enum iommu_attr attr)
return "DOMAIN_ATTR_S1_BYPASS";
case DOMAIN_ATTR_FAST:
return "DOMAIN_ATTR_FAST";
+ case DOMAIN_ATTR_EARLY_MAP:
+ return "DOMAIN_ATTR_EARLY_MAP";
default:
return "Unknown attr!";
}
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 50c8c92d575d..cfdc235c1d28 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -521,6 +521,13 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
if (static_key_true(&supports_deactivate))
gic_write_dir(irqnr);
#ifdef CONFIG_SMP
+ /*
+ * Unlike GICv2, we don't need an smp_rmb() here.
+ * The control dependency from gic_read_iar to
+ * the ISB in gic_write_eoir is enough to ensure
+ * that any shared data read by handle_IPI will
+ * be read after the ACK.
+ */
handle_IPI(irqnr, regs);
#else
WARN_ONCE(true, "Unexpected SGI received!\n");
@@ -540,6 +547,15 @@ static void __init gic_dist_init(void)
writel_relaxed(0, base + GICD_CTLR);
gic_dist_wait_for_rwp();
+ /*
+ * Configure SPIs as non-secure Group-1. This will only matter
+ * if the GIC only has a single security state. This will not
+ * do the right thing if the kernel is running in secure mode,
+ * but that's not the intended use case anyway.
+ */
+ for (i = 32; i < gic_data.irq_nr; i += 32)
+ writel_relaxed(~0, base + GICD_IGROUPR + i / 8);
+
gic_dist_config(base, gic_data.irq_nr, gic_dist_wait_for_rwp);
/* Enable distributor with ARE, Group1 */
@@ -651,6 +667,9 @@ static void gic_cpu_init(void)
rbase = gic_data_rdist_sgi_base();
+ /* Configure SGIs/PPIs as non-secure Group-1 */
+ writel_relaxed(~0, rbase + GICR_IGROUPR0);
+
gic_cpu_config(rbase, gic_redist_wait_for_rwp);
/* Give LPIs a spin */
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index fb940e92b64e..10b73d9bea78 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -405,6 +405,14 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
if (static_key_true(&supports_deactivate))
writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);
#ifdef CONFIG_SMP
+ /*
+ * Ensure any shared data written by the CPU sending
+ * the IPI is read after we've read the ACK register
+ * on the GIC.
+ *
+ * Pairs with the write barrier in gic_raise_softirq
+ */
+ smp_rmb();
handle_IPI(irqnr, regs);
#endif
continue;
diff --git a/drivers/irqchip/irq-mxs.c b/drivers/irqchip/irq-mxs.c
index efe50845939d..17304705f2cf 100644
--- a/drivers/irqchip/irq-mxs.c
+++ b/drivers/irqchip/irq-mxs.c
@@ -183,7 +183,7 @@ static void __iomem * __init icoll_init_iobase(struct device_node *np)
void __iomem *icoll_base;
icoll_base = of_io_request_and_map(np, 0, np->name);
- if (!icoll_base)
+ if (IS_ERR(icoll_base))
panic("%s: unable to map resource", np->full_name);
return icoll_base;
}
diff --git a/drivers/irqchip/irq-sunxi-nmi.c b/drivers/irqchip/irq-sunxi-nmi.c
index 4ef178078e5b..1254e98f6b57 100644
--- a/drivers/irqchip/irq-sunxi-nmi.c
+++ b/drivers/irqchip/irq-sunxi-nmi.c
@@ -154,9 +154,9 @@ static int __init sunxi_sc_nmi_irq_init(struct device_node *node,
gc = irq_get_domain_generic_chip(domain, 0);
gc->reg_base = of_io_request_and_map(node, 0, of_node_full_name(node));
- if (!gc->reg_base) {
+ if (IS_ERR(gc->reg_base)) {
pr_err("unable to map resource\n");
- ret = -ENOMEM;
+ ret = PTR_ERR(gc->reg_base);
goto fail_irqd_remove;
}
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index bc94dff08d21..ea3d0eed47e1 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -49,6 +49,10 @@
#define FLASH_LED_REG_VPH_DROOP_THRESHOLD(base) (base + 0x61)
#define FLASH_LED_REG_VPH_DROOP_DEBOUNCE(base) (base + 0x62)
#define FLASH_LED_REG_ILED_GRT_THRSH(base) (base + 0x67)
+#define FLASH_LED_REG_LED1N2_ICLAMP_LOW(base) (base + 0x68)
+#define FLASH_LED_REG_LED1N2_ICLAMP_MID(base) (base + 0x69)
+#define FLASH_LED_REG_LED3_ICLAMP_LOW(base) (base + 0x6A)
+#define FLASH_LED_REG_LED3_ICLAMP_MID(base) (base + 0x6B)
#define FLASH_LED_REG_MITIGATION_SEL(base) (base + 0x6E)
#define FLASH_LED_REG_MITIGATION_SW(base) (base + 0x6F)
#define FLASH_LED_REG_LMH_LEVEL(base) (base + 0x70)
@@ -58,6 +62,7 @@
#define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4)
#define FLASH_LED_CURRENT_MASK GENMASK(6, 0)
#define FLASH_LED_ENABLE_MASK GENMASK(2, 0)
+#define FLASH_HW_STROBE_MASK GENMASK(2, 0)
#define FLASH_LED_SAFETY_TMR_MASK GENMASK(7, 0)
#define FLASH_LED_INT_RT_STS_MASK GENMASK(7, 0)
#define FLASH_LED_ISC_WARMUP_DELAY_MASK GENMASK(1, 0)
@@ -72,7 +77,7 @@
#define FLASH_LED_THERMAL_THRSH_MASK GENMASK(2, 0)
#define FLASH_LED_THERMAL_OTST_MASK GENMASK(2, 0)
#define FLASH_LED_MOD_CTRL_MASK BIT(7)
-#define FLASH_LED_HW_SW_STROBE_SEL_MASK BIT(2)
+#define FLASH_LED_HW_SW_STROBE_SEL_BIT BIT(2)
#define FLASH_LED_VPH_DROOP_FAULT_MASK BIT(4)
#define FLASH_LED_LMH_MITIGATION_EN_MASK BIT(0)
#define FLASH_LED_CHGR_MITIGATION_EN_MASK BIT(4)
@@ -82,6 +87,8 @@
#define VPH_DROOP_THRESH_MV_TO_VAL(val_mv) ((val_mv / 100) - 25)
#define VPH_DROOP_THRESH_VAL_TO_UV(val) ((val + 25) * 100000)
#define MITIGATION_THRSH_MA_TO_VAL(val_ma) (val_ma / 100)
+#define CURRENT_MA_TO_REG_VAL(curr_ma, ires_ua) ((curr_ma * 1000) / ires_ua - 1)
+#define SAFETY_TMR_TO_REG_VAL(duration_ms) ((duration_ms / 10) - 1)
#define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6
#define FLASH_LED_WARMUP_DELAY_DEFAULT 2
@@ -97,8 +104,6 @@
#define FLASH_LED_VLED_MAX_DEFAULT_UV 3500000
#define FLASH_LED_IBATT_OCP_THRESH_DEFAULT_UA 4500000
#define FLASH_LED_RPARA_DEFAULT_UOHM 0
-#define FLASH_LED_SAFETY_TMR_VAL_OFFSET 1
-#define FLASH_LED_SAFETY_TMR_VAL_DIVISOR 10
#define FLASH_LED_SAFETY_TMR_ENABLE BIT(7)
#define FLASH_LED_LMH_LEVEL_DEFAULT 0
#define FLASH_LED_LMH_MITIGATION_ENABLE 1
@@ -172,18 +177,12 @@ struct flash_node_data {
bool led_on;
};
-struct flash_regulator_data {
- struct regulator *vreg;
- const char *reg_name;
- u32 max_volt_uv;
-};
struct flash_switch_data {
struct platform_device *pdev;
+ struct regulator *vreg;
struct led_classdev cdev;
- struct flash_regulator_data *reg_data;
int led_mask;
- int num_regulators;
bool regulator_on;
bool enabled;
};
@@ -201,6 +200,10 @@ struct flash_led_platform_data {
int rpara_uohm;
int lmh_rbatt_threshold_uohm;
int lmh_ocv_threshold_uv;
+ u32 led1n2_iclamp_low_ma;
+ u32 led1n2_iclamp_mid_ma;
+ u32 led3_iclamp_low_ma;
+ u32 led3_iclamp_mid_ma;
u8 isc_delay;
u8 warmup_delay;
u8 current_derate_en_cfg;
@@ -384,6 +387,46 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
if (rc < 0)
return rc;
+ if (led->pdata->led1n2_iclamp_low_ma) {
+ val = CURRENT_MA_TO_REG_VAL(led->pdata->led1n2_iclamp_low_ma,
+ led->fnode[0].ires_ua);
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_LED1N2_ICLAMP_LOW(led->base),
+ FLASH_LED_CURRENT_MASK, val);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (led->pdata->led1n2_iclamp_mid_ma) {
+ val = CURRENT_MA_TO_REG_VAL(led->pdata->led1n2_iclamp_mid_ma,
+ led->fnode[0].ires_ua);
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_LED1N2_ICLAMP_MID(led->base),
+ FLASH_LED_CURRENT_MASK, val);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (led->pdata->led3_iclamp_low_ma) {
+ val = CURRENT_MA_TO_REG_VAL(led->pdata->led3_iclamp_low_ma,
+ led->fnode[3].ires_ua);
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_LED3_ICLAMP_LOW(led->base),
+ FLASH_LED_CURRENT_MASK, val);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (led->pdata->led3_iclamp_mid_ma) {
+ val = CURRENT_MA_TO_REG_VAL(led->pdata->led3_iclamp_mid_ma,
+ led->fnode[3].ires_ua);
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_LED3_ICLAMP_MID(led->base),
+ FLASH_LED_CURRENT_MASK, val);
+ if (rc < 0)
+ return rc;
+ }
+
return 0;
}
@@ -425,34 +468,27 @@ static int qpnp_flash_led_hw_strobe_enable(struct flash_node_data *fnode,
static int qpnp_flash_led_regulator_enable(struct qpnp_flash_led *led,
struct flash_switch_data *snode, bool on)
{
- int i, rc = 0;
+ int rc = 0;
+
+ if (!snode || !snode->vreg)
+ return 0;
if (snode->regulator_on == on)
return 0;
- if (on == false) {
- i = snode->num_regulators;
- goto out;
- }
+ if (on)
+ rc = regulator_enable(snode->vreg);
+ else
+ rc = regulator_disable(snode->vreg);
- for (i = 0; i < snode->num_regulators; i++) {
- rc = regulator_enable(snode->reg_data[i].vreg);
- if (rc < 0) {
- dev_err(&led->pdev->dev,
- "regulator enable failed, rc=%d\n", rc);
- goto out;
- }
+ if (rc < 0) {
+ dev_err(&led->pdev->dev, "regulator_%s failed, rc=%d\n",
+ on ? "enable" : "disable", rc);
+ return rc;
}
- snode->regulator_on = true;
- return rc;
-
-out:
- while (i--)
- regulator_disable(snode->reg_data[i].vreg);
-
- snode->regulator_on = false;
- return rc;
+ snode->regulator_on = on ? true : false;
+ return 0;
}
static int get_property_from_fg(struct qpnp_flash_led *led,
@@ -738,7 +774,8 @@ static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value)
prgm_current_ma = min(prgm_current_ma, fnode->max_current);
fnode->current_ma = prgm_current_ma;
fnode->cdev.brightness = prgm_current_ma;
- fnode->current_reg_val = prgm_current_ma * 1000 / fnode->ires_ua + 1;
+ fnode->current_reg_val = CURRENT_MA_TO_REG_VAL(prgm_current_ma,
+ fnode->ires_ua);
fnode->led_on = prgm_current_ma != 0;
}
@@ -810,7 +847,7 @@ static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode)
}
}
- if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_MASK) {
+ if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_BIT) {
rc = qpnp_flash_led_hw_strobe_enable(&led->fnode[i],
led->pdata->hw_strobe_option, false);
if (rc < 0) {
@@ -830,10 +867,10 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
{
struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev);
int rc, i, addr_offset;
- u8 val;
+ u8 val, mask;
if (snode->enabled == on) {
- dev_warn(&led->pdev->dev, "Switch node is already %s!\n",
+ dev_dbg(&led->pdev->dev, "Switch node is already %s!\n",
on ? "enabled" : "disabled");
return 0;
}
@@ -868,9 +905,13 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
continue;
addr_offset = led->fnode[i].id;
+ if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_BIT)
+ mask = FLASH_HW_STROBE_MASK;
+ else
+ mask = FLASH_LED_HW_SW_STROBE_SEL_BIT;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_STROBE_CTRL(led->base + addr_offset),
- FLASH_LED_ENABLE_MASK, led->fnode[i].trigger);
+ mask, led->fnode[i].trigger);
if (rc < 0)
return rc;
@@ -898,7 +939,7 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
}
}
- if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_MASK) {
+ if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_BIT) {
rc = qpnp_flash_led_hw_strobe_enable(&led->fnode[i],
led->pdata->hw_strobe_option, true);
if (rc < 0) {
@@ -1044,6 +1085,32 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
spin_unlock(&led->lock);
}
+/* sysfs show function for flash_max_current */
+static ssize_t qpnp_flash_led_max_current_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rc;
+ struct flash_switch_data *snode;
+ struct qpnp_flash_led *led;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+ snode = container_of(led_cdev, struct flash_switch_data, cdev);
+ led = dev_get_drvdata(&snode->pdev->dev);
+
+ rc = qpnp_flash_led_get_max_avail_current(led);
+ if (rc < 0)
+ dev_err(&led->pdev->dev, "query max current failed, rc=%d\n",
+ rc);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", rc);
+}
+
+/* sysfs attributes exported by flash_led */
+static struct device_attribute qpnp_flash_led_attrs[] = {
+ __ATTR(max_current, (S_IRUGO | S_IWUSR | S_IWGRP),
+ qpnp_flash_led_max_current_show, NULL),
+};
+
static int flash_led_psy_notifier_call(struct notifier_block *nb,
unsigned long ev, void *v)
{
@@ -1150,104 +1217,6 @@ int qpnp_flash_led_unregister_irq_notifier(struct notifier_block *nb)
return atomic_notifier_chain_unregister(&irq_notifier_list, nb);
}
-static int qpnp_flash_led_regulator_setup(struct qpnp_flash_led *led,
- struct flash_switch_data *snode, bool on)
-{
- int i, rc = 0;
-
- if (on == false) {
- i = snode->num_regulators;
- goto out;
- }
-
- for (i = 0; i < snode->num_regulators; i++) {
- snode->reg_data[i].vreg = regulator_get(snode->cdev.dev,
- snode->reg_data[i].reg_name);
- if (IS_ERR(snode->reg_data[i].vreg)) {
- rc = PTR_ERR(snode->reg_data[i].vreg);
- dev_err(&led->pdev->dev,
- "Failed to get regulator, rc=%d\n", rc);
- goto out;
- }
-
- if (regulator_count_voltages(snode->reg_data[i].vreg) > 0) {
- rc = regulator_set_voltage(snode->reg_data[i].vreg,
- snode->reg_data[i].max_volt_uv,
- snode->reg_data[i].max_volt_uv);
- if (rc < 0) {
- dev_err(&led->pdev->dev,
- "regulator set voltage failed, rc=%d\n",
- rc);
- regulator_put(snode->reg_data[i].vreg);
- goto out;
- }
- }
- }
-
- return rc;
-
-out:
- while (i--) {
- if (regulator_count_voltages(snode->reg_data[i].vreg) > 0)
- regulator_set_voltage(snode->reg_data[i].vreg, 0,
- snode->reg_data[i].max_volt_uv);
-
- regulator_put(snode->reg_data[i].vreg);
- }
-
- return rc;
-}
-
-static int qpnp_flash_led_regulator_parse_dt(struct qpnp_flash_led *led,
- struct flash_switch_data *snode,
- struct device_node *node) {
-
- int i = 0, rc = 0, num_regs = 0;
- struct device_node *temp = NULL;
- const char *temp_string;
- u32 val;
-
- while ((temp = of_get_next_available_child(node, temp))) {
- if (of_find_property(temp, "regulator-name", NULL))
- num_regs++;
- }
- snode->num_regulators = num_regs;
-
- if (snode->num_regulators == 0)
- return 0;
-
- snode->reg_data = devm_kcalloc(&led->pdev->dev, snode->num_regulators,
- sizeof(*snode->reg_data),
- GFP_KERNEL);
- if (!snode->reg_data)
- return -ENOMEM;
-
- for_each_available_child_of_node(node, temp) {
- rc = of_property_read_string(temp, "regulator-name",
- &temp_string);
- if (!rc)
- snode->reg_data[i].reg_name = temp_string;
- else {
- dev_err(&led->pdev->dev,
- "Unable to read regulator name, rc=%d\n", rc);
- return rc;
- }
-
- rc = of_property_read_u32(temp, "max-voltage-uv", &val);
- if (!rc) {
- snode->reg_data[i].max_volt_uv = val;
- } else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev,
- "Unable to read max voltage, rc=%d\n", rc);
- return rc;
- }
-
- i++;
- }
-
- return 0;
-}
-
static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
struct flash_node_data *fnode, struct device_node *node)
{
@@ -1341,9 +1310,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
fnode->duration = FLASH_LED_SAFETY_TMR_DISABLED;
rc = of_property_read_u32(node, "qcom,duration-ms", &val);
if (!rc) {
- fnode->duration = (u8)(((val -
- FLASH_LED_SAFETY_TMR_VAL_OFFSET) /
- FLASH_LED_SAFETY_TMR_VAL_DIVISOR) |
+ fnode->duration = (u8)(SAFETY_TMR_TO_REG_VAL(val) |
FLASH_LED_SAFETY_TMR_ENABLE);
} else if (rc == -EINVAL) {
if (fnode->type == FLASH_LED_TYPE_FLASH) {
@@ -1390,7 +1357,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
}
fnode->trigger = (strobe_sel << 2) | (edge_trigger << 1) | active_high;
- if (fnode->trigger & FLASH_LED_HW_SW_STROBE_SEL_MASK) {
+ if (fnode->trigger & FLASH_LED_HW_SW_STROBE_SEL_BIT) {
if (of_find_property(node, "qcom,hw-strobe-gpio", NULL)) {
fnode->hw_strobe_gpio = of_get_named_gpio(node,
"qcom,hw-strobe-gpio", 0);
@@ -1436,7 +1403,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
fnode->pinctrl = devm_pinctrl_get(fnode->cdev.dev);
if (IS_ERR_OR_NULL(fnode->pinctrl)) {
- dev_warn(&led->pdev->dev, "No pinctrl defined\n");
+ dev_dbg(&led->pdev->dev, "No pinctrl defined\n");
fnode->pinctrl = NULL;
} else {
fnode->gpio_state_active =
@@ -1467,7 +1434,8 @@ static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led,
struct flash_switch_data *snode,
struct device_node *node)
{
- int rc = 0;
+ int rc = 0, num;
+ char reg_name[16], reg_sup_name[16];
rc = of_property_read_string(node, "qcom,led-name", &snode->cdev.name);
if (rc < 0) {
@@ -1476,6 +1444,12 @@ static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led,
return rc;
}
+ rc = sscanf(snode->cdev.name, "led:switch_%d", &num);
+ if (!rc) {
+ pr_err("No number for switch device?\n");
+ return -EINVAL;
+ }
+
rc = of_property_read_string(node, "qcom,default-led-trigger",
&snode->cdev.default_trigger);
if (rc < 0) {
@@ -1495,18 +1469,16 @@ static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led,
return -EINVAL;
}
- rc = qpnp_flash_led_regulator_parse_dt(led, snode, node);
- if (rc < 0) {
- dev_err(&led->pdev->dev,
- "Unable to parse regulator data, rc=%d\n", rc);
- return rc;
- }
-
- if (snode->num_regulators) {
- rc = qpnp_flash_led_regulator_setup(led, snode, true);
- if (rc < 0) {
- dev_err(&led->pdev->dev,
- "Unable to setup regulator, rc=%d\n", rc);
+ scnprintf(reg_name, sizeof(reg_name), "switch%d-supply", num);
+ if (of_find_property(led->pdev->dev.of_node, reg_name, NULL)) {
+ scnprintf(reg_sup_name, sizeof(reg_sup_name), "switch%d", num);
+ snode->vreg = devm_regulator_get(&led->pdev->dev, reg_sup_name);
+ if (IS_ERR_OR_NULL(snode->vreg)) {
+ rc = PTR_ERR(snode->vreg);
+ if (rc != -EPROBE_DEFER)
+ dev_err(&led->pdev->dev, "Failed to get regulator, rc=%d\n",
+ rc);
+ snode->vreg = NULL;
return rc;
}
}
@@ -1647,6 +1619,42 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return rc;
}
+ rc = of_property_read_u32(node, "qcom,led1n2-iclamp-low-ma", &val);
+ if (!rc) {
+ led->pdata->led1n2_iclamp_low_ma = val;
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev, "Unable to read led1n2_iclamp_low current, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,led1n2-iclamp-mid-ma", &val);
+ if (!rc) {
+ led->pdata->led1n2_iclamp_mid_ma = val;
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev, "Unable to read led1n2_iclamp_mid current, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,led3-iclamp-low-ma", &val);
+ if (!rc) {
+ led->pdata->led3_iclamp_low_ma = val;
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev, "Unable to read led3_iclamp_low current, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,led3-iclamp-mid-ma", &val);
+ if (!rc) {
+ led->pdata->led3_iclamp_mid_ma = val;
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev, "Unable to read led3_iclamp_mid current, rc=%d\n",
+ rc);
+ return rc;
+ }
+
led->pdata->vled_max_uv = FLASH_LED_VLED_MAX_DEFAULT_UV;
rc = of_property_read_u32(node, "qcom,vled-max-uv", &val);
if (!rc) {
@@ -1781,7 +1789,7 @@ static int qpnp_flash_led_probe(struct platform_device *pdev)
struct device_node *node, *temp;
const char *temp_string;
unsigned int base;
- int rc, i = 0;
+ int rc, i = 0, j = 0;
node = pdev->dev.of_node;
if (!node) {
@@ -1859,26 +1867,35 @@ static int qpnp_flash_led_probe(struct platform_device *pdev)
return -ENOMEM;
temp = NULL;
- for (i = 0; i < led->num_fnodes; i++) {
- temp = of_get_next_available_child(node, temp);
- rc = qpnp_flash_led_parse_each_led_dt(led,
- &led->fnode[i], temp);
+ i = 0;
+ j = 0;
+ for_each_available_child_of_node(node, temp) {
+ rc = of_property_read_string(temp, "label", &temp_string);
if (rc < 0) {
dev_err(&pdev->dev,
- "Unable to parse flash node %d rc=%d\n", i, rc);
- goto error_led_register;
+ "Failed to parse label, rc=%d\n", rc);
+ return rc;
}
- }
- for (i = 0; i < led->num_snodes; i++) {
- temp = of_get_next_available_child(node, temp);
- rc = qpnp_flash_led_parse_and_register_switch(led,
- &led->snode[i], temp);
- if (rc < 0) {
- dev_err(&pdev->dev,
- "Unable to parse and register switch node, rc=%d\n",
- rc);
- goto error_switch_register;
+ if (!strcmp("flash", temp_string) ||
+ !strcmp("torch", temp_string)) {
+ rc = qpnp_flash_led_parse_each_led_dt(led,
+ &led->fnode[i++], temp);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Unable to parse flash node %d rc=%d\n",
+ i, rc);
+ goto error_led_register;
+ }
+ }
+
+ if (!strcmp("switch", temp_string)) {
+ rc = qpnp_flash_led_parse_and_register_switch(led,
+ &led->snode[j++], temp);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Unable to parse and register switch node, rc=%d\n",
+ rc);
+ goto error_switch_register;
+ }
}
}
@@ -1942,12 +1959,36 @@ static int qpnp_flash_led_probe(struct platform_device *pdev)
goto unreg_notifier;
}
+ for (i = 0; i < led->num_snodes; i++) {
+ for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++) {
+ rc = sysfs_create_file(&led->snode[i].cdev.dev->kobj,
+ &qpnp_flash_led_attrs[j].attr);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "sysfs creation failed, rc=%d\n",
+ rc);
+ goto sysfs_fail;
+ }
+ }
+ }
+
spin_lock_init(&led->lock);
dev_set_drvdata(&pdev->dev, led);
return 0;
+sysfs_fail:
+ for (--j; j >= 0; j--)
+ sysfs_remove_file(&led->snode[i].cdev.dev->kobj,
+ &qpnp_flash_led_attrs[j].attr);
+
+ for (--i; i >= 0; i--) {
+ for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++)
+ sysfs_remove_file(&led->snode[i].cdev.dev->kobj,
+ &qpnp_flash_led_attrs[j].attr);
+ }
+
+ i = led->num_snodes;
unreg_notifier:
power_supply_unreg_notifier(&led->nb);
error_switch_register:
@@ -1964,20 +2005,21 @@ error_led_register:
static int qpnp_flash_led_remove(struct platform_device *pdev)
{
struct qpnp_flash_led *led = dev_get_drvdata(&pdev->dev);
- int i;
+ int i, j;
for (i = 0; i < led->num_snodes; i++) {
- if (led->snode[i].num_regulators) {
- if (led->snode[i].regulator_on)
- qpnp_flash_led_regulator_enable(led,
- &led->snode[i], false);
- qpnp_flash_led_regulator_setup(led,
+ for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++)
+ sysfs_remove_file(&led->snode[i].cdev.dev->kobj,
+ &qpnp_flash_led_attrs[j].attr);
+
+ if (led->snode[i].regulator_on)
+ qpnp_flash_led_regulator_enable(led,
&led->snode[i], false);
- }
}
while (i > 0)
led_classdev_unregister(&led->snode[--i].cdev);
+
i = led->num_fnodes;
while (i > 0)
led_classdev_unregister(&led->fnode[--i].cdev);
diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c
index d9626c29ce76..894c1d88b3ef 100644
--- a/drivers/leds/leds-qpnp-wled.c
+++ b/drivers/leds/leds-qpnp-wled.c
@@ -25,6 +25,7 @@
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/leds-qpnp-wled.h>
+#include <linux/qpnp/qpnp-revid.h>
#define QPNP_IRQ_FLAGS (IRQF_TRIGGER_RISING | \
IRQF_TRIGGER_FALLING | \
@@ -44,15 +45,19 @@
#define QPNP_WLED_SWITCH_FREQ_REG(b) (b + 0x4C)
#define QPNP_WLED_OVP_REG(b) (b + 0x4D)
#define QPNP_WLED_ILIM_REG(b) (b + 0x4E)
+#define QPNP_WLED_AMOLED_VOUT_REG(b) (b + 0x4F)
#define QPNP_WLED_SOFTSTART_RAMP_DLY(b) (b + 0x53)
#define QPNP_WLED_VLOOP_COMP_RES_REG(b) (b + 0x55)
#define QPNP_WLED_VLOOP_COMP_GM_REG(b) (b + 0x56)
#define QPNP_WLED_PSM_CTRL_REG(b) (b + 0x5B)
#define QPNP_WLED_SC_PRO_REG(b) (b + 0x5E)
+#define QPNP_WLED_SWIRE_AVDD_REG(b) (b + 0x5F)
+#define QPNP_WLED_CTRL_SPARE_REG(b) (b + 0xDF)
#define QPNP_WLED_TEST1_REG(b) (b + 0xE2)
#define QPNP_WLED_TEST4_REG(b) (b + 0xE5)
#define QPNP_WLED_REF_7P7_TRIM_REG(b) (b + 0xF2)
+#define QPNP_WLED_7P7_TRIM_MASK GENMASK(3, 0)
#define QPNP_WLED_EN_MASK 0x7F
#define QPNP_WLED_EN_SHIFT 7
#define QPNP_WLED_FDBK_OP_MASK 0xF8
@@ -79,16 +84,16 @@
#define QPNP_WLED_VREF_PSM_MAX_MV 750
#define QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV 450
#define QPNP_WLED_PSM_CTRL_OVERWRITE 0x80
-#define QPNP_WLED_AVDD_MIN_TRIM_VALUE -7
-#define QPNP_WLED_AVDD_MAX_TRIM_VALUE 8
-#define QPNP_WLED_AVDD_TRIM_CENTER_VALUE 7
-
-#define QPNP_WLED_ILIM_MASK 0xF8
-#define QPNP_WLED_ILIM_MIN_MA 105
-#define QPNP_WLED_ILIM_MAX_MA 1980
-#define QPNP_WLED_ILIM_STEP_MA 280
-#define QPNP_WLED_DFLT_ILIM_MA 980
-#define QPNP_WLED_ILIM_OVERWRITE 0x80
+
+#define QPNP_WLED_ILIM_MASK GENMASK(2, 0)
+#define QPNP_WLED_ILIM_OVERWRITE BIT(7)
+#define PMI8994_WLED_ILIM_MIN_MA 105
+#define PMI8994_WLED_ILIM_MAX_MA 1980
+#define PMI8994_WLED_DFLT_ILIM_MA 980
+#define PMI8994_AMOLED_DFLT_ILIM_MA 385
+#define PMICOBALT_WLED_ILIM_MAX_MA 1500
+#define PMICOBALT_WLED_DFLT_ILIM_MA 970
+#define PMICOBALT_AMOLED_DFLT_ILIM_MA 620
#define QPNP_WLED_BOOST_DUTY_MASK 0xFC
#define QPNP_WLED_BOOST_DUTY_STEP_NS 52
#define QPNP_WLED_BOOST_DUTY_MIN_NS 26
@@ -98,11 +103,7 @@
#define QPNP_WLED_SWITCH_FREQ_800_KHZ 800
#define QPNP_WLED_SWITCH_FREQ_1600_KHZ 1600
#define QPNP_WLED_SWITCH_FREQ_OVERWRITE 0x80
-#define QPNP_WLED_OVP_MASK 0xFC
-#define QPNP_WLED_OVP_17800_MV 17800
-#define QPNP_WLED_OVP_19400_MV 19400
-#define QPNP_WLED_OVP_29500_MV 29500
-#define QPNP_WLED_OVP_31000_MV 31000
+#define QPNP_WLED_OVP_MASK GENMASK(1, 0)
#define QPNP_WLED_TEST4_EN_VREF_UP 0x32
#define QPNP_WLED_INT_EN_SET_OVP_EN 0x02
#define QPNP_WLED_OVP_FLT_SLEEP_US 10
@@ -198,6 +199,22 @@
#define QPNP_WLED_MIN_MSLEEP 20
#define QPNP_WLED_SC_DLY_MS 20
+#define NUM_SUPPORTED_AVDD_VOLTAGES 6
+#define QPNP_WLED_DFLT_AVDD_MV 7600
+#define QPNP_WLED_AVDD_MIN_MV 5650
+#define QPNP_WLED_AVDD_MAX_MV 7900
+#define QPNP_WLED_AVDD_STEP_MV 150
+#define QPNP_WLED_AVDD_MIN_TRIM_VAL 0x0
+#define QPNP_WLED_AVDD_MAX_TRIM_VAL 0xF
+#define QPNP_WLED_AVDD_SEL_SPMI_BIT BIT(7)
+#define QPNP_WLED_AVDD_SET_BIT BIT(4)
+
+#define NUM_SUPPORTED_OVP_THRESHOLDS 4
+#define NUM_SUPPORTED_ILIM_THRESHOLDS 8
+
+#define QPNP_WLED_AVDD_MV_TO_REG(val) \
+ ((val - QPNP_WLED_AVDD_MIN_MV) / QPNP_WLED_AVDD_STEP_MV)
+
/* output feedback mode */
enum qpnp_wled_fdbk_op {
QPNP_WLED_FDBK_AUTO,
@@ -230,10 +247,38 @@ static u8 qpnp_wled_sink_dbg_regs[] = {
0xe6,
};
+static int qpnp_wled_avdd_target_voltages[NUM_SUPPORTED_AVDD_VOLTAGES] = {
+ 7900, 7600, 7300, 6400, 6100, 5800,
+};
+
+static u8 qpnp_wled_ovp_reg_settings[NUM_SUPPORTED_AVDD_VOLTAGES] = {
+ 0x0, 0x0, 0x1, 0x2, 0x2, 0x3,
+};
+
+static int qpnp_wled_avdd_trim_adjustments[NUM_SUPPORTED_AVDD_VOLTAGES] = {
+ 3, 0, -2, 7, 3, 3,
+};
+
+static int qpnp_wled_ovp_thresholds_pmi8994[NUM_SUPPORTED_OVP_THRESHOLDS] = {
+ 31000, 29500, 19400, 17800,
+};
+
+static int qpnp_wled_ovp_thresholds_pmicobalt[NUM_SUPPORTED_OVP_THRESHOLDS] = {
+ 31100, 29600, 19600, 18100,
+};
+
+static int qpnp_wled_ilim_settings_pmi8994[NUM_SUPPORTED_ILIM_THRESHOLDS] = {
+ 105, 385, 660, 980, 1150, 1420, 1700, 1980,
+};
+
+static int qpnp_wled_ilim_settings_pmicobalt[NUM_SUPPORTED_ILIM_THRESHOLDS] = {
+ 105, 280, 450, 620, 970, 1150, 1300, 1500,
+};
+
/**
* qpnp_wled - wed data structure
* @ cdev - led class device
- * @ spmi - spmi device
+ * @ pdev - platform device
* @ work - worker for led operation
* @ lock - mutex lock for exclusive access
* @ fdbk_op - output feedback mode
@@ -241,7 +286,7 @@ static u8 qpnp_wled_sink_dbg_regs[] = {
* @ ovp_irq - over voltage protection irq
* @ sc_irq - short circuit irq
* @ sc_cnt - short circuit irq count
- * @ avdd_trim_steps_from_center - number of steps to trim from center value
+ * @ avdd_target_voltage_mv - target voltage for AVDD module in mV
* @ ctrl_base - base address for wled ctrl
* @ sink_base - base address for wled sink
* @ ibb_base - base address for IBB(Inverting Buck Boost)
@@ -264,6 +309,7 @@ static u8 qpnp_wled_sink_dbg_regs[] = {
* @ cons_sync_write_delay_us - delay between two consecutive writes to SYNC
* @ strings - supported list of strings
* @ num_strings - number of strings
+ * @ avdd_mode_spmi - enable avdd programming via spmi
* @ en_9b_dim_res - enable or disable 9bit dimming
* @ en_phase_stag - enable or disable phase staggering
* @ en_cabc - enable or disable cabc
@@ -276,14 +322,16 @@ struct qpnp_wled {
struct led_classdev cdev;
struct platform_device *pdev;
struct regmap *regmap;
+ struct pmic_revid_data *pmic_rev_id;
struct work_struct work;
struct mutex lock;
+ struct mutex bus_lock;
enum qpnp_wled_fdbk_op fdbk_op;
enum qpnp_wled_dim_mode dim_mode;
int ovp_irq;
int sc_irq;
u32 sc_cnt;
- u32 avdd_trim_steps_from_center;
+ u32 avdd_target_voltage_mv;
u16 ctrl_base;
u16 sink_base;
u16 mod_freq_khz;
@@ -304,6 +352,7 @@ struct qpnp_wled {
u16 cons_sync_write_delay_us;
u8 strings[QPNP_WLED_MAX_STRINGS];
u8 num_strings;
+ bool avdd_mode_spmi;
bool en_9b_dim_res;
bool en_phase_stag;
bool en_cabc;
@@ -319,39 +368,79 @@ static int qpnp_wled_read_reg(struct qpnp_wled *wled, u8 *data, u16 addr)
uint val;
rc = regmap_read(wled->regmap, addr, &val);
- if (rc < 0)
+ if (rc < 0) {
dev_err(&wled->pdev->dev,
"Error reading address: %x(%d)\n", addr, rc);
+ return rc;
+ }
+
*data = (u8)val;
- return rc;
+ return 0;
}
/* helper to write a pmic register */
-static int qpnp_wled_write_reg(struct qpnp_wled *wled, u8 *data, u16 addr)
+static int qpnp_wled_write_reg(struct qpnp_wled *wled, u8 data, u16 addr)
{
int rc;
- rc = regmap_write(wled->regmap, addr, *data);
+ mutex_lock(&wled->bus_lock);
+ rc = regmap_write(wled->regmap, addr, data);
+ if (rc < 0) {
+ dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
+ addr, rc);
+ goto out;
+ }
+
+ dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
+out:
+ mutex_unlock(&wled->bus_lock);
+ return rc;
+}
+
+static int qpnp_wled_masked_write_reg(struct qpnp_wled *wled, u8 mask, u8 *data,
+ u16 addr)
+{
+ u8 reg;
+ int rc;
+
+ rc = qpnp_wled_read_reg(wled, &reg, addr);
if (rc < 0)
- dev_err(&wled->pdev->dev,
- "Error writing address: %x(%d)\n", addr, rc);
+ return rc;
+
+ reg &= ~mask;
+ reg |= *data & mask;
- dev_dbg(&wled->pdev->dev, "write: WLED_0x%x = 0x%x\n", addr, *data);
+ rc = qpnp_wled_write_reg(wled, reg, addr);
return rc;
}
-static int qpnp_wled_sec_access(struct qpnp_wled *wled, u16 base_addr)
+static int qpnp_wled_sec_write_reg(struct qpnp_wled *wled, u8 data, u16 addr)
{
int rc;
u8 reg = QPNP_WLED_SEC_UNLOCK;
+ u16 base_addr = addr & 0xFF00;
- rc = qpnp_wled_write_reg(wled, &reg,
- QPNP_WLED_SEC_ACCESS_REG(base_addr));
- if (rc)
- return rc;
+ mutex_lock(&wled->bus_lock);
+ rc = regmap_write(wled->regmap, QPNP_WLED_SEC_ACCESS_REG(base_addr),
+ reg);
+ if (rc < 0) {
+ dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
+ QPNP_WLED_SEC_ACCESS_REG(base_addr), rc);
+ goto out;
+ }
- return 0;
+ rc = regmap_write(wled->regmap, addr, data);
+ if (rc < 0) {
+ dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
+ addr, rc);
+ goto out;
+ }
+
+ dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
+out:
+ mutex_unlock(&wled->bus_lock);
+ return rc;
}
static int qpnp_wled_sync_reg_toggle(struct qpnp_wled *wled)
@@ -361,7 +450,7 @@ static int qpnp_wled_sync_reg_toggle(struct qpnp_wled *wled)
/* sync */
reg = QPNP_WLED_SYNC;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_SYNC_REG(wled->sink_base));
if (rc < 0)
return rc;
@@ -371,7 +460,7 @@ static int qpnp_wled_sync_reg_toggle(struct qpnp_wled *wled)
wled->cons_sync_write_delay_us + 1);
reg = QPNP_WLED_SYNC_RESET;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_SYNC_REG(wled->sink_base));
if (rc < 0)
return rc;
@@ -388,7 +477,7 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
/* set brightness registers */
for (i = 0; i < wled->num_strings; i++) {
reg = level & QPNP_WLED_BRIGHT_LSB_MASK;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_BRIGHT_LSB_REG(wled->sink_base,
wled->strings[i]));
if (rc < 0)
@@ -396,7 +485,7 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
reg = level >> QPNP_WLED_BRIGHT_MSB_SHIFT;
reg = reg & QPNP_WLED_BRIGHT_MSB_MASK;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_BRIGHT_MSB_REG(wled->sink_base,
wled->strings[i]));
if (rc < 0)
@@ -421,7 +510,7 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled,
/* disable OVP fault interrupt */
if (state) {
reg = QPNP_WLED_INT_EN_SET_OVP_EN;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_INT_EN_CLR(base_addr));
if (rc)
return rc;
@@ -433,7 +522,7 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled,
return rc;
reg &= QPNP_WLED_MODULE_EN_MASK;
reg |= (state << QPNP_WLED_MODULE_EN_SHIFT);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_MODULE_EN_REG(base_addr));
if (rc)
return rc;
@@ -442,7 +531,7 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled,
if (state && (wled->ovp_irq > 0)) {
udelay(QPNP_WLED_OVP_FLT_SLEEP_US);
reg = QPNP_WLED_INT_EN_SET_OVP_EN;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_INT_EN_SET(base_addr));
if (rc)
return rc;
@@ -678,7 +767,7 @@ static ssize_t qpnp_wled_dim_mode_store(struct device *dev,
reg |= temp;
}
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_MOD_REG(wled->sink_base));
if (rc)
return rc;
@@ -722,7 +811,7 @@ static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev,
reg &= QPNP_WLED_FS_CURR_MASK;
temp = data / QPNP_WLED_FS_CURR_STEP_UA;
reg |= temp;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_FS_CURR_REG(wled->sink_base,
wled->strings[i]));
if (rc)
@@ -838,11 +927,7 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
reg &= QPNP_WLED_DISP_SEL_MASK;
reg |= (wled->disp_type_amoled << QPNP_WLED_DISP_SEL_SHIFT);
- rc = qpnp_wled_sec_access(wled, base_addr);
- if (rc)
- return rc;
-
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_sec_write_reg(wled, reg,
QPNP_WLED_DISP_SEL_REG(base_addr));
if (rc)
return rc;
@@ -863,7 +948,7 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
reg |= ((wled->vref_psm_mv - QPNP_WLED_VREF_PSM_MIN_MV)/
QPNP_WLED_VREF_PSM_STEP_MV);
reg |= QPNP_WLED_PSM_CTRL_OVERWRITE;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -887,7 +972,7 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
QPNP_WLED_LOOP_COMP_RES_MIN_KOHM)/
QPNP_WLED_LOOP_COMP_RES_STEP_KOHM);
reg |= QPNP_WLED_VLOOP_COMP_RES_OVERWRITE;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_VLOOP_COMP_RES_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -905,7 +990,7 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
reg &= QPNP_WLED_VLOOP_COMP_GM_MASK;
reg |= (wled->loop_ea_gm | QPNP_WLED_VLOOP_COMP_GM_OVERWRITE);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_VLOOP_COMP_GM_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -916,12 +1001,8 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
if (rc < 0)
return rc;
- rc = qpnp_wled_sec_access(wled, base_addr);
- if (rc)
- return rc;
-
reg |= QPNP_WLED_TEST4_EN_IIND_UP;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_sec_write_reg(wled, reg,
QPNP_WLED_TEST4_REG(base_addr));
if (rc)
return rc;
@@ -929,12 +1010,8 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
/*
* enable VREF_UP to avoid false ovp on low brightness for LCD
*/
- rc = qpnp_wled_sec_access(wled, base_addr);
- if (rc)
- return rc;
-
reg = QPNP_WLED_TEST4_EN_VREF_UP;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_sec_write_reg(wled, reg,
QPNP_WLED_TEST4_REG(base_addr));
if (rc)
return rc;
@@ -968,6 +1045,209 @@ static irqreturn_t qpnp_wled_sc_irq(int irq, void *_wled)
return IRQ_HANDLED;
}
+static bool is_avdd_trim_adjustment_required(struct qpnp_wled *wled)
+{
+ int rc;
+ u8 reg = 0;
+
+ /*
+ * AVDD trim adjustment is not required for pmicobalt/pm2falcon and not
+ * supported for pmi8994.
+ */
+ if (wled->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PM2FALCON_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PMI8994_SUBTYPE)
+ return false;
+
+ /*
+ * Configure TRIM_REG only if disp_type_amoled and it has
+ * not already been programmed by bootloader.
+ */
+ if (!wled->disp_type_amoled)
+ return false;
+
+ rc = qpnp_wled_read_reg(wled, &reg,
+ QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base));
+ if (rc < 0)
+ return false;
+
+ return !(reg & QPNP_WLED_AVDD_SET_BIT);
+}
+
+static int qpnp_wled_ovp_config(struct qpnp_wled *wled)
+{
+ int rc, i, *ovp_table;
+ u8 reg;
+
+ /*
+ * Configure the OVP register based on ovp_mv only if display type is
+ * not AMOLED.
+ */
+ if (wled->disp_type_amoled)
+ return 0;
+
+ if (wled->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PM2FALCON_SUBTYPE)
+ ovp_table = qpnp_wled_ovp_thresholds_pmicobalt;
+ else
+ ovp_table = qpnp_wled_ovp_thresholds_pmi8994;
+
+ for (i = 0; i < NUM_SUPPORTED_OVP_THRESHOLDS; i++) {
+ if (wled->ovp_mv == ovp_table[i])
+ break;
+ }
+
+ if (i == NUM_SUPPORTED_OVP_THRESHOLDS) {
+ dev_err(&wled->pdev->dev,
+ "Invalid ovp threshold specified in device tree\n");
+ return -EINVAL;
+ }
+
+ reg = i & QPNP_WLED_OVP_MASK;
+ rc = qpnp_wled_masked_write_reg(wled, QPNP_WLED_OVP_MASK, &reg,
+ QPNP_WLED_OVP_REG(wled->ctrl_base));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int qpnp_wled_avdd_trim_config(struct qpnp_wled *wled)
+{
+ int rc, i;
+ u8 reg;
+
+ for (i = 0; i < NUM_SUPPORTED_AVDD_VOLTAGES; i++) {
+ if (wled->avdd_target_voltage_mv ==
+ qpnp_wled_avdd_target_voltages[i])
+ break;
+ }
+
+ if (i == NUM_SUPPORTED_AVDD_VOLTAGES) {
+ dev_err(&wled->pdev->dev,
+ "Invalid avdd target voltage specified in device tree\n");
+ return -EINVAL;
+ }
+
+ /* Update WLED_OVP register based on desired target voltage */
+ reg = qpnp_wled_ovp_reg_settings[i];
+ rc = qpnp_wled_masked_write_reg(wled, QPNP_WLED_OVP_MASK, &reg,
+ QPNP_WLED_OVP_REG(wled->ctrl_base));
+ if (rc)
+ return rc;
+
+ /* Update WLED_TRIM register based on desired target voltage */
+ rc = qpnp_wled_read_reg(wled, &reg,
+ QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base));
+ if (rc)
+ return rc;
+
+ reg += qpnp_wled_avdd_trim_adjustments[i];
+ if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL ||
+ (s8)reg > QPNP_WLED_AVDD_MAX_TRIM_VAL) {
+ dev_dbg(&wled->pdev->dev,
+ "adjusted trim %d is not within range, capping it\n",
+ (s8)reg);
+ if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL)
+ reg = QPNP_WLED_AVDD_MIN_TRIM_VAL;
+ else
+ reg = QPNP_WLED_AVDD_MAX_TRIM_VAL;
+ }
+
+ reg &= QPNP_WLED_7P7_TRIM_MASK;
+ rc = qpnp_wled_sec_write_reg(wled, reg,
+ QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base));
+ if (rc < 0)
+ dev_err(&wled->pdev->dev, "Write to 7P7_TRIM register failed, rc=%d\n",
+ rc);
+ return rc;
+}
+
+static int qpnp_wled_avdd_mode_config(struct qpnp_wled *wled)
+{
+ int rc;
+ u8 reg = 0;
+
+ /*
+ * At present, configuring the mode to SPMI/SWIRE for controlling
+ * AVDD voltage is available only in pmicobalt/pm2falcon.
+ */
+ if (wled->pmic_rev_id->pmic_subtype != PMICOBALT_SUBTYPE &&
+ wled->pmic_rev_id->pmic_subtype != PM2FALCON_SUBTYPE)
+ return 0;
+
+ /* AMOLED_VOUT should be configured for AMOLED */
+ if (!wled->disp_type_amoled)
+ return 0;
+
+ /* Configure avdd register */
+ if (wled->avdd_target_voltage_mv > QPNP_WLED_AVDD_MAX_MV) {
+ dev_dbg(&wled->pdev->dev, "Capping avdd target voltage to %d\n",
+ QPNP_WLED_AVDD_MAX_MV);
+ wled->avdd_target_voltage_mv = QPNP_WLED_AVDD_MAX_MV;
+ } else if (wled->avdd_target_voltage_mv < QPNP_WLED_AVDD_MIN_MV) {
+ dev_info(&wled->pdev->dev, "Capping avdd target voltage to %d\n",
+ QPNP_WLED_AVDD_MIN_MV);
+ wled->avdd_target_voltage_mv = QPNP_WLED_AVDD_MIN_MV;
+ }
+
+ reg = QPNP_WLED_AVDD_MV_TO_REG(wled->avdd_target_voltage_mv);
+
+ if (wled->avdd_mode_spmi) {
+ reg |= QPNP_WLED_AVDD_SEL_SPMI_BIT;
+ rc = qpnp_wled_write_reg(wled, reg,
+ QPNP_WLED_AMOLED_VOUT_REG(wled->ctrl_base));
+ } else {
+ rc = qpnp_wled_write_reg(wled, reg,
+ QPNP_WLED_SWIRE_AVDD_REG(wled->ctrl_base));
+ }
+
+ if (rc < 0)
+ dev_err(&wled->pdev->dev, "Write to VOUT/AVDD register failed, rc=%d\n",
+ rc);
+ return rc;
+}
+
+static int qpnp_wled_ilim_config(struct qpnp_wled *wled)
+{
+ int rc, i, *ilim_table;
+ u8 reg;
+
+ if (wled->ilim_ma < PMI8994_WLED_ILIM_MIN_MA)
+ wled->ilim_ma = PMI8994_WLED_ILIM_MIN_MA;
+
+ if (wled->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PM2FALCON_SUBTYPE) {
+ ilim_table = qpnp_wled_ilim_settings_pmicobalt;
+ if (wled->ilim_ma > PMICOBALT_WLED_ILIM_MAX_MA)
+ wled->ilim_ma = PMICOBALT_WLED_ILIM_MAX_MA;
+ } else {
+ ilim_table = qpnp_wled_ilim_settings_pmi8994;
+ if (wled->ilim_ma > PMI8994_WLED_ILIM_MAX_MA)
+ wled->ilim_ma = PMI8994_WLED_ILIM_MAX_MA;
+ }
+
+ for (i = 0; i < NUM_SUPPORTED_ILIM_THRESHOLDS; i++) {
+ if (wled->ilim_ma == ilim_table[i])
+ break;
+ }
+
+ if (i == NUM_SUPPORTED_ILIM_THRESHOLDS) {
+ dev_err(&wled->pdev->dev,
+ "Invalid ilim threshold specified in device tree\n");
+ return -EINVAL;
+ }
+
+ reg = (i & QPNP_WLED_ILIM_MASK) | QPNP_WLED_ILIM_OVERWRITE;
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_ILIM_MASK | QPNP_WLED_ILIM_OVERWRITE,
+ &reg, QPNP_WLED_ILIM_REG(wled->ctrl_base));
+ if (rc < 0)
+ dev_err(&wled->pdev->dev, "Write to ILIM register failed, rc=%d\n",
+ rc);
+ return rc;
+}
+
/* Configure WLED registers */
static int qpnp_wled_config(struct qpnp_wled *wled)
{
@@ -986,7 +1266,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
return rc;
reg &= QPNP_WLED_FDBK_OP_MASK;
reg |= wled->fdbk_op;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_FDBK_OP_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -1004,35 +1284,21 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
reg &= QPNP_WLED_VREF_MASK;
temp = wled->vref_mv - QPNP_WLED_VREF_MIN_MV;
reg |= (temp / QPNP_WLED_VREF_STEP_MV);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_VREF_REG(wled->ctrl_base));
if (rc)
return rc;
/* Configure the ILIM register */
- if (wled->ilim_ma < QPNP_WLED_ILIM_MIN_MA)
- wled->ilim_ma = QPNP_WLED_ILIM_MIN_MA;
- else if (wled->ilim_ma > QPNP_WLED_ILIM_MAX_MA)
- wled->ilim_ma = QPNP_WLED_ILIM_MAX_MA;
-
- rc = qpnp_wled_read_reg(wled, &reg,
- QPNP_WLED_ILIM_REG(wled->ctrl_base));
- if (rc < 0)
+ rc = qpnp_wled_ilim_config(wled);
+ if (rc < 0) {
+ pr_err("Error in configuring wled ilim, rc=%d\n", rc);
return rc;
- temp = (wled->ilim_ma / QPNP_WLED_ILIM_STEP_MA);
- if (temp != (reg & ~QPNP_WLED_ILIM_MASK)) {
- reg &= QPNP_WLED_ILIM_MASK;
- reg |= temp;
- reg |= QPNP_WLED_ILIM_OVERWRITE;
- rc = qpnp_wled_write_reg(wled, &reg,
- QPNP_WLED_ILIM_REG(wled->ctrl_base));
- if (rc)
- return rc;
}
/* Configure the Soft start Ramp delay: for AMOLED - 0,for LCD - 2 */
reg = (wled->disp_type_amoled) ? 0 : 2;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_SOFTSTART_RAMP_DLY(wled->ctrl_base));
if (rc)
return rc;
@@ -1049,7 +1315,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
return rc;
reg &= QPNP_WLED_BOOST_DUTY_MASK;
reg |= (wled->boost_duty_ns / QPNP_WLED_BOOST_DUTY_STEP_NS);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -1066,62 +1332,27 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
return rc;
reg &= QPNP_WLED_SWITCH_FREQ_MASK;
reg |= (temp | QPNP_WLED_SWITCH_FREQ_OVERWRITE);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base));
if (rc)
return rc;
- /* Configure the OVP register */
- if (wled->ovp_mv <= QPNP_WLED_OVP_17800_MV) {
- wled->ovp_mv = QPNP_WLED_OVP_17800_MV;
- temp = 3;
- } else if (wled->ovp_mv <= QPNP_WLED_OVP_19400_MV) {
- wled->ovp_mv = QPNP_WLED_OVP_19400_MV;
- temp = 2;
- } else if (wled->ovp_mv <= QPNP_WLED_OVP_29500_MV) {
- wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
- temp = 1;
- } else {
- wled->ovp_mv = QPNP_WLED_OVP_31000_MV;
- temp = 0;
- }
-
- rc = qpnp_wled_read_reg(wled, &reg,
- QPNP_WLED_OVP_REG(wled->ctrl_base));
- if (rc < 0)
- return rc;
- reg &= QPNP_WLED_OVP_MASK;
- reg |= temp;
- rc = qpnp_wled_write_reg(wled, &reg,
- QPNP_WLED_OVP_REG(wled->ctrl_base));
- if (rc)
+ rc = qpnp_wled_ovp_config(wled);
+ if (rc < 0) {
+ pr_err("Error in configuring OVP threshold, rc=%d\n", rc);
return rc;
+ }
- if (wled->disp_type_amoled) {
- /* Configure avdd trim register */
- rc = qpnp_wled_sec_access(wled, wled->ctrl_base);
- if (rc)
- return rc;
-
- /* Check if wled->avdd_trim_steps_from_center is negative */
- if ((s32)wled->avdd_trim_steps_from_center <
- QPNP_WLED_AVDD_MIN_TRIM_VALUE) {
- wled->avdd_trim_steps_from_center =
- QPNP_WLED_AVDD_MIN_TRIM_VALUE;
- } else if ((s32)wled->avdd_trim_steps_from_center >
- QPNP_WLED_AVDD_MAX_TRIM_VALUE) {
- wled->avdd_trim_steps_from_center =
- QPNP_WLED_AVDD_MAX_TRIM_VALUE;
- }
- reg = wled->avdd_trim_steps_from_center +
- QPNP_WLED_AVDD_TRIM_CENTER_VALUE;
-
- rc = qpnp_wled_write_reg(wled, &reg,
- QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base));
- if (rc)
+ if (is_avdd_trim_adjustment_required(wled)) {
+ rc = qpnp_wled_avdd_trim_config(wled);
+ if (rc < 0)
return rc;
}
+ rc = qpnp_wled_avdd_mode_config(wled);
+ if (rc < 0)
+ return rc;
+
/* Configure the MODULATION register */
if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_1200_KHZ) {
wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_1200_KHZ;
@@ -1166,7 +1397,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
reg |= wled->dim_mode;
}
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_MOD_REG(wled->sink_base));
if (rc)
return rc;
@@ -1184,7 +1415,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
reg &= QPNP_WLED_HYB_THRES_MASK;
temp = fls(wled->hyb_thres / QPNP_WLED_HYB_THRES_MIN) - 1;
reg |= temp;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_HYB_THRES_REG(wled->sink_base));
if (rc)
return rc;
@@ -1195,17 +1426,14 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
else
reg = QPNP_WLED_SINK_TEST5_HYB;
- rc = qpnp_wled_sec_access(wled, wled->sink_base);
- if (rc)
- return rc;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_sec_write_reg(wled, reg,
QPNP_WLED_SINK_TEST5_REG(wled->sink_base));
if (rc)
return rc;
/* disable all current sinks and enable selected strings */
reg = 0x00;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_CURR_SINK_REG(wled->sink_base));
for (i = 0; i < wled->num_strings; i++) {
@@ -1228,7 +1456,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
else
reg |= ~QPNP_WLED_GATE_DRV_MASK;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_MOD_EN_REG(wled->sink_base,
wled->strings[i]));
if (rc)
@@ -1246,7 +1474,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
reg &= QPNP_WLED_SYNC_DLY_MASK;
temp = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US;
reg |= temp;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
wled->strings[i]));
if (rc)
@@ -1264,7 +1492,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
reg &= QPNP_WLED_FS_CURR_MASK;
temp = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
reg |= temp;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_FS_CURR_REG(wled->sink_base,
wled->strings[i]));
if (rc)
@@ -1278,7 +1506,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
return rc;
reg &= QPNP_WLED_CABC_MASK;
reg |= (wled->en_cabc << QPNP_WLED_CABC_SHIFT);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_CABC_REG(wled->sink_base,
wled->strings[i]));
if (rc)
@@ -1291,7 +1519,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
return rc;
temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT;
reg |= (1 << temp);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_CURR_SINK_REG(wled->sink_base));
if (rc)
return rc;
@@ -1348,18 +1576,14 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
if (wled->disp_type_amoled)
reg |= QPNP_WLED_SC_PRO_EN_DSCHGR;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_SC_PRO_REG(wled->ctrl_base));
if (rc)
return rc;
if (wled->en_ext_pfet_sc_pro) {
- rc = qpnp_wled_sec_access(wled, wled->ctrl_base);
- if (rc)
- return rc;
-
reg = QPNP_WLED_EXT_FET_DTEST2;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_sec_write_reg(wled, reg,
QPNP_WLED_TEST1_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -1378,7 +1602,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
temp = fls(wled->sc_deb_cycles) - QPNP_WLED_SC_DEB_CYCLES_SUB;
reg |= (temp << 1);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_SC_PRO_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -1447,13 +1671,16 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
return rc;
}
- wled->avdd_trim_steps_from_center = 0;
+ wled->avdd_mode_spmi = of_property_read_bool(pdev->dev.of_node,
+ "qcom,avdd-mode-spmi");
+
+ wled->avdd_target_voltage_mv = QPNP_WLED_DFLT_AVDD_MV;
rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,avdd-trim-steps-from-center", &temp_val);
+ "qcom,avdd-target-voltage-mv", &temp_val);
if (!rc) {
- wled->avdd_trim_steps_from_center = temp_val;
+ wled->avdd_target_voltage_mv = temp_val;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read avdd trim steps from center value\n");
+ dev_err(&pdev->dev, "Unable to read avdd target voltage\n");
return rc;
}
}
@@ -1507,17 +1734,33 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
return rc;
}
- wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
+ if (wled->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PM2FALCON_SUBTYPE)
+ wled->ovp_mv = 29600;
+ else
+ wled->ovp_mv = 29500;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,ovp-mv", &temp_val);
if (!rc) {
wled->ovp_mv = temp_val;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read vref\n");
+ dev_err(&pdev->dev, "Unable to read ovp\n");
return rc;
}
- wled->ilim_ma = QPNP_WLED_DFLT_ILIM_MA;
+ if (wled->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PM2FALCON_SUBTYPE) {
+ if (wled->disp_type_amoled)
+ wled->ilim_ma = PMICOBALT_AMOLED_DFLT_ILIM_MA;
+ else
+ wled->ilim_ma = PMICOBALT_WLED_DFLT_ILIM_MA;
+ } else {
+ if (wled->disp_type_amoled)
+ wled->ilim_ma = PMI8994_AMOLED_DFLT_ILIM_MA;
+ else
+ wled->ilim_ma = PMI8994_WLED_DFLT_ILIM_MA;
+ }
+
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,ilim-ma", &temp_val);
if (!rc) {
@@ -1638,6 +1881,7 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
static int qpnp_wled_probe(struct platform_device *pdev)
{
struct qpnp_wled *wled;
+ struct device_node *revid_node;
int rc = 0, i;
const __be32 *prop;
@@ -1652,6 +1896,27 @@ static int qpnp_wled_probe(struct platform_device *pdev)
wled->pdev = pdev;
+ revid_node = of_parse_phandle(pdev->dev.of_node, "qcom,pmic-revid", 0);
+ if (!revid_node) {
+ pr_err("Missing qcom,pmic-revid property - driver failed\n");
+ return -EINVAL;
+ }
+
+ wled->pmic_rev_id = get_revid_data(revid_node);
+ if (IS_ERR_OR_NULL(wled->pmic_rev_id)) {
+ pr_err("Unable to get pmic_revid rc=%ld\n",
+ PTR_ERR(wled->pmic_rev_id));
+ /*
+ * the revid peripheral must be registered, any failure
+ * here only indicates that the rev-id module has not
+ * probed yet.
+ */
+ return -EPROBE_DEFER;
+ }
+
+ pr_debug("PMIC subtype %d Digital major %d\n",
+ wled->pmic_rev_id->pmic_subtype, wled->pmic_rev_id->rev4);
+
prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_SINK_BASE,
0, 0);
if (!prop) {
@@ -1676,6 +1941,7 @@ static int qpnp_wled_probe(struct platform_device *pdev)
return rc;
}
+ mutex_init(&wled->bus_lock);
rc = qpnp_wled_config(wled);
if (rc) {
dev_err(&pdev->dev, "wled config failed\n");
diff --git a/drivers/mcb/mcb-parse.c b/drivers/mcb/mcb-parse.c
index 004926955263..b0155b05cddb 100644
--- a/drivers/mcb/mcb-parse.c
+++ b/drivers/mcb/mcb-parse.c
@@ -57,7 +57,7 @@ static int chameleon_parse_gdd(struct mcb_bus *bus,
mdev->id = GDD_DEV(reg1);
mdev->rev = GDD_REV(reg1);
mdev->var = GDD_VAR(reg1);
- mdev->bar = GDD_BAR(reg1);
+ mdev->bar = GDD_BAR(reg2);
mdev->group = GDD_GRP(reg2);
mdev->inst = GDD_INS(reg2);
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 998bd1ec0415..e562bdedfb07 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -517,4 +517,20 @@ config DM_LOG_WRITES
If unsure, say N.
+config DM_ANDROID_VERITY
+ bool "Android verity target support"
+ depends on DM_VERITY
+ depends on X509_CERTIFICATE_PARSER
+ depends on SYSTEM_TRUSTED_KEYRING
+ depends on PUBLIC_KEY_ALGO_RSA
+ depends on KEYS
+ depends on ASYMMETRIC_KEY_TYPE
+ depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ ---help---
+ This device-mapper target is virtually a VERITY target. This
+ target is setup by reading the metadata contents piggybacked
+ to the actual data blocks in the block device. The signature
+ of the metadata contents are verified against the key included
+ in the system keyring. Upon success, the underlying verity
+ target is setup.
endif # MD
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index d470143dcf40..ce7cf06d0e8a 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -69,3 +69,7 @@ endif
ifeq ($(CONFIG_DM_VERITY_FEC),y)
dm-verity-objs += dm-verity-fec.o
endif
+
+ifeq ($(CONFIG_DM_ANDROID_VERITY),y)
+dm-verity-objs += dm-android-verity.o
+endif
diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c
new file mode 100644
index 000000000000..bb6c1285e499
--- /dev/null
+++ b/drivers/md/dm-android-verity.c
@@ -0,0 +1,925 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/buffer_head.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/device-mapper.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/key.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/of.h>
+#include <linux/reboot.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
+
+#include <asm/setup.h>
+#include <crypto/hash.h>
+#include <crypto/public_key.h>
+#include <crypto/sha.h>
+#include <keys/asymmetric-type.h>
+#include <keys/system_keyring.h>
+
+#include "dm-verity.h"
+#include "dm-android-verity.h"
+
+static char verifiedbootstate[VERITY_COMMANDLINE_PARAM_LENGTH];
+static char veritymode[VERITY_COMMANDLINE_PARAM_LENGTH];
+static char veritykeyid[VERITY_DEFAULT_KEY_ID_LENGTH];
+static char buildvariant[BUILD_VARIANT];
+
+static bool target_added;
+static bool verity_enabled = true;
+struct dentry *debug_dir;
+static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv);
+
+static struct target_type android_verity_target = {
+ .name = "android-verity",
+ .version = {1, 0, 0},
+ .module = THIS_MODULE,
+ .ctr = android_verity_ctr,
+ .dtr = verity_dtr,
+ .map = verity_map,
+ .status = verity_status,
+ .prepare_ioctl = verity_prepare_ioctl,
+ .iterate_devices = verity_iterate_devices,
+ .io_hints = verity_io_hints,
+};
+
+static int __init verified_boot_state_param(char *line)
+{
+ strlcpy(verifiedbootstate, line, sizeof(verifiedbootstate));
+ return 1;
+}
+
+__setup("androidboot.verifiedbootstate=", verified_boot_state_param);
+
+static int __init verity_mode_param(char *line)
+{
+ strlcpy(veritymode, line, sizeof(veritymode));
+ return 1;
+}
+
+__setup("androidboot.veritymode=", verity_mode_param);
+
+static int __init verity_keyid_param(char *line)
+{
+ strlcpy(veritykeyid, line, sizeof(veritykeyid));
+ return 1;
+}
+
+__setup("veritykeyid=", verity_keyid_param);
+
+static int __init verity_buildvariant(char *line)
+{
+ strlcpy(buildvariant, line, sizeof(buildvariant));
+ return 1;
+}
+
+__setup("buildvariant=", verity_buildvariant);
+
+static inline bool default_verity_key_id(void)
+{
+ return veritykeyid[0] != '\0';
+}
+
+static inline bool is_eng(void)
+{
+ static const char typeeng[] = "eng";
+
+ return !strncmp(buildvariant, typeeng, sizeof(typeeng));
+}
+
+static inline bool is_userdebug(void)
+{
+ static const char typeuserdebug[] = "userdebug";
+
+ return !strncmp(buildvariant, typeuserdebug, sizeof(typeuserdebug));
+}
+
+
+static int table_extract_mpi_array(struct public_key_signature *pks,
+ const void *data, size_t len)
+{
+ MPI mpi = mpi_read_raw_data(data, len);
+
+ if (!mpi) {
+ DMERR("Error while allocating mpi array");
+ return -ENOMEM;
+ }
+
+ pks->mpi[0] = mpi;
+ pks->nr_mpi = 1;
+ return 0;
+}
+
+static struct public_key_signature *table_make_digest(
+ enum hash_algo hash,
+ const void *table,
+ unsigned long table_len)
+{
+ struct public_key_signature *pks = NULL;
+ struct crypto_shash *tfm;
+ struct shash_desc *desc;
+ size_t digest_size, desc_size;
+ int ret;
+
+ /* Allocate the hashing algorithm we're going to need and find out how
+ * big the hash operational data will be.
+ */
+ tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0);
+ if (IS_ERR(tfm))
+ return ERR_CAST(tfm);
+
+ desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+ digest_size = crypto_shash_digestsize(tfm);
+
+ /* We allocate the hash operational data storage on the end of out
+ * context data and the digest output buffer on the end of that.
+ */
+ ret = -ENOMEM;
+ pks = kzalloc(digest_size + sizeof(*pks) + desc_size, GFP_KERNEL);
+ if (!pks)
+ goto error;
+
+ pks->pkey_hash_algo = hash;
+ pks->digest = (u8 *)pks + sizeof(*pks) + desc_size;
+ pks->digest_size = digest_size;
+
+ desc = (struct shash_desc *)(pks + 1);
+ desc->tfm = tfm;
+ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ ret = crypto_shash_init(desc);
+ if (ret < 0)
+ goto error;
+
+ ret = crypto_shash_finup(desc, table, table_len, pks->digest);
+ if (ret < 0)
+ goto error;
+
+ crypto_free_shash(tfm);
+ return pks;
+
+error:
+ kfree(pks);
+ crypto_free_shash(tfm);
+ return ERR_PTR(ret);
+}
+
+static int read_block_dev(struct bio_read *payload, struct block_device *bdev,
+ sector_t offset, int length)
+{
+ struct bio *bio;
+ int err = 0, i;
+
+ payload->number_of_pages = DIV_ROUND_UP(length, PAGE_SIZE);
+
+ bio = bio_alloc(GFP_KERNEL, payload->number_of_pages);
+ if (!bio) {
+ DMERR("Error while allocating bio");
+ return -ENOMEM;
+ }
+
+ bio->bi_bdev = bdev;
+ bio->bi_iter.bi_sector = offset;
+
+ payload->page_io = kzalloc(sizeof(struct page *) *
+ payload->number_of_pages, GFP_KERNEL);
+ if (!payload->page_io) {
+ DMERR("page_io array alloc failed");
+ err = -ENOMEM;
+ goto free_bio;
+ }
+
+ for (i = 0; i < payload->number_of_pages; i++) {
+ payload->page_io[i] = alloc_page(GFP_KERNEL);
+ if (!payload->page_io[i]) {
+ DMERR("alloc_page failed");
+ err = -ENOMEM;
+ goto free_pages;
+ }
+ if (!bio_add_page(bio, payload->page_io[i], PAGE_SIZE, 0)) {
+ DMERR("bio_add_page error");
+ err = -EIO;
+ goto free_pages;
+ }
+ }
+
+ if (!submit_bio_wait(READ, bio))
+ /* success */
+ goto free_bio;
+ DMERR("bio read failed");
+ err = -EIO;
+
+free_pages:
+ for (i = 0; i < payload->number_of_pages; i++)
+ if (payload->page_io[i])
+ __free_page(payload->page_io[i]);
+ kfree(payload->page_io);
+free_bio:
+ bio_put(bio);
+ return err;
+}
+
+static inline u64 fec_div_round_up(u64 x, u64 y)
+{
+ u64 remainder;
+
+ return div64_u64_rem(x, y, &remainder) +
+ (remainder > 0 ? 1 : 0);
+}
+
+static inline void populate_fec_metadata(struct fec_header *header,
+ struct fec_ecc_metadata *ecc)
+{
+ ecc->blocks = fec_div_round_up(le64_to_cpu(header->inp_size),
+ FEC_BLOCK_SIZE);
+ ecc->roots = le32_to_cpu(header->roots);
+ ecc->start = le64_to_cpu(header->inp_size);
+}
+
+static inline int validate_fec_header(struct fec_header *header, u64 offset)
+{
+ /* move offset to make the sanity check work for backup header
+ * as well. */
+ offset -= offset % FEC_BLOCK_SIZE;
+ if (le32_to_cpu(header->magic) != FEC_MAGIC ||
+ le32_to_cpu(header->version) != FEC_VERSION ||
+ le32_to_cpu(header->size) != sizeof(struct fec_header) ||
+ le32_to_cpu(header->roots) == 0 ||
+ le32_to_cpu(header->roots) >= FEC_RSM)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int extract_fec_header(dev_t dev, struct fec_header *fec,
+ struct fec_ecc_metadata *ecc)
+{
+ u64 device_size;
+ struct bio_read payload;
+ int i, err = 0;
+ struct block_device *bdev;
+
+ bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL);
+
+ if (IS_ERR_OR_NULL(bdev)) {
+ DMERR("bdev get error");
+ return PTR_ERR(bdev);
+ }
+
+ device_size = i_size_read(bdev->bd_inode);
+
+ /* fec metadata size is a power of 2 and PAGE_SIZE
+ * is a power of 2 as well.
+ */
+ BUG_ON(FEC_BLOCK_SIZE > PAGE_SIZE);
+ /* 512 byte sector alignment */
+ BUG_ON(((device_size - FEC_BLOCK_SIZE) % (1 << SECTOR_SHIFT)) != 0);
+
+ err = read_block_dev(&payload, bdev, (device_size -
+ FEC_BLOCK_SIZE) / (1 << SECTOR_SHIFT), FEC_BLOCK_SIZE);
+ if (err) {
+ DMERR("Error while reading verity metadata");
+ goto error;
+ }
+
+ BUG_ON(sizeof(struct fec_header) > PAGE_SIZE);
+ memcpy(fec, page_address(payload.page_io[0]),
+ sizeof(*fec));
+
+ ecc->valid = true;
+ if (validate_fec_header(fec, device_size - FEC_BLOCK_SIZE)) {
+ /* Try the backup header */
+ memcpy(fec, page_address(payload.page_io[0]) + FEC_BLOCK_SIZE
+ - sizeof(*fec) ,
+ sizeof(*fec));
+ if (validate_fec_header(fec, device_size -
+ sizeof(struct fec_header)))
+ ecc->valid = false;
+ }
+
+ if (ecc->valid)
+ populate_fec_metadata(fec, ecc);
+
+ for (i = 0; i < payload.number_of_pages; i++)
+ __free_page(payload.page_io[i]);
+ kfree(payload.page_io);
+
+error:
+ blkdev_put(bdev, FMODE_READ);
+ return err;
+}
+static void find_metadata_offset(struct fec_header *fec,
+ struct block_device *bdev, u64 *metadata_offset)
+{
+ u64 device_size;
+
+ device_size = i_size_read(bdev->bd_inode);
+
+ if (le32_to_cpu(fec->magic) == FEC_MAGIC)
+ *metadata_offset = le64_to_cpu(fec->inp_size) -
+ VERITY_METADATA_SIZE;
+ else
+ *metadata_offset = device_size - VERITY_METADATA_SIZE;
+}
+
+static int find_size(dev_t dev, u64 *device_size)
+{
+ struct block_device *bdev;
+
+ bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL);
+ if (IS_ERR_OR_NULL(bdev)) {
+ DMERR("blkdev_get_by_dev failed");
+ return PTR_ERR(bdev);
+ }
+
+ *device_size = i_size_read(bdev->bd_inode);
+ *device_size >>= SECTOR_SHIFT;
+
+ DMINFO("blkdev size in sectors: %llu", *device_size);
+ blkdev_put(bdev, FMODE_READ);
+ return 0;
+}
+
+static int verify_header(struct android_metadata_header *header)
+{
+ int retval = -EINVAL;
+
+ if (is_userdebug() && le32_to_cpu(header->magic_number) ==
+ VERITY_METADATA_MAGIC_DISABLE)
+ return VERITY_STATE_DISABLE;
+
+ if (!(le32_to_cpu(header->magic_number) ==
+ VERITY_METADATA_MAGIC_NUMBER) ||
+ (le32_to_cpu(header->magic_number) ==
+ VERITY_METADATA_MAGIC_DISABLE)) {
+ DMERR("Incorrect magic number");
+ return retval;
+ }
+
+ if (le32_to_cpu(header->protocol_version) !=
+ VERITY_METADATA_VERSION) {
+ DMERR("Unsupported version %u",
+ le32_to_cpu(header->protocol_version));
+ return retval;
+ }
+
+ return 0;
+}
+
+static int extract_metadata(dev_t dev, struct fec_header *fec,
+ struct android_metadata **metadata,
+ bool *verity_enabled)
+{
+ struct block_device *bdev;
+ struct android_metadata_header *header;
+ int i;
+ u32 table_length, copy_length, offset;
+ u64 metadata_offset;
+ struct bio_read payload;
+ int err = 0;
+
+ bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL);
+
+ if (IS_ERR_OR_NULL(bdev)) {
+ DMERR("blkdev_get_by_dev failed");
+ return -ENODEV;
+ }
+
+ find_metadata_offset(fec, bdev, &metadata_offset);
+
+ /* Verity metadata size is a power of 2 and PAGE_SIZE
+ * is a power of 2 as well.
+ * PAGE_SIZE is also a multiple of 512 bytes.
+ */
+ if (VERITY_METADATA_SIZE > PAGE_SIZE)
+ BUG_ON(VERITY_METADATA_SIZE % PAGE_SIZE != 0);
+ /* 512 byte sector alignment */
+ BUG_ON(metadata_offset % (1 << SECTOR_SHIFT) != 0);
+
+ err = read_block_dev(&payload, bdev, metadata_offset /
+ (1 << SECTOR_SHIFT), VERITY_METADATA_SIZE);
+ if (err) {
+ DMERR("Error while reading verity metadata");
+ goto blkdev_release;
+ }
+
+ header = kzalloc(sizeof(*header), GFP_KERNEL);
+ if (!header) {
+ DMERR("kzalloc failed for header");
+ err = -ENOMEM;
+ goto free_payload;
+ }
+
+ memcpy(header, page_address(payload.page_io[0]),
+ sizeof(*header));
+
+ DMINFO("bio magic_number:%u protocol_version:%d table_length:%u",
+ le32_to_cpu(header->magic_number),
+ le32_to_cpu(header->protocol_version),
+ le32_to_cpu(header->table_length));
+
+ err = verify_header(header);
+
+ if (err == VERITY_STATE_DISABLE) {
+ DMERR("Mounting root with verity disabled");
+ *verity_enabled = false;
+ /* we would still have to read the metadata to figure out
+ * the data blocks size. Or may be could map the entire
+ * partition similar to mounting the device.
+ *
+ * Reset error as well as the verity_enabled flag is changed.
+ */
+ err = 0;
+ } else if (err)
+ goto free_header;
+
+ *metadata = kzalloc(sizeof(**metadata), GFP_KERNEL);
+ if (!*metadata) {
+ DMERR("kzalloc for metadata failed");
+ err = -ENOMEM;
+ goto free_header;
+ }
+
+ (*metadata)->header = header;
+ table_length = le32_to_cpu(header->table_length);
+
+ if (table_length == 0 ||
+ table_length > (VERITY_METADATA_SIZE -
+ sizeof(struct android_metadata_header))) {
+ DMERR("table_length too long");
+ err = -EINVAL;
+ goto free_metadata;
+ }
+
+ (*metadata)->verity_table = kzalloc(table_length + 1, GFP_KERNEL);
+
+ if (!(*metadata)->verity_table) {
+ DMERR("kzalloc verity_table failed");
+ err = -ENOMEM;
+ goto free_metadata;
+ }
+
+ if (sizeof(struct android_metadata_header) +
+ table_length <= PAGE_SIZE) {
+ memcpy((*metadata)->verity_table,
+ page_address(payload.page_io[0])
+ + sizeof(struct android_metadata_header),
+ table_length);
+ } else {
+ copy_length = PAGE_SIZE -
+ sizeof(struct android_metadata_header);
+ memcpy((*metadata)->verity_table,
+ page_address(payload.page_io[0])
+ + sizeof(struct android_metadata_header),
+ copy_length);
+ table_length -= copy_length;
+ offset = copy_length;
+ i = 1;
+ while (table_length != 0) {
+ if (table_length > PAGE_SIZE) {
+ memcpy((*metadata)->verity_table + offset,
+ page_address(payload.page_io[i]),
+ PAGE_SIZE);
+ offset += PAGE_SIZE;
+ table_length -= PAGE_SIZE;
+ } else {
+ memcpy((*metadata)->verity_table + offset,
+ page_address(payload.page_io[i]),
+ table_length);
+ table_length = 0;
+ }
+ i++;
+ }
+ }
+ (*metadata)->verity_table[table_length] = '\0';
+
+ DMINFO("verity_table: %s", (*metadata)->verity_table);
+ goto free_payload;
+
+free_metadata:
+ kfree(*metadata);
+free_header:
+ kfree(header);
+free_payload:
+ for (i = 0; i < payload.number_of_pages; i++)
+ if (payload.page_io[i])
+ __free_page(payload.page_io[i]);
+ kfree(payload.page_io);
+blkdev_release:
+ blkdev_put(bdev, FMODE_READ);
+ return err;
+}
+
+/* helper functions to extract properties from dts */
+const char *find_dt_value(const char *name)
+{
+ struct device_node *firmware;
+ const char *value;
+
+ firmware = of_find_node_by_path("/firmware/android");
+ if (!firmware)
+ return NULL;
+ value = of_get_property(firmware, name, NULL);
+ of_node_put(firmware);
+
+ return value;
+}
+
+static int verity_mode(void)
+{
+ static const char enforcing[] = "enforcing";
+ static const char verified_mode_prop[] = "veritymode";
+ const char *value;
+
+ value = find_dt_value(verified_mode_prop);
+ if (!value)
+ value = veritymode;
+ if (!strncmp(value, enforcing, sizeof(enforcing) - 1))
+ return DM_VERITY_MODE_RESTART;
+
+ return DM_VERITY_MODE_EIO;
+}
+
+static int verify_verity_signature(char *key_id,
+ struct android_metadata *metadata)
+{
+ key_ref_t key_ref;
+ struct key *key;
+ struct public_key_signature *pks = NULL;
+ int retval = -EINVAL;
+
+ key_ref = keyring_search(make_key_ref(system_trusted_keyring, 1),
+ &key_type_asymmetric, key_id);
+
+ if (IS_ERR(key_ref)) {
+ DMERR("keyring: key not found");
+ return -ENOKEY;
+ }
+
+ key = key_ref_to_ptr(key_ref);
+
+ pks = table_make_digest(HASH_ALGO_SHA256,
+ (const void *)metadata->verity_table,
+ le32_to_cpu(metadata->header->table_length));
+
+ if (IS_ERR(pks)) {
+ DMERR("hashing failed");
+ goto error;
+ }
+
+ retval = table_extract_mpi_array(pks, &metadata->header->signature[0],
+ RSANUMBYTES);
+ if (retval < 0) {
+ DMERR("Error extracting mpi %d", retval);
+ goto error;
+ }
+
+ retval = verify_signature(key, pks);
+ mpi_free(pks->rsa.s);
+error:
+ kfree(pks);
+ key_put(key);
+
+ return retval;
+}
+
+static void handle_error(void)
+{
+ int mode = verity_mode();
+ if (mode == DM_VERITY_MODE_RESTART) {
+ DMERR("triggering restart");
+ kernel_restart("dm-verity device corrupted");
+ } else {
+ DMERR("Mounting verity root failed");
+ }
+}
+
+static inline bool test_mult_overflow(sector_t a, u32 b)
+{
+ sector_t r = (sector_t)~0ULL;
+
+ sector_div(r, b);
+ return a > r;
+}
+
+static int add_as_linear_device(struct dm_target *ti, char *dev)
+{
+ /*Move to linear mapping defines*/
+ char *linear_table_args[DM_LINEAR_ARGS] = {dev,
+ DM_LINEAR_TARGET_OFFSET};
+ int err = 0;
+
+ android_verity_target.dtr = dm_linear_dtr,
+ android_verity_target.map = dm_linear_map,
+ android_verity_target.status = dm_linear_status,
+ android_verity_target.prepare_ioctl = dm_linear_prepare_ioctl,
+ android_verity_target.iterate_devices = dm_linear_iterate_devices,
+ android_verity_target.io_hints = NULL;
+
+ err = dm_linear_ctr(ti, DM_LINEAR_ARGS, linear_table_args);
+
+ if (!err) {
+ DMINFO("Added android-verity as a linear target");
+ target_added = true;
+ } else
+ DMERR("Failed to add android-verity as linear target");
+
+ return err;
+}
+
+/*
+ * Target parameters:
+ * <key id> Key id of the public key in the system keyring.
+ * Verity metadata's signature would be verified against
+ * this. If the key id contains spaces, replace them
+ * with '#'.
+ * <block device> The block device for which dm-verity is being setup.
+ */
+static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
+{
+ dev_t uninitialized_var(dev);
+ struct android_metadata *metadata = NULL;
+ int err = 0, i, mode;
+ char *key_id, *table_ptr, dummy, *target_device,
+ *verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS];
+ /* One for specifying number of opt args and one for mode */
+ sector_t data_sectors;
+ u32 data_block_size;
+ unsigned int no_of_args = VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS;
+ struct fec_header uninitialized_var(fec);
+ struct fec_ecc_metadata uninitialized_var(ecc);
+ char buf[FEC_ARG_LENGTH], *buf_ptr;
+ unsigned long long tmpll;
+ u64 uninitialized_var(device_size);
+
+ if (argc == 1) {
+ /* Use the default keyid */
+ if (default_verity_key_id())
+ key_id = veritykeyid;
+ else if (!is_eng()) {
+ DMERR("veritykeyid= is not set");
+ handle_error();
+ return -EINVAL;
+ }
+ } else if (argc == 2)
+ key_id = argv[1];
+ else {
+ DMERR("Incorrect number of arguments");
+ handle_error();
+ return -EINVAL;
+ }
+
+ target_device = argv[0];
+
+ dev = name_to_dev_t(target_device);
+ if (!dev) {
+ DMERR("no dev found for %s", target_device);
+ handle_error();
+ return -EINVAL;
+ }
+
+ if (is_eng()) {
+ err = find_size(dev, &device_size);
+ if (err) {
+ DMERR("error finding bdev size");
+ handle_error();
+ return err;
+ }
+
+ ti->len = device_size;
+ err = add_as_linear_device(ti, target_device);
+ if (err) {
+ handle_error();
+ return err;
+ }
+ verity_enabled = false;
+ return 0;
+ }
+
+ strreplace(key_id, '#', ' ');
+
+ DMINFO("key:%s dev:%s", key_id, target_device);
+
+ if (extract_fec_header(dev, &fec, &ecc)) {
+ DMERR("Error while extracting fec header");
+ handle_error();
+ return -EINVAL;
+ }
+
+ err = extract_metadata(dev, &fec, &metadata, &verity_enabled);
+
+ if (err) {
+ DMERR("Error while extracting metadata");
+ handle_error();
+ goto free_metadata;
+ }
+
+ if (verity_enabled) {
+ err = verify_verity_signature(key_id, metadata);
+
+ if (err) {
+ DMERR("Signature verification failed");
+ handle_error();
+ goto free_metadata;
+ } else
+ DMINFO("Signature verification success");
+ }
+
+ table_ptr = metadata->verity_table;
+
+ for (i = 0; i < VERITY_TABLE_ARGS; i++) {
+ verity_table_args[i] = strsep(&table_ptr, " ");
+ if (verity_table_args[i] == NULL)
+ break;
+ }
+
+ if (i != VERITY_TABLE_ARGS) {
+ DMERR("Verity table not in the expected format");
+ err = -EINVAL;
+ handle_error();
+ goto free_metadata;
+ }
+
+ if (sscanf(verity_table_args[5], "%llu%c", &tmpll, &dummy)
+ != 1) {
+ DMERR("Verity table not in the expected format");
+ handle_error();
+ err = -EINVAL;
+ goto free_metadata;
+ }
+
+ if (tmpll > ULONG_MAX) {
+ DMERR("<num_data_blocks> too large. Forgot to turn on CONFIG_LBDAF?");
+ handle_error();
+ err = -EINVAL;
+ goto free_metadata;
+ }
+
+ data_sectors = tmpll;
+
+ if (sscanf(verity_table_args[3], "%u%c", &data_block_size, &dummy)
+ != 1) {
+ DMERR("Verity table not in the expected format");
+ handle_error();
+ err = -EINVAL;
+ goto free_metadata;
+ }
+
+ if (test_mult_overflow(data_sectors, data_block_size >>
+ SECTOR_SHIFT)) {
+ DMERR("data_sectors too large");
+ handle_error();
+ err = -EOVERFLOW;
+ goto free_metadata;
+ }
+
+ data_sectors *= data_block_size >> SECTOR_SHIFT;
+ DMINFO("Data sectors %llu", (unsigned long long)data_sectors);
+
+ /* update target length */
+ ti->len = data_sectors;
+
+ /* Setup linear target and free */
+ if (!verity_enabled) {
+ err = add_as_linear_device(ti, target_device);
+ goto free_metadata;
+ }
+
+ /*substitute data_dev and hash_dev*/
+ verity_table_args[1] = target_device;
+ verity_table_args[2] = target_device;
+
+ mode = verity_mode();
+
+ if (ecc.valid && IS_BUILTIN(CONFIG_DM_VERITY_FEC)) {
+ if (mode) {
+ err = snprintf(buf, FEC_ARG_LENGTH,
+ "%u %s " VERITY_TABLE_OPT_FEC_FORMAT,
+ 1 + VERITY_TABLE_OPT_FEC_ARGS,
+ mode == DM_VERITY_MODE_RESTART ?
+ VERITY_TABLE_OPT_RESTART :
+ VERITY_TABLE_OPT_LOGGING,
+ target_device,
+ ecc.start / FEC_BLOCK_SIZE, ecc.blocks,
+ ecc.roots);
+ } else {
+ err = snprintf(buf, FEC_ARG_LENGTH,
+ "%u " VERITY_TABLE_OPT_FEC_FORMAT,
+ VERITY_TABLE_OPT_FEC_ARGS, target_device,
+ ecc.start / FEC_BLOCK_SIZE, ecc.blocks,
+ ecc.roots);
+ }
+ } else if (mode) {
+ err = snprintf(buf, FEC_ARG_LENGTH,
+ "2 " VERITY_TABLE_OPT_IGNZERO " %s",
+ mode == DM_VERITY_MODE_RESTART ?
+ VERITY_TABLE_OPT_RESTART : VERITY_TABLE_OPT_LOGGING);
+ } else {
+ err = snprintf(buf, FEC_ARG_LENGTH, "1 %s",
+ "ignore_zero_blocks");
+ }
+
+ if (err < 0 || err >= FEC_ARG_LENGTH)
+ goto free_metadata;
+
+ buf_ptr = buf;
+
+ for (i = VERITY_TABLE_ARGS; i < (VERITY_TABLE_ARGS +
+ VERITY_TABLE_OPT_FEC_ARGS + 2); i++) {
+ verity_table_args[i] = strsep(&buf_ptr, " ");
+ if (verity_table_args[i] == NULL) {
+ no_of_args = i;
+ break;
+ }
+ }
+
+ err = verity_ctr(ti, no_of_args, verity_table_args);
+
+ if (err)
+ DMERR("android-verity failed to mount as verity target");
+ else {
+ target_added = true;
+ DMINFO("android-verity mounted as verity target");
+ }
+
+free_metadata:
+ if (metadata) {
+ kfree(metadata->header);
+ kfree(metadata->verity_table);
+ }
+ kfree(metadata);
+ return err;
+}
+
+static int __init dm_android_verity_init(void)
+{
+ int r;
+ struct dentry *file;
+
+ r = dm_register_target(&android_verity_target);
+ if (r < 0)
+ DMERR("register failed %d", r);
+
+ /* Tracks the status of the last added target */
+ debug_dir = debugfs_create_dir("android_verity", NULL);
+
+ if (IS_ERR_OR_NULL(debug_dir)) {
+ DMERR("Cannot create android_verity debugfs directory: %ld",
+ PTR_ERR(debug_dir));
+ goto end;
+ }
+
+ file = debugfs_create_bool("target_added", S_IRUGO, debug_dir,
+ &target_added);
+
+ if (IS_ERR_OR_NULL(file)) {
+ DMERR("Cannot create android_verity debugfs directory: %ld",
+ PTR_ERR(debug_dir));
+ debugfs_remove_recursive(debug_dir);
+ goto end;
+ }
+
+ file = debugfs_create_bool("verity_enabled", S_IRUGO, debug_dir,
+ &verity_enabled);
+
+ if (IS_ERR_OR_NULL(file)) {
+ DMERR("Cannot create android_verity debugfs directory: %ld",
+ PTR_ERR(debug_dir));
+ debugfs_remove_recursive(debug_dir);
+ }
+
+end:
+ return r;
+}
+
+static void __exit dm_android_verity_exit(void)
+{
+ if (!IS_ERR_OR_NULL(debug_dir))
+ debugfs_remove_recursive(debug_dir);
+
+ dm_unregister_target(&android_verity_target);
+}
+
+module_init(dm_android_verity_init);
+module_exit(dm_android_verity_exit);
diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h
new file mode 100644
index 000000000000..0fcd54aaf5f6
--- /dev/null
+++ b/drivers/md/dm-android-verity.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef DM_ANDROID_VERITY_H
+#define DM_ANDROID_VERITY_H
+
+#include <crypto/sha.h>
+
+#define RSANUMBYTES 256
+#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001
+#define VERITY_METADATA_MAGIC_DISABLE 0x46464f56
+#define VERITY_METADATA_VERSION 0
+#define VERITY_STATE_DISABLE 1
+#define DATA_BLOCK_SIZE (4 * 1024)
+#define VERITY_METADATA_SIZE (8 * DATA_BLOCK_SIZE)
+#define VERITY_TABLE_ARGS 10
+#define VERITY_COMMANDLINE_PARAM_LENGTH 20
+#define BUILD_VARIANT 20
+
+/*
+ * <subject>:<sha1-id> is the format for the identifier.
+ * subject can either be the Common Name(CN) + Organization Name(O) or
+ * just the CN if the it is prefixed with O
+ * From https://tools.ietf.org/html/rfc5280#appendix-A
+ * ub-organization-name-length INTEGER ::= 64
+ * ub-common-name-length INTEGER ::= 64
+ *
+ * http://lxr.free-electrons.com/source/crypto/asymmetric_keys/x509_cert_parser.c?v=3.9#L278
+ * ctx->o_size + 2 + ctx->cn_size + 1
+ * + 41 characters for ":" and sha1 id
+ * 64 + 2 + 64 + 1 + 1 + 40 (172)
+ * setting VERITY_DEFAULT_KEY_ID_LENGTH to 200 characters.
+ */
+#define VERITY_DEFAULT_KEY_ID_LENGTH 200
+
+#define FEC_MAGIC 0xFECFECFE
+#define FEC_BLOCK_SIZE (4 * 1024)
+#define FEC_VERSION 0
+#define FEC_RSM 255
+#define FEC_ARG_LENGTH 300
+
+#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
+#define VERITY_TABLE_OPT_LOGGING "ignore_corruption"
+#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
+
+#define VERITY_TABLE_OPT_FEC_FORMAT \
+ "use_fec_from_device %s fec_start %llu fec_blocks %llu fec_roots %u ignore_zero_blocks"
+#define VERITY_TABLE_OPT_FEC_ARGS 9
+
+#define VERITY_DEBUG 0
+
+#define DM_MSG_PREFIX "android-verity"
+
+#define DM_LINEAR_ARGS 2
+#define DM_LINEAR_TARGET_OFFSET "0"
+
+/*
+ * There can be two formats.
+ * if fec is present
+ * <data_blocks> <verity_tree> <verity_metdata_32K><fec_data><fec_data_4K>
+ * if fec is not present
+ * <data_blocks> <verity_tree> <verity_metdata_32K>
+ */
+/* TODO: rearrange structure to reduce memory holes
+ * depends on userspace change.
+ */
+struct fec_header {
+ __le32 magic;
+ __le32 version;
+ __le32 size;
+ __le32 roots;
+ __le32 fec_size;
+ __le64 inp_size;
+ u8 hash[SHA256_DIGEST_SIZE];
+};
+
+struct android_metadata_header {
+ __le32 magic_number;
+ __le32 protocol_version;
+ char signature[RSANUMBYTES];
+ __le32 table_length;
+};
+
+struct android_metadata {
+ struct android_metadata_header *header;
+ char *verity_table;
+};
+
+struct fec_ecc_metadata {
+ bool valid;
+ u32 roots;
+ u64 blocks;
+ u64 rounds;
+ u64 start;
+};
+
+struct bio_read {
+ struct page **page_io;
+ int number_of_pages;
+};
+
+extern struct target_type linear_target;
+
+extern void dm_linear_dtr(struct dm_target *ti);
+extern int dm_linear_map(struct dm_target *ti, struct bio *bio);
+extern void dm_linear_status(struct dm_target *ti, status_type_t type,
+ unsigned status_flags, char *result, unsigned maxlen);
+extern int dm_linear_prepare_ioctl(struct dm_target *ti,
+ struct block_device **bdev, fmode_t *mode);
+extern int dm_linear_iterate_devices(struct dm_target *ti,
+ iterate_devices_callout_fn fn, void *data);
+extern int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv);
+#endif /* DM_ANDROID_VERITY_H */
diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c
index 27f2ef300f8b..3970cda10080 100644
--- a/drivers/md/dm-cache-metadata.c
+++ b/drivers/md/dm-cache-metadata.c
@@ -867,39 +867,55 @@ static int blocks_are_unmapped_or_clean(struct dm_cache_metadata *cmd,
return 0;
}
-#define WRITE_LOCK(cmd) \
- down_write(&cmd->root_lock); \
- if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
- up_write(&cmd->root_lock); \
- return -EINVAL; \
+static bool cmd_write_lock(struct dm_cache_metadata *cmd)
+{
+ down_write(&cmd->root_lock);
+ if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) {
+ up_write(&cmd->root_lock);
+ return false;
}
+ return true;
+}
-#define WRITE_LOCK_VOID(cmd) \
- down_write(&cmd->root_lock); \
- if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
- up_write(&cmd->root_lock); \
- return; \
- }
+#define WRITE_LOCK(cmd) \
+ do { \
+ if (!cmd_write_lock((cmd))) \
+ return -EINVAL; \
+ } while(0)
+
+#define WRITE_LOCK_VOID(cmd) \
+ do { \
+ if (!cmd_write_lock((cmd))) \
+ return; \
+ } while(0)
#define WRITE_UNLOCK(cmd) \
- up_write(&cmd->root_lock)
+ up_write(&(cmd)->root_lock)
-#define READ_LOCK(cmd) \
- down_read(&cmd->root_lock); \
- if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
- up_read(&cmd->root_lock); \
- return -EINVAL; \
+static bool cmd_read_lock(struct dm_cache_metadata *cmd)
+{
+ down_read(&cmd->root_lock);
+ if (cmd->fail_io) {
+ up_read(&cmd->root_lock);
+ return false;
}
+ return true;
+}
-#define READ_LOCK_VOID(cmd) \
- down_read(&cmd->root_lock); \
- if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
- up_read(&cmd->root_lock); \
- return; \
- }
+#define READ_LOCK(cmd) \
+ do { \
+ if (!cmd_read_lock((cmd))) \
+ return -EINVAL; \
+ } while(0)
+
+#define READ_LOCK_VOID(cmd) \
+ do { \
+ if (!cmd_read_lock((cmd))) \
+ return; \
+ } while(0)
#define READ_UNLOCK(cmd) \
- up_read(&cmd->root_lock)
+ up_read(&(cmd)->root_lock)
int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size)
{
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index 80a439543259..bc5e9a5b1f30 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -1923,6 +1923,45 @@ void dm_interface_exit(void)
dm_hash_exit();
}
+
+/**
+ * dm_ioctl_export - Permanently export a mapped device via the ioctl interface
+ * @md: Pointer to mapped_device
+ * @name: Buffer (size DM_NAME_LEN) for name
+ * @uuid: Buffer (size DM_UUID_LEN) for uuid or NULL if not desired
+ */
+int dm_ioctl_export(struct mapped_device *md, const char *name,
+ const char *uuid)
+{
+ int r = 0;
+ struct hash_cell *hc;
+
+ if (!md) {
+ r = -ENXIO;
+ goto out;
+ }
+
+ /* The name and uuid can only be set once. */
+ mutex_lock(&dm_hash_cells_mutex);
+ hc = dm_get_mdptr(md);
+ mutex_unlock(&dm_hash_cells_mutex);
+ if (hc) {
+ DMERR("%s: already exported", dm_device_name(md));
+ r = -ENXIO;
+ goto out;
+ }
+
+ r = dm_hash_insert(name, uuid, md);
+ if (r) {
+ DMERR("%s: could not bind to '%s'", dm_device_name(md), name);
+ goto out;
+ }
+
+ /* Let udev know we've changed. */
+ dm_kobject_uevent(md, KOBJ_CHANGE, dm_get_event_nr(md));
+out:
+ return r;
+}
/**
* dm_copy_name_and_uuid - Copy mapped device name & uuid into supplied buffers
* @md: Pointer to mapped_device
diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c
index 05c35aacb3aa..8505a771de42 100644
--- a/drivers/md/dm-linear.c
+++ b/drivers/md/dm-linear.c
@@ -25,7 +25,7 @@ struct linear_c {
/*
* Construct a linear mapping: <dev_path> <offset>
*/
-static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
struct linear_c *lc;
unsigned long long tmp;
@@ -67,7 +67,7 @@ static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
return ret;
}
-static void linear_dtr(struct dm_target *ti)
+void dm_linear_dtr(struct dm_target *ti)
{
struct linear_c *lc = (struct linear_c *) ti->private;
@@ -92,14 +92,14 @@ static void linear_map_bio(struct dm_target *ti, struct bio *bio)
linear_map_sector(ti, bio->bi_iter.bi_sector);
}
-static int linear_map(struct dm_target *ti, struct bio *bio)
+int dm_linear_map(struct dm_target *ti, struct bio *bio)
{
linear_map_bio(ti, bio);
return DM_MAPIO_REMAPPED;
}
-static void linear_status(struct dm_target *ti, status_type_t type,
+void dm_linear_status(struct dm_target *ti, status_type_t type,
unsigned status_flags, char *result, unsigned maxlen)
{
struct linear_c *lc = (struct linear_c *) ti->private;
@@ -116,7 +116,7 @@ static void linear_status(struct dm_target *ti, status_type_t type,
}
}
-static int linear_prepare_ioctl(struct dm_target *ti,
+int dm_linear_prepare_ioctl(struct dm_target *ti,
struct block_device **bdev, fmode_t *mode)
{
struct linear_c *lc = (struct linear_c *) ti->private;
@@ -133,7 +133,7 @@ static int linear_prepare_ioctl(struct dm_target *ti,
return 0;
}
-static int linear_iterate_devices(struct dm_target *ti,
+int dm_linear_iterate_devices(struct dm_target *ti,
iterate_devices_callout_fn fn, void *data)
{
struct linear_c *lc = ti->private;
@@ -145,12 +145,12 @@ static struct target_type linear_target = {
.name = "linear",
.version = {1, 2, 1},
.module = THIS_MODULE,
- .ctr = linear_ctr,
- .dtr = linear_dtr,
- .map = linear_map,
- .status = linear_status,
- .prepare_ioctl = linear_prepare_ioctl,
- .iterate_devices = linear_iterate_devices,
+ .ctr = dm_linear_ctr,
+ .dtr = dm_linear_dtr,
+ .map = dm_linear_map,
+ .status = dm_linear_status,
+ .prepare_ioctl = dm_linear_prepare_ioctl,
+ .iterate_devices = dm_linear_iterate_devices,
};
int __init dm_linear_init(void)
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index cb5d0daf53bb..b3d78bba3a79 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -11,6 +11,7 @@
#include <linux/vmalloc.h>
#include <linux/blkdev.h>
#include <linux/namei.h>
+#include <linux/mount.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/slab.h>
diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c
index ad10d6d8ed28..1dd667b97530 100644
--- a/drivers/md/dm-verity-fec.c
+++ b/drivers/md/dm-verity-fec.c
@@ -442,6 +442,13 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
if (!verity_fec_is_enabled(v))
return -EOPNOTSUPP;
+ if (fio->level >= DM_VERITY_FEC_MAX_RECURSION) {
+ DMWARN_LIMIT("%s: FEC: recursion too deep", v->data_dev->name);
+ return -EIO;
+ }
+
+ fio->level++;
+
if (type == DM_VERITY_BLOCK_TYPE_METADATA)
block += v->data_blocks;
@@ -456,9 +463,7 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
*/
offset = block << v->data_dev_block_bits;
-
- res = offset;
- div64_u64(res, v->fec->rounds << v->data_dev_block_bits);
+ res = div64_u64(offset, v->fec->rounds << v->data_dev_block_bits);
/*
* The base RS block we can feed to the interleaver to find out all
@@ -475,7 +480,7 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
if (r < 0) {
r = fec_decode_rsb(v, io, fio, rsb, offset, true);
if (r < 0)
- return r;
+ goto done;
}
if (dest)
@@ -485,6 +490,8 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
r = verity_for_bv_block(v, io, iter, fec_bv_copy);
}
+done:
+ fio->level--;
return r;
}
@@ -525,6 +532,7 @@ void verity_fec_init_io(struct dm_verity_io *io)
memset(fio->bufs, 0, sizeof(fio->bufs));
fio->nbufs = 0;
fio->output = NULL;
+ fio->level = 0;
}
/*
@@ -680,7 +688,8 @@ static struct attribute *fec_attrs[] = {
static struct kobj_type fec_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
- .default_attrs = fec_attrs
+ .default_attrs = fec_attrs,
+ .release = dm_kobject_release
};
/*
diff --git a/drivers/md/dm-verity-fec.h b/drivers/md/dm-verity-fec.h
index 8c4bee052a73..b8e21cef3ad1 100644
--- a/drivers/md/dm-verity-fec.h
+++ b/drivers/md/dm-verity-fec.h
@@ -28,6 +28,9 @@
#define DM_VERITY_FEC_BUF_MAX \
(1 << (PAGE_SHIFT - DM_VERITY_FEC_BUF_RS_BITS))
+/* maximum recursion level for verity_fec_decode */
+#define DM_VERITY_FEC_MAX_RECURSION 4
+
#define DM_VERITY_OPT_FEC_DEV "use_fec_from_device"
#define DM_VERITY_OPT_FEC_BLOCKS "fec_blocks"
#define DM_VERITY_OPT_FEC_START "fec_start"
@@ -61,6 +64,7 @@ struct dm_verity_fec_io {
unsigned nbufs; /* number of buffers allocated */
u8 *output; /* buffer for corrected output */
size_t output_pos;
+ unsigned level; /* recursion level */
};
#ifdef CONFIG_DM_VERITY_FEC
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index 5c5d30cb6ec5..5214ed2c7507 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -551,7 +551,7 @@ static void verity_submit_prefetch(struct dm_verity *v, struct dm_verity_io *io)
* Bio map function. It allocates dm_verity_io structure and bio vector and
* fills them. Then it issues prefetches and the I/O.
*/
-static int verity_map(struct dm_target *ti, struct bio *bio)
+int verity_map(struct dm_target *ti, struct bio *bio)
{
struct dm_verity *v = ti->private;
struct dm_verity_io *io;
@@ -596,7 +596,7 @@ static int verity_map(struct dm_target *ti, struct bio *bio)
/*
* Status: V (valid) or C (corruption found)
*/
-static void verity_status(struct dm_target *ti, status_type_t type,
+void verity_status(struct dm_target *ti, status_type_t type,
unsigned status_flags, char *result, unsigned maxlen)
{
struct dm_verity *v = ti->private;
@@ -656,7 +656,7 @@ static void verity_status(struct dm_target *ti, status_type_t type,
}
}
-static int verity_prepare_ioctl(struct dm_target *ti,
+int verity_prepare_ioctl(struct dm_target *ti,
struct block_device **bdev, fmode_t *mode)
{
struct dm_verity *v = ti->private;
@@ -669,7 +669,7 @@ static int verity_prepare_ioctl(struct dm_target *ti,
return 0;
}
-static int verity_iterate_devices(struct dm_target *ti,
+int verity_iterate_devices(struct dm_target *ti,
iterate_devices_callout_fn fn, void *data)
{
struct dm_verity *v = ti->private;
@@ -677,7 +677,7 @@ static int verity_iterate_devices(struct dm_target *ti,
return fn(ti, v->data_dev, v->data_start, ti->len, data);
}
-static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits)
+void verity_io_hints(struct dm_target *ti, struct queue_limits *limits)
{
struct dm_verity *v = ti->private;
@@ -690,7 +690,7 @@ static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits)
blk_limits_io_min(limits, limits->logical_block_size);
}
-static void verity_dtr(struct dm_target *ti)
+void verity_dtr(struct dm_target *ti)
{
struct dm_verity *v = ti->private;
@@ -817,7 +817,7 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
* <digest>
* <salt> Hex string or "-" if no salt.
*/
-static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
+int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
struct dm_verity *v;
struct dm_arg_set as;
diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h
index fb419f422d73..75effca400a3 100644
--- a/drivers/md/dm-verity.h
+++ b/drivers/md/dm-verity.h
@@ -126,4 +126,14 @@ extern int verity_hash(struct dm_verity *v, struct shash_desc *desc,
extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
sector_t block, u8 *digest, bool *is_zero);
+extern void verity_status(struct dm_target *ti, status_type_t type,
+ unsigned status_flags, char *result, unsigned maxlen);
+extern int verity_prepare_ioctl(struct dm_target *ti,
+ struct block_device **bdev, fmode_t *mode);
+extern int verity_iterate_devices(struct dm_target *ti,
+ iterate_devices_callout_fn fn, void *data);
+extern void verity_io_hints(struct dm_target *ti, struct queue_limits *limits);
+extern void verity_dtr(struct dm_target *ti);
+extern int verity_ctr(struct dm_target *ti, unsigned argc, char **argv);
+extern int verity_map(struct dm_target *ti, struct bio *bio);
#endif /* DM_VERITY_H */
diff --git a/drivers/md/md.c b/drivers/md/md.c
index b1e1f6b95782..c57fdf847b47 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -293,6 +293,8 @@ static blk_qc_t md_make_request(struct request_queue *q, struct bio *bio)
* go away inside make_request
*/
sectors = bio_sectors(bio);
+ /* bio could be mergeable after passing to underlayer */
+ bio->bi_rw &= ~REQ_NOMERGE;
mddev->pers->make_request(mddev, bio);
cpu = part_stat_lock();
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
index d6bb18522e0c..6c09f3820dfd 100644
--- a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
+++ b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
@@ -460,7 +460,7 @@ int msm_camera_set_clk_flags(struct clk *clk, unsigned long flags)
if (!clk)
return -EINVAL;
- CDBG("clk : %p, flags : %ld\n", clk, flags);
+ CDBG("clk : %pK, flags : %ld\n", clk, flags);
return clk_set_flags(clk, flags);
}
@@ -1040,8 +1040,11 @@ uint32_t msm_camera_unregister_bus_client(enum cam_bus_client id)
mutex_destroy(&g_cv[id].lock);
msm_bus_scale_unregister_client(g_cv[id].bus_client);
- msm_bus_cl_clear_pdata(g_cv[id].pdata);
- memset(&g_cv[id], 0, sizeof(struct msm_cam_bus_pscale_data));
+ g_cv[id].bus_client = 0;
+ g_cv[id].num_usecases = 0;
+ g_cv[id].num_paths = 0;
+ g_cv[id].vector_index = 0;
+ g_cv[id].dyn_vote = 0;
return 0;
}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
index 9e68af87b86c..96f9e61578f2 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -105,6 +105,9 @@ static void msm_vfe40_config_irq(struct vfe_device *vfe_dev,
case MSM_ISP_IRQ_ENABLE:
vfe_dev->irq0_mask |= irq0_mask;
vfe_dev->irq1_mask |= irq1_mask;
+ msm_camera_io_w(irq0_mask, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w(irq0_mask, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
break;
case MSM_ISP_IRQ_DISABLE:
vfe_dev->irq0_mask &= ~irq0_mask;
@@ -113,6 +116,9 @@ static void msm_vfe40_config_irq(struct vfe_device *vfe_dev,
case MSM_ISP_IRQ_SET:
vfe_dev->irq0_mask = irq0_mask;
vfe_dev->irq1_mask = irq1_mask;
+ msm_camera_io_w(irq0_mask, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w(irq0_mask, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
}
msm_camera_io_w_mb(vfe_dev->irq0_mask, vfe_dev->vfe_base + 0x28);
msm_camera_io_w_mb(vfe_dev->irq1_mask, vfe_dev->vfe_base + 0x2C);
@@ -329,13 +335,6 @@ static void msm_vfe40_init_hardware_reg(struct vfe_device *vfe_dev)
msm_camera_io_w(0x10000001, vfe_dev->vfe_base + 0x50);
msm_vfe40_config_irq(vfe_dev, 0x800000E0, 0xFEFFFF7E,
MSM_ISP_IRQ_ENABLE);
- msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30);
- msm_camera_io_w_mb(0xFEFFFFFF, vfe_dev->vfe_base + 0x34);
- msm_camera_io_w(1, vfe_dev->vfe_base + 0x24);
-
- msm_camera_io_w(0, vfe_dev->vfe_base + 0x30);
- msm_camera_io_w_mb(0, vfe_dev->vfe_base + 0x34);
- msm_camera_io_w(1, vfe_dev->vfe_base + 0x24);
}
static void msm_vfe40_clear_status_reg(struct vfe_device *vfe_dev)
@@ -1028,7 +1027,7 @@ static int msm_vfe40_start_fetch_engine(struct vfe_device *vfe_dev,
rc = vfe_dev->buf_mgr->ops->get_buf_by_index(
vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf);
if (rc < 0 || !buf) {
- pr_err("%s: No fetch buffer rc= %d buf= %p\n",
+ pr_err("%s: No fetch buffer rc= %d buf= %pK\n",
__func__, rc, buf);
return -EINVAL;
}
@@ -1742,10 +1741,6 @@ static int msm_vfe40_axi_halt(struct vfe_device *vfe_dev,
/* Keep only halt and restart mask */
msm_vfe40_config_irq(vfe_dev, (1 << 31), (1 << 8),
MSM_ISP_IRQ_SET);
- /*Clear IRQ Status */
- msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x30);
- msm_camera_io_w(0xFEFFFEFF, vfe_dev->vfe_base + 0x34);
- msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
msm_isp_get_timestamp(&ts, vfe_dev);
/* if any stream is waiting for update, signal complete */
@@ -1777,12 +1772,8 @@ static int msm_vfe40_axi_halt(struct vfe_device *vfe_dev,
static void msm_vfe40_axi_restart(struct vfe_device *vfe_dev,
uint32_t blocking, uint32_t enable_camif)
{
- msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask,
- MSM_ISP_IRQ_SET);
- /* Clear IRQ Status */
- msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x30);
- msm_camera_io_w(0xFEFFFEFF, vfe_dev->vfe_base + 0x34);
- msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
+ msm_vfe40_config_irq(vfe_dev, 0x800000E0, 0xFEFFFF7E,
+ MSM_ISP_IRQ_ENABLE);
msm_camera_io_w_mb(0x140000, vfe_dev->vfe_base + 0x318);
/* Start AXI */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
index fb4f7a1dcc92..a9940927d426 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
@@ -74,6 +74,9 @@ static void msm_vfe44_config_irq(struct vfe_device *vfe_dev,
case MSM_ISP_IRQ_ENABLE:
vfe_dev->irq0_mask |= irq0_mask;
vfe_dev->irq1_mask |= irq1_mask;
+ msm_camera_io_w(irq0_mask, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w(irq1_mask, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
break;
case MSM_ISP_IRQ_DISABLE:
vfe_dev->irq0_mask &= ~irq0_mask;
@@ -82,6 +85,9 @@ static void msm_vfe44_config_irq(struct vfe_device *vfe_dev,
case MSM_ISP_IRQ_SET:
vfe_dev->irq0_mask = irq0_mask;
vfe_dev->irq1_mask = irq1_mask;
+ msm_camera_io_w(irq0_mask, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w(irq1_mask, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
break;
}
msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x28);
@@ -175,9 +181,6 @@ static void msm_vfe44_init_hardware_reg(struct vfe_device *vfe_dev)
msm_camera_io_w(0x10000001, vfe_dev->vfe_base + 0x50);
msm_vfe44_config_irq(vfe_dev, 0x800000E0, 0xFFFFFF7E,
MSM_ISP_IRQ_ENABLE);
- msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30);
- msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x34);
- msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x24);
}
@@ -1349,15 +1352,6 @@ static int msm_vfe44_axi_halt(struct vfe_device *vfe_dev,
msm_vfe44_config_irq(vfe_dev, (1 << 31), (1 << 8),
MSM_ISP_IRQ_SET);
- /*Clear IRQ Status0, only leave reset irq mask*/
- msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x30);
-
- /*Clear IRQ Status1, only leave halt irq mask*/
- msm_camera_io_w(0xFEFFFEFF, vfe_dev->vfe_base + 0x34);
-
- /*push clear cmd*/
- msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
-
if (atomic_read(&vfe_dev->error_info.overflow_state)
== OVERFLOW_DETECTED)
pr_err_ratelimited("%s: VFE%d halt for recovery, blocking %d\n",
@@ -1393,11 +1387,8 @@ static int msm_vfe44_axi_halt(struct vfe_device *vfe_dev,
static void msm_vfe44_axi_restart(struct vfe_device *vfe_dev,
uint32_t blocking, uint32_t enable_camif)
{
- msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask,
- MSM_ISP_IRQ_SET);
- msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x30);
- msm_camera_io_w(0xFEFFFEFF, vfe_dev->vfe_base + 0x34);
- msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
+ msm_vfe44_config_irq(vfe_dev, 0x800000E0, 0xFFFFFF7E,
+ MSM_ISP_IRQ_ENABLE);
msm_camera_io_w_mb(0x140000, vfe_dev->vfe_base + 0x318);
/* Start AXI */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
index d45b6ff0a7d0..d239c6069ad9 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
@@ -96,6 +96,9 @@ static void msm_vfe46_config_irq(struct vfe_device *vfe_dev,
case MSM_ISP_IRQ_ENABLE:
vfe_dev->irq0_mask |= irq0_mask;
vfe_dev->irq1_mask |= irq1_mask;
+ msm_camera_io_w(irq0_mask, vfe_dev->vfe_base + 0x64);
+ msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x68);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58);
break;
case MSM_ISP_IRQ_DISABLE:
vfe_dev->irq0_mask &= ~irq0_mask;
@@ -104,6 +107,9 @@ static void msm_vfe46_config_irq(struct vfe_device *vfe_dev,
case MSM_ISP_IRQ_SET:
vfe_dev->irq0_mask = irq0_mask;
vfe_dev->irq1_mask = irq1_mask;
+ msm_camera_io_w(irq1_mask, vfe_dev->vfe_base + 0x64);
+ msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x68);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58);
break;
}
msm_camera_io_w_mb(vfe_dev->irq0_mask,
@@ -204,9 +210,6 @@ static void msm_vfe46_init_hardware_reg(struct vfe_device *vfe_dev)
/* IRQ_MASK/CLEAR */
msm_vfe46_config_irq(vfe_dev, 0x810000E0, 0xFFFFFF7E,
MSM_ISP_IRQ_ENABLE);
- msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64);
- msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68);
- msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58);
}
static void msm_vfe46_clear_status_reg(struct vfe_device *vfe_dev)
@@ -1159,11 +1162,6 @@ static void msm_vfe46_update_camif_state(struct vfe_device *vfe_dev,
/* testgen OFF*/
if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
msm_camera_io_w(1 << 1, vfe_dev->vfe_base + 0xAF4);
- msm_camera_io_w(0, vfe_dev->vfe_base + 0x64);
- msm_camera_io_w((1 << 0), vfe_dev->vfe_base + 0x68);
- msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x58);
- msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask,
- vfe_dev->irq1_mask, MSM_ISP_IRQ_SET);
}
}
@@ -1436,15 +1434,6 @@ static int msm_vfe46_axi_halt(struct vfe_device *vfe_dev,
msm_vfe46_config_irq(vfe_dev, (1 << 31), (1 << 8),
MSM_ISP_IRQ_SET);
- /*Clear IRQ Status0, only leave reset irq mask*/
- msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x64);
-
- /*Clear IRQ Status1, only leave halt irq mask*/
- msm_camera_io_w(0xFFFFFEFF, vfe_dev->vfe_base + 0x68);
-
- /*push clear cmd*/
- msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x58);
-
if (atomic_read(&vfe_dev->error_info.overflow_state)
== OVERFLOW_DETECTED)
pr_err_ratelimited("%s: VFE%d halt for recovery, blocking %d\n",
@@ -1479,11 +1468,8 @@ static int msm_vfe46_axi_halt(struct vfe_device *vfe_dev,
static void msm_vfe46_axi_restart(struct vfe_device *vfe_dev,
uint32_t blocking, uint32_t enable_camif)
{
- msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask,
- MSM_ISP_IRQ_SET);
- msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x64);
- msm_camera_io_w(0xFFFFFEFF, vfe_dev->vfe_base + 0x68);
- msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x58);
+ msm_vfe46_config_irq(vfe_dev, 0x810000E0, 0xFFFFFF7E,
+ MSM_ISP_IRQ_ENABLE);
msm_camera_io_w_mb(0x20000, vfe_dev->vfe_base + 0x3CC);
/* Start AXI */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
index 6d1ad8ef6804..c50c55a69fb5 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
@@ -156,6 +156,9 @@ void msm_vfe47_config_irq(struct vfe_device *vfe_dev,
case MSM_ISP_IRQ_ENABLE:
vfe_dev->irq0_mask |= irq0_mask;
vfe_dev->irq1_mask |= irq1_mask;
+ msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x64);
+ msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x68);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58);
break;
case MSM_ISP_IRQ_DISABLE:
vfe_dev->irq0_mask &= ~irq0_mask;
@@ -164,6 +167,9 @@ void msm_vfe47_config_irq(struct vfe_device *vfe_dev,
case MSM_ISP_IRQ_SET:
vfe_dev->irq0_mask = irq0_mask;
vfe_dev->irq1_mask = irq1_mask;
+ msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x64);
+ msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x68);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58);
break;
}
msm_camera_io_w_mb(vfe_dev->irq0_mask,
@@ -404,9 +410,6 @@ void msm_vfe47_init_hardware_reg(struct vfe_device *vfe_dev)
/* IRQ_MASK/CLEAR */
vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
0x810000E0, 0xFFFFFF7E, MSM_ISP_IRQ_ENABLE);
- msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64);
- msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68);
- msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58);
}
void msm_vfe47_clear_status_reg(struct vfe_device *vfe_dev)
@@ -792,6 +795,8 @@ void msm_vfe47_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
uint32_t comp_mask, comp_mask_index;
+ int i;
+ uint32_t overflow_mask = 0;
comp_mask_index = stream_info->comp_mask_index[vfe_idx];
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x74);
@@ -800,8 +805,11 @@ void msm_vfe47_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
stream_composite_mask << (comp_mask_index * 8));
msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74);
+ for (i = 0; i < stream_info->num_planes; i++)
+ overflow_mask |= (1 << (stream_info->wm[vfe_idx][i] + 9));
+
vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
- 1 << (comp_mask_index + 25), 0,
+ 1 << (comp_mask_index + 25), overflow_mask,
MSM_ISP_IRQ_ENABLE);
}
@@ -827,7 +835,8 @@ void msm_vfe47_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
- 1 << (stream_info->wm[vfe_idx][0] + 8), 0,
+ 1 << (stream_info->wm[vfe_idx][0] + 8),
+ 1 << (stream_info->wm[vfe_idx][0] + 9),
MSM_ISP_IRQ_ENABLE);
}
@@ -1309,7 +1318,7 @@ void msm_vfe47_cfg_camif(struct vfe_device *vfe_dev,
msm_camera_io_w(
subsample_cfg->first_line << 16 |
subsample_cfg->last_line,
- vfe_dev->vfe_base + 0xCE4);
+ vfe_dev->vfe_base + 0xCE8);
val = msm_camera_io_r(
vfe_dev->vfe_base + 0x47C);
ISP_DBG("%s: camif raw crop enabled\n", __func__);
@@ -1417,11 +1426,8 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev,
val = msm_camera_io_r(vfe_dev->vfe_base + 0x47C);
if (update_state == ENABLE_CAMIF) {
- msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x64);
- msm_camera_io_w(0x81, vfe_dev->vfe_base + 0x68);
- msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x58);
vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
- 0x15, 0x81,
+ 0x15, 0x91,
MSM_ISP_IRQ_ENABLE);
if ((vfe_dev->hvx_cmd > HVX_DISABLE) &&
@@ -1452,8 +1458,8 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev,
/* For testgen always halt on camif boundary */
if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
update_state = DISABLE_CAMIF;
- /* turn off camif violation and error irqs */
- vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, 0, 0x81,
+ /* turn off camif, violation and write master overwrite irq */
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, 0, 0x91,
MSM_ISP_IRQ_DISABLE);
val = msm_camera_io_r(vfe_dev->vfe_base + 0x464);
/* disable danger signal */
@@ -1471,16 +1477,6 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev,
if ((vfe_dev->hvx_cmd > HVX_DISABLE) &&
(vfe_dev->hvx_cmd <= HVX_ROUND_TRIP))
msm_vfe47_configure_hvx(vfe_dev, 0);
- /*
- * restore the irq that were disabled for camif stop and clear
- * the camif error interrupts if generated during that period
- */
- msm_camera_io_w(0, vfe_dev->vfe_base + 0x64);
- msm_camera_io_w(1 << 0, vfe_dev->vfe_base + 0x68);
- msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x58);
- vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
- vfe_dev->irq0_mask,
- vfe_dev->irq1_mask, MSM_ISP_IRQ_SET);
}
}
@@ -1768,16 +1764,6 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev,
(1 << 31), (1 << 8),
MSM_ISP_IRQ_SET);
- /*Clear IRQ Status0, only leave reset irq mask*/
- msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x64);
-
- /*Clear IRQ Status1, only leave halt irq mask*/
- msm_camera_io_w(0xFFFFFEFF, vfe_dev->vfe_base + 0x68);
-
- /*push clear cmd*/
- msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x58);
-
-
if (atomic_read(&vfe_dev->error_info.overflow_state)
== OVERFLOW_DETECTED)
pr_err_ratelimited("%s: VFE%d halt for recovery, blocking %d\n",
@@ -1815,12 +1801,7 @@ void msm_vfe47_axi_restart(struct vfe_device *vfe_dev,
uint32_t blocking, uint32_t enable_camif)
{
vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
- vfe_dev->irq0_mask, vfe_dev->irq1_mask,
- MSM_ISP_IRQ_SET);
- msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x64);
- msm_camera_io_w(0xFFFFFEFF, vfe_dev->vfe_base + 0x68);
- msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x58);
-
+ 0x810000E0, 0xFFFFFF7E, MSM_ISP_IRQ_ENABLE);
/* Start AXI */
msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x400);
@@ -1832,6 +1813,8 @@ void msm_vfe47_axi_restart(struct vfe_device *vfe_dev,
vfe_dev->hw_info->vfe_ops.core_ops.
update_camif_state(vfe_dev, ENABLE_CAMIF);
}
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 0x810000E0, 0xFFFFFF7E, MSM_ISP_IRQ_ENABLE);
}
uint32_t msm_vfe47_get_wm_mask(
@@ -1957,40 +1940,40 @@ void msm_vfe47_stats_cfg_wm_irq_mask(
switch (STATS_IDX(stream_info->stream_handle[vfe_idx])) {
case STATS_COMP_IDX_AEC_BG:
vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
- 1 << 15, 0, MSM_ISP_IRQ_ENABLE);
+ 1 << 15, 1 << 24, MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_HDR_BE:
vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
- 1 << 16, 0, MSM_ISP_IRQ_ENABLE);
+ 1 << 16, 1 << 16, MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_BG:
vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
- 1 << 17, 0, MSM_ISP_IRQ_ENABLE);
+ 1 << 17, 1 << 17, MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_BF:
vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
- 1 << 18, 1 << 26,
+ 1 << 18, 1 << 26 | 1 << 18,
MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_HDR_BHIST:
vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
- 1 << 19, 0, MSM_ISP_IRQ_ENABLE);
+ 1 << 19, 1 << 19, MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_RS:
vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
- 1 << 20, 0, MSM_ISP_IRQ_ENABLE);
+ 1 << 20, 1 << 20, MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_CS:
vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
- 1 << 21, 0, MSM_ISP_IRQ_ENABLE);
+ 1 << 21, 1 << 21, MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_IHIST:
vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
- 1 << 22, 0, MSM_ISP_IRQ_ENABLE);
+ 1 << 22, 1 << 22, MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_BHIST:
vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
- 1 << 23, 0, MSM_ISP_IRQ_ENABLE);
+ 1 << 23, 1 << 23, MSM_ISP_IRQ_ENABLE);
break;
default:
pr_err("%s: Invalid stats idx %d\n", __func__,
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 fbc2fee5a51d..e8289f05d28f 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
@@ -14,6 +14,7 @@
#include <asm/div64.h>
#include "msm_isp_util.h"
#include "msm_isp_axi_util.h"
+#include "msm_isp48.h"
#define HANDLE_TO_IDX(handle) (handle & 0xFF)
#define ISP_SOF_DEBUG_COUNT 0
@@ -66,7 +67,7 @@ static int msm_isp_axi_create_stream(struct vfe_device *vfe_dev,
} else {
/* check if the stream has been added for the vfe-device */
if (stream_info->vfe_mask & (1 << vfe_dev->pdev->id)) {
- pr_err("%s: stream %p/%x is already added for vfe dev %d vfe_mask %x\n",
+ pr_err("%s: stream %pK/%x is already added for vfe dev %d vfe_mask %x\n",
__func__, stream_info, stream_info->stream_id,
vfe_dev->pdev->id, stream_info->vfe_mask);
return -EINVAL;
@@ -1254,7 +1255,7 @@ int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg)
if (vfe_idx == -ENOTTY ||
stream_release_cmd->stream_handle !=
stream_info->stream_handle[vfe_idx]) {
- pr_err("%s: Invalid stream %p handle %x/%x vfe_idx %d vfe_dev %d num_isp %d\n",
+ pr_err("%s: Invalid stream %pK handle %x/%x vfe_idx %d vfe_dev %d num_isp %d\n",
__func__, stream_info,
stream_release_cmd->stream_handle,
vfe_idx != -ENOTTY ?
@@ -2017,22 +2018,25 @@ static void msm_isp_input_disable(struct vfe_device *vfe_dev)
if (i != VFE_PIX_0 || ext_read)
continue;
/* halt camif */
- if (total_stream_count == 0)
+ if (total_stream_count == 0) {
vfe_dev->hw_info->vfe_ops.core_ops.
update_camif_state(vfe_dev,
DISABLE_CAMIF_IMMEDIATELY);
- else
+ } else {
vfe_dev->hw_info->vfe_ops.core_ops.
update_camif_state(vfe_dev, DISABLE_CAMIF);
+ }
}
-
/* halt and reset hardware if all streams are disabled */
if (total_stream_count == 0) {
vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1);
+ msm_isp_flush_tasklet(vfe_dev);
vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, 0, 1);
- vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev);
-
+ if (msm_vfe_is_vfe48(vfe_dev))
+ vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev,
+ 0, 1);
}
+
}
/**
@@ -2719,6 +2723,20 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev,
for (i = 0; i < num_streams; i++) {
stream_info = streams[i];
+ msm_isp_update_intf_stream_cnt(stream_info, 0);
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ update_vfes[vfe_dev->pdev->id] = vfe_dev;
+ }
+ }
+ for (k = 0; k < MAX_VFE; k++) {
+ if (!update_vfes[k])
+ continue;
+ msm_isp_input_disable(update_vfes[k]);
+ }
+
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
spin_lock_irqsave(&stream_info->lock, flags);
/*
* since we can get here from start axi stream error path due
@@ -2743,12 +2761,10 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev,
else
vfe_dev->hw_info->vfe_ops.axi_ops.
clear_wm_irq_mask(vfe_dev, stream_info);
- update_vfes[vfe_dev->pdev->id] = vfe_dev;
}
init_completion(&stream_info->inactive_comp);
stream_info->state = STOP_PENDING;
spin_unlock_irqrestore(&stream_info->lock, flags);
- msm_isp_update_intf_stream_cnt(stream_info, 0);
}
for (k = 0; k < MAX_VFE; k++) {
@@ -2843,7 +2859,6 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev,
if (!update_vfes[k])
continue;
msm_isp_update_stream_bandwidth(update_vfes[k]);
- msm_isp_input_disable(update_vfes[k]);
}
for (i = 0; i < num_streams; i++) {
@@ -3468,7 +3483,7 @@ static int msm_isp_stream_axi_cfg_update(struct vfe_device *vfe_dev,
if (stream_info->update_vfe_mask) {
if (stream_info->update_vfe_mask & (1 << vfe_dev->pdev->id)) {
spin_unlock_irqrestore(&stream_info->lock, flags);
- pr_err("%s: Stream %p/%x Update already in progress for vfe %d\n",
+ pr_err("%s: Stream %pK/%x Update already in progress for vfe %d\n",
__func__, stream_info, stream_info->stream_src,
vfe_dev->pdev->id);
return -EINVAL;
@@ -3777,8 +3792,9 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev,
(~(pingpong_status >>
stream_info->wm[vfe_idx][i]) & 0x1)) {
spin_unlock_irqrestore(&stream_info->lock, flags);
- pr_err("%s: Write master ping pong mismatch. Status: 0x%x\n",
- __func__, pingpong_status);
+ pr_err("%s: Write master ping pong mismatch. Status: 0x%x %x\n",
+ __func__, pingpong_status,
+ stream_info->stream_src);
msm_isp_halt_send_error(vfe_dev,
ISP_EVENT_PING_PONG_MISMATCH);
return;
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
index c9656e748f09..094a7861831a 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -840,7 +840,7 @@ static uint16_t msm_ispif_get_cids_mask_from_cfg(
uint16_t cids_mask = 0;
BUG_ON(!entry);
- for (i = 0; i < entry->num_cids; i++)
+ for (i = 0; i < entry->num_cids && i < MAX_CID_CH_PARAM_ENTRY; i++)
cids_mask |= (1 << entry->cids[i]);
return cids_mask;
@@ -970,7 +970,7 @@ static void msm_ispif_intf_cmd(struct ispif_device *ispif, uint32_t cmd_bits,
pr_err("%s: invalid interface type\n", __func__);
return;
}
- if (params->entries[i].num_cids > MAX_CID_CH) {
+ if (params->entries[i].num_cids > MAX_CID_CH_PARAM_ENTRY) {
pr_err("%s: out of range of cid_num %d\n",
__func__, params->entries[i].num_cids);
return;
@@ -1535,9 +1535,6 @@ static void msm_ispif_release(struct ispif_device *ispif)
{
BUG_ON(!ispif);
- msm_ispif_reset(ispif);
- msm_ispif_reset_hw(ispif);
-
msm_camera_enable_irq(ispif->irq, 0);
ispif->ispif_state = ISPIF_POWER_DOWN;
diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
index 59135225cb15..c779ee46c19a 100644
--- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
+++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
@@ -286,7 +286,7 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id,
break;
}
if (vb2_v4l2_buf != vb) {
- pr_err("VB buffer is INVALID vb=%p, ses_id=%d, str_id=%d\n",
+ pr_err("VB buffer is INVALID vb=%pK, ses_id=%d, str_id=%d\n",
vb, session_id, stream_id);
spin_unlock_irqrestore(&stream->stream_lock, flags);
return -EINVAL;
@@ -329,7 +329,7 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id,
break;
}
if (vb2_v4l2_buf != vb) {
- pr_err("VB buffer is INVALID ses_id=%d, str_id=%d, vb=%p\n",
+ pr_err("VB buffer is INVALID ses_id=%d, str_id=%d, vb=%pK\n",
session_id, stream_id, vb);
spin_unlock_irqrestore(&stream->stream_lock, flags);
return -EINVAL;
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
index e0d6977b24a6..ab074ffbcdfb 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -1074,6 +1074,13 @@ static int32_t cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin)
goto end;
}
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CPP,
+ CAM_AHB_NOMINAL_VOTE);
+ if (rc < 0) {
+ pr_err("%s:%d: failed to vote for AHB\n", __func__, __LINE__);
+ goto end;
+ }
+
msm_camera_io_w(0x1, cpp_dev->base + MSM_CPP_MICRO_CLKEN_CTL);
msm_camera_io_w(0x1, cpp_dev->base +
MSM_CPP_MICRO_BOOT_START);
@@ -1082,7 +1089,7 @@ static int32_t cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin)
if (rc) {
pr_err("%s:%d] poll command %x failed %d", __func__, __LINE__,
MSM_CPP_MSG_ID_CMD, rc);
- goto end;
+ goto vote;
}
msm_camera_io_w(0xFFFFFFFF, cpp_dev->base +
@@ -1092,7 +1099,7 @@ static int32_t cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin)
if (rc) {
pr_err("%s:%d] poll rx empty failed %d",
__func__, __LINE__, rc);
- goto end;
+ goto vote;
}
/*Start firmware loading*/
msm_cpp_write(MSM_CPP_CMD_FW_LOAD, cpp_dev->base);
@@ -1102,7 +1109,7 @@ static int32_t cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin)
if (rc) {
pr_err("%s:%d] poll rx empty failed %d",
__func__, __LINE__, rc);
- goto end;
+ goto vote;
}
for (i = 0; i < cpp_dev->fw->size/4; i++) {
msm_cpp_write(*ptr_bin, cpp_dev->base);
@@ -1111,7 +1118,7 @@ static int32_t cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin)
if (rc) {
pr_err("%s:%d] poll rx empty failed %d",
__func__, __LINE__, rc);
- goto end;
+ goto vote;
}
}
ptr_bin++;
@@ -1124,21 +1131,21 @@ static int32_t cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin)
if (rc) {
pr_err("%s:%d] poll command %x failed %d", __func__, __LINE__,
MSM_CPP_MSG_ID_OK, rc);
- goto end;
+ goto vote;
}
rc = msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
if (rc) {
pr_err("%s:%d] poll command %x failed %d", __func__, __LINE__,
MSM_CPP_MSG_ID_CMD, rc);
- goto end;
+ goto vote;
}
rc = msm_cpp_poll_rx_empty(cpp_dev->base);
if (rc) {
pr_err("%s:%d] poll rx empty failed %d",
__func__, __LINE__, rc);
- goto end;
+ goto vote;
}
/*Trigger MC to jump to start address*/
msm_cpp_write(MSM_CPP_CMD_EXEC_JUMP, cpp_dev->base);
@@ -1148,21 +1155,21 @@ static int32_t cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin)
if (rc) {
pr_err("%s:%d] poll command %x failed %d", __func__, __LINE__,
MSM_CPP_MSG_ID_CMD, rc);
- goto end;
+ goto vote;
}
rc = msm_cpp_poll(cpp_dev->base, 0x1);
if (rc) {
pr_err("%s:%d] poll command 0x1 failed %d", __func__, __LINE__,
rc);
- goto end;
+ goto vote;
}
rc = msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_JUMP_ACK);
if (rc) {
pr_err("%s:%d] poll command %x failed %d", __func__, __LINE__,
MSM_CPP_MSG_ID_JUMP_ACK, rc);
- goto end;
+ goto vote;
}
rc = msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_TRAILER);
@@ -1171,6 +1178,11 @@ static int32_t cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin)
MSM_CPP_MSG_ID_JUMP_ACK, rc);
}
+vote:
+ rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CPP,
+ CAM_AHB_SVS_VOTE);
+ if (rc < 0)
+ pr_err("%s:%d: failed to vote for AHB\n", __func__, __LINE__);
end:
return rc;
}
@@ -2344,21 +2356,19 @@ static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev,
return -EINVAL;
}
- if (!new_frame->partial_frame_indicator) {
- if (cpp_frame_msg[new_frame->msg_len - 1] !=
- MSM_CPP_MSG_ID_TRAILER) {
- pr_err("Invalid frame message\n");
- return -EINVAL;
- }
+ if (cpp_frame_msg[new_frame->msg_len - 1] !=
+ MSM_CPP_MSG_ID_TRAILER) {
+ pr_err("Invalid frame message\n");
+ return -EINVAL;
+ }
- if ((stripe_base + new_frame->num_strips * stripe_size + 1) !=
- new_frame->msg_len) {
- pr_err("Invalid frame message,len=%d,expected=%d\n",
- new_frame->msg_len,
- (stripe_base +
- new_frame->num_strips * stripe_size + 1));
- return -EINVAL;
- }
+ if ((stripe_base + new_frame->num_strips * stripe_size + 1) !=
+ new_frame->msg_len) {
+ pr_err("Invalid frame message,len=%d,expected=%d\n",
+ new_frame->msg_len,
+ (stripe_base +
+ new_frame->num_strips * stripe_size + 1));
+ return -EINVAL;
}
if (cpp_dev->iommu_state != CPP_IOMMU_STATE_ATTACHED) {
@@ -4188,7 +4198,7 @@ static int cpp_probe(struct platform_device *pdev)
cpp_dev->state = CPP_STATE_BOOT;
rc = cpp_init_hardware(cpp_dev);
if (rc < 0)
- goto cpp_probe_init_error;
+ goto bus_de_init;
media_entity_init(&cpp_dev->msm_sd.sd.entity, 0, NULL, 0);
cpp_dev->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
@@ -4227,7 +4237,7 @@ static int cpp_probe(struct platform_device *pdev)
if (!cpp_dev->work) {
pr_err("no enough memory\n");
rc = -ENOMEM;
- goto cpp_probe_init_error;
+ goto bus_de_init;
}
INIT_WORK((struct work_struct *)cpp_dev->work, msm_cpp_do_timeout_work);
@@ -4247,6 +4257,12 @@ static int cpp_probe(struct platform_device *pdev)
else
CPP_DBG("FAILED.");
return rc;
+
+bus_de_init:
+ if (cpp_dev->bus_master_flag)
+ msm_cpp_deinit_bandwidth_mgr(cpp_dev);
+ else
+ msm_isp_deinit_bandwidth_mgr(ISP_CPP);
cpp_probe_init_error:
media_entity_cleanup(&cpp_dev->msm_sd.sd.entity);
msm_sd_unregister(&cpp_dev->msm_sd);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
index 5f749bd46273..0d27de5c9b4b 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
@@ -645,7 +645,7 @@ static int32_t msm_flash_release(
static int32_t msm_flash_config(struct msm_flash_ctrl_t *flash_ctrl,
void __user *argp)
{
- int32_t rc = -EINVAL;
+ int32_t rc = 0;
struct msm_flash_cfg_data_t *flash_data =
(struct msm_flash_cfg_data_t *) argp;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c
index 25c152be2b71..5a330db0f9a5 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c
@@ -794,7 +794,7 @@ int32_t msm_camera_tz_i2c_register_sensor(
return -EINVAL;
}
- CDBG("id=%d, client=%p\n", s_ctrl->id, s_ctrl);
+ CDBG("id=%d, client=%pK\n", s_ctrl->id, s_ctrl);
sensor_info[s_ctrl->id].s_ctrl = s_ctrl;
sensor_info[s_ctrl->id].secure = s_ctrl->is_secure;
return 0;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index 594bac6c5902..0d8c6cb8f3f3 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -266,7 +266,7 @@ static int sde_rotator_update_clk(struct sde_rot_mgr *mgr)
SDEROT_DBG("core_clk %lu\n", total_clk_rate);
ATRACE_INT("core_clk", total_clk_rate);
- sde_rotator_set_clk_rate(mgr, total_clk_rate, mgr->core_clk_idx);
+ sde_rotator_set_clk_rate(mgr, total_clk_rate, SDE_ROTATOR_CLK_ROT_CORE);
return 0;
}
@@ -300,11 +300,34 @@ static void sde_rotator_footswitch_ctrl(struct sde_rot_mgr *mgr, bool on)
mgr->regulator_enable = on;
}
-int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable)
+static int sde_rotator_enable_clk(struct sde_rot_mgr *mgr, int clk_idx)
+{
+ struct clk *clk;
+ int ret = 0;
+
+ clk = sde_rotator_get_clk(mgr, clk_idx);
+ if (clk) {
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ SDEROT_ERR("enable failed clk_idx %d\n", clk_idx);
+ }
+
+ return ret;
+}
+
+static void sde_rotator_disable_clk(struct sde_rot_mgr *mgr, int clk_idx)
{
struct clk *clk;
+
+ clk = sde_rotator_get_clk(mgr, clk_idx);
+ if (clk)
+ clk_disable_unprepare(clk);
+}
+
+int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable)
+{
int ret = 0;
- int i, changed = 0;
+ int changed = 0;
if (enable) {
if (mgr->rot_enable_clk_cnt == 0)
@@ -323,32 +346,41 @@ int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable)
if (changed) {
SDEROT_EVTLOG(enable);
SDEROT_DBG("Rotator clk %s\n", enable ? "enable" : "disable");
- for (i = 0; i < mgr->num_rot_clk; i++) {
- clk = mgr->rot_clk[i].clk;
-
- if (!clk)
- continue;
-
- if (enable) {
- ret = clk_prepare_enable(clk);
- if (ret) {
- SDEROT_ERR(
- "enable failed clk_idx %d\n",
- i);
- goto error;
- }
- } else {
- clk_disable_unprepare(clk);
- }
- }
if (enable) {
+ ret = sde_rotator_enable_clk(mgr,
+ SDE_ROTATOR_CLK_MNOC_AHB);
+ if (ret)
+ goto error_mnoc_ahb;
+ ret = sde_rotator_enable_clk(mgr,
+ SDE_ROTATOR_CLK_MDSS_AHB);
+ if (ret)
+ goto error_mdss_ahb;
+ ret = sde_rotator_enable_clk(mgr,
+ SDE_ROTATOR_CLK_MDSS_AXI);
+ if (ret)
+ goto error_mdss_axi;
+ ret = sde_rotator_enable_clk(mgr,
+ SDE_ROTATOR_CLK_ROT_CORE);
+ if (ret)
+ goto error_rot_core;
+ ret = sde_rotator_enable_clk(mgr,
+ SDE_ROTATOR_CLK_MDSS_ROT);
+ if (ret)
+ goto error_mdss_rot;
+
/* Active+Sleep */
msm_bus_scale_client_update_context(
mgr->data_bus.bus_hdl, false,
mgr->data_bus.curr_bw_uc_idx);
trace_rot_bw_ao_as_context(0);
} else {
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MDSS_ROT);
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_ROT_CORE);
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MDSS_AXI);
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MDSS_AHB);
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MNOC_AHB);
+
/* Active Only */
msm_bus_scale_client_update_context(
mgr->data_bus.bus_hdl, true,
@@ -358,9 +390,15 @@ int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable)
}
return ret;
-error:
- for (i--; i >= 0; i--)
- clk_disable_unprepare(mgr->rot_clk[i].clk);
+error_mdss_rot:
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_ROT_CORE);
+error_rot_core:
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MDSS_AXI);
+error_mdss_axi:
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MDSS_AHB);
+error_mdss_ahb:
+ sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MNOC_AHB);
+error_mnoc_ahb:
return ret;
}
@@ -2101,7 +2139,6 @@ static ssize_t sde_rotator_show_state(struct device *dev,
SPRINT("footswitch_cnt=%d\n", mgr->res_ref_cnt);
SPRINT("regulator_enable=%d\n", mgr->regulator_enable);
SPRINT("enable_clk_cnt=%d\n", mgr->rot_enable_clk_cnt);
- SPRINT("core_clk_idx=%d\n", mgr->core_clk_idx);
for (i = 0; i < mgr->num_rot_clk; i++)
if (mgr->rot_clk[i].clk)
SPRINT("%s=%lu\n", mgr->rot_clk[i].clk_name,
@@ -2301,17 +2338,39 @@ static int sde_rotator_bus_scale_register(struct sde_rot_mgr *mgr)
return 0;
}
+static inline int sde_rotator_search_dt_clk(struct platform_device *pdev,
+ struct sde_rot_mgr *mgr, char *clk_name, int clk_idx)
+{
+ struct clk *tmp;
+
+ if (clk_idx >= SDE_ROTATOR_CLK_MAX) {
+ SDEROT_ERR("invalid clk index %d\n", clk_idx);
+ return -EINVAL;
+ }
+
+ tmp = devm_clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(tmp)) {
+ SDEROT_ERR("unable to get clk: %s\n", clk_name);
+ return PTR_ERR(tmp);
+ }
+
+ strlcpy(mgr->rot_clk[clk_idx].clk_name, clk_name,
+ sizeof(mgr->rot_clk[clk_idx].clk_name));
+
+ mgr->rot_clk[clk_idx].clk = tmp;
+ return 0;
+}
+
static int sde_rotator_parse_dt_clk(struct platform_device *pdev,
struct sde_rot_mgr *mgr)
{
- u32 i = 0, rc = 0;
- const char *clock_name;
+ u32 rc = 0;
int num_clk;
num_clk = of_property_count_strings(pdev->dev.of_node,
"clock-names");
- if (num_clk <= 0) {
- SDEROT_ERR("clocks are not defined\n");
+ if ((num_clk <= 0) || (num_clk > SDE_ROTATOR_CLK_MAX)) {
+ SDEROT_ERR("Number of clocks are out of range: %d\n", num_clk);
goto clk_err;
}
@@ -2325,19 +2384,17 @@ static int sde_rotator_parse_dt_clk(struct platform_device *pdev,
goto clk_err;
}
- for (i = 0; i < mgr->num_rot_clk; i++) {
- u32 clock_rate = 0;
-
- of_property_read_string_index(pdev->dev.of_node, "clock-names",
- i, &clock_name);
- strlcpy(mgr->rot_clk[i].clk_name, clock_name,
- sizeof(mgr->rot_clk[i].clk_name));
-
- of_property_read_u32_index(pdev->dev.of_node, "clock-rate",
- i, &clock_rate);
- mgr->rot_clk[i].rate = clock_rate;
- }
-
+ if (sde_rotator_search_dt_clk(pdev, mgr, "mnoc_clk",
+ SDE_ROTATOR_CLK_MNOC_AHB) ||
+ sde_rotator_search_dt_clk(pdev, mgr, "iface_clk",
+ SDE_ROTATOR_CLK_MDSS_AHB) ||
+ sde_rotator_search_dt_clk(pdev, mgr, "axi_clk",
+ SDE_ROTATOR_CLK_MDSS_AXI) ||
+ sde_rotator_search_dt_clk(pdev, mgr, "rot_core_clk",
+ SDE_ROTATOR_CLK_ROT_CORE) ||
+ sde_rotator_search_dt_clk(pdev, mgr, "rot_clk",
+ SDE_ROTATOR_CLK_MDSS_ROT))
+ rc = -EINVAL;
clk_err:
return rc;
}
@@ -2345,10 +2402,7 @@ clk_err:
static int sde_rotator_register_clk(struct platform_device *pdev,
struct sde_rot_mgr *mgr)
{
- int i, ret;
- struct clk *clk;
- struct sde_rot_clk *rot_clk;
- int core_clk_idx = -1;
+ int ret;
ret = sde_rotator_parse_dt_clk(pdev, mgr);
if (ret) {
@@ -2356,28 +2410,6 @@ static int sde_rotator_register_clk(struct platform_device *pdev,
return -EINVAL;
}
- for (i = 0; i < mgr->num_rot_clk; i++) {
- rot_clk = &mgr->rot_clk[i];
-
- clk = devm_clk_get(&pdev->dev, rot_clk->clk_name);
- if (IS_ERR(clk)) {
- SDEROT_ERR("unable to get clk: %s\n",
- rot_clk->clk_name);
- return PTR_ERR(clk);
- }
- rot_clk->clk = clk;
-
- if (strcmp(rot_clk->clk_name, "rot_core_clk") == 0)
- core_clk_idx = i;
- }
-
- if (core_clk_idx < 0) {
- SDEROT_ERR("undefined core clk\n");
- return -ENXIO;
- }
-
- mgr->core_clk_idx = core_clk_idx;
-
return 0;
}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
index 781b03e1b974..e1b326b8eb1c 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
@@ -92,6 +92,15 @@ enum sde_rotator_ts {
SDE_ROTATOR_TS_MAX
};
+enum sde_rotator_clk_type {
+ SDE_ROTATOR_CLK_MDSS_AHB,
+ SDE_ROTATOR_CLK_MDSS_AXI,
+ SDE_ROTATOR_CLK_ROT_CORE,
+ SDE_ROTATOR_CLK_MDSS_ROT,
+ SDE_ROTATOR_CLK_MNOC_AHB,
+ SDE_ROTATOR_CLK_MAX
+};
+
struct sde_rotation_item {
/* rotation request flag */
uint32_t flags;
@@ -275,7 +284,6 @@ struct sde_rot_mgr {
int rot_enable_clk_cnt;
struct sde_rot_clk *rot_clk;
int num_rot_clk;
- int core_clk_idx;
u32 rdot_limit;
u32 wrot_limit;
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 94223b557990..cf33bc6437cc 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
@@ -49,6 +49,10 @@
#define SDE_ROT_EVTLOG_BUF_ALIGN 32
#define SDE_ROT_DEBUG_BASE_MAX 10
+#define SDE_ROT_DEFAULT_BASE_REG_CNT 0x100
+#define GROUP_BYTES 4
+#define ROW_BYTES 16
+
static DEFINE_SPINLOCK(sde_rot_xlock);
/*
@@ -963,6 +967,273 @@ static const struct file_operations sde_rotator_raw_ops = {
.release = single_release
};
+static int sde_rotator_debug_base_open(struct inode *inode, struct file *file)
+{
+ /* non-seekable */
+ file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static int sde_rotator_debug_base_release(struct inode *inode,
+ struct file *file)
+{
+ struct sde_rotator_debug_base *dbg = file->private_data;
+
+ if (dbg && dbg->buf) {
+ kfree(dbg->buf);
+ dbg->buf_len = 0;
+ dbg->buf = NULL;
+ }
+ return 0;
+}
+
+static ssize_t sde_rotator_debug_base_offset_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_rotator_debug_base *dbg = file->private_data;
+ u32 off = 0;
+ u32 cnt = SDE_ROT_DEFAULT_BASE_REG_CNT;
+ char buf[24];
+
+ if (!dbg)
+ return -ENODEV;
+
+ if (count >= sizeof(buf))
+ return -EFAULT;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ buf[count] = 0;
+
+ if (sscanf(buf, "%5x %x", &off, &cnt) < 2)
+ return -EINVAL;
+
+ if (off > dbg->max_offset)
+ return -EINVAL;
+
+ if (cnt > (dbg->max_offset - off))
+ cnt = dbg->max_offset - off;
+
+ dbg->off = off;
+ dbg->cnt = cnt;
+
+ SDEROT_DBG("offset=%x cnt=%x\n", off, cnt);
+
+ return count;
+}
+
+static ssize_t sde_rotator_debug_base_offset_read(struct file *file,
+ char __user *buff, size_t count, loff_t *ppos)
+{
+ struct sde_rotator_debug_base *dbg = file->private_data;
+ int len = 0;
+ char buf[24] = {'\0'};
+
+ if (!dbg)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0; /* the end */
+
+ len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt);
+ if (len < 0 || len >= sizeof(buf))
+ return 0;
+
+ if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len; /* increase offset */
+
+ return len;
+}
+
+static ssize_t sde_rotator_debug_base_reg_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_rotator_debug_base *dbg = file->private_data;
+ size_t off;
+ u32 data, cnt;
+ char buf[24];
+
+ if (!dbg)
+ return -ENODEV;
+
+ if (count >= sizeof(buf))
+ return -EFAULT;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ buf[count] = 0;
+
+ cnt = sscanf(buf, "%zx %x", &off, &data);
+
+ if (cnt < 2)
+ return -EFAULT;
+
+ if (off >= dbg->max_offset)
+ return -EFAULT;
+
+ /* Enable Clock for register access */
+ sde_rotator_clk_ctrl(dbg->mgr, true);
+
+ writel_relaxed(data, dbg->base + off);
+
+ /* Disable Clock after register access */
+ sde_rotator_clk_ctrl(dbg->mgr, false);
+
+ SDEROT_DBG("addr=%zx data=%x\n", off, data);
+
+ return count;
+}
+
+static ssize_t sde_rotator_debug_base_reg_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_rotator_debug_base *dbg = file->private_data;
+ size_t len;
+
+ if (!dbg) {
+ SDEROT_ERR("invalid handle\n");
+ return -ENODEV;
+ }
+
+ if (!dbg->buf) {
+ char dump_buf[64];
+ char *ptr;
+ int cnt, tot;
+
+ dbg->buf_len = sizeof(dump_buf) *
+ DIV_ROUND_UP(dbg->cnt, ROW_BYTES);
+ dbg->buf = kzalloc(dbg->buf_len, GFP_KERNEL);
+
+ if (!dbg->buf) {
+ SDEROT_ERR("not enough memory to hold reg dump\n");
+ return -ENOMEM;
+ }
+
+ ptr = dbg->base + dbg->off;
+ tot = 0;
+
+ /* Enable clock for register access */
+ sde_rotator_clk_ctrl(dbg->mgr, true);
+
+ for (cnt = dbg->cnt; cnt > 0; cnt -= ROW_BYTES) {
+ hex_dump_to_buffer(ptr, min(cnt, ROW_BYTES),
+ ROW_BYTES, GROUP_BYTES, dump_buf,
+ sizeof(dump_buf), false);
+ len = scnprintf(dbg->buf + tot, dbg->buf_len - tot,
+ "0x%08x: %s\n",
+ ((int) (unsigned long) ptr) -
+ ((int) (unsigned long) dbg->base),
+ dump_buf);
+
+ ptr += ROW_BYTES;
+ tot += len;
+ if (tot >= dbg->buf_len)
+ break;
+ }
+ /* Disable clock after register access */
+ sde_rotator_clk_ctrl(dbg->mgr, false);
+
+ dbg->buf_len = tot;
+ }
+
+ if (*ppos >= dbg->buf_len)
+ return 0; /* done reading */
+
+ len = min(count, dbg->buf_len - (size_t) *ppos);
+ if (copy_to_user(user_buf, dbg->buf + *ppos, len)) {
+ SDEROT_ERR("failed to copy to user\n");
+ return -EFAULT;
+ }
+
+ *ppos += len; /* increase offset */
+
+ return len;
+}
+
+static const struct file_operations sde_rotator_off_fops = {
+ .open = sde_rotator_debug_base_open,
+ .release = sde_rotator_debug_base_release,
+ .read = sde_rotator_debug_base_offset_read,
+ .write = sde_rotator_debug_base_offset_write,
+};
+
+static const struct file_operations sde_rotator_reg_fops = {
+ .open = sde_rotator_debug_base_open,
+ .release = sde_rotator_debug_base_release,
+ .read = sde_rotator_debug_base_reg_read,
+ .write = sde_rotator_debug_base_reg_write,
+};
+
+int sde_rotator_debug_register_base(struct sde_rotator_device *rot_dev,
+ struct dentry *debugfs_root,
+ const char *name,
+ struct sde_io_data *io_data)
+{
+ struct sde_rotator_debug_base *dbg;
+ struct dentry *ent_off, *ent_reg;
+ char dbgname[80] = "";
+ int prefix_len = 0;
+
+ if (!io_data)
+ return -EINVAL;
+
+ dbg = kzalloc(sizeof(*dbg), GFP_KERNEL);
+ if (!dbg)
+ return -ENOMEM;
+
+ if (name)
+ strlcpy(dbg->name, name, sizeof(dbg->name));
+ dbg->base = io_data->base;
+ dbg->max_offset = io_data->len;
+ dbg->off = 0;
+ dbg->cnt = SDE_ROT_DEFAULT_BASE_REG_CNT;
+
+ if (name) {
+ if (strcmp(name, "sde"))
+ prefix_len = snprintf(dbgname, sizeof(dbgname), "%s_",
+ name);
+ else
+ /*
+ * For SDE Rotator registers block, the IO base address
+ * is based on MDP IO address base. It is necessary to
+ * apply the initial offset to it from the first
+ * regdump setting.
+ */
+ dbg->base += rot_dev->mdata->regdump ?
+ rot_dev->mdata->regdump[0].offset : 0;
+ }
+
+ strlcpy(dbgname + prefix_len, "off", sizeof(dbgname) - prefix_len);
+ ent_off = debugfs_create_file(dbgname, 0644, debugfs_root, dbg,
+ &sde_rotator_off_fops);
+ if (IS_ERR_OR_NULL(ent_off)) {
+ SDEROT_ERR("debugfs_create_file: offset fail\n");
+ goto off_fail;
+ }
+
+ strlcpy(dbgname + prefix_len, "reg", sizeof(dbgname) - prefix_len);
+ ent_reg = debugfs_create_file(dbgname, 0644, debugfs_root, dbg,
+ &sde_rotator_reg_fops);
+ if (IS_ERR_OR_NULL(ent_reg)) {
+ SDEROT_ERR("debugfs_create_file: reg fail\n");
+ goto reg_fail;
+ }
+
+ dbg->mgr = rot_dev->mgr;
+
+ return 0;
+reg_fail:
+ debugfs_remove(ent_off);
+off_fail:
+ kfree(dbg);
+ return -ENODEV;
+}
+
/*
* sde_rotator_create_debugfs - Setup rotator debugfs directory structure.
* @rot_dev: Pointer to rotator device
@@ -1040,6 +1311,20 @@ struct dentry *sde_rotator_create_debugfs(
return NULL;
}
+ if (sde_rotator_debug_register_base(rot_dev, debugfs_root,
+ "sde", &rot_dev->mdata->sde_io)) {
+ SDEROT_ERR("fail create debug register for sde rotator\n");
+ debugfs_remove_recursive(debugfs_root);
+ return NULL;
+ }
+
+ if (sde_rotator_debug_register_base(rot_dev, debugfs_root,
+ "vbif_nrt", &rot_dev->mdata->vbif_nrt_io)) {
+ SDEROT_ERR("fail create debug register for sderot vbif_nrt\n");
+ debugfs_remove_recursive(debugfs_root);
+ return NULL;
+ }
+
return debugfs_root;
}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
index dcda54274fad..c2c6f9775602 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
@@ -44,6 +44,17 @@ void sde_rot_evtlog_tout_handler(bool queue, const char *name, ...);
struct sde_rotator_device;
+struct sde_rotator_debug_base {
+ char name[80];
+ void __iomem *base;
+ size_t off;
+ size_t cnt;
+ size_t max_offset;
+ char *buf;
+ size_t buf_len;
+ struct sde_rot_mgr *mgr;
+};
+
#if defined(CONFIG_DEBUG_FS)
struct dentry *sde_rotator_create_debugfs(
struct sde_rotator_device *rot_dev);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index b88f03ce89ae..08075ae90507 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -305,6 +305,39 @@ static void sde_rotator_buf_queue(struct vb2_buffer *vb)
}
/*
+ * sde_rotator_buf_finish - vb2_ops buf_finish to finalize buffer before going
+ * back to user space
+ * @vb: Pointer to vb2 buffer struct.
+ */
+static void sde_rotator_buf_finish(struct vb2_buffer *vb)
+{
+ struct sde_rotator_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ int i;
+
+ SDEDEV_DBG(ctx->rot_dev->dev,
+ "buf_finish t:%d i:%d s:%d m:%u np:%d up:%lu\n",
+ vb->type, vb->index, vb->state,
+ vb->vb2_queue->memory,
+ vb->num_planes,
+ vb->planes[0].m.userptr);
+
+ if (vb->vb2_queue->memory != VB2_MEMORY_USERPTR)
+ return;
+
+ /*
+ * We use userptr to tunnel fd, and fd can be the same across qbuf
+ * even though the underlying buffer is different. Since vb2 layer
+ * optimizes memory mapping for userptr by first checking if userptr
+ * has changed, it will not trigger put_userptr if fd value does
+ * not change. In order to force buffer release, we need to clear
+ * userptr when the current buffer is done and ready to go back to
+ * user mode. Since 0 is a valid fd, reset userptr to -1 instead.
+ */
+ for (i = 0; i < vb->num_planes; i++)
+ vb->planes[i].m.userptr = ~0;
+}
+
+/*
* sde_rotator_return_all_buffers - Return all buffers with the given status.
* @q: Pointer to vb2 buffer queue struct.
* @state: State of the buffer
@@ -460,6 +493,7 @@ static struct vb2_ops sde_rotator_vb2_q_ops = {
.stop_streaming = sde_rotator_stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
+ .buf_finish = sde_rotator_buf_finish,
};
/*
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
index d2bc76874c48..925b8497273a 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -2337,9 +2337,9 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr)
goto error_hw_rev_init;
/* set rotator CBCR to shutoff memory/periphery on clock off.*/
- clk_set_flags(mgr->rot_clk[mgr->core_clk_idx].clk,
+ clk_set_flags(mgr->rot_clk[SDE_ROTATOR_CLK_ROT_CORE].clk,
CLKFLAG_NORETAIN_MEM);
- clk_set_flags(mgr->rot_clk[mgr->core_clk_idx].clk,
+ clk_set_flags(mgr->rot_clk[SDE_ROTATOR_CLK_ROT_CORE].clk,
CLKFLAG_NORETAIN_PERIPH);
mdata->sde_rot_hw = rot;
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index 0b44896bf6b3..7388dab92c34 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -2153,6 +2153,33 @@ int create_pkt_cmd_session_set_property(
pkt->size += sizeof(u32) + sizeof(*signal_info);
break;
}
+ case HAL_PARAM_VENC_IFRAMESIZE_TYPE:
+ {
+ enum hal_iframesize_type hal =
+ *(enum hal_iframesize_type *)pdata;
+ struct hfi_iframe_size *hfi = (struct hfi_iframe_size *)
+ &pkt->rg_property_data[1];
+
+ switch (hal) {
+ case HAL_IFRAMESIZE_TYPE_DEFAULT:
+ hfi->type = HFI_IFRAME_SIZE_DEFAULT;
+ break;
+ case HAL_IFRAMESIZE_TYPE_MEDIUM:
+ hfi->type = HFI_IFRAME_SIZE_MEDIUM;
+ break;
+ case HAL_IFRAMESIZE_TYPE_HUGE:
+ hfi->type = HFI_IFRAME_SIZE_HIGH;
+ break;
+ case HAL_IFRAMESIZE_TYPE_UNLIMITED:
+ hfi->type = HFI_IFRAME_SIZE_UNLIMITED;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+ pkt->rg_property_data[0] = HFI_PROPERTY_PARAM_VENC_IFRAMESIZE;
+ pkt->size += sizeof(u32) + sizeof(struct hfi_iframe_size);
+ break;
+ }
/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
case HAL_CONFIG_BUFFER_REQUIREMENTS:
case HAL_CONFIG_PRIORITY:
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index a18840b1a1a4..88a3b4b6f7ba 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -112,6 +112,7 @@ static int hfi_process_sess_evt_seq_changed(u32 device_id,
u8 *data_ptr;
int prop_id;
enum msm_vidc_pixel_depth luma_bit_depth, chroma_bit_depth;
+ struct hfi_colour_space *colour_info;
if (sizeof(struct hfi_msg_event_notify_packet) > pkt->size) {
dprintk(VIDC_ERR,
@@ -205,6 +206,18 @@ static int hfi_process_sess_evt_seq_changed(u32 device_id,
data_ptr +=
sizeof(struct hfi_pic_struct);
break;
+ case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE:
+ data_ptr = data_ptr + sizeof(u32);
+ colour_info =
+ (struct hfi_colour_space *) data_ptr;
+ event_notify.colour_space =
+ colour_info->colour_space;
+ dprintk(VIDC_DBG,
+ "Colour space value is: %d\n",
+ colour_info->colour_space);
+ data_ptr +=
+ sizeof(struct hfi_colour_space);
+ break;
default:
dprintk(VIDC_ERR,
"%s cmd: %#x not supported\n",
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index 3633d1fb3cd1..fbdccea56f67 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -36,7 +36,7 @@
struct msm_vidc_drv *vidc_driver;
-uint32_t msm_vidc_pwr_collapse_delay = 2000;
+uint32_t msm_vidc_pwr_collapse_delay = 3000;
static inline struct msm_vidc_inst *get_vidc_inst(struct file *filp, void *fh)
{
@@ -138,12 +138,20 @@ int msm_v4l2_reqbufs(struct file *file, void *fh,
{
struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
int rc = 0;
- if (!b->count)
+ if (!b->count) {
rc = msm_vidc_release_buffers(vidc_inst, b->type);
- if (rc)
- dprintk(VIDC_WARN,
- "Failed in %s for release output buffers\n", __func__);
- return msm_vidc_reqbufs((void *)vidc_inst, b);
+ if (rc)
+ dprintk(VIDC_WARN,
+ "Failed in %s for release output buffers\n",
+ __func__);
+ } else {
+ rc = msm_vidc_reqbufs((void *)vidc_inst, b);
+ if (rc)
+ dprintk(VIDC_WARN,
+ "Failed in %s for buffer requirements\n",
+ __func__);
+ }
+ return rc;
}
int msm_v4l2_prepare_buf(struct file *file, void *fh,
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index f071aae3ccab..4ec331d121d9 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -185,6 +185,13 @@ static const char *const timestamp_mode[] = {
"Ignore",
};
+static const char *const iframe_sizes[] = {
+ "Default",
+ "Medium",
+ "Huge",
+ "Unlimited"
+};
+
static struct msm_vidc_ctrl msm_venc_ctrls[] = {
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD,
@@ -1281,6 +1288,20 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.step = 1,
.qmenu = NULL,
},
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE,
+ .name = "Bounds of I-frame size",
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT,
+ .maximum = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_UNLIMITED,
+ .default_value = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT,
+ .menu_skip_mask = ~(
+ (1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT) |
+ (1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_MEDIUM) |
+ (1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_HUGE) |
+ (1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_UNLIMITED)),
+ .qmenu = iframe_sizes,
+ },
};
@@ -2117,6 +2138,19 @@ static inline int venc_v4l2_to_hal(int id, int value)
default:
goto unknown_value;
}
+ case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE:
+ switch (value) {
+ case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT:
+ return HAL_IFRAMESIZE_TYPE_DEFAULT;
+ case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_MEDIUM:
+ return HAL_IFRAMESIZE_TYPE_MEDIUM;
+ case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_HUGE:
+ return HAL_IFRAMESIZE_TYPE_HUGE;
+ case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_UNLIMITED:
+ return HAL_IFRAMESIZE_TYPE_UNLIMITED;
+ default:
+ goto unknown_value;
+ }
}
unknown_value:
@@ -2159,6 +2193,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
int frameqp = 0;
int pic_order_cnt = 0;
struct hal_video_signal_info signal_info = {0};
+ enum hal_iframesize_type iframesize_type = HAL_IFRAMESIZE_TYPE_DEFAULT;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
@@ -2940,7 +2975,10 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL:
switch (ctrl->val) {
case V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL:
- inst->flags &= ~VIDC_TURBO;
+ if (inst->flags & VIDC_TURBO) {
+ inst->flags &= ~VIDC_TURBO;
+ msm_dcvs_init_load(inst);
+ }
break;
case V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO:
inst->flags |= VIDC_TURBO;
@@ -3239,6 +3277,13 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
}
pdata = &enable;
break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE:
+ property_id = HAL_PARAM_VENC_IFRAMESIZE_TYPE;
+ iframesize_type = venc_v4l2_to_hal(
+ V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE,
+ ctrl->val);
+ pdata = &iframesize_type;
+ break;
default:
dprintk(VIDC_ERR, "Unsupported index: %x\n", ctrl->id);
rc = -ENOTSUPP;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index b12eeddc678f..c4d06b658b30 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -652,10 +652,6 @@ int output_buffer_cache_invalidate(struct msm_vidc_inst *inst,
return -EINVAL;
}
- if (binfo->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- return 0;
-
-
for (i = 0; i < binfo->num_planes; i++) {
if (binfo->handle[i]) {
rc = msm_comm_smem_cache_operations(inst,
@@ -1172,6 +1168,7 @@ void *msm_vidc_open(int core_id, int session_type)
inst->bit_depth = MSM_VIDC_BIT_DEPTH_8;
inst->instant_bitrate = 0;
inst->pic_struct = MSM_VIDC_PIC_STRUCT_PROGRESSIVE;
+ inst->colour_space = MSM_VIDC_BT601_6_525;
for (i = SESSION_MSG_INDEX(SESSION_MSG_START);
i <= SESSION_MSG_INDEX(SESSION_MSG_END); i++) {
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index d1cc08d53017..fa2ad1754e77 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -1200,29 +1200,46 @@ static void handle_event_change(enum hal_command_response cmd, void *data)
* ptr[2] = flag to indicate bit depth or/and pic struct changed
* ptr[3] = bit depth
* ptr[4] = pic struct (progressive or interlaced)
+ * ptr[5] = colour space
*/
ptr = (u32 *)seq_changed_event.u.data;
- ptr[2] = 0x0;
- ptr[3] = inst->bit_depth;
- ptr[4] = inst->pic_struct;
- if (inst->bit_depth != event_notify->bit_depth) {
- inst->bit_depth = event_notify->bit_depth;
- ptr[2] |= V4L2_EVENT_BITDEPTH_FLAG;
+ if (ptr != NULL) {
+ ptr[2] = 0x0;
ptr[3] = inst->bit_depth;
- event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
- dprintk(VIDC_DBG,
- "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT due to bit-depth change\n");
- }
-
- if (inst->pic_struct != event_notify->pic_struct) {
- inst->pic_struct = event_notify->pic_struct;
- event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
- ptr[2] |= V4L2_EVENT_PICSTRUCT_FLAG;
ptr[4] = inst->pic_struct;
- dprintk(VIDC_DBG,
- "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT due to pic-struct change\n");
+ ptr[5] = inst->colour_space;
+
+ if (inst->bit_depth != event_notify->bit_depth) {
+ inst->bit_depth = event_notify->bit_depth;
+ ptr[2] |= V4L2_EVENT_BITDEPTH_FLAG;
+ ptr[3] = inst->bit_depth;
+ event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
+ dprintk(VIDC_DBG,
+ "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT due to bit-depth change\n");
+ }
+
+ if (inst->pic_struct != event_notify->pic_struct) {
+ inst->pic_struct = event_notify->pic_struct;
+ event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
+ ptr[2] |= V4L2_EVENT_PICSTRUCT_FLAG;
+ ptr[4] = inst->pic_struct;
+ dprintk(VIDC_DBG,
+ "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT due to pic-struct change\n");
+ }
+
+ if (inst->bit_depth == MSM_VIDC_BIT_DEPTH_10
+ && inst->colour_space !=
+ event_notify->colour_space) {
+ inst->colour_space = event_notify->colour_space;
+ event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
+ ptr[2] |= V4L2_EVENT_COLOUR_SPACE_FLAG;
+ ptr[5] = inst->colour_space;
+ dprintk(VIDC_DBG,
+ "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT due to colour space change\n");
+ }
+
}
if (event == V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT) {
@@ -3688,6 +3705,10 @@ static void log_frame(struct msm_vidc_inst *inst, struct vidc_frame_data *data,
if (msm_comm_scale_clocks(inst->core))
dprintk(VIDC_WARN,
"Failed to scale clocks. Performance might be impacted\n");
+
+ if (msm_comm_vote_bus(inst->core))
+ dprintk(VIDC_WARN,
+ "Failed to scale bus. Performance might be impacted\n");
}
static int request_seq_header(struct msm_vidc_inst *inst,
@@ -5265,24 +5286,28 @@ void msm_comm_print_inst_info(struct msm_vidc_inst *inst)
int i = 0;
bool is_decode = false;
enum vidc_ports port;
+ bool is_secure = false;
if (!inst) {
- dprintk(VIDC_ERR, "%s - invalid param %p\n",
+ dprintk(VIDC_ERR, "%s - invalid param %pK\n",
__func__, inst);
return;
}
is_decode = inst->session_type == MSM_VIDC_DECODER;
port = is_decode ? OUTPUT_PORT : CAPTURE_PORT;
+ is_secure = inst->flags & VIDC_SECURE;
dprintk(VIDC_ERR,
- "%s session, Codec type: %s HxW: %d x %d fps: %d bitrate: %d bit-depth: %s\n",
- is_decode ? "Decode" : "Encode", inst->fmts[port].name,
+ "%s session, %s, Codec type: %s HxW: %d x %d fps: %d bitrate: %d bit-depth: %s\n",
+ is_decode ? "Decode" : "Encode",
+ is_secure ? "Secure" : "Non-Secure",
+ inst->fmts[port].name,
inst->prop.height[port], inst->prop.width[port],
inst->prop.fps, inst->prop.bitrate,
!inst->bit_depth ? "8" : "10");
dprintk(VIDC_ERR,
- "---Buffer details for inst: %p of type: %d---\n",
+ "---Buffer details for inst: %pK of type: %d---\n",
inst, inst->session_type);
mutex_lock(&inst->registeredbufs.lock);
dprintk(VIDC_ERR, "registered buffer list:\n");
@@ -5326,7 +5351,7 @@ static void msm_comm_print_debug_info(struct msm_vidc_inst *inst)
struct msm_vidc_inst *temp = NULL;
if (!inst || !inst->core) {
- dprintk(VIDC_ERR, "%s - invalid param %p %p\n",
+ dprintk(VIDC_ERR, "%s - invalid param %pK %pK\n",
__func__, inst, core);
return;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
index efb90c69881f..2e1df75cb248 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
@@ -24,7 +24,7 @@ EXPORT_SYMBOL(msm_vidc_debug_out);
int msm_vidc_fw_debug = 0x18;
int msm_vidc_fw_debug_mode = 1;
int msm_vidc_fw_low_power_mode = 1;
-int msm_vidc_hw_rsp_timeout = 1000;
+int msm_vidc_hw_rsp_timeout = 2000;
bool msm_vidc_fw_coverage = false;
bool msm_vidc_vpe_csc_601_to_709 = false;
bool msm_vidc_dec_dcvs_mode = true;
@@ -157,7 +157,7 @@ struct dentry *msm_vidc_debugfs_init_drv(void)
struct dentry *f = debugfs_create_##__type(__name, S_IRUGO | S_IWUSR, \
dir, __value); \
if (IS_ERR_OR_NULL(f)) { \
- dprintk(VIDC_ERR, "Failed creating debugfs file '%pKd/%s'\n", \
+ dprintk(VIDC_ERR, "Failed creating debugfs file '%pd/%s'\n", \
dir, __name); \
f = NULL; \
} \
@@ -349,7 +349,7 @@ struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst,
dprintk(VIDC_ERR, "Invalid params, inst: %pK\n", inst);
goto failed_create_dir;
}
- snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%pK", inst);
+ snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%p", inst);
dir = debugfs_create_dir(debugfs_name, parent);
if (!dir) {
dprintk(VIDC_ERR, "Failed to create debugfs for msm_vidc\n");
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index 161e94f99040..ffe4456570e3 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -297,6 +297,7 @@ struct msm_vidc_inst {
u32 buffers_held_in_driver;
atomic_t in_flush;
u32 pic_struct;
+ u32 colour_space;
};
extern struct msm_vidc_drv *vidc_driver;
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index c87b6fc585c7..787ee43ccbd2 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -3407,10 +3407,12 @@ static int __response_handler(struct venus_hfi_device *device)
packets = device->response_pkt;
- raw_packet = kzalloc(VIDC_IFACEQ_VAR_HUGE_PKT_SIZE, GFP_TEMPORARY);
+ raw_packet = device->raw_packet;
+
if (!raw_packet || !packets) {
- dprintk(VIDC_ERR, "%s: Failed to allocate memory\n", __func__);
- kfree(raw_packet);
+ dprintk(VIDC_ERR,
+ "%s: Invalid args : Res packet = %p, Raw packet = %p\n",
+ __func__, packets, raw_packet);
return 0;
}
@@ -3566,7 +3568,6 @@ static int __response_handler(struct venus_hfi_device *device)
exit:
__flush_debug_queue(device, raw_packet);
- kfree(raw_packet);
return packet_count;
}
@@ -4553,6 +4554,13 @@ static struct venus_hfi_device *__add_device(u32 device_id,
goto err_cleanup;
}
+ hdevice->raw_packet =
+ kzalloc(VIDC_IFACEQ_VAR_HUGE_PKT_SIZE, GFP_TEMPORARY);
+ if (!hdevice->raw_packet) {
+ dprintk(VIDC_ERR, "failed to allocate raw packet\n");
+ goto err_cleanup;
+ }
+
rc = __init_regs_and_interrupts(hdevice, res);
if (rc)
goto err_cleanup;
@@ -4590,6 +4598,7 @@ err_cleanup:
if (hdevice->vidc_workq)
destroy_workqueue(hdevice->vidc_workq);
kfree(hdevice->response_pkt);
+ kfree(hdevice->raw_packet);
kfree(hdevice);
exit:
return NULL;
@@ -4631,6 +4640,7 @@ void venus_hfi_delete_device(void *device)
iounmap(dev->hal_data->register_base);
kfree(close->hal_data);
kfree(close->response_pkt);
+ kfree(close->raw_packet);
kfree(close);
break;
}
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.h b/drivers/media/platform/msm/vidc/venus_hfi.h
index 7cc71a470c2e..1d2ca88a3c1d 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.h
+++ b/drivers/media/platform/msm/vidc/venus_hfi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -247,6 +247,7 @@ struct venus_hfi_device {
struct hfi_packetization_ops *pkt_ops;
enum hfi_packetization_type packetization_type;
struct msm_vidc_cb_info *response_pkt;
+ u8 *raw_packet;
struct pm_qos_request qos;
unsigned int skip_pc_count;
struct msm_vidc_capability *sys_init_capabilities;
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 34ab36a4647b..116ce12c8dba 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -242,6 +242,7 @@ enum hal_property {
HAL_PARAM_VENC_SESSION_QP_RANGE_PACKED,
HAL_PARAM_VENC_H264_TRANSFORM_8x8,
HAL_PARAM_VENC_VIDEO_SIGNAL_INFO,
+ HAL_PARAM_VENC_IFRAMESIZE_TYPE,
};
enum hal_domain {
@@ -1002,6 +1003,13 @@ struct hal_video_signal_info {
bool full_range;
};
+enum hal_iframesize_type {
+ HAL_IFRAMESIZE_TYPE_DEFAULT,
+ HAL_IFRAMESIZE_TYPE_MEDIUM,
+ HAL_IFRAMESIZE_TYPE_HUGE,
+ HAL_IFRAMESIZE_TYPE_UNLIMITED,
+};
+
enum vidc_resource_id {
VIDC_RESOURCE_NONE,
VIDC_RESOURCE_OCMEM,
@@ -1357,6 +1365,7 @@ struct msm_vidc_cb_event {
ion_phys_addr_t packet_buffer;
ion_phys_addr_t extra_data_buffer;
u32 pic_struct;
+ u32 colour_space;
};
struct msm_vidc_cb_data_done {
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index 23240746baf1..bb9958b0a819 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -293,6 +293,8 @@ struct hfi_buffer_info {
(HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x007)
#define HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT \
(HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x009)
+#define HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE \
+ (HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x00A)
#define HFI_PROPERTY_CONFIG_VDEC_COMMON_START \
@@ -384,6 +386,8 @@ struct hfi_buffer_info {
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x031)
#define HFI_PROPERTY_PARAM_VENC_VQZIP_SEI_TYPE \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x033)
+#define HFI_PROPERTY_PARAM_VENC_IFRAMESIZE \
+ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x034)
#define HFI_PROPERTY_CONFIG_VENC_COMMON_START \
(HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x6000)
@@ -435,6 +439,10 @@ struct hfi_bitrate {
u32 layer_id;
};
+struct hfi_colour_space {
+ u32 colour_space;
+};
+
#define HFI_CAPABILITY_FRAME_WIDTH (HFI_COMMON_BASE + 0x1)
#define HFI_CAPABILITY_FRAME_HEIGHT (HFI_COMMON_BASE + 0x2)
#define HFI_CAPABILITY_MBS_PER_FRAME (HFI_COMMON_BASE + 0x3)
@@ -882,6 +890,14 @@ struct hfi_aspect_ratio {
u32 aspect_height;
};
+#define HFI_IFRAME_SIZE_DEFAULT (HFI_COMMON_BASE + 0x1)
+#define HFI_IFRAME_SIZE_MEDIUM (HFI_COMMON_BASE + 0x2)
+#define HFI_IFRAME_SIZE_HIGH (HFI_COMMON_BASE + 0x3)
+#define HFI_IFRAME_SIZE_UNLIMITED (HFI_COMMON_BASE + 0x4)
+struct hfi_iframe_size {
+ u32 type;
+};
+
#define HFI_MVC_BUFFER_LAYOUT_TOP_BOTTOM (0)
#define HFI_MVC_BUFFER_LAYOUT_SIDEBYSIDE (1)
#define HFI_MVC_BUFFER_LAYOUT_SEQ (2)
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 2764f43607c1..0e7d16fe84d4 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1388,47 +1388,44 @@ static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp,
static long uvc_v4l2_compat_ioctl32(struct file *file,
unsigned int cmd, unsigned long arg)
{
+ struct uvc_fh *handle = file->private_data;
union {
struct uvc_xu_control_mapping xmap;
struct uvc_xu_control_query xqry;
} karg;
void __user *up = compat_ptr(arg);
- mm_segment_t old_fs;
long ret;
switch (cmd) {
case UVCIOC_CTRL_MAP32:
- cmd = UVCIOC_CTRL_MAP;
ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
+ if (ret)
+ return ret;
+ ret = uvc_ioctl_ctrl_map(handle->chain, &karg.xmap);
+ if (ret)
+ return ret;
+ ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up);
+ if (ret)
+ return ret;
+
break;
case UVCIOC_CTRL_QUERY32:
- cmd = UVCIOC_CTRL_QUERY;
ret = uvc_v4l2_get_xu_query(&karg.xqry, up);
+ if (ret)
+ return ret;
+ ret = uvc_xu_ctrl_query(handle->chain, &karg.xqry);
+ if (ret)
+ return ret;
+ ret = uvc_v4l2_put_xu_query(&karg.xqry, up);
+ if (ret)
+ return ret;
break;
default:
return -ENOIOCTLCMD;
}
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- ret = video_ioctl2(file, cmd, (unsigned long)&karg);
- set_fs(old_fs);
-
- if (ret < 0)
- return ret;
-
- switch (cmd) {
- case UVCIOC_CTRL_MAP:
- ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up);
- break;
-
- case UVCIOC_CTRL_QUERY:
- ret = uvc_v4l2_put_xu_query(&karg.xqry, up);
- break;
- }
-
return ret;
}
#endif
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 2da7fd7deacd..2f1c03783414 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -280,7 +280,8 @@ static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user
static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
{
if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) ||
- copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format)))
+ copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format)) ||
+ copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
return -EFAULT;
return __put_v4l2_format32(&kp->format, &up->format);
}
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index 33bdd81065e8..11f39791ec33 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -1502,7 +1502,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
* Will sleep if required for nonblocking == false.
*/
static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
- int nonblocking)
+ void *pb, int nonblocking)
{
unsigned long flags;
int ret;
@@ -1523,10 +1523,10 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
/*
* Only remove the buffer from done_list if v4l2_buffer can handle all
* the planes.
- * Verifying planes is NOT necessary since it already has been checked
- * before the buffer is queued/prepared. So it can never fail.
*/
- list_del(&(*vb)->done_entry);
+ ret = call_bufop(q, verify_planes_array, *vb, pb);
+ if (!ret)
+ list_del(&(*vb)->done_entry);
spin_unlock_irqrestore(&q->done_lock, flags);
return ret;
@@ -1604,7 +1604,7 @@ int vb2_core_dqbuf(struct vb2_queue *q, void *pb, bool nonblocking)
struct vb2_buffer *vb = NULL;
int ret;
- ret = __vb2_get_done_vb(q, &vb, nonblocking);
+ ret = __vb2_get_done_vb(q, &vb, pb, nonblocking);
if (ret < 0)
return ret;
diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c
index dbec5923fcf0..3c3b517f1d1c 100644
--- a/drivers/media/v4l2-core/videobuf2-memops.c
+++ b/drivers/media/v4l2-core/videobuf2-memops.c
@@ -49,7 +49,7 @@ struct frame_vector *vb2_create_framevec(unsigned long start,
vec = frame_vector_create(nr);
if (!vec)
return ERR_PTR(-ENOMEM);
- ret = get_vaddr_frames(start, nr, write, 1, vec);
+ ret = get_vaddr_frames(start & PAGE_MASK, nr, write, true, vec);
if (ret < 0)
goto out_destroy;
/* We accept only complete set of PFNs */
diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c
index 6515dfc2b805..55cba89dbdb8 100644
--- a/drivers/memory/omap-gpmc.c
+++ b/drivers/memory/omap-gpmc.c
@@ -394,7 +394,7 @@ static void gpmc_cs_bool_timings(int cs, const struct gpmc_bool_timings *p)
gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG4,
GPMC_CONFIG4_OEEXTRADELAY, p->oe_extra_delay);
gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG4,
- GPMC_CONFIG4_OEEXTRADELAY, p->we_extra_delay);
+ GPMC_CONFIG4_WEEXTRADELAY, p->we_extra_delay);
gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG6,
GPMC_CONFIG6_CYCLE2CYCLESAMECSEN,
p->cycle2cyclesamecsen);
diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c
index 6255513f54c7..88e80ec772f6 100644
--- a/drivers/mfd/intel-lpss.c
+++ b/drivers/mfd/intel-lpss.c
@@ -33,6 +33,7 @@
#define LPSS_DEV_SIZE 0x200
#define LPSS_PRIV_OFFSET 0x200
#define LPSS_PRIV_SIZE 0x100
+#define LPSS_PRIV_REG_COUNT (LPSS_PRIV_SIZE / 4)
#define LPSS_IDMA64_OFFSET 0x800
#define LPSS_IDMA64_SIZE 0x800
@@ -75,6 +76,7 @@ struct intel_lpss {
const struct mfd_cell *cell;
struct device *dev;
void __iomem *priv;
+ u32 priv_ctx[LPSS_PRIV_REG_COUNT];
int devid;
u32 caps;
u32 active_ltr;
@@ -445,6 +447,7 @@ int intel_lpss_probe(struct device *dev,
err_remove_ltr:
intel_lpss_debugfs_remove(lpss);
intel_lpss_ltr_hide(lpss);
+ intel_lpss_unregister_clock(lpss);
err_clk_register:
ida_simple_remove(&intel_lpss_devid_ida, lpss->devid);
@@ -484,6 +487,16 @@ EXPORT_SYMBOL_GPL(intel_lpss_prepare);
int intel_lpss_suspend(struct device *dev)
{
+ struct intel_lpss *lpss = dev_get_drvdata(dev);
+ unsigned int i;
+
+ /* Save device context */
+ for (i = 0; i < LPSS_PRIV_REG_COUNT; i++)
+ lpss->priv_ctx[i] = readl(lpss->priv + i * 4);
+
+ /* Put the device into reset state */
+ writel(0, lpss->priv + LPSS_PRIV_RESETS);
+
return 0;
}
EXPORT_SYMBOL_GPL(intel_lpss_suspend);
@@ -491,8 +504,13 @@ EXPORT_SYMBOL_GPL(intel_lpss_suspend);
int intel_lpss_resume(struct device *dev)
{
struct intel_lpss *lpss = dev_get_drvdata(dev);
+ unsigned int i;
- intel_lpss_init_dev(lpss);
+ intel_lpss_deassert_reset(lpss);
+
+ /* Restore device context */
+ for (i = 0; i < LPSS_PRIV_REG_COUNT; i++)
+ writel(lpss->priv_ctx[i], lpss->priv + i * 4);
return 0;
}
diff --git a/drivers/mfd/intel_soc_pmic_core.c b/drivers/mfd/intel_soc_pmic_core.c
index d9e15cf7c6c8..12d6ebb4ae5d 100644
--- a/drivers/mfd/intel_soc_pmic_core.c
+++ b/drivers/mfd/intel_soc_pmic_core.c
@@ -35,6 +35,7 @@ static struct gpiod_lookup_table panel_gpio_table = {
.table = {
/* Panel EN/DISABLE */
GPIO_LOOKUP("gpio_crystalcove", 94, "panel", GPIO_ACTIVE_HIGH),
+ { },
},
};
diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index b7b3e8ee64f2..c30290f33430 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -269,6 +269,8 @@ static int usbtll_omap_probe(struct platform_device *pdev)
if (IS_ERR(tll->ch_clk[i]))
dev_dbg(dev, "can't get clock : %s\n", clkname);
+ else
+ clk_prepare(tll->ch_clk[i]);
}
pm_runtime_put_sync(dev);
@@ -301,9 +303,12 @@ static int usbtll_omap_remove(struct platform_device *pdev)
tll_dev = NULL;
spin_unlock(&tll_lock);
- for (i = 0; i < tll->nch; i++)
- if (!IS_ERR(tll->ch_clk[i]))
+ for (i = 0; i < tll->nch; i++) {
+ if (!IS_ERR(tll->ch_clk[i])) {
+ clk_unprepare(tll->ch_clk[i]);
clk_put(tll->ch_clk[i]);
+ }
+ }
pm_runtime_disable(&pdev->dev);
return 0;
@@ -420,7 +425,7 @@ int omap_tll_enable(struct usbhs_omap_platform_data *pdata)
if (IS_ERR(tll->ch_clk[i]))
continue;
- r = clk_prepare_enable(tll->ch_clk[i]);
+ r = clk_enable(tll->ch_clk[i]);
if (r) {
dev_err(tll_dev,
"Error enabling ch %d clock: %d\n", i, r);
@@ -448,7 +453,7 @@ int omap_tll_disable(struct usbhs_omap_platform_data *pdata)
for (i = 0; i < tll->nch; i++) {
if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {
if (!IS_ERR(tll->ch_clk[i]))
- clk_disable_unprepare(tll->ch_clk[i]);
+ clk_disable(tll->ch_clk[i]);
}
}
diff --git a/drivers/mfd/wcd934x-regmap.c b/drivers/mfd/wcd934x-regmap.c
index 3ed3d125f430..fbaf05e58aff 100644
--- a/drivers/mfd/wcd934x-regmap.c
+++ b/drivers/mfd/wcd934x-regmap.c
@@ -1922,6 +1922,10 @@ static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg)
case WCD934X_SIDO_NEW_VOUT_A_STARTUP:
case WCD934X_SIDO_NEW_VOUT_D_STARTUP:
case WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL:
+ case WCD934X_ANA_MBHC_MECH:
+ case WCD934X_ANA_MBHC_ELECT:
+ case WCD934X_ANA_MBHC_ZDET:
+ case WCD934X_ANA_MICB2:
return true;
}
diff --git a/drivers/mfd/wcd9xxx-utils.c b/drivers/mfd/wcd9xxx-utils.c
index fab594992df3..2b0a5f8ce7f2 100644
--- a/drivers/mfd/wcd9xxx-utils.c
+++ b/drivers/mfd/wcd9xxx-utils.c
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/regmap.h>
#include <linux/delay.h>
+#include <linux/sched.h>
#include <linux/mfd/core.h>
#include <linux/mfd/wcd9xxx/pdata.h>
#include <linux/mfd/wcd9xxx/core.h>
@@ -310,6 +311,7 @@ struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev)
u32 ecpp_dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED;
u32 dmic_clk_drive = WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED;
u32 prop_val;
+ int rc = 0;
if (!dev || !dev->of_node)
return NULL;
@@ -368,9 +370,13 @@ struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev)
pdata->mclk_rate,
"mad_dmic_rate");
- if (!(wcd9xxx_read_of_property_u32(dev, "qcom,cdc-ecpp-dmic-rate",
- &prop_val)))
- ecpp_dmic_sample_rate = prop_val;
+ if (of_find_property(dev->of_node, "qcom,cdc-ecpp-dmic-rate", NULL)) {
+ rc = wcd9xxx_read_of_property_u32(dev,
+ "qcom,cdc-ecpp-dmic-rate",
+ &prop_val);
+ if (!rc)
+ ecpp_dmic_sample_rate = prop_val;
+ }
pdata->ecpp_dmic_sample_rate = wcd9xxx_validate_dmic_sample_rate(dev,
ecpp_dmic_sample_rate,
@@ -379,13 +385,14 @@ struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev)
if (!(of_property_read_u32(dev->of_node,
"qcom,cdc-dmic-clk-drv-strength",
- &prop_val)))
+ &prop_val))) {
dmic_clk_drive = prop_val;
- if (dmic_clk_drive != 2 && dmic_clk_drive != 4 &&
- dmic_clk_drive != 8 && dmic_clk_drive != 16)
- dev_err(dev, "Invalid cdc-dmic-clk-drv-strength %d\n",
- dmic_clk_drive);
+ if (dmic_clk_drive != 2 && dmic_clk_drive != 4 &&
+ dmic_clk_drive != 8 && dmic_clk_drive != 16)
+ dev_err(dev, "Invalid cdc-dmic-clk-drv-strength %d\n",
+ dmic_clk_drive);
+ }
pdata->dmic_clk_drv = dmic_clk_drive;
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 8a08ca61062a..9f0d9b7b7e17 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -439,7 +439,7 @@ config ARM_CHARLCD
still useful.
config BMP085
- bool
+ tristate
depends on SYSFS
config BMP085_I2C
diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c
index 15e88078ba1e..f1a0b99f5a9a 100644
--- a/drivers/misc/ad525x_dpot.c
+++ b/drivers/misc/ad525x_dpot.c
@@ -216,7 +216,7 @@ static s32 dpot_read_i2c(struct dpot_data *dpot, u8 reg)
*/
value = swab16(value);
- if (dpot->uid == DPOT_UID(AD5271_ID))
+ if (dpot->uid == DPOT_UID(AD5274_ID))
value = value >> 2;
return value;
default:
diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c
index 09a406058c46..efbb6945eb18 100644
--- a/drivers/misc/cxl/irq.c
+++ b/drivers/misc/cxl/irq.c
@@ -288,7 +288,6 @@ unsigned int cxl_map_irq(struct cxl *adapter, irq_hw_number_t hwirq,
void cxl_unmap_irq(unsigned int virq, void *cookie)
{
free_irq(virq, cookie);
- irq_dispose_mapping(virq);
}
static int cxl_register_one_irq(struct cxl *adapter,
diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c
index 76add503b6b8..56ddf8467d16 100644
--- a/drivers/misc/hdcp.c
+++ b/drivers/misc/hdcp.c
@@ -160,43 +160,46 @@
static const struct hdcp_msg_data hdcp_msg_lookup[HDCP2P2_MAX_MESSAGES] = {
[AKE_INIT_MESSAGE_ID] = { 2,
- { {0x69000, 8}, {0x69008, 3} },
+ { {"rtx", 0x69000, 8}, {"TxCaps", 0x69008, 3} },
0 },
[AKE_SEND_CERT_MESSAGE_ID] = { 3,
- { {0x6900B, 522}, {0x69215, 8}, {0x6921D, 3} },
+ { {"cert-rx", 0x6900B, 522}, {"rrx", 0x69215, 8},
+ {"RxCaps", 0x6921D, 3} },
0 },
[AKE_NO_STORED_KM_MESSAGE_ID] = { 1,
- { {0x69220, 128} },
+ { {"Ekpub_km", 0x69220, 128} },
0 },
[AKE_STORED_KM_MESSAGE_ID] = { 2,
- { {0x692A0, 16}, {0x692B0, 16} },
+ { {"Ekh_km", 0x692A0, 16}, {"m", 0x692B0, 16} },
0 },
[AKE_SEND_H_PRIME_MESSAGE_ID] = { 1,
- { {0x692C0, 32} },
+ { {"H'", 0x692C0, 32} },
(1 << 1) },
[AKE_SEND_PAIRING_INFO_MESSAGE_ID] = { 1,
- { {0x692E0, 16} },
+ { {"Ekh_km", 0x692E0, 16} },
(1 << 2) },
[LC_INIT_MESSAGE_ID] = { 1,
- { {0x692F0, 8} },
+ { {"rn", 0x692F0, 8} },
0 },
[LC_SEND_L_PRIME_MESSAGE_ID] = { 1,
- { {0x692F8, 32} },
+ { {"L'", 0x692F8, 32} },
0 },
[SKE_SEND_EKS_MESSAGE_ID] = { 2,
- { {0x69318, 16}, {0x69328, 8} },
+ { {"Edkey_ks", 0x69318, 16}, {"riv", 0x69328, 8} },
0 },
[REPEATER_AUTH_SEND_RECEIVERID_LIST_MESSAGE_ID] = { 4,
- { {0x69330, 2}, {0x69332, 3}, {0x69335, 16}, {0x69345, 155} },
+ { {"RxInfo", 0x69330, 2}, {"seq_num_V", 0x69332, 3},
+ {"V'", 0x69335, 16}, {"ridlist", 0x69345, 155} },
(1 << 0) },
[REPEATER_AUTH_SEND_ACK_MESSAGE_ID] = { 1,
- { {0x693E0, 16} },
+ { {"V", 0x693E0, 16} },
0 },
[REPEATER_AUTH_STREAM_MANAGE_MESSAGE_ID] = { 3,
- { {0x693F0, 3}, {0x693F3, 2}, {0x693F5, 126} },
+ { {"seq_num_M", 0x693F0, 3}, {"k", 0x693F3, 2},
+ {"streamID_Type", 0x693F5, 126} },
0 },
[REPEATER_AUTH_STREAM_READY_MESSAGE_ID] = { 1,
- { {0x69473, 32} },
+ { {"M'", 0x69473, 32} },
0 }
};
@@ -252,6 +255,15 @@ struct __attribute__ ((__packed__)) hdcp_version_rsp {
uint32_t appversion;
};
+struct __attribute__ ((__packed__)) hdcp_verify_key_req {
+ uint32_t commandid;
+};
+
+struct __attribute__ ((__packed__)) hdcp_verify_key_rsp {
+ uint32_t status;
+ uint32_t commandId;
+};
+
struct __attribute__ ((__packed__)) hdcp_lib_init_req_v1 {
uint32_t commandid;
};
@@ -616,36 +628,59 @@ static int hdcp_lib_get_next_message(struct hdcp_lib_handle *handle,
}
}
-static inline void hdcp_lib_wakeup_client(struct hdcp_lib_handle *handle,
- struct hdmi_hdcp_wakeup_data *data)
+static void hdcp_lib_wakeup_client(struct hdcp_lib_handle *handle,
+ struct hdmi_hdcp_wakeup_data *data)
{
- int rc = 0;
+ int rc = 0, i;
+
+ if (!handle || !handle->client_ops || !handle->client_ops->wakeup ||
+ !data || (data->cmd == HDMI_HDCP_WKUP_CMD_INVALID))
+ return;
- if (handle && handle->client_ops && handle->client_ops->wakeup &&
- data && (data->cmd != HDMI_HDCP_WKUP_CMD_INVALID)) {
- data->abort_mask = REAUTH_REQ | LINK_INTEGRITY_FAILURE;
+ data->abort_mask = REAUTH_REQ | LINK_INTEGRITY_FAILURE;
- if (data->cmd == HDMI_HDCP_WKUP_CMD_SEND_MESSAGE ||
- data->cmd == HDMI_HDCP_WKUP_CMD_RECV_MESSAGE ||
- data->cmd == HDMI_HDCP_WKUP_CMD_LINK_POLL) {
- handle->last_msg =
- hdcp_lib_get_next_message(handle, data);
+ if (data->cmd == HDMI_HDCP_WKUP_CMD_SEND_MESSAGE ||
+ data->cmd == HDMI_HDCP_WKUP_CMD_RECV_MESSAGE ||
+ data->cmd == HDMI_HDCP_WKUP_CMD_LINK_POLL) {
+ handle->last_msg = hdcp_lib_get_next_message(handle, data);
- if (handle->last_msg > INVALID_MESSAGE_ID &&
- handle->last_msg < HDCP2P2_MAX_MESSAGES)
- data->message_data =
- &hdcp_msg_lookup[handle->last_msg];
- }
+ pr_debug("lib->client: %s (%s)\n",
+ hdmi_hdcp_cmd_to_str(data->cmd),
+ hdcp_lib_message_name(handle->last_msg));
- rc = handle->client_ops->wakeup(data);
- if (rc)
- pr_err("error sending %s to client\n",
- hdmi_hdcp_cmd_to_str(data->cmd));
+ if (handle->last_msg > INVALID_MESSAGE_ID &&
+ handle->last_msg < HDCP2P2_MAX_MESSAGES) {
+ u32 msg_num, rx_status;
+ const struct hdcp_msg_part *msg;
+
+ data->message_data = &hdcp_msg_lookup[handle->last_msg];
+
+ msg_num = data->message_data->num_messages;
+ msg = data->message_data->messages;
+ rx_status = data->message_data->rx_status;
+
+ pr_debug("rxstatus 0x%x\n", rx_status);
+ pr_debug("%10s | %6s | %4s\n", "name", "offset", "len");
+
+ for (i = 0; i < msg_num; i++)
+ pr_debug("%10s | %6x | %4d\n",
+ msg[i].name, msg[i].offset,
+ msg[i].length);
+ }
+ } else {
+ pr_debug("lib->client: %s\n",
+ hdmi_hdcp_cmd_to_str(data->cmd));
}
+
+ rc = handle->client_ops->wakeup(data);
+ if (rc)
+ pr_err("error sending %s to client\n",
+ hdmi_hdcp_cmd_to_str(data->cmd));
}
static inline void hdcp_lib_send_message(struct hdcp_lib_handle *handle)
{
+ char msg_name[50];
struct hdmi_hdcp_wakeup_data cdata = {
HDMI_HDCP_WKUP_CMD_SEND_MESSAGE
};
@@ -655,6 +690,13 @@ static inline void hdcp_lib_send_message(struct hdcp_lib_handle *handle)
cdata.send_msg_len = handle->msglen;
cdata.timeout = handle->hdcp_timeout;
+ snprintf(msg_name, sizeof(msg_name), "%s: ",
+ hdcp_lib_message_name((int)cdata.send_msg_buf[0]));
+
+ print_hex_dump(KERN_DEBUG, msg_name,
+ DUMP_PREFIX_NONE, 16, 1, cdata.send_msg_buf,
+ cdata.send_msg_len, false);
+
hdcp_lib_wakeup_client(handle, &cdata);
}
@@ -761,6 +803,48 @@ exit:
return rc;
}
+static int hdcp_lib_verify_keys(struct hdcp_lib_handle *handle)
+{
+ int rc = -EINVAL;
+ struct hdcp_verify_key_req *req_buf;
+ struct hdcp_verify_key_rsp *rsp_buf;
+
+ if (!handle) {
+ pr_err("invalid input\n");
+ goto exit;
+ }
+
+ if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) {
+ pr_err("app not loaded\n");
+ goto exit;
+ }
+
+ req_buf = (struct hdcp_verify_key_req *)handle->qseecom_handle->sbuf;
+ req_buf->commandid = HDCP_TXMTR_VERIFY_KEY;
+
+ rsp_buf = (struct hdcp_verify_key_rsp *)
+ (handle->qseecom_handle->sbuf +
+ QSEECOM_ALIGN(sizeof(struct hdcp_verify_key_req)));
+
+ rc = qseecom_send_command(handle->qseecom_handle,
+ req_buf,
+ QSEECOM_ALIGN(sizeof
+ (struct hdcp_verify_key_req)),
+ rsp_buf,
+ QSEECOM_ALIGN(sizeof
+ (struct hdcp_verify_key_rsp)));
+
+ if (rc < 0) {
+ pr_err("qseecom cmd failed err = %d\n", rc);
+ goto exit;
+ }
+
+ return rsp_buf->status;
+exit:
+ return rc;
+}
+
+
static int hdcp_app_init_legacy(struct hdcp_lib_handle *handle)
{
int rc = 0;
@@ -1423,10 +1507,12 @@ static bool hdcp_lib_client_feature_supported(void *phdcpcontext)
rc = hdcp_lib_library_load(handle);
if (!rc) {
- pr_debug("HDCP2p2 supported\n");
- handle->feature_supported = true;
+ if (!hdcp_lib_verify_keys(handle)) {
+ pr_debug("HDCP2p2 supported\n");
+ handle->feature_supported = true;
+ supported = true;
+ }
hdcp_lib_library_unload(handle);
- supported = true;
}
exit:
return supported;
@@ -1486,6 +1572,7 @@ static int hdcp_lib_check_valid_state(struct hdcp_lib_handle *handle)
if (handle->wakeup_cmd == HDCP_LIB_WKUP_CMD_START) {
if (!list_empty(&handle->worker.work_list)) {
+ pr_debug("error: queue not empty\n");
rc = -EBUSY;
goto exit;
}
@@ -1543,9 +1630,8 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data)
handle->wakeup_cmd = data->cmd;
handle->timeout_left = data->timeout;
- pr_debug("%s, timeout left: %dms, tethered %d\n",
- hdcp_lib_cmd_to_str(handle->wakeup_cmd),
- handle->timeout_left, handle->tethered);
+ pr_debug("client->lib: %s\n",
+ hdcp_lib_cmd_to_str(handle->wakeup_cmd));
rc = hdcp_lib_check_valid_state(handle);
if (rc)
@@ -1599,6 +1685,8 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data)
break;
case HDCP_LIB_WKUP_CMD_MSG_SEND_FAILED:
case HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED:
+ case HDCP_LIB_WKUP_CMD_LINK_FAILED:
+ handle->hdcp_state |= HDCP_STATE_ERROR;
HDCP_LIB_EXECUTE(clean);
break;
case HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS:
@@ -1825,7 +1913,7 @@ static void hdcp_lib_clean(struct hdcp_lib_handle *handle)
if (!handle) {
pr_err("invalid input\n");
return;
- };
+ }
hdcp_lib_txmtr_deinit(handle);
if (!handle->legacy_app)
@@ -1859,6 +1947,7 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle)
struct hdcp_rcvd_msg_rsp *rsp_buf;
uint32_t msglen;
char *msg = NULL;
+ char msg_name[50];
uint32_t message_id_bytes = 0;
if (!handle || !handle->qseecom_handle ||
@@ -1907,8 +1996,11 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle)
mutex_unlock(&handle->msg_lock);
- pr_debug("msg received: %s from sink\n",
- hdcp_lib_message_name((int)msg[0]));
+ snprintf(msg_name, sizeof(msg_name), "%s: ",
+ hdcp_lib_message_name((int)msg[0]));
+
+ print_hex_dump(KERN_DEBUG, msg_name,
+ DUMP_PREFIX_NONE, 16, 1, msg, msglen, false);
/* send the message to QSEECOM */
req_buf = (struct hdcp_rcvd_msg_req *)(handle->qseecom_handle->sbuf);
@@ -1989,13 +2081,8 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle)
handle->hdcp_timeout = rsp_buf->timeout;
handle->msglen = rsp_buf->msglen;
- if (!atomic_read(&handle->hdcp_off)) {
- cdata.cmd = HDMI_HDCP_WKUP_CMD_SEND_MESSAGE;
- cdata.send_msg_buf = handle->listener_buf;
- cdata.send_msg_len = handle->msglen;
- cdata.timeout = handle->hdcp_timeout;
- }
-
+ if (!atomic_read(&handle->hdcp_off))
+ hdcp_lib_send_message(handle);
exit:
kzfree(msg);
@@ -2026,6 +2113,16 @@ static void hdcp_lib_topology_work(struct kthread_work *work)
return;
}
+ if (atomic_read(&handle->hdcp_off)) {
+ pr_debug("invalid state: hdcp off\n");
+ return;
+ }
+
+ if (handle->hdcp_state & HDCP_STATE_ERROR) {
+ pr_debug("invalid state: hdcp error\n");
+ return;
+ }
+
reinit_completion(&handle->topo_wait);
timeout = wait_for_completion_timeout(&handle->topo_wait, HZ * 3);
if (!timeout) {
diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c
index cd0403f09267..e79c0371ee6f 100644
--- a/drivers/misc/mei/amthif.c
+++ b/drivers/misc/mei/amthif.c
@@ -417,8 +417,10 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl,
dev = cl->dev;
- if (dev->iamthif_state != MEI_IAMTHIF_READING)
+ if (dev->iamthif_state != MEI_IAMTHIF_READING) {
+ mei_irq_discard_msg(dev, mei_hdr);
return 0;
+ }
ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list);
if (ret)
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index 1a173d0af694..a77643954523 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -222,17 +222,23 @@ EXPORT_SYMBOL_GPL(mei_cldev_recv);
static void mei_cl_bus_event_work(struct work_struct *work)
{
struct mei_cl_device *cldev;
+ struct mei_device *bus;
cldev = container_of(work, struct mei_cl_device, event_work);
+ bus = cldev->bus;
+
if (cldev->event_cb)
cldev->event_cb(cldev, cldev->events, cldev->event_context);
cldev->events = 0;
/* Prepare for the next read */
- if (cldev->events_mask & BIT(MEI_CL_EVENT_RX))
+ if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) {
+ mutex_lock(&bus->device_lock);
mei_cl_read_start(cldev->cl, 0, NULL);
+ mutex_unlock(&bus->device_lock);
+ }
}
/**
@@ -296,6 +302,7 @@ int mei_cldev_register_event_cb(struct mei_cl_device *cldev,
unsigned long events_mask,
mei_cldev_event_cb_t event_cb, void *context)
{
+ struct mei_device *bus = cldev->bus;
int ret;
if (cldev->event_cb)
@@ -308,15 +315,17 @@ int mei_cldev_register_event_cb(struct mei_cl_device *cldev,
INIT_WORK(&cldev->event_work, mei_cl_bus_event_work);
if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) {
+ mutex_lock(&bus->device_lock);
ret = mei_cl_read_start(cldev->cl, 0, NULL);
+ mutex_unlock(&bus->device_lock);
if (ret && ret != -EBUSY)
return ret;
}
if (cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF)) {
- mutex_lock(&cldev->cl->dev->device_lock);
+ mutex_lock(&bus->device_lock);
ret = mei_cl_notify_request(cldev->cl, NULL, event_cb ? 1 : 0);
- mutex_unlock(&cldev->cl->dev->device_lock);
+ mutex_unlock(&bus->device_lock);
if (ret)
return ret;
}
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index a6c87c713193..958af84884b5 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -1735,6 +1735,10 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
wake_up(&cl->wait);
break;
+ case MEI_FOP_DISCONNECT_RSP:
+ mei_io_cb_free(cb);
+ mei_cl_set_disconnected(cl);
+ break;
default:
BUG_ON(0);
}
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index e7b7aad0999b..fd8a9f057ea6 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -873,8 +873,7 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT_RSP, NULL);
if (!cb)
return -ENOMEM;
- cl_dbg(dev, cl, "add disconnect response as first\n");
- list_add(&cb->list, &dev->ctrl_wr_list.list);
+ list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
}
return 0;
}
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index 64b568a0268d..d1df797c7568 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -76,7 +76,6 @@ static inline int mei_cl_hbm_equal(struct mei_cl *cl,
* @dev: mei device
* @hdr: message header
*/
-static inline
void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr)
{
/*
@@ -184,10 +183,7 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb,
return -EMSGSIZE;
ret = mei_hbm_cl_disconnect_rsp(dev, cl);
- mei_cl_set_disconnected(cl);
- mei_io_cb_free(cb);
- mei_me_cl_put(cl->me_cl);
- cl->me_cl = NULL;
+ list_move_tail(&cb->list, &cmpl_list->list);
return ret;
}
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index 4250555d5e72..1b06e2fd6858 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -782,6 +782,8 @@ bool mei_hbuf_acquire(struct mei_device *dev);
bool mei_write_is_idle(struct mei_device *dev);
+void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr);
+
#if IS_ENABLED(CONFIG_DEBUG_FS)
int mei_dbgfs_register(struct mei_device *dev, const char *name);
void mei_dbgfs_deregister(struct mei_device *dev);
diff --git a/drivers/misc/mic/scif/scif_rma.c b/drivers/misc/mic/scif/scif_rma.c
index 8310b4dbff06..6a451bd65bf3 100644
--- a/drivers/misc/mic/scif/scif_rma.c
+++ b/drivers/misc/mic/scif/scif_rma.c
@@ -1511,7 +1511,7 @@ off_t scif_register_pinned_pages(scif_epd_t epd,
if ((map_flags & SCIF_MAP_FIXED) &&
((ALIGN(offset, PAGE_SIZE) != offset) ||
(offset < 0) ||
- (offset + (off_t)len < offset)))
+ (len > LONG_MAX - offset)))
return -EINVAL;
might_sleep();
@@ -1614,7 +1614,7 @@ off_t scif_register(scif_epd_t epd, void *addr, size_t len, off_t offset,
if ((map_flags & SCIF_MAP_FIXED) &&
((ALIGN(offset, PAGE_SIZE) != offset) ||
(offset < 0) ||
- (offset + (off_t)len < offset)))
+ (len > LONG_MAX - offset)))
return -EINVAL;
/* Unsupported protection requested */
@@ -1732,7 +1732,8 @@ scif_unregister(scif_epd_t epd, off_t offset, size_t len)
/* Offset is not page aligned or offset+len wraps around */
if ((ALIGN(offset, PAGE_SIZE) != offset) ||
- (offset + (off_t)len < offset))
+ (offset < 0) ||
+ (len > LONG_MAX - offset))
return -EINVAL;
err = scif_verify_epd(ep);
diff --git a/drivers/misc/qcom/qdsp6v2/amrwb_in.c b/drivers/misc/qcom/qdsp6v2/amrwb_in.c
index 4f94ed2673e6..5e9dbca420a7 100644
--- a/drivers/misc/qcom/qdsp6v2/amrwb_in.c
+++ b/drivers/misc/qcom/qdsp6v2/amrwb_in.c
@@ -310,7 +310,7 @@ static int amrwb_in_open(struct inode *inode, struct file *file)
(void *)audio);
if (!audio->ac) {
- pr_err("%s:audio[%p]: Could not allocate memory for audio"
+ pr_err("%s:audio[%pK]: Could not allocate memory for audio"
"client\n", __func__, audio);
kfree(audio->enc_cfg);
kfree(audio);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_aac.c b/drivers/misc/qcom/qdsp6v2/audio_aac.c
index e49d91d74514..94d563a211ec 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_aac.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_aac.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2016, 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
@@ -221,10 +221,10 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
default: {
- pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
if (rc)
- pr_err("%s[%p]:Failed in utils_ioctl: %d\n",
+ pr_err("%s[%pK]:Failed in utils_ioctl: %d\n",
__func__, audio, rc);
}
}
@@ -328,10 +328,10 @@ static long audio_compat_ioctl(struct file *file, unsigned int cmd,
break;
}
default: {
- pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_compat_ioctl(file, cmd, arg);
if (rc)
- pr_err("%s[%p]:Failed in utils_ioctl: %d\n",
+ pr_err("%s[%pK]:Failed in utils_ioctl: %d\n",
__func__, audio, rc);
}
}
diff --git a/drivers/misc/qcom/qdsp6v2/audio_alac.c b/drivers/misc/qcom/qdsp6v2/audio_alac.c
index 3de204c1ebc8..f25c8ae47b4c 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_alac.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_alac.c
@@ -52,7 +52,7 @@ static long audio_ioctl_shared(struct file *file, unsigned int cmd,
__func__, audio->pcm_cfg.channel_count);
}
- pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrnb.c b/drivers/misc/qcom/qdsp6v2/audio_amrnb.c
index 1625adb82be9..78bcdb74af0e 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_amrnb.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_amrnb.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016, 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
@@ -33,7 +33,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
switch (cmd) {
case AUDIO_START: {
- pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
@@ -62,7 +62,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
default:
- pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
}
return rc;
diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrwb.c b/drivers/misc/qcom/qdsp6v2/audio_amrwb.c
index c7ff607414a5..2283cf26bda9 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_amrwb.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_amrwb.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016, 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
@@ -34,7 +34,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
switch (cmd) {
case AUDIO_START: {
- pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
@@ -65,7 +65,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
default:
- pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
}
return rc;
diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c b/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c
index bfd730017d41..727a5369c2a9 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c
@@ -55,7 +55,7 @@ static long audio_ioctl_shared(struct file *file, unsigned int cmd,
switch (cmd) {
case AUDIO_START: {
- pr_err("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ pr_err("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
@@ -162,7 +162,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd,
break;
}
default: {
- pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
break;
}
@@ -278,7 +278,7 @@ static long audio_compat_ioctl(struct file *file, unsigned int cmd,
break;
}
default: {
- pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_compat_ioctl(file, cmd, arg);
break;
}
diff --git a/drivers/misc/qcom/qdsp6v2/audio_ape.c b/drivers/misc/qcom/qdsp6v2/audio_ape.c
index 670ec555b8c6..d7d550c40dff 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_ape.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_ape.c
@@ -39,7 +39,7 @@ static long audio_ioctl_shared(struct file *file, unsigned int cmd,
case AUDIO_START: {
struct asm_ape_cfg ape_cfg;
struct msm_audio_ape_config *ape_config;
- pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
@@ -133,7 +133,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
default: {
- pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
if (rc)
pr_err("Failed in utils_ioctl: %d\n", rc);
@@ -231,7 +231,7 @@ static long audio_compat_ioctl(struct file *file, unsigned int cmd,
break;
}
default: {
- pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_compat_ioctl(file, cmd, arg);
if (rc)
pr_err("Failed in utils_ioctl: %d\n", rc);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_evrc.c b/drivers/misc/qcom/qdsp6v2/audio_evrc.c
index 08ca94e62059..5a89f4e25a27 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_evrc.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_evrc.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016, 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
@@ -34,7 +34,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
switch (cmd) {
case AUDIO_START: {
- pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
@@ -65,7 +65,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
default:
- pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
}
return rc;
diff --git a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c
index 3632fc2b961b..940fd08654d2 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c
@@ -99,7 +99,7 @@ static void audio_effects_event_handler(uint32_t opcode, uint32_t token,
struct q6audio_effects *effects;
if (!payload || !priv) {
- pr_err("%s: invalid data to handle events, payload: %p, priv: %p\n",
+ pr_err("%s: invalid data to handle events, payload: %pK, priv: %pK\n",
__func__, payload, priv);
return;
}
@@ -705,7 +705,7 @@ static int audio_effects_release(struct inode *inode, struct file *file)
__func__);
rc = q6asm_cmd(effects->ac, CMD_CLOSE);
if (rc < 0)
- pr_err("%s[%p]:Failed to close the session rc=%d\n",
+ pr_err("%s[%pK]:Failed to close the session rc=%d\n",
__func__, effects, rc);
effects->opened = 0;
effects->started = 0;
diff --git a/drivers/misc/qcom/qdsp6v2/audio_mp3.c b/drivers/misc/qcom/qdsp6v2/audio_mp3.c
index 83e300721c8e..fa5132e83ff4 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_mp3.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_mp3.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016, 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
@@ -33,7 +33,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
int rc = 0;
switch (cmd) {
case AUDIO_START: {
- pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
@@ -69,7 +69,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
default:
- pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
}
return rc;
diff --git a/drivers/misc/qcom/qdsp6v2/audio_qcelp.c b/drivers/misc/qcom/qdsp6v2/audio_qcelp.c
index 653aee9c8eff..508a95b7bf79 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_qcelp.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_qcelp.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016, 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
@@ -36,7 +36,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
switch (cmd) {
case AUDIO_START: {
- pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
@@ -67,7 +67,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
default:
- pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
}
return rc;
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.c b/drivers/misc/qcom/qdsp6v2/audio_utils.c
index 840597314a5f..15d82d126df7 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_utils.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils.c
@@ -757,7 +757,7 @@ ssize_t audio_in_read(struct file *file,
count -= bytes_to_copy;
buf += bytes_to_copy;
} else {
- pr_err("%s:session id %d: short read data[%p] bytesavail[%d]bytesrequest[%zd]\n",
+ pr_err("%s:session id %d: short read data[%pK] bytesavail[%d]bytesrequest[%zd]\n",
__func__,
audio->ac->session,
data, size, count);
@@ -896,7 +896,7 @@ ssize_t audio_in_write(struct file *file,
buf += xfer;
}
mutex_unlock(&audio->write_lock);
- pr_debug("%s:session id %d: eos_condition 0x%x buf[0x%p] start[0x%p]\n",
+ pr_debug("%s:session id %d: eos_condition 0x%x buf[0x%pK] start[0x%pK]\n",
__func__, audio->ac->session,
nflags, buf, start);
if (nflags & AUD_EOS_SET) {
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
index 567c948b0efe..c963280e5bf5 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
@@ -83,7 +83,7 @@ int insert_eos_buf(struct q6audio_aio *audio,
struct audio_aio_buffer_node *buf_node)
{
struct dec_meta_out *eos_buf = buf_node->kvaddr;
- pr_debug("%s[%p]:insert_eos_buf\n", __func__, audio);
+ pr_debug("%s[%pK]:insert_eos_buf\n", __func__, audio);
eos_buf->num_of_frames = 0xFFFFFFFF;
eos_buf->meta_out_dsp[0].offset_to_frame = 0x0;
eos_buf->meta_out_dsp[0].nflags = AUDIO_DEC_EOS_SET;
@@ -131,14 +131,14 @@ static int audio_aio_ion_lookup_vaddr(struct q6audio_aio *audio, void *addr,
}
if (match_count > 1) {
- pr_err("%s[%p]:multiple hits for vaddr %p, len %ld\n",
+ pr_err("%s[%pK]:multiple hits for vaddr %pK, len %ld\n",
__func__, audio, addr, len);
list_for_each_entry(region_elt, &audio->ion_region_queue,
list) {
if (addr >= region_elt->vaddr &&
addr < region_elt->vaddr + region_elt->len &&
addr + len <= region_elt->vaddr + region_elt->len)
- pr_err("\t%s[%p]:%p, %ld --> %pa\n",
+ pr_err("\t%s[%pK]:%pK, %ld --> %pK\n",
__func__, audio,
region_elt->vaddr,
region_elt->len,
@@ -158,7 +158,7 @@ static phys_addr_t audio_aio_ion_fixup(struct q6audio_aio *audio, void *addr,
ret = audio_aio_ion_lookup_vaddr(audio, addr, len, &region);
if (ret) {
- pr_err("%s[%p]:lookup (%p, %ld) failed\n",
+ pr_err("%s[%pK]:lookup (%pK, %ld) failed\n",
__func__, audio, addr, len);
return 0;
}
@@ -166,7 +166,7 @@ static phys_addr_t audio_aio_ion_fixup(struct q6audio_aio *audio, void *addr,
region->ref_cnt++;
else
region->ref_cnt--;
- pr_debug("%s[%p]:found region %p ref_cnt %d\n",
+ pr_debug("%s[%pK]:found region %pK ref_cnt %d\n",
__func__, audio, region, region->ref_cnt);
paddr = region->paddr + (addr - region->vaddr);
/* provide kernel virtual address for accessing meta information */
@@ -179,26 +179,26 @@ static int audio_aio_pause(struct q6audio_aio *audio)
{
int rc = -EINVAL;
- pr_debug("%s[%p], enabled = %d\n", __func__, audio,
+ pr_debug("%s[%pK], enabled = %d\n", __func__, audio,
audio->enabled);
if (audio->enabled) {
rc = q6asm_cmd(audio->ac, CMD_PAUSE);
if (rc < 0)
- pr_err("%s[%p]: pause cmd failed rc=%d\n",
+ pr_err("%s[%pK]: pause cmd failed rc=%d\n",
__func__, audio, rc);
if (rc == 0) {
/* Send suspend only if pause was successful */
rc = q6asm_cmd(audio->ac, CMD_SUSPEND);
if (rc < 0)
- pr_err("%s[%p]: suspend cmd failed rc=%d\n",
+ pr_err("%s[%pK]: suspend cmd failed rc=%d\n",
__func__, audio, rc);
} else
- pr_err("%s[%p]: not sending suspend since pause failed\n",
+ pr_err("%s[%pK]: not sending suspend since pause failed\n",
__func__, audio);
} else
- pr_err("%s[%p]: Driver not enabled\n", __func__, audio);
+ pr_err("%s[%pK]: Driver not enabled\n", __func__, audio);
return rc;
}
@@ -212,7 +212,7 @@ static int audio_aio_flush(struct q6audio_aio *audio)
if (!(audio->drv_status & ADRV_STATUS_PAUSE)) {
rc = audio_aio_pause(audio);
if (rc < 0)
- pr_err("%s[%p}: pause cmd failed rc=%d\n",
+ pr_err("%s[%pK}: pause cmd failed rc=%d\n",
__func__, audio,
rc);
else
@@ -220,13 +220,13 @@ static int audio_aio_flush(struct q6audio_aio *audio)
}
rc = q6asm_cmd(audio->ac, CMD_FLUSH);
if (rc < 0)
- pr_err("%s[%p]: flush cmd failed rc=%d\n",
+ pr_err("%s[%pK]: flush cmd failed rc=%d\n",
__func__, audio, rc);
/* Not in stop state, reenable the stream */
if (audio->stopped == 0) {
rc = audio_aio_enable(audio);
if (rc)
- pr_err("%s[%p]:audio re-enable failed\n",
+ pr_err("%s[%pK]:audio re-enable failed\n",
__func__, audio);
else {
audio->enabled = 1;
@@ -235,9 +235,9 @@ static int audio_aio_flush(struct q6audio_aio *audio)
}
}
}
- pr_debug("%s[%p]:in_bytes %d\n",
+ pr_debug("%s[%pK]:in_bytes %d\n",
__func__, audio, atomic_read(&audio->in_bytes));
- pr_debug("%s[%p]:in_samples %d\n",
+ pr_debug("%s[%pK]:in_samples %d\n",
__func__, audio, atomic_read(&audio->in_samples));
atomic_set(&audio->in_bytes, 0);
atomic_set(&audio->in_samples, 0);
@@ -250,7 +250,7 @@ static int audio_aio_outport_flush(struct q6audio_aio *audio)
rc = q6asm_cmd(audio->ac, CMD_OUT_FLUSH);
if (rc < 0)
- pr_err("%s[%p}: output port flush cmd failed rc=%d\n",
+ pr_err("%s[%pK}: output port flush cmd failed rc=%d\n",
__func__, audio, rc);
return rc;
}
@@ -278,19 +278,19 @@ void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token,
if (token == used_buf->token) {
list_del(&used_buf->list);
spin_unlock_irqrestore(&audio->dsp_lock, flags);
- pr_debug("%s[%p]:consumed buffer\n", __func__, audio);
+ pr_debug("%s[%pK]:consumed buffer\n", __func__, audio);
event_payload.aio_buf = used_buf->buf;
audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE,
event_payload);
kfree(used_buf);
if (list_empty(&audio->out_queue) &&
(audio->drv_status & ADRV_STATUS_FSYNC)) {
- pr_debug("%s[%p]: list is empty, reached EOS in Tunnel\n",
+ pr_debug("%s[%pK]: list is empty, reached EOS in Tunnel\n",
__func__, audio);
wake_up(&audio->write_wait);
}
} else {
- pr_err("%s[%p]:expected=%x ret=%x\n",
+ pr_err("%s[%pK]:expected=%x ret=%x\n",
__func__, audio, used_buf->token, token);
spin_unlock_irqrestore(&audio->dsp_lock, flags);
}
@@ -304,13 +304,13 @@ void audio_aio_async_out_flush(struct q6audio_aio *audio)
union msm_audio_event_payload payload;
unsigned long flags;
- pr_debug("%s[%p}\n", __func__, audio);
+ pr_debug("%s[%pK}\n", __func__, audio);
/* EOS followed by flush, EOS response not guranteed, free EOS i/p
buffer */
spin_lock_irqsave(&audio->dsp_lock, flags);
if (audio->eos_flag && (audio->eos_write_payload.aio_buf.buf_addr)) {
- pr_debug("%s[%p]: EOS followed by flush received,acknowledge"\
+ pr_debug("%s[%pK]: EOS followed by flush received,acknowledge"
" eos i/p buffer immediately\n", __func__, audio);
audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE,
audio->eos_write_payload);
@@ -324,7 +324,7 @@ void audio_aio_async_out_flush(struct q6audio_aio *audio)
payload.aio_buf = buf_node->buf;
audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, payload);
kfree(buf_node);
- pr_debug("%s[%p]: Propagate WRITE_DONE during flush\n",
+ pr_debug("%s[%pK]: Propagate WRITE_DONE during flush\n",
__func__, audio);
}
}
@@ -335,14 +335,14 @@ void audio_aio_async_in_flush(struct q6audio_aio *audio)
struct list_head *ptr, *next;
union msm_audio_event_payload payload;
- pr_debug("%s[%p]\n", __func__, audio);
+ pr_debug("%s[%pK]\n", __func__, audio);
list_for_each_safe(ptr, next, &audio->in_queue) {
buf_node = list_entry(ptr, struct audio_aio_buffer_node, list);
list_del(&buf_node->list);
/* Forcefull send o/p eos buffer after flush, if no eos response
* received by dsp even after sending eos command */
if ((audio->eos_rsp != 1) && audio->eos_flag) {
- pr_debug("%s[%p]: send eos on o/p buffer during flush\n",
+ pr_debug("%s[%pK]: send eos on o/p buffer during flush\n",
__func__, audio);
payload.aio_buf = buf_node->buf;
payload.aio_buf.data_len =
@@ -355,7 +355,7 @@ void audio_aio_async_in_flush(struct q6audio_aio *audio)
}
audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, payload);
kfree(buf_node);
- pr_debug("%s[%p]: Propagate READ_DONE during flush\n",
+ pr_debug("%s[%pK]: Propagate READ_DONE during flush\n",
__func__, audio);
}
}
@@ -373,19 +373,19 @@ int audio_aio_disable(struct q6audio_aio *audio)
if (audio->opened) {
audio->enabled = 0;
audio->opened = 0;
- pr_debug("%s[%p]: inbytes[%d] insamples[%d]\n", __func__,
+ pr_debug("%s[%pK]: inbytes[%d] insamples[%d]\n", __func__,
audio, atomic_read(&audio->in_bytes),
atomic_read(&audio->in_samples));
/* Close the session */
rc = q6asm_cmd(audio->ac, CMD_CLOSE);
if (rc < 0)
- pr_err("%s[%p]:Failed to close the session rc=%d\n",
+ pr_err("%s[%pK]:Failed to close the session rc=%d\n",
__func__, audio, rc);
audio->stopped = 1;
wake_up(&audio->write_wait);
wake_up(&audio->cmd_wait);
}
- pr_debug("%s[%p]:enabled[%d]\n", __func__, audio, audio->enabled);
+ pr_debug("%s[%pK]:enabled[%d]\n", __func__, audio, audio->enabled);
return rc;
}
@@ -434,16 +434,16 @@ static void audio_aio_unmap_ion_region(struct q6audio_aio *audio)
struct list_head *ptr, *next;
int rc = -EINVAL;
- pr_debug("%s[%p]:\n", __func__, audio);
+ pr_debug("%s[%pK]:\n", __func__, audio);
list_for_each_safe(ptr, next, &audio->ion_region_queue) {
region = list_entry(ptr, struct audio_aio_ion_region, list);
if (region != NULL) {
- pr_debug("%s[%p]: phy_address = 0x%pa\n",
+ pr_debug("%s[%pK]: phy_address = 0x%pK\n",
__func__, audio, &region->paddr);
rc = q6asm_memory_unmap(audio->ac,
region->paddr, IN);
if (rc < 0)
- pr_err("%s[%p]: memory unmap failed\n",
+ pr_err("%s[%pK]: memory unmap failed\n",
__func__, audio);
}
}
@@ -460,20 +460,20 @@ static void audio_aio_listner(u32 evt_id, union auddev_evt_data *evt_payload,
switch (evt_id) {
case AUDDEV_EVT_STREAM_VOL_CHG:
audio->volume = evt_payload->session_vol;
- pr_debug("%s[%p]: AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d, enabled = %d\n",
+ pr_debug("%s[%pK]: AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d, enabled = %d\n",
__func__, audio, audio->volume, audio->enabled);
if (audio->enabled == 1) {
if (audio->ac) {
rc = q6asm_set_volume(audio->ac, audio->volume);
if (rc < 0) {
- pr_err("%s[%p]: Send Volume command failed rc=%d\n",
+ pr_err("%s[%pK]: Send Volume command failed rc=%d\n",
__func__, audio, rc);
}
}
}
break;
default:
- pr_err("%s[%p]:ERROR:wrong event\n", __func__, audio);
+ pr_err("%s[%pK]:ERROR:wrong event\n", __func__, audio);
break;
}
}
@@ -490,7 +490,7 @@ int register_volume_listener(struct q6audio_aio *audio)
audio_aio_listner,
(void *)audio);
if (rc < 0) {
- pr_err("%s[%p]: Event listener failed\n", __func__, audio);
+ pr_err("%s[%pK]: Event listener failed\n", __func__, audio);
rc = -EACCES;
}
return rc;
@@ -508,7 +508,7 @@ int enable_volume_ramp(struct q6audio_aio *audio)
if (audio->ac == NULL)
return -EINVAL;
- pr_debug("%s[%p]\n", __func__, audio);
+ pr_debug("%s[%pK]\n", __func__, audio);
softpause.enable = SOFT_PAUSE_ENABLE;
softpause.period = SOFT_PAUSE_PERIOD;
softpause.step = SOFT_PAUSE_STEP;
@@ -568,7 +568,7 @@ int enable_volume_ramp(struct q6audio_aio *audio)
int audio_aio_release(struct inode *inode, struct file *file)
{
struct q6audio_aio *audio = file->private_data;
- pr_debug("%s[%p]\n", __func__, audio);
+ pr_debug("%s[%pK]\n", __func__, audio);
mutex_lock(&audio->lock);
mutex_lock(&audio->read_lock);
mutex_lock(&audio->write_lock);
@@ -628,56 +628,56 @@ int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync)
audio->drv_status |= ADRV_STATUS_FSYNC;
mutex_unlock(&audio->lock);
- pr_debug("%s[%p]:\n", __func__, audio);
+ pr_debug("%s[%pK]:\n", __func__, audio);
audio->eos_rsp = 0;
- pr_debug("%s[%p]Wait for write done from DSP\n", __func__, audio);
+ pr_debug("%s[%pK]Wait for write done from DSP\n", __func__, audio);
rc = wait_event_interruptible(audio->write_wait,
(list_empty(&audio->out_queue)) ||
audio->wflush || audio->stopped);
if (audio->stopped || audio->wflush) {
- pr_debug("%s[%p]: Audio Flushed or Stopped,this is not EOS\n"
+ pr_debug("%s[%pK]: Audio Flushed or Stopped,this is not EOS\n"
, __func__, audio);
audio->wflush = 0;
rc = -EBUSY;
}
if (rc < 0) {
- pr_err("%s[%p]: wait event for list_empty failed, rc = %d\n",
+ pr_err("%s[%pK]: wait event for list_empty failed, rc = %d\n",
__func__, audio, rc);
goto done;
}
rc = q6asm_cmd(audio->ac, CMD_EOS);
- pr_debug("%s[%p]: EOS cmd sent to DSP\n", __func__, audio);
+ pr_debug("%s[%pK]: EOS cmd sent to DSP\n", __func__, audio);
if (rc < 0)
- pr_err("%s[%p]: q6asm_cmd failed, rc = %d",
+ pr_err("%s[%pK]: q6asm_cmd failed, rc = %d",
__func__, audio, rc);
- pr_debug("%s[%p]: wait for RENDERED_EOS from DSP\n"
+ pr_debug("%s[%pK]: wait for RENDERED_EOS from DSP\n"
, __func__, audio);
rc = wait_event_interruptible(audio->write_wait,
(audio->eos_rsp || audio->wflush ||
audio->stopped));
if (rc < 0) {
- pr_err("%s[%p]: wait event for eos_rsp failed, rc = %d\n",
+ pr_err("%s[%pK]: wait event for eos_rsp failed, rc = %d\n",
__func__, audio, rc);
goto done;
}
if (audio->stopped || audio->wflush) {
audio->wflush = 0;
- pr_debug("%s[%p]: Audio Flushed or Stopped,this is not EOS\n"
+ pr_debug("%s[%pK]: Audio Flushed or Stopped,this is not EOS\n"
, __func__, audio);
rc = -EBUSY;
}
if (audio->eos_rsp == 1)
- pr_debug("%s[%p]: EOS\n", __func__, audio);
+ pr_debug("%s[%pK]: EOS\n", __func__, audio);
done:
@@ -748,21 +748,21 @@ static long audio_aio_process_event_req_common(struct q6audio_aio *audio,
usr_evt->event_payload = drv_evt->payload;
list_add_tail(&drv_evt->list, &audio->free_event_queue);
} else {
- pr_err("%s[%p]:Unexpected path\n", __func__, audio);
+ pr_err("%s[%pK]:Unexpected path\n", __func__, audio);
spin_unlock_irqrestore(&audio->event_queue_lock, flags);
return -EPERM;
}
spin_unlock_irqrestore(&audio->event_queue_lock, flags);
if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) {
- pr_debug("%s[%p]:posted AUDIO_EVENT_WRITE_DONE to user\n",
+ pr_debug("%s[%pK]:posted AUDIO_EVENT_WRITE_DONE to user\n",
__func__, audio);
mutex_lock(&audio->write_lock);
audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
drv_evt->payload.aio_buf.buf_len, 0, 0);
mutex_unlock(&audio->write_lock);
} else if (drv_evt->event_type == AUDIO_EVENT_READ_DONE) {
- pr_debug("%s[%p]:posted AUDIO_EVENT_READ_DONE to user\n",
+ pr_debug("%s[%pK]:posted AUDIO_EVENT_READ_DONE to user\n",
__func__, audio);
mutex_lock(&audio->read_lock);
audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
@@ -774,7 +774,7 @@ static long audio_aio_process_event_req_common(struct q6audio_aio *audio,
* Once EOS indicated
*/
if (audio->eos_rsp && !list_empty(&audio->in_queue)) {
- pr_debug("%s[%p]:Send flush command to release read buffers"\
+ pr_debug("%s[%pK]:Send flush command to release read buffers"
" held up in DSP\n", __func__, audio);
mutex_lock(&audio->lock);
audio_aio_flush(audio);
@@ -917,7 +917,7 @@ static int audio_aio_ion_check(struct q6audio_aio *audio,
list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
OVERLAPS(region_elt, &t)) {
- pr_err("%s[%p]:region (vaddr %p len %ld) clashes with registered region (vaddr %p paddr %pa len %ld)\n",
+ pr_err("%s[%pK]:region (vaddr %pK len %ld) clashes with registered region (vaddr %pK paddr %pK len %ld)\n",
__func__, audio, vaddr, len,
region_elt->vaddr,
&region_elt->paddr, region_elt->len);
@@ -939,7 +939,7 @@ static int audio_aio_ion_add(struct q6audio_aio *audio,
unsigned long ionflag;
void *kvaddr = NULL;
- pr_debug("%s[%p]:\n", __func__, audio);
+ pr_debug("%s[%pK]:\n", __func__, audio);
region = kmalloc(sizeof(*region), GFP_KERNEL);
if (!region) {
@@ -968,14 +968,14 @@ static int audio_aio_ion_add(struct q6audio_aio *audio,
region->kvaddr = kvaddr;
region->len = len;
region->ref_cnt = 0;
- pr_debug("%s[%p]:add region paddr %pa vaddr %p, len %lu kvaddr %p\n",
+ pr_debug("%s[%pK]:add region paddr %pK vaddr %pK, len %lu kvaddr %pK\n",
__func__, audio,
&region->paddr, region->vaddr, region->len,
region->kvaddr);
list_add_tail(&region->list, &audio->ion_region_queue);
rc = q6asm_memory_map(audio->ac, paddr, IN, len, 1);
if (rc < 0) {
- pr_err("%s[%p]: memory map failed\n", __func__, audio);
+ pr_err("%s[%pK]: memory map failed\n", __func__, audio);
goto mmap_error;
} else {
goto end;
@@ -997,7 +997,7 @@ static int audio_aio_ion_remove(struct q6audio_aio *audio,
struct list_head *ptr, *next;
int rc = -EINVAL;
- pr_debug("%s[%p]:info fd %d vaddr %p\n",
+ pr_debug("%s[%pK]:info fd %d vaddr %pK\n",
__func__, audio, info->fd, info->vaddr);
list_for_each_safe(ptr, next, &audio->ion_region_queue) {
@@ -1006,17 +1006,17 @@ static int audio_aio_ion_remove(struct q6audio_aio *audio,
if ((region->fd == info->fd) &&
(region->vaddr == info->vaddr)) {
if (region->ref_cnt) {
- pr_debug("%s[%p]:region %p in use ref_cnt %d\n",
+ pr_debug("%s[%pK]:region %pK in use ref_cnt %d\n",
__func__, audio, region,
region->ref_cnt);
break;
}
- pr_debug("%s[%p]:remove region fd %d vaddr %p\n",
+ pr_debug("%s[%pK]:remove region fd %d vaddr %pK\n",
__func__, audio, info->fd, info->vaddr);
rc = q6asm_memory_unmap(audio->ac,
region->paddr, IN);
if (rc < 0)
- pr_err("%s[%p]: memory unmap failed\n",
+ pr_err("%s[%pK]: memory unmap failed\n",
__func__, audio);
list_del(&region->list);
@@ -1039,15 +1039,15 @@ static int audio_aio_async_write(struct q6audio_aio *audio,
struct audio_aio_write_param param;
if (!audio || !buf_node) {
- pr_err("%s NULL pointer audio=[0x%p], buf_node=[0x%p]\n",
+ pr_err("%s NULL pointer audio=[0x%pK], buf_node=[0x%pK]\n",
__func__, audio, buf_node);
return -EINVAL;
}
- pr_debug("%s[%p]: Send write buff %p phy %pa len %d meta_enable = %d\n",
+ pr_debug("%s[%pK]: Send write buff %pK phy %pK len %d meta_enable = %d\n",
__func__, audio, buf_node, &buf_node->paddr,
buf_node->buf.data_len,
audio->buf_cfg.meta_info_enable);
- pr_debug("%s[%p]: flags = 0x%x\n", __func__, audio,
+ pr_debug("%s[%pK]: flags = 0x%x\n", __func__, audio,
buf_node->meta_info.meta_in.nflags);
ac = audio->ac;
@@ -1076,7 +1076,7 @@ static int audio_aio_async_write(struct q6audio_aio *audio,
buf_node->token = ac->session;
rc = q6asm_async_write(ac, &param);
if (rc < 0)
- pr_err("%s[%p]:failed\n", __func__, audio);
+ pr_err("%s[%pK]:failed\n", __func__, audio);
return rc;
}
@@ -1095,8 +1095,6 @@ void audio_aio_post_event(struct q6audio_aio *audio, int type,
} else {
e_node = kmalloc(sizeof(struct audio_aio_event), GFP_ATOMIC);
if (!e_node) {
- pr_err("%s[%p]:No mem to post event %d\n",
- __func__, audio, type);
spin_unlock_irqrestore(&audio->event_queue_lock, flags);
return;
}
@@ -1117,7 +1115,7 @@ static int audio_aio_async_read(struct q6audio_aio *audio,
struct audio_aio_read_param param;
int rc;
- pr_debug("%s[%p]: Send read buff %p phy %pa len %d\n",
+ pr_debug("%s[%pK]: Send read buff %pK phy %pK len %d\n",
__func__, audio, buf_node,
&buf_node->paddr, buf_node->buf.buf_len);
ac = audio->ac;
@@ -1131,7 +1129,7 @@ static int audio_aio_async_read(struct q6audio_aio *audio,
buf_node->token = ac->session;
rc = q6asm_async_read(ac, &param);
if (rc < 0)
- pr_err("%s[%p]:failed\n", __func__, audio);
+ pr_err("%s[%pK]:failed\n", __func__, audio);
return rc;
}
@@ -1140,7 +1138,7 @@ static int audio_aio_buf_add_shared(struct q6audio_aio *audio, u32 dir,
{
unsigned long flags;
int ret = 0;
- pr_debug("%s[%p]:node %p dir %x buf_addr %p buf_len %d data_len %d\n",
+ pr_debug("%s[%pK]:node %pK dir %x buf_addr %pK buf_len %d data_len %d\n",
__func__, audio, buf_node, dir, buf_node->buf.buf_addr,
buf_node->buf.buf_len, buf_node->buf.data_len);
buf_node->paddr = audio_aio_ion_fixup(audio, buf_node->buf.buf_addr,
@@ -1165,7 +1163,7 @@ static int audio_aio_buf_add_shared(struct q6audio_aio *audio, u32 dir,
} else if (buf_node->meta_info.meta_in.nflags
& AUDIO_DEC_EOS_SET) {
if (!audio->wflush) {
- pr_debug("%s[%p]:Send EOS cmd at i/p\n",
+ pr_debug("%s[%pK]:Send EOS cmd at i/p\n",
__func__, audio);
/* Driver will forcefully post writedone event
* once eos ack recived from DSP
@@ -1211,7 +1209,7 @@ static int audio_aio_buf_add_shared(struct q6audio_aio *audio, u32 dir,
event_payload.aio_buf = buf_node->buf;
event_payload.aio_buf.data_len =
insert_eos_buf(audio, buf_node);
- pr_debug("%s[%p]: propagate READ_DONE as EOS done\n",\
+ pr_debug("%s[%pK]: propagate READ_DONE as EOS done\n",
__func__, audio);
audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE,
event_payload);
@@ -1280,7 +1278,8 @@ void audio_aio_ioport_reset(struct q6audio_aio *audio)
* abort due to flush
*/
if (audio->drv_status & ADRV_STATUS_FSYNC) {
- pr_debug("%s[%p]:fsync in progress\n", __func__, audio);
+ pr_debug("%s[%pK]:fsync in progress\n",
+ __func__, audio);
audio->drv_ops.out_flush(audio);
} else
audio->drv_ops.out_flush(audio);
@@ -1307,13 +1306,13 @@ int audio_aio_open(struct q6audio_aio *audio, struct file *file)
/* Only AIO interface */
if (file->f_flags & O_NONBLOCK) {
- pr_debug("%s[%p]:set to aio interface\n", __func__, audio);
+ pr_debug("%s[%pK]:set to aio interface\n", __func__, audio);
audio->drv_status |= ADRV_STATUS_AIO_INTF;
audio->drv_ops.out_flush = audio_aio_async_out_flush;
audio->drv_ops.in_flush = audio_aio_async_in_flush;
q6asm_set_io_mode(audio->ac, ASYNC_IO_MODE);
} else {
- pr_err("%s[%p]:SIO interface not supported\n",
+ pr_err("%s[%pK]:SIO interface not supported\n",
__func__, audio);
rc = -EACCES;
goto fail;
@@ -1346,7 +1345,7 @@ int audio_aio_open(struct q6audio_aio *audio, struct file *file)
if (e_node)
list_add_tail(&e_node->list, &audio->free_event_queue);
else {
- pr_err("%s[%p]:event pkt alloc failed\n",
+ pr_err("%s[%pK]:event pkt alloc failed\n",
__func__, audio);
rc = -ENOMEM;
goto cleanup;
@@ -1358,7 +1357,7 @@ int audio_aio_open(struct q6audio_aio *audio, struct file *file)
rc = -ENOMEM;
goto cleanup;
}
- pr_debug("Ion client create in audio_aio_open %p", audio->client);
+ pr_debug("Ion client create in audio_aio_open %pK", audio->client);
rc = register_volume_listener(audio);
if (rc < 0)
@@ -1392,11 +1391,11 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd,
break;
}
case AUDIO_OUTPORT_FLUSH: {
- pr_debug("%s[%p]:AUDIO_OUTPORT_FLUSH\n", __func__, audio);
+ pr_debug("%s[%pK]:AUDIO_OUTPORT_FLUSH\n", __func__, audio);
mutex_lock(&audio->read_lock);
rc = audio_aio_outport_flush(audio);
if (rc < 0) {
- pr_err("%s[%p]: AUDIO_OUTPORT_FLUSH failed\n",
+ pr_err("%s[%pK]: AUDIO_OUTPORT_FLUSH failed\n",
__func__, audio);
rc = -EINTR;
}
@@ -1404,13 +1403,13 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd,
break;
}
case AUDIO_STOP: {
- pr_debug("%s[%p]: AUDIO_STOP session_id[%d]\n", __func__,
+ pr_debug("%s[%pK]: AUDIO_STOP session_id[%d]\n", __func__,
audio, audio->ac->session);
mutex_lock(&audio->lock);
audio->stopped = 1;
rc = audio_aio_flush(audio);
if (rc < 0) {
- pr_err("%s[%p]:Audio Stop procedure failed rc=%d\n",
+ pr_err("%s[%pK]:Audio Stop procedure failed rc=%d\n",
__func__, audio, rc);
mutex_unlock(&audio->lock);
break;
@@ -1418,7 +1417,7 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd,
audio->enabled = 0;
audio->drv_status &= ~ADRV_STATUS_PAUSE;
if (audio->drv_status & ADRV_STATUS_FSYNC) {
- pr_debug("%s[%p] Waking up the audio_aio_fsync\n",
+ pr_debug("%s[%pK] Waking up the audio_aio_fsync\n",
__func__, audio);
wake_up(&audio->write_wait);
}
@@ -1426,12 +1425,12 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd,
break;
}
case AUDIO_PAUSE: {
- pr_debug("%s[%p]:AUDIO_PAUSE %ld\n", __func__, audio, arg);
+ pr_debug("%s[%pK]:AUDIO_PAUSE %ld\n", __func__, audio, arg);
mutex_lock(&audio->lock);
if (arg == 1) {
rc = audio_aio_pause(audio);
if (rc < 0) {
- pr_err("%s[%p]: pause FAILED rc=%d\n",
+ pr_err("%s[%pK]: pause FAILED rc=%d\n",
__func__, audio, rc);
mutex_unlock(&audio->lock);
break;
@@ -1441,7 +1440,7 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd,
if (audio->drv_status & ADRV_STATUS_PAUSE) {
rc = audio_aio_enable(audio);
if (rc)
- pr_err("%s[%p]: audio enable failed\n",
+ pr_err("%s[%pK]: audio enable failed\n",
__func__, audio);
else {
audio->drv_status &= ~ADRV_STATUS_PAUSE;
@@ -1453,13 +1452,13 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd,
break;
}
case AUDIO_FLUSH: {
- pr_debug("%s[%p]: AUDIO_FLUSH sessionid[%d]\n", __func__,
+ pr_debug("%s[%pK]: AUDIO_FLUSH sessionid[%d]\n", __func__,
audio, audio->ac->session);
mutex_lock(&audio->lock);
audio->rflush = 1;
audio->wflush = 1;
if (audio->drv_status & ADRV_STATUS_FSYNC) {
- pr_debug("%s[%p] Waking up the audio_aio_fsync\n",
+ pr_debug("%s[%pK] Waking up the audio_aio_fsync\n",
__func__, audio);
wake_up(&audio->write_wait);
}
@@ -1468,7 +1467,7 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd,
/* Flush input / Output buffer in software*/
audio_aio_ioport_reset(audio);
if (rc < 0) {
- pr_err("%s[%p]:AUDIO_FLUSH interrupted\n",
+ pr_err("%s[%pK]:AUDIO_FLUSH interrupted\n",
__func__, audio);
rc = -EINTR;
} else {
@@ -1498,12 +1497,12 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd,
case AUDIO_PM_AWAKE: {
if ((audio->audio_ws_mgr == NULL) ||
(audio->miscdevice == NULL)) {
- pr_err("%s[%p]: invalid ws_mgr or miscdevice",
+ pr_err("%s[%pK]: invalid ws_mgr or miscdevice",
__func__, audio);
rc = -EACCES;
break;
}
- pr_debug("%s[%p]:AUDIO_PM_AWAKE\n", __func__, audio);
+ pr_debug("%s[%pK]:AUDIO_PM_AWAKE\n", __func__, audio);
mutex_lock(&audio->lock);
if (!audio->wakelock_voted) {
audio->wakelock_voted = true;
@@ -1518,12 +1517,12 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd,
case AUDIO_PM_RELAX: {
if ((audio->audio_ws_mgr == NULL) ||
(audio->miscdevice == NULL)) {
- pr_err("%s[%p]: invalid ws_mgr or miscdevice",
+ pr_err("%s[%pK]: invalid ws_mgr or miscdevice",
__func__, audio);
rc = -EACCES;
break;
}
- pr_debug("%s[%p]:AUDIO_PM_RELAX\n", __func__, audio);
+ pr_debug("%s[%pK]:AUDIO_PM_RELAX\n", __func__, audio);
mutex_lock(&audio->lock);
if (audio->wakelock_voted) {
audio->wakelock_voted = false;
@@ -1582,7 +1581,7 @@ static long audio_aio_ioctl(struct file *file, unsigned int cmd,
break;
}
case AUDIO_GET_EVENT: {
- pr_debug("%s[%p]:AUDIO_GET_EVENT\n", __func__, audio);
+ pr_debug("%s[%pK]:AUDIO_GET_EVENT\n", __func__, audio);
if (mutex_trylock(&audio->get_event_lock)) {
rc = audio_aio_process_event_req(audio,
(void __user *)arg);
@@ -1622,7 +1621,7 @@ static long audio_aio_ioctl(struct file *file, unsigned int cmd,
memset(&cfg, 0, sizeof(cfg));
cfg.buffer_size = audio->str_cfg.buffer_size;
cfg.buffer_count = audio->str_cfg.buffer_count;
- pr_debug("%s[%p]:GET STREAM CFG %d %d\n",
+ pr_debug("%s[%pK]:GET STREAM CFG %d %d\n",
__func__, audio, cfg.buffer_size, cfg.buffer_count);
if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) {
pr_err(
@@ -1635,7 +1634,7 @@ static long audio_aio_ioctl(struct file *file, unsigned int cmd,
}
case AUDIO_SET_STREAM_CONFIG: {
struct msm_audio_stream_config cfg;
- pr_debug("%s[%p]:SET STREAM CONFIG\n", __func__, audio);
+ pr_debug("%s[%pK]:SET STREAM CONFIG\n", __func__, audio);
mutex_lock(&audio->lock);
if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
pr_err(
@@ -1665,7 +1664,7 @@ static long audio_aio_ioctl(struct file *file, unsigned int cmd,
}
case AUDIO_SET_CONFIG: {
struct msm_audio_config config;
- pr_err("%s[%p]:AUDIO_SET_CONFIG\n", __func__, audio);
+ pr_err("%s[%pK]:AUDIO_SET_CONFIG\n", __func__, audio);
mutex_lock(&audio->lock);
if (copy_from_user(&config, (void *)arg, sizeof(config))) {
pr_err(
@@ -1676,7 +1675,7 @@ static long audio_aio_ioctl(struct file *file, unsigned int cmd,
break;
}
if (audio->feedback != NON_TUNNEL_MODE) {
- pr_err("%s[%p]:Not sufficient permission to change the playback mode\n",
+ pr_err("%s[%pK]:Not sufficient permission to change the playback mode\n",
__func__, audio);
rc = -EACCES;
mutex_unlock(&audio->lock);
@@ -1716,14 +1715,14 @@ static long audio_aio_ioctl(struct file *file, unsigned int cmd,
}
audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
- pr_debug("%s[%p]:session id %d: Set-buf-cfg: meta[%d]",
+ pr_debug("%s[%pK]:session id %d: Set-buf-cfg: meta[%d]",
__func__, audio,
audio->ac->session, cfg.meta_info_enable);
mutex_unlock(&audio->lock);
break;
}
case AUDIO_GET_BUF_CFG: {
- pr_debug("%s[%p]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
+ pr_debug("%s[%pK]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
__func__, audio,
audio->ac->session, audio->buf_cfg.meta_info_enable,
audio->buf_cfg.frames_per_buf);
@@ -1741,7 +1740,7 @@ static long audio_aio_ioctl(struct file *file, unsigned int cmd,
}
case AUDIO_REGISTER_ION: {
struct msm_audio_ion_info info;
- pr_debug("%s[%p]:AUDIO_REGISTER_ION\n", __func__, audio);
+ pr_debug("%s[%pK]:AUDIO_REGISTER_ION\n", __func__, audio);
mutex_lock(&audio->lock);
if (copy_from_user(&info, (void *)arg, sizeof(info))) {
pr_err(
@@ -1761,7 +1760,7 @@ static long audio_aio_ioctl(struct file *file, unsigned int cmd,
case AUDIO_DEREGISTER_ION: {
struct msm_audio_ion_info info;
mutex_lock(&audio->lock);
- pr_debug("%s[%p]:AUDIO_DEREGISTER_ION\n", __func__, audio);
+ pr_debug("%s[%pK]:AUDIO_DEREGISTER_ION\n", __func__, audio);
if (copy_from_user(&info, (void *)arg, sizeof(info))) {
pr_err(
"%s: copy_from_user for AUDIO_DEREGISTER_ION failed\n",
@@ -1881,7 +1880,7 @@ static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
break;
}
case AUDIO_GET_EVENT_32: {
- pr_debug("%s[%p]:AUDIO_GET_EVENT\n", __func__, audio);
+ pr_debug("%s[%pK]:AUDIO_GET_EVENT\n", __func__, audio);
if (mutex_trylock(&audio->get_event_lock)) {
rc = audio_aio_process_event_req_compat(audio,
(void __user *)arg);
@@ -1921,7 +1920,7 @@ static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
memset(&cfg, 0, sizeof(cfg));
cfg.buffer_size = audio->str_cfg.buffer_size;
cfg.buffer_count = audio->str_cfg.buffer_count;
- pr_debug("%s[%p]:GET STREAM CFG %d %d\n",
+ pr_debug("%s[%pK]:GET STREAM CFG %d %d\n",
__func__, audio, cfg.buffer_size, cfg.buffer_count);
if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) {
pr_err("%s: copy_to_user for AUDIO_GET_STREAM_CONFIG_32 failed\n",
@@ -1934,7 +1933,7 @@ static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
case AUDIO_SET_STREAM_CONFIG_32: {
struct msm_audio_stream_config32 cfg_32;
struct msm_audio_stream_config cfg;
- pr_debug("%s[%p]:SET STREAM CONFIG\n", __func__, audio);
+ pr_debug("%s[%pK]:SET STREAM CONFIG\n", __func__, audio);
mutex_lock(&audio->lock);
if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG_32 failed\n",
@@ -1978,13 +1977,13 @@ static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
mutex_lock(&audio->lock);
if (audio->feedback != NON_TUNNEL_MODE) {
- pr_err("%s[%p]:Not sufficient permission to change the playback mode\n",
+ pr_err("%s[%pK]:Not sufficient permission to change the playback mode\n",
__func__, audio);
rc = -EACCES;
mutex_unlock(&audio->lock);
break;
}
- pr_err("%s[%p]:AUDIO_SET_CONFIG\n", __func__, audio);
+ pr_err("%s[%pK]:AUDIO_SET_CONFIG\n", __func__, audio);
if (copy_from_user(&config_32, (void *)arg,
sizeof(config_32))) {
pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n",
@@ -2038,7 +2037,7 @@ static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
}
audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
- pr_debug("%s[%p]:session id %d: Set-buf-cfg: meta[%d]",
+ pr_debug("%s[%pK]:session id %d: Set-buf-cfg: meta[%d]",
__func__, audio,
audio->ac->session, cfg.meta_info_enable);
mutex_unlock(&audio->lock);
@@ -2046,7 +2045,7 @@ static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
}
case AUDIO_GET_BUF_CFG_32: {
struct msm_audio_buf_cfg32 cfg_32;
- pr_debug("%s[%p]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
+ pr_debug("%s[%pK]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
__func__, audio,
audio->ac->session, audio->buf_cfg.meta_info_enable,
audio->buf_cfg.frames_per_buf);
@@ -2067,7 +2066,7 @@ static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
case AUDIO_REGISTER_ION_32: {
struct msm_audio_ion_info32 info_32;
struct msm_audio_ion_info info;
- pr_debug("%s[%p]:AUDIO_REGISTER_ION\n", __func__, audio);
+ pr_debug("%s[%pK]:AUDIO_REGISTER_ION\n", __func__, audio);
mutex_lock(&audio->lock);
if (copy_from_user(&info_32, (void *)arg, sizeof(info_32))) {
pr_err("%s: copy_from_user for AUDIO_REGISTER_ION_32 failed\n",
@@ -2089,7 +2088,7 @@ static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
struct msm_audio_ion_info32 info_32;
struct msm_audio_ion_info info;
mutex_lock(&audio->lock);
- pr_debug("%s[%p]:AUDIO_DEREGISTER_ION\n", __func__, audio);
+ pr_debug("%s[%pK]:AUDIO_DEREGISTER_ION\n", __func__, audio);
if (copy_from_user(&info_32, (void *)arg, sizeof(info_32))) {
pr_err("%s: copy_from_user for AUDIO_DEREGISTER_ION_32 failed\n",
__func__);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_wma.c b/drivers/misc/qcom/qdsp6v2/audio_wma.c
index 74f678da925a..b7dfdf23bec7 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_wma.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_wma.c
@@ -40,7 +40,7 @@ static long audio_ioctl_shared(struct file *file, unsigned int cmd,
case AUDIO_START: {
struct asm_wma_cfg wma_cfg;
struct msm_audio_wma_config_v2 *wma_config;
- pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
audio, audio->ac->session);
if (audio->feedback == NON_TUNNEL_MODE) {
/* Configure PCM output block */
@@ -122,7 +122,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
default: {
- pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
if (rc)
pr_err("Failed in utils_ioctl: %d\n", rc);
@@ -211,7 +211,7 @@ static long audio_compat_ioctl(struct file *file, unsigned int cmd,
break;
}
default: {
- pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_compat_ioctl(file, cmd, arg);
if (rc)
pr_err("Failed in utils_ioctl: %d\n", rc);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_wmapro.c b/drivers/misc/qcom/qdsp6v2/audio_wmapro.c
index 21ad33b7fd5d..d37a5789391c 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_wmapro.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_wmapro.c
@@ -173,7 +173,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
default: {
- pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_ioctl(file, cmd, arg);
if (rc)
pr_err("Failed in utils_ioctl: %d\n", rc);
@@ -283,7 +283,7 @@ static long audio_compat_ioctl(struct file *file, unsigned int cmd,
break;
}
default: {
- pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
rc = audio->codec_compat_ioctl(file, cmd, arg);
if (rc)
pr_err("Failed in utils_ioctl: %d\n", rc);
diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c b/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c
index 6e82c8051886..09b83f354406 100644
--- a/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c
+++ b/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c
@@ -54,18 +54,18 @@ void audio_aio_cb(uint32_t opcode, uint32_t token,
switch (opcode) {
case ASM_DATA_EVENT_WRITE_DONE_V2:
- pr_debug("%s[%p]:ASM_DATA_EVENT_WRITE_DONE token = 0x%x\n",
+ pr_debug("%s[%pK]:ASM_DATA_EVENT_WRITE_DONE token = 0x%x\n",
__func__, audio, token);
audio_aio_async_write_ack(audio, token, payload);
break;
case ASM_DATA_EVENT_READ_DONE_V2:
- pr_debug("%s[%p]:ASM_DATA_EVENT_READ_DONE token = 0x%x\n",
+ pr_debug("%s[%pK]:ASM_DATA_EVENT_READ_DONE token = 0x%x\n",
__func__, audio, token);
audio_aio_async_read_ack(audio, token, payload);
break;
case ASM_DATA_EVENT_RENDERED_EOS:
/* EOS Handle */
- pr_debug("%s[%p]:ASM_DATA_CMDRSP_EOS\n", __func__, audio);
+ pr_debug("%s[%pK]:ASM_DATA_CMDRSP_EOS\n", __func__, audio);
if (audio->feedback) { /* Non-Tunnel mode */
audio->eos_rsp = 1;
/* propagate input EOS i/p buffer,
@@ -87,16 +87,16 @@ void audio_aio_cb(uint32_t opcode, uint32_t token,
break;
case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
- pr_debug("%s[%p]:payload0[%x] payloa1d[%x]opcode= 0x%x\n",
+ pr_debug("%s[%pK]:payload0[%x] payloa1d[%x]opcode= 0x%x\n",
__func__, audio, payload[0], payload[1], opcode);
break;
case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY:
- pr_debug("%s[%p]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0]-sr = %d, payload[1]-chl = %d, payload[2] = %d, payload[3] = %d\n",
+ pr_debug("%s[%pK]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0]-sr = %d, payload[1]-chl = %d, payload[2] = %d, payload[3] = %d\n",
__func__, audio, payload[0],
payload[1], payload[2], payload[3]);
- pr_debug("%s[%p]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, sr(prev) = %d, chl(prev) = %d,",
+ pr_debug("%s[%pK]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, sr(prev) = %d, chl(prev) = %d,",
__func__, audio, audio->pcm_cfg.sample_rate,
audio->pcm_cfg.channel_count);
@@ -130,7 +130,7 @@ void extract_meta_out_info(struct q6audio_aio *audio,
else
memset(&buf_node->meta_info.meta_in,
0, sizeof(struct dec_meta_in));
- pr_debug("%s[%p]:i/p: msw_ts 0x%d lsw_ts 0x%d nflags 0x%8x\n",
+ pr_debug("%s[%pK]:i/p: msw_ts %d lsw_ts %d nflags 0x%8x\n",
__func__, audio,
buf_node->meta_info.meta_in.ntimestamp.highpart,
buf_node->meta_info.meta_in.ntimestamp.lowpart,
@@ -145,7 +145,7 @@ void extract_meta_out_info(struct q6audio_aio *audio,
meta_data->meta_out_dsp[0].lsw_ts;
meta_data->meta_out_dsp[0].lsw_ts = temp;
- pr_debug("%s[%p]:o/p: msw_ts 0x%d lsw_ts 0x%d nflags 0x%8x, num_frames = %d\n",
+ pr_debug("%s[%pK]:o/p: msw_ts %d lsw_ts %d nflags 0x%8x, num_frames = %d\n",
__func__, audio,
((struct dec_meta_out *)buf_node->kvaddr)->\
meta_out_dsp[0].msw_ts,
@@ -201,7 +201,7 @@ void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token,
= payload[9];
event_payload.aio_buf.data_len = payload[4]\
+ payload[5] + sizeof(struct dec_meta_out);
- pr_debug("%s[%p]:nr of frames 0x%8x len=%d\n",
+ pr_debug("%s[%pK]:nr of frames 0x%8x len=%d\n",
__func__, audio,
filled_buf->meta_info.meta_out.num_of_frames,
event_payload.aio_buf.data_len);
@@ -213,7 +213,7 @@ void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token,
event_payload);
kfree(filled_buf);
} else {
- pr_err("%s[%p]:expected=%x ret=%x\n",
+ pr_err("%s[%pK]:expected=%x ret=%x\n",
__func__, audio, filled_buf->token, token);
spin_unlock_irqrestore(&audio->dsp_lock, flags);
}
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c b/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c
index 30274fd4b725..334e705ca8f1 100644
--- a/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c
@@ -208,7 +208,7 @@ static int q6usm_us_client_buf_free(unsigned int dir,
rc = q6usm_memory_unmap(port->phys, dir, usc->session,
*((uint32_t *)port->ext));
- pr_debug("%s: data[%p]phys[%llx][%p]\n", __func__,
+ pr_debug("%s: data[%pK]phys[%llx][%pK]\n", __func__,
(void *)port->data, (u64)port->phys, (void *)&port->phys);
msm_audio_ion_free(port->client, port->handle);
@@ -248,7 +248,7 @@ int q6usm_us_param_buf_free(unsigned int dir,
rc = q6usm_memory_unmap(port->param_phys, dir, usc->session,
*((uint32_t *)port->param_buf_mem_handle));
- pr_debug("%s: data[%p]phys[%llx][%p]\n", __func__,
+ pr_debug("%s: data[%pK]phys[%llx][%pK]\n", __func__,
(void *)port->param_buf, (u64)port->param_phys,
(void *)&port->param_phys);
@@ -362,7 +362,7 @@ struct us_client *q6usm_us_client_alloc(
spin_lock_init(&usc->port[lcnt].dsp_lock);
usc->port[lcnt].ext = (void *)p_mem_handle++;
usc->port[lcnt].param_buf_mem_handle = (void *)p_mem_handle++;
- pr_err("%s: usc->port[%d].ext=%p;\n",
+ pr_err("%s: usc->port[%d].ext=%pK;\n",
__func__, lcnt, usc->port[lcnt].ext);
}
atomic_set(&usc->cmd_state, 0);
@@ -417,7 +417,7 @@ int q6usm_us_client_buf_alloc(unsigned int dir,
port->buf_cnt = bufcnt;
port->buf_size = bufsz;
- pr_debug("%s: data[%p]; phys[%llx]; [%p]\n", __func__,
+ pr_debug("%s: data[%pK]; phys[%llx]; [%pK]\n", __func__,
(void *)port->data,
(u64)port->phys,
(void *)&port->phys);
@@ -482,7 +482,7 @@ int q6usm_us_param_buf_alloc(unsigned int dir,
}
port->param_buf_size = bufsz;
- pr_debug("%s: param_buf[%p]; param_phys[%llx]; [%p]\n", __func__,
+ pr_debug("%s: param_buf[%pK]; param_phys[%llx]; [%pK]\n", __func__,
(void *)port->param_buf,
(u64)port->param_phys,
(void *)&port->param_phys);
@@ -1335,7 +1335,7 @@ int q6usm_set_us_detection(struct us_client *usc,
if ((usc == NULL) ||
(detect_info_size == 0) ||
(detect_info == NULL)) {
- pr_err("%s: wrong input: usc=0x%p, inf_size=%d; info=0x%p",
+ pr_err("%s: wrong input: usc=0x%pK, inf_size=%d; info=0x%pK",
__func__,
usc,
detect_info_size,
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c b/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c
index 7572374cc524..3bb95f50bc13 100644
--- a/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c
@@ -23,6 +23,7 @@
#include <linux/time.h>
#include <linux/kmemleak.h>
#include <linux/wakelock.h>
+#include <linux/mutex.h>
#include <sound/apr_audio.h>
#include <linux/qdsp6v2/usf.h>
#include "q6usm.h"
@@ -135,6 +136,8 @@ struct usf_type {
uint16_t conflicting_event_filters;
/* The requested buttons bitmap */
uint16_t req_buttons_bitmap;
+ /* Mutex for exclusive operations (all public APIs) */
+ struct mutex mutex;
};
struct usf_input_dev_type {
@@ -1403,9 +1406,22 @@ static int __usf_set_stream_param(struct usf_xx_type *usf_xx,
int dir)
{
struct us_client *usc = usf_xx->usc;
- struct us_port_data *port = &usc->port[dir];
+ struct us_port_data *port;
int rc = 0;
+ if (usc == NULL) {
+ pr_err("%s: usc is null\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ port = &usc->port[dir];
+ if (port == NULL) {
+ pr_err("%s: port is null\n",
+ __func__);
+ return -EFAULT;
+ }
+
if (port->param_buf == NULL) {
pr_err("%s: parameter buffer is null\n",
__func__);
@@ -1538,10 +1554,12 @@ static int usf_get_stream_param(struct usf_xx_type *usf_xx,
return __usf_get_stream_param(usf_xx, &get_stream_param, dir);
} /* usf_get_stream_param */
-static long usf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static long __usf_ioctl(struct usf_type *usf,
+ unsigned int cmd,
+ unsigned long arg)
{
+
int rc = 0;
- struct usf_type *usf = file->private_data;
struct usf_xx_type *usf_xx = NULL;
switch (cmd) {
@@ -1704,6 +1722,18 @@ static long usf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
release_xx(usf_xx);
return rc;
+} /* __usf_ioctl */
+
+static long usf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct usf_type *usf = file->private_data;
+ int rc = 0;
+
+ mutex_lock(&usf->mutex);
+ rc = __usf_ioctl(usf, cmd, arg);
+ mutex_unlock(&usf->mutex);
+
+ return rc;
} /* usf_ioctl */
#ifdef CONFIG_COMPAT
@@ -2147,12 +2177,11 @@ static int usf_get_stream_param32(struct usf_xx_type *usf_xx,
return __usf_get_stream_param(usf_xx, &get_stream_param, dir);
} /* usf_get_stream_param32 */
-static long usf_compat_ioctl(struct file *file,
+static long __usf_compat_ioctl(struct usf_type *usf,
unsigned int cmd,
unsigned long arg)
{
int rc = 0;
- struct usf_type *usf = file->private_data;
struct usf_xx_type *usf_xx = NULL;
switch (cmd) {
@@ -2160,7 +2189,7 @@ static long usf_compat_ioctl(struct file *file,
case US_START_RX:
case US_STOP_TX:
case US_STOP_RX: {
- return usf_ioctl(file, cmd, arg);
+ return __usf_ioctl(usf, cmd, arg);
}
case US_SET_TX_INFO32: {
@@ -2269,6 +2298,20 @@ static long usf_compat_ioctl(struct file *file,
release_xx(usf_xx);
return rc;
+} /* __usf_compat_ioctl */
+
+static long usf_compat_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct usf_type *usf = file->private_data;
+ int rc = 0;
+
+ mutex_lock(&usf->mutex);
+ rc = __usf_compat_ioctl(usf, cmd, arg);
+ mutex_unlock(&usf->mutex);
+
+ return rc;
} /* usf_compat_ioctl */
#endif /* CONFIG_COMPAT */
@@ -2277,13 +2320,17 @@ static int usf_mmap(struct file *file, struct vm_area_struct *vms)
struct usf_type *usf = file->private_data;
int dir = OUT;
struct usf_xx_type *usf_xx = &usf->usf_tx;
+ int rc = 0;
+ mutex_lock(&usf->mutex);
if (vms->vm_flags & USF_VM_WRITE) { /* RX buf mapping */
dir = IN;
usf_xx = &usf->usf_rx;
}
+ rc = q6usm_get_virtual_address(dir, usf_xx->usc, vms);
+ mutex_unlock(&usf->mutex);
- return q6usm_get_virtual_address(dir, usf_xx->usc, vms);
+ return rc;
}
static uint16_t add_opened_dev(int minor)
@@ -2336,6 +2383,8 @@ static int usf_open(struct inode *inode, struct file *file)
usf->usf_tx.us_detect_type = USF_US_DETECT_UNDEF;
usf->usf_rx.us_detect_type = USF_US_DETECT_UNDEF;
+ mutex_init(&usf->mutex);
+
pr_debug("%s:usf in open\n", __func__);
return 0;
}
@@ -2346,6 +2395,7 @@ static int usf_release(struct inode *inode, struct file *file)
pr_debug("%s: release entry\n", __func__);
+ mutex_lock(&usf->mutex);
usf_release_input(usf);
usf_disable(&usf->usf_tx);
@@ -2354,6 +2404,8 @@ static int usf_release(struct inode *inode, struct file *file)
s_opened_devs[usf->dev_ind] = 0;
wakeup_source_trash(&usf_wakeup_source);
+ mutex_unlock(&usf->mutex);
+ mutex_destroy(&usf->mutex);
kfree(usf);
pr_debug("%s: release exit\n", __func__);
return 0;
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c b/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c
index 76bcc83e1c5e..a4d63f0c0d1a 100644
--- a/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 2016 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -170,7 +170,7 @@ static int usfcdev_connect(struct input_handler *handler, struct input_dev *dev,
}
usfc_handle->dev = dev;
ret = input_register_handle(usfc_handle);
- pr_debug("%s: name=[%s]; ind=%d; dev=0x%p\n",
+ pr_debug("%s: name=[%s]; ind=%d; dev=0x%pK\n",
__func__,
dev->name,
ind,
@@ -259,7 +259,7 @@ bool usfcdev_register(
bool rc = false;
if ((event_type_ind >= MAX_EVENT_TYPE_NUM) || !match_cb) {
- pr_err("%s: wrong input: event_type_ind=%d; match_cb=0x%p\n",
+ pr_err("%s: wrong input: event_type_ind=%d; match_cb=0x%pK\n",
__func__,
event_type_ind,
match_cb);
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 644178a0cdfc..7dc7271b05c1 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -131,6 +131,35 @@ static DEFINE_MUTEX(qsee_bw_mutex);
static DEFINE_MUTEX(app_access_lock);
static DEFINE_MUTEX(clk_access_lock);
+struct sglist_info {
+ uint32_t indexAndFlags;
+ uint32_t sizeOrCount;
+};
+
+/*
+ * The 31th bit indicates only one or multiple physical address inside
+ * the request buffer. If it is set, the index locates a single physical addr
+ * inside the request buffer, and `sizeOrCount` is the size of the memory being
+ * shared at that physical address.
+ * Otherwise, the index locates an array of {start, len} pairs (a
+ * "scatter/gather list"), and `sizeOrCount` gives the number of entries in
+ * that array.
+ *
+ * The 30th bit indicates 64 or 32bit address; when it is set, physical addr
+ * and scatter gather entry sizes are 64-bit values. Otherwise, 32-bit values.
+ *
+ * The bits [0:29] of `indexAndFlags` hold an offset into the request buffer.
+ */
+#define SGLISTINFO_SET_INDEX_FLAG(c, s, i) \
+ ((uint32_t)(((c & 1) << 31) | ((s & 1) << 30) | (i & 0x3fffffff)))
+
+#define SGLISTINFO_TABLE_SIZE (sizeof(struct sglist_info) * MAX_ION_FD)
+
+#define FEATURE_ID_WHITELIST 15 /*whitelist feature id*/
+
+#define MAKE_WHITELIST_VERSION(major, minor, patch) \
+ (((major & 0x3FF) << 22) | ((minor & 0x3FF) << 12) | (patch & 0xFFF))
+
struct qseecom_registered_listener_list {
struct list_head list;
struct qseecom_register_listener_req svc;
@@ -145,6 +174,8 @@ struct qseecom_registered_listener_list {
bool listener_in_use;
/* wq for thread blocked on this listener*/
wait_queue_head_t listener_block_app_wq;
+ struct sglist_info sglistinfo_ptr[MAX_ION_FD];
+ uint32_t sglist_cnt;
};
struct qseecom_registered_app_list {
@@ -268,30 +299,6 @@ struct qseecom_listener_handle {
static struct qseecom_control qseecom;
-struct sglist_info {
- uint32_t indexAndFlags;
- uint32_t sizeOrCount;
-};
-
-/*
- * The 31th bit indicates only one or multiple physical address inside
- * the request buffer. If it is set, the index locates a single physical addr
- * inside the request buffer, and `sizeOrCount` is the size of the memory being
- * shared at that physical address.
- * Otherwise, the index locates an array of {start, len} pairs (a
- * "scatter/gather list"), and `sizeOrCount` gives the number of entries in
- * that array.
- *
- * The 30th bit indicates 64 or 32bit address; when it is set, physical addr
- * and scatter gather entry sizes are 64-bit values. Otherwise, 32-bit values.
- *
- * The bits [0:29] of `indexAndFlags` hold an offset into the request buffer.
- */
-#define SGLISTINFO_SET_INDEX_FLAG(c, s, i) \
- ((uint32_t)(((c & 1) << 31) | ((s & 1) << 30) | (i & 0x3fffffff)))
-
-#define SGLISTINFO_TABLE_SIZE (sizeof(struct sglist_info) * MAX_ION_FD)
-
struct qseecom_dev_handle {
enum qseecom_client_handle_type type;
union {
@@ -305,8 +312,9 @@ struct qseecom_dev_handle {
bool perf_enabled;
bool fast_load_enabled;
enum qseecom_bandwidth_request_mode mode;
- struct sglist_info *sglistinfo_ptr;
+ struct sglist_info sglistinfo_ptr[MAX_ION_FD];
uint32_t sglist_cnt;
+ bool use_legacy_cmd;
};
struct qseecom_key_id_usage_desc {
@@ -584,6 +592,34 @@ static int qseecom_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id,
ret = scm_call2(smc_id, &desc);
break;
}
+ case QSEOS_LISTENER_DATA_RSP_COMMAND_WHITELIST: {
+ struct qseecom_client_listener_data_irsp *req;
+ struct qseecom_client_listener_data_64bit_irsp *req_64;
+
+ smc_id =
+ TZ_OS_LISTENER_RESPONSE_HANDLER_WITH_WHITELIST_ID;
+ desc.arginfo =
+ TZ_OS_LISTENER_RESPONSE_HANDLER_WITH_WHITELIST_PARAM_ID;
+ if (qseecom.qsee_version < QSEE_VERSION_40) {
+ req =
+ (struct qseecom_client_listener_data_irsp *)
+ req_buf;
+ desc.args[0] = req->listener_id;
+ desc.args[1] = req->status;
+ desc.args[2] = req->sglistinfo_ptr;
+ desc.args[3] = req->sglistinfo_len;
+ } else {
+ req_64 =
+ (struct qseecom_client_listener_data_64bit_irsp *)
+ req_buf;
+ desc.args[0] = req_64->listener_id;
+ desc.args[1] = req_64->status;
+ desc.args[2] = req_64->sglistinfo_ptr;
+ desc.args[3] = req_64->sglistinfo_len;
+ }
+ ret = scm_call2(smc_id, &desc);
+ break;
+ }
case QSEOS_LOAD_EXTERNAL_ELF_COMMAND: {
struct qseecom_load_app_ireq *req;
struct qseecom_load_app_64bit_ireq *req_64bit;
@@ -1065,6 +1101,10 @@ static int __qseecom_set_sb_memory(struct qseecom_registered_listener_list *svc,
}
/* Populate the structure for sending scm call to load image */
svc->sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt, svc->ihandle);
+ if (IS_ERR_OR_NULL(svc->sb_virt)) {
+ pr_err("ION memory mapping for listener shared buffer failed\n");
+ return -ENOMEM;
+ }
svc->sb_phys = (phys_addr_t)pa;
if (qseecom.qsee_version < QSEE_VERSION_40) {
@@ -1124,7 +1164,7 @@ static int qseecom_register_listener(struct qseecom_dev_handle *data,
return -EBUSY;
}
- new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+ new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
if (!new_entry) {
pr_err("kmalloc failed\n");
return -ENOMEM;
@@ -1522,6 +1562,10 @@ static int qseecom_set_client_mem_param(struct qseecom_dev_handle *data,
/* Populate the structure for sending scm call to load image */
data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
data->client.ihandle);
+ if (IS_ERR_OR_NULL(data->client.sb_virt)) {
+ pr_err("ION memory mapping for client shared buf failed\n");
+ return -ENOMEM;
+ }
data->client.sb_phys = (phys_addr_t)pa;
data->client.sb_length = req.sb_len;
data->client.user_virt_sb_base = (uintptr_t)req.virt_sb_base;
@@ -1585,6 +1629,16 @@ static int __qseecom_qseos_fail_return_resp_tz(struct qseecom_dev_handle *data,
return ret;
}
+static void __qseecom_clean_listener_sglistinfo(
+ struct qseecom_registered_listener_list *ptr_svc)
+{
+ if (ptr_svc->sglist_cnt) {
+ memset(ptr_svc->sglistinfo_ptr, 0,
+ SGLISTINFO_TABLE_SIZE);
+ ptr_svc->sglist_cnt = 0;
+ }
+}
+
static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
struct qseecom_command_scm_resp *resp)
{
@@ -1593,9 +1647,14 @@ static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
uint32_t lstnr;
unsigned long flags;
struct qseecom_client_listener_data_irsp send_data_rsp;
+ struct qseecom_client_listener_data_64bit_irsp send_data_rsp_64bit;
struct qseecom_registered_listener_list *ptr_svc = NULL;
sigset_t new_sigset;
sigset_t old_sigset;
+ uint32_t status;
+ void *cmd_buf = NULL;
+ size_t cmd_len;
+ struct sglist_info *table = NULL;
while (resp->result == QSEOS_RESULT_INCOMPLETE) {
lstnr = resp->data;
@@ -1669,15 +1728,42 @@ static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
pr_err("Abort clnt %d waiting on lstnr svc %d, ret %d",
data->client.app_id, lstnr, ret);
rc = -ENODEV;
- send_data_rsp.status = QSEOS_RESULT_FAILURE;
+ status = QSEOS_RESULT_FAILURE;
} else {
- send_data_rsp.status = QSEOS_RESULT_SUCCESS;
+ status = QSEOS_RESULT_SUCCESS;
}
qseecom.send_resp_flag = 0;
ptr_svc->send_resp_flag = 0;
- send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND;
- send_data_rsp.listener_id = lstnr;
+ table = ptr_svc->sglistinfo_ptr;
+ if (qseecom.qsee_version < QSEE_VERSION_40) {
+ send_data_rsp.listener_id = lstnr;
+ send_data_rsp.status = status;
+ send_data_rsp.sglistinfo_ptr =
+ (uint32_t)virt_to_phys(table);
+ send_data_rsp.sglistinfo_len =
+ SGLISTINFO_TABLE_SIZE;
+ dmac_flush_range((void *)table,
+ (void *)table + SGLISTINFO_TABLE_SIZE);
+ cmd_buf = (void *)&send_data_rsp;
+ cmd_len = sizeof(send_data_rsp);
+ } else {
+ send_data_rsp_64bit.listener_id = lstnr;
+ send_data_rsp_64bit.status = status;
+ send_data_rsp_64bit.sglistinfo_ptr =
+ virt_to_phys(table);
+ send_data_rsp_64bit.sglistinfo_len =
+ SGLISTINFO_TABLE_SIZE;
+ dmac_flush_range((void *)table,
+ (void *)table + SGLISTINFO_TABLE_SIZE);
+ cmd_buf = (void *)&send_data_rsp_64bit;
+ cmd_len = sizeof(send_data_rsp_64bit);
+ }
+ if (qseecom.whitelist_support == false)
+ *(uint32_t *)cmd_buf = QSEOS_LISTENER_DATA_RSP_COMMAND;
+ else
+ *(uint32_t *)cmd_buf =
+ QSEOS_LISTENER_DATA_RSP_COMMAND_WHITELIST;
if (ptr_svc)
msm_ion_do_cache_op(qseecom.ion_clnt, ptr_svc->ihandle,
ptr_svc->sb_virt, ptr_svc->sb_length,
@@ -1687,10 +1773,9 @@ static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
__qseecom_enable_clk(CLK_QSEE);
ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1,
- (const void *)&send_data_rsp,
- sizeof(send_data_rsp), resp,
- sizeof(*resp));
+ cmd_buf, cmd_len, resp, sizeof(*resp));
ptr_svc->listener_in_use = false;
+ __qseecom_clean_listener_sglistinfo(ptr_svc);
if (ret) {
pr_err("scm_call() failed with err: %d (app_id = %d)\n",
ret, data->client.app_id);
@@ -1818,9 +1903,14 @@ static int __qseecom_reentrancy_process_incomplete_cmd(
uint32_t lstnr;
unsigned long flags;
struct qseecom_client_listener_data_irsp send_data_rsp;
+ struct qseecom_client_listener_data_64bit_irsp send_data_rsp_64bit;
struct qseecom_registered_listener_list *ptr_svc = NULL;
sigset_t new_sigset;
sigset_t old_sigset;
+ uint32_t status;
+ void *cmd_buf = NULL;
+ size_t cmd_len;
+ struct sglist_info *table = NULL;
while (ret == 0 && rc == 0 && resp->result == QSEOS_RESULT_INCOMPLETE) {
lstnr = resp->data;
@@ -1883,13 +1973,38 @@ static int __qseecom_reentrancy_process_incomplete_cmd(
pr_err("Abort clnt %d waiting on lstnr svc %d, ret %d",
data->client.app_id, lstnr, ret);
rc = -ENODEV;
- send_data_rsp.status = QSEOS_RESULT_FAILURE;
+ status = QSEOS_RESULT_FAILURE;
} else {
- send_data_rsp.status = QSEOS_RESULT_SUCCESS;
+ status = QSEOS_RESULT_SUCCESS;
}
-
- send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND;
- send_data_rsp.listener_id = lstnr;
+ table = ptr_svc->sglistinfo_ptr;
+ if (qseecom.qsee_version < QSEE_VERSION_40) {
+ send_data_rsp.listener_id = lstnr;
+ send_data_rsp.status = status;
+ send_data_rsp.sglistinfo_ptr =
+ (uint32_t)virt_to_phys(table);
+ send_data_rsp.sglistinfo_len = SGLISTINFO_TABLE_SIZE;
+ dmac_flush_range((void *)table,
+ (void *)table + SGLISTINFO_TABLE_SIZE);
+ cmd_buf = (void *)&send_data_rsp;
+ cmd_len = sizeof(send_data_rsp);
+ } else {
+ send_data_rsp_64bit.listener_id = lstnr;
+ send_data_rsp_64bit.status = status;
+ send_data_rsp_64bit.sglistinfo_ptr =
+ virt_to_phys(table);
+ send_data_rsp_64bit.sglistinfo_len =
+ SGLISTINFO_TABLE_SIZE;
+ dmac_flush_range((void *)table,
+ (void *)table + SGLISTINFO_TABLE_SIZE);
+ cmd_buf = (void *)&send_data_rsp_64bit;
+ cmd_len = sizeof(send_data_rsp_64bit);
+ }
+ if (qseecom.whitelist_support == false)
+ *(uint32_t *)cmd_buf = QSEOS_LISTENER_DATA_RSP_COMMAND;
+ else
+ *(uint32_t *)cmd_buf =
+ QSEOS_LISTENER_DATA_RSP_COMMAND_WHITELIST;
if (ptr_svc)
msm_ion_do_cache_op(qseecom.ion_clnt, ptr_svc->ihandle,
ptr_svc->sb_virt, ptr_svc->sb_length,
@@ -1899,11 +2014,9 @@ static int __qseecom_reentrancy_process_incomplete_cmd(
__qseecom_enable_clk(CLK_QSEE);
ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1,
- (const void *)&send_data_rsp,
- sizeof(send_data_rsp), resp,
- sizeof(*resp));
-
+ cmd_buf, cmd_len, resp, sizeof(*resp));
ptr_svc->listener_in_use = false;
+ __qseecom_clean_listener_sglistinfo(ptr_svc);
wake_up_interruptible(&ptr_svc->listener_block_app_wq);
if (ret) {
@@ -2071,6 +2184,7 @@ static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
struct qseecom_load_app_64bit_ireq load_req_64bit;
void *cmd_buf = NULL;
size_t cmd_len;
+ bool first_time = false;
/* Copy the relevant information needed for loading the image */
if (copy_from_user(&load_img_req,
@@ -2142,6 +2256,7 @@ static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
&qseecom.registered_app_list_lock, flags);
ret = 0;
} else {
+ first_time = true;
pr_warn("App (%s) does'nt exist, loading apps for first time\n",
(char *)(load_img_req.img_name));
/* Get the handle of the shared fd */
@@ -2273,8 +2388,15 @@ static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
load_img_req.app_id = app_id;
if (copy_to_user(argp, &load_img_req, sizeof(load_img_req))) {
pr_err("copy_to_user failed\n");
- kzfree(entry);
ret = -EFAULT;
+ if (first_time == true) {
+ spin_lock_irqsave(
+ &qseecom.registered_app_list_lock, flags);
+ list_del(&entry->list);
+ spin_unlock_irqrestore(
+ &qseecom.registered_app_list_lock, flags);
+ kzfree(entry);
+ }
}
loadapp_err:
@@ -2902,7 +3024,7 @@ static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
cmd_len = sizeof(struct qseecom_client_send_data_64bit_ireq);
}
- if (qseecom.whitelist_support == false)
+ if (qseecom.whitelist_support == false || data->use_legacy_cmd == true)
*(uint32_t *)cmd_buf = QSEOS_CLIENT_SEND_DATA_COMMAND;
else
*(uint32_t *)cmd_buf = QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST;
@@ -3007,6 +3129,8 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup,
struct qseecom_send_modfd_cmd_req *req = NULL;
struct qseecom_send_modfd_listener_resp *lstnr_resp = NULL;
struct qseecom_registered_listener_list *this_lstnr = NULL;
+ uint32_t offset;
+ struct sg_table *sg_ptr;
if ((data->type != QSEECOM_LISTENER_SERVICE) &&
(data->type != QSEECOM_CLIENT_APP))
@@ -3028,7 +3152,6 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup,
}
for (i = 0; i < MAX_ION_FD; i++) {
- struct sg_table *sg_ptr = NULL;
if ((data->type != QSEECOM_LISTENER_SERVICE) &&
(req->ifd_data[i].fd > 0)) {
ihandle = ion_import_dma_buf(qseecom.ion_clnt,
@@ -3170,14 +3293,25 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup,
ihandle, NULL, len,
ION_IOC_CLEAN_INV_CACHES);
if (data->type == QSEECOM_CLIENT_APP) {
+ offset = req->ifd_data[i].cmd_buf_offset;
data->sglistinfo_ptr[i].indexAndFlags =
SGLISTINFO_SET_INDEX_FLAG(
- (sg_ptr->nents == 1), 0,
- req->ifd_data[i].cmd_buf_offset);
+ (sg_ptr->nents == 1), 0, offset);
data->sglistinfo_ptr[i].sizeOrCount =
(sg_ptr->nents == 1) ?
sg->length : sg_ptr->nents;
data->sglist_cnt = i + 1;
+ } else {
+ offset = (lstnr_resp->ifd_data[i].cmd_buf_offset
+ + (uintptr_t)lstnr_resp->resp_buf_ptr -
+ (uintptr_t)this_lstnr->sb_virt);
+ this_lstnr->sglistinfo_ptr[i].indexAndFlags =
+ SGLISTINFO_SET_INDEX_FLAG(
+ (sg_ptr->nents == 1), 0, offset);
+ this_lstnr->sglistinfo_ptr[i].sizeOrCount =
+ (sg_ptr->nents == 1) ?
+ sg->length : sg_ptr->nents;
+ this_lstnr->sglist_cnt = i + 1;
}
}
/* Deallocate the handle */
@@ -3250,6 +3384,8 @@ static int __qseecom_update_cmd_buf_64(void *msg, bool cleanup,
struct qseecom_send_modfd_cmd_req *req = NULL;
struct qseecom_send_modfd_listener_resp *lstnr_resp = NULL;
struct qseecom_registered_listener_list *this_lstnr = NULL;
+ uint32_t offset;
+ struct sg_table *sg_ptr;
if ((data->type != QSEECOM_LISTENER_SERVICE) &&
(data->type != QSEECOM_CLIENT_APP))
@@ -3271,7 +3407,6 @@ static int __qseecom_update_cmd_buf_64(void *msg, bool cleanup,
}
for (i = 0; i < MAX_ION_FD; i++) {
- struct sg_table *sg_ptr = NULL;
if ((data->type != QSEECOM_LISTENER_SERVICE) &&
(req->ifd_data[i].fd > 0)) {
ihandle = ion_import_dma_buf(qseecom.ion_clnt,
@@ -3388,14 +3523,25 @@ cleanup:
ihandle, NULL, len,
ION_IOC_CLEAN_INV_CACHES);
if (data->type == QSEECOM_CLIENT_APP) {
+ offset = req->ifd_data[i].cmd_buf_offset;
data->sglistinfo_ptr[i].indexAndFlags =
SGLISTINFO_SET_INDEX_FLAG(
- (sg_ptr->nents == 1), 1,
- req->ifd_data[i].cmd_buf_offset);
+ (sg_ptr->nents == 1), 1, offset);
data->sglistinfo_ptr[i].sizeOrCount =
(sg_ptr->nents == 1) ?
sg->length : sg_ptr->nents;
data->sglist_cnt = i + 1;
+ } else {
+ offset = (lstnr_resp->ifd_data[i].cmd_buf_offset
+ + (uintptr_t)lstnr_resp->resp_buf_ptr -
+ (uintptr_t)this_lstnr->sb_virt);
+ this_lstnr->sglistinfo_ptr[i].indexAndFlags =
+ SGLISTINFO_SET_INDEX_FLAG(
+ (sg_ptr->nents == 1), 1, offset);
+ this_lstnr->sglistinfo_ptr[i].sizeOrCount =
+ (sg_ptr->nents == 1) ?
+ sg->length : sg_ptr->nents;
+ this_lstnr->sglist_cnt = i + 1;
}
}
/* Deallocate the handle */
@@ -3591,7 +3737,7 @@ static bool __qseecom_is_fw_image_valid(const struct firmware *fw_entry)
return true;
}
-static int __qseecom_get_fw_size(char *appname, uint32_t *fw_size,
+static int __qseecom_get_fw_size(const char *appname, uint32_t *fw_size,
uint32_t *app_arch)
{
int ret = -1;
@@ -3629,14 +3775,21 @@ static int __qseecom_get_fw_size(char *appname, uint32_t *fw_size,
}
pr_debug("QSEE %s app, arch %u\n", appname, *app_arch);
release_firmware(fw_entry);
+ fw_entry = NULL;
for (i = 0; i < num_images; i++) {
memset(fw_name, 0, sizeof(fw_name));
snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", appname, i);
ret = request_firmware(&fw_entry, fw_name, qseecom.pdev);
if (ret)
goto err;
+ if (*fw_size > U32_MAX - fw_entry->size) {
+ pr_err("QSEE %s app file size overflow\n", appname);
+ ret = -EINVAL;
+ goto err;
+ }
*fw_size += fw_entry->size;
release_firmware(fw_entry);
+ fw_entry = NULL;
}
return ret;
@@ -3647,8 +3800,9 @@ err:
return ret;
}
-static int __qseecom_get_fw_data(char *appname, u8 *img_data,
- struct qseecom_load_app_ireq *load_req)
+static int __qseecom_get_fw_data(const char *appname, u8 *img_data,
+ uint32_t fw_size,
+ struct qseecom_load_app_ireq *load_req)
{
int ret = -1;
int i = 0, rc = 0;
@@ -3668,6 +3822,12 @@ static int __qseecom_get_fw_data(char *appname, u8 *img_data,
}
load_req->img_len = fw_entry->size;
+ if (load_req->img_len > fw_size) {
+ pr_err("app %s size %zu is larger than buf size %u\n",
+ appname, fw_entry->size, fw_size);
+ ret = -EINVAL;
+ goto err;
+ }
memcpy(img_data_ptr, fw_entry->data, fw_entry->size);
img_data_ptr = img_data_ptr + fw_entry->size;
load_req->mdt_len = fw_entry->size; /*Get MDT LEN*/
@@ -3686,6 +3846,7 @@ static int __qseecom_get_fw_data(char *appname, u8 *img_data,
goto err;
}
release_firmware(fw_entry);
+ fw_entry = NULL;
for (i = 0; i < num_images; i++) {
snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", appname, i);
ret = request_firmware(&fw_entry, fw_name, qseecom.pdev);
@@ -3693,10 +3854,17 @@ static int __qseecom_get_fw_data(char *appname, u8 *img_data,
pr_err("Failed to locate blob %s\n", fw_name);
goto err;
}
+ if ((fw_entry->size > U32_MAX - load_req->img_len) ||
+ (fw_entry->size + load_req->img_len > fw_size)) {
+ pr_err("Invalid file size for %s\n", fw_name);
+ ret = -EINVAL;
+ goto err;
+ }
memcpy(img_data_ptr, fw_entry->data, fw_entry->size);
img_data_ptr = img_data_ptr + fw_entry->size;
load_req->img_len += fw_entry->size;
release_firmware(fw_entry);
+ fw_entry = NULL;
}
return ret;
err:
@@ -3801,7 +3969,7 @@ static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname)
if (ret)
return ret;
- ret = __qseecom_get_fw_data(appname, img_data, &load_req);
+ ret = __qseecom_get_fw_data(appname, img_data, fw_size, &load_req);
if (ret) {
ret = -EIO;
goto exit_free_img_data;
@@ -3922,7 +4090,7 @@ static int qseecom_load_commonlib_image(struct qseecom_dev_handle *data,
if (ret)
return -EIO;
- ret = __qseecom_get_fw_data(cmnlib_name, img_data, &load_req);
+ ret = __qseecom_get_fw_data(cmnlib_name, img_data, fw_size, &load_req);
if (ret) {
ret = -EIO;
goto exit_free_img_data;
@@ -4092,21 +4260,12 @@ int qseecom_start_app(struct qseecom_handle **handle,
data->client.user_virt_sb_base = 0;
data->client.ihandle = NULL;
- /* Allocate sglistinfo buffer for kernel client */
- data->sglistinfo_ptr = kzalloc(SGLISTINFO_TABLE_SIZE, GFP_KERNEL);
- if (!(data->sglistinfo_ptr)) {
- kfree(data);
- kfree(*handle);
- *handle = NULL;
- return -ENOMEM;
- }
init_waitqueue_head(&data->abort_wq);
data->client.ihandle = ion_alloc(qseecom.ion_clnt, size, 4096,
ION_HEAP(ION_QSECOM_HEAP_ID), 0);
if (IS_ERR_OR_NULL(data->client.ihandle)) {
pr_err("Ion client could not retrieve the handle\n");
- kfree(data->sglistinfo_ptr);
kfree(data);
kfree(*handle);
*handle = NULL;
@@ -4181,6 +4340,11 @@ int qseecom_start_app(struct qseecom_handle **handle,
/* Populate the structure for sending scm call to load image */
data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
data->client.ihandle);
+ if (IS_ERR_OR_NULL(data->client.sb_virt)) {
+ pr_err("ION memory mapping for client shared buf failed\n");
+ ret = -ENOMEM;
+ goto err;
+ }
data->client.user_virt_sb_base = (uintptr_t)data->client.sb_virt;
data->client.sb_phys = (phys_addr_t)pa;
(*handle)->dev = (void *)data;
@@ -4204,7 +4368,6 @@ int qseecom_start_app(struct qseecom_handle **handle,
return 0;
err:
- kfree(data->sglistinfo_ptr);
kfree(data);
kfree(*handle);
*handle = NULL;
@@ -4252,7 +4415,6 @@ int qseecom_shutdown_app(struct qseecom_handle **handle)
mutex_unlock(&app_access_lock);
if (ret == 0) {
- kzfree(data->sglistinfo_ptr);
kzfree(data);
kzfree(*handle);
kzfree(kclient);
@@ -4318,8 +4480,11 @@ int qseecom_send_command(struct qseecom_handle *handle, void *send_buf,
}
perf_enabled = true;
}
+ if (!strcmp(data->client.app_name, "securemm"))
+ data->use_legacy_cmd = true;
ret = __qseecom_send_cmd(data, &req);
+ data->use_legacy_cmd = false;
if (qseecom.support_bus_scaling)
__qseecom_add_bw_scale_down_timer(
QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
@@ -6995,6 +7160,7 @@ long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg)
wake_up_all(&data->abort_wq);
if (ret)
pr_err("failed qseecom_send_mod_resp: %d\n", ret);
+ __qseecom_clean_data_sglistinfo(data);
break;
}
case QSEECOM_QTEEC_IOCTL_OPEN_SESSION_REQ: {
@@ -7144,12 +7310,6 @@ static int qseecom_open(struct inode *inode, struct file *file)
data->mode = INACTIVE;
init_waitqueue_head(&data->abort_wq);
atomic_set(&data->ioctl_count, 0);
-
- data->sglistinfo_ptr = kzalloc(SGLISTINFO_TABLE_SIZE, GFP_KERNEL);
- if (!(data->sglistinfo_ptr)) {
- kzfree(data);
- return -ENOMEM;
- }
return ret;
}
@@ -7204,7 +7364,6 @@ static int qseecom_release(struct inode *inode, struct file *file)
if (data->perf_enabled == true)
qsee_disable_clock_vote(data, CLK_DFAB);
}
- kfree(data->sglistinfo_ptr);
kfree(data);
return ret;
@@ -7953,73 +8112,14 @@ out:
}
/*
- * Check if whitelist feature is supported by making a test scm_call
- * to send a whitelist command to an invalid app ID 0
+ * Check whitelist feature, and if TZ feature version is < 1.0.0,
+ * then whitelist feature is not supported.
*/
static int qseecom_check_whitelist_feature(void)
{
- struct qseecom_client_send_data_ireq send_data_req = {0};
- struct qseecom_client_send_data_64bit_ireq send_data_req_64bit = {0};
- struct qseecom_command_scm_resp resp;
- uint32_t buf_size = 128;
- void *buf = NULL;
- void *cmd_buf = NULL;
- size_t cmd_len;
- int ret = 0;
- phys_addr_t pa;
+ int version = scm_get_feat_version(FEATURE_ID_WHITELIST);
- buf = kzalloc(buf_size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- pa = virt_to_phys(buf);
- if (qseecom.qsee_version < QSEE_VERSION_40) {
- send_data_req.qsee_cmd_id =
- QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST;
- send_data_req.app_id = 0;
- send_data_req.req_ptr = (uint32_t)pa;
- send_data_req.req_len = buf_size;
- send_data_req.rsp_ptr = (uint32_t)pa;
- send_data_req.rsp_len = buf_size;
- send_data_req.sglistinfo_ptr = (uint32_t)pa;
- send_data_req.sglistinfo_len = buf_size;
- cmd_buf = (void *)&send_data_req;
- cmd_len = sizeof(struct qseecom_client_send_data_ireq);
- } else {
- send_data_req_64bit.qsee_cmd_id =
- QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST;
- send_data_req_64bit.app_id = 0;
- send_data_req_64bit.req_ptr = (uint64_t)pa;
- send_data_req_64bit.req_len = buf_size;
- send_data_req_64bit.rsp_ptr = (uint64_t)pa;
- send_data_req_64bit.rsp_len = buf_size;
- send_data_req_64bit.sglistinfo_ptr = (uint64_t)pa;
- send_data_req_64bit.sglistinfo_len = buf_size;
- cmd_buf = (void *)&send_data_req_64bit;
- cmd_len = sizeof(struct qseecom_client_send_data_64bit_ireq);
- }
- ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1,
- cmd_buf, cmd_len,
- &resp, sizeof(resp));
-/*
- * If this cmd exists and whitelist is supported, scm_call return -2 (scm
- * driver remap it to -EINVAL) and resp.result 0xFFFFFFED(-19); Otherwise,
- * scm_call return -1 (remap to -EIO).
- */
- if (ret == -EIO) {
- qseecom.whitelist_support = false;
- ret = 0;
- } else if (ret == -EINVAL &&
- resp.result == QSEOS_RESULT_FAIL_SEND_CMD_NO_THREAD) {
- qseecom.whitelist_support = true;
- ret = 0;
- } else {
- pr_info("Check whitelist with ret = %d, result = 0x%x\n",
- ret, resp.result);
- qseecom.whitelist_support = false;
- ret = 0;
- }
- kfree(buf);
- return ret;
+ return version >= MAKE_WHITELIST_VERSION(1, 0, 0);
}
static int qseecom_probe(struct platform_device *pdev)
@@ -8270,11 +8370,7 @@ static int qseecom_probe(struct platform_device *pdev)
qseecom.qsee_perf_client = msm_bus_scale_register_client(
qseecom_platform_support);
- rc = qseecom_check_whitelist_feature();
- if (rc) {
- rc = -EINVAL;
- goto exit_destroy_ion_client;
- }
+ qseecom.whitelist_support = qseecom_check_whitelist_feature();
pr_warn("qseecom.whitelist_support = %d\n",
qseecom.whitelist_support);
diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c
deleted file mode 100644
index 185c69c9738a..000000000000
--- a/drivers/misc/uid_stat.c
+++ /dev/null
@@ -1,153 +0,0 @@
-/* drivers/misc/uid_stat.c
- *
- * Copyright (C) 2008 - 2009 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/atomic.h>
-
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/stat.h>
-#include <linux/uid_stat.h>
-#include <net/activity_stats.h>
-
-static DEFINE_SPINLOCK(uid_lock);
-static LIST_HEAD(uid_list);
-static struct proc_dir_entry *parent;
-
-struct uid_stat {
- struct list_head link;
- uid_t uid;
- atomic_t tcp_rcv;
- atomic_t tcp_snd;
-};
-
-static struct uid_stat *find_uid_stat(uid_t uid) {
- struct uid_stat *entry;
-
- list_for_each_entry(entry, &uid_list, link) {
- if (entry->uid == uid) {
- return entry;
- }
- }
- return NULL;
-}
-
-static int uid_stat_atomic_int_show(struct seq_file *m, void *v)
-{
- unsigned int bytes;
- atomic_t *counter = m->private;
-
- bytes = (unsigned int) (atomic_read(counter) + INT_MIN);
- seq_printf(m, "%u\n", bytes);
- return seq_has_overflowed(m) ? -ENOSPC : 0;
-}
-
-static int uid_stat_read_atomic_int_open(struct inode *inode, struct file *file)
-{
- return single_open(file, uid_stat_atomic_int_show, PDE_DATA(inode));
-}
-
-static const struct file_operations uid_stat_read_atomic_int_fops = {
- .open = uid_stat_read_atomic_int_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-
-/* Create a new entry for tracking the specified uid. */
-static struct uid_stat *create_stat(uid_t uid) {
- struct uid_stat *new_uid;
- /* Create the uid stat struct and append it to the list. */
- new_uid = kmalloc(sizeof(struct uid_stat), GFP_ATOMIC);
- if (!new_uid)
- return NULL;
-
- new_uid->uid = uid;
- /* Counters start at INT_MIN, so we can track 4GB of network traffic. */
- atomic_set(&new_uid->tcp_rcv, INT_MIN);
- atomic_set(&new_uid->tcp_snd, INT_MIN);
-
- list_add_tail(&new_uid->link, &uid_list);
- return new_uid;
-}
-
-static void create_stat_proc(struct uid_stat *new_uid)
-{
- char uid_s[32];
- struct proc_dir_entry *entry;
- sprintf(uid_s, "%d", new_uid->uid);
- entry = proc_mkdir(uid_s, parent);
-
- /* Keep reference to uid_stat so we know what uid to read stats from. */
- proc_create_data("tcp_snd", S_IRUGO, entry,
- &uid_stat_read_atomic_int_fops, &new_uid->tcp_snd);
-
- proc_create_data("tcp_rcv", S_IRUGO, entry,
- &uid_stat_read_atomic_int_fops, &new_uid->tcp_rcv);
-}
-
-static struct uid_stat *find_or_create_uid_stat(uid_t uid)
-{
- struct uid_stat *entry;
- unsigned long flags;
- spin_lock_irqsave(&uid_lock, flags);
- entry = find_uid_stat(uid);
- if (entry) {
- spin_unlock_irqrestore(&uid_lock, flags);
- return entry;
- }
- entry = create_stat(uid);
- spin_unlock_irqrestore(&uid_lock, flags);
- if (entry)
- create_stat_proc(entry);
- return entry;
-}
-
-int uid_stat_tcp_snd(uid_t uid, int size) {
- struct uid_stat *entry;
- activity_stats_update();
- entry = find_or_create_uid_stat(uid);
- if (!entry)
- return -1;
- atomic_add(size, &entry->tcp_snd);
- return 0;
-}
-
-int uid_stat_tcp_rcv(uid_t uid, int size) {
- struct uid_stat *entry;
- activity_stats_update();
- entry = find_or_create_uid_stat(uid);
- if (!entry)
- return -1;
- atomic_add(size, &entry->tcp_rcv);
- return 0;
-}
-
-static int __init uid_stat_init(void)
-{
- parent = proc_mkdir("uid_stat", NULL);
- if (!parent) {
- pr_err("uid_stat: failed to create proc entry\n");
- return -1;
- }
- return 0;
-}
-
-__initcall(uid_stat_init);
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 7547463928d6..6eee4aa0e574 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -4407,11 +4407,12 @@ static const struct mmc_fixup blk_fixups[] =
add_quirk_mmc, MMC_QUIRK_CMDQ_EMPTY_BEFORE_DCMD),
/*
- * Some Micron MMC cards needs longer data read timeout than
- * indicated in CSD.
+ * Some MMC cards need longer data read timeout than indicated in CSD.
*/
MMC_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc,
MMC_QUIRK_LONG_READ_TIME),
+ MMC_FIXUP("008GE0", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_LONG_READ_TIME),
/*
* Some Samsung MMC cards need longer data read timeout than
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 3762f698e1ee..828d2b85f6e4 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1970,7 +1970,7 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
/*
* Some cards require longer data read timeout than indicated in CSD.
* Address this by setting the read timeout to a "reasonably high"
- * value. For the cards tested, 300ms has proven enough. If necessary,
+ * value. For the cards tested, 600ms has proven enough. If necessary,
* this value can be increased if other problematic cards require this.
* Certain Hynix 5.x cards giving read timeout even with 300ms.
* Increasing further to max value (4s).
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 5489f243a682..89288bd1eaa4 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -348,6 +348,9 @@ static void mmc_manage_gp_partitions(struct mmc_card *card, u8 *ext_csd)
}
}
+/* Minimum partition switch timeout in milliseconds */
+#define MMC_MIN_PART_SWITCH_TIME 300
+
/*
* Decode extended CSD.
*/
@@ -412,6 +415,10 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
/* EXT_CSD value is in units of 10ms, but we store in ms */
card->ext_csd.part_time = 10 * ext_csd[EXT_CSD_PART_SWITCH_TIME];
+ /* Some eMMC set the value too low so set a minimum */
+ if (card->ext_csd.part_time &&
+ card->ext_csd.part_time < MMC_MIN_PART_SWITCH_TIME)
+ card->ext_csd.part_time = MMC_MIN_PART_SWITCH_TIME;
/* Sleep / awake timeout in 100ns units */
if (sa_shift > 0 && sa_shift <= 0x17)
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
index a5cda926d38e..8aea3fa6938b 100644
--- a/drivers/mmc/host/sdhci-acpi.c
+++ b/drivers/mmc/host/sdhci-acpi.c
@@ -233,7 +233,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
.chip = &sdhci_acpi_chip_int,
.caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR |
- MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY,
+ MMC_CAP_WAIT_WHILE_BUSY,
.caps2 = MMC_CAP2_HC_ERASE_SZ,
.flags = SDHCI_ACPI_RUNTIME_PM,
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
@@ -248,7 +248,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
.caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD |
- MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY,
+ MMC_CAP_WAIT_WHILE_BUSY,
.flags = SDHCI_ACPI_RUNTIME_PM,
.pm_caps = MMC_PM_KEEP_POWER,
.probe_slot = sdhci_acpi_sdio_probe_slot,
@@ -260,7 +260,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
SDHCI_QUIRK2_STOP_WITH_TC,
- .caps = MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY,
+ .caps = MMC_CAP_WAIT_WHILE_BUSY,
.probe_slot = sdhci_acpi_sd_probe_slot,
};
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 610154836d79..5ebe6eb6b89e 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -361,7 +361,6 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
{
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR |
- MMC_CAP_BUS_WIDTH_TEST |
MMC_CAP_WAIT_WHILE_BUSY;
slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ;
slot->hw_reset = sdhci_pci_int_hw_reset;
@@ -377,15 +376,13 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
{
slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE |
- MMC_CAP_BUS_WIDTH_TEST |
MMC_CAP_WAIT_WHILE_BUSY;
return 0;
}
static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
{
- slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST |
- MMC_CAP_WAIT_WHILE_BUSY;
+ slot->host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
slot->cd_con_id = NULL;
slot->cd_idx = 0;
slot->cd_override_level = true;
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c
index 12c6190c6e33..4a07ba1195b5 100644
--- a/drivers/mtd/nand/brcmnand/brcmnand.c
+++ b/drivers/mtd/nand/brcmnand/brcmnand.c
@@ -309,6 +309,36 @@ static const u16 brcmnand_regs_v60[] = {
[BRCMNAND_FC_BASE] = 0x400,
};
+/* BRCMNAND v7.1 */
+static const u16 brcmnand_regs_v71[] = {
+ [BRCMNAND_CMD_START] = 0x04,
+ [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
+ [BRCMNAND_CMD_ADDRESS] = 0x0c,
+ [BRCMNAND_INTFC_STATUS] = 0x14,
+ [BRCMNAND_CS_SELECT] = 0x18,
+ [BRCMNAND_CS_XOR] = 0x1c,
+ [BRCMNAND_LL_OP] = 0x20,
+ [BRCMNAND_CS0_BASE] = 0x50,
+ [BRCMNAND_CS1_BASE] = 0,
+ [BRCMNAND_CORR_THRESHOLD] = 0xdc,
+ [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0,
+ [BRCMNAND_UNCORR_COUNT] = 0xfc,
+ [BRCMNAND_CORR_COUNT] = 0x100,
+ [BRCMNAND_CORR_EXT_ADDR] = 0x10c,
+ [BRCMNAND_CORR_ADDR] = 0x110,
+ [BRCMNAND_UNCORR_EXT_ADDR] = 0x114,
+ [BRCMNAND_UNCORR_ADDR] = 0x118,
+ [BRCMNAND_SEMAPHORE] = 0x150,
+ [BRCMNAND_ID] = 0x194,
+ [BRCMNAND_ID_EXT] = 0x198,
+ [BRCMNAND_LL_RDATA] = 0x19c,
+ [BRCMNAND_OOB_READ_BASE] = 0x200,
+ [BRCMNAND_OOB_READ_10_BASE] = 0,
+ [BRCMNAND_OOB_WRITE_BASE] = 0x280,
+ [BRCMNAND_OOB_WRITE_10_BASE] = 0,
+ [BRCMNAND_FC_BASE] = 0x400,
+};
+
enum brcmnand_cs_reg {
BRCMNAND_CS_CFG_EXT = 0,
BRCMNAND_CS_CFG,
@@ -404,7 +434,9 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
}
/* Register offsets */
- if (ctrl->nand_version >= 0x0600)
+ if (ctrl->nand_version >= 0x0701)
+ ctrl->reg_offsets = brcmnand_regs_v71;
+ else if (ctrl->nand_version >= 0x0600)
ctrl->reg_offsets = brcmnand_regs_v60;
else if (ctrl->nand_version >= 0x0500)
ctrl->reg_offsets = brcmnand_regs_v50;
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 3ff583f165cd..ce7b2cab5762 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3979,7 +3979,6 @@ static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip,
* This is the first phase of the normal nand_scan() function. It reads the
* flash ID and sets up MTD fields accordingly.
*
- * The mtd->owner field must be set to the module of the caller.
*/
int nand_scan_ident(struct mtd_info *mtd, int maxchips,
struct nand_flash_dev *table)
@@ -4403,19 +4402,12 @@ EXPORT_SYMBOL(nand_scan_tail);
*
* This fills out all the uninitialized function pointers with the defaults.
* The flash ID is read and the mtd/chip structures are filled with the
- * appropriate values. The mtd->owner field must be set to the module of the
- * caller.
+ * appropriate values.
*/
int nand_scan(struct mtd_info *mtd, int maxchips)
{
int ret;
- /* Many callers got this wrong, so check for it for a while... */
- if (!mtd->owner && caller_is_module()) {
- pr_crit("%s called with NULL mtd->owner!\n", __func__);
- BUG();
- }
-
ret = nand_scan_ident(mtd, maxchips, NULL);
if (!ret)
ret = nand_scan_tail(mtd);
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 32477c4eb421..37e4135ab213 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -1067,45 +1067,6 @@ static int spansion_quad_enable(struct spi_nor *nor)
return 0;
}
-static int micron_quad_enable(struct spi_nor *nor)
-{
- int ret;
- u8 val;
-
- ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1);
- if (ret < 0) {
- dev_err(nor->dev, "error %d reading EVCR\n", ret);
- return ret;
- }
-
- write_enable(nor);
-
- /* set EVCR, enable quad I/O */
- nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON;
- ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1);
- if (ret < 0) {
- dev_err(nor->dev, "error while writing EVCR register\n");
- return ret;
- }
-
- ret = spi_nor_wait_till_ready(nor);
- if (ret)
- return ret;
-
- /* read EVCR and check it */
- ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1);
- if (ret < 0) {
- dev_err(nor->dev, "error %d reading EVCR\n", ret);
- return ret;
- }
- if (val & EVCR_QUAD_EN_MICRON) {
- dev_err(nor->dev, "Micron EVCR Quad bit not clear\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
{
int status;
@@ -1119,12 +1080,7 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
}
return status;
case SNOR_MFR_MICRON:
- status = micron_quad_enable(nor);
- if (status) {
- dev_err(nor->dev, "Micron quad-read not enabled\n");
- return -EINVAL;
- }
- return status;
+ return 0;
default:
status = spansion_quad_enable(nor);
if (status) {
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index 5b9834cf2820..4dd0391d2942 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -426,8 +426,25 @@ retry:
pnum, vol_id, lnum);
err = -EBADMSG;
} else {
- err = -EINVAL;
- ubi_ro_mode(ubi);
+ /*
+ * Ending up here in the non-Fastmap case
+ * is a clear bug as the VID header had to
+ * be present at scan time to have it referenced.
+ * With fastmap the story is more complicated.
+ * Fastmap has the mapping info without the need
+ * of a full scan. So the LEB could have been
+ * unmapped, Fastmap cannot know this and keeps
+ * the LEB referenced.
+ * This is valid and works as the layer above UBI
+ * has to do bookkeeping about used/referenced
+ * LEBs in any case.
+ */
+ if (ubi->fast_attach) {
+ err = -EBADMSG;
+ } else {
+ err = -EINVAL;
+ ubi_ro_mode(ubi);
+ }
}
}
goto out_free;
@@ -558,6 +575,7 @@ static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum,
int err, idx = vol_id2idx(ubi, vol_id), new_pnum, data_size, tries = 0;
struct ubi_volume *vol = ubi->volumes[idx];
struct ubi_vid_hdr *vid_hdr;
+ uint32_t crc;
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
if (!vid_hdr)
@@ -582,14 +600,8 @@ retry:
goto out_put;
}
- vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
- err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr);
- if (err) {
- up_read(&ubi->fm_eba_sem);
- goto write_error;
- }
+ ubi_assert(vid_hdr->vol_type == UBI_VID_DYNAMIC);
- data_size = offset + len;
mutex_lock(&ubi->buf_mutex);
memset(ubi->peb_buf + offset, 0xFF, len);
@@ -604,6 +616,19 @@ retry:
memcpy(ubi->peb_buf + offset, buf, len);
+ data_size = offset + len;
+ crc = crc32(UBI_CRC32_INIT, ubi->peb_buf, data_size);
+ vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
+ vid_hdr->copy_flag = 1;
+ vid_hdr->data_size = cpu_to_be32(data_size);
+ vid_hdr->data_crc = cpu_to_be32(crc);
+ err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr);
+ if (err) {
+ mutex_unlock(&ubi->buf_mutex);
+ up_read(&ubi->fm_eba_sem);
+ goto write_error;
+ }
+
err = ubi_io_write_data(ubi, ubi->peb_buf, new_pnum, 0, data_size);
if (err) {
mutex_unlock(&ubi->buf_mutex);
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index 263b439e21a8..990898b9dc72 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -1058,6 +1058,7 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
ubi_msg(ubi, "fastmap WL pool size: %d",
ubi->fm_wl_pool.max_size);
ubi->fm_disabled = 0;
+ ubi->fast_attach = 1;
ubi_free_vid_hdr(ubi, vh);
kfree(ech);
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index fdb1931f66ed..bdb885d9d3fc 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -466,6 +466,7 @@ struct ubi_debug_info {
* @fm_eba_sem: allows ubi_update_fastmap() to block EBA table changes
* @fm_work: fastmap work queue
* @fm_work_scheduled: non-zero if fastmap work was scheduled
+ * @fast_attach: non-zero if UBI was attached by fastmap
*
* @used: RB-tree of used physical eraseblocks
* @erroneous: RB-tree of erroneous used physical eraseblocks
@@ -574,6 +575,7 @@ struct ubi_device {
size_t fm_size;
struct work_struct fm_work;
int fm_work_scheduled;
+ int fast_attach;
/* Wear-leveling sub-system's stuff */
struct rb_root used;
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index 141c2a42d7ed..910c12e2638e 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -696,11 +696,17 @@ int can_change_mtu(struct net_device *dev, int new_mtu)
/* allow change of MTU according to the CANFD ability of the device */
switch (new_mtu) {
case CAN_MTU:
+ /* 'CANFD-only' controllers can not switch to CAN_MTU */
+ if (priv->ctrlmode_static & CAN_CTRLMODE_FD)
+ return -EINVAL;
+
priv->ctrlmode &= ~CAN_CTRLMODE_FD;
break;
case CANFD_MTU:
- if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD))
+ /* check for potential CANFD ability */
+ if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD) &&
+ !(priv->ctrlmode_static & CAN_CTRLMODE_FD))
return -EINVAL;
priv->ctrlmode |= CAN_CTRLMODE_FD;
@@ -782,6 +788,35 @@ static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
= { .len = sizeof(struct can_bittiming_const) },
};
+static int can_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+ bool is_can_fd = false;
+
+ /* Make sure that valid CAN FD configurations always consist of
+ * - nominal/arbitration bittiming
+ * - data bittiming
+ * - control mode with CAN_CTRLMODE_FD set
+ */
+
+ if (data[IFLA_CAN_CTRLMODE]) {
+ struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);
+
+ is_can_fd = cm->flags & cm->mask & CAN_CTRLMODE_FD;
+ }
+
+ if (is_can_fd) {
+ if (!data[IFLA_CAN_BITTIMING] || !data[IFLA_CAN_DATA_BITTIMING])
+ return -EOPNOTSUPP;
+ }
+
+ if (data[IFLA_CAN_DATA_BITTIMING]) {
+ if (!is_can_fd || !data[IFLA_CAN_BITTIMING])
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
static int can_changelink(struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
{
@@ -813,19 +848,31 @@ static int can_changelink(struct net_device *dev,
if (data[IFLA_CAN_CTRLMODE]) {
struct can_ctrlmode *cm;
+ u32 ctrlstatic;
+ u32 maskedflags;
/* Do not allow changing controller mode while running */
if (dev->flags & IFF_UP)
return -EBUSY;
cm = nla_data(data[IFLA_CAN_CTRLMODE]);
+ ctrlstatic = priv->ctrlmode_static;
+ maskedflags = cm->flags & cm->mask;
+
+ /* check whether provided bits are allowed to be passed */
+ if (cm->mask & ~(priv->ctrlmode_supported | ctrlstatic))
+ return -EOPNOTSUPP;
+
+ /* do not check for static fd-non-iso if 'fd' is disabled */
+ if (!(maskedflags & CAN_CTRLMODE_FD))
+ ctrlstatic &= ~CAN_CTRLMODE_FD_NON_ISO;
- /* check whether changed bits are allowed to be modified */
- if (cm->mask & ~priv->ctrlmode_supported)
+ /* make sure static options are provided by configuration */
+ if ((maskedflags & ctrlstatic) != ctrlstatic)
return -EOPNOTSUPP;
/* clear bits to be modified and copy the flag values */
priv->ctrlmode &= ~cm->mask;
- priv->ctrlmode |= (cm->flags & cm->mask);
+ priv->ctrlmode |= maskedflags;
/* CAN_CTRLMODE_FD can only be set when driver supports FD */
if (priv->ctrlmode & CAN_CTRLMODE_FD)
@@ -966,6 +1013,7 @@ static struct rtnl_link_ops can_link_ops __read_mostly = {
.maxtype = IFLA_CAN_MAX,
.policy = can_policy,
.setup = can_setup,
+ .validate = can_validate,
.newlink = can_newlink,
.changelink = can_changelink,
.get_size = can_get_size,
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index 39cf911f7a1e..195f15edb32e 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -955,7 +955,7 @@ static struct net_device *alloc_m_can_dev(void)
priv->can.do_get_berr_counter = m_can_get_berr_counter;
/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.1 */
- priv->can.ctrlmode = CAN_CTRLMODE_FD_NON_ISO;
+ can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
/* CAN_CTRLMODE_FD_NON_ISO can not be changed with M_CAN IP v3.0.1 */
priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c
index bd377a6b067d..df54475d163b 100644
--- a/drivers/net/ethernet/atheros/alx/main.c
+++ b/drivers/net/ethernet/atheros/alx/main.c
@@ -86,9 +86,14 @@ static int alx_refill_rx_ring(struct alx_priv *alx, gfp_t gfp)
while (!cur_buf->skb && next != rxq->read_idx) {
struct alx_rfd *rfd = &rxq->rfd[cur];
- skb = __netdev_alloc_skb(alx->dev, alx->rxbuf_size, gfp);
+ skb = __netdev_alloc_skb(alx->dev, alx->rxbuf_size + 64, gfp);
if (!skb)
break;
+
+ /* Workround for the HW RX DMA overflow issue */
+ if (((unsigned long)skb->data & 0xfff) == 0xfc0)
+ skb_reserve(skb, 64);
+
dma = dma_map_single(&alx->hw.pdev->dev,
skb->data, alx->rxbuf_size,
DMA_FROM_DEVICE);
diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c
index 8f76f4558a88..2ff465848b65 100644
--- a/drivers/net/ethernet/atheros/atlx/atl2.c
+++ b/drivers/net/ethernet/atheros/atlx/atl2.c
@@ -1412,7 +1412,7 @@ static int atl2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
err = -EIO;
- netdev->hw_features = NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_RX;
+ netdev->hw_features = NETIF_F_HW_VLAN_CTAG_RX;
netdev->features |= (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX);
/* Init PHY as early as possible due to power saving issue */
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index 169059c92f80..8d54e7b41bbf 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -2405,9 +2405,9 @@ static int macb_init(struct platform_device *pdev)
if (bp->phy_interface == PHY_INTERFACE_MODE_RGMII)
val = GEM_BIT(RGMII);
else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII &&
- (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
+ (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII))
val = MACB_BIT(RMII);
- else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
+ else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII))
val = MACB_BIT(MII);
if (bp->caps & MACB_CAPS_USRIO_HAS_CLKEN)
@@ -2738,7 +2738,7 @@ static int at91ether_init(struct platform_device *pdev)
}
static const struct macb_config at91sam9260_config = {
- .caps = MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_USRIO_DEFAULT_IS_MII,
+ .caps = MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII,
.clk_init = macb_clk_init,
.init = macb_init,
};
@@ -2751,21 +2751,22 @@ static const struct macb_config pc302gem_config = {
};
static const struct macb_config sama5d2_config = {
- .caps = 0,
+ .caps = MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII,
.dma_burst_length = 16,
.clk_init = macb_clk_init,
.init = macb_init,
};
static const struct macb_config sama5d3_config = {
- .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE,
+ .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE
+ | MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII,
.dma_burst_length = 16,
.clk_init = macb_clk_init,
.init = macb_init,
};
static const struct macb_config sama5d4_config = {
- .caps = 0,
+ .caps = MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII,
.dma_burst_length = 4,
.clk_init = macb_clk_init,
.init = macb_init,
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index d83b0db77821..3f385ab94988 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -398,7 +398,7 @@
/* Capability mask bits */
#define MACB_CAPS_ISR_CLEAR_ON_WRITE 0x00000001
#define MACB_CAPS_USRIO_HAS_CLKEN 0x00000002
-#define MACB_CAPS_USRIO_DEFAULT_IS_MII 0x00000004
+#define MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII 0x00000004
#define MACB_CAPS_NO_GIGABIT_HALF 0x00000008
#define MACB_CAPS_FIFO_MODE 0x10000000
#define MACB_CAPS_GIGABIT_MODE_AVAILABLE 0x20000000
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
index 206b6a71a545..d1c217eaf417 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
@@ -550,6 +550,7 @@ static void nicvf_rcv_queue_config(struct nicvf *nic, struct queue_set *qs,
nicvf_config_vlan_stripping(nic, nic->netdev->features);
/* Enable Receive queue */
+ memset(&rq_cfg, 0, sizeof(struct rq_cfg));
rq_cfg.ena = 1;
rq_cfg.tcp_ena = 0;
nicvf_queue_reg_write(nic, NIC_QSET_RQ_0_7_CFG, qidx, *(u64 *)&rq_cfg);
@@ -582,6 +583,7 @@ void nicvf_cmp_queue_config(struct nicvf *nic, struct queue_set *qs,
qidx, (u64)(cq->dmem.phys_base));
/* Enable Completion queue */
+ memset(&cq_cfg, 0, sizeof(struct cq_cfg));
cq_cfg.ena = 1;
cq_cfg.reset = 0;
cq_cfg.caching = 0;
@@ -630,6 +632,7 @@ static void nicvf_snd_queue_config(struct nicvf *nic, struct queue_set *qs,
qidx, (u64)(sq->dmem.phys_base));
/* Enable send queue & set queue size */
+ memset(&sq_cfg, 0, sizeof(struct sq_cfg));
sq_cfg.ena = 1;
sq_cfg.reset = 0;
sq_cfg.ldwb = 0;
@@ -666,6 +669,7 @@ static void nicvf_rbdr_config(struct nicvf *nic, struct queue_set *qs,
/* Enable RBDR & set queue size */
/* Buffer size should be in multiples of 128 bytes */
+ memset(&rbdr_cfg, 0, sizeof(struct rbdr_cfg));
rbdr_cfg.ena = 1;
rbdr_cfg.reset = 0;
rbdr_cfg.ldwb = 0;
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index b2a32209ffbf..f6147ffc7fbc 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -1557,9 +1557,15 @@ fec_enet_rx(struct net_device *ndev, int budget)
struct fec_enet_private *fep = netdev_priv(ndev);
for_each_set_bit(queue_id, &fep->work_rx, FEC_ENET_MAX_RX_QS) {
- clear_bit(queue_id, &fep->work_rx);
- pkt_received += fec_enet_rx_queue(ndev,
+ int ret;
+
+ ret = fec_enet_rx_queue(ndev,
budget - pkt_received, queue_id);
+
+ if (ret < budget - pkt_received)
+ clear_bit(queue_id, &fep->work_rx);
+
+ pkt_received += ret;
}
return pkt_received;
}
diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c
index 973dade2d07f..1257b18e6b90 100644
--- a/drivers/net/ethernet/jme.c
+++ b/drivers/net/ethernet/jme.c
@@ -270,11 +270,17 @@ jme_reset_mac_processor(struct jme_adapter *jme)
}
static inline void
-jme_clear_pm(struct jme_adapter *jme)
+jme_clear_pm_enable_wol(struct jme_adapter *jme)
{
jwrite32(jme, JME_PMCS, PMCS_STMASK | jme->reg_pmcs);
}
+static inline void
+jme_clear_pm_disable_wol(struct jme_adapter *jme)
+{
+ jwrite32(jme, JME_PMCS, PMCS_STMASK);
+}
+
static int
jme_reload_eeprom(struct jme_adapter *jme)
{
@@ -1853,7 +1859,7 @@ jme_open(struct net_device *netdev)
struct jme_adapter *jme = netdev_priv(netdev);
int rc;
- jme_clear_pm(jme);
+ jme_clear_pm_disable_wol(jme);
JME_NAPI_ENABLE(jme);
tasklet_init(&jme->linkch_task, jme_link_change_tasklet,
@@ -1925,11 +1931,11 @@ jme_wait_link(struct jme_adapter *jme)
static void
jme_powersave_phy(struct jme_adapter *jme)
{
- if (jme->reg_pmcs) {
+ if (jme->reg_pmcs && device_may_wakeup(&jme->pdev->dev)) {
jme_set_100m_half(jme);
if (jme->reg_pmcs & (PMCS_LFEN | PMCS_LREN))
jme_wait_link(jme);
- jme_clear_pm(jme);
+ jme_clear_pm_enable_wol(jme);
} else {
jme_phy_off(jme);
}
@@ -2646,9 +2652,6 @@ jme_set_wol(struct net_device *netdev,
if (wol->wolopts & WAKE_MAGIC)
jme->reg_pmcs |= PMCS_MFEN;
- jwrite32(jme, JME_PMCS, jme->reg_pmcs);
- device_set_wakeup_enable(&jme->pdev->dev, !!(jme->reg_pmcs));
-
return 0;
}
@@ -3172,8 +3175,8 @@ jme_init_one(struct pci_dev *pdev,
jme->mii_if.mdio_read = jme_mdio_read;
jme->mii_if.mdio_write = jme_mdio_write;
- jme_clear_pm(jme);
- device_set_wakeup_enable(&pdev->dev, true);
+ jme_clear_pm_disable_wol(jme);
+ device_init_wakeup(&pdev->dev, true);
jme_set_phyfifo_5level(jme);
jme->pcirev = pdev->revision;
@@ -3304,7 +3307,7 @@ jme_resume(struct device *dev)
if (!netif_running(netdev))
return 0;
- jme_clear_pm(jme);
+ jme_clear_pm_disable_wol(jme);
jme_phy_on(jme);
if (test_bit(JME_FLAG_SSET, &jme->flags))
jme_set_settings(netdev, &jme->old_ecmd);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index e7a5000aa12c..bbff8ec6713e 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -704,7 +704,7 @@ static int get_fixed_ipv6_csum(__wsum hw_checksum, struct sk_buff *skb,
if (ipv6h->nexthdr == IPPROTO_FRAGMENT || ipv6h->nexthdr == IPPROTO_HOPOPTS)
return -1;
- hw_checksum = csum_add(hw_checksum, (__force __wsum)(ipv6h->nexthdr << 8));
+ hw_checksum = csum_add(hw_checksum, (__force __wsum)htons(ipv6h->nexthdr));
csum_pseudo_hdr = csum_partial(&ipv6h->saddr,
sizeof(ipv6h->saddr) + sizeof(ipv6h->daddr), 0);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
index 4421bf5463f6..e4019a803a9c 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
@@ -400,7 +400,6 @@ static bool mlx4_en_process_tx_cq(struct net_device *dev,
u32 packets = 0;
u32 bytes = 0;
int factor = priv->cqe_factor;
- u64 timestamp = 0;
int done = 0;
int budget = priv->tx_work_limit;
u32 last_nr_txbb;
@@ -440,9 +439,12 @@ static bool mlx4_en_process_tx_cq(struct net_device *dev,
new_index = be16_to_cpu(cqe->wqe_index) & size_mask;
do {
+ u64 timestamp = 0;
+
txbbs_skipped += last_nr_txbb;
ring_index = (ring_index + last_nr_txbb) & size_mask;
- if (ring->tx_info[ring_index].ts_requested)
+
+ if (unlikely(ring->tx_info[ring_index].ts_requested))
timestamp = mlx4_en_get_cqe_ts(cqe);
/* free next descriptor */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 1203d892e842..cbd17e25beeb 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -1372,7 +1372,7 @@ static int mlx5e_set_dev_port_mtu(struct net_device *netdev)
{
struct mlx5e_priv *priv = netdev_priv(netdev);
struct mlx5_core_dev *mdev = priv->mdev;
- int hw_mtu;
+ u16 hw_mtu;
int err;
err = mlx5_set_port_mtu(mdev, MLX5E_SW2HW_MTU(netdev->mtu), 1);
@@ -1891,22 +1891,27 @@ static int mlx5e_set_features(struct net_device *netdev,
return err;
}
+#define MXL5_HW_MIN_MTU 64
+#define MXL5E_MIN_MTU (MXL5_HW_MIN_MTU + ETH_FCS_LEN)
+
static int mlx5e_change_mtu(struct net_device *netdev, int new_mtu)
{
struct mlx5e_priv *priv = netdev_priv(netdev);
struct mlx5_core_dev *mdev = priv->mdev;
bool was_opened;
- int max_mtu;
+ u16 max_mtu;
+ u16 min_mtu;
int err = 0;
mlx5_query_port_max_mtu(mdev, &max_mtu, 1);
max_mtu = MLX5E_HW2SW_MTU(max_mtu);
+ min_mtu = MLX5E_HW2SW_MTU(MXL5E_MIN_MTU);
- if (new_mtu > max_mtu) {
+ if (new_mtu > max_mtu || new_mtu < min_mtu) {
netdev_err(netdev,
- "%s: Bad MTU (%d) > (%d) Max\n",
- __func__, new_mtu, max_mtu);
+ "%s: Bad MTU (%d), valid range is: [%d..%d]\n",
+ __func__, new_mtu, min_mtu, max_mtu);
return -EINVAL;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c
index a87e773e93f3..53a793bc2e3d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/port.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c
@@ -246,8 +246,8 @@ int mlx5_query_port_admin_status(struct mlx5_core_dev *dev,
}
EXPORT_SYMBOL_GPL(mlx5_query_port_admin_status);
-static void mlx5_query_port_mtu(struct mlx5_core_dev *dev, int *admin_mtu,
- int *max_mtu, int *oper_mtu, u8 port)
+static void mlx5_query_port_mtu(struct mlx5_core_dev *dev, u16 *admin_mtu,
+ u16 *max_mtu, u16 *oper_mtu, u8 port)
{
u32 in[MLX5_ST_SZ_DW(pmtu_reg)];
u32 out[MLX5_ST_SZ_DW(pmtu_reg)];
@@ -267,7 +267,7 @@ static void mlx5_query_port_mtu(struct mlx5_core_dev *dev, int *admin_mtu,
*admin_mtu = MLX5_GET(pmtu_reg, out, admin_mtu);
}
-int mlx5_set_port_mtu(struct mlx5_core_dev *dev, int mtu, u8 port)
+int mlx5_set_port_mtu(struct mlx5_core_dev *dev, u16 mtu, u8 port)
{
u32 in[MLX5_ST_SZ_DW(pmtu_reg)];
u32 out[MLX5_ST_SZ_DW(pmtu_reg)];
@@ -282,14 +282,14 @@ int mlx5_set_port_mtu(struct mlx5_core_dev *dev, int mtu, u8 port)
}
EXPORT_SYMBOL_GPL(mlx5_set_port_mtu);
-void mlx5_query_port_max_mtu(struct mlx5_core_dev *dev, int *max_mtu,
+void mlx5_query_port_max_mtu(struct mlx5_core_dev *dev, u16 *max_mtu,
u8 port)
{
mlx5_query_port_mtu(dev, NULL, max_mtu, NULL, port);
}
EXPORT_SYMBOL_GPL(mlx5_query_port_max_mtu);
-void mlx5_query_port_oper_mtu(struct mlx5_core_dev *dev, int *oper_mtu,
+void mlx5_query_port_oper_mtu(struct mlx5_core_dev *dev, u16 *oper_mtu,
u8 port)
{
mlx5_query_port_mtu(dev, NULL, NULL, oper_mtu, port);
diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c
index 2b34622a4bfe..3920c3eb6006 100644
--- a/drivers/net/ethernet/rocker/rocker.c
+++ b/drivers/net/ethernet/rocker/rocker.c
@@ -4475,7 +4475,7 @@ static int rocker_port_obj_add(struct net_device *dev,
fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj);
err = rocker_port_fib_ipv4(rocker_port, trans,
htonl(fib4->dst), fib4->dst_len,
- &fib4->fi, fib4->tb_id, 0);
+ fib4->fi, fib4->tb_id, 0);
break;
case SWITCHDEV_OBJ_ID_PORT_FDB:
err = rocker_port_fdb_add(rocker_port, trans,
@@ -4547,7 +4547,7 @@ static int rocker_port_obj_del(struct net_device *dev,
fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj);
err = rocker_port_fib_ipv4(rocker_port, NULL,
htonl(fib4->dst), fib4->dst_len,
- &fib4->fi, fib4->tb_id,
+ fib4->fi, fib4->tb_id,
ROCKER_OP_FLAG_REMOVE);
break;
case SWITCHDEV_OBJ_ID_PORT_FDB:
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
index e6a084a6be12..cbe9a330117a 100644
--- a/drivers/net/ethernet/sfc/ef10.c
+++ b/drivers/net/ethernet/sfc/ef10.c
@@ -619,6 +619,17 @@ fail:
return rc;
}
+static void efx_ef10_forget_old_piobufs(struct efx_nic *efx)
+{
+ struct efx_channel *channel;
+ struct efx_tx_queue *tx_queue;
+
+ /* All our existing PIO buffers went away */
+ efx_for_each_channel(channel, efx)
+ efx_for_each_channel_tx_queue(tx_queue, channel)
+ tx_queue->piobuf = NULL;
+}
+
#else /* !EFX_USE_PIO */
static int efx_ef10_alloc_piobufs(struct efx_nic *efx, unsigned int n)
@@ -635,6 +646,10 @@ static void efx_ef10_free_piobufs(struct efx_nic *efx)
{
}
+static void efx_ef10_forget_old_piobufs(struct efx_nic *efx)
+{
+}
+
#endif /* EFX_USE_PIO */
static void efx_ef10_remove(struct efx_nic *efx)
@@ -1018,6 +1033,7 @@ static void efx_ef10_reset_mc_allocations(struct efx_nic *efx)
nic_data->must_realloc_vis = true;
nic_data->must_restore_filters = true;
nic_data->must_restore_piobufs = true;
+ efx_ef10_forget_old_piobufs(efx);
nic_data->rx_rss_context = EFX_EF10_RSS_CONTEXT_INVALID;
/* Driver-created vswitches and vports must be re-created */
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 58efdec12f30..69e31e2a68fc 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -310,15 +310,15 @@ static int geneve_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
/* Need Geneve and inner Ethernet header to be present */
if (unlikely(!pskb_may_pull(skb, GENEVE_BASE_HLEN)))
- goto error;
+ goto drop;
/* Return packets with reserved bits set */
geneveh = geneve_hdr(skb);
if (unlikely(geneveh->ver != GENEVE_VER))
- goto error;
+ goto drop;
if (unlikely(geneveh->proto_type != htons(ETH_P_TEB)))
- goto error;
+ goto drop;
opts_len = geneveh->opt_len * 4;
if (iptunnel_pull_header(skb, GENEVE_BASE_HLEN + opts_len,
@@ -336,10 +336,6 @@ drop:
/* Consume bad packet */
kfree_skb(skb);
return 0;
-
-error:
- /* Let the UDP layer deal with the skb */
- return 1;
}
static struct socket *geneve_create_sock(struct net *net, bool ipv6,
@@ -998,6 +994,17 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
return geneve_xmit_skb(skb, dev, info);
}
+static int geneve_change_mtu(struct net_device *dev, int new_mtu)
+{
+ /* GENEVE overhead is not fixed, so we can't enforce a more
+ * precise max MTU.
+ */
+ if (new_mtu < 68 || new_mtu > IP_MAX_MTU)
+ return -EINVAL;
+ dev->mtu = new_mtu;
+ return 0;
+}
+
static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
{
struct ip_tunnel_info *info = skb_tunnel_info(skb);
@@ -1042,7 +1049,7 @@ static const struct net_device_ops geneve_netdev_ops = {
.ndo_stop = geneve_stop,
.ndo_start_xmit = geneve_xmit,
.ndo_get_stats64 = ip_tunnel_get_stats64,
- .ndo_change_mtu = eth_change_mtu,
+ .ndo_change_mtu = geneve_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_fill_metadata_dst = geneve_fill_metadata_dst,
@@ -1349,11 +1356,21 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
err = geneve_configure(net, dev, &geneve_remote_unspec,
0, 0, 0, htons(dst_port), true);
- if (err) {
- free_netdev(dev);
- return ERR_PTR(err);
- }
+ if (err)
+ goto err;
+
+ /* openvswitch users expect packet sizes to be unrestricted,
+ * so set the largest MTU we can.
+ */
+ err = geneve_change_mtu(dev, IP_MAX_MTU);
+ if (err)
+ goto err;
+
return dev;
+
+ err:
+ free_netdev(dev);
+ return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(geneve_dev_create_fb);
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 59fefca74263..a5f392ae30d5 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -969,7 +969,7 @@ static void team_port_disable(struct team *team,
NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \
NETIF_F_HIGHDMA | NETIF_F_LRO)
-static void __team_compute_features(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;
@@ -993,15 +993,20 @@ static void __team_compute_features(struct team *team)
team->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
if (dst_release_flag == (IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM))
team->dev->priv_flags |= IFF_XMIT_DST_RELEASE;
+}
+static void __team_compute_features(struct team *team)
+{
+ ___team_compute_features(team);
netdev_change_features(team->dev);
}
static void team_compute_features(struct team *team)
{
mutex_lock(&team->lock);
- __team_compute_features(team);
+ ___team_compute_features(team);
mutex_unlock(&team->lock);
+ netdev_change_features(team->dev);
}
static int team_port_enter(struct team *team, struct team_port *port)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 4b15d9ee5a54..935e0b45e151 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -567,11 +567,13 @@ static void tun_detach_all(struct net_device *dev)
for (i = 0; i < n; i++) {
tfile = rtnl_dereference(tun->tfiles[i]);
BUG_ON(!tfile);
+ tfile->socket.sk->sk_shutdown = RCV_SHUTDOWN;
tfile->socket.sk->sk_data_ready(tfile->socket.sk);
RCU_INIT_POINTER(tfile->tun, NULL);
--tun->numqueues;
}
list_for_each_entry(tfile, &tun->disabled, next) {
+ tfile->socket.sk->sk_shutdown = RCV_SHUTDOWN;
tfile->socket.sk->sk_data_ready(tfile->socket.sk);
RCU_INIT_POINTER(tfile->tun, NULL);
}
@@ -627,6 +629,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file, bool skip_filte
goto out;
}
tfile->queue_index = tun->numqueues;
+ tfile->socket.sk->sk_shutdown &= ~RCV_SHUTDOWN;
rcu_assign_pointer(tfile->tun, tun);
rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile);
tun->numqueues++;
@@ -1412,9 +1415,6 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile,
if (!iov_iter_count(to))
return 0;
- if (tun->dev->reg_state != NETREG_REGISTERED)
- return -EIO;
-
/* Read frames from queue */
skb = __skb_recv_datagram(tfile->socket.sk, noblock ? MSG_DONTWAIT : 0,
&peeked, &off, &err);
diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c
index bd9acff1eb7b..7fbd8f044207 100644
--- a/drivers/net/usb/asix_common.c
+++ b/drivers/net/usb/asix_common.c
@@ -66,7 +66,7 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb,
* buffer.
*/
if (rx->remaining && (rx->remaining + sizeof(u32) <= skb->len)) {
- offset = ((rx->remaining + 1) & 0xfffe) + sizeof(u32);
+ offset = ((rx->remaining + 1) & 0xfffe);
rx->header = get_unaligned_le32(skb->data + offset);
offset = 0;
diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c
index bdd83d95ec0a..96a5028621c8 100644
--- a/drivers/net/usb/cdc_mbim.c
+++ b/drivers/net/usb/cdc_mbim.c
@@ -617,8 +617,13 @@ static const struct usb_device_id mbim_devs[] = {
{ USB_VENDOR_AND_INTERFACE_INFO(0x0bdb, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&cdc_mbim_info,
},
- /* Huawei E3372 fails unless NDP comes after the IP packets */
- { USB_DEVICE_AND_INTERFACE_INFO(0x12d1, 0x157d, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
+
+ /* Some Huawei devices, ME906s-158 (12d1:15c1) and E3372
+ * (12d1:157d), are known to fail unless the NDP is placed
+ * after the IP packets. Applying the quirk to all Huawei
+ * devices is broader than necessary, but harmless.
+ */
+ { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&cdc_mbim_info_ndp_to_end,
},
/* default entry */
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index b11fe09552bf..e0e94b855bbe 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -809,6 +809,13 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
if (cdc_ncm_init(dev))
goto error2;
+ /* Some firmwares need a pause here or they will silently fail
+ * to set up the interface properly. This value was decided
+ * empirically on a Sierra Wireless MC7455 running 02.08.02.00
+ * firmware.
+ */
+ usleep_range(10000, 20000);
+
/* configure data interface */
temp = usb_set_interface(dev->udev, iface_no, data_altsetting);
if (temp) {
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 3c0df70e2f53..003780901628 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -1254,7 +1254,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
/* Need Vxlan and inner Ethernet header to be present */
if (!pskb_may_pull(skb, VXLAN_HLEN))
- goto error;
+ goto drop;
vxh = (struct vxlanhdr *)(udp_hdr(skb) + 1);
flags = ntohl(vxh->vx_flags);
@@ -1344,13 +1344,7 @@ drop:
bad_flags:
netdev_dbg(skb->dev, "invalid vxlan flags=%#x vni=%#x\n",
ntohl(vxh->vx_flags), ntohl(vxh->vx_vni));
-
-error:
- if (tun_dst)
- dst_release((struct dst_entry *)tun_dst);
-
- /* Return non vxlan pkt */
- return 1;
+ goto drop;
}
static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
@@ -2370,29 +2364,43 @@ static void vxlan_set_multicast_list(struct net_device *dev)
{
}
-static int vxlan_change_mtu(struct net_device *dev, int new_mtu)
+static int __vxlan_change_mtu(struct net_device *dev,
+ struct net_device *lowerdev,
+ struct vxlan_rdst *dst, int new_mtu, bool strict)
{
- struct vxlan_dev *vxlan = netdev_priv(dev);
- struct vxlan_rdst *dst = &vxlan->default_dst;
- struct net_device *lowerdev;
- int max_mtu;
+ int max_mtu = IP_MAX_MTU;
- lowerdev = __dev_get_by_index(vxlan->net, dst->remote_ifindex);
- if (lowerdev == NULL)
- return eth_change_mtu(dev, new_mtu);
+ if (lowerdev)
+ max_mtu = lowerdev->mtu;
if (dst->remote_ip.sa.sa_family == AF_INET6)
- max_mtu = lowerdev->mtu - VXLAN6_HEADROOM;
+ max_mtu -= VXLAN6_HEADROOM;
else
- max_mtu = lowerdev->mtu - VXLAN_HEADROOM;
+ max_mtu -= VXLAN_HEADROOM;
- if (new_mtu < 68 || new_mtu > max_mtu)
+ if (new_mtu < 68)
return -EINVAL;
+ if (new_mtu > max_mtu) {
+ if (strict)
+ return -EINVAL;
+
+ new_mtu = max_mtu;
+ }
+
dev->mtu = new_mtu;
return 0;
}
+static int vxlan_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct vxlan_dev *vxlan = netdev_priv(dev);
+ struct vxlan_rdst *dst = &vxlan->default_dst;
+ struct net_device *lowerdev = __dev_get_by_index(vxlan->net,
+ dst->remote_ifindex);
+ return __vxlan_change_mtu(dev, lowerdev, dst, new_mtu, true);
+}
+
static int egress_ipv4_tun_info(struct net_device *dev, struct sk_buff *skb,
struct ip_tunnel_info *info,
__be16 sport, __be16 dport)
@@ -2768,6 +2776,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
int err;
bool use_ipv6 = false;
__be16 default_port = vxlan->cfg.dst_port;
+ struct net_device *lowerdev = NULL;
vxlan->net = src_net;
@@ -2788,9 +2797,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
}
if (conf->remote_ifindex) {
- struct net_device *lowerdev
- = __dev_get_by_index(src_net, conf->remote_ifindex);
-
+ lowerdev = __dev_get_by_index(src_net, conf->remote_ifindex);
dst->remote_ifindex = conf->remote_ifindex;
if (!lowerdev) {
@@ -2814,6 +2821,12 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
needed_headroom = lowerdev->hard_header_len;
}
+ if (conf->mtu) {
+ err = __vxlan_change_mtu(dev, lowerdev, dst, conf->mtu, false);
+ if (err)
+ return err;
+ }
+
if (use_ipv6 || conf->flags & VXLAN_F_COLLECT_METADATA)
needed_headroom += VXLAN6_HEADROOM;
else
@@ -2991,6 +3004,9 @@ static int vxlan_newlink(struct net *src_net, struct net_device *dev,
if (data[IFLA_VXLAN_REMCSUM_NOPARTIAL])
conf.flags |= VXLAN_F_REMCSUM_NOPARTIAL;
+ if (tb[IFLA_MTU])
+ conf.mtu = nla_get_u32(tb[IFLA_MTU]);
+
err = vxlan_dev_configure(src_net, dev, &conf);
switch (err) {
case -ENODEV:
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 0947cc271e69..531de256d58d 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -1681,6 +1681,10 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
goto err_hif_stop;
}
+ ar->free_vdev_map = (1LL << ar->max_num_vdevs) - 1;
+
+ INIT_LIST_HEAD(&ar->arvifs);
+
/* we don't care about HTT in UTF mode */
if (mode == ATH10K_FIRMWARE_MODE_NORMAL) {
status = ath10k_htt_setup(&ar->htt);
@@ -1694,10 +1698,6 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
if (status)
goto err_hif_stop;
- ar->free_vdev_map = (1LL << ar->max_num_vdevs) - 1;
-
- INIT_LIST_HEAD(&ar->arvifs);
-
return 0;
err_hif_stop:
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 6cc1aa3449c8..1a88a24ffeac 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -1986,7 +1986,12 @@ static ssize_t ath10k_write_pktlog_filter(struct file *file,
goto out;
}
- if (filter && (filter != ar->debug.pktlog_filter)) {
+ if (filter == ar->debug.pktlog_filter) {
+ ret = count;
+ goto out;
+ }
+
+ if (filter) {
ret = ath10k_wmi_pdev_pktlog_enable(ar, filter);
if (ret) {
ath10k_warn(ar, "failed to enable pktlog filter %x: %d\n",
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 95a55405ebf0..1e1bef349487 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -4456,7 +4456,10 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
goto err_vdev_delete;
}
- if (ar->cfg_tx_chainmask) {
+ /* Configuring number of spatial stream for monitor interface is causing
+ * target assert in qca9888 and qca6174.
+ */
+ if (ar->cfg_tx_chainmask && (vif->type != NL80211_IFTYPE_MONITOR)) {
u16 nss = get_nss_from_chainmask(ar->cfg_tx_chainmask);
vdev_param = ar->wmi.vdev_param->nss;
@@ -6416,7 +6419,13 @@ ath10k_mac_update_rx_channel(struct ath10k *ar,
def = &vifs[0].new_ctx->def;
ar->rx_channel = def->chan;
- } else if (ctx && ath10k_mac_num_chanctxs(ar) == 0) {
+ } else if ((ctx && ath10k_mac_num_chanctxs(ar) == 0) ||
+ (ctx && (ar->state == ATH10K_STATE_RESTARTED))) {
+ /* During driver restart due to firmware assert, since mac80211
+ * already has valid channel context for given radio, channel
+ * context iteration return num_chanctx > 0. So fix rx_channel
+ * when restart is in progress.
+ */
ar->rx_channel = ctx->def.chan;
} else {
ar->rx_channel = NULL;
diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c
index 803030fd17d3..6a2a16856763 100644
--- a/drivers/net/wireless/ath/ath5k/led.c
+++ b/drivers/net/wireless/ath/ath5k/led.c
@@ -77,7 +77,7 @@ static const struct pci_device_id ath5k_led_devices[] = {
/* HP Compaq CQ60-206US (ddreggors@jumptv.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137a), ATH_LED(3, 1) },
/* HP Compaq C700 (nitrousnrg@gmail.com) */
- { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 1) },
+ { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 0) },
/* LiteOn AR5BXB63 (magooz@salug.it) */
{ ATH_SDEVICE(PCI_VENDOR_ID_ATHEROS, 0x3067), ATH_LED(3, 0) },
/* IBM-specific AR5212 (all others) */
diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
index 8f8793004b9f..1b271b99c49e 100644
--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
@@ -274,6 +274,9 @@ void ar5008_hw_cmn_spur_mitigate(struct ath_hw *ah,
};
static const int inc[4] = { 0, 100, 0, 0 };
+ memset(&mask_m, 0, sizeof(int8_t) * 123);
+ memset(&mask_p, 0, sizeof(int8_t) * 123);
+
cur_bin = -6000;
upper = bin + 100;
lower = bin - 100;
@@ -424,14 +427,9 @@ static void ar5008_hw_spur_mitigate(struct ath_hw *ah,
int tmp, new;
int i;
- int8_t mask_m[123];
- int8_t mask_p[123];
int cur_bb_spur;
bool is2GHz = IS_CHAN_2GHZ(chan);
- memset(&mask_m, 0, sizeof(int8_t) * 123);
- memset(&mask_p, 0, sizeof(int8_t) * 123);
-
for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz);
if (AR_NO_SPUR == cur_bb_spur)
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
index db6624527d99..53d7445a5d12 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
@@ -178,14 +178,9 @@ static void ar9002_hw_spur_mitigate(struct ath_hw *ah,
int i;
struct chan_centers centers;
- int8_t mask_m[123];
- int8_t mask_p[123];
int cur_bb_spur;
bool is2GHz = IS_CHAN_2GHZ(chan);
- memset(&mask_m, 0, sizeof(int8_t) * 123);
- memset(&mask_p, 0, sizeof(int8_t) * 123);
-
ath9k_hw_get_channel_centers(ah, chan, &centers);
freq = centers.synth_center;
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 2e2b92ba96b8..1bdeacf7b257 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -49,6 +49,10 @@ int ath9k_led_blink;
module_param_named(blink, ath9k_led_blink, int, 0444);
MODULE_PARM_DESC(blink, "Enable LED blink on activity");
+static int ath9k_led_active_high = -1;
+module_param_named(led_active_high, ath9k_led_active_high, int, 0444);
+MODULE_PARM_DESC(led_active_high, "Invert LED polarity");
+
static int ath9k_btcoex_enable;
module_param_named(btcoex_enable, ath9k_btcoex_enable, int, 0444);
MODULE_PARM_DESC(btcoex_enable, "Enable wifi-BT coexistence");
@@ -600,6 +604,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
if (ret)
return ret;
+ if (ath9k_led_active_high != -1)
+ ah->config.led_active_high = ath9k_led_active_high == 1;
+
/*
* Enable WLAN/BT RX Antenna diversity only when:
*
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index e6fef1be9977..7cdaf40c3057 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -28,6 +28,16 @@ static const struct pci_device_id ath_pci_id_table[] = {
{ PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */
{ PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */
{ PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */
+
+#ifdef CONFIG_ATH9K_PCOEM
+ /* Mini PCI AR9220 MB92 cards: Compex WLM200NX, Wistron DNMA-92 */
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0029,
+ PCI_VENDOR_ID_ATHEROS,
+ 0x2096),
+ .driver_data = ATH9K_PCI_LED_ACT_HI },
+#endif
+
{ PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
#ifdef CONFIG_ATH9K_PCOEM
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 64046e0bd0a2..c3853a63b083 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -141,7 +141,7 @@ static void wil6210_unmask_irq_misc(struct wil6210_priv *wil, bool unmask_halp)
unmask_halp ? WIL6210_IMC_MISC : WIL6210_IMC_MISC_NO_HALP);
}
-static void wil6210_unmask_halp(struct wil6210_priv *wil)
+void wil6210_unmask_halp(struct wil6210_priv *wil)
{
wil_dbg_irq(wil, "%s()\n", __func__);
@@ -149,7 +149,7 @@ static void wil6210_unmask_halp(struct wil6210_priv *wil)
BIT_DMA_EP_MISC_ICR_HALP);
}
-static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil)
+void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil)
{
wil_dbg_irq(wil, "%s()\n", __func__);
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 5285ebc8b9af..94e5b67abd59 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -875,19 +875,29 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
flush_workqueue(wil->wq_service);
flush_workqueue(wil->wmi_wq);
+ wil6210_unmask_irq_pseudo(wil);
+ wil6210_unmask_halp(wil);
+ wil_halp_vote(wil);
+
wil_bl_crash_info(wil, false);
rc = wil_target_reset(wil);
+ /* wil_target_reset clears the HALP IRQ, need to set it again.
+ * Call wil_halp_unvote to clear the HALP reference counter
+ * and unmask the HALP interrupt before setting it again
+ */
+ wil_halp_unvote(wil);
+ wil_halp_vote(wil);
wil_rx_fini(wil);
if (rc) {
wil_bl_crash_info(wil, true);
- return rc;
+ goto out;
}
rc = wil_get_bl_info(wil);
if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */
rc = 0;
if (rc)
- return rc;
+ goto out;
wil_set_oob_mode(wil, oob_mode);
if (load_fw) {
@@ -899,14 +909,19 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
/* Loading f/w from the file */
rc = wil_request_firmware(wil, WIL_FW_NAME, true);
if (rc)
- return rc;
+ goto out;
rc = wil_request_firmware(wil, WIL_FW2_NAME, true);
if (rc)
- return rc;
+ goto out;
/* Mark FW as loaded from host */
wil_s(wil, RGF_USER_USAGE_6, 1);
+ /* Clear the HALP while in BL, before clearing all the IRQs
+ * and running the FW.
+ */
+ wil_halp_unvote(wil);
+
/* clear any interrupts which on-card-firmware
* may have set
*/
@@ -917,6 +932,9 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
wil_release_cpu(wil);
+ } else {
+ /* Allow XTAL off when going down */
+ wil_halp_unvote(wil);
}
/* init after reset */
@@ -955,6 +973,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
}
return rc;
+
+out:
+ wil_halp_unvote(wil);
+ return rc;
}
void wil_fw_error_recovery(struct wil6210_priv *wil)
diff --git a/drivers/net/wireless/ath/wil6210/pmc.c b/drivers/net/wireless/ath/wil6210/pmc.c
index 5ca0307a3274..b9faae0278c9 100644
--- a/drivers/net/wireless/ath/wil6210/pmc.c
+++ b/drivers/net/wireless/ath/wil6210/pmc.c
@@ -54,6 +54,7 @@ void wil_pmc_alloc(struct wil6210_priv *wil,
struct pmc_ctx *pmc = &wil->pmc;
struct device *dev = wil_to_dev(wil);
struct wmi_pmc_cmd pmc_cmd = {0};
+ int last_cmd_err = -ENOMEM;
mutex_lock(&pmc->lock);
@@ -62,6 +63,29 @@ void wil_pmc_alloc(struct wil6210_priv *wil,
wil_err(wil, "%s: ERROR pmc is already allocated\n", __func__);
goto no_release_err;
}
+ if ((num_descriptors <= 0) || (descriptor_size <= 0)) {
+ wil_err(wil,
+ "Invalid params num_descriptors(%d), descriptor_size(%d)\n",
+ num_descriptors, descriptor_size);
+ last_cmd_err = -EINVAL;
+ goto no_release_err;
+ }
+
+ if (num_descriptors > (1 << WIL_RING_SIZE_ORDER_MAX)) {
+ wil_err(wil,
+ "num_descriptors(%d) exceeds max ring size %d\n",
+ num_descriptors, 1 << WIL_RING_SIZE_ORDER_MAX);
+ last_cmd_err = -EINVAL;
+ goto no_release_err;
+ }
+
+ if (num_descriptors > INT_MAX / descriptor_size) {
+ wil_err(wil,
+ "Overflow in num_descriptors(%d)*descriptor_size(%d)\n",
+ num_descriptors, descriptor_size);
+ last_cmd_err = -EINVAL;
+ goto no_release_err;
+ }
pmc->num_descriptors = num_descriptors;
pmc->descriptor_size = descriptor_size;
@@ -189,7 +213,7 @@ release_pmc_skb_list:
pmc->descriptors = NULL;
no_release_err:
- pmc->last_cmd_status = -ENOMEM;
+ pmc->last_cmd_status = last_cmd_err;
mutex_unlock(&pmc->lock);
}
@@ -295,7 +319,7 @@ ssize_t wil_pmc_read(struct file *filp, char __user *buf, size_t count,
size_t retval = 0;
unsigned long long idx;
loff_t offset;
- size_t pmc_size = pmc->descriptor_size * pmc->num_descriptors;
+ size_t pmc_size;
mutex_lock(&pmc->lock);
@@ -306,6 +330,8 @@ ssize_t wil_pmc_read(struct file *filp, char __user *buf, size_t count,
return -EPERM;
}
+ pmc_size = pmc->descriptor_size * pmc->num_descriptors;
+
wil_dbg_misc(wil,
"%s: size %u, pos %lld\n",
__func__, (unsigned)count, *f_pos);
@@ -345,7 +371,18 @@ loff_t wil_pmc_llseek(struct file *filp, loff_t off, int whence)
loff_t newpos;
struct wil6210_priv *wil = filp->private_data;
struct pmc_ctx *pmc = &wil->pmc;
- size_t pmc_size = pmc->descriptor_size * pmc->num_descriptors;
+ size_t pmc_size;
+
+ mutex_lock(&pmc->lock);
+
+ if (!wil_is_pmc_allocated(pmc)) {
+ wil_err(wil, "error, pmc is not allocated!\n");
+ pmc->last_cmd_status = -EPERM;
+ mutex_unlock(&pmc->lock);
+ return -EPERM;
+ }
+
+ pmc_size = pmc->descriptor_size * pmc->num_descriptors;
switch (whence) {
case 0: /* SEEK_SET */
@@ -361,15 +398,21 @@ loff_t wil_pmc_llseek(struct file *filp, loff_t off, int whence)
break;
default: /* can't happen */
- return -EINVAL;
+ newpos = -EINVAL;
+ goto out;
}
- if (newpos < 0)
- return -EINVAL;
+ if (newpos < 0) {
+ newpos = -EINVAL;
+ goto out;
+ }
if (newpos > pmc_size)
newpos = pmc_size;
filp->f_pos = newpos;
+out:
+ mutex_unlock(&pmc->lock);
+
return newpos;
}
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index a19dba5b9e5f..8961b4ce4898 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -837,6 +837,7 @@ void wil_configure_interrupt_moderation(struct wil6210_priv *wil);
void wil_disable_irq(struct wil6210_priv *wil);
void wil_enable_irq(struct wil6210_priv *wil);
void wil6210_mask_halp(struct wil6210_priv *wil);
+void wil6210_unmask_halp(struct wil6210_priv *wil);
/* P2P */
bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request);
@@ -902,6 +903,8 @@ void wil6210_unmask_irq_tx(struct wil6210_priv *wil);
void wil_rx_handle(struct wil6210_priv *wil, int *quota);
void wil6210_unmask_irq_rx(struct wil6210_priv *wil);
+void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil);
+
int wil_iftype_nl2wmi(enum nl80211_iftype type);
int wil_ioctl(struct wil6210_priv *wil, void __user *data, int cmd);
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index e88afac51c5d..f96ab2f4b90e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -1557,6 +1557,8 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
/* the fw is stopped, the aux sta is dead: clean up driver state */
iwl_mvm_del_aux_sta(mvm);
+ iwl_free_fw_paging(mvm);
+
/*
* Clear IN_HW_RESTART flag when stopping the hw (as restart_complete()
* won't be called in this case).
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index c3adf2bcdc85..13c97f665ba8 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -645,8 +645,6 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++)
kfree(mvm->nvm_sections[i].data);
- iwl_free_fw_paging(mvm);
-
iwl_mvm_tof_clean(mvm);
ieee80211_free_hw(mvm->hw);
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 8c7204738aa3..00e0332e2544 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -731,8 +731,8 @@ static int iwl_pcie_rsa_race_bug_wa(struct iwl_trans *trans)
*/
val = iwl_read_prph(trans, PREG_AUX_BUS_WPROT_0);
if (val & (BIT(1) | BIT(17))) {
- IWL_INFO(trans,
- "can't access the RSA semaphore it is write protected\n");
+ IWL_DEBUG_INFO(trans,
+ "can't access the RSA semaphore it is write protected\n");
return 0;
}
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index c00a7daaa4bc..0cd95120bc78 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -2723,6 +2723,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] ||
!info->attrs[HWSIM_ATTR_FLAGS] ||
!info->attrs[HWSIM_ATTR_COOKIE] ||
+ !info->attrs[HWSIM_ATTR_SIGNAL] ||
!info->attrs[HWSIM_ATTR_TX_INFO])
goto out;
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c
index a6c8a4f7bfe9..d6c4f0f60839 100644
--- a/drivers/net/wireless/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/mwifiex/sta_ioctl.c
@@ -313,6 +313,7 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
mwifiex_dbg(adapter, ERROR,
"Attempt to reconnect on csa closed chan(%d)\n",
bss_desc->channel);
+ ret = -1;
goto done;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c
index 0517a4f2d3f2..7a40d8dffa36 100644
--- a/drivers/net/wireless/realtek/rtlwifi/base.c
+++ b/drivers/net/wireless/realtek/rtlwifi/base.c
@@ -1660,9 +1660,9 @@ void rtl_watchdog_wq_callback(void *data)
if (((rtlpriv->link_info.num_rx_inperiod +
rtlpriv->link_info.num_tx_inperiod) > 8) ||
(rtlpriv->link_info.num_rx_inperiod > 2))
- rtl_lps_enter(hw);
- else
rtl_lps_leave(hw);
+ else
+ rtl_lps_enter(hw);
}
rtlpriv->link_info.num_rx_inperiod = 0;
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c
index f2b9d11adc9e..e85f1652ce55 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c
@@ -1203,7 +1203,6 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist,
/* Force GNT_BT to low */
btcoexist->btc_write_1byte_bitmask(btcoexist, 0x765, 0x18, 0x0);
- btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0);
if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) {
/* tell firmware "no antenna inverse" */
@@ -1211,19 +1210,25 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist,
h2c_parameter[1] = 1; /* ext switch type */
btcoexist->btc_fill_h2c(btcoexist, 0x65, 2,
h2c_parameter);
+ btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0);
} else {
/* tell firmware "antenna inverse" */
h2c_parameter[0] = 1;
h2c_parameter[1] = 1; /* ext switch type */
btcoexist->btc_fill_h2c(btcoexist, 0x65, 2,
h2c_parameter);
+ btcoexist->btc_write_2byte(btcoexist, 0x948, 0x280);
}
}
/* ext switch setting */
if (use_ext_switch) {
/* fixed internal switch S1->WiFi, S0->BT */
- btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0);
+ if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT)
+ btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0);
+ else
+ btcoexist->btc_write_2byte(btcoexist, 0x948, 0x280);
+
switch (antpos_type) {
case BTC_ANT_WIFI_AT_MAIN:
/* ext switch main at wifi */
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c
index b2791c893417..babd1490f20c 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c
@@ -965,13 +965,38 @@ void exhalbtc_set_chip_type(u8 chip_type)
}
}
-void exhalbtc_set_ant_num(u8 type, u8 ant_num)
+void exhalbtc_set_ant_num(struct rtl_priv *rtlpriv, u8 type, u8 ant_num)
{
if (BT_COEX_ANT_TYPE_PG == type) {
gl_bt_coexist.board_info.pg_ant_num = ant_num;
gl_bt_coexist.board_info.btdm_ant_num = ant_num;
+ /* The antenna position:
+ * Main (default) or Aux for pgAntNum=2 && btdmAntNum =1.
+ * The antenna position should be determined by
+ * auto-detect mechanism.
+ * The following is assumed to main,
+ * and those must be modified
+ * if y auto-detect mechanism is ready
+ */
+ if ((gl_bt_coexist.board_info.pg_ant_num == 2) &&
+ (gl_bt_coexist.board_info.btdm_ant_num == 1))
+ gl_bt_coexist.board_info.btdm_ant_pos =
+ BTC_ANTENNA_AT_MAIN_PORT;
+ else
+ gl_bt_coexist.board_info.btdm_ant_pos =
+ BTC_ANTENNA_AT_MAIN_PORT;
} else if (BT_COEX_ANT_TYPE_ANTDIV == type) {
gl_bt_coexist.board_info.btdm_ant_num = ant_num;
+ gl_bt_coexist.board_info.btdm_ant_pos =
+ BTC_ANTENNA_AT_MAIN_PORT;
+ } else if (type == BT_COEX_ANT_TYPE_DETECTED) {
+ gl_bt_coexist.board_info.btdm_ant_num = ant_num;
+ if (rtlpriv->cfg->mod_params->ant_sel == 1)
+ gl_bt_coexist.board_info.btdm_ant_pos =
+ BTC_ANTENNA_AT_AUX_PORT;
+ else
+ gl_bt_coexist.board_info.btdm_ant_pos =
+ BTC_ANTENNA_AT_MAIN_PORT;
}
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h
index 0a903ea179ef..f41ca57dd8a7 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h
@@ -535,7 +535,7 @@ void exhalbtc_set_bt_patch_version(u16 bt_hci_version, u16 bt_patch_version);
void exhalbtc_update_min_bt_rssi(char bt_rssi);
void exhalbtc_set_bt_exist(bool bt_exist);
void exhalbtc_set_chip_type(u8 chip_type);
-void exhalbtc_set_ant_num(u8 type, u8 ant_num);
+void exhalbtc_set_ant_num(struct rtl_priv *rtlpriv, u8 type, u8 ant_num);
void exhalbtc_display_bt_coex_info(struct btc_coexist *btcoexist);
void exhalbtc_signal_compensation(struct btc_coexist *btcoexist,
u8 *rssi_wifi, u8 *rssi_bt);
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c
index b9b0cb7af8ea..d3fd9211b3a4 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c
@@ -72,7 +72,10 @@ void rtl_btc_init_hal_vars(struct rtl_priv *rtlpriv)
__func__, bt_type);
exhalbtc_set_chip_type(bt_type);
- exhalbtc_set_ant_num(BT_COEX_ANT_TYPE_PG, ant_num);
+ if (rtlpriv->cfg->mod_params->ant_sel == 1)
+ exhalbtc_set_ant_num(rtlpriv, BT_COEX_ANT_TYPE_DETECTED, 1);
+ else
+ exhalbtc_set_ant_num(rtlpriv, BT_COEX_ANT_TYPE_PG, ant_num);
}
void rtl_btc_init_hw_config(struct rtl_priv *rtlpriv)
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index 7f471bff435c..5b4048041147 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -1573,7 +1573,7 @@ int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw)
true,
HW_DESC_TXBUFF_ADDR),
skb->len, PCI_DMA_TODEVICE);
- kfree_skb(skb);
+ dev_kfree_skb_irq(skb);
ring->idx = (ring->idx + 1) % ring->entries;
}
ring->idx = 0;
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c
index c983d2fe147f..5a3df9198ddf 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c
@@ -2684,6 +2684,7 @@ void rtl8723be_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw,
bool auto_load_fail, u8 *hwinfo)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_mod_params *mod_params = rtlpriv->cfg->mod_params;
u8 value;
u32 tmpu_32;
@@ -2702,6 +2703,10 @@ void rtl8723be_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw,
rtlpriv->btcoexist.btc_info.ant_num = ANT_X2;
}
+ /* override ant_num / ant_path */
+ if (mod_params->ant_sel)
+ rtlpriv->btcoexist.btc_info.ant_num =
+ (mod_params->ant_sel == 1 ? ANT_X2 : ANT_X1);
}
void rtl8723be_bt_reg_init(struct ieee80211_hw *hw)
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c
index a78eaeda0008..2101793438ed 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c
@@ -273,6 +273,7 @@ static struct rtl_mod_params rtl8723be_mod_params = {
.msi_support = false,
.disable_watchdog = false,
.debug = DBG_EMERG,
+ .ant_sel = 0,
};
static struct rtl_hal_cfg rtl8723be_hal_cfg = {
@@ -394,6 +395,7 @@ module_param_named(fwlps, rtl8723be_mod_params.fwctrl_lps, bool, 0444);
module_param_named(msi, rtl8723be_mod_params.msi_support, bool, 0444);
module_param_named(disable_watchdog, rtl8723be_mod_params.disable_watchdog,
bool, 0444);
+module_param_named(ant_sel, rtl8723be_mod_params.ant_sel, int, 0444);
MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n");
MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n");
MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n");
@@ -402,6 +404,7 @@ MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n");
MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
MODULE_PARM_DESC(disable_watchdog,
"Set to 1 to disable the watchdog (default 0)\n");
+MODULE_PARM_DESC(ant_sel, "Set to 1 or 2 to force antenna number (default 0)\n");
static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume);
diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h
index 4544752a2ba8..b6faf624480e 100644
--- a/drivers/net/wireless/realtek/rtlwifi/wifi.h
+++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h
@@ -2252,6 +2252,9 @@ struct rtl_mod_params {
/* default 0: 1 means do not disable interrupts */
bool int_clear;
+
+ /* select antenna */
+ int ant_sel;
};
struct rtl_hal_usbint_cfg {
diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c
index e92f2639af2c..9fd3c6af0a61 100644
--- a/drivers/net/wireless/ti/wlcore/init.c
+++ b/drivers/net/wireless/ti/wlcore/init.c
@@ -549,6 +549,11 @@ static int wl12xx_init_ap_role(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret;
+ /* Disable filtering */
+ ret = wl1271_acx_group_address_tbl(wl, wlvif, false, NULL, 0);
+ if (ret < 0)
+ return ret;
+
ret = wl1271_acx_ap_max_tx_retry(wl, wlvif);
if (ret < 0)
return ret;
diff --git a/drivers/nfc/nq-nci.c b/drivers/nfc/nq-nci.c
index 154310020997..2dd18dd78677 100644
--- a/drivers/nfc/nq-nci.c
+++ b/drivers/nfc/nq-nci.c
@@ -53,6 +53,7 @@ struct nqx_dev {
struct mutex read_mutex;
struct i2c_client *client;
struct miscdevice nqx_device;
+ union nqx_uinfo nqx_info;
/* NFC GPIO variables */
unsigned int irq_gpio;
unsigned int en_gpio;
@@ -467,6 +468,25 @@ int nfc_ioctl_core_reset_ntf(struct file *filp)
return nqx_dev->core_reset_ntf;
}
+/*
+ * Inside nfc_ioctl_nfcc_info
+ *
+ * @brief nfc_ioctl_nfcc_info
+ *
+ * Check the NQ Chipset and firmware version details
+ */
+unsigned int nfc_ioctl_nfcc_info(struct file *filp, unsigned long arg)
+{
+ unsigned int r = 0;
+ struct nqx_dev *nqx_dev = filp->private_data;
+
+ r = nqx_dev->nqx_info.i;
+ dev_dbg(&nqx_dev->client->dev,
+ "nqx nfc : nfc_ioctl_nfcc_info r = %d\n", r);
+
+ return r;
+}
+
static long nfc_ioctl(struct file *pfile, unsigned int cmd,
unsigned long arg)
{
@@ -489,6 +509,9 @@ static long nfc_ioctl(struct file *pfile, unsigned int cmd,
case NFCC_INITIAL_CORE_RESET_NTF:
r = nfc_ioctl_core_reset_ntf(pfile);
break;
+ case NFCC_GET_INFO:
+ r = nfc_ioctl_nfcc_info(pfile, arg);
+ break;
default:
r = -ENOIOCTLCMD;
}
@@ -508,13 +531,16 @@ static const struct file_operations nfc_dev_fops = {
};
/* Check for availability of NQ_ NFC controller hardware */
-static int nfcc_hw_check(struct i2c_client *client, unsigned int enable_gpio)
+static int nfcc_hw_check(struct i2c_client *client, struct nqx_dev *nqx_dev)
{
int ret = 0;
unsigned char raw_nci_reset_cmd[] = {0x20, 0x00, 0x01, 0x00};
+ unsigned char raw_nci_init_cmd[] = {0x20, 0x01, 0x00};
+ unsigned char nci_init_rsp[28];
unsigned char nci_reset_rsp[6];
-
+ unsigned char init_rsp_len = 0;
+ unsigned int enable_gpio = nqx_dev->en_gpio;
/* making sure that the NFCC starts in a clean state. */
gpio_set_value(enable_gpio, 0);/* ULPM: Disable */
/* hardware dependent delay */
@@ -536,16 +562,75 @@ static int nfcc_hw_check(struct i2c_client *client, unsigned int enable_gpio)
/* Read Response of RESET command */
ret = i2c_master_recv(client, nci_reset_rsp,
- sizeof(nci_reset_rsp));
+ sizeof(nci_reset_rsp));
dev_err(&client->dev,
- "%s: - nq - reset cmd answer : NfcNciRx %x %x %x\n",
- __func__, nci_reset_rsp[0],
- nci_reset_rsp[1], nci_reset_rsp[2]);
+ "%s: - nq - reset cmd answer : NfcNciRx %x %x %x\n",
+ __func__, nci_reset_rsp[0],
+ nci_reset_rsp[1], nci_reset_rsp[2]);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s: - i2c_master_recv Error\n", __func__);
+ goto err_nfcc_hw_check;
+ }
+ ret = i2c_master_send(client, raw_nci_init_cmd,
+ sizeof(raw_nci_init_cmd));
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s: - i2c_master_send Error\n", __func__);
+ goto err_nfcc_hw_check;
+ }
+ /* hardware dependent delay */
+ msleep(30);
+ /* Read Response of INIT command */
+ ret = i2c_master_recv(client, nci_init_rsp,
+ sizeof(nci_init_rsp));
if (ret < 0) {
dev_err(&client->dev,
"%s: - i2c_master_recv Error\n", __func__);
goto err_nfcc_hw_check;
}
+ init_rsp_len = 2 + nci_init_rsp[2]; /*payload + len*/
+ if (init_rsp_len > PAYLOAD_HEADER_LENGTH) {
+ nqx_dev->nqx_info.info.chip_type =
+ nci_init_rsp[init_rsp_len - 3];
+ nqx_dev->nqx_info.info.rom_version =
+ nci_init_rsp[init_rsp_len - 2];
+ nqx_dev->nqx_info.info.fw_major =
+ nci_init_rsp[init_rsp_len - 1];
+ nqx_dev->nqx_info.info.fw_minor =
+ nci_init_rsp[init_rsp_len];
+ }
+ dev_dbg(&nqx_dev->client->dev, "NQ NFCC chip_type = %x\n",
+ nqx_dev->nqx_info.info.chip_type);
+ dev_dbg(&nqx_dev->client->dev, "NQ fw version = %x.%x.%x\n",
+ nqx_dev->nqx_info.info.rom_version,
+ nqx_dev->nqx_info.info.fw_major,
+ nqx_dev->nqx_info.info.fw_minor);
+
+ switch (nqx_dev->nqx_info.info.chip_type) {
+ case NFCC_NQ_210:
+ dev_dbg(&client->dev,
+ "%s: ## NFCC == NQ210 ##\n", __func__);
+ break;
+ case NFCC_NQ_220:
+ dev_dbg(&client->dev,
+ "%s: ## NFCC == NQ220 ##\n", __func__);
+ break;
+ case NFCC_NQ_310:
+ dev_dbg(&client->dev,
+ "%s: ## NFCC == NQ310 ##\n", __func__);
+ break;
+ case NFCC_NQ_330:
+ dev_dbg(&client->dev,
+ "%s: ## NFCC == NQ330 ##\n", __func__);
+ break;
+ default:
+ dev_err(&client->dev,
+ "%s: - NFCC HW not Supported\n", __func__);
+ break;
+ }
+
+ /*Disable NFC by default to save power on boot*/
gpio_set_value(enable_gpio, 0);/* ULPM: Disable */
ret = 0;
goto done;
@@ -566,9 +651,7 @@ done:
static int nqx_clock_select(struct nqx_dev *nqx_dev)
{
int r = 0;
-
- nqx_dev->s_clk =
- clk_get(&nqx_dev->client->dev, "ref_clk");
+ nqx_dev->s_clk = clk_get(&nqx_dev->client->dev, "ref_clk");
if (nqx_dev->s_clk == NULL)
goto err_clk;
@@ -867,8 +950,7 @@ static int nqx_probe(struct i2c_client *client,
* present before attempting further hardware initialisation.
*
*/
-
- r = nfcc_hw_check(client, platform_data->en_gpio);
+ r = nfcc_hw_check(client, nqx_dev);
if (r) {
/* make sure NFCC is not enabled */
gpio_set_value(platform_data->en_gpio, 0);
diff --git a/drivers/nfc/nq-nci.h b/drivers/nfc/nq-nci.h
index d62100c2d15a..c635e818b1f3 100644
--- a/drivers/nfc/nq-nci.h
+++ b/drivers/nfc/nq-nci.h
@@ -22,6 +22,7 @@
#include <linux/ioctl.h>
#include <linux/miscdevice.h>
+#include <linux/nfcinfo.h>
#define NFC_SET_PWR _IOW(0xE9, 0x01, unsigned int)
#define ESE_SET_PWR _IOW(0xE9, 0x02, unsigned int)
@@ -42,4 +43,12 @@ enum nfcc_initial_core_reset_ntf {
DEFAULT_INITIAL_CORE_RESET_NTF, /*2*/
};
+enum nfcc_chip_variant {
+ NFCC_NQ_210 = 0x48, /**< NFCC NQ210 */
+ NFCC_NQ_220 = 0x58, /**< NFCC NQ220 */
+ NFCC_NQ_310 = 0x40, /**< NFCC NQ310 */
+ NFCC_NQ_330 = 0x51, /**< NFCC NQ330 */
+ NFCC_NOT_SUPPORTED = 0xFF /**< NFCC is not supported */
+};
+
#endif
diff --git a/drivers/nvmem/mxs-ocotp.c b/drivers/nvmem/mxs-ocotp.c
index 8ba19bba3156..2bb3c5799ac4 100644
--- a/drivers/nvmem/mxs-ocotp.c
+++ b/drivers/nvmem/mxs-ocotp.c
@@ -94,7 +94,7 @@ static int mxs_ocotp_read(void *context, const void *reg, size_t reg_size,
if (ret)
goto close_banks;
- while (val_size) {
+ while (val_size >= reg_size) {
if ((offset < OCOTP_DATA_OFFSET) || (offset % 16)) {
/* fill up non-data register */
*buf = 0;
@@ -103,7 +103,7 @@ static int mxs_ocotp_read(void *context, const void *reg, size_t reg_size,
}
buf++;
- val_size--;
+ val_size -= reg_size;
offset += reg_size;
}
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 0acebc87ec20..0438512f4d69 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -760,6 +760,16 @@ const void * __init of_flat_dt_match_machine(const void *default_match,
}
#ifdef CONFIG_BLK_DEV_INITRD
+#ifndef __early_init_dt_declare_initrd
+static void __early_init_dt_declare_initrd(unsigned long start,
+ unsigned long end)
+{
+ initrd_start = (unsigned long)__va(start);
+ initrd_end = (unsigned long)__va(end);
+ initrd_below_start_ok = 1;
+}
+#endif
+
/**
* early_init_dt_check_for_initrd - Decode initrd location from flat tree
* @node: reference to node containing initrd location ('chosen')
@@ -782,9 +792,7 @@ static void __init early_init_dt_check_for_initrd(unsigned long node)
return;
end = of_read_number(prop, len/4);
- initrd_start = (unsigned long)__va(start);
- initrd_end = (unsigned long)__va(end);
- initrd_below_start_ok = 1;
+ __early_init_dt_declare_initrd(start, end);
pr_debug("initrd_start=0x%llx initrd_end=0x%llx\n",
(unsigned long long)start, (unsigned long long)end);
@@ -1006,13 +1014,16 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
}
#ifdef CONFIG_HAVE_MEMBLOCK
+#ifndef MIN_MEMBLOCK_ADDR
+#define MIN_MEMBLOCK_ADDR __pa(PAGE_OFFSET)
+#endif
#ifndef MAX_MEMBLOCK_ADDR
#define MAX_MEMBLOCK_ADDR ((phys_addr_t)~0)
#endif
void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
{
- const u64 phys_offset = __pa(PAGE_OFFSET);
+ const u64 phys_offset = MIN_MEMBLOCK_ADDR;
if (!PAGE_ALIGNED(base)) {
if (size < PAGE_SIZE - (base & ~PAGE_MASK)) {
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 72a2c1969646..28da6242eb84 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -386,13 +386,13 @@ int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)
EXPORT_SYMBOL_GPL(of_irq_to_resource);
/**
- * of_irq_get - Decode a node's IRQ and return it as a Linux irq number
+ * of_irq_get - Decode a node's IRQ and return it as a Linux IRQ number
* @dev: pointer to device tree node
- * @index: zero-based index of the irq
- *
- * Returns Linux irq number on success, or -EPROBE_DEFER if the irq domain
- * is not yet created.
+ * @index: zero-based index of the IRQ
*
+ * Returns Linux IRQ number on success, or 0 on the IRQ mapping failure, or
+ * -EPROBE_DEFER if the IRQ domain is not yet created, or error code in case
+ * of any other failure.
*/
int of_irq_get(struct device_node *dev, int index)
{
@@ -413,12 +413,13 @@ int of_irq_get(struct device_node *dev, int index)
EXPORT_SYMBOL_GPL(of_irq_get);
/**
- * of_irq_get_byname - Decode a node's IRQ and return it as a Linux irq number
+ * of_irq_get_byname - Decode a node's IRQ and return it as a Linux IRQ number
* @dev: pointer to device tree node
- * @name: irq name
+ * @name: IRQ name
*
- * Returns Linux irq number on success, or -EPROBE_DEFER if the irq domain
- * is not yet created, or error code in case of any other failure.
+ * Returns Linux IRQ number on success, or 0 on the IRQ mapping failure, or
+ * -EPROBE_DEFER if the IRQ domain is not yet created, or error code in case
+ * of any other failure.
*/
int of_irq_get_byname(struct device_node *dev, const char *name)
{
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index 14af8ca66d1c..240bf2903308 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -619,6 +619,7 @@ struct msm_pcie_dev_t {
bool ext_ref_clk;
bool common_phy;
uint32_t ep_latency;
+ uint32_t wr_halt_size;
uint32_t cpl_timeout;
uint32_t current_bdf;
short current_short_bdf;
@@ -1976,6 +1977,8 @@ static void msm_pcie_show_status(struct msm_pcie_dev_t *dev)
dev->common_phy);
PCIE_DBG_FS(dev, "ep_latency: %dms\n",
dev->ep_latency);
+ PCIE_DBG_FS(dev, "wr_halt_size: 0x%x\n",
+ dev->wr_halt_size);
PCIE_DBG_FS(dev, "cpl_timeout: 0x%x\n",
dev->cpl_timeout);
PCIE_DBG_FS(dev, "current_bdf: 0x%x\n",
@@ -4495,8 +4498,19 @@ int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options)
if (dev->use_msi) {
PCIE_DBG(dev, "RC%d: enable WR halt.\n", dev->rc_idx);
- msm_pcie_write_mask(dev->parf +
- PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT, 0, BIT(31));
+ val = dev->wr_halt_size ? dev->wr_halt_size :
+ readl_relaxed(dev->parf +
+ PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
+
+ msm_pcie_write_reg(dev->parf,
+ PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT,
+ BIT(31) | val);
+
+ PCIE_DBG(dev,
+ "RC%d: PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT: 0x%x.\n",
+ dev->rc_idx,
+ readl_relaxed(dev->parf +
+ PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT));
}
mutex_lock(&com_phy_lock);
@@ -5385,9 +5399,6 @@ static irqreturn_t handle_linkdown_irq(int irq, void *data)
dev->link_status = MSM_PCIE_LINK_DISABLED;
dev->shadow_en = false;
- pcie_phy_dump(dev);
- pcie_parf_dump(dev);
-
if (dev->linkdown_panic)
panic("User has chosen to panic on linkdown\n");
@@ -5420,7 +5431,7 @@ static irqreturn_t handle_msi_irq(int irq, void *data)
struct msm_pcie_dev_t *dev = data;
void __iomem *ctrl_status;
- PCIE_DBG(dev, "irq=%d\n", irq);
+ PCIE_DUMP(dev, "irq: %d\n", irq);
/* check for set bits, clear it by setting that bit
and trigger corresponding irq */
@@ -5696,7 +5707,7 @@ static int arch_setup_msi_irq_qgic(struct pci_dev *pdev,
irq_set_msi_desc(firstirq, desc);
msg.address_hi = 0;
msg.address_lo = dev->msi_gicm_addr;
- msg.data = dev->msi_gicm_base;
+ msg.data = dev->msi_gicm_base + (firstirq - dev->msi[0].num);
write_msi_msg(firstirq, &msg);
return 0;
@@ -6071,6 +6082,18 @@ static int msm_pcie_probe(struct platform_device *pdev)
PCIE_DBG(&msm_pcie_dev[rc_idx], "RC%d: ep-latency: 0x%x.\n",
rc_idx, msm_pcie_dev[rc_idx].ep_latency);
+ msm_pcie_dev[rc_idx].wr_halt_size = 0;
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,wr-halt-size",
+ &msm_pcie_dev[rc_idx].wr_halt_size);
+ if (ret)
+ PCIE_DBG(&msm_pcie_dev[rc_idx],
+ "RC%d: wr-halt-size not specified in dt. Use default value.\n",
+ rc_idx);
+ else
+ PCIE_DBG(&msm_pcie_dev[rc_idx], "RC%d: wr-halt-size: 0x%x.\n",
+ rc_idx, msm_pcie_dev[rc_idx].wr_halt_size);
+
msm_pcie_dev[rc_idx].cpl_timeout = 0;
ret = of_property_read_u32((&pdev->dev)->of_node,
"qcom,cpl-timeout",
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 7aafb5fb9336..9757cf9037a2 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -179,9 +179,6 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
u16 orig_cmd;
struct pci_bus_region region, inverted_region;
- if (dev->non_compliant_bars)
- return 0;
-
mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
/* No printks while decoding is disabled! */
@@ -322,6 +319,9 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
{
unsigned int pos, reg;
+ if (dev->non_compliant_bars)
+ return;
+
for (pos = 0; pos < howmany; pos++) {
struct resource *res = &dev->resource[pos];
reg = PCI_BASE_ADDRESS_0 + (pos << 2);
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
index 5c717275a7fa..181b35879ebd 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
@@ -939,7 +939,8 @@ static int mtk_gpio_set_debounce(struct gpio_chip *chip, unsigned offset,
struct mtk_pinctrl *pctl = dev_get_drvdata(chip->dev);
int eint_num, virq, eint_offset;
unsigned int set_offset, bit, clr_bit, clr_offset, rst, i, unmask, dbnc;
- static const unsigned int dbnc_arr[] = {0 , 1, 16, 32, 64, 128, 256};
+ static const unsigned int debounce_time[] = {500, 1000, 16000, 32000, 64000,
+ 128000, 256000};
const struct mtk_desc_pin *pin;
struct irq_data *d;
@@ -957,9 +958,9 @@ static int mtk_gpio_set_debounce(struct gpio_chip *chip, unsigned offset,
if (!mtk_eint_can_en_debounce(pctl, eint_num))
return -ENOSYS;
- dbnc = ARRAY_SIZE(dbnc_arr);
- for (i = 0; i < ARRAY_SIZE(dbnc_arr); i++) {
- if (debounce <= dbnc_arr[i]) {
+ dbnc = ARRAY_SIZE(debounce_time);
+ for (i = 0; i < ARRAY_SIZE(debounce_time); i++) {
+ if (debounce <= debounce_time[i]) {
dbnc = i;
break;
}
@@ -1190,9 +1191,10 @@ static void mtk_eint_irq_handler(struct irq_desc *desc)
const struct mtk_desc_pin *pin;
chained_irq_enter(chip, desc);
- for (eint_num = 0; eint_num < pctl->devdata->ap_num; eint_num += 32) {
+ for (eint_num = 0;
+ eint_num < pctl->devdata->ap_num;
+ eint_num += 32, reg += 4) {
status = readl(reg);
- reg += 4;
while (status) {
offset = __ffs(status);
index = eint_num + offset;
diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c
index 33edd07d9149..b3235fd2950c 100644
--- a/drivers/pinctrl/pinctrl-at91-pio4.c
+++ b/drivers/pinctrl/pinctrl-at91-pio4.c
@@ -717,9 +717,11 @@ static int atmel_conf_pin_config_group_set(struct pinctrl_dev *pctldev,
break;
case PIN_CONFIG_BIAS_PULL_UP:
conf |= ATMEL_PIO_PUEN_MASK;
+ conf &= (~ATMEL_PIO_PDEN_MASK);
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
conf |= ATMEL_PIO_PDEN_MASK;
+ conf &= (~ATMEL_PIO_PUEN_MASK);
break;
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
if (arg == 0)
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
index ef04b962c3d5..23b6b8c29a99 100644
--- a/drivers/pinctrl/pinctrl-single.c
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -1273,9 +1273,9 @@ static int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs,
/* Parse pins in each row from LSB */
while (mask) {
- bit_pos = ffs(mask);
+ bit_pos = __ffs(mask);
pin_num_from_lsb = bit_pos / pcs->bits_per_pin;
- mask_pos = ((pcs->fmask) << (bit_pos - 1));
+ mask_pos = ((pcs->fmask) << bit_pos);
val_pos = val & mask_pos;
submask = mask & mask_pos;
@@ -1847,7 +1847,7 @@ static int pcs_probe(struct platform_device *pdev)
ret = of_property_read_u32(np, "pinctrl-single,function-mask",
&pcs->fmask);
if (!ret) {
- pcs->fshift = ffs(pcs->fmask) - 1;
+ pcs->fshift = __ffs(pcs->fmask);
pcs->fmax = pcs->fmask >> pcs->fshift;
} else {
/* If mask property doesn't exist, function mux is invalid. */
diff --git a/drivers/pinctrl/qcom/pinctrl-msmfalcon.c b/drivers/pinctrl/qcom/pinctrl-msmfalcon.c
index 45db409eb7c1..3678f952fe0f 100644
--- a/drivers/pinctrl/qcom/pinctrl-msmfalcon.c
+++ b/drivers/pinctrl/qcom/pinctrl-msmfalcon.c
@@ -354,6 +354,8 @@ enum msmfalcon_functions {
msm_mux_blsp_i2c2,
msm_mux_phase_flag31,
msm_mux_blsp_spi3,
+ msm_mux_blsp_spi3_cs1,
+ msm_mux_blsp_spi3_cs2,
msm_mux_wlan1_adc1,
msm_mux_atest_usb13,
msm_mux_tgu_ch1,
@@ -380,7 +382,10 @@ enum msmfalcon_functions {
msm_mux_blsp_spi4,
msm_mux_pri_mi2s,
msm_mux_phase_flag26,
- msm_mux_qdss_cti,
+ msm_mux_qdss_cti0_a,
+ msm_mux_qdss_cti0_b,
+ msm_mux_qdss_cti1_a,
+ msm_mux_qdss_cti1_b,
msm_mux_DP_HOT,
msm_mux_pri_mi2s_ws,
msm_mux_phase_flag27,
@@ -402,7 +407,8 @@ enum msmfalcon_functions {
msm_mux_phase_flag13,
msm_mux_vsense_mode,
msm_mux_blsp_spi7,
- msm_mux_BLSP_UART,
+ msm_mux_blsp_uart6_a,
+ msm_mux_blsp_uart6_b,
msm_mux_sec_mi2s,
msm_mux_sndwire_clk,
msm_mux_phase_flag17,
@@ -415,13 +421,15 @@ enum msmfalcon_functions {
msm_mux_vfr_1,
msm_mux_phase_flag20,
msm_mux_NFC_INT,
- msm_mux_blsp_spi,
+ msm_mux_blsp_spi8_cs1,
+ msm_mux_blsp_spi8_cs2,
msm_mux_m_voc,
msm_mux_phase_flag21,
msm_mux_NFC_EN,
msm_mux_phase_flag22,
msm_mux_NFC_DWL,
- msm_mux_BLSP_I2C,
+ msm_mux_blsp_i2c8_a,
+ msm_mux_blsp_i2c8_b,
msm_mux_phase_flag23,
msm_mux_NFC_ESE,
msm_mux_pwr_modem,
@@ -519,10 +527,15 @@ enum msmfalcon_functions {
msm_mux_atest_char0,
msm_mux_US_EURO,
msm_mux_LCD_BACKLIGHT,
- msm_mux_blsp_spi8,
+ msm_mux_blsp_spi8_a,
+ msm_mux_blsp_spi8_b,
msm_mux_sp_cmu,
- msm_mux_NAV_PPS,
- msm_mux_GPS_TX,
+ msm_mux_nav_pps_a,
+ msm_mux_nav_pps_b,
+ msm_mux_nav_pps_c,
+ msm_mux_gps_tx_a,
+ msm_mux_gps_tx_b,
+ msm_mux_gps_tx_c,
msm_mux_adsp_ext,
msm_mux_TS_RESET,
msm_mux_ssc_irq,
@@ -560,16 +573,23 @@ static const char * const blsp_spi1_groups[] = {
"gpio0", "gpio1", "gpio2", "gpio3", "gpio46",
};
static const char * const gpio_groups[] = {
- "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio6", "gpio7", "gpio8",
- "gpio9", "gpio10", "gpio11", "gpio14", "gpio15", "gpio16", "gpio17",
- "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", "gpio23", "gpio24",
- "gpio25", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36", "gpio37",
- "gpio38", "gpio39", "gpio57", "gpio58", "gpio59", "gpio61", "gpio65",
- "gpio81", "gpio82", "gpio83", "gpio84", "gpio85", "gpio86", "gpio87",
- "gpio88", "gpio89", "gpio90", "gpio91", "gpio92", "gpio93", "gpio94",
- "gpio95", "gpio96", "gpio97", "gpio98", "gpio99", "gpio100", "gpio101",
- "gpio102", "gpio103", "gpio104", "gpio105", "gpio106", "gpio107",
- "gpio108", "gpio109", "gpio110", "gpio111", "gpio112", "gpio113",
+ "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7",
+ "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14",
+ "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21",
+ "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28",
+ "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35",
+ "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42",
+ "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49",
+ "gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56",
+ "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63",
+ "gpio64", "gpio65", "gpio66", "gpio67", "gpio68", "gpio69", "gpio70",
+ "gpio71", "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77",
+ "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", "gpio84",
+ "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", "gpio91",
+ "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98",
+ "gpio99", "gpio100", "gpio101", "gpio102", "gpio103", "gpio104",
+ "gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110",
+ "gpio111", "gpio112", "gpio113",
};
static const char * const blsp_uim1_groups[] = {
"gpio0", "gpio1",
@@ -599,7 +619,13 @@ static const char * const phase_flag31_groups[] = {
"gpio6",
};
static const char * const blsp_spi3_groups[] = {
- "gpio8", "gpio9", "gpio10", "gpio11", "gpio30", "gpio65",
+ "gpio8", "gpio9", "gpio10", "gpio11",
+};
+static const char * const blsp_spi3_cs1_groups[] = {
+ "gpio30",
+};
+static const char * const blsp_spi3_cs2_groups[] = {
+ "gpio65",
};
static const char * const wlan1_adc1_groups[] = {
"gpio8",
@@ -679,9 +705,17 @@ static const char * const pri_mi2s_groups[] = {
static const char * const phase_flag26_groups[] = {
"gpio12",
};
-static const char * const qdss_cti_groups[] = {
- "gpio12", "gpio13", "gpio21", "gpio49", "gpio50", "gpio53", "gpio55",
- "gpio66",
+static const char * const qdss_cti0_a_groups[] = {
+ "gpio49", "gpio50",
+};
+static const char * const qdss_cti0_b_groups[] = {
+ "gpio13", "gpio21",
+};
+static const char * const qdss_cti1_a_groups[] = {
+ "gpio53", "gpio55",
+};
+static const char * const qdss_cti1_b_groups[] = {
+ "gpio12", "gpio66",
};
static const char * const DP_HOT_groups[] = {
"gpio13",
@@ -746,9 +780,11 @@ static const char * const vsense_mode_groups[] = {
static const char * const blsp_spi7_groups[] = {
"gpio24", "gpio25", "gpio26", "gpio27",
};
-static const char * const BLSP_UART_groups[] = {
- "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", "gpio30",
- "gpio31",
+static const char * const blsp_uart6_a_groups[] = {
+ "gpio24", "gpio25", "gpio26", "gpio27",
+};
+static const char * const blsp_uart6_b_groups[] = {
+ "gpio28", "gpio29", "gpio30", "gpio31",
};
static const char * const sec_mi2s_groups[] = {
"gpio24", "gpio25", "gpio26", "gpio27", "gpio62",
@@ -786,9 +822,11 @@ static const char * const phase_flag20_groups[] = {
static const char * const NFC_INT_groups[] = {
"gpio28",
};
-static const char * const blsp_spi_groups[] = {
- "gpio28", "gpio29", "gpio30", "gpio31", "gpio40", "gpio41", "gpio44",
- "gpio52",
+static const char * const blsp_spi8_a_groups[] = {
+ "gpio28", "gpio29", "gpio30", "gpio31",
+};
+static const char * const blsp_spi8_b_groups[] = {
+ "gpio40", "gpio41", "gpio44", "gpio52",
};
static const char * const m_voc_groups[] = {
"gpio28",
@@ -805,8 +843,11 @@ static const char * const phase_flag22_groups[] = {
static const char * const NFC_DWL_groups[] = {
"gpio30",
};
-static const char * const BLSP_I2C_groups[] = {
- "gpio30", "gpio31", "gpio44", "gpio52",
+static const char * const blsp_i2c8_a_groups[] = {
+ "gpio30", "gpio31",
+};
+static const char * const blsp_i2c8_b_groups[] = {
+ "gpio44", "gpio52",
};
static const char * const phase_flag23_groups[] = {
"gpio30",
@@ -1099,17 +1140,32 @@ static const char * const US_EURO_groups[] = {
static const char * const LCD_BACKLIGHT_groups[] = {
"gpio64",
};
-static const char * const blsp_spi8_groups[] = {
- "gpio64", "gpio76",
+static const char * const blsp_spi8_cs1_groups[] = {
+ "gpio64",
+};
+static const char * const blsp_spi8_cs2_groups[] = {
+ "gpio76",
};
static const char * const sp_cmu_groups[] = {
"gpio64",
};
-static const char * const NAV_PPS_groups[] = {
- "gpio65", "gpio65", "gpio80", "gpio80", "gpio98", "gpio98",
+static const char * const nav_pps_a_groups[] = {
+ "gpio65",
};
-static const char * const GPS_TX_groups[] = {
- "gpio65", "gpio80", "gpio98",
+static const char * const nav_pps_b_groups[] = {
+ "gpio98",
+};
+static const char * const nav_pps_c_groups[] = {
+ "gpio80",
+};
+static const char * const gps_tx_a_groups[] = {
+ "gpio65",
+};
+static const char * const gps_tx_b_groups[] = {
+ "gpio98",
+};
+static const char * const gps_tx_c_groups[] = {
+ "gpio80",
};
static const char * const adsp_ext_groups[] = {
"gpio65",
@@ -1216,6 +1272,8 @@ static const struct msm_function msmfalcon_functions[] = {
FUNCTION(blsp_i2c2),
FUNCTION(phase_flag31),
FUNCTION(blsp_spi3),
+ FUNCTION(blsp_spi3_cs1),
+ FUNCTION(blsp_spi3_cs2),
FUNCTION(wlan1_adc1),
FUNCTION(atest_usb13),
FUNCTION(tgu_ch1),
@@ -1242,7 +1300,10 @@ static const struct msm_function msmfalcon_functions[] = {
FUNCTION(blsp_spi4),
FUNCTION(pri_mi2s),
FUNCTION(phase_flag26),
- FUNCTION(qdss_cti),
+ FUNCTION(qdss_cti0_a),
+ FUNCTION(qdss_cti0_b),
+ FUNCTION(qdss_cti1_a),
+ FUNCTION(qdss_cti1_b),
FUNCTION(DP_HOT),
FUNCTION(pri_mi2s_ws),
FUNCTION(phase_flag27),
@@ -1264,7 +1325,8 @@ static const struct msm_function msmfalcon_functions[] = {
FUNCTION(phase_flag13),
FUNCTION(vsense_mode),
FUNCTION(blsp_spi7),
- FUNCTION(BLSP_UART),
+ FUNCTION(blsp_uart6_a),
+ FUNCTION(blsp_uart6_b),
FUNCTION(sec_mi2s),
FUNCTION(sndwire_clk),
FUNCTION(phase_flag17),
@@ -1277,13 +1339,15 @@ static const struct msm_function msmfalcon_functions[] = {
FUNCTION(vfr_1),
FUNCTION(phase_flag20),
FUNCTION(NFC_INT),
- FUNCTION(blsp_spi),
+ FUNCTION(blsp_spi8_cs1),
+ FUNCTION(blsp_spi8_cs2),
FUNCTION(m_voc),
FUNCTION(phase_flag21),
FUNCTION(NFC_EN),
FUNCTION(phase_flag22),
FUNCTION(NFC_DWL),
- FUNCTION(BLSP_I2C),
+ FUNCTION(blsp_i2c8_a),
+ FUNCTION(blsp_i2c8_b),
FUNCTION(phase_flag23),
FUNCTION(NFC_ESE),
FUNCTION(pwr_modem),
@@ -1381,10 +1445,15 @@ static const struct msm_function msmfalcon_functions[] = {
FUNCTION(atest_char0),
FUNCTION(US_EURO),
FUNCTION(LCD_BACKLIGHT),
- FUNCTION(blsp_spi8),
+ FUNCTION(blsp_spi8_a),
+ FUNCTION(blsp_spi8_b),
FUNCTION(sp_cmu),
- FUNCTION(NAV_PPS),
- FUNCTION(GPS_TX),
+ FUNCTION(nav_pps_a),
+ FUNCTION(nav_pps_b),
+ FUNCTION(nav_pps_c),
+ FUNCTION(gps_tx_a),
+ FUNCTION(gps_tx_b),
+ FUNCTION(gps_tx_c),
FUNCTION(adsp_ext),
FUNCTION(TS_RESET),
FUNCTION(ssc_irq),
@@ -1441,10 +1510,10 @@ static const struct msm_pingroup msmfalcon_groups[] = {
atest_usb11, bimc_dte1, NA),
PINGROUP(11, NORTH, blsp_spi3, blsp_i2c3, NA, dbg_out, wlan2_adc0,
atest_usb10, bimc_dte0, NA, NA),
- PINGROUP(12, NORTH, blsp_spi4, pri_mi2s, NA, phase_flag26, qdss_cti,
+ PINGROUP(12, NORTH, blsp_spi4, pri_mi2s, NA, phase_flag26, qdss_cti1_b,
NA, NA, NA, NA),
PINGROUP(13, NORTH, blsp_spi4, DP_HOT, pri_mi2s_ws, NA, NA,
- phase_flag27, qdss_cti, NA, NA),
+ phase_flag27, qdss_cti0_b, NA, NA),
PINGROUP(14, NORTH, blsp_spi4, blsp_i2c4, pri_mi2s, NA, phase_flag28,
NA, NA, NA, NA),
PINGROUP(15, NORTH, blsp_spi4, blsp_i2c4, pri_mi2s, NA, NA, NA, NA, NA,
@@ -1460,27 +1529,27 @@ static const struct msm_pingroup msmfalcon_groups[] = {
PINGROUP(20, SOUTH, blsp_spi6, blsp_uart2, blsp_uim6, NA, NA, NA, NA,
NA, NA),
PINGROUP(21, SOUTH, blsp_spi6, blsp_uart2, blsp_uim6, NA, phase_flag11,
- qdss_cti, vsense_data0, NA, NA),
+ qdss_cti0_b, vsense_data0, NA, NA),
PINGROUP(22, CENTER, blsp_spi6, blsp_uart2, blsp_i2c6, NA,
phase_flag12, vsense_data1, NA, NA, NA),
PINGROUP(23, CENTER, blsp_spi6, blsp_uart2, blsp_i2c6, NA,
phase_flag13, vsense_mode, NA, NA, NA),
- PINGROUP(24, NORTH, blsp_spi7, BLSP_UART, sec_mi2s, sndwire_clk, NA,
+ PINGROUP(24, NORTH, blsp_spi7, blsp_uart6_a, sec_mi2s, sndwire_clk, NA,
NA, phase_flag17, vsense_clkout, NA),
- PINGROUP(25, NORTH, blsp_spi7, BLSP_UART, sec_mi2s, sndwire_data, NA,
+ PINGROUP(25, NORTH, blsp_spi7, blsp_uart6_a, sec_mi2s, sndwire_data, NA,
NA, phase_flag18, NA, NA),
- PINGROUP(26, NORTH, blsp_spi7, BLSP_UART, blsp_i2c7, sec_mi2s, NA,
+ PINGROUP(26, NORTH, blsp_spi7, blsp_uart6_a, blsp_i2c7, sec_mi2s, NA,
phase_flag19, NA, NA, NA),
- PINGROUP(27, NORTH, blsp_spi7, BLSP_UART, blsp_i2c7, vfr_1, sec_mi2s,
+ PINGROUP(27, NORTH, blsp_spi7, blsp_uart6_a, blsp_i2c7, vfr_1, sec_mi2s,
NA, phase_flag20, NA, NA),
- PINGROUP(28, CENTER, blsp_spi, BLSP_UART, m_voc, NA, phase_flag21, NA,
- NA, NA, NA),
- PINGROUP(29, CENTER, blsp_spi, BLSP_UART, NA, NA, phase_flag22, NA, NA,
- NA, NA),
- PINGROUP(30, CENTER, blsp_spi, BLSP_UART, BLSP_I2C, blsp_spi3, NA,
- phase_flag23, NA, NA, NA),
- PINGROUP(31, CENTER, blsp_spi, BLSP_UART, BLSP_I2C, pwr_modem, NA,
- phase_flag24, qdss_gpio, NA, NA),
+ PINGROUP(28, CENTER, blsp_spi8_a, blsp_uart6_b, m_voc, NA, phase_flag21,
+ NA, NA, NA, NA),
+ PINGROUP(29, CENTER, blsp_spi8_a, blsp_uart6_b, NA, NA, phase_flag22,
+ NA, NA, NA, NA),
+ PINGROUP(30, CENTER, blsp_spi8_a, blsp_uart6_b, blsp_i2c8_a,
+ blsp_spi3_cs1, NA, phase_flag23, NA, NA, NA),
+ PINGROUP(31, CENTER, blsp_spi8_a, blsp_uart6_b, blsp_i2c8_a, pwr_modem,
+ NA, phase_flag24, qdss_gpio, NA, NA),
PINGROUP(32, SOUTH, cam_mclk, pwr_nav, NA, NA, qdss_gpio0, NA, NA, NA,
NA),
PINGROUP(33, SOUTH, cam_mclk, qspi_data0, pwr_crypto, NA, NA,
@@ -1495,30 +1564,35 @@ static const struct msm_pingroup msmfalcon_groups[] = {
atest_usb23, NA, NA, NA),
PINGROUP(38, SOUTH, cci_i2c, NA, NA, qdss_gpio6, NA, NA, NA, NA, NA),
PINGROUP(39, SOUTH, cci_i2c, NA, NA, qdss_gpio7, NA, NA, NA, NA, NA),
- PINGROUP(40, SOUTH, CCI_TIMER0, NA, blsp_spi, NA, NA, NA, NA, NA, NA),
- PINGROUP(41, SOUTH, CCI_TIMER1, NA, blsp_spi, NA, NA, NA, NA, NA, NA),
+ PINGROUP(40, SOUTH, CCI_TIMER0, NA, blsp_spi8_b, NA, NA, NA, NA, NA,
+ NA),
+ PINGROUP(41, SOUTH, CCI_TIMER1, NA, blsp_spi8_b, NA, NA, NA, NA, NA,
+ NA),
PINGROUP(42, SOUTH, mdss_vsync0, mdss_vsync1, mdss_vsync2, mdss_vsync3,
NA, NA, qdss_gpio9, NA, NA),
PINGROUP(43, SOUTH, CCI_TIMER3, CCI_ASYNC, qspi_cs, NA, NA,
qdss_gpio10, NA, NA, NA),
- PINGROUP(44, SOUTH, CCI_TIMER4, CCI_ASYNC, blsp_spi, BLSP_I2C, NA, NA,
- qdss_gpio11, NA, NA),
+ PINGROUP(44, SOUTH, CCI_TIMER4, CCI_ASYNC, blsp_spi8_b, blsp_i2c8_b, NA,
+ NA, qdss_gpio11, NA, NA),
PINGROUP(45, SOUTH, cci_async, NA, NA, qdss_gpio12, NA, NA, NA, NA, NA),
PINGROUP(46, SOUTH, blsp_spi1, NA, NA, qdss_gpio13, NA, NA, NA, NA, NA),
PINGROUP(47, SOUTH, qspi_clk, NA, phase_flag30, qdss_gpio14, NA, NA,
NA, NA, NA),
PINGROUP(48, SOUTH, NA, phase_flag1, qdss_gpio15, NA, NA, NA, NA, NA,
NA),
- PINGROUP(49, SOUTH, NA, phase_flag2, qdss_cti, NA, NA, NA, NA, NA, NA),
- PINGROUP(50, SOUTH, qspi_cs, NA, phase_flag9, qdss_cti, NA, NA, NA, NA,
+ PINGROUP(49, SOUTH, NA, phase_flag2, qdss_cti0_a, NA, NA, NA, NA, NA,
NA),
+ PINGROUP(50, SOUTH, qspi_cs, NA, phase_flag9, qdss_cti0_a, NA, NA, NA,
+ NA, NA),
PINGROUP(51, SOUTH, qspi_data3, NA, phase_flag15, qdss_gpio8, NA, NA,
NA, NA, NA),
- PINGROUP(52, SOUTH, CCI_TIMER2, blsp_spi, BLSP_I2C, NA, phase_flag16,
- qdss_gpio, NA, NA, NA),
- PINGROUP(53, NORTH, NA, phase_flag6, qdss_cti, NA, NA, NA, NA, NA, NA),
+ PINGROUP(52, SOUTH, CCI_TIMER2, blsp_spi8_b, blsp_i2c8_b, NA,
+ phase_flag16, qdss_gpio, NA, NA, NA),
+ PINGROUP(53, NORTH, NA, phase_flag6, qdss_cti1_a, NA, NA, NA, NA, NA,
+ NA),
PINGROUP(54, NORTH, NA, NA, phase_flag29, NA, NA, NA, NA, NA, NA),
- PINGROUP(55, SOUTH, NA, phase_flag25, qdss_cti, NA, NA, NA, NA, NA, NA),
+ PINGROUP(55, SOUTH, NA, phase_flag25, qdss_cti1_a, NA, NA, NA, NA, NA,
+ NA),
PINGROUP(56, SOUTH, NA, phase_flag10, qdss_gpio3, NA, atest_usb20, NA,
NA, NA, NA),
PINGROUP(57, SOUTH, gcc_gp1, NA, phase_flag4, atest_usb22, NA, NA, NA,
@@ -1533,11 +1607,11 @@ static const struct msm_pingroup msmfalcon_groups[] = {
PINGROUP(62, NORTH, sec_mi2s, audio_ref, MDP_VSYNC, cri_trng, NA, NA,
atest_char0, NA, NA),
PINGROUP(63, NORTH, NA, NA, NA, qdss_gpio1, NA, NA, NA, NA, NA),
- PINGROUP(64, SOUTH, blsp_spi8, sp_cmu, NA, NA, qdss_gpio2, NA, NA, NA,
- NA),
- PINGROUP(65, SOUTH, NA, NAV_PPS, NAV_PPS, GPS_TX, blsp_spi3, adsp_ext,
- NA, NA, NA),
- PINGROUP(66, NORTH, NA, NA, qdss_cti, NA, NA, NA, NA, NA, NA),
+ PINGROUP(64, SOUTH, blsp_spi8_cs1, sp_cmu, NA, NA, qdss_gpio2, NA, NA,
+ NA, NA),
+ PINGROUP(65, SOUTH, NA, nav_pps_a, nav_pps_a, gps_tx_a, blsp_spi3_cs2,
+ adsp_ext, NA, NA, NA),
+ PINGROUP(66, NORTH, NA, NA, qdss_cti1_b, NA, NA, NA, NA, NA, NA),
PINGROUP(67, NORTH, NA, NA, qdss_gpio0, NA, NA, NA, NA, NA, NA),
PINGROUP(68, NORTH, isense_dbg, NA, phase_flag0, qdss_gpio, NA, NA, NA,
NA, NA),
@@ -1550,12 +1624,13 @@ static const struct msm_pingroup msmfalcon_groups[] = {
PINGROUP(73, NORTH, NA, NA, qdss_gpio15, NA, NA, NA, NA, NA, NA),
PINGROUP(74, NORTH, mdp_vsync, NA, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(75, NORTH, NA, NA, qdss_gpio8, NA, NA, NA, NA, NA, NA),
- PINGROUP(76, NORTH, blsp_spi8, NA, NA, NA, qdss_gpio9, NA, NA, NA, NA),
+ PINGROUP(76, NORTH, blsp_spi8_cs2, NA, NA, NA, qdss_gpio9, NA, NA, NA,
+ NA),
PINGROUP(77, NORTH, NA, NA, qdss_gpio10, NA, NA, NA, NA, NA, NA),
PINGROUP(78, NORTH, gcc_gp1, NA, qdss_gpio13, NA, NA, NA, NA, NA, NA),
PINGROUP(79, SOUTH, NA, NA, qdss_gpio11, NA, NA, NA, NA, NA, NA),
- PINGROUP(80, SOUTH, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, qdss_gpio12, NA,
- NA, NA),
+ PINGROUP(80, SOUTH, nav_pps_b, nav_pps_b, gps_tx_c, NA, NA, qdss_gpio12,
+ NA, NA, NA),
PINGROUP(81, CENTER, mss_lte, gcc_gp2, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(82, CENTER, mss_lte, gcc_gp3, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(83, SOUTH, uim2_data, NA, NA, NA, NA, NA, NA, NA, NA),
@@ -1573,8 +1648,8 @@ static const struct msm_pingroup msmfalcon_groups[] = {
PINGROUP(95, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(96, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(97, SOUTH, NA, ldo_en, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(98, SOUTH, NA, NAV_PPS, NAV_PPS, GPS_TX, ldo_update, NA, NA,
- NA, NA),
+ PINGROUP(98, SOUTH, NA, nav_pps_c, nav_pps_c, gps_tx_b, ldo_update, NA,
+ NA, NA, NA),
PINGROUP(99, SOUTH, qlink_request, NA, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(100, SOUTH, qlink_enable, NA, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(101, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
diff --git a/drivers/pinctrl/samsung/pinctrl-exynos5440.c b/drivers/pinctrl/samsung/pinctrl-exynos5440.c
index 82dc109f7ed4..3149a877c51f 100644
--- a/drivers/pinctrl/samsung/pinctrl-exynos5440.c
+++ b/drivers/pinctrl/samsung/pinctrl-exynos5440.c
@@ -107,6 +107,7 @@ struct exynos5440_pmx_func {
* @nr_groups: number of pin groups available.
* @pmx_functions: list of pin functions parsed from device tree.
* @nr_functions: number of pin functions available.
+ * @range: gpio range to register with pinctrl
*/
struct exynos5440_pinctrl_priv_data {
void __iomem *reg_base;
@@ -117,6 +118,7 @@ struct exynos5440_pinctrl_priv_data {
unsigned int nr_groups;
const struct exynos5440_pmx_func *pmx_functions;
unsigned int nr_functions;
+ struct pinctrl_gpio_range range;
};
/**
@@ -742,7 +744,6 @@ static int exynos5440_pinctrl_register(struct platform_device *pdev,
struct pinctrl_desc *ctrldesc;
struct pinctrl_dev *pctl_dev;
struct pinctrl_pin_desc *pindesc, *pdesc;
- struct pinctrl_gpio_range grange;
char *pin_names;
int pin, ret;
@@ -794,12 +795,12 @@ static int exynos5440_pinctrl_register(struct platform_device *pdev,
return PTR_ERR(pctl_dev);
}
- grange.name = "exynos5440-pctrl-gpio-range";
- grange.id = 0;
- grange.base = 0;
- grange.npins = EXYNOS5440_MAX_PINS;
- grange.gc = priv->gc;
- pinctrl_add_gpio_range(pctl_dev, &grange);
+ priv->range.name = "exynos5440-pctrl-gpio-range";
+ priv->range.id = 0;
+ priv->range.base = 0;
+ priv->range.npins = EXYNOS5440_MAX_PINS;
+ priv->range.gc = priv->gc;
+ pinctrl_add_gpio_range(pctl_dev, &priv->range);
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
index d18308344431..293371b88ab9 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
@@ -127,6 +127,7 @@ enum ipa3_usb_state {
IPA_USB_SUSPEND_REQUESTED,
IPA_USB_SUSPEND_IN_PROGRESS,
IPA_USB_SUSPENDED,
+ IPA_USB_SUSPENDED_NO_RWAKEUP,
IPA_USB_RESUME_IN_PROGRESS
};
@@ -152,6 +153,12 @@ struct finish_suspend_work_context {
u32 ul_clnt_hdl;
};
+struct ipa3_usb_teth_prot_conn_params {
+ u32 usb_to_ipa_clnt_hdl;
+ u32 ipa_to_usb_clnt_hdl;
+ struct ipa_usb_teth_prot_params params;
+};
+
/**
* Transport type - could be either data tethering or DPL
* Each transport has it's own RM resources and statuses
@@ -163,6 +170,7 @@ struct ipa3_usb_transport_type_ctx {
enum ipa3_usb_state state;
struct finish_suspend_work_context finish_suspend_work;
struct ipa_usb_xdci_chan_params ch_params;
+ struct ipa3_usb_teth_prot_conn_params teth_conn_params;
};
struct ipa3_usb_smmu_reg_map {
@@ -189,14 +197,15 @@ struct ipa3_usb_context {
};
enum ipa3_usb_op {
- IPA_USB_INIT_TETH_PROT,
- IPA_USB_REQUEST_CHANNEL,
- IPA_USB_CONNECT,
- IPA_USB_DISCONNECT,
- IPA_USB_RELEASE_CHANNEL,
- IPA_USB_DEINIT_TETH_PROT,
- IPA_USB_SUSPEND,
- IPA_USB_RESUME
+ IPA_USB_OP_INIT_TETH_PROT,
+ IPA_USB_OP_REQUEST_CHANNEL,
+ IPA_USB_OP_CONNECT,
+ IPA_USB_OP_DISCONNECT,
+ IPA_USB_OP_RELEASE_CHANNEL,
+ IPA_USB_OP_DEINIT_TETH_PROT,
+ IPA_USB_OP_SUSPEND,
+ IPA_USB_OP_SUSPEND_NO_RWAKEUP,
+ IPA_USB_OP_RESUME
};
struct ipa3_usb_status_dbg_info {
@@ -228,22 +237,24 @@ struct ipa3_usb_context *ipa3_usb_ctx;
static char *ipa3_usb_op_to_string(enum ipa3_usb_op op)
{
switch (op) {
- case IPA_USB_INIT_TETH_PROT:
- return "IPA_USB_INIT_TETH_PROT";
- case IPA_USB_REQUEST_CHANNEL:
- return "IPA_USB_REQUEST_CHANNEL";
- case IPA_USB_CONNECT:
- return "IPA_USB_CONNECT";
- case IPA_USB_DISCONNECT:
- return "IPA_USB_DISCONNECT";
- case IPA_USB_RELEASE_CHANNEL:
- return "IPA_USB_RELEASE_CHANNEL";
- case IPA_USB_DEINIT_TETH_PROT:
- return "IPA_USB_DEINIT_TETH_PROT";
- case IPA_USB_SUSPEND:
- return "IPA_USB_SUSPEND";
- case IPA_USB_RESUME:
- return "IPA_USB_RESUME";
+ case IPA_USB_OP_INIT_TETH_PROT:
+ return "IPA_USB_OP_INIT_TETH_PROT";
+ case IPA_USB_OP_REQUEST_CHANNEL:
+ return "IPA_USB_OP_REQUEST_CHANNEL";
+ case IPA_USB_OP_CONNECT:
+ return "IPA_USB_OP_CONNECT";
+ case IPA_USB_OP_DISCONNECT:
+ return "IPA_USB_OP_DISCONNECT";
+ case IPA_USB_OP_RELEASE_CHANNEL:
+ return "IPA_USB_OP_RELEASE_CHANNEL";
+ case IPA_USB_OP_DEINIT_TETH_PROT:
+ return "IPA_USB_OP_DEINIT_TETH_PROT";
+ case IPA_USB_OP_SUSPEND:
+ return "IPA_USB_OP_SUSPEND";
+ case IPA_USB_OP_SUSPEND_NO_RWAKEUP:
+ return "IPA_USB_OP_SUSPEND_NO_RWAKEUP";
+ case IPA_USB_OP_RESUME:
+ return "IPA_USB_OP_RESUME";
}
return "UNSUPPORTED";
@@ -266,6 +277,8 @@ static char *ipa3_usb_state_to_string(enum ipa3_usb_state state)
return "IPA_USB_SUSPEND_IN_PROGRESS";
case IPA_USB_SUSPENDED:
return "IPA_USB_SUSPENDED";
+ case IPA_USB_SUSPENDED_NO_RWAKEUP:
+ return "IPA_USB_SUSPENDED_NO_RWAKEUP";
case IPA_USB_RESUME_IN_PROGRESS:
return "IPA_USB_RESUME_IN_PROGRESS";
}
@@ -312,6 +325,7 @@ static bool ipa3_usb_set_state(enum ipa3_usb_state new_state, bool err_permit,
if (state == IPA_USB_INITIALIZED ||
state == IPA_USB_STOPPED ||
state == IPA_USB_RESUME_IN_PROGRESS ||
+ state == IPA_USB_SUSPENDED_NO_RWAKEUP ||
/*
* In case of failure during suspend request
* handling, state is reverted to connected.
@@ -327,7 +341,8 @@ static bool ipa3_usb_set_state(enum ipa3_usb_state new_state, bool err_permit,
case IPA_USB_STOPPED:
if (state == IPA_USB_SUSPEND_IN_PROGRESS ||
state == IPA_USB_CONNECTED ||
- state == IPA_USB_SUSPENDED)
+ state == IPA_USB_SUSPENDED ||
+ state == IPA_USB_SUSPENDED_NO_RWAKEUP)
state_legal = true;
break;
case IPA_USB_SUSPEND_REQUESTED:
@@ -354,6 +369,10 @@ static bool ipa3_usb_set_state(enum ipa3_usb_state new_state, bool err_permit,
(err_permit && state == IPA_USB_RESUME_IN_PROGRESS))
state_legal = true;
break;
+ case IPA_USB_SUSPENDED_NO_RWAKEUP:
+ if (state == IPA_USB_CONNECTED)
+ state_legal = true;
+ break;
case IPA_USB_RESUME_IN_PROGRESS:
if (state == IPA_USB_SUSPEND_IN_PROGRESS ||
state == IPA_USB_SUSPENDED)
@@ -418,32 +437,33 @@ static bool ipa3_usb_check_legal_op(enum ipa3_usb_op op,
spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags);
state = ipa3_usb_ctx->ttype_ctx[ttype].state;
switch (op) {
- case IPA_USB_INIT_TETH_PROT:
+ case IPA_USB_OP_INIT_TETH_PROT:
if (state == IPA_USB_INVALID ||
(!is_dpl && state == IPA_USB_INITIALIZED))
is_legal = true;
break;
- case IPA_USB_REQUEST_CHANNEL:
+ case IPA_USB_OP_REQUEST_CHANNEL:
if (state == IPA_USB_INITIALIZED)
is_legal = true;
break;
- case IPA_USB_CONNECT:
+ case IPA_USB_OP_CONNECT:
if (state == IPA_USB_INITIALIZED || state == IPA_USB_STOPPED)
is_legal = true;
break;
- case IPA_USB_DISCONNECT:
+ case IPA_USB_OP_DISCONNECT:
if (state == IPA_USB_CONNECTED ||
state == IPA_USB_SUSPEND_IN_PROGRESS ||
- state == IPA_USB_SUSPENDED)
+ state == IPA_USB_SUSPENDED ||
+ state == IPA_USB_SUSPENDED_NO_RWAKEUP)
is_legal = true;
break;
- case IPA_USB_RELEASE_CHANNEL:
+ case IPA_USB_OP_RELEASE_CHANNEL:
/* when releasing 1st channel state will be changed already */
if (state == IPA_USB_STOPPED ||
(!is_dpl && state == IPA_USB_INITIALIZED))
is_legal = true;
break;
- case IPA_USB_DEINIT_TETH_PROT:
+ case IPA_USB_OP_DEINIT_TETH_PROT:
/*
* For data tethering we should allow deinit an inited protocol
* always. E.g. rmnet is inited and rndis is connected.
@@ -453,13 +473,18 @@ static bool ipa3_usb_check_legal_op(enum ipa3_usb_op op,
if (!is_dpl || state == IPA_USB_INITIALIZED)
is_legal = true;
break;
- case IPA_USB_SUSPEND:
+ case IPA_USB_OP_SUSPEND:
if (state == IPA_USB_CONNECTED)
is_legal = true;
break;
- case IPA_USB_RESUME:
+ case IPA_USB_OP_SUSPEND_NO_RWAKEUP:
+ if (state == IPA_USB_CONNECTED)
+ is_legal = true;
+ break;
+ case IPA_USB_OP_RESUME:
if (state == IPA_USB_SUSPENDED ||
- state == IPA_USB_SUSPEND_IN_PROGRESS)
+ state == IPA_USB_SUSPEND_IN_PROGRESS ||
+ state == IPA_USB_SUSPENDED_NO_RWAKEUP)
is_legal = true;
break;
default:
@@ -638,6 +663,7 @@ static int ipa3_usb_cons_request_resource_cb_do(
ipa3_usb_ctx->ttype_ctx[ttype].state));
switch (ipa3_usb_ctx->ttype_ctx[ttype].state) {
case IPA_USB_CONNECTED:
+ case IPA_USB_SUSPENDED_NO_RWAKEUP:
rm_ctx->cons_state = IPA_USB_CONS_GRANTED;
result = 0;
break;
@@ -717,6 +743,7 @@ static int ipa3_usb_cons_release_resource_cb_do(
break;
case IPA_USB_STOPPED:
case IPA_USB_RESUME_IN_PROGRESS:
+ case IPA_USB_SUSPENDED_NO_RWAKEUP:
if (rm_ctx->cons_requested)
rm_ctx->cons_requested = false;
break;
@@ -886,7 +913,7 @@ int ipa_usb_init_teth_prot(enum ipa_usb_teth_prot teth_prot,
ttype = IPA3_USB_GET_TTYPE(teth_prot);
- if (!ipa3_usb_check_legal_op(IPA_USB_INIT_TETH_PROT, ttype)) {
+ if (!ipa3_usb_check_legal_op(IPA_USB_OP_INIT_TETH_PROT, ttype)) {
IPA_USB_ERR("Illegal operation.\n");
result = -EPERM;
goto bad_params;
@@ -1204,7 +1231,7 @@ static int ipa3_usb_request_xdci_channel(
ttype = IPA3_USB_GET_TTYPE(params->teth_prot);
- if (!ipa3_usb_check_legal_op(IPA_USB_REQUEST_CHANNEL, ttype)) {
+ if (!ipa3_usb_check_legal_op(IPA_USB_OP_REQUEST_CHANNEL, ttype)) {
IPA_USB_ERR("Illegal operation\n");
return -EPERM;
}
@@ -1347,7 +1374,7 @@ static int ipa3_usb_release_xdci_channel(u32 clnt_hdl,
return -EINVAL;
}
- if (!ipa3_usb_check_legal_op(IPA_USB_RELEASE_CHANNEL, ttype)) {
+ if (!ipa3_usb_check_legal_op(IPA_USB_OP_RELEASE_CHANNEL, ttype)) {
IPA_USB_ERR("Illegal operation.\n");
return -EPERM;
}
@@ -1511,81 +1538,79 @@ static int ipa3_usb_connect_dpl(void)
return 0;
}
-static int ipa3_usb_connect_teth_prot(
- struct ipa_usb_xdci_connect_params_internal *params,
- enum ipa3_usb_transport_type ttype)
+static int ipa3_usb_connect_teth_prot(enum ipa_usb_teth_prot teth_prot)
{
int result;
struct teth_bridge_connect_params teth_bridge_params;
+ struct ipa3_usb_teth_prot_conn_params *teth_conn_params;
+ enum ipa3_usb_transport_type ttype;
- IPA_USB_DBG("connecting protocol = %d\n",
- params->teth_prot);
- switch (params->teth_prot) {
+ IPA_USB_DBG("connecting protocol = %s\n",
+ ipa3_usb_teth_prot_to_string(teth_prot));
+
+ ttype = IPA3_USB_GET_TTYPE(teth_prot);
+
+ teth_conn_params = &(ipa3_usb_ctx->ttype_ctx[ttype].teth_conn_params);
+
+ switch (teth_prot) {
case IPA_USB_RNDIS:
if (ipa3_usb_ctx->teth_prot_ctx[IPA_USB_RNDIS].state ==
IPA_USB_TETH_PROT_CONNECTED) {
IPA_USB_DBG("%s is already connected.\n",
- ipa3_usb_teth_prot_to_string(
- params->teth_prot));
+ ipa3_usb_teth_prot_to_string(teth_prot));
break;
}
ipa3_usb_ctx->ttype_ctx[ttype].user_data =
ipa3_usb_ctx->teth_prot_ctx[IPA_USB_RNDIS].user_data;
result = rndis_ipa_pipe_connect_notify(
- params->usb_to_ipa_clnt_hdl,
- params->ipa_to_usb_clnt_hdl,
- params->teth_prot_params.max_xfer_size_bytes_to_dev,
- params->teth_prot_params.max_packet_number_to_dev,
- params->teth_prot_params.max_xfer_size_bytes_to_host,
+ teth_conn_params->usb_to_ipa_clnt_hdl,
+ teth_conn_params->ipa_to_usb_clnt_hdl,
+ teth_conn_params->params.max_xfer_size_bytes_to_dev,
+ teth_conn_params->params.max_packet_number_to_dev,
+ teth_conn_params->params.max_xfer_size_bytes_to_host,
ipa3_usb_ctx->teth_prot_ctx[IPA_USB_RNDIS].
teth_prot_params.rndis.private);
if (result) {
IPA_USB_ERR("failed to connect %s.\n",
- ipa3_usb_teth_prot_to_string(
- params->teth_prot));
+ ipa3_usb_teth_prot_to_string(teth_prot));
ipa3_usb_ctx->ttype_ctx[ttype].user_data = NULL;
return result;
}
ipa3_usb_ctx->teth_prot_ctx[IPA_USB_RNDIS].state =
IPA_USB_TETH_PROT_CONNECTED;
IPA_USB_DBG("%s is connected.\n",
- ipa3_usb_teth_prot_to_string(
- params->teth_prot));
+ ipa3_usb_teth_prot_to_string(teth_prot));
break;
case IPA_USB_ECM:
if (ipa3_usb_ctx->teth_prot_ctx[IPA_USB_ECM].state ==
IPA_USB_TETH_PROT_CONNECTED) {
IPA_USB_DBG("%s is already connected.\n",
- ipa3_usb_teth_prot_to_string(
- params->teth_prot));
+ ipa3_usb_teth_prot_to_string(teth_prot));
break;
}
ipa3_usb_ctx->ttype_ctx[ttype].user_data =
ipa3_usb_ctx->teth_prot_ctx[IPA_USB_ECM].user_data;
- result = ecm_ipa_connect(params->usb_to_ipa_clnt_hdl,
- params->ipa_to_usb_clnt_hdl,
+ result = ecm_ipa_connect(teth_conn_params->usb_to_ipa_clnt_hdl,
+ teth_conn_params->ipa_to_usb_clnt_hdl,
ipa3_usb_ctx->teth_prot_ctx[IPA_USB_ECM].
teth_prot_params.ecm.private);
if (result) {
IPA_USB_ERR("failed to connect %s.\n",
- ipa3_usb_teth_prot_to_string(
- params->teth_prot));
+ ipa3_usb_teth_prot_to_string(teth_prot));
ipa3_usb_ctx->ttype_ctx[ttype].user_data = NULL;
return result;
}
ipa3_usb_ctx->teth_prot_ctx[IPA_USB_ECM].state =
IPA_USB_TETH_PROT_CONNECTED;
IPA_USB_DBG("%s is connected.\n",
- ipa3_usb_teth_prot_to_string(
- params->teth_prot));
+ ipa3_usb_teth_prot_to_string(teth_prot));
break;
case IPA_USB_RMNET:
case IPA_USB_MBIM:
- if (ipa3_usb_ctx->teth_prot_ctx[params->teth_prot].state ==
+ if (ipa3_usb_ctx->teth_prot_ctx[teth_prot].state ==
IPA_USB_TETH_PROT_CONNECTED) {
IPA_USB_DBG("%s is already connected.\n",
- ipa3_usb_teth_prot_to_string(
- params->teth_prot));
+ ipa3_usb_teth_prot_to_string(teth_prot));
break;
}
result = ipa3_usb_init_teth_bridge();
@@ -1593,14 +1618,14 @@ static int ipa3_usb_connect_teth_prot(
return result;
ipa3_usb_ctx->ttype_ctx[ttype].user_data =
- ipa3_usb_ctx->teth_prot_ctx[params->teth_prot].
+ ipa3_usb_ctx->teth_prot_ctx[teth_prot].
user_data;
teth_bridge_params.ipa_usb_pipe_hdl =
- params->ipa_to_usb_clnt_hdl;
+ teth_conn_params->ipa_to_usb_clnt_hdl;
teth_bridge_params.usb_ipa_pipe_hdl =
- params->usb_to_ipa_clnt_hdl;
+ teth_conn_params->usb_to_ipa_clnt_hdl;
teth_bridge_params.tethering_mode =
- (params->teth_prot == IPA_USB_RMNET) ?
+ (teth_prot == IPA_USB_RMNET) ?
(TETH_TETHERING_MODE_RMNET):(TETH_TETHERING_MODE_MBIM);
teth_bridge_params.client_type = IPA_CLIENT_USB_PROD;
result = ipa3_usb_connect_teth_bridge(&teth_bridge_params);
@@ -1608,27 +1633,23 @@ static int ipa3_usb_connect_teth_prot(
ipa3_usb_ctx->ttype_ctx[ttype].user_data = NULL;
return result;
}
- ipa3_usb_ctx->teth_prot_ctx[params->teth_prot].state =
+ ipa3_usb_ctx->teth_prot_ctx[teth_prot].state =
IPA_USB_TETH_PROT_CONNECTED;
ipa3_usb_notify_do(ttype, IPA_USB_DEVICE_READY);
IPA_USB_DBG("%s (%s) is connected.\n",
- ipa3_usb_teth_prot_to_string(
- params->teth_prot),
- ipa3_usb_teth_bridge_prot_to_string(
- params->teth_prot));
+ ipa3_usb_teth_prot_to_string(teth_prot),
+ ipa3_usb_teth_bridge_prot_to_string(teth_prot));
break;
case IPA_USB_DIAG:
if (ipa3_usb_ctx->teth_prot_ctx[IPA_USB_DIAG].state ==
IPA_USB_TETH_PROT_CONNECTED) {
IPA_USB_DBG("%s is already connected.\n",
- ipa3_usb_teth_prot_to_string(
- params->teth_prot));
+ ipa3_usb_teth_prot_to_string(teth_prot));
break;
}
ipa3_usb_ctx->ttype_ctx[ttype].user_data =
- ipa3_usb_ctx->teth_prot_ctx[params->teth_prot].
- user_data;
+ ipa3_usb_ctx->teth_prot_ctx[teth_prot].user_data;
result = ipa3_usb_connect_dpl();
if (result) {
IPA_USB_ERR("Failed connecting DPL result=%d\n",
@@ -1640,8 +1661,7 @@ static int ipa3_usb_connect_teth_prot(
IPA_USB_TETH_PROT_CONNECTED;
ipa3_usb_notify_do(ttype, IPA_USB_DEVICE_READY);
IPA_USB_DBG("%s is connected.\n",
- ipa3_usb_teth_prot_to_string(
- params->teth_prot));
+ ipa3_usb_teth_prot_to_string(teth_prot));
break;
default:
IPA_USB_ERR("Invalid tethering protocol\n");
@@ -1775,11 +1795,19 @@ static int ipa3_usb_xdci_connect_internal(
ttype = (params->teth_prot == IPA_USB_DIAG) ? IPA_USB_TRANSPORT_DPL :
IPA_USB_TRANSPORT_TETH;
- if (!ipa3_usb_check_legal_op(IPA_USB_CONNECT, ttype)) {
+ if (!ipa3_usb_check_legal_op(IPA_USB_OP_CONNECT, ttype)) {
IPA_USB_ERR("Illegal operation.\n");
return -EPERM;
}
+ ipa3_usb_ctx->ttype_ctx[ttype].teth_conn_params.ipa_to_usb_clnt_hdl
+ = params->ipa_to_usb_clnt_hdl;
+ if (!IPA3_USB_IS_TTYPE_DPL(ttype))
+ ipa3_usb_ctx->ttype_ctx[ttype].teth_conn_params.
+ usb_to_ipa_clnt_hdl = params->usb_to_ipa_clnt_hdl;
+ ipa3_usb_ctx->ttype_ctx[ttype].teth_conn_params.params
+ = params->teth_prot_params;
+
/* Set EE xDCI specific scratch */
result = ipa3_set_usb_max_packet_size(params->max_pkt_size);
if (result) {
@@ -1816,7 +1844,7 @@ static int ipa3_usb_xdci_connect_internal(
if (params->teth_prot != IPA_USB_DIAG) {
/* Start UL channel */
- result = ipa3_xdci_connect(params->usb_to_ipa_clnt_hdl,
+ result = ipa3_xdci_start(params->usb_to_ipa_clnt_hdl,
params->usb_to_ipa_xferrscidx,
params->usb_to_ipa_xferrscidx_valid);
if (result) {
@@ -1826,7 +1854,7 @@ static int ipa3_usb_xdci_connect_internal(
}
/* Start DL/DPL channel */
- result = ipa3_xdci_connect(params->ipa_to_usb_clnt_hdl,
+ result = ipa3_xdci_start(params->ipa_to_usb_clnt_hdl,
params->ipa_to_usb_xferrscidx,
params->ipa_to_usb_xferrscidx_valid);
if (result) {
@@ -1835,7 +1863,7 @@ static int ipa3_usb_xdci_connect_internal(
}
/* Connect tethering protocol */
- result = ipa3_usb_connect_teth_prot(params, ttype);
+ result = ipa3_usb_connect_teth_prot(params->teth_prot);
if (result) {
IPA_USB_ERR("failed to connect teth protocol\n");
goto connect_teth_prot_fail;
@@ -2164,6 +2192,70 @@ static int ipa3_usb_check_disconnect_prot(enum ipa_usb_teth_prot teth_prot)
return 0;
}
+/* Assumes lock already acquired */
+static int ipa_usb_xdci_dismiss_channels(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
+ enum ipa_usb_teth_prot teth_prot)
+{
+ int result = 0;
+ enum ipa3_usb_transport_type ttype;
+
+ ttype = IPA3_USB_GET_TTYPE(teth_prot);
+
+ IPA_USB_DBG_LOW("entry\n");
+
+ /* Reset DL channel */
+ result = ipa3_reset_gsi_channel(dl_clnt_hdl);
+ if (result) {
+ IPA_USB_ERR("failed to reset DL channel.\n");
+ return result;
+ }
+
+ /* Reset DL event ring */
+ result = ipa3_reset_gsi_event_ring(dl_clnt_hdl);
+ if (result) {
+ IPA_USB_ERR("failed to reset DL event ring.\n");
+ return result;
+ }
+
+ if (!IPA3_USB_IS_TTYPE_DPL(ttype)) {
+ /* Reset UL channel */
+ result = ipa3_reset_gsi_channel(ul_clnt_hdl);
+ if (result) {
+ IPA_USB_ERR("failed to reset UL channel.\n");
+ return result;
+ }
+
+ /* Reset UL event ring */
+ result = ipa3_reset_gsi_event_ring(ul_clnt_hdl);
+ if (result) {
+ IPA_USB_ERR("failed to reset UL event ring.\n");
+ return result;
+ }
+ }
+
+ /* Change state to STOPPED */
+ if (!ipa3_usb_set_state(IPA_USB_STOPPED, false, ttype))
+ IPA_USB_ERR("failed to change state to stopped\n");
+
+ if (!IPA3_USB_IS_TTYPE_DPL(ttype)) {
+ result = ipa3_usb_release_xdci_channel(ul_clnt_hdl, ttype);
+ if (result) {
+ IPA_USB_ERR("failed to release UL channel.\n");
+ return result;
+ }
+ }
+
+ result = ipa3_usb_release_xdci_channel(dl_clnt_hdl, ttype);
+ if (result) {
+ IPA_USB_ERR("failed to release DL channel.\n");
+ return result;
+ }
+
+ IPA_USB_DBG_LOW("exit\n");
+
+ return 0;
+}
+
int ipa_usb_xdci_disconnect(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
enum ipa_usb_teth_prot teth_prot)
{
@@ -2175,20 +2267,31 @@ int ipa_usb_xdci_disconnect(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
mutex_lock(&ipa3_usb_ctx->general_mutex);
IPA_USB_DBG_LOW("entry\n");
- if (ipa3_usb_check_disconnect_prot(teth_prot)) {
- result = -EINVAL;
- goto bad_params;
- }
ttype = IPA3_USB_GET_TTYPE(teth_prot);
- if (!ipa3_usb_check_legal_op(IPA_USB_DISCONNECT, ttype)) {
+ if (!ipa3_usb_check_legal_op(IPA_USB_OP_DISCONNECT, ttype)) {
IPA_USB_ERR("Illegal operation.\n");
result = -EPERM;
goto bad_params;
}
spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags);
+ if (ipa3_usb_ctx->ttype_ctx[ttype].state ==
+ IPA_USB_SUSPENDED_NO_RWAKEUP) {
+ spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags);
+ result = ipa_usb_xdci_dismiss_channels(ul_clnt_hdl, dl_clnt_hdl,
+ teth_prot);
+ mutex_unlock(&ipa3_usb_ctx->general_mutex);
+ return result;
+ }
+
+ if (ipa3_usb_check_disconnect_prot(teth_prot)) {
+ spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags);
+ result = -EINVAL;
+ goto bad_params;
+ }
+
if (ipa3_usb_ctx->ttype_ctx[ttype].state != IPA_USB_SUSPENDED) {
spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags);
/* Stop DL/DPL channel */
@@ -2227,53 +2330,10 @@ int ipa_usb_xdci_disconnect(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
} else
spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags);
- /* Reset DL channel */
- result = ipa3_reset_gsi_channel(dl_clnt_hdl);
- if (result) {
- IPA_USB_ERR("failed to reset DL channel.\n");
- goto bad_params;
- }
-
- /* Reset DL event ring */
- result = ipa3_reset_gsi_event_ring(dl_clnt_hdl);
- if (result) {
- IPA_USB_ERR("failed to reset DL event ring.\n");
- goto bad_params;
- }
-
- if (!IPA3_USB_IS_TTYPE_DPL(ttype)) {
- /* Reset UL channel */
- result = ipa3_reset_gsi_channel(ul_clnt_hdl);
- if (result) {
- IPA_USB_ERR("failed to reset UL channel.\n");
- goto bad_params;
- }
-
- /* Reset UL event ring */
- result = ipa3_reset_gsi_event_ring(ul_clnt_hdl);
- if (result) {
- IPA_USB_ERR("failed to reset UL event ring.\n");
- goto bad_params;
- }
- }
-
- /* Change state to STOPPED */
- if (!ipa3_usb_set_state(IPA_USB_STOPPED, false, ttype))
- IPA_USB_ERR("failed to change state to stopped\n");
-
- if (!IPA3_USB_IS_TTYPE_DPL(ttype)) {
- result = ipa3_usb_release_xdci_channel(ul_clnt_hdl, ttype);
- if (result) {
- IPA_USB_ERR("failed to release UL channel.\n");
- goto bad_params;
- }
- }
-
- result = ipa3_usb_release_xdci_channel(dl_clnt_hdl, ttype);
- if (result) {
- IPA_USB_ERR("failed to release DL channel.\n");
+ result = ipa_usb_xdci_dismiss_channels(ul_clnt_hdl, dl_clnt_hdl,
+ teth_prot);
+ if (result)
goto bad_params;
- }
/* Disconnect tethering protocol */
result = ipa3_usb_disconnect_teth_prot(teth_prot);
@@ -2315,7 +2375,7 @@ int ipa_usb_deinit_teth_prot(enum ipa_usb_teth_prot teth_prot)
ttype = IPA3_USB_GET_TTYPE(teth_prot);
- if (!ipa3_usb_check_legal_op(IPA_USB_DEINIT_TETH_PROT, ttype)) {
+ if (!ipa3_usb_check_legal_op(IPA_USB_OP_DEINIT_TETH_PROT, ttype)) {
IPA_USB_ERR("Illegal operation.\n");
result = -EPERM;
goto bad_params;
@@ -2411,25 +2471,104 @@ bad_params:
}
EXPORT_SYMBOL(ipa_usb_deinit_teth_prot);
-int ipa_usb_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
+/* Assumes lock already acquired */
+static int ipa3_usb_suspend_no_remote_wakeup(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
enum ipa_usb_teth_prot teth_prot)
{
int result = 0;
+ enum ipa3_usb_transport_type ttype;
+
+ ttype = IPA3_USB_GET_TTYPE(teth_prot);
+
+ if (!ipa3_usb_check_legal_op(IPA_USB_OP_SUSPEND_NO_RWAKEUP, ttype)) {
+ IPA_USB_ERR("Illegal operation.\n");
+ result = -EPERM;
+ goto fail_exit;
+ }
+
+ IPA_USB_DBG("Start suspend with no remote wakeup sequence: %s\n",
+ IPA3_USB_IS_TTYPE_DPL(ttype) ?
+ "DPL channel":"Data Tethering channels");
+
+ if (ipa3_usb_check_disconnect_prot(teth_prot)) {
+ result = -EINVAL;
+ goto fail_exit;
+ }
+
+ /* Stop DL/DPL channel */
+ result = ipa3_xdci_disconnect(dl_clnt_hdl, false, -1);
+ if (result) {
+ IPA_USB_ERR("failed to disconnect DL/DPL channel.\n");
+ goto fail_exit;
+ }
+
+ if (!IPA3_USB_IS_TTYPE_DPL(ttype)) {
+ /* Stop UL channel */
+ result = ipa3_xdci_disconnect(ul_clnt_hdl, true,
+ ipa3_usb_ctx->qmi_req_id);
+ if (result) {
+ IPA_USB_ERR("failed disconnect UL channel\n");
+ goto start_dl;
+ }
+ ipa3_usb_ctx->qmi_req_id++;
+ }
+
+ /* Disconnect tethering protocol */
+ result = ipa3_usb_disconnect_teth_prot(teth_prot);
+ if (result)
+ goto start_ul;
+
+ result = ipa3_usb_release_prod(ttype);
+ if (result) {
+ IPA_USB_ERR("failed to release PROD.\n");
+ goto connect_teth;
+ }
+
+ /* Change ipa_usb state to SUSPENDED_NO_RWAKEUP */
+ if (!ipa3_usb_set_state(IPA_USB_SUSPENDED_NO_RWAKEUP, false, ttype))
+ IPA_USB_ERR("failed to change state to suspend no rwakeup\n");
+
+ IPA_USB_DBG_LOW("exit\n");
+ return 0;
+
+connect_teth:
+ (void)ipa3_usb_connect_teth_prot(teth_prot);
+start_ul:
+ if (!IPA3_USB_IS_TTYPE_DPL(ttype))
+ (void)ipa3_xdci_connect(ul_clnt_hdl);
+start_dl:
+ (void)ipa3_xdci_connect(dl_clnt_hdl);
+fail_exit:
+ return result;
+}
+
+int ipa_usb_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
+ enum ipa_usb_teth_prot teth_prot, bool with_remote_wakeup)
+{
+ int result = 0;
unsigned long flags;
enum ipa3_usb_cons_state curr_cons_state;
enum ipa3_usb_transport_type ttype;
mutex_lock(&ipa3_usb_ctx->general_mutex);
IPA_USB_DBG_LOW("entry\n");
+
if (teth_prot > IPA_USB_MAX_TETH_PROT_SIZE) {
IPA_USB_ERR("bad parameters.\n");
result = -EINVAL;
goto bad_params;
}
+ if (!with_remote_wakeup) {
+ result = ipa3_usb_suspend_no_remote_wakeup(ul_clnt_hdl,
+ dl_clnt_hdl, teth_prot);
+ mutex_unlock(&ipa3_usb_ctx->general_mutex);
+ return result;
+ }
+
ttype = IPA3_USB_GET_TTYPE(teth_prot);
- if (!ipa3_usb_check_legal_op(IPA_USB_SUSPEND, ttype)) {
+ if (!ipa3_usb_check_legal_op(IPA_USB_OP_SUSPEND, ttype)) {
IPA_USB_ERR("Illegal operation.\n");
result = -EPERM;
goto bad_params;
@@ -2538,6 +2677,72 @@ bad_params:
}
EXPORT_SYMBOL(ipa_usb_xdci_suspend);
+/* Assumes lock already acquired */
+static int ipa3_usb_resume_no_remote_wakeup(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
+ enum ipa_usb_teth_prot teth_prot)
+{
+ int result = -EFAULT;
+ enum ipa3_usb_transport_type ttype;
+
+ ttype = IPA3_USB_GET_TTYPE(teth_prot);
+
+ IPA_USB_DBG("Start resume with no remote wakeup sequence: %s\n",
+ IPA3_USB_IS_TTYPE_DPL(ttype) ?
+ "DPL channel":"Data Tethering channels");
+
+ /* Request USB_PROD */
+ result = ipa3_usb_request_prod(ttype);
+ if (result)
+ goto fail_exit;
+
+ /* Connect tethering protocol */
+ result = ipa3_usb_connect_teth_prot(teth_prot);
+ if (result) {
+ IPA_USB_ERR("failed to connect teth protocol\n");
+ goto release_prod;
+ }
+
+ if (!IPA3_USB_IS_TTYPE_DPL(ttype)) {
+ /* Start UL channel */
+ result = ipa3_xdci_connect(ul_clnt_hdl);
+ if (result) {
+ IPA_USB_ERR("failed to start UL channel.\n");
+ goto disconn_teth;
+ }
+ }
+
+ /* Start DL/DPL channel */
+ result = ipa3_xdci_connect(dl_clnt_hdl);
+ if (result) {
+ IPA_USB_ERR("failed to start DL/DPL channel.\n");
+ goto stop_ul;
+ }
+
+ /* Change state to CONNECTED */
+ if (!ipa3_usb_set_state(IPA_USB_CONNECTED, false, ttype)) {
+ IPA_USB_ERR("failed to change state to connected\n");
+ result = -EFAULT;
+ goto stop_dl;
+ }
+
+ return 0;
+
+stop_dl:
+ (void)ipa3_xdci_disconnect(dl_clnt_hdl, false, -1);
+stop_ul:
+ if (!IPA3_USB_IS_TTYPE_DPL(ttype)) {
+ (void)ipa3_xdci_disconnect(ul_clnt_hdl, true,
+ ipa3_usb_ctx->qmi_req_id);
+ ipa3_usb_ctx->qmi_req_id++;
+ }
+disconn_teth:
+ (void)ipa3_usb_disconnect_teth_prot(teth_prot);
+release_prod:
+ (void)ipa3_usb_release_prod(ttype);
+fail_exit:
+ return result;
+}
+
int ipa_usb_xdci_resume(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
enum ipa_usb_teth_prot teth_prot)
{
@@ -2557,19 +2762,25 @@ int ipa_usb_xdci_resume(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
ttype = IPA3_USB_GET_TTYPE(teth_prot);
- if (!ipa3_usb_check_legal_op(IPA_USB_RESUME, ttype)) {
+ if (!ipa3_usb_check_legal_op(IPA_USB_OP_RESUME, ttype)) {
IPA_USB_ERR("Illegal operation.\n");
result = -EPERM;
goto bad_params;
}
- IPA_USB_DBG_LOW("Start resume sequence: %s\n",
- IPA3_USB_IS_TTYPE_DPL(ttype) ?
- "DPL channel" : "Data Tethering channels");
-
spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags);
prev_state = ipa3_usb_ctx->ttype_ctx[ttype].state;
spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags);
+ if (prev_state == IPA_USB_SUSPENDED_NO_RWAKEUP) {
+ result = ipa3_usb_resume_no_remote_wakeup(ul_clnt_hdl,
+ dl_clnt_hdl, teth_prot);
+ mutex_unlock(&ipa3_usb_ctx->general_mutex);
+ return result;
+ }
+
+ IPA_USB_DBG("Start resume sequence: %s\n",
+ IPA3_USB_IS_TTYPE_DPL(ttype) ?
+ "DPL channel" : "Data Tethering channels");
/* Change state to RESUME_IN_PROGRESS */
if (!ipa3_usb_set_state(IPA_USB_RESUME_IN_PROGRESS, false, ttype)) {
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index 804c89dc9533..73add50cf224 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -4907,20 +4907,16 @@ int ipa_iommu_map(struct iommu_domain *domain,
IPADBG("domain =0x%p iova 0x%lx\n", domain, iova);
IPADBG("paddr =0x%pa size 0x%x\n", &paddr, (u32)size);
- /* make sure no overlapping */
+ /* Checking the address overlapping */
if (domain == ipa2_get_smmu_domain()) {
if (iova >= ap_cb->va_start && iova < ap_cb->va_end) {
IPAERR("iommu AP overlap addr 0x%lx\n", iova);
- ipa_assert();
- return -EFAULT;
}
} else if (domain == ipa2_get_wlan_smmu_domain()) {
/* wlan is one time map */
} else if (domain == ipa2_get_uc_smmu_domain()) {
if (iova >= uc_cb->va_start && iova < uc_cb->va_end) {
IPAERR("iommu uC overlap addr 0x%lx\n", iova);
- ipa_assert();
- return -EFAULT;
}
} else {
IPAERR("Unexpected domain 0x%p\n", domain);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
index 3c2a6d4620ba..d51e9ac97fe0 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
@@ -18,6 +18,7 @@
#include "ipa_i.h"
#include "ipa_trace.h"
+#define IPA_WAN_AGGR_PKT_CNT 5
#define IPA_LAST_DESC_CNT 0xFFFF
#define POLLING_INACTIVITY_RX 40
#define POLLING_INACTIVITY_TX 40
@@ -1099,16 +1100,18 @@ int ipa2_rx_poll(u32 clnt_hdl, int weight)
break;
ipa_wq_rx_common(ep->sys, iov.size);
- cnt += 5;
+ cnt += IPA_WAN_AGGR_PKT_CNT;
};
- if (cnt == 0) {
+ if (cnt == 0 || cnt < weight) {
ep->inactive_cycles++;
ep->client_notify(ep->priv, IPA_CLIENT_COMP_NAPI, 0);
if (ep->inactive_cycles > 3 || ep->sys->len == 0) {
ep->switch_to_intr = true;
delay = 0;
+ } else if (cnt < weight) {
+ delay = 0;
}
queue_delayed_work(ep->sys->wq,
&ep->sys->switch_to_intr_work, msecs_to_jiffies(delay));
@@ -1165,8 +1168,11 @@ void ipa_update_repl_threshold(enum ipa_client_type ipa_client)
* Determine how many buffers/descriptors remaining will
* cause to drop below the yellow WM bar.
*/
- ep->rx_replenish_threshold = ipa_get_sys_yellow_wm(ep->sys)
- / ep->sys->rx_buff_sz;
+ if (ep->sys->rx_buff_sz)
+ ep->rx_replenish_threshold = ipa_get_sys_yellow_wm(ep->sys)
+ / ep->sys->rx_buff_sz;
+ else
+ ep->rx_replenish_threshold = 0;
}
/**
@@ -1361,8 +1367,11 @@ int ipa2_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
* Determine how many buffers/descriptors remaining will
* cause to drop below the yellow WM bar.
*/
- ep->rx_replenish_threshold = ipa_get_sys_yellow_wm(ep->sys)
- / ep->sys->rx_buff_sz;
+ if (ep->sys->rx_buff_sz)
+ ep->rx_replenish_threshold =
+ ipa_get_sys_yellow_wm(ep->sys) / ep->sys->rx_buff_sz;
+ else
+ ep->rx_replenish_threshold = 0;
/* Only when the WAN pipes are setup, actual threshold will
* be read from the register. So update LAN_CONS ep again with
* right value.
@@ -3162,14 +3171,9 @@ static int ipa_assign_policy_v2(struct ipa_sys_connect_params *in,
sys->repl_hdlr =
ipa_replenish_rx_cache;
}
- if (in->napi_enabled) {
- sys->rx_pool_sz =
- IPA_WAN_NAPI_CONS_RX_POOL_SZ;
- if (in->recycle_enabled) {
- sys->repl_hdlr =
- ipa_replenish_rx_cache_recycle;
- }
- }
+ if (in->napi_enabled && in->recycle_enabled)
+ sys->repl_hdlr =
+ ipa_replenish_rx_cache_recycle;
sys->ep->wakelock_client =
IPA_WAKELOCK_REF_CLIENT_WAN_RX;
in->ipa_ep_cfg.aggr.aggr_sw_eof_active
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
index c36ecfef66f1..d6e563b935b6 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -235,7 +235,7 @@ static int ipa_generate_flt_hw_rule(enum ipa_ip_type ip,
* @ip: the ip address family type
* @hdr_sz: header size
*
- * Returns: 0 on success, negative on failure
+ * Returns: size on success, negative on failure
*
* caller needs to hold any needed locks to ensure integrity
*
@@ -373,7 +373,12 @@ static int ipa_generate_flt_hw_tbl_common(enum ipa_ip_type ip, u8 *base,
((long)body &
IPA_FLT_ENTRY_MEMORY_ALLIGNMENT));
} else {
- WARN_ON(tbl->sz == 0);
+ if (tbl->sz == 0) {
+ IPAERR("tbl size is 0\n");
+ WARN_ON(1);
+ goto proc_err;
+ }
+
/* allocate memory for the flt tbl */
flt_tbl_mem.size = tbl->sz;
flt_tbl_mem.base =
@@ -460,7 +465,12 @@ static int ipa_generate_flt_hw_tbl_common(enum ipa_ip_type ip, u8 *base,
((long)body &
IPA_FLT_ENTRY_MEMORY_ALLIGNMENT));
} else {
- WARN_ON(tbl->sz == 0);
+ if (tbl->sz == 0) {
+ IPAERR("tbl size is 0\n");
+ WARN_ON(1);
+ goto proc_err;
+ }
+
/* allocate memory for the flt tbl */
flt_tbl_mem.size = tbl->sz;
flt_tbl_mem.base =
@@ -534,8 +544,15 @@ static int ipa_generate_flt_hw_tbl_v1_1(enum ipa_ip_type ip,
u8 *hdr;
u8 *body;
u8 *base;
+ int res;
+
+ res = ipa_get_flt_hw_tbl_size(ip, &hdr_sz);
+ if (res < 0) {
+ IPAERR("ipa_get_flt_hw_tbl_size failed %d\n", res);
+ return res;
+ }
- mem->size = ipa_get_flt_hw_tbl_size(ip, &hdr_sz);
+ mem->size = res;
mem->size = IPA_HW_TABLE_ALIGNMENT(mem->size);
if (mem->size == 0) {
@@ -720,6 +737,7 @@ static int ipa_generate_flt_hw_tbl_v2(enum ipa_ip_type ip,
u32 *entr;
u32 body_start_offset;
u32 hdr_top;
+ int res;
if (ip == IPA_IP_v4)
body_start_offset = IPA_MEM_PART(apps_v4_flt_ofst) -
@@ -756,7 +774,13 @@ static int ipa_generate_flt_hw_tbl_v2(enum ipa_ip_type ip,
entr++;
}
- mem->size = ipa_get_flt_hw_tbl_size(ip, &hdr_sz);
+ res = ipa_get_flt_hw_tbl_size(ip, &hdr_sz);
+ if (res < 0) {
+ IPAERR("ipa_get_flt_hw_tbl_size failed %d\n", res);
+ goto body_err;
+ }
+
+ mem->size = res;
mem->size -= hdr_sz;
mem->size = IPA_HW_TABLE_ALIGNMENT(mem->size);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
index fec4d5484d28..866170d3324d 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
@@ -51,8 +51,6 @@
#define IPA_UC_FINISH_MAX 6
#define IPA_UC_WAIT_MIN_SLEEP 1000
#define IPA_UC_WAII_MAX_SLEEP 1200
-#define IPA_WAN_NAPI_CONS_RX_POOL_SZ (IPA_GENERIC_RX_POOL_SZ*3)
-#define IPA_WAN_CONS_DESC_FIFO_SZ (IPA_SYS_DESC_FIFO_SZ*3)
#define IPA_MAX_STATUS_STAT_NUM 30
@@ -139,7 +137,7 @@
#define IPA_HW_TABLE_ALIGNMENT(start_ofst) \
(((start_ofst) + 127) & ~127)
-#define IPA_RT_FLT_HW_RULE_BUF_SIZE (128)
+#define IPA_RT_FLT_HW_RULE_BUF_SIZE (256)
#define IPA_HDR_PROC_CTX_TABLE_ALIGNMENT_BYTE 8
#define IPA_HDR_PROC_CTX_TABLE_ALIGNMENT(start_ofst) \
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c b/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c
index 6202992c2c4e..314b09593026 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -25,6 +25,16 @@
#define IPA_NAT_SHARED_MEMORY 1
#define IPA_NAT_TEMP_MEM_SIZE 128
+enum nat_table_type {
+ IPA_NAT_BASE_TBL = 0,
+ IPA_NAT_EXPN_TBL = 1,
+ IPA_NAT_INDX_TBL = 2,
+ IPA_NAT_INDEX_EXPN_TBL = 3,
+};
+
+#define NAT_TABLE_ENTRY_SIZE_BYTE 32
+#define NAT_INTEX_TABLE_ENTRY_SIZE_BYTE 4
+
static int ipa_nat_vma_fault_remap(
struct vm_area_struct *vma, struct vm_fault *vmf)
{
@@ -568,6 +578,71 @@ int ipa2_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
goto bail;
}
+ for (cnt = 0; cnt < dma->entries; cnt++) {
+ if (dma->dma[cnt].table_index >= 1) {
+ IPAERR("Invalid table index %d\n",
+ dma->dma[cnt].table_index);
+ ret = -EPERM;
+ goto bail;
+ }
+
+ switch (dma->dma[cnt].base_addr) {
+ case IPA_NAT_BASE_TBL:
+ if (dma->dma[cnt].offset >=
+ (ipa_ctx->nat_mem.size_base_tables + 1) *
+ NAT_TABLE_ENTRY_SIZE_BYTE) {
+ IPAERR("Invalid offset %d\n",
+ dma->dma[cnt].offset);
+ ret = -EPERM;
+ goto bail;
+ }
+
+ break;
+
+ case IPA_NAT_EXPN_TBL:
+ if (dma->dma[cnt].offset >=
+ ipa_ctx->nat_mem.size_expansion_tables *
+ NAT_TABLE_ENTRY_SIZE_BYTE) {
+ IPAERR("Invalid offset %d\n",
+ dma->dma[cnt].offset);
+ ret = -EPERM;
+ goto bail;
+ }
+
+ break;
+
+ case IPA_NAT_INDX_TBL:
+ if (dma->dma[cnt].offset >=
+ (ipa_ctx->nat_mem.size_base_tables + 1) *
+ NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) {
+ IPAERR("Invalid offset %d\n",
+ dma->dma[cnt].offset);
+ ret = -EPERM;
+ goto bail;
+ }
+
+ break;
+
+ case IPA_NAT_INDEX_EXPN_TBL:
+ if (dma->dma[cnt].offset >=
+ ipa_ctx->nat_mem.size_expansion_tables *
+ NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) {
+ IPAERR("Invalid offset %d\n",
+ dma->dma[cnt].offset);
+ ret = -EPERM;
+ goto bail;
+ }
+
+ break;
+
+ default:
+ IPAERR("Invalid base_addr %d\n",
+ dma->dma[cnt].base_addr);
+ ret = -EPERM;
+ goto bail;
+ }
+ }
+
size = sizeof(struct ipa_desc) * NUM_OF_DESC;
desc = kzalloc(size, GFP_KERNEL);
if (desc == NULL) {
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
index 15476f38cf44..5e7a5383334c 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
@@ -227,7 +227,7 @@ int __ipa_generate_rt_hw_rule_v2_6L(enum ipa_ip_type ip,
* @hdr_sz: header size
* @max_rt_idx: maximal index
*
- * Returns: 0 on success, negative on failure
+ * Returns: size on success, negative on failure
*
* caller needs to hold any needed locks to ensure integrity
*
@@ -356,7 +356,11 @@ static int ipa_generate_rt_hw_tbl_common(enum ipa_ip_type ip, u8 *base, u8 *hdr,
((long)body &
IPA_RT_ENTRY_MEMORY_ALLIGNMENT));
} else {
- WARN_ON(tbl->sz == 0);
+ if (tbl->sz == 0) {
+ IPAERR("cannot generate 0 size table\n");
+ goto proc_err;
+ }
+
/* allocate memory for the RT tbl */
rt_tbl_mem.size = tbl->sz;
rt_tbl_mem.base =
@@ -429,8 +433,15 @@ static int ipa_generate_rt_hw_tbl_v1_1(enum ipa_ip_type ip,
u8 *base;
int max_rt_idx;
int i;
+ int res;
- mem->size = ipa_get_rt_hw_tbl_size(ip, &hdr_sz, &max_rt_idx);
+ res = ipa_get_rt_hw_tbl_size(ip, &hdr_sz, &max_rt_idx);
+ if (res < 0) {
+ IPAERR("ipa_get_rt_hw_tbl_size failed %d\n", res);
+ goto error;
+ }
+
+ mem->size = res;
mem->size = (mem->size + IPA_RT_TABLE_MEMORY_ALLIGNMENT) &
~IPA_RT_TABLE_MEMORY_ALLIGNMENT;
@@ -603,6 +614,7 @@ static int ipa_generate_rt_hw_tbl_v2(enum ipa_ip_type ip,
int num_index;
u32 body_start_offset;
u32 apps_start_idx;
+ int res;
if (ip == IPA_IP_v4) {
num_index = IPA_MEM_PART(v4_apps_rt_index_hi) -
@@ -632,7 +644,13 @@ static int ipa_generate_rt_hw_tbl_v2(enum ipa_ip_type ip,
entr++;
}
- mem->size = ipa_get_rt_hw_tbl_size(ip, &hdr_sz, &max_rt_idx);
+ res = ipa_get_rt_hw_tbl_size(ip, &hdr_sz, &max_rt_idx);
+ if (res < 0) {
+ IPAERR("ipa_get_rt_hw_tbl_size failed %d\n", res);
+ goto base_err;
+ }
+
+ mem->size = res;
mem->size -= hdr_sz;
mem->size = (mem->size + IPA_RT_TABLE_MEMORY_ALLIGNMENT) &
~IPA_RT_TABLE_MEMORY_ALLIGNMENT;
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index 96003d7a16a0..c2e43a62ab69 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -62,6 +62,7 @@
#define IPA_UEVENT_NUM_EVNP 4 /* number of event pointers */
#define NAPI_WEIGHT 60
+#define IPA_WWAN_CONS_DESC_FIFO_SZ 1024
static struct net_device *ipa_netdevs[IPA_WWAN_DEVICE_COUNT];
static struct ipa_sys_connect_params apps_to_ipa_ep_cfg, ipa_to_apps_ep_cfg;
@@ -100,6 +101,7 @@ struct ipa_rmnet_plat_drv_res {
bool ipa_loaduC;
bool ipa_advertise_sg_support;
bool ipa_napi_enable;
+ u32 wan_rx_desc_size;
};
static struct ipa_rmnet_plat_drv_res ipa_rmnet_res;
@@ -1053,6 +1055,8 @@ static int ipa_wwan_xmit(struct sk_buff *skb, struct net_device *dev)
IPAWANDBG
("SW filtering out none QMAP packet received from %s",
current->comm);
+ dev_kfree_skb_any(skb);
+ dev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
@@ -1094,6 +1098,8 @@ send:
if (ret) {
pr_err("[%s] fatal: ipa rm timer request resource failed %d\n",
dev->name, ret);
+ dev_kfree_skb_any(skb);
+ dev->stats.tx_dropped++;
return -EFAULT;
}
/* IPA_RM checking end */
@@ -1109,7 +1115,6 @@ send:
if (ret) {
ret = NETDEV_TX_BUSY;
- dev->stats.tx_dropped++;
goto out;
}
@@ -1288,10 +1293,8 @@ static int handle_ingress_format(struct net_device *dev,
ipa_to_apps_ep_cfg.priv = dev;
ipa_to_apps_ep_cfg.napi_enabled = ipa_rmnet_res.ipa_napi_enable;
- if (ipa_to_apps_ep_cfg.napi_enabled)
- ipa_to_apps_ep_cfg.desc_fifo_sz = IPA_WAN_CONS_DESC_FIFO_SZ;
- else
- ipa_to_apps_ep_cfg.desc_fifo_sz = IPA_SYS_DESC_FIFO_SZ;
+ ipa_to_apps_ep_cfg.desc_fifo_sz =
+ ipa_rmnet_res.wan_rx_desc_size * sizeof(struct sps_iovec);
mutex_lock(&ipa_to_apps_pipe_handle_guard);
if (atomic_read(&is_ssr)) {
@@ -1922,6 +1925,9 @@ static struct notifier_block ssr_notifier = {
static int get_ipa_rmnet_dts_configuration(struct platform_device *pdev,
struct ipa_rmnet_plat_drv_res *ipa_rmnet_drv_res)
{
+ int result;
+
+ ipa_rmnet_drv_res->wan_rx_desc_size = IPA_WWAN_CONS_DESC_FIFO_SZ;
ipa_rmnet_drv_res->ipa_rmnet_ssr =
of_property_read_bool(pdev->dev.of_node,
"qcom,rmnet-ipa-ssr");
@@ -1944,6 +1950,18 @@ static int get_ipa_rmnet_dts_configuration(struct platform_device *pdev,
"qcom,ipa-napi-enable");
pr_info("IPA Napi Enable = %s\n",
ipa_rmnet_drv_res->ipa_napi_enable ? "True" : "False");
+
+ /* Get IPA WAN RX desc fifo size */
+ result = of_property_read_u32(pdev->dev.of_node,
+ "qcom,wan-rx-desc-size",
+ &ipa_rmnet_drv_res->wan_rx_desc_size);
+ if (result)
+ pr_info("using default for wan-rx-desc-size = %u\n",
+ ipa_rmnet_drv_res->wan_rx_desc_size);
+ else
+ IPAWANDBG(": found ipa_drv_res->wan-rx-desc-size = %u\n",
+ ipa_rmnet_drv_res->wan_rx_desc_size);
+
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index ab62dbcddd22..8676b35914e2 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -3790,6 +3790,12 @@ static int ipa3_gsi_pre_fw_load_init(void)
return 0;
}
+static void ipa3_uc_is_loaded(void)
+{
+ IPADBG("\n");
+ complete_all(&ipa3_ctx->uc_loaded_completion_obj);
+}
+
static enum gsi_ver ipa3_get_gsi_ver(enum ipa_hw_type ipa_hw_type)
{
enum gsi_ver gsi_ver;
@@ -3842,6 +3848,7 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p,
int result;
struct sps_bam_props bam_props = { 0 };
struct gsi_per_props gsi_props;
+ struct ipa3_uc_hdlrs uc_hdlrs = { 0 };
if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
memset(&gsi_props, 0, sizeof(gsi_props));
@@ -3918,6 +3925,9 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p,
else
IPADBG(":ipa Uc interface init ok\n");
+ uc_hdlrs.ipa_uc_loaded_hdlr = ipa3_uc_is_loaded;
+ ipa3_uc_register_handlers(IPA_HW_FEATURE_COMMON, &uc_hdlrs);
+
result = ipa3_wdi_init();
if (result)
IPAERR(":wdi init failed (%d)\n", -result);
@@ -4609,6 +4619,7 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
INIT_LIST_HEAD(&ipa3_ctx->ipa_ready_cb_list);
init_completion(&ipa3_ctx->init_completion_obj);
+ init_completion(&ipa3_ctx->uc_loaded_completion_obj);
/*
* For GSI, we can't register the GSI driver yet, as it expects
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index 8326c3fdd9d1..26bd180624f1 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -61,7 +61,7 @@ int ipa3_enable_data_path(u32 clnt_hdl)
!ipa3_should_pipe_be_suspended(ep->client))) {
memset(&ep_cfg_ctrl, 0 , sizeof(ep_cfg_ctrl));
ep_cfg_ctrl.ipa_ep_suspend = false;
- ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
+ res = ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
}
/* Assign the resource group for pipe */
@@ -99,9 +99,21 @@ int ipa3_disable_data_path(u32 clnt_hdl)
/* Suspend the pipe */
if (IPA_CLIENT_IS_CONS(ep->client)) {
+ /*
+ * for RG10 workaround uC needs to be loaded before pipe can
+ * be suspended in this case.
+ */
+ if (ipa3_ctx->apply_rg10_wa && ipa3_uc_state_check()) {
+ IPADBG("uC is not loaded yet, waiting...\n");
+ res = wait_for_completion_timeout(
+ &ipa3_ctx->uc_loaded_completion_obj, 60 * HZ);
+ if (res == 0)
+ IPADBG("timeout waiting for uC to load\n");
+ }
+
memset(&ep_cfg_ctrl, 0 , sizeof(struct ipa_ep_cfg_ctrl));
ep_cfg_ctrl.ipa_ep_suspend = true;
- ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
+ res = ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
}
udelay(IPA_PKT_FLUSH_TO_US);
@@ -1311,7 +1323,46 @@ int ipa3_set_usb_max_packet_size(
return 0;
}
-int ipa3_xdci_connect(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid)
+int ipa3_xdci_connect(u32 clnt_hdl)
+{
+ int result;
+ struct ipa3_ep_context *ep;
+
+ IPADBG("entry\n");
+
+ if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
+ ipa3_ctx->ep[clnt_hdl].valid == 0) {
+ IPAERR("Bad parameter.\n");
+ return -EINVAL;
+ }
+
+ ep = &ipa3_ctx->ep[clnt_hdl];
+ IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl));
+
+ result = ipa3_start_gsi_channel(clnt_hdl);
+ if (result) {
+ IPAERR("failed to start gsi channel clnt_hdl=%u\n", clnt_hdl);
+ goto exit;
+ }
+
+ result = ipa3_enable_data_path(clnt_hdl);
+ if (result) {
+ IPAERR("enable data path failed res=%d clnt_hdl=%d.\n", result,
+ clnt_hdl);
+ goto stop_ch;
+ }
+
+ IPADBG("exit\n");
+ goto exit;
+
+stop_ch:
+ (void)ipa3_stop_gsi_channel(clnt_hdl);
+exit:
+ IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl));
+ return result;
+}
+
+int ipa3_xdci_start(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid)
{
struct ipa3_ep_context *ep;
int result = -EFAULT;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 09c7c1b0fd05..4b0cd46082a3 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -21,6 +21,7 @@
#include "ipahal/ipahal.h"
#include "ipahal/ipahal_fltrt.h"
+#define IPA_WAN_AGGR_PKT_CNT 5
#define IPA_LAST_DESC_CNT 0xFFFF
#define POLLING_INACTIVITY_RX 40
#define POLLING_MIN_SLEEP_RX 1010
@@ -60,7 +61,6 @@
#define IPA_ODU_RX_POOL_SZ 64
#define IPA_SIZE_DL_CSUM_META_TRAILER 8
-#define IPA_GSI_EVT_RING_LEN 4096
#define IPA_GSI_MAX_CH_LOW_WEIGHT 15
#define IPA_GSI_EVT_RING_INT_MODT 3200 /* 0.1s under 32KHz clock */
@@ -767,6 +767,30 @@ static void ipa3_transport_irq_cmd_ack(void *user1, int user2)
}
/**
+ * ipa3_transport_irq_cmd_ack_free - callback function which will be
+ * called by SPS/GSI driver after an immediate command is complete.
+ * This function will also free the completion object once it is done.
+ * @tag_comp: pointer to the completion object
+ * @ignored: parameter not used
+ *
+ * Complete the immediate commands completion object, this will release the
+ * thread which waits on this completion object (ipa3_send_cmd())
+ */
+static void ipa3_transport_irq_cmd_ack_free(void *tag_comp, int ignored)
+{
+ struct ipa3_tag_completion *comp = tag_comp;
+
+ if (!comp) {
+ IPAERR("comp is NULL\n");
+ return;
+ }
+
+ complete(&comp->comp);
+ if (atomic_dec_return(&comp->cnt) == 0)
+ kfree(comp);
+}
+
+/**
* ipa3_send_cmd - send immediate commands
* @num_desc: number of descriptors within the desc struct
* @descr: descriptor structure
@@ -778,7 +802,58 @@ static void ipa3_transport_irq_cmd_ack(void *user1, int user2)
*/
int ipa3_send_cmd(u16 num_desc, struct ipa3_desc *descr)
{
- return ipa3_send_cmd_timeout(num_desc, descr, 0);
+ struct ipa3_desc *desc;
+ int i, result = 0;
+ struct ipa3_sys_context *sys;
+ int ep_idx;
+
+ for (i = 0; i < num_desc; i++)
+ IPADBG("sending imm cmd %d\n", descr[i].opcode);
+
+ ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_APPS_CMD_PROD);
+ if (-1 == ep_idx) {
+ IPAERR("Client %u is not mapped\n",
+ IPA_CLIENT_APPS_CMD_PROD);
+ return -EFAULT;
+ }
+
+ sys = ipa3_ctx->ep[ep_idx].sys;
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+ if (num_desc == 1) {
+ init_completion(&descr->xfer_done);
+
+ if (descr->callback || descr->user1)
+ WARN_ON(1);
+
+ descr->callback = ipa3_transport_irq_cmd_ack;
+ descr->user1 = descr;
+ if (ipa3_send_one(sys, descr, true)) {
+ IPAERR("fail to send immediate command\n");
+ result = -EFAULT;
+ goto bail;
+ }
+ wait_for_completion(&descr->xfer_done);
+ } else {
+ desc = &descr[num_desc - 1];
+ init_completion(&desc->xfer_done);
+
+ if (desc->callback || desc->user1)
+ WARN_ON(1);
+
+ desc->callback = ipa3_transport_irq_cmd_ack;
+ desc->user1 = desc;
+ if (ipa3_send(sys, num_desc, descr, true)) {
+ IPAERR("fail to send multiple immediate command set\n");
+ result = -EFAULT;
+ goto bail;
+ }
+ wait_for_completion(&desc->xfer_done);
+ }
+
+bail:
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ return result;
}
/**
@@ -800,6 +875,7 @@ int ipa3_send_cmd_timeout(u16 num_desc, struct ipa3_desc *descr, u32 timeout)
struct ipa3_sys_context *sys;
int ep_idx;
int completed;
+ struct ipa3_tag_completion *comp;
for (i = 0; i < num_desc; i++)
IPADBG("sending imm cmd %d\n", descr[i].opcode);
@@ -810,55 +886,56 @@ int ipa3_send_cmd_timeout(u16 num_desc, struct ipa3_desc *descr, u32 timeout)
IPA_CLIENT_APPS_CMD_PROD);
return -EFAULT;
}
+
+ comp = kzalloc(sizeof(*comp), GFP_ATOMIC);
+ if (!comp) {
+ IPAERR("no mem\n");
+ return -ENOMEM;
+ }
+ init_completion(&comp->comp);
+
+ /* completion needs to be released from both here and in ack callback */
+ atomic_set(&comp->cnt, 2);
+
sys = ipa3_ctx->ep[ep_idx].sys;
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
if (num_desc == 1) {
- init_completion(&descr->xfer_done);
-
if (descr->callback || descr->user1)
WARN_ON(1);
- descr->callback = ipa3_transport_irq_cmd_ack;
- descr->user1 = descr;
+ descr->callback = ipa3_transport_irq_cmd_ack_free;
+ descr->user1 = comp;
if (ipa3_send_one(sys, descr, true)) {
IPAERR("fail to send immediate command\n");
+ kfree(comp);
result = -EFAULT;
goto bail;
}
- if (timeout) {
- completed = wait_for_completion_timeout(
- &descr->xfer_done, msecs_to_jiffies(timeout));
- if (!completed)
- IPADBG("timeout waiting for imm-cmd ACK\n");
- } else {
- wait_for_completion(&descr->xfer_done);
- }
} else {
desc = &descr[num_desc - 1];
- init_completion(&desc->xfer_done);
if (desc->callback || desc->user1)
WARN_ON(1);
- desc->callback = ipa3_transport_irq_cmd_ack;
- desc->user1 = desc;
+ desc->callback = ipa3_transport_irq_cmd_ack_free;
+ desc->user1 = comp;
if (ipa3_send(sys, num_desc, descr, true)) {
IPAERR("fail to send multiple immediate command set\n");
+ kfree(comp);
result = -EFAULT;
goto bail;
}
- if (timeout) {
- completed = wait_for_completion_timeout(
- &desc->xfer_done, msecs_to_jiffies(timeout));
- if (!completed)
- IPADBG("timeout waiting for imm-cmd ACK\n");
- } else {
- wait_for_completion(&desc->xfer_done);
- }
-
}
+ completed = wait_for_completion_timeout(
+ &comp->comp, msecs_to_jiffies(timeout));
+ if (!completed)
+ IPADBG("timeout waiting for imm-cmd ACK\n");
+
+ if (atomic_dec_return(&comp->cnt) == 0)
+ kfree(comp);
+
bail:
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return result;
@@ -3221,9 +3298,6 @@ static int ipa3_assign_policy(struct ipa_sys_connect_params *in,
sys->repl_hdlr =
ipa3_replenish_rx_cache;
}
- if (in->napi_enabled)
- sys->rx_pool_sz =
- IPA_WAN_NAPI_CONS_RX_POOL_SZ;
if (in->napi_enabled && in->recycle_enabled)
sys->repl_hdlr =
ipa3_replenish_rx_cache_recycle;
@@ -3888,13 +3962,19 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in,
gsi_evt_ring_props.re_size =
GSI_EVT_RING_RE_SIZE_16B;
- gsi_evt_ring_props.ring_len = IPA_GSI_EVT_RING_LEN;
+ /*
+ * GSI ring length is calculated based on the desc_fifo_sz
+ * which was meant to define the BAM desc fifo. GSI descriptors
+ * are 16B as opposed to 8B for BAM.
+ */
+ gsi_evt_ring_props.ring_len = 2 * in->desc_fifo_sz;
+
gsi_evt_ring_props.ring_base_vaddr =
- dma_alloc_coherent(ipa3_ctx->pdev, IPA_GSI_EVT_RING_LEN,
- &evt_dma_addr, GFP_KERNEL);
+ dma_alloc_coherent(ipa3_ctx->pdev,
+ gsi_evt_ring_props.ring_len, &evt_dma_addr, GFP_KERNEL);
if (!gsi_evt_ring_props.ring_base_vaddr) {
IPAERR("fail to dma alloc %u bytes\n",
- IPA_GSI_EVT_RING_LEN);
+ gsi_evt_ring_props.ring_len);
return -ENOMEM;
}
gsi_evt_ring_props.ring_base_addr = evt_dma_addr;
@@ -4021,7 +4101,7 @@ fail_get_gsi_ep_info:
}
fail_alloc_evt_ring:
if (gsi_evt_ring_props.ring_base_vaddr)
- dma_free_coherent(ipa3_ctx->pdev, IPA_GSI_EVT_RING_LEN,
+ dma_free_coherent(ipa3_ctx->pdev, gsi_evt_ring_props.ring_len,
gsi_evt_ring_props.ring_base_vaddr, evt_dma_addr);
IPAERR("Return with err: %d\n", result);
return result;
@@ -4203,16 +4283,18 @@ int ipa3_rx_poll(u32 clnt_hdl, int weight)
break;
ipa3_wq_rx_common(ep->sys, mem_info.size);
- cnt += 5;
+ cnt += IPA_WAN_AGGR_PKT_CNT;
};
- if (cnt == 0) {
+ if (cnt == 0 || cnt < weight) {
ep->inactive_cycles++;
ep->client_notify(ep->priv, IPA_CLIENT_COMP_NAPI, 0);
if (ep->inactive_cycles > 3 || ep->sys->len == 0) {
ep->switch_to_intr = true;
delay = 0;
+ } else if (cnt < weight) {
+ delay = 0;
}
queue_delayed_work(ep->sys->wq,
&ep->sys->switch_to_intr_work, msecs_to_jiffies(delay));
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 33be22f98b9d..1b78835cda6b 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -53,8 +53,6 @@
#define IPA_UC_FINISH_MAX 6
#define IPA_UC_WAIT_MIN_SLEEP 1000
#define IPA_UC_WAII_MAX_SLEEP 1200
-#define IPA_WAN_NAPI_CONS_RX_POOL_SZ (IPA_GENERIC_RX_POOL_SZ*3)
-#define IPA_WAN_CONS_DESC_FIFO_SZ (IPA_SYS_DESC_FIFO_SZ*3)
#define IPA_MAX_STATUS_STAT_NUM 30
@@ -1232,6 +1230,7 @@ struct ipa3_context {
bool ipa_initialization_complete;
struct list_head ipa_ready_cb_list;
struct completion init_completion_obj;
+ struct completion uc_loaded_completion_obj;
struct ipa3_smp2p_info smp2p_info;
u32 ipa_tz_unlock_reg_num;
struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg;
@@ -1482,7 +1481,9 @@ int ipa3_reset_gsi_event_ring(u32 clnt_hdl);
int ipa3_set_usb_max_packet_size(
enum ipa_usb_max_usb_packet_size usb_max_packet_size);
-int ipa3_xdci_connect(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid);
+int ipa3_xdci_start(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid);
+
+int ipa3_xdci_connect(u32 clnt_hdl);
int ipa3_xdci_disconnect(u32 clnt_hdl, bool should_force_clear, u32 qmi_req_id);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
index 67e9b397a8b4..e7e5cf114242 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
@@ -24,6 +24,17 @@
#define IPA_NAT_TEMP_MEM_SIZE 128
+enum nat_table_type {
+ IPA_NAT_BASE_TBL = 0,
+ IPA_NAT_EXPN_TBL = 1,
+ IPA_NAT_INDX_TBL = 2,
+ IPA_NAT_INDEX_EXPN_TBL = 3,
+};
+
+#define NAT_TABLE_ENTRY_SIZE_BYTE 32
+#define NAT_INTEX_TABLE_ENTRY_SIZE_BYTE 4
+
+
static int ipa3_nat_vma_fault_remap(
struct vm_area_struct *vma, struct vm_fault *vmf)
{
@@ -571,6 +582,71 @@ int ipa3_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
goto bail;
}
+ for (cnt = 0; cnt < dma->entries; cnt++) {
+ if (dma->dma[cnt].table_index >= 1) {
+ IPAERR("Invalid table index %d\n",
+ dma->dma[cnt].table_index);
+ ret = -EPERM;
+ goto bail;
+ }
+
+ switch (dma->dma[cnt].base_addr) {
+ case IPA_NAT_BASE_TBL:
+ if (dma->dma[cnt].offset >=
+ (ipa3_ctx->nat_mem.size_base_tables + 1) *
+ NAT_TABLE_ENTRY_SIZE_BYTE) {
+ IPAERR("Invalid offset %d\n",
+ dma->dma[cnt].offset);
+ ret = -EPERM;
+ goto bail;
+ }
+
+ break;
+
+ case IPA_NAT_EXPN_TBL:
+ if (dma->dma[cnt].offset >=
+ ipa3_ctx->nat_mem.size_expansion_tables *
+ NAT_TABLE_ENTRY_SIZE_BYTE) {
+ IPAERR("Invalid offset %d\n",
+ dma->dma[cnt].offset);
+ ret = -EPERM;
+ goto bail;
+ }
+
+ break;
+
+ case IPA_NAT_INDX_TBL:
+ if (dma->dma[cnt].offset >=
+ (ipa3_ctx->nat_mem.size_base_tables + 1) *
+ NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) {
+ IPAERR("Invalid offset %d\n",
+ dma->dma[cnt].offset);
+ ret = -EPERM;
+ goto bail;
+ }
+
+ break;
+
+ case IPA_NAT_INDEX_EXPN_TBL:
+ if (dma->dma[cnt].offset >=
+ ipa3_ctx->nat_mem.size_expansion_tables *
+ NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) {
+ IPAERR("Invalid offset %d\n",
+ dma->dma[cnt].offset);
+ ret = -EPERM;
+ goto bail;
+ }
+
+ break;
+
+ default:
+ IPAERR("Invalid base_addr %d\n",
+ dma->dma[cnt].base_addr);
+ ret = -EPERM;
+ goto bail;
+ }
+ }
+
size = sizeof(struct ipa3_desc) * NUM_OF_DESC;
desc = kzalloc(size, GFP_KERNEL);
if (desc == NULL) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c
index cb47773e8a39..21ce28204069 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c
@@ -585,7 +585,9 @@ send_cmd:
if (ipa3_ctx->uc_ctx.uc_status != expected_status) {
if (ipa3_ctx->uc_ctx.uc_status ==
- IPA_HW_PROD_DISABLE_CMD_GSI_STOP_FAILURE) {
+ IPA_HW_PROD_DISABLE_CMD_GSI_STOP_FAILURE ||
+ ipa3_ctx->uc_ctx.uc_status ==
+ IPA_HW_CONS_DISABLE_CMD_GSI_STOP_FAILURE) {
retries++;
if (retries == IPA_GSI_CHANNEL_STOP_MAX_RETRY) {
IPAERR("Failed after %d tries\n", retries);
@@ -594,7 +596,9 @@ send_cmd:
return -EFAULT;
}
IPA3_UC_UNLOCK(flags);
- ipa3_inject_dma_task_for_gsi();
+ if (ipa3_ctx->uc_ctx.uc_status ==
+ IPA_HW_PROD_DISABLE_CMD_GSI_STOP_FAILURE)
+ ipa3_inject_dma_task_for_gsi();
/* sleep for short period to flush IPA */
usleep_range(IPA_GSI_CHANNEL_STOP_SLEEP_MIN_USEC,
IPA_GSI_CHANNEL_STOP_SLEEP_MAX_USEC);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index c0a6e8b00d71..4ea68ae1e95c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -3522,7 +3522,7 @@ int ipa3_stop_gsi_channel(u32 clnt_hdl)
goto end_sequence;
IPADBG("Inject a DMA_TASK with 1B packet to IPA and retry\n");
- /* Send a 1B packet DMA_RASK to IPA and try again*/
+ /* Send a 1B packet DMA_TASK to IPA and try again */
res = ipa3_inject_dma_task_for_gsi();
if (res) {
IPAERR("Failed to inject DMA TASk for GSI\n");
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index f134852e046e..0419249890e9 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -65,6 +65,7 @@
((rmnet_ipa3_ctx && rmnet_ipa3_ctx->wwan_priv) ? \
rmnet_ipa3_ctx->wwan_priv->net : NULL)
+#define IPA_WWAN_CONS_DESC_FIFO_SZ 256
static int ipa3_wwan_add_ul_flt_rule_to_ipa(void);
static int ipa3_wwan_del_ul_flt_rule_to_ipa(void);
@@ -89,6 +90,7 @@ struct ipa3_rmnet_plat_drv_res {
bool ipa_loaduC;
bool ipa_advertise_sg_support;
bool ipa_napi_enable;
+ u32 wan_rx_desc_size;
};
/**
@@ -1066,6 +1068,8 @@ static int ipa3_wwan_xmit(struct sk_buff *skb, struct net_device *dev)
IPAWANDBG_LOW
("SW filtering out none QMAP packet received from %s",
current->comm);
+ dev_kfree_skb_any(skb);
+ dev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
@@ -1077,7 +1081,8 @@ static int ipa3_wwan_xmit(struct sk_buff *skb, struct net_device *dev)
pr_err("[%s]Queue stop, send ctrl pkts\n", dev->name);
goto send;
} else {
- pr_err("[%s]fatal: ipa_wwan_xmit stopped\n", dev->name);
+ pr_err("[%s]fatal: ipa3_wwan_xmit stopped\n",
+ dev->name);
return NETDEV_TX_BUSY;
}
}
@@ -1107,6 +1112,8 @@ send:
if (ret) {
pr_err("[%s] fatal: ipa rm timer request resource failed %d\n",
dev->name, ret);
+ dev_kfree_skb_any(skb);
+ dev->stats.tx_dropped++;
return -EFAULT;
}
/* IPA_RM checking end */
@@ -1122,7 +1129,6 @@ send:
if (ret) {
ret = NETDEV_TX_BUSY;
- dev->stats.tx_dropped++;
goto out;
}
@@ -1271,7 +1277,7 @@ static int handle3_ingress_format(struct net_device *dev,
ipa_wan_ep_cfg->ipa_ep_cfg.aggr.aggr_pkt_limit =
in->u.ingress_format.agg_count;
- if (ipa_wan_ep_cfg->napi_enabled) {
+ if (ipa3_rmnet_res.ipa_napi_enable) {
ipa_wan_ep_cfg->recycle_enabled = true;
ep_cfg = (struct rmnet_phys_ep_conf_s *)
rcu_dereference(dev->rx_handler_data);
@@ -1299,10 +1305,8 @@ static int handle3_ingress_format(struct net_device *dev,
ipa_wan_ep_cfg->priv = dev;
ipa_wan_ep_cfg->napi_enabled = ipa3_rmnet_res.ipa_napi_enable;
- if (ipa_wan_ep_cfg->napi_enabled)
- ipa_wan_ep_cfg->desc_fifo_sz = IPA_WAN_CONS_DESC_FIFO_SZ;
- else
- ipa_wan_ep_cfg->desc_fifo_sz = IPA_SYS_DESC_FIFO_SZ;
+ ipa_wan_ep_cfg->desc_fifo_sz =
+ ipa3_rmnet_res.wan_rx_desc_size * sizeof(struct sps_iovec);
mutex_lock(&rmnet_ipa3_ctx->ipa_to_apps_pipe_handle_guard);
@@ -1953,6 +1957,9 @@ static struct notifier_block ipa3_ssr_notifier = {
static int get_ipa_rmnet_dts_configuration(struct platform_device *pdev,
struct ipa3_rmnet_plat_drv_res *ipa_rmnet_drv_res)
{
+ int result;
+
+ ipa_rmnet_drv_res->wan_rx_desc_size = IPA_WWAN_CONS_DESC_FIFO_SZ;
ipa_rmnet_drv_res->ipa_rmnet_ssr =
of_property_read_bool(pdev->dev.of_node,
"qcom,rmnet-ipa-ssr");
@@ -1975,6 +1982,18 @@ static int get_ipa_rmnet_dts_configuration(struct platform_device *pdev,
"qcom,ipa-napi-enable");
pr_info("IPA Napi Enable = %s\n",
ipa_rmnet_drv_res->ipa_napi_enable ? "True" : "False");
+
+ /* Get IPA WAN RX desc fifo size */
+ result = of_property_read_u32(pdev->dev.of_node,
+ "qcom,wan-rx-desc-size",
+ &ipa_rmnet_drv_res->wan_rx_desc_size);
+ if (result)
+ pr_info("using default for wan-rx-desc-size = %u\n",
+ ipa_rmnet_drv_res->wan_rx_desc_size);
+ else
+ IPAWANDBG(": found ipa_drv_res->wan-rx-desc-size = %u\n",
+ ipa_rmnet_drv_res->wan_rx_desc_size);
+
return 0;
}
diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c
index 45fedfa72bda..5810f7bf7f2f 100644
--- a/drivers/platform/msm/msm_11ad/msm_11ad.c
+++ b/drivers/platform/msm/msm_11ad/msm_11ad.c
@@ -51,6 +51,9 @@
#define VDDIO_MAX_UV 2040000
#define VDDIO_MAX_UA 70300
+#define DISABLE_PCIE_L1_MASK 0xFFFFFFFD
+#define PCIE20_CAP_LINKCTRLSTATUS 0x80
+
struct device;
static const char * const gpio_en_name = "qcom,wigig-en";
@@ -505,6 +508,7 @@ static int ops_resume(void *handle)
int rc;
struct msm11ad_ctx *ctx = handle;
struct pci_dev *pcidev;
+ u32 val;
pr_info("%s(%p)\n", __func__, handle);
if (!ctx) {
@@ -548,6 +552,27 @@ static int ops_resume(void *handle)
goto err_suspend_rc;
}
+ /* Disable L1 */
+ rc = pci_read_config_dword(ctx->pcidev,
+ PCIE20_CAP_LINKCTRLSTATUS, &val);
+ if (rc) {
+ dev_err(ctx->dev,
+ "reading PCIE20_CAP_LINKCTRLSTATUS failed:%d\n",
+ rc);
+ goto err_suspend_rc;
+ }
+ val &= DISABLE_PCIE_L1_MASK; /* disable bit 1 */
+ dev_dbg(ctx->dev, "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x)\n",
+ val);
+ rc = pci_write_config_dword(ctx->pcidev,
+ PCIE20_CAP_LINKCTRLSTATUS, val);
+ if (rc) {
+ dev_err(ctx->dev,
+ "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x) failed:%d\n",
+ val, rc);
+ goto err_suspend_rc;
+ }
+
return 0;
err_suspend_rc:
@@ -792,6 +817,7 @@ static int msm_11ad_probe(struct platform_device *pdev)
struct device_node *rc_node;
struct pci_dev *pcidev = NULL;
int rc;
+ u32 val;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
@@ -907,6 +933,28 @@ static int msm_11ad_probe(struct platform_device *pdev)
goto out_rc;
}
ctx->pcidev = pcidev;
+
+ /* Disable L1 */
+ rc = pci_read_config_dword(ctx->pcidev,
+ PCIE20_CAP_LINKCTRLSTATUS, &val);
+ if (rc) {
+ dev_err(ctx->dev,
+ "reading PCIE20_CAP_LINKCTRLSTATUS failed:%d\n",
+ rc);
+ goto out_rc;
+ }
+ val &= DISABLE_PCIE_L1_MASK; /* disable bit 1 */
+ dev_dbg(ctx->dev, "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x)\n",
+ val);
+ rc = pci_write_config_dword(ctx->pcidev,
+ PCIE20_CAP_LINKCTRLSTATUS, val);
+ if (rc) {
+ dev_err(ctx->dev,
+ "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x) failed:%d\n",
+ val, rc);
+ goto out_rc;
+ }
+
rc = pci_save_state(pcidev);
if (rc) {
dev_err(ctx->dev, "pci_save_state failed :%d\n", rc);
diff --git a/drivers/platform/msm/qpnp-revid.c b/drivers/platform/msm/qpnp-revid.c
index 78e685f789cd..1ef8ebe3ed7d 100644
--- a/drivers/platform/msm/qpnp-revid.c
+++ b/drivers/platform/msm/qpnp-revid.c
@@ -56,6 +56,8 @@ static const char *const pmic_names[] = {
[PMICOBALT_SUBTYPE] = "PMICOBALT",
[PM8005_SUBTYPE] = "PM8005",
[PM8937_SUBTYPE] = "PM8937",
+ [PM2FALCON_SUBTYPE] = "PM2FALCON",
+ [PMFALCON_SUBTYPE] = "PMFALCON",
[PMI8937_SUBTYPE] = "PMI8937",
};
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index 43188c9d690e..5cb017fdf2d3 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -1323,6 +1323,9 @@ static void usb_bam_finish_suspend(enum usb_ctrl cur_bam)
__func__, ret);
goto no_lpm;
}
+ } else {
+ log_event_err("%s: pipe type is not B2B\n", __func__);
+ cons_empty = true;
}
spin_lock(&usb_bam_ipa_handshake_info_lock);
@@ -1959,8 +1962,8 @@ static void usb_bam_finish_resume(struct work_struct *w)
spin_unlock(&usb_bam_ipa_handshake_info_lock);
mutex_unlock(&info[cur_bam].suspend_resume_mutex);
- log_event_dbg("%s: done..PM Runtime PUT %d, count: %d\n",
- __func__, idx, get_pm_runtime_counter(bam_dev));
+ log_event_dbg("%s: done..PM Runtime PUT :%d\n",
+ __func__, get_pm_runtime_counter(bam_dev));
/* Put to match _get at the beginning of this routine */
pm_runtime_put(&ctx->usb_bam_pdev->dev);
}
@@ -2762,16 +2765,14 @@ static void usb_bam_sps_events(enum sps_callback_case sps_cb_case, void *user)
log_event_dbg("%s: received SPS_CALLBACK_BAM_TIMER_IRQ\n",
__func__);
- spin_lock(&ctx->usb_bam_lock);
-
bam = get_bam_type_from_core_name((char *)user);
if (bam < 0 || bam >= MAX_BAMS) {
log_event_err("%s: Invalid bam, type=%d ,name=%s\n",
__func__, bam, (char *)user);
- spin_unlock(&ctx->usb_bam_lock);
return;
}
ctx = &msm_usb_bam[bam];
+ spin_lock(&ctx->usb_bam_lock);
ctx->is_bam_inactivity = true;
log_event_dbg("%s: Inactivity happened on bam=%s,%d\n",
diff --git a/drivers/platform/x86/dell-rbtn.c b/drivers/platform/x86/dell-rbtn.c
index cd410e392550..d33e9ad3218f 100644
--- a/drivers/platform/x86/dell-rbtn.c
+++ b/drivers/platform/x86/dell-rbtn.c
@@ -28,6 +28,7 @@ struct rbtn_data {
enum rbtn_type type;
struct rfkill *rfkill;
struct input_dev *input_dev;
+ bool suspended;
};
@@ -220,9 +221,55 @@ static const struct acpi_device_id rbtn_ids[] = {
{ "", 0 },
};
+#ifdef CONFIG_PM_SLEEP
+static void ACPI_SYSTEM_XFACE rbtn_clear_suspended_flag(void *context)
+{
+ struct rbtn_data *rbtn_data = context;
+
+ rbtn_data->suspended = false;
+}
+
+static int rbtn_suspend(struct device *dev)
+{
+ struct acpi_device *device = to_acpi_device(dev);
+ struct rbtn_data *rbtn_data = acpi_driver_data(device);
+
+ rbtn_data->suspended = true;
+
+ return 0;
+}
+
+static int rbtn_resume(struct device *dev)
+{
+ struct acpi_device *device = to_acpi_device(dev);
+ struct rbtn_data *rbtn_data = acpi_driver_data(device);
+ acpi_status status;
+
+ /*
+ * Upon resume, some BIOSes send an ACPI notification thet triggers
+ * an unwanted input event. In order to ignore it, we use a flag
+ * that we set at suspend and clear once we have received the extra
+ * ACPI notification. Since ACPI notifications are delivered
+ * asynchronously to drivers, we clear the flag from the workqueue
+ * used to deliver the notifications. This should be enough
+ * to have the flag cleared only after we received the extra
+ * notification, if any.
+ */
+ status = acpi_os_execute(OSL_NOTIFY_HANDLER,
+ rbtn_clear_suspended_flag, rbtn_data);
+ if (ACPI_FAILURE(status))
+ rbtn_clear_suspended_flag(rbtn_data);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(rbtn_pm_ops, rbtn_suspend, rbtn_resume);
+
static struct acpi_driver rbtn_driver = {
.name = "dell-rbtn",
.ids = rbtn_ids,
+ .drv.pm = &rbtn_pm_ops,
.ops = {
.add = rbtn_add,
.remove = rbtn_remove,
@@ -384,6 +431,15 @@ static void rbtn_notify(struct acpi_device *device, u32 event)
{
struct rbtn_data *rbtn_data = device->driver_data;
+ /*
+ * Some BIOSes send a notification at resume.
+ * Ignore it to prevent unwanted input events.
+ */
+ if (rbtn_data->suspended) {
+ dev_dbg(&device->dev, "ACPI notification ignored\n");
+ return;
+ }
+
if (event != 0x80) {
dev_info(&device->dev, "Received unknown event (0x%x)\n",
event);
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index b0f62141ea4d..f774cb576ffa 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -131,7 +131,7 @@ MODULE_LICENSE("GPL");
/* Field definitions */
#define HCI_ACCEL_MASK 0x7fff
#define HCI_HOTKEY_DISABLE 0x0b
-#define HCI_HOTKEY_ENABLE 0x01
+#define HCI_HOTKEY_ENABLE 0x09
#define HCI_HOTKEY_SPECIAL_FUNCTIONS 0x10
#define HCI_LCD_BRIGHTNESS_BITS 3
#define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS)
diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h
index a703b208f6e4..d612016b1b79 100644
--- a/drivers/power/qcom-charger/fg-core.h
+++ b/drivers/power/qcom-charger/fg-core.h
@@ -74,6 +74,7 @@ enum fg_debug_flag {
FG_BUS_WRITE = BIT(5), /* Show REGMAP writes */
FG_BUS_READ = BIT(6), /* Show REGMAP reads */
FG_CAP_LEARN = BIT(7), /* Show capacity learning */
+ FG_TTF = BIT(8), /* Show time to full */
};
/* SRAM access */
@@ -128,6 +129,7 @@ enum {
*/
enum fg_sram_param_id {
FG_SRAM_BATT_SOC = 0,
+ FG_SRAM_FULL_SOC,
FG_SRAM_VOLTAGE_PRED,
FG_SRAM_OCV,
FG_SRAM_RSLOW,
@@ -222,7 +224,6 @@ struct fg_batt_props {
int float_volt_uv;
int vbatt_full_mv;
int fastchg_curr_ma;
- int batt_id_kohm;
};
struct fg_cyc_ctr_data {
@@ -251,6 +252,29 @@ struct fg_irq_info {
int irq;
};
+struct fg_circ_buf {
+ int arr[20];
+ int size;
+ int head;
+};
+
+struct fg_pt {
+ s32 x;
+ s32 y;
+};
+
+static const struct fg_pt fg_ln_table[] = {
+ { 1000, 0 },
+ { 2000, 693 },
+ { 4000, 1386 },
+ { 6000, 1792 },
+ { 8000, 2079 },
+ { 16000, 2773 },
+ { 32000, 3466 },
+ { 64000, 4159 },
+ { 128000, 4852 },
+};
+
struct fg_chip {
struct device *dev;
struct pmic_revid_data *pmic_rev_id;
@@ -260,6 +284,7 @@ struct fg_chip {
struct power_supply *batt_psy;
struct power_supply *usb_psy;
struct power_supply *dc_psy;
+ struct power_supply *parallel_psy;
struct iio_channel *batt_id_chan;
struct fg_memif *sram;
struct fg_irq_info *irqs;
@@ -275,12 +300,15 @@ struct fg_chip {
struct fg_cap_learning cl;
struct mutex bus_lock;
struct mutex sram_rw_lock;
+ struct mutex batt_avg_lock;
u32 batt_soc_base;
u32 batt_info_base;
u32 mem_if_base;
- int batt_id;
- int status;
+ int batt_id_kohms;
+ int charge_status;
+ int prev_charge_status;
int charge_done;
+ int charge_type;
int last_soc;
int last_batt_temp;
int health;
@@ -291,11 +319,15 @@ struct fg_chip {
bool charge_full;
bool recharge_soc_adjusted;
bool ki_coeff_dischg_en;
+ bool esr_fcc_ctrl_en;
struct completion soc_update;
struct completion soc_ready;
struct delayed_work profile_load_work;
struct work_struct status_change_work;
struct work_struct cycle_count_work;
+ struct delayed_work batt_avg_work;
+ struct fg_circ_buf ibatt_circ_buf;
+ struct fg_circ_buf vbatt_circ_buf;
};
/* Debugfs data structures are below */
@@ -340,9 +372,15 @@ extern int fg_read(struct fg_chip *chip, int addr, u8 *val, int len);
extern int fg_write(struct fg_chip *chip, int addr, u8 *val, int len);
extern int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val);
extern int fg_ima_init(struct fg_chip *chip);
+extern int fg_clear_ima_errors_if_any(struct fg_chip *chip, bool check_hw_sts);
+extern int fg_clear_dma_errors_if_any(struct fg_chip *chip);
extern int fg_debugfs_create(struct fg_chip *chip);
extern void fill_string(char *str, size_t str_len, u8 *buf, int buf_len);
extern int64_t twos_compliment_extend(int64_t val, int s_bit_pos);
extern s64 fg_float_decode(u16 val);
extern bool is_input_present(struct fg_chip *chip);
+extern void fg_circ_buf_add(struct fg_circ_buf *, int);
+extern void fg_circ_buf_clr(struct fg_circ_buf *);
+extern int fg_circ_buf_avg(struct fg_circ_buf *, int *);
+extern int fg_lerp(const struct fg_pt *, size_t, s32, s32 *);
#endif
diff --git a/drivers/power/qcom-charger/fg-memif.c b/drivers/power/qcom-charger/fg-memif.c
index c271b24adfc4..a98ff7d765e3 100644
--- a/drivers/power/qcom-charger/fg-memif.c
+++ b/drivers/power/qcom-charger/fg-memif.c
@@ -64,44 +64,90 @@ static int fg_config_access_mode(struct fg_chip *chip, bool access, bool burst)
static int fg_run_iacs_clear_sequence(struct fg_chip *chip)
{
- u8 tmp;
- int rc;
+ u8 val, hw_sts, exp_sts;
+ int rc, tries = 250;
/*
* Values to write for running IACS clear sequence comes from
* hardware documentation.
*/
- rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_CLR_BIT,
- IACS_CLR_BIT);
+ rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip),
+ IACS_CLR_BIT | STATIC_CLK_EN_BIT,
+ IACS_CLR_BIT | STATIC_CLK_EN_BIT);
if (rc < 0) {
pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_IMA_CFG(chip),
rc);
return rc;
}
- tmp = 0x4;
- rc = fg_write(chip, MEM_IF_ADDR_MSB(chip), &tmp, 1);
+ rc = fg_config_access_mode(chip, FG_READ, false);
if (rc < 0) {
- pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_ADDR_LSB(chip),
- rc);
+ pr_err("failed to write to 0x%04x, rc=%d\n",
+ MEM_IF_IMA_CTL(chip), rc);
return rc;
}
- tmp = 0x0;
- rc = fg_write(chip, MEM_IF_WR_DATA3(chip), &tmp, 1);
+ rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip),
+ MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT,
+ MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT);
if (rc < 0) {
- pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_WR_DATA3(chip),
- rc);
+ pr_err("failed to set ima_req_access bit rc=%d\n", rc);
return rc;
}
- rc = fg_read(chip, MEM_IF_RD_DATA3(chip), &tmp, 1);
- if (rc < 0) {
- pr_err("failed to read 0x%04x, rc=%d\n", MEM_IF_RD_DATA3(chip),
- rc);
- return rc;
+ /* Delay for the clock to reach FG */
+ usleep_range(35, 40);
+
+ while (1) {
+ val = 0;
+ rc = fg_write(chip, MEM_IF_ADDR_MSB(chip), &val, 1);
+ if (rc < 0) {
+ pr_err("failed to write 0x%04x, rc=%d\n",
+ MEM_IF_ADDR_MSB(chip), rc);
+ return rc;
+ }
+
+ val = 0;
+ rc = fg_write(chip, MEM_IF_WR_DATA3(chip), &val, 1);
+ if (rc < 0) {
+ pr_err("failed to write 0x%04x, rc=%d\n",
+ MEM_IF_WR_DATA3(chip), rc);
+ return rc;
+ }
+
+ rc = fg_read(chip, MEM_IF_RD_DATA3(chip), &val, 1);
+ if (rc < 0) {
+ pr_err("failed to read 0x%04x, rc=%d\n",
+ MEM_IF_RD_DATA3(chip), rc);
+ return rc;
+ }
+
+ /* Delay for IMA hardware to clear */
+ usleep_range(35, 40);
+
+ rc = fg_read(chip, MEM_IF_IMA_HW_STS(chip), &hw_sts, 1);
+ if (rc < 0) {
+ pr_err("failed to read ima_hw_sts rc=%d\n", rc);
+ return rc;
+ }
+
+ if (hw_sts != 0)
+ continue;
+
+ rc = fg_read(chip, MEM_IF_IMA_EXP_STS(chip), &exp_sts, 1);
+ if (rc < 0) {
+ pr_err("failed to read ima_exp_sts rc=%d\n", rc);
+ return rc;
+ }
+
+ if (exp_sts == 0 || !(--tries))
+ break;
}
+ if (!tries)
+ pr_err("Failed to clear the error? hw_sts: %x exp_sts: %d\n",
+ hw_sts, exp_sts);
+
rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_CLR_BIT, 0);
if (rc < 0) {
pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_IMA_CFG(chip),
@@ -109,14 +155,65 @@ static int fg_run_iacs_clear_sequence(struct fg_chip *chip)
return rc;
}
+ udelay(5);
+
+ rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip),
+ MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, 0);
+ if (rc < 0) {
+ pr_err("failed to write to 0x%04x, rc=%d\n",
+ MEM_IF_MEM_INTF_CFG(chip), rc);
+ return rc;
+ }
+
+ /* Delay before next transaction is attempted */
+ usleep_range(35, 40);
fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "IACS clear sequence complete\n");
return rc;
}
-static int fg_check_for_ima_errors(struct fg_chip *chip)
+int fg_clear_dma_errors_if_any(struct fg_chip *chip)
+{
+ int rc;
+ u8 dma_sts;
+
+ rc = fg_read(chip, MEM_IF_DMA_STS(chip), &dma_sts, 1);
+ if (rc < 0) {
+ pr_err("failed to read addr=0x%04x, rc=%d\n",
+ MEM_IF_DMA_STS(chip), rc);
+ return rc;
+ }
+ fg_dbg(chip, FG_STATUS, "dma_sts: %x\n", dma_sts);
+
+ if (dma_sts & (DMA_WRITE_ERROR_BIT | DMA_READ_ERROR_BIT)) {
+ rc = fg_masked_write(chip, MEM_IF_DMA_CTL(chip),
+ DMA_CLEAR_LOG_BIT, DMA_CLEAR_LOG_BIT);
+ if (rc < 0) {
+ pr_err("failed to write addr=0x%04x, rc=%d\n",
+ MEM_IF_DMA_CTL(chip), rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+int fg_clear_ima_errors_if_any(struct fg_chip *chip, bool check_hw_sts)
{
int rc = 0;
u8 err_sts, exp_sts = 0, hw_sts = 0;
+ bool run_err_clr_seq = false;
+
+ rc = fg_read(chip, MEM_IF_IMA_EXP_STS(chip), &exp_sts, 1);
+ if (rc < 0) {
+ pr_err("failed to read ima_exp_sts rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_read(chip, MEM_IF_IMA_HW_STS(chip), &hw_sts, 1);
+ if (rc < 0) {
+ pr_err("failed to read ima_hw_sts rc=%d\n", rc);
+ return rc;
+ }
rc = fg_read(chip, MEM_IF_IMA_ERR_STS(chip), &err_sts, 1);
if (rc < 0) {
@@ -124,22 +221,30 @@ static int fg_check_for_ima_errors(struct fg_chip *chip)
return rc;
}
- if (err_sts & (ADDR_STBL_ERR_BIT | WR_ACS_ERR_BIT | RD_ACS_ERR_BIT)) {
- rc = fg_read(chip, MEM_IF_IMA_EXP_STS(chip), &exp_sts, 1);
- if (rc < 0) {
- pr_err("failed to read ima_exp_sts rc=%d\n", rc);
- return rc;
- }
+ fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n",
+ err_sts, exp_sts, hw_sts);
- rc = fg_read(chip, MEM_IF_IMA_HW_STS(chip), &hw_sts, 1);
- if (rc < 0) {
- pr_err("failed to read ima_hw_sts rc=%d\n", rc);
- return rc;
+ if (check_hw_sts) {
+ /*
+ * Lower nibble should be equal to upper nibble before SRAM
+ * transactions begins from SW side. If they are unequal, then
+ * the error clear sequence should be run irrespective of IMA
+ * exception errors.
+ */
+ if ((hw_sts & 0x0F) != hw_sts >> 4) {
+ pr_err("IMA HW not in correct state, hw_sts=%x\n",
+ hw_sts);
+ run_err_clr_seq = true;
}
+ }
- pr_err("ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n",
- err_sts, exp_sts, hw_sts);
+ if (exp_sts & (IACS_ERR_BIT | XCT_TYPE_ERR_BIT | DATA_RD_ERR_BIT |
+ DATA_WR_ERR_BIT | ADDR_BURST_WRAP_BIT | ADDR_STABLE_ERR_BIT)) {
+ pr_err("IMA exception bit set, exp_sts=%x\n", exp_sts);
+ run_err_clr_seq = true;
+ }
+ if (run_err_clr_seq) {
/* clear the error */
rc = fg_run_iacs_clear_sequence(chip);
if (rc < 0) {
@@ -156,7 +261,7 @@ static int fg_check_for_ima_errors(struct fg_chip *chip)
static int fg_check_iacs_ready(struct fg_chip *chip)
{
- int rc = 0, timeout = 250;
+ int rc = 0, tries = 250;
u8 ima_opr_sts = 0;
/*
@@ -176,17 +281,17 @@ static int fg_check_iacs_ready(struct fg_chip *chip)
if (ima_opr_sts & IACS_RDY_BIT)
break;
- if (!(--timeout))
+ if (!(--tries))
break;
/* delay for iacs_ready to be asserted */
usleep_range(5000, 7000);
}
- if (!timeout) {
+ if (!tries) {
pr_err("IACS_RDY not set\n");
-
- rc = fg_check_for_ima_errors(chip);
+ /* check for error condition */
+ rc = fg_clear_ima_errors_if_any(chip, false);
if (rc < 0) {
pr_err("Failed to check for ima errors rc=%d\n", rc);
return rc;
@@ -250,7 +355,7 @@ static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address,
}
/* check for error condition */
- rc = fg_check_for_ima_errors(chip);
+ rc = fg_clear_ima_errors_if_any(chip, false);
if (rc < 0) {
pr_err("Failed to check for ima errors rc=%d\n", rc);
return rc;
@@ -296,7 +401,7 @@ static int __fg_interleaved_mem_read(struct fg_chip *chip, u16 address,
offset = 0;
/* check for error condition */
- rc = fg_check_for_ima_errors(chip);
+ rc = fg_clear_ima_errors_if_any(chip, false);
if (rc < 0) {
pr_err("Failed to check for ima errors rc=%d\n", rc);
return rc;
@@ -581,5 +686,19 @@ int fg_ima_init(struct fg_chip *chip)
return rc;
}
+ /* Clear DMA errors if any before clearing IMA errors */
+ rc = fg_clear_dma_errors_if_any(chip);
+ if (rc < 0) {
+ pr_err("Error in checking DMA errors rc:%d\n", rc);
+ return rc;
+ }
+
+ /* Clear IMA errors if any before SRAM transactions can begin */
+ rc = fg_clear_ima_errors_if_any(chip, true);
+ if (rc < 0 && rc != -EAGAIN) {
+ pr_err("Error in checking IMA errors rc:%d\n", rc);
+ return rc;
+ }
+
return 0;
}
diff --git a/drivers/power/qcom-charger/fg-reg.h b/drivers/power/qcom-charger/fg-reg.h
index 431e28a7eb1f..ffc46f328f91 100644
--- a/drivers/power/qcom-charger/fg-reg.h
+++ b/drivers/power/qcom-charger/fg-reg.h
@@ -26,6 +26,9 @@
#define BATT_SOC_LOW_PWR_CFG(chip) (chip->batt_soc_base + 0x52)
#define BATT_SOC_LOW_PWR_STS(chip) (chip->batt_soc_base + 0x56)
+/* BATT_SOC_INT_RT_STS */
+#define MSOC_EMPTY_BIT BIT(5)
+
/* BATT_SOC_EN_CTL */
#define FG_ALGORITHM_EN_BIT BIT(7)
@@ -258,6 +261,7 @@
#define ESR_REQ_CTL_EN_BIT BIT(0)
/* FG_MEM_IF register and bit definitions */
+#define MEM_IF_INT_RT_STS(chip) ((chip->mem_if_base) + 0x10)
#define MEM_IF_MEM_INTF_CFG(chip) ((chip->mem_if_base) + 0x50)
#define MEM_IF_IMA_CTL(chip) ((chip->mem_if_base) + 0x51)
#define MEM_IF_IMA_CFG(chip) ((chip->mem_if_base) + 0x52)
@@ -273,6 +277,11 @@
#define MEM_IF_WR_DATA3(chip) ((chip->mem_if_base) + 0x66)
#define MEM_IF_RD_DATA0(chip) ((chip->mem_if_base) + 0x67)
#define MEM_IF_RD_DATA3(chip) ((chip->mem_if_base) + 0x6A)
+#define MEM_IF_DMA_STS(chip) ((chip->mem_if_base) + 0x70)
+#define MEM_IF_DMA_CTL(chip) ((chip->mem_if_base) + 0x71)
+
+/* MEM_IF_INT_RT_STS */
+#define MEM_XCP_BIT BIT(1)
/* MEM_IF_MEM_INTF_CFG */
#define MEM_ACCESS_REQ_BIT BIT(7)
@@ -286,10 +295,19 @@
/* MEM_IF_IMA_CFG */
#define IACS_CLR_BIT BIT(2)
#define IACS_INTR_SRC_SLCT_BIT BIT(3)
+#define STATIC_CLK_EN_BIT BIT(4)
/* MEM_IF_IMA_OPR_STS */
#define IACS_RDY_BIT BIT(1)
+/* MEM_IF_IMA_EXP_STS */
+#define IACS_ERR_BIT BIT(0)
+#define XCT_TYPE_ERR_BIT BIT(1)
+#define DATA_RD_ERR_BIT BIT(3)
+#define DATA_WR_ERR_BIT BIT(4)
+#define ADDR_BURST_WRAP_BIT BIT(5)
+#define ADDR_STABLE_ERR_BIT BIT(7)
+
/* MEM_IF_IMA_ERR_STS */
#define ADDR_STBL_ERR_BIT BIT(7)
#define WR_ACS_ERR_BIT BIT(6)
@@ -297,4 +315,11 @@
/* MEM_IF_FG_BEAT_COUNT */
#define BEAT_COUNT_MASK GENMASK(3, 0)
+
+/* MEM_IF_DMA_STS */
+#define DMA_WRITE_ERROR_BIT BIT(1)
+#define DMA_READ_ERROR_BIT BIT(2)
+
+/* MEM_IF_DMA_CTL */
+#define DMA_CLEAR_LOG_BIT BIT(0)
#endif
diff --git a/drivers/power/qcom-charger/fg-util.c b/drivers/power/qcom-charger/fg-util.c
index 0e3c7dbb5731..405d875ea7df 100644
--- a/drivers/power/qcom-charger/fg-util.c
+++ b/drivers/power/qcom-charger/fg-util.c
@@ -14,6 +14,82 @@
#include "fg-core.h"
+void fg_circ_buf_add(struct fg_circ_buf *buf, int val)
+{
+ buf->arr[buf->head] = val;
+ buf->head = (buf->head + 1) % ARRAY_SIZE(buf->arr);
+ buf->size = min(++buf->size, (int)ARRAY_SIZE(buf->arr));
+}
+
+void fg_circ_buf_clr(struct fg_circ_buf *buf)
+{
+ memset(buf, 0, sizeof(*buf));
+}
+
+int fg_circ_buf_avg(struct fg_circ_buf *buf, int *avg)
+{
+ s64 result = 0;
+ int i;
+
+ if (buf->size == 0)
+ return -ENODATA;
+
+ for (i = 0; i < buf->size; i++)
+ result += buf->arr[i];
+
+ *avg = div_s64(result, buf->size);
+ return 0;
+}
+
+int fg_lerp(const struct fg_pt *pts, size_t tablesize, s32 input, s32 *output)
+{
+ int i;
+ s64 temp;
+
+ if (pts == NULL) {
+ pr_err("Table is NULL\n");
+ return -EINVAL;
+ }
+
+ if (tablesize < 1) {
+ pr_err("Table has no entries\n");
+ return -ENOENT;
+ }
+
+ if (tablesize == 1) {
+ *output = pts[0].y;
+ return 0;
+ }
+
+ if (pts[0].x > pts[1].x) {
+ pr_err("Table is not in acending order\n");
+ return -EINVAL;
+ }
+
+ if (input <= pts[0].x) {
+ *output = pts[0].y;
+ return 0;
+ }
+
+ if (input >= pts[tablesize - 1].x) {
+ *output = pts[tablesize - 1].y;
+ return 0;
+ }
+
+ for (i = 1; i < tablesize; i++) {
+ if (input >= pts[i].x)
+ continue;
+
+ temp = (s64)(pts[i].y - pts[i - 1].y) *
+ (s64)(input - pts[i - 1].x);
+ temp = div_s64(temp, pts[i].x - pts[i - 1].x);
+ *output = temp + pts[i - 1].y;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
static struct fg_dbgfs dbgfs_data = {
.help_msg = {
.data =
diff --git a/drivers/power/qcom-charger/pmic-voter.c b/drivers/power/qcom-charger/pmic-voter.c
index d0bad7dec094..8072b63f53fe 100644
--- a/drivers/power/qcom-charger/pmic-voter.c
+++ b/drivers/power/qcom-charger/pmic-voter.c
@@ -421,6 +421,7 @@ static int show_votable_clients(struct seq_file *m, void *data)
lock_votable(votable);
+ seq_printf(m, "%s:\n", votable->name);
seq_puts(m, "Clients:\n");
for (i = 0; i < votable->num_clients; i++) {
if (votable->client_strs[i]) {
diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c
index 4ee94b990382..100153280d9e 100644
--- a/drivers/power/qcom-charger/qpnp-fg-gen3.c
+++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c
@@ -146,6 +146,8 @@ static void fg_encode_default(struct fg_sram_param *sp,
static struct fg_sram_param pmicobalt_v1_sram_params[] = {
PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, 0, NULL,
fg_decode_default),
+ PARAM(FULL_SOC, FULL_SOC_WORD, FULL_SOC_OFFSET, 2, 1, 1, 0, NULL,
+ fg_decode_default),
PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141,
1000, 0, NULL, fg_decode_voltage_15b),
PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL,
@@ -198,6 +200,8 @@ static struct fg_sram_param pmicobalt_v1_sram_params[] = {
static struct fg_sram_param pmicobalt_v2_sram_params[] = {
PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, 0, NULL,
fg_decode_default),
+ PARAM(FULL_SOC, FULL_SOC_WORD, FULL_SOC_OFFSET, 2, 1, 1, 0, NULL,
+ fg_decode_default),
PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141,
1000, 0, NULL, fg_decode_voltage_15b),
PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL,
@@ -655,12 +659,71 @@ static int fg_get_msoc_raw(struct fg_chip *chip, int *val)
return 0;
}
+static bool is_batt_empty(struct fg_chip *chip)
+{
+ u8 status;
+ int rc, vbatt_uv, msoc;
+
+ rc = fg_read(chip, BATT_SOC_INT_RT_STS(chip), &status, 1);
+ if (rc < 0) {
+ pr_err("failed to read addr=0x%04x, rc=%d\n",
+ BATT_SOC_INT_RT_STS(chip), rc);
+ return false;
+ }
+
+ if (!(status & MSOC_EMPTY_BIT))
+ return false;
+
+ rc = fg_get_battery_voltage(chip, &vbatt_uv);
+ if (rc < 0) {
+ pr_err("failed to get battery voltage, rc=%d\n", rc);
+ return false;
+ }
+
+ rc = fg_get_msoc_raw(chip, &msoc);
+ if (!rc)
+ pr_warn("batt_soc_rt_sts: %x vbatt: %d uV msoc:%d\n", status,
+ vbatt_uv, msoc);
+
+ return ((vbatt_uv < chip->dt.cutoff_volt_mv * 1000) ? true : false);
+}
+
+#define DEBUG_BATT_ID_KOHMS 7
+static bool is_debug_batt_id(struct fg_chip *chip)
+{
+ int batt_id_delta = 0;
+
+ if (!chip->batt_id_kohms)
+ return false;
+
+ batt_id_delta = abs(chip->batt_id_kohms - DEBUG_BATT_ID_KOHMS);
+ if (batt_id_delta <= 1) {
+ fg_dbg(chip, FG_POWER_SUPPLY, "Debug battery id: %dKohms\n",
+ chip->batt_id_kohms);
+ return true;
+ }
+
+ return false;
+}
+
#define FULL_CAPACITY 100
#define FULL_SOC_RAW 255
+#define DEBUG_BATT_SOC 67
+#define EMPTY_SOC 0
static int fg_get_prop_capacity(struct fg_chip *chip, int *val)
{
int rc, msoc;
+ if (is_debug_batt_id(chip)) {
+ *val = DEBUG_BATT_SOC;
+ return 0;
+ }
+
+ if (is_batt_empty(chip)) {
+ *val = EMPTY_SOC;
+ return 0;
+ }
+
if (chip->charge_full) {
*val = FULL_CAPACITY;
return 0;
@@ -725,7 +788,7 @@ static int fg_get_batt_profile(struct fg_chip *chip)
}
batt_id /= 1000;
- chip->batt_id = batt_id;
+ chip->batt_id_kohms = batt_id;
batt_node = of_find_node_by_name(node, "qcom,battery-data");
if (!batt_node) {
pr_err("Batterydata not available\n");
@@ -879,6 +942,17 @@ static bool is_charger_available(struct fg_chip *chip)
return true;
}
+static bool is_parallel_charger_available(struct fg_chip *chip)
+{
+ if (!chip->parallel_psy)
+ chip->parallel_psy = power_supply_get_by_name("parallel");
+
+ if (!chip->parallel_psy)
+ return false;
+
+ return true;
+}
+
static int fg_save_learned_cap_to_sram(struct fg_chip *chip)
{
int16_t cc_mah;
@@ -1126,11 +1200,11 @@ static void fg_cap_learning_update(struct fg_chip *chip)
batt_soc = (u32)batt_soc >> 24;
fg_dbg(chip, FG_CAP_LEARN, "Chg_status: %d cl_active: %d batt_soc: %d\n",
- chip->status, chip->cl.active, batt_soc);
+ chip->charge_status, chip->cl.active, batt_soc);
/* Initialize the starting point of learning capacity */
if (!chip->cl.active) {
- if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
+ if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) {
rc = fg_cap_learning_begin(chip, batt_soc);
chip->cl.active = (rc == 0);
}
@@ -1146,7 +1220,7 @@ static void fg_cap_learning_update(struct fg_chip *chip)
chip->cl.init_cc_uah = 0;
}
- if (chip->status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
+ if (chip->charge_status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n",
batt_soc);
chip->cl.active = false;
@@ -1176,7 +1250,7 @@ static int fg_adjust_ki_coeff_dischg(struct fg_chip *chip)
return rc;
}
- if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ if (chip->charge_status == POWER_SUPPLY_STATUS_DISCHARGING) {
for (i = KI_COEFF_SOC_LEVELS - 1; i >= 0; i--) {
if (msoc < chip->dt.ki_coeff_soc[i]) {
ki_coeff_med = chip->dt.ki_coeff_med_dischg[i];
@@ -1250,7 +1324,7 @@ static int fg_charge_full_update(struct fg_chip *chip)
}
fg_dbg(chip, FG_STATUS, "msoc: %d health: %d status: %d\n", msoc,
- chip->health, chip->status);
+ chip->health, chip->charge_status);
if (chip->charge_done) {
if (msoc >= 99 && chip->health == POWER_SUPPLY_HEALTH_GOOD)
chip->charge_full = true;
@@ -1351,6 +1425,88 @@ static int fg_adjust_recharge_soc(struct fg_chip *chip)
return 0;
}
+static int fg_esr_fcc_config(struct fg_chip *chip)
+{
+ union power_supply_propval prop = {0, };
+ int rc;
+ bool parallel_en = false;
+
+ if (is_parallel_charger_available(chip)) {
+ rc = power_supply_get_property(chip->parallel_psy,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop);
+ if (rc < 0) {
+ pr_err("Error in reading charging_enabled from parallel_psy, rc=%d\n",
+ rc);
+ return rc;
+ }
+ parallel_en = prop.intval;
+ }
+
+ fg_dbg(chip, FG_POWER_SUPPLY, "charge_status: %d parallel_en: %d esr_fcc_ctrl_en: %d\n",
+ chip->charge_status, parallel_en, chip->esr_fcc_ctrl_en);
+
+ if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING &&
+ parallel_en) {
+ if (chip->esr_fcc_ctrl_en)
+ return 0;
+
+ /*
+ * When parallel charging is enabled, configure ESR FCC to
+ * 300mA to trigger an ESR pulse. Without this, FG can ask
+ * the main charger to increase FCC when it is supposed to
+ * decrease it.
+ */
+ rc = fg_masked_write(chip, BATT_INFO_ESR_FAST_CRG_CFG(chip),
+ ESR_FAST_CRG_IVAL_MASK |
+ ESR_FAST_CRG_CTL_EN_BIT,
+ ESR_FCC_300MA | ESR_FAST_CRG_CTL_EN_BIT);
+ if (rc < 0) {
+ pr_err("Error in writing to %04x, rc=%d\n",
+ BATT_INFO_ESR_FAST_CRG_CFG(chip), rc);
+ return rc;
+ }
+
+ chip->esr_fcc_ctrl_en = true;
+ } else {
+ if (!chip->esr_fcc_ctrl_en)
+ return 0;
+
+ /*
+ * If we're here, then it means either the device is not in
+ * charging state or parallel charging is disabled. Disable
+ * ESR fast charge current control in SW.
+ */
+ rc = fg_masked_write(chip, BATT_INFO_ESR_FAST_CRG_CFG(chip),
+ ESR_FAST_CRG_CTL_EN_BIT, 0);
+ if (rc < 0) {
+ pr_err("Error in writing to %04x, rc=%d\n",
+ BATT_INFO_ESR_FAST_CRG_CFG(chip), rc);
+ return rc;
+ }
+
+ chip->esr_fcc_ctrl_en = false;
+ }
+
+ fg_dbg(chip, FG_STATUS, "esr_fcc_ctrl_en set to %d\n",
+ chip->esr_fcc_ctrl_en);
+ return 0;
+}
+
+static void fg_batt_avg_update(struct fg_chip *chip)
+{
+ if (chip->charge_status == chip->prev_charge_status)
+ return;
+
+ cancel_delayed_work_sync(&chip->batt_avg_work);
+ fg_circ_buf_clr(&chip->ibatt_circ_buf);
+ fg_circ_buf_clr(&chip->vbatt_circ_buf);
+
+ if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING ||
+ chip->charge_status == POWER_SUPPLY_STATUS_DISCHARGING)
+ schedule_delayed_work(&chip->batt_avg_work,
+ msecs_to_jiffies(2000));
+}
+
static void status_change_work(struct work_struct *work)
{
struct fg_chip *chip = container_of(work,
@@ -1370,7 +1526,16 @@ static void status_change_work(struct work_struct *work)
goto out;
}
- chip->status = prop.intval;
+ chip->prev_charge_status = chip->charge_status;
+ chip->charge_status = prop.intval;
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
+ if (rc < 0) {
+ pr_err("Error in getting charge type, rc=%d\n", rc);
+ goto out;
+ }
+
+ chip->charge_type = prop.intval;
rc = power_supply_get_property(chip->batt_psy,
POWER_SUPPLY_PROP_CHARGE_DONE, &prop);
if (rc < 0) {
@@ -1379,9 +1544,6 @@ static void status_change_work(struct work_struct *work)
}
chip->charge_done = prop.intval;
- fg_dbg(chip, FG_POWER_SUPPLY, "curr_status:%d charge_done: %d\n",
- chip->status, chip->charge_done);
-
if (chip->cyc_ctr.en)
schedule_work(&chip->cycle_count_work);
@@ -1398,7 +1560,16 @@ static void status_change_work(struct work_struct *work)
rc = fg_adjust_ki_coeff_dischg(chip);
if (rc < 0)
pr_err("Error in adjusting ki_coeff_dischg, rc=%d\n", rc);
+
+ rc = fg_esr_fcc_config(chip);
+ if (rc < 0)
+ pr_err("Error in adjusting FCC for ESR, rc=%d\n", rc);
+
+ fg_batt_avg_update(chip);
+
out:
+ fg_dbg(chip, FG_POWER_SUPPLY, "charge_status:%d charge_type:%d charge_done:%d\n",
+ chip->charge_status, chip->charge_type, chip->charge_done);
pm_relax(chip->dev);
}
@@ -1485,7 +1656,7 @@ static void cycle_count_work(struct work_struct *work)
/* We need only the most significant byte here */
batt_soc = (u32)batt_soc >> 24;
- if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
+ if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) {
/* Find out which bucket the SOC falls in */
bucket = batt_soc / BUCKET_SOC_PCT;
pr_debug("batt_soc: %d bucket: %d\n", batt_soc, bucket);
@@ -1556,6 +1727,10 @@ static void dump_sram(u8 *buf, int len)
}
}
+#define PROFILE_LOAD_BIT BIT(0)
+#define BOOTLOADER_LOAD_BIT BIT(1)
+#define BOOTLOADER_RESTART_BIT BIT(2)
+#define HLOS_RESTART_BIT BIT(3)
static bool is_profile_load_required(struct fg_chip *chip)
{
u8 buf[PROFILE_COMP_LEN], val;
@@ -1570,7 +1745,7 @@ static bool is_profile_load_required(struct fg_chip *chip)
}
/* Check if integrity bit is set */
- if (val == 0x01) {
+ if (val & PROFILE_LOAD_BIT) {
fg_dbg(chip, FG_STATUS, "Battery profile integrity bit is set\n");
rc = fg_sram_read(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET,
buf, PROFILE_COMP_LEN, FG_IMA_DEFAULT);
@@ -1728,7 +1903,7 @@ static void profile_load_work(struct work_struct *work)
fg_dbg(chip, FG_STATUS, "SOC is ready\n");
/* Set the profile integrity bit */
- val = 0x1;
+ val = HLOS_RESTART_BIT | PROFILE_LOAD_BIT;
rc = fg_sram_write(chip, PROFILE_INTEGRITY_WORD,
PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT);
if (rc < 0) {
@@ -1798,6 +1973,229 @@ static struct kernel_param_ops fg_restart_ops = {
module_param_cb(restart, &fg_restart_ops, &fg_restart, 0644);
+#define BATT_AVG_POLL_PERIOD_MS 10000
+static void batt_avg_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work, struct fg_chip,
+ batt_avg_work.work);
+ int rc, ibatt_now, vbatt_now;
+
+ mutex_lock(&chip->batt_avg_lock);
+ rc = fg_get_battery_current(chip, &ibatt_now);
+ if (rc < 0) {
+ pr_err("failed to get battery current, rc=%d\n", rc);
+ goto reschedule;
+ }
+
+ rc = fg_get_battery_voltage(chip, &vbatt_now);
+ if (rc < 0) {
+ pr_err("failed to get battery voltage, rc=%d\n", rc);
+ goto reschedule;
+ }
+
+ fg_circ_buf_add(&chip->ibatt_circ_buf, ibatt_now);
+ fg_circ_buf_add(&chip->vbatt_circ_buf, vbatt_now);
+
+reschedule:
+ mutex_unlock(&chip->batt_avg_lock);
+ schedule_delayed_work(&chip->batt_avg_work,
+ msecs_to_jiffies(BATT_AVG_POLL_PERIOD_MS));
+}
+
+#define DECI_TAU_SCALE 13
+#define HOURS_TO_SECONDS 3600
+#define OCV_SLOPE_UV 10869
+#define MILLI_UNIT 1000
+#define MICRO_UNIT 1000000
+static int fg_get_time_to_full(struct fg_chip *chip, int *val)
+{
+ int rc, ibatt_avg, vbatt_avg, rbatt, msoc, ocv_cc2cv, full_soc,
+ act_cap_uah;
+ s32 i_cc2cv, soc_cc2cv, ln_val;
+ s64 t_predicted_cc = 0, t_predicted_cv = 0;
+
+ if (chip->bp.float_volt_uv <= 0) {
+ pr_err("battery profile is not loaded\n");
+ return -ENODATA;
+ }
+
+ if (!is_charger_available(chip)) {
+ fg_dbg(chip, FG_TTF, "charger is not available\n");
+ return -ENODATA;
+ }
+
+ if (chip->charge_status == POWER_SUPPLY_STATUS_FULL) {
+ *val = 0;
+ return 0;
+ }
+
+ mutex_lock(&chip->batt_avg_lock);
+ rc = fg_circ_buf_avg(&chip->ibatt_circ_buf, &ibatt_avg);
+ if (rc < 0) {
+ /* try to get instantaneous current */
+ rc = fg_get_battery_current(chip, &ibatt_avg);
+ if (rc < 0) {
+ mutex_unlock(&chip->batt_avg_lock);
+ pr_err("failed to get battery current, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ rc = fg_circ_buf_avg(&chip->vbatt_circ_buf, &vbatt_avg);
+ if (rc < 0) {
+ /* try to get instantaneous voltage */
+ rc = fg_get_battery_voltage(chip, &vbatt_avg);
+ if (rc < 0) {
+ mutex_unlock(&chip->batt_avg_lock);
+ pr_err("failed to get battery voltage, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ mutex_unlock(&chip->batt_avg_lock);
+ fg_dbg(chip, FG_TTF, "vbatt_avg=%d\n", vbatt_avg);
+
+ /* clamp ibatt_avg to -150mA */
+ if (ibatt_avg > -150000)
+ ibatt_avg = -150000;
+ fg_dbg(chip, FG_TTF, "ibatt_avg=%d\n", ibatt_avg);
+
+ /* reverse polarity to be consistent with unsigned current settings */
+ ibatt_avg = abs(ibatt_avg);
+
+ /* estimated battery current at the CC to CV transition */
+ i_cc2cv = div_s64((s64)ibatt_avg * vbatt_avg, chip->bp.float_volt_uv);
+ fg_dbg(chip, FG_TTF, "i_cc2cv=%d\n", i_cc2cv);
+
+ rc = fg_get_battery_resistance(chip, &rbatt);
+ if (rc < 0) {
+ pr_err("failed to get battery resistance rc=%d\n", rc);
+ return rc;
+ }
+
+ /* clamp rbatt to 50mOhms */
+ if (rbatt < 50000)
+ rbatt = 50000;
+
+ fg_dbg(chip, FG_TTF, "rbatt=%d\n", rbatt);
+
+ rc = fg_get_sram_prop(chip, FG_SRAM_ACT_BATT_CAP, &act_cap_uah);
+ if (rc < 0) {
+ pr_err("failed to get ACT_BATT_CAP rc=%d\n", rc);
+ return rc;
+ }
+ act_cap_uah *= MILLI_UNIT;
+ fg_dbg(chip, FG_TTF, "actual_capacity_uah=%d\n", act_cap_uah);
+
+ rc = fg_get_prop_capacity(chip, &msoc);
+ if (rc < 0) {
+ pr_err("failed to get msoc rc=%d\n", rc);
+ return rc;
+ }
+ fg_dbg(chip, FG_TTF, "msoc=%d\n", msoc);
+
+ rc = fg_get_sram_prop(chip, FG_SRAM_FULL_SOC, &full_soc);
+ if (rc < 0) {
+ pr_err("failed to get full soc rc=%d\n", rc);
+ return rc;
+ }
+ full_soc = DIV_ROUND_CLOSEST(((u16)full_soc >> 8) * FULL_CAPACITY,
+ FULL_SOC_RAW);
+ fg_dbg(chip, FG_TTF, "full_soc=%d\n", full_soc);
+
+ /* if we are already in CV state then we can skip estimating CC */
+ if (chip->charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER)
+ goto skip_cc_estimate;
+
+ /* if the charger is current limited then use power approximation */
+ if (ibatt_avg > chip->bp.fastchg_curr_ma * MILLI_UNIT - 50000)
+ ocv_cc2cv = div_s64((s64)rbatt * ibatt_avg, MICRO_UNIT);
+ else
+ ocv_cc2cv = div_s64((s64)rbatt * i_cc2cv, MICRO_UNIT);
+ ocv_cc2cv = chip->bp.float_volt_uv - ocv_cc2cv;
+ fg_dbg(chip, FG_TTF, "ocv_cc2cv=%d\n", ocv_cc2cv);
+
+ soc_cc2cv = div_s64(chip->bp.float_volt_uv - ocv_cc2cv, OCV_SLOPE_UV);
+ /* estimated SOC at the CC to CV transition */
+ soc_cc2cv = 100 - soc_cc2cv;
+ fg_dbg(chip, FG_TTF, "soc_cc2cv=%d\n", soc_cc2cv);
+
+ /* the esimated SOC may be lower than the current SOC */
+ if (soc_cc2cv - msoc <= 0)
+ goto skip_cc_estimate;
+
+ t_predicted_cc = div_s64((s64)full_soc * act_cap_uah, 100);
+ t_predicted_cc = div_s64(t_predicted_cc * (soc_cc2cv - msoc), 100);
+ t_predicted_cc *= HOURS_TO_SECONDS;
+ t_predicted_cc = div_s64(t_predicted_cc, (ibatt_avg + i_cc2cv) / 2);
+
+skip_cc_estimate:
+ fg_dbg(chip, FG_TTF, "t_predicted_cc=%lld\n", t_predicted_cc);
+
+ /* CV estimate starts here */
+ if (chip->charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER)
+ ln_val = ibatt_avg / abs(chip->dt.sys_term_curr_ma);
+ else
+ ln_val = i_cc2cv / abs(chip->dt.sys_term_curr_ma);
+
+ fg_dbg(chip, FG_TTF, "ln_in=%d\n", ln_val);
+ rc = fg_lerp(fg_ln_table, ARRAY_SIZE(fg_ln_table), ln_val, &ln_val);
+ fg_dbg(chip, FG_TTF, "ln_out=%d\n", ln_val);
+ t_predicted_cv = div_s64((s64)act_cap_uah * rbatt, MICRO_UNIT);
+ t_predicted_cv = div_s64(t_predicted_cv * DECI_TAU_SCALE, 10);
+ t_predicted_cv = div_s64(t_predicted_cv * ln_val, MILLI_UNIT);
+ t_predicted_cv = div_s64(t_predicted_cv * HOURS_TO_SECONDS, MICRO_UNIT);
+ fg_dbg(chip, FG_TTF, "t_predicted_cv=%lld\n", t_predicted_cv);
+ *val = t_predicted_cc + t_predicted_cv;
+ return 0;
+}
+
+#define CENTI_ICORRECT_C0 105
+#define CENTI_ICORRECT_C1 20
+static int fg_get_time_to_empty(struct fg_chip *chip, int *val)
+{
+ int rc, ibatt_avg, msoc, act_cap_uah;
+ s32 divisor;
+ s64 t_predicted;
+
+ rc = fg_circ_buf_avg(&chip->ibatt_circ_buf, &ibatt_avg);
+ if (rc < 0) {
+ /* try to get instantaneous current */
+ rc = fg_get_battery_current(chip, &ibatt_avg);
+ if (rc < 0) {
+ pr_err("failed to get battery current, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /* clamp ibatt_avg to 150mA */
+ if (ibatt_avg < 150000)
+ ibatt_avg = 150000;
+
+ rc = fg_get_sram_prop(chip, FG_SRAM_ACT_BATT_CAP, &act_cap_uah);
+ if (rc < 0) {
+ pr_err("Error in getting ACT_BATT_CAP, rc=%d\n", rc);
+ return rc;
+ }
+ act_cap_uah *= MILLI_UNIT;
+
+ rc = fg_get_prop_capacity(chip, &msoc);
+ if (rc < 0) {
+ pr_err("Error in getting capacity, rc=%d\n", rc);
+ return rc;
+ }
+
+ t_predicted = div_s64((s64)msoc * act_cap_uah, 100);
+ t_predicted *= HOURS_TO_SECONDS;
+ divisor = CENTI_ICORRECT_C0 * 100 + CENTI_ICORRECT_C1 * msoc;
+ divisor = div_s64((s64)divisor * ibatt_avg, 10000);
+ if (divisor > 0)
+ t_predicted = div_s64(t_predicted, divisor);
+
+ *val = t_predicted;
+ return 0;
+}
+
/* PSY CALLBACKS STAY HERE */
static int fg_psy_get_property(struct power_supply *psy,
@@ -1859,11 +2257,22 @@ static int fg_psy_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
rc = fg_get_cc_soc_sw(chip, &pval->intval);
break;
+ case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
+ rc = fg_get_time_to_full(chip, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+ rc = fg_get_time_to_empty(chip, &pval->intval);
+ break;
default:
+ pr_err("unsupported property %d\n", psp);
+ rc = -EINVAL;
break;
}
- return rc;
+ if (rc < 0)
+ return -ENODATA;
+
+ return 0;
}
static int fg_psy_set_property(struct power_supply *psy,
@@ -1946,6 +2355,8 @@ static enum power_supply_property fg_psy_props[] = {
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
};
static const struct power_supply_desc fg_psy_desc = {
@@ -2152,6 +2563,37 @@ static int fg_memif_init(struct fg_chip *chip)
/* INTERRUPT HANDLERS STAY HERE */
+static irqreturn_t fg_mem_xcp_irq_handler(int irq, void *data)
+{
+ struct fg_chip *chip = data;
+ u8 status;
+ int rc;
+
+ rc = fg_read(chip, MEM_IF_INT_RT_STS(chip), &status, 1);
+ if (rc < 0) {
+ pr_err("failed to read addr=0x%04x, rc=%d\n",
+ MEM_IF_INT_RT_STS(chip), rc);
+ return IRQ_HANDLED;
+ }
+
+ fg_dbg(chip, FG_IRQ, "irq %d triggered, status:%d\n", irq, status);
+ if (status & MEM_XCP_BIT) {
+ rc = fg_clear_dma_errors_if_any(chip);
+ if (rc < 0) {
+ pr_err("Error in clearing DMA error, rc=%d\n", rc);
+ return IRQ_HANDLED;
+ }
+
+ mutex_lock(&chip->sram_rw_lock);
+ rc = fg_clear_ima_errors_if_any(chip, true);
+ if (rc < 0 && rc != -EAGAIN)
+ pr_err("Error in checking IMA errors rc:%d\n", rc);
+ mutex_unlock(&chip->sram_rw_lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t fg_vbatt_low_irq_handler(int irq, void *data)
{
struct fg_chip *chip = data;
@@ -2248,14 +2690,10 @@ static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data)
struct fg_chip *chip = data;
int rc;
+ fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
if (chip->cyc_ctr.en)
schedule_work(&chip->cycle_count_work);
- if (is_charger_available(chip))
- power_supply_changed(chip->batt_psy);
-
- fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
-
if (chip->cl.active)
fg_cap_learning_update(chip);
@@ -2267,6 +2705,9 @@ static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data)
if (rc < 0)
pr_err("Error in adjusting ki_coeff_dischg, rc=%d\n", rc);
+ if (is_charger_available(chip))
+ power_supply_changed(chip->batt_psy);
+
return IRQ_HANDLED;
}
@@ -2274,10 +2715,10 @@ static irqreturn_t fg_empty_soc_irq_handler(int irq, void *data)
{
struct fg_chip *chip = data;
+ fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
if (is_charger_available(chip))
power_supply_changed(chip->batt_psy);
- fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
return IRQ_HANDLED;
}
@@ -2291,9 +2732,7 @@ static irqreturn_t fg_soc_irq_handler(int irq, void *data)
static irqreturn_t fg_dummy_irq_handler(int irq, void *data)
{
- struct fg_chip *chip = data;
-
- fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
+ pr_debug("irq %d triggered\n", irq);
return IRQ_HANDLED;
}
@@ -2367,7 +2806,7 @@ static struct fg_irq_info fg_irqs[FG_IRQ_MAX] = {
},
[MEM_XCP_IRQ] = {
.name = "mem-xcp",
- .handler = fg_dummy_irq_handler,
+ .handler = fg_mem_xcp_irq_handler,
},
[IMA_RDY_IRQ] = {
.name = "ima-rdy",
@@ -2495,7 +2934,7 @@ static int fg_parse_ki_coefficients(struct fg_chip *chip)
}
#define DEFAULT_CUTOFF_VOLT_MV 3200
-#define DEFAULT_EMPTY_VOLT_MV 3100
+#define DEFAULT_EMPTY_VOLT_MV 2800
#define DEFAULT_CHG_TERM_CURR_MA 100
#define DEFAULT_SYS_TERM_CURR_MA -125
#define DEFAULT_DELTA_SOC_THR 1
@@ -2612,7 +3051,7 @@ static int fg_parse_dt(struct fg_chip *chip)
rc = fg_get_batt_profile(chip);
if (rc < 0)
pr_warn("profile for batt_id=%dKOhms not found..using OTP, rc:%d\n",
- chip->batt_id, rc);
+ chip->batt_id_kohms, rc);
/* Read all the optional properties below */
rc = of_property_read_u32(node, "qcom,fg-cutoff-voltage", &temp);
@@ -2783,7 +3222,7 @@ static int fg_gen3_probe(struct platform_device *pdev)
{
struct fg_chip *chip;
struct power_supply_config fg_psy_cfg;
- int rc;
+ int rc, msoc, volt_uv, batt_temp;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
@@ -2792,6 +3231,8 @@ static int fg_gen3_probe(struct platform_device *pdev)
chip->dev = &pdev->dev;
chip->debug_mask = &fg_gen3_debug_mask;
chip->irqs = fg_irqs;
+ chip->charge_status = -EINVAL;
+ chip->prev_charge_status = -EINVAL;
chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
if (!chip->regmap) {
dev_err(chip->dev, "Parent regmap is unavailable\n");
@@ -2816,11 +3257,13 @@ static int fg_gen3_probe(struct platform_device *pdev)
mutex_init(&chip->sram_rw_lock);
mutex_init(&chip->cyc_ctr.lock);
mutex_init(&chip->cl.lock);
+ mutex_init(&chip->batt_avg_lock);
init_completion(&chip->soc_update);
init_completion(&chip->soc_ready);
INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work);
INIT_WORK(&chip->status_change_work, status_change_work);
INIT_WORK(&chip->cycle_count_work, cycle_count_work);
+ INIT_DELAYED_WORK(&chip->batt_avg_work, batt_avg_work);
rc = fg_memif_init(chip);
if (rc < 0) {
@@ -2876,11 +3319,22 @@ static int fg_gen3_probe(struct platform_device *pdev)
goto exit;
}
+ rc = fg_get_battery_voltage(chip, &volt_uv);
+ if (!rc)
+ rc = fg_get_prop_capacity(chip, &msoc);
+
+ if (!rc)
+ rc = fg_get_battery_temp(chip, &batt_temp);
+
+ if (!rc)
+ pr_info("battery SOC:%d voltage: %duV temp: %d id: %dKOhms\n",
+ msoc, volt_uv, batt_temp, chip->batt_id_kohms);
+
+ device_init_wakeup(chip->dev, true);
if (chip->profile_available)
schedule_delayed_work(&chip->profile_load_work, 0);
- device_init_wakeup(chip->dev, true);
- pr_debug("FG GEN3 driver successfully probed\n");
+ pr_debug("FG GEN3 driver probed successfully\n");
return 0;
exit:
fg_cleanup(chip);
@@ -2902,6 +3356,7 @@ static int fg_gen3_suspend(struct device *dev)
}
}
+ cancel_delayed_work_sync(&chip->batt_avg_work);
return 0;
}
@@ -2920,6 +3375,9 @@ static int fg_gen3_resume(struct device *dev)
}
}
+ fg_circ_buf_clr(&chip->ibatt_circ_buf);
+ fg_circ_buf_clr(&chip->vbatt_circ_buf);
+ schedule_delayed_work(&chip->batt_avg_work, 0);
return 0;
}
diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c
index 93965dbe99ae..6968ab2ab11c 100644
--- a/drivers/power/qcom-charger/qpnp-smb2.c
+++ b/drivers/power/qcom-charger/qpnp-smb2.c
@@ -203,6 +203,13 @@ static struct smb_params v1_params = {
.get_proc = smblib_mapping_cc_delta_to_field_value,
.set_proc = smblib_mapping_cc_delta_from_field_value,
},
+ .freq_buck = {
+ .name = "buck switching frequency",
+ .reg = CFG_BUCKBOOST_FREQ_SELECT_BUCK_REG,
+ .min_u = 600,
+ .max_u = 2000,
+ .step_u = 200,
+ },
};
#define STEP_CHARGING_MAX_STEPS 5
@@ -218,6 +225,7 @@ struct smb_dt_props {
s32 step_cc_delta[STEP_CHARGING_MAX_STEPS];
struct device_node *revid_dev_node;
int float_option;
+ int chg_inhibit_thr_mv;
bool hvdcp_disable;
};
@@ -328,6 +336,14 @@ static int smb2_parse_dt(struct smb2 *chip)
chip->dt.hvdcp_disable = of_property_read_bool(node,
"qcom,hvdcp-disable");
+ of_property_read_u32(node, "qcom,chg-inhibit-threshold-mv",
+ &chip->dt.chg_inhibit_thr_mv);
+ if ((chip->dt.chg_inhibit_thr_mv < 0 ||
+ chip->dt.chg_inhibit_thr_mv > 300)) {
+ pr_err("qcom,chg-inhibit-threshold-mv is incorrect\n");
+ return -EINVAL;
+ }
+
return 0;
}
@@ -1033,6 +1049,7 @@ static int smb2_init_hw(struct smb2 *chip)
{
struct smb_charger *chg = &chip->chg;
int rc;
+ u8 stat;
if (chip->dt.no_battery)
chg->fake_capacity = 50;
@@ -1053,6 +1070,21 @@ static int smb2_init_hw(struct smb2 *chip)
chg->otg_cl_ua = chip->dt.otg_cl_ua;
+ rc = smblib_read(chg, APSD_RESULT_STATUS_REG, &stat);
+ if (rc < 0) {
+ pr_err("Couldn't read APSD_RESULT_STATUS rc=%d\n", rc);
+ return rc;
+ }
+
+ /* clear the ICL override if it is set */
+ if (stat & ICL_OVERRIDE_LATCH_BIT) {
+ rc = smblib_write(chg, CMD_APSD_REG, ICL_OVERRIDE_BIT);
+ if (rc < 0) {
+ pr_err("Couldn't disable ICL override rc=%d\n", rc);
+ return rc;
+ }
+ }
+
/* votes must be cast before configuring software control */
vote(chg->pl_disable_votable,
PL_INDIRECT_VOTER, true, 0);
@@ -1145,6 +1177,15 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
+ /* increase VCONN softstart */
+ rc = smblib_masked_write(chg, TYPE_C_CFG_2_REG,
+ VCONN_SOFTSTART_CFG_MASK, VCONN_SOFTSTART_CFG_MASK);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't increase VCONN softstart rc=%d\n",
+ rc);
+ return rc;
+ }
+
rc = smblib_masked_write(chg, QNOVO_PT_ENABLE_CMD_REG,
QNOVO_PT_ENABLE_CMD_BIT, QNOVO_PT_ENABLE_CMD_BIT);
if (rc < 0) {
@@ -1167,13 +1208,12 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
- /* configure PMI stat output to enable and disable parallel charging */
+ /* disable SW STAT override */
rc = smblib_masked_write(chg, STAT_CFG_REG,
- STAT_PARALLEL_CFG_BIT | STAT_SW_OVERRIDE_CFG_BIT,
- STAT_PARALLEL_CFG_BIT);
+ STAT_SW_OVERRIDE_CFG_BIT, 0);
if (rc < 0) {
- dev_err(chg->dev,
- "Couldn't configure signal for parallel rc=%d\n", rc);
+ dev_err(chg->dev, "Couldn't disable SW STAT override rc=%d\n",
+ rc);
return rc;
}
@@ -1206,6 +1246,40 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
+ switch (chip->dt.chg_inhibit_thr_mv) {
+ case 50:
+ rc = smblib_masked_write(chg, CHARGE_INHIBIT_THRESHOLD_CFG_REG,
+ CHARGE_INHIBIT_THRESHOLD_MASK,
+ CHARGE_INHIBIT_THRESHOLD_50MV);
+ break;
+ case 100:
+ rc = smblib_masked_write(chg, CHARGE_INHIBIT_THRESHOLD_CFG_REG,
+ CHARGE_INHIBIT_THRESHOLD_MASK,
+ CHARGE_INHIBIT_THRESHOLD_100MV);
+ break;
+ case 200:
+ rc = smblib_masked_write(chg, CHARGE_INHIBIT_THRESHOLD_CFG_REG,
+ CHARGE_INHIBIT_THRESHOLD_MASK,
+ CHARGE_INHIBIT_THRESHOLD_200MV);
+ break;
+ case 300:
+ rc = smblib_masked_write(chg, CHARGE_INHIBIT_THRESHOLD_CFG_REG,
+ CHARGE_INHIBIT_THRESHOLD_MASK,
+ CHARGE_INHIBIT_THRESHOLD_300MV);
+ break;
+ case 0:
+ rc = smblib_masked_write(chg, CHGR_CFG2_REG,
+ CHARGER_INHIBIT_BIT, 0);
+ default:
+ break;
+ }
+
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't configure charge inhibit threshold rc=%d\n",
+ rc);
+ return rc;
+ }
+
return rc;
}
@@ -1234,8 +1308,11 @@ static int smb2_setup_wa_flags(struct smb2 *chip)
switch (pmic_rev_id->pmic_subtype) {
case PMICOBALT_SUBTYPE:
+ chip->chg.wa_flags |= BOOST_BACK_WA;
if (pmic_rev_id->rev4 == PMICOBALT_V1P1_REV4) /* PMI rev 1.1 */
chg->wa_flags |= QC_CHARGER_DETECTION_WA_BIT;
+ if (pmic_rev_id->rev4 == PMICOBALT_V2P0_REV4) /* PMI rev 2.0 */
+ chg->wa_flags |= TYPEC_CC2_REMOVAL_WA_BIT;
break;
default:
pr_err("PMIC subtype %d not supported\n",
@@ -1444,7 +1521,8 @@ static struct smb2_irq_info smb2_irqs[] = {
},
{
.name = "switcher-power-ok",
- .handler = smblib_handle_debug,
+ .handler = smblib_handle_switcher_power_ok,
+ .storm_data = {true, 1000, 3},
},
};
@@ -1739,6 +1817,16 @@ static int smb2_remove(struct platform_device *pdev)
return 0;
}
+static void smb2_shutdown(struct platform_device *pdev)
+{
+ struct smb2 *chip = platform_get_drvdata(pdev);
+ struct smb_charger *chg = &chip->chg;
+
+ smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+ HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT, 0);
+ smblib_write(chg, CMD_HVDCP_2_REG, FORCE_5V_BIT);
+}
+
static const struct of_device_id match_table[] = {
{ .compatible = "qcom,qpnp-smb2", },
{ },
@@ -1752,6 +1840,7 @@ static struct platform_driver smb2_driver = {
},
.probe = smb2_probe,
.remove = smb2_remove,
+ .shutdown = smb2_shutdown,
};
module_platform_driver(smb2_driver);
diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c
index de4391024970..6aae7d49271f 100644
--- a/drivers/power/qcom-charger/smb-lib.c
+++ b/drivers/power/qcom-charger/smb-lib.c
@@ -220,6 +220,34 @@ int smblib_get_usb_suspend(struct smb_charger *chg, int *suspend)
return rc;
}
+#define FSW_600HZ_FOR_5V 600
+#define FSW_800HZ_FOR_6V_8V 800
+#define FSW_1MHZ_FOR_REMOVAL 1000
+#define FSW_1MHZ_FOR_9V 1000
+#define FSW_1P2MHZ_FOR_12V 1200
+static int smblib_set_opt_freq_buck(struct smb_charger *chg, int fsw_khz)
+{
+ union power_supply_propval pval = {0, };
+ int rc = 0;
+
+ rc = smblib_set_charge_param(chg, &chg->param.freq_buck, fsw_khz);
+ if (rc < 0)
+ dev_err(chg->dev, "Error in setting freq_buck rc=%d\n", rc);
+
+ if (chg->mode == PARALLEL_MASTER && chg->pl.psy) {
+ pval.intval = fsw_khz;
+ rc = power_supply_set_property(chg->pl.psy,
+ POWER_SUPPLY_PROP_BUCK_FREQ, &pval);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Could not set parallel buck_freq rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
struct apsd_result {
const char * const name;
const u8 bit;
@@ -416,10 +444,13 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg,
if (min_allowed_uv == MICRO_5V && max_allowed_uv == MICRO_5V) {
allowed_voltage = USBIN_ADAPTER_ALLOW_5V;
+ smblib_set_opt_freq_buck(chg, FSW_600HZ_FOR_5V);
} else if (min_allowed_uv == MICRO_9V && max_allowed_uv == MICRO_9V) {
allowed_voltage = USBIN_ADAPTER_ALLOW_9V;
+ smblib_set_opt_freq_buck(chg, FSW_1MHZ_FOR_9V);
} else if (min_allowed_uv == MICRO_12V && max_allowed_uv == MICRO_12V) {
allowed_voltage = USBIN_ADAPTER_ALLOW_12V;
+ smblib_set_opt_freq_buck(chg, FSW_1P2MHZ_FOR_12V);
} else if (min_allowed_uv < MICRO_9V && max_allowed_uv <= MICRO_9V) {
allowed_voltage = USBIN_ADAPTER_ALLOW_5V_TO_9V;
} else if (min_allowed_uv < MICRO_9V && max_allowed_uv <= MICRO_12V) {
@@ -459,7 +490,7 @@ static int try_rerun_apsd_for_hvdcp(struct smb_charger *chg)
/* ensure hvdcp is enabled */
if (!get_effective_result(chg->hvdcp_disable_votable)) {
apsd_result = smblib_get_apsd_result(chg);
- if (apsd_result->pst == POWER_SUPPLY_TYPE_USB_HVDCP) {
+ if (apsd_result->bit & (QC_2P0_BIT | QC_3P0_BIT)) {
/* rerun APSD */
smblib_dbg(chg, PR_MISC, "rerun APSD\n");
smblib_masked_write(chg, CMD_APSD_REG,
@@ -565,7 +596,11 @@ static int smblib_usb_suspend_vote_callback(struct votable *votable, void *data,
{
struct smb_charger *chg = data;
- return smblib_set_usb_suspend(chg, suspend);
+ /* resume input if suspend is invalid */
+ if (suspend < 0)
+ suspend = 0;
+
+ return smblib_set_usb_suspend(chg, (bool)suspend);
}
static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data,
@@ -573,10 +608,11 @@ static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data,
{
struct smb_charger *chg = data;
+ /* resume input if suspend is invalid */
if (suspend < 0)
- suspend = false;
+ suspend = 0;
- return smblib_set_dc_suspend(chg, suspend);
+ return smblib_set_dc_suspend(chg, (bool)suspend);
}
static int smblib_fcc_max_vote_callback(struct votable *votable, void *data,
@@ -925,12 +961,13 @@ static int smblib_apsd_disable_vote_callback(struct votable *votable,
* OTG REGULATOR *
*****************/
-#define OTG_SOFT_START_DELAY_MS 20
+#define MAX_SOFTSTART_TRIES 2
int smblib_vbus_regulator_enable(struct regulator_dev *rdev)
{
struct smb_charger *chg = rdev_get_drvdata(rdev);
u8 stat;
int rc = 0;
+ int tries = MAX_SOFTSTART_TRIES;
rc = smblib_masked_write(chg, OTG_ENG_OTG_CFG_REG,
ENG_BUCKBOOST_HALT1_8_MODE_BIT,
@@ -947,14 +984,25 @@ int smblib_vbus_regulator_enable(struct regulator_dev *rdev)
return rc;
}
- msleep(OTG_SOFT_START_DELAY_MS);
- rc = smblib_read(chg, OTG_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read OTG_STATUS_REG rc=%d\n", rc);
- return rc;
- }
- if (stat & BOOST_SOFTSTART_DONE_BIT)
- smblib_otg_cl_config(chg, chg->otg_cl_ua);
+ /* waiting for boost readiness, usually ~1ms, 2ms in worst case */
+ do {
+ usleep_range(1000, 1100);
+
+ rc = smblib_read(chg, OTG_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read OTG_STATUS_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+ if (stat & BOOST_SOFTSTART_DONE_BIT) {
+ smblib_otg_cl_config(chg, chg->otg_cl_ua);
+ break;
+ }
+ } while (--tries);
+
+ if (tries == 0)
+ smblib_err(chg, "Timeout waiting for boost softstart rc=%d\n",
+ rc);
return rc;
}
@@ -1416,21 +1464,17 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg,
int smblib_get_prop_dc_present(struct smb_charger *chg,
union power_supply_propval *val)
{
- int rc = 0;
+ int rc;
u8 stat;
- rc = smblib_read(chg, DC_INT_RT_STS_REG, &stat);
+ rc = smblib_read(chg, DCIN_BASE + INT_RT_STS_OFFSET, &stat);
if (rc < 0) {
- smblib_err(chg, "Couldn't read DC_INT_RT_STS_REG rc=%d\n",
- rc);
+ smblib_err(chg, "Couldn't read DCIN_RT_STS rc=%d\n", rc);
return rc;
}
- smblib_dbg(chg, PR_REGISTER, "DC_INT_RT_STS_REG = 0x%02x\n",
- stat);
val->intval = (bool)(stat & DCIN_PLUGIN_RT_STS_BIT);
-
- return rc;
+ return 0;
}
int smblib_get_prop_dc_online(struct smb_charger *chg,
@@ -1486,20 +1530,17 @@ int smblib_set_prop_dc_current_max(struct smb_charger *chg,
int smblib_get_prop_usb_present(struct smb_charger *chg,
union power_supply_propval *val)
{
- int rc = 0;
+ int rc;
u8 stat;
- rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
+ rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
+ smblib_err(chg, "Couldn't read USBIN_RT_STS rc=%d\n", rc);
return rc;
}
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n",
- stat);
-
- val->intval = (bool)(stat & CC_ATTACHED_BIT);
- return rc;
+ val->intval = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
+ return 0;
}
int smblib_get_prop_usb_online(struct smb_charger *chg,
@@ -1966,6 +2007,45 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
"Couldn't enable vconn on CC line rc=%d\n", rc);
return rc;
}
+
+ rc = vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_500MA);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't vote for USB ICL rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG,
+ USBIN_MODE_CHG_BIT, USBIN_MODE_CHG_BIT);
+ if (rc < 0) {
+ smblib_err(chg,
+ "Couldn't change USB mode rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smblib_masked_write(chg, CMD_APSD_REG,
+ ICL_OVERRIDE_BIT, ICL_OVERRIDE_BIT);
+ if (rc < 0) {
+ smblib_err(chg,
+ "Couldn't override APSD rc=%d\n", rc);
+ return rc;
+ }
+ } else {
+ rc = smblib_masked_write(chg, CMD_APSD_REG,
+ ICL_OVERRIDE_BIT, 0);
+ if (rc < 0) {
+ smblib_err(chg,
+ "Couldn't override APSD rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG,
+ USBIN_MODE_CHG_BIT, 0);
+ if (rc < 0) {
+ smblib_err(chg,
+ "Couldn't change USB mode rc=%d\n", rc);
+ return rc;
+ }
}
/* CC pin selection s/w override in PD session; h/w otherwise. */
@@ -1996,6 +2076,146 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
return rc;
}
+int smblib_reg_block_update(struct smb_charger *chg,
+ struct reg_info *entry)
+{
+ int rc = 0;
+
+ while (entry && entry->reg) {
+ rc = smblib_read(chg, entry->reg, &entry->bak);
+ if (rc < 0) {
+ dev_err(chg->dev, "Error in reading %s rc=%d\n",
+ entry->desc, rc);
+ break;
+ }
+ entry->bak &= entry->mask;
+
+ rc = smblib_masked_write(chg, entry->reg,
+ entry->mask, entry->val);
+ if (rc < 0) {
+ dev_err(chg->dev, "Error in writing %s rc=%d\n",
+ entry->desc, rc);
+ break;
+ }
+ entry++;
+ }
+
+ return rc;
+}
+
+int smblib_reg_block_restore(struct smb_charger *chg,
+ struct reg_info *entry)
+{
+ int rc = 0;
+
+ while (entry && entry->reg) {
+ rc = smblib_masked_write(chg, entry->reg,
+ entry->mask, entry->bak);
+ if (rc < 0) {
+ dev_err(chg->dev, "Error in writing %s rc=%d\n",
+ entry->desc, rc);
+ break;
+ }
+ entry++;
+ }
+
+ return rc;
+}
+
+static struct reg_info cc2_detach_settings[] = {
+ {
+ .reg = TYPE_C_CFG_2_REG,
+ .mask = TYPE_C_UFP_MODE_BIT | EN_TRY_SOURCE_MODE_BIT,
+ .val = TYPE_C_UFP_MODE_BIT,
+ .desc = "TYPE_C_CFG_2_REG",
+ },
+ {
+ .reg = TYPE_C_CFG_3_REG,
+ .mask = EN_TRYSINK_MODE_BIT,
+ .val = 0,
+ .desc = "TYPE_C_CFG_3_REG",
+ },
+ {
+ .reg = TAPER_TIMER_SEL_CFG_REG,
+ .mask = TYPEC_SPARE_CFG_BIT,
+ .val = TYPEC_SPARE_CFG_BIT,
+ .desc = "TAPER_TIMER_SEL_CFG_REG",
+ },
+ {
+ .reg = TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ .mask = VCONN_EN_ORIENTATION_BIT,
+ .val = 0,
+ .desc = "TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG",
+ },
+ {
+ .reg = MISC_CFG_REG,
+ .mask = TCC_DEBOUNCE_20MS_BIT,
+ .val = TCC_DEBOUNCE_20MS_BIT,
+ .desc = "Tccdebounce time"
+ },
+ {
+ },
+};
+
+static int smblib_cc2_sink_removal_enter(struct smb_charger *chg)
+{
+ int rc = 0;
+ union power_supply_propval cc2_val = {0, };
+
+ if ((chg->wa_flags & TYPEC_CC2_REMOVAL_WA_BIT) == 0)
+ return rc;
+
+ if (chg->cc2_sink_detach_flag != CC2_SINK_NONE)
+ return rc;
+
+ rc = smblib_get_prop_typec_cc_orientation(chg, &cc2_val);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get cc orientation rc=%d\n", rc);
+ return rc;
+ }
+ if (cc2_val.intval == 1)
+ return rc;
+
+ rc = smblib_get_prop_typec_mode(chg, &cc2_val);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get prop typec mode rc=%d\n", rc);
+ return rc;
+ }
+
+ switch (cc2_val.intval) {
+ case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
+ smblib_reg_block_update(chg, cc2_detach_settings);
+ chg->cc2_sink_detach_flag = CC2_SINK_STD;
+ schedule_work(&chg->rdstd_cc2_detach_work);
+ break;
+ case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
+ case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
+ chg->cc2_sink_detach_flag = CC2_SINK_MEDIUM_HIGH;
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+static int smblib_cc2_sink_removal_exit(struct smb_charger *chg)
+{
+ int rc = 0;
+
+ if ((chg->wa_flags & TYPEC_CC2_REMOVAL_WA_BIT) == 0)
+ return rc;
+
+ if (chg->cc2_sink_detach_flag == CC2_SINK_STD) {
+ cancel_work_sync(&chg->rdstd_cc2_detach_work);
+ smblib_reg_block_restore(chg, cc2_detach_settings);
+ }
+
+ chg->cc2_sink_detach_flag = CC2_SINK_NONE;
+
+ return rc;
+}
+
int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg,
const union power_supply_propval *val)
{
@@ -2004,9 +2224,24 @@ int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg,
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
EXIT_SNK_BASED_ON_CC_BIT,
(val->intval) ? EXIT_SNK_BASED_ON_CC_BIT : 0);
+ if (rc < 0) {
+ smblib_err(chg, "Could not set EXIT_SNK_BASED_ON_CC rc=%d\n",
+ rc);
+ return rc;
+ }
vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, val->intval, 0);
+ if (val->intval)
+ rc = smblib_cc2_sink_removal_enter(chg);
+ else
+ rc = smblib_cc2_sink_removal_exit(chg);
+
+ if (rc < 0) {
+ smblib_err(chg, "Could not detect cc2 removal rc=%d\n", rc);
+ return rc;
+ }
+
return rc;
}
@@ -2177,6 +2412,17 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
struct smb_charger *chg = irq_data->parent_data;
int rc;
u8 stat;
+ bool vbus_rising;
+
+ rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't read USB_INT_RT_STS rc=%d\n", rc);
+ return IRQ_HANDLED;
+ }
+
+ vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
+ smblib_set_opt_freq_buck(chg,
+ vbus_rising ? FSW_600HZ_FOR_5V : FSW_1MHZ_FOR_REMOVAL);
/* fetch the DPDM regulator */
if (!chg->dpdm_reg && of_get_property(chg->dev->of_node,
@@ -2189,19 +2435,8 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
}
}
- if (!chg->dpdm_reg)
- goto skip_dpdm_float;
-
- rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc);
- return IRQ_HANDLED;
- }
-
- chg->vbus_present = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
-
- if (chg->vbus_present) {
- if (!regulator_is_enabled(chg->dpdm_reg)) {
+ if (vbus_rising) {
+ if (chg->dpdm_reg && !regulator_is_enabled(chg->dpdm_reg)) {
smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n");
rc = regulator_enable(chg->dpdm_reg);
if (rc < 0)
@@ -2209,7 +2444,14 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
rc);
}
} else {
- if (regulator_is_enabled(chg->dpdm_reg)) {
+ if (chg->wa_flags & BOOST_BACK_WA) {
+ vote(chg->usb_suspend_votable,
+ BOOST_BACK_VOTER, false, 0);
+ vote(chg->dc_suspend_votable,
+ BOOST_BACK_VOTER, false, 0);
+ }
+
+ if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) {
smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
rc = regulator_disable(chg->dpdm_reg);
if (rc < 0)
@@ -2218,10 +2460,9 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
}
}
-skip_dpdm_float:
power_supply_changed(chg->usb_psy);
smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n",
- irq_data->name, chg->vbus_present ? "attached" : "detached");
+ irq_data->name, vbus_rising ? "attached" : "detached");
return IRQ_HANDLED;
}
@@ -2271,6 +2512,60 @@ static void smblib_handle_sdp_enumeration_done(struct smb_charger *chg,
rising ? "rising" : "falling");
}
+#define QC3_PULSES_FOR_6V 5
+#define QC3_PULSES_FOR_9V 20
+#define QC3_PULSES_FOR_12V 35
+static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg)
+{
+ int rc;
+ u8 stat;
+ int pulses;
+
+ if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_HVDCP) {
+ rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg,
+ "Couldn't read QC_CHANGE_STATUS rc=%d\n", rc);
+ return;
+ }
+
+ switch (stat & QC_2P0_STATUS_MASK) {
+ case QC_5V_BIT:
+ smblib_set_opt_freq_buck(chg, FSW_600HZ_FOR_5V);
+ break;
+ case QC_9V_BIT:
+ smblib_set_opt_freq_buck(chg, FSW_1MHZ_FOR_9V);
+ break;
+ case QC_12V_BIT:
+ smblib_set_opt_freq_buck(chg, FSW_1P2MHZ_FOR_12V);
+ break;
+ default:
+ smblib_set_opt_freq_buck(chg, FSW_1MHZ_FOR_REMOVAL);
+ break;
+ }
+ }
+
+ if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_HVDCP_3) {
+ rc = smblib_read(chg, QC_PULSE_COUNT_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg,
+ "Couldn't read QC_PULSE_COUNT rc=%d\n", rc);
+ return;
+ }
+ pulses = (stat & QC_PULSE_COUNT_MASK);
+
+ if (pulses < QC3_PULSES_FOR_6V)
+ smblib_set_opt_freq_buck(chg, FSW_600HZ_FOR_5V);
+ else if (pulses < QC3_PULSES_FOR_9V)
+ smblib_set_opt_freq_buck(chg, FSW_800HZ_FOR_6V_8V);
+ else if (pulses < QC3_PULSES_FOR_12V)
+ smblib_set_opt_freq_buck(chg, FSW_1MHZ_FOR_9V);
+ else
+ smblib_set_opt_freq_buck(chg, FSW_1P2MHZ_FOR_12V);
+
+ }
+}
+
static void smblib_handle_adaptive_voltage_done(struct smb_charger *chg,
bool rising)
{
@@ -2283,10 +2578,20 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg,
bool rising)
{
const struct apsd_result *apsd_result;
+ int rc;
if (!rising)
return;
+ /*
+ * Disable AUTH_IRQ_EN_CFG_BIT to receive adapter voltage
+ * change interrupt.
+ */
+ rc = smblib_masked_write(chg, USBIN_SOURCE_CHANGE_INTRPT_ENB_REG,
+ AUTH_IRQ_EN_CFG_BIT, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable QC auth setting rc=%d\n", rc);
+
if (chg->mode == PARALLEL_MASTER)
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, true, 0);
@@ -2392,6 +2697,8 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data)
smblib_handle_slow_plugin_timeout(chg,
(bool)(stat & SLOW_PLUGIN_TIMEOUT_BIT));
+ smblib_hvdcp_adaptive_voltage_change(chg);
+
power_supply_changed(chg->usb_psy);
return IRQ_HANDLED;
@@ -2410,6 +2717,12 @@ static void typec_source_removal(struct smb_charger *chg)
cancel_delayed_work_sync(&chg->hvdcp_detect_work);
+ /* reset AUTH_IRQ_EN_CFG_BIT */
+ rc = smblib_masked_write(chg, USBIN_SOURCE_CHANGE_INTRPT_ENB_REG,
+ AUTH_IRQ_EN_CFG_BIT, AUTH_IRQ_EN_CFG_BIT);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable QC auth setting rc=%d\n", rc);
+
/* reconfigure allowed voltage for HVDCP */
rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG,
USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
@@ -2516,6 +2829,24 @@ static void smblib_handle_typec_debounce_done(struct smb_charger *chg,
if (rc < 0)
smblib_err(chg, "Couldn't get prop typec mode rc=%d\n", rc);
+ /*
+ * HW BUG - after cable is removed, medium or high rd reading
+ * falls to std. Use it for signal of typec cc detachment in
+ * software WA.
+ */
+ if (chg->cc2_sink_detach_flag == CC2_SINK_MEDIUM_HIGH
+ && pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) {
+
+ chg->cc2_sink_detach_flag = CC2_SINK_WA_DONE;
+
+ rc = smblib_masked_write(chg,
+ TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ EXIT_SNK_BASED_ON_CC_BIT, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't get prop typec mode rc=%d\n",
+ rc);
+ }
+
smblib_dbg(chg, PR_INTERRUPT, "IRQ: debounce-done %s; Type-C %s detected\n",
rising ? "rising" : "falling",
smblib_typec_mode_name[pval.intval]);
@@ -2529,6 +2860,10 @@ irqreturn_t smblib_handle_usb_typec_change(int irq, void *data)
u8 stat;
bool debounce_done, sink_attached, legacy_cable;
+ /* WA - not when PD hard_reset WIP on cc2 in sink mode */
+ if (chg->cc2_sink_detach_flag == CC2_SINK_STD)
+ return IRQ_HANDLED;
+
rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
if (rc < 0) {
smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
@@ -2578,6 +2913,39 @@ irqreturn_t smblib_handle_high_duty_cycle(int irq, void *data)
return IRQ_HANDLED;
}
+irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+ int rc;
+ u8 stat;
+
+ if (!(chg->wa_flags & BOOST_BACK_WA))
+ return IRQ_HANDLED;
+
+ rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n", rc);
+ return IRQ_HANDLED;
+ }
+
+ if ((stat & USE_USBIN_BIT) &&
+ get_effective_result(chg->usb_suspend_votable))
+ return IRQ_HANDLED;
+
+ if ((stat & USE_DCIN_BIT) &&
+ get_effective_result(chg->dc_suspend_votable))
+ return IRQ_HANDLED;
+
+ if (is_storming(&irq_data->storm_data)) {
+ smblib_dbg(chg, PR_MISC, "reverse boost detected; suspending input\n");
+ vote(chg->usb_suspend_votable, BOOST_BACK_VOTER, true, 0);
+ vote(chg->dc_suspend_votable, BOOST_BACK_VOTER, true, 0);
+ }
+
+ return IRQ_HANDLED;
+}
+
/***************
* Work Queues *
***************/
@@ -2638,7 +3006,9 @@ static void smblib_pl_taper_work(struct work_struct *work)
union power_supply_propval pval = {0, };
int rc;
+ smblib_dbg(chg, PR_PARALLEL, "starting parallel taper work\n");
if (chg->pl.slave_fcc_ua < MINIMUM_PARALLEL_FCC_UA) {
+ smblib_dbg(chg, PR_PARALLEL, "parallel taper is done\n");
vote(chg->pl_disable_votable, TAPER_END_VOTER, true, 0);
goto done;
}
@@ -2650,6 +3020,7 @@ static void smblib_pl_taper_work(struct work_struct *work)
}
if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) {
+ smblib_dbg(chg, PR_PARALLEL, "master is taper charging; reducing slave FCC\n");
vote(chg->awake_votable, PL_TAPER_WORK_RUNNING_VOTER, true, 0);
/* Reduce the taper percent by 25 percent */
chg->pl.taper_pct = chg->pl.taper_pct
@@ -2663,6 +3034,8 @@ static void smblib_pl_taper_work(struct work_struct *work)
/*
* Master back to Fast Charge, get out of this round of taper reduction
*/
+ smblib_dbg(chg, PR_PARALLEL, "master is fast charging; waiting for next taper\n");
+
done:
vote(chg->awake_votable, PL_TAPER_WORK_RUNNING_VOTER, false, 0);
}
@@ -2675,6 +3048,74 @@ static void clear_hdc_work(struct work_struct *work)
chg->is_hdc = 0;
}
+static void rdstd_cc2_detach_work(struct work_struct *work)
+{
+ int rc;
+ u8 stat;
+ struct smb_irq_data irq_data = {NULL, "cc2-removal-workaround"};
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ rdstd_cc2_detach_work);
+
+ /*
+ * WA steps -
+ * 1. Enable both UFP and DFP, wait for 10ms.
+ * 2. Disable DFP, wait for 30ms.
+ * 3. Removal detected if both TYPEC_DEBOUNCE_DONE_STATUS
+ * and TIMER_STAGE bits are gone, otherwise repeat all by
+ * work rescheduling.
+ * Note, work will be cancelled when pd_hard_reset is 0.
+ */
+
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ UFP_EN_CMD_BIT | DFP_EN_CMD_BIT,
+ UFP_EN_CMD_BIT | DFP_EN_CMD_BIT);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't write TYPE_C_CTRL_REG rc=%d\n", rc);
+ return;
+ }
+
+ usleep_range(10000, 11000);
+
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ UFP_EN_CMD_BIT | DFP_EN_CMD_BIT,
+ UFP_EN_CMD_BIT);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't write TYPE_C_CTRL_REG rc=%d\n", rc);
+ return;
+ }
+
+ usleep_range(30000, 31000);
+
+ rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n",
+ rc);
+ return;
+ }
+ if (stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT)
+ goto rerun;
+
+ rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg,
+ "Couldn't read TYPE_C_STATUS_5_REG rc=%d\n", rc);
+ return;
+ }
+ if (stat & TIMER_STAGE_2_BIT)
+ goto rerun;
+
+ /* Bingo, cc2 removal detected */
+ smblib_reg_block_restore(chg, cc2_detach_settings);
+ chg->cc2_sink_detach_flag = CC2_SINK_WA_DONE;
+ irq_data.parent_data = chg;
+ smblib_handle_usb_typec_change(0, &irq_data);
+
+ return;
+
+rerun:
+ schedule_work(&chg->rdstd_cc2_detach_work);
+}
+
static int smblib_create_votables(struct smb_charger *chg)
{
int rc = 0;
@@ -2857,6 +3298,7 @@ int smblib_init(struct smb_charger *chg)
mutex_init(&chg->write_lock);
INIT_WORK(&chg->bms_update_work, bms_update_work);
INIT_WORK(&chg->pl_detect_work, smblib_pl_detect_work);
+ INIT_WORK(&chg->rdstd_cc2_detach_work, rdstd_cc2_detach_work);
INIT_DELAYED_WORK(&chg->hvdcp_detect_work, smblib_hvdcp_detect_work);
INIT_DELAYED_WORK(&chg->pl_taper_work, smblib_pl_taper_work);
INIT_DELAYED_WORK(&chg->step_soc_req_work, step_soc_req_work);
diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h
index 4be06ffcfb25..a0237412ee8b 100644
--- a/drivers/power/qcom-charger/smb-lib.h
+++ b/drivers/power/qcom-charger/smb-lib.h
@@ -46,6 +46,7 @@ enum print_reason {
#define VBUS_CC_SHORT_VOTER "VBUS_CC_SHORT_VOTER"
#define LEGACY_CABLE_VOTER "LEGACY_CABLE_VOTER"
#define PD_INACTIVE_VOTER "PD_INACTIVE_VOTER"
+#define BOOST_BACK_VOTER "BOOST_BACK_VOTER"
enum smb_mode {
PARALLEL_MASTER = 0,
@@ -53,8 +54,17 @@ enum smb_mode {
NUM_MODES,
};
+enum cc2_sink_type {
+ CC2_SINK_NONE = 0,
+ CC2_SINK_STD,
+ CC2_SINK_MEDIUM_HIGH,
+ CC2_SINK_WA_DONE,
+};
+
enum {
- QC_CHARGER_DETECTION_WA_BIT = BIT(0),
+ QC_CHARGER_DETECTION_WA_BIT = BIT(0),
+ BOOST_BACK_WA = BIT(1),
+ TYPEC_CC2_REMOVAL_WA_BIT = BIT(2),
};
struct smb_regulator {
@@ -116,6 +126,14 @@ struct smb_iio {
struct iio_channel *batt_i_chan;
};
+struct reg_info {
+ u16 reg;
+ u8 mask;
+ u8 val;
+ u8 bak;
+ const char *desc;
+};
+
struct smb_charger {
struct device *dev;
char *name;
@@ -167,6 +185,7 @@ struct smb_charger {
/* work */
struct work_struct bms_update_work;
struct work_struct pl_detect_work;
+ struct work_struct rdstd_cc2_detach_work;
struct delayed_work hvdcp_detect_work;
struct delayed_work ps_change_timeout_work;
struct delayed_work pl_taper_work;
@@ -177,7 +196,6 @@ struct smb_charger {
int voltage_min_uv;
int voltage_max_uv;
int pd_active;
- bool vbus_present;
bool system_suspend_supported;
int system_temp_level;
@@ -195,6 +213,7 @@ struct smb_charger {
/* workaround flag */
u32 wa_flags;
+ enum cc2_sink_type cc2_sink_detach_flag;
};
int smblib_read(struct smb_charger *chg, u16 addr, u8 *val);
@@ -240,6 +259,7 @@ irqreturn_t smblib_handle_icl_change(int irq, void *data);
irqreturn_t smblib_handle_usb_typec_change(int irq, void *data);
irqreturn_t smblib_handle_dc_plugin(int irq, void *data);
irqreturn_t smblib_handle_high_duty_cycle(int irq, void *data);
+irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data);
int smblib_get_prop_input_suspend(struct smb_charger *chg,
union power_supply_propval *val);
diff --git a/drivers/power/qcom-charger/smb-reg.h b/drivers/power/qcom-charger/smb-reg.h
index ba501761c209..c2a2b0c86d73 100644
--- a/drivers/power/qcom-charger/smb-reg.h
+++ b/drivers/power/qcom-charger/smb-reg.h
@@ -184,6 +184,10 @@ enum {
#define CHARGE_INHIBIT_THRESHOLD_CFG_REG (CHGR_BASE + 0x72)
#define CHARGE_INHIBIT_THRESHOLD_MASK GENMASK(1, 0)
+#define CHARGE_INHIBIT_THRESHOLD_50MV 0
+#define CHARGE_INHIBIT_THRESHOLD_100MV 1
+#define CHARGE_INHIBIT_THRESHOLD_200MV 2
+#define CHARGE_INHIBIT_THRESHOLD_300MV 3
#define RECHARGE_THRESHOLD_CFG_REG (CHGR_BASE + 0x73)
#define RECHARGE_THRESHOLD_MASK GENMASK(1, 0)
@@ -428,6 +432,7 @@ enum {
#define USBIN_5V_TO_12V_BIT BIT(2)
#define USBIN_5V_TO_9V_BIT BIT(1)
#define USBIN_5V_BIT BIT(0)
+#define QC_2P0_STATUS_MASK GENMASK(2, 0)
#define APSD_STATUS_REG (USBIN_BASE + 0x07)
#define APSD_STATUS_7_BIT BIT(7)
@@ -700,9 +705,6 @@ enum {
#define WIPWR_RANGE_STATUS_REG (DCIN_BASE + 0x08)
#define WIPWR_RANGE_STATUS_MASK GENMASK(4, 0)
-#define DC_INT_RT_STS_REG (DCIN_BASE + 0x10)
-#define DCIN_PLUGIN_RT_STS_BIT BIT(4)
-
/* DCIN Interrupt Bits */
#define WIPWR_VOLTAGE_RANGE_RT_STS_BIT BIT(7)
#define DCIN_ICL_CHANGE_RT_STS_BIT BIT(6)
@@ -900,6 +902,7 @@ enum {
#define MISC_CFG_REG (MISC_BASE + 0x52)
#define GSM_PA_ON_ADJ_SEL_BIT BIT(0)
+#define TCC_DEBOUNCE_20MS_BIT BIT(5)
#define SNARL_BARK_BITE_WD_CFG_REG (MISC_BASE + 0x53)
#define BITE_WDOG_DISABLE_CHARGING_CFG_BIT BIT(7)
diff --git a/drivers/power/qcom-charger/smb138x-charger.c b/drivers/power/qcom-charger/smb138x-charger.c
index 3db295b3e6e8..9dc528a6bb45 100644
--- a/drivers/power/qcom-charger/smb138x-charger.c
+++ b/drivers/power/qcom-charger/smb138x-charger.c
@@ -48,14 +48,14 @@ static struct smb_params v1_params = {
.name = "fast charge current",
.reg = FAST_CHARGE_CURRENT_CFG_REG,
.min_u = 0,
- .max_u = 4500000,
+ .max_u = 6000000,
.step_u = 25000,
},
.fv = {
.name = "float voltage",
.reg = FLOAT_VOLTAGE_CFG_REG,
- .min_u = 2500000,
- .max_u = 5000000,
+ .min_u = 2450000,
+ .max_u = 4950000,
.step_u = 10000,
},
.usb_icl = {
@@ -397,6 +397,7 @@ static int smb138x_init_batt_psy(struct smb138x *chip)
*****************************/
static enum power_supply_property smb138x_parallel_props[] = {
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_CHARGING_ENABLED,
POWER_SUPPLY_PROP_PIN_ENABLED,
POWER_SUPPLY_PROP_INPUT_SUSPEND,
@@ -417,6 +418,9 @@ static int smb138x_parallel_get_prop(struct power_supply *psy,
u8 temp;
switch (prop) {
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ rc = smblib_get_prop_batt_charge_type(chg, val);
+ break;
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
rc = smblib_read(chg, BATTERY_CHARGER_STATUS_5_REG,
&temp);
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index 17c8a5b00843..7569e35b59e0 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -183,5 +183,19 @@ config POWER_RESET_ZX
help
Reboot support for ZTE SoCs.
+config REBOOT_MODE
+ tristate
+
+config SYSCON_REBOOT_MODE
+ tristate "Generic SYSCON regmap reboot mode driver"
+ depends on OF
+ select REBOOT_MODE
+ select MFD_SYSCON
+ help
+ Say y here will enable reboot mode driver. This will
+ get reboot mode arguments and store it in SYSCON mapped
+ register, then the bootloader can read it to take different
+ action according to the mode.
+
endif
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index 3904e7977d07..66568c4497a4 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -20,3 +20,5 @@ obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o
obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o
obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o
+obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
+obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c
index 2f109013f723..d32f293695bb 100644
--- a/drivers/power/reset/msm-poweroff.c
+++ b/drivers/power/reset/msm-poweroff.c
@@ -380,7 +380,6 @@ static void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd)
msm_trigger_wdog_bite();
#endif
- scm_disable_sdi();
halt_spmi_pmic_arbiter();
deassert_ps_hold();
diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c
new file mode 100644
index 000000000000..2dfbbce0f817
--- /dev/null
+++ b/drivers/power/reset/reboot-mode.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/reboot.h>
+#include "reboot-mode.h"
+
+#define PREFIX "mode-"
+
+struct mode_info {
+ const char *mode;
+ u32 magic;
+ struct list_head list;
+};
+
+static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
+ const char *cmd)
+{
+ const char *normal = "normal";
+ int magic = 0;
+ struct mode_info *info;
+
+ if (!cmd)
+ cmd = normal;
+
+ list_for_each_entry(info, &reboot->head, list) {
+ if (!strcmp(info->mode, cmd)) {
+ magic = info->magic;
+ break;
+ }
+ }
+
+ return magic;
+}
+
+static int reboot_mode_notify(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ struct reboot_mode_driver *reboot;
+ unsigned int magic;
+
+ reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
+ magic = get_reboot_mode_magic(reboot, cmd);
+ if (magic)
+ reboot->write(reboot, magic);
+
+ return NOTIFY_DONE;
+}
+
+/**
+ * reboot_mode_register - register a reboot mode driver
+ * @reboot: reboot mode driver
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+int reboot_mode_register(struct reboot_mode_driver *reboot)
+{
+ struct mode_info *info;
+ struct property *prop;
+ struct device_node *np = reboot->dev->of_node;
+ size_t len = strlen(PREFIX);
+ int ret;
+
+ INIT_LIST_HEAD(&reboot->head);
+
+ for_each_property_of_node(np, prop) {
+ if (strncmp(prop->name, PREFIX, len))
+ continue;
+
+ info = devm_kzalloc(reboot->dev, sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ if (of_property_read_u32(np, prop->name, &info->magic)) {
+ dev_err(reboot->dev, "reboot mode %s without magic number\n",
+ info->mode);
+ devm_kfree(reboot->dev, info);
+ continue;
+ }
+
+ info->mode = kstrdup_const(prop->name + len, GFP_KERNEL);
+ if (!info->mode) {
+ ret = -ENOMEM;
+ goto error;
+ } else if (info->mode[0] == '\0') {
+ kfree_const(info->mode);
+ ret = -EINVAL;
+ dev_err(reboot->dev, "invalid mode name(%s): too short!\n",
+ prop->name);
+ goto error;
+ }
+
+ list_add_tail(&info->list, &reboot->head);
+ }
+
+ reboot->reboot_notifier.notifier_call = reboot_mode_notify;
+ register_reboot_notifier(&reboot->reboot_notifier);
+
+ return 0;
+
+error:
+ list_for_each_entry(info, &reboot->head, list)
+ kfree_const(info->mode);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(reboot_mode_register);
+
+/**
+ * reboot_mode_unregister - unregister a reboot mode driver
+ * @reboot: reboot mode driver
+ */
+int reboot_mode_unregister(struct reboot_mode_driver *reboot)
+{
+ struct mode_info *info;
+
+ unregister_reboot_notifier(&reboot->reboot_notifier);
+
+ list_for_each_entry(info, &reboot->head, list)
+ kfree_const(info->mode);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(reboot_mode_unregister);
+
+MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com");
+MODULE_DESCRIPTION("System reboot mode core library");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/reset/reboot-mode.h b/drivers/power/reset/reboot-mode.h
new file mode 100644
index 000000000000..2491bb71f591
--- /dev/null
+++ b/drivers/power/reset/reboot-mode.h
@@ -0,0 +1,14 @@
+#ifndef __REBOOT_MODE_H__
+#define __REBOOT_MODE_H__
+
+struct reboot_mode_driver {
+ struct device *dev;
+ struct list_head head;
+ int (*write)(struct reboot_mode_driver *reboot, unsigned int magic);
+ struct notifier_block reboot_notifier;
+};
+
+int reboot_mode_register(struct reboot_mode_driver *reboot);
+int reboot_mode_unregister(struct reboot_mode_driver *reboot);
+
+#endif
diff --git a/drivers/power/reset/syscon-reboot-mode.c b/drivers/power/reset/syscon-reboot-mode.c
new file mode 100644
index 000000000000..9e1cba5dd58e
--- /dev/null
+++ b/drivers/power/reset/syscon-reboot-mode.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include "reboot-mode.h"
+
+struct syscon_reboot_mode {
+ struct regmap *map;
+ struct reboot_mode_driver reboot;
+ u32 offset;
+ u32 mask;
+};
+
+static int syscon_reboot_mode_write(struct reboot_mode_driver *reboot,
+ unsigned int magic)
+{
+ struct syscon_reboot_mode *syscon_rbm;
+ int ret;
+
+ syscon_rbm = container_of(reboot, struct syscon_reboot_mode, reboot);
+
+ ret = regmap_update_bits(syscon_rbm->map, syscon_rbm->offset,
+ syscon_rbm->mask, magic);
+ if (ret < 0)
+ dev_err(reboot->dev, "update reboot mode bits failed\n");
+
+ return ret;
+}
+
+static int syscon_reboot_mode_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct syscon_reboot_mode *syscon_rbm;
+
+ syscon_rbm = devm_kzalloc(&pdev->dev, sizeof(*syscon_rbm), GFP_KERNEL);
+ if (!syscon_rbm)
+ return -ENOMEM;
+
+ syscon_rbm->reboot.dev = &pdev->dev;
+ syscon_rbm->reboot.write = syscon_reboot_mode_write;
+ syscon_rbm->mask = 0xffffffff;
+
+ dev_set_drvdata(&pdev->dev, syscon_rbm);
+
+ syscon_rbm->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
+ if (IS_ERR(syscon_rbm->map))
+ return PTR_ERR(syscon_rbm->map);
+
+ if (of_property_read_u32(pdev->dev.of_node, "offset",
+ &syscon_rbm->offset))
+ return -EINVAL;
+
+ of_property_read_u32(pdev->dev.of_node, "mask", &syscon_rbm->mask);
+
+ ret = reboot_mode_register(&syscon_rbm->reboot);
+ if (ret)
+ dev_err(&pdev->dev, "can't register reboot mode\n");
+
+ return ret;
+}
+
+static int syscon_reboot_mode_remove(struct platform_device *pdev)
+{
+ struct syscon_reboot_mode *syscon_rbm = dev_get_drvdata(&pdev->dev);
+
+ return reboot_mode_unregister(&syscon_rbm->reboot);
+}
+
+static const struct of_device_id syscon_reboot_mode_of_match[] = {
+ { .compatible = "syscon-reboot-mode" },
+ {}
+};
+
+static struct platform_driver syscon_reboot_mode_driver = {
+ .probe = syscon_reboot_mode_probe,
+ .remove = syscon_reboot_mode_remove,
+ .driver = {
+ .name = "syscon-reboot-mode",
+ .of_match_table = syscon_reboot_mode_of_match,
+ },
+};
+module_platform_driver(syscon_reboot_mode_driver);
+
+MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com");
+MODULE_DESCRIPTION("SYSCON reboot mode driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-brcmstb.c b/drivers/pwm/pwm-brcmstb.c
index 423ce087cd9c..5d5adee16886 100644
--- a/drivers/pwm/pwm-brcmstb.c
+++ b/drivers/pwm/pwm-brcmstb.c
@@ -274,8 +274,8 @@ static int brcmstb_pwm_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
p->base = devm_ioremap_resource(&pdev->dev, res);
- if (!p->base) {
- ret = -ENOMEM;
+ if (IS_ERR(p->base)) {
+ ret = PTR_ERR(p->base);
goto out_clk;
}
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
index f2e1a39ce0f3..5cf4a97e0304 100644
--- a/drivers/regulator/axp20x-regulator.c
+++ b/drivers/regulator/axp20x-regulator.c
@@ -221,10 +221,10 @@ static const struct regulator_desc axp22x_regulators[] = {
AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)),
AXP_DESC(AXP22X, ELDO3, "eldo3", "eldoin", 700, 3300, 100,
AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)),
- AXP_DESC_IO(AXP22X, LDO_IO0, "ldo_io0", "ips", 1800, 3300, 100,
+ AXP_DESC_IO(AXP22X, LDO_IO0, "ldo_io0", "ips", 700, 3300, 100,
AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07,
AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
- AXP_DESC_IO(AXP22X, LDO_IO1, "ldo_io1", "ips", 1800, 3300, 100,
+ AXP_DESC_IO(AXP22X, LDO_IO1, "ldo_io1", "ips", 700, 3300, 100,
AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07,
AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
AXP_DESC_FIXED(AXP22X, RTC_LDO, "rtc_ldo", "ips", 3000),
diff --git a/drivers/regulator/cprh-kbss-regulator.c b/drivers/regulator/cprh-kbss-regulator.c
index 953ea5f33f40..5661ae75843c 100644
--- a/drivers/regulator/cprh-kbss-regulator.c
+++ b/drivers/regulator/cprh-kbss-regulator.c
@@ -257,7 +257,7 @@ msmcobalt_v2_kbss_fuse_ref_volt[2][MSMCOBALT_KBSS_FUSE_CORNERS] = {
#define MSMCOBALT_KBSS_POWER_TEMP_SENSOR_ID_START 1
#define MSMCOBALT_KBSS_POWER_TEMP_SENSOR_ID_END 5
#define MSMCOBALT_KBSS_PERFORMANCE_TEMP_SENSOR_ID_START 6
-#define MSMCOBALT_KBSS_PERFORMANCE_TEMP_SENSOR_ID_END 11
+#define MSMCOBALT_KBSS_PERFORMANCE_TEMP_SENSOR_ID_END 10
#define MSMCOBALT_KBSS_POWER_AGING_SENSOR_ID 0
#define MSMCOBALT_KBSS_POWER_AGING_BYPASS_MASK0 0
diff --git a/drivers/regulator/qpnp-labibb-regulator.c b/drivers/regulator/qpnp-labibb-regulator.c
index 2e41401f8580..3d0be1f8a5b5 100644
--- a/drivers/regulator/qpnp-labibb-regulator.c
+++ b/drivers/regulator/qpnp-labibb-regulator.c
@@ -32,6 +32,7 @@
#define QPNP_LABIBB_REGULATOR_DRIVER_NAME "qcom,qpnp-labibb-regulator"
+#define REG_REVISION_2 0x01
#define REG_PERPH_TYPE 0x04
#define QPNP_LAB_TYPE 0x24
@@ -60,6 +61,7 @@
#define REG_LAB_PRECHARGE_CTL 0x5E
#define REG_LAB_SOFT_START_CTL 0x5F
#define REG_LAB_SPARE_CTL 0x60
+#define REG_LAB_PFM_CTL 0x62
/* LAB register bits definitions */
@@ -91,9 +93,9 @@
#define LAB_IBB_EN_RDY_EN BIT(7)
/* REG_LAB_CURRENT_LIMIT */
-#define LAB_CURRENT_LIMIT_BITS 3
-#define LAB_CURRENT_LIMIT_MASK ((1 << LAB_CURRENT_LIMIT_BITS) - 1)
-#define LAB_CURRENT_LIMIT_EN BIT(7)
+#define LAB_CURRENT_LIMIT_MASK GENMASK(2, 0)
+#define LAB_CURRENT_LIMIT_EN_BIT BIT(7)
+#define LAB_OVERRIDE_CURRENT_MAX_BIT BIT(3)
/* REG_LAB_CURRENT_SENSE */
#define LAB_CURRENT_SENSE_GAIN_BITS 2
@@ -129,6 +131,9 @@
#define LAB_SPARE_TOUCH_WAKE_BIT BIT(3)
#define LAB_SPARE_DISABLE_SCP_BIT BIT(0)
+/* REG_LAB_PFM_CTL */
+#define LAB_PFM_EN_BIT BIT(7)
+
/* IBB register offset definitions */
#define REG_IBB_REVISION4 0x03
#define REG_IBB_STATUS1 0x08
@@ -253,12 +258,6 @@
#define SWIRE_DEFAULT_2ND_CMD_DLY_MS 20
#define SWIRE_DEFAULT_IBB_PS_ENABLE_DLY_MS 200
-enum pmic_subtype {
- PMI8994 = 10,
- PMI8950 = 17,
- PMI8996 = 19,
-};
-
/**
* enum qpnp_labibb_mode - working mode of LAB/IBB regulators
* %QPNP_LABIBB_LCD_MODE: configure LAB and IBB regulators
@@ -350,6 +349,10 @@ static const int lab_current_limit_plan[] = {
400,
600,
800,
+ 1000,
+ 1200,
+ 1400,
+ 1600,
};
static const char * const lab_current_sense_plan[] = {
@@ -477,6 +480,8 @@ struct qpnp_labibb {
struct pmic_revid_data *pmic_rev_id;
u16 lab_base;
u16 ibb_base;
+ u8 lab_dig_major;
+ u8 ibb_dig_major;
struct lab_regulator lab_vreg;
struct ibb_regulator ibb_vreg;
enum qpnp_labibb_mode mode;
@@ -487,6 +492,7 @@ struct qpnp_labibb {
bool swire_control;
bool ttw_force_lab_on;
bool skip_2nd_swire_cmd;
+ bool pfm_enable;
u32 swire_2nd_cmd_delay;
u32 swire_ibb_ps_enable_delay;
};
@@ -539,60 +545,58 @@ static struct settings lab_settings[LAB_SETTINGS_MAX] = {
SETTING(LAB_RDSON_MNGMNT, false),
};
-static int
-qpnp_labibb_read(struct qpnp_labibb *labibb, u8 *val,
- u16 base, int count)
+static int qpnp_labibb_read(struct qpnp_labibb *labibb, u8 *val, u16 address,
+ int count)
{
int rc = 0;
struct platform_device *pdev = labibb->pdev;
- if (base == 0) {
- pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
- base, to_spmi_device(pdev->dev.parent)->usid, rc);
+ if (address == 0) {
+ pr_err("address cannot be zero address=0x%02x sid=0x%02x rc=%d\n",
+ address, to_spmi_device(pdev->dev.parent)->usid, rc);
return -EINVAL;
}
- rc = regmap_bulk_read(labibb->regmap, base, val, count);
+ rc = regmap_bulk_read(labibb->regmap, address, val, count);
if (rc) {
- pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", base,
- to_spmi_device(pdev->dev.parent)->usid, rc);
+ pr_err("SPMI read failed address=0x%02x sid=0x%02x rc=%d\n",
+ address, to_spmi_device(pdev->dev.parent)->usid, rc);
return rc;
}
+
return 0;
}
-static int
-qpnp_labibb_write(struct qpnp_labibb *labibb, u16 base,
- u8 *val, int count)
+static int qpnp_labibb_write(struct qpnp_labibb *labibb, u16 address, u8 *val,
+ int count)
{
int rc = 0;
struct platform_device *pdev = labibb->pdev;
- if (base == 0) {
- pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
- base, to_spmi_device(pdev->dev.parent)->usid, rc);
+ if (address == 0) {
+ pr_err("address cannot be zero address=0x%02x sid=0x%02x rc=%d\n",
+ address, to_spmi_device(pdev->dev.parent)->usid, rc);
return -EINVAL;
}
- rc = regmap_bulk_write(labibb->regmap, base, val, count);
+ rc = regmap_bulk_write(labibb->regmap, address, val, count);
if (rc) {
- pr_err("write failed base=0x%02x sid=0x%02x rc=%d\n",
- base, to_spmi_device(pdev->dev.parent)->usid, rc);
+ pr_err("write failed address=0x%02x sid=0x%02x rc=%d\n",
+ address, to_spmi_device(pdev->dev.parent)->usid, rc);
return rc;
}
return 0;
}
-static int
-qpnp_labibb_masked_write(struct qpnp_labibb *labibb, u16 base,
- u8 mask, u8 val)
+static int qpnp_labibb_masked_write(struct qpnp_labibb *labibb, u16 address,
+ u8 mask, u8 val)
{
int rc;
- rc = regmap_update_bits(labibb->regmap, base, mask, val);
+ rc = regmap_update_bits(labibb->regmap, address, mask, val);
if (rc) {
- pr_err("spmi write failed: addr=%03X, rc=%d\n", base, rc);
+ pr_err("spmi write failed: addr=%03X, rc=%d\n", address, rc);
return rc;
}
@@ -709,18 +713,24 @@ static int qpnp_lab_dt_init(struct qpnp_labibb *labibb,
u8 i, val;
u32 tmp;
- if (labibb->mode == QPNP_LABIBB_LCD_MODE)
- val = REG_LAB_IBB_LCD_MODE;
- else
- val = REG_LAB_IBB_AMOLED_MODE;
+ /*
+ * Do not configure LCD_AMOLED_SEL for pmicobalt as it will be done by
+ * GPIO selector.
+ */
+ if (labibb->pmic_rev_id->pmic_subtype != PMICOBALT_SUBTYPE) {
+ if (labibb->mode == QPNP_LABIBB_LCD_MODE)
+ val = REG_LAB_IBB_LCD_MODE;
+ else
+ val = REG_LAB_IBB_AMOLED_MODE;
- rc = qpnp_labibb_sec_write(labibb, labibb->lab_base,
- REG_LAB_LCD_AMOLED_SEL, &val, 1);
+ rc = qpnp_labibb_sec_write(labibb, labibb->lab_base,
+ REG_LAB_LCD_AMOLED_SEL, &val, 1);
- if (rc) {
- pr_err("qpnp_lab_sec_write register %x failed rc = %d\n",
- REG_LAB_LCD_AMOLED_SEL, rc);
- return rc;
+ if (rc) {
+ pr_err("qpnp_lab_sec_write register %x failed rc = %d\n",
+ REG_LAB_LCD_AMOLED_SEL, rc);
+ return rc;
+ }
}
val = 0;
@@ -785,7 +795,7 @@ static int qpnp_lab_dt_init(struct qpnp_labibb *labibb,
if (of_property_read_bool(of_node,
"qcom,qpnp-lab-limit-max-current-enable"))
- val |= LAB_CURRENT_LIMIT_EN;
+ val |= LAB_CURRENT_LIMIT_EN_BIT;
rc = qpnp_labibb_write(labibb, labibb->lab_base +
REG_LAB_CURRENT_LIMIT, &val, 1);
@@ -938,6 +948,88 @@ static int qpnp_lab_dt_init(struct qpnp_labibb *labibb,
return rc;
}
+#define LAB_CURRENT_MAX_1600MA 0x7
+#define LAB_CURRENT_MAX_400MA 0x1
+static int qpnp_lab_pfm_disable(struct qpnp_labibb *labibb)
+{
+ int rc = 0;
+ u8 val, mask;
+
+ mutex_lock(&(labibb->lab_vreg.lab_mutex));
+ if (!labibb->pfm_enable) {
+ pr_debug("PFM already disabled\n");
+ goto out;
+ }
+
+ val = 0;
+ mask = LAB_PFM_EN_BIT;
+ rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+ REG_LAB_PFM_CTL, mask, val);
+ if (rc < 0) {
+ pr_err("Write register %x failed rc = %d\n",
+ REG_LAB_PFM_CTL, rc);
+ goto out;
+ }
+
+ val = LAB_CURRENT_MAX_1600MA;
+ mask = LAB_OVERRIDE_CURRENT_MAX_BIT | LAB_CURRENT_LIMIT_MASK;
+ rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+ REG_LAB_CURRENT_LIMIT, mask, val);
+ if (rc < 0) {
+ pr_err("Write register %x failed rc = %d\n",
+ REG_LAB_CURRENT_LIMIT, rc);
+ goto out;
+ }
+
+ labibb->pfm_enable = false;
+out:
+ mutex_unlock(&(labibb->lab_vreg.lab_mutex));
+ return rc;
+}
+
+static int qpnp_lab_pfm_enable(struct qpnp_labibb *labibb)
+{
+ int rc = 0;
+ u8 val, mask;
+
+ mutex_lock(&(labibb->lab_vreg.lab_mutex));
+ if (labibb->pfm_enable) {
+ pr_debug("PFM already enabled\n");
+ goto out;
+ }
+
+ /* Wait for ~100uS */
+ usleep_range(100, 105);
+
+ val = LAB_OVERRIDE_CURRENT_MAX_BIT | LAB_CURRENT_MAX_400MA;
+ mask = LAB_OVERRIDE_CURRENT_MAX_BIT | LAB_CURRENT_LIMIT_MASK;
+ rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+ REG_LAB_CURRENT_LIMIT, mask, val);
+ if (rc < 0) {
+ pr_err("Write register %x failed rc = %d\n",
+ REG_LAB_CURRENT_LIMIT, rc);
+ goto out;
+ }
+
+ /* Wait for ~100uS */
+ usleep_range(100, 105);
+
+ val = LAB_PFM_EN_BIT;
+ mask = LAB_PFM_EN_BIT;
+ rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+ REG_LAB_PFM_CTL, mask, val);
+ if (rc < 0) {
+ pr_err("Write register %x failed rc = %d\n",
+ REG_LAB_PFM_CTL, rc);
+ goto out;
+ }
+
+ labibb->pfm_enable = true;
+out:
+ mutex_unlock(&(labibb->lab_vreg.lab_mutex));
+ return rc;
+}
+
static int qpnp_labibb_restore_settings(struct qpnp_labibb *labibb)
{
int rc, i;
@@ -1172,7 +1264,7 @@ static int qpnp_labibb_regulator_ttw_mode_enter(struct qpnp_labibb *labibb)
}
val = LAB_SPARE_DISABLE_SCP_BIT;
- if (labibb->pmic_rev_id->pmic_subtype != PMI8950)
+ if (labibb->pmic_rev_id->pmic_subtype != PMI8950_SUBTYPE)
val |= LAB_SPARE_TOUCH_WAKE_BIT;
rc = qpnp_labibb_write(labibb, labibb->lab_base +
REG_LAB_SPARE_CTL, &val, 1);
@@ -1199,10 +1291,10 @@ static int qpnp_labibb_regulator_ttw_mode_enter(struct qpnp_labibb *labibb)
}
switch (labibb->pmic_rev_id->pmic_subtype) {
- case PMI8996:
+ case PMI8996_SUBTYPE:
rc = qpnp_labibb_ttw_enter_ibb_pmi8996(labibb);
break;
- case PMI8950:
+ case PMI8950_SUBTYPE:
rc = qpnp_labibb_ttw_enter_ibb_pmi8950(labibb);
break;
}
@@ -1282,9 +1374,9 @@ static int qpnp_labibb_regulator_ttw_mode_exit(struct qpnp_labibb *labibb)
}
switch (labibb->pmic_rev_id->pmic_subtype) {
- case PMI8996:
- case PMI8994:
- case PMI8950:
+ case PMI8996_SUBTYPE:
+ case PMI8994_SUBTYPE:
+ case PMI8950_SUBTYPE:
rc = qpnp_labibb_ttw_exit_ibb_common(labibb);
break;
}
@@ -1437,6 +1529,15 @@ static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb)
return -EINVAL;
}
+ if (labibb->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE &&
+ labibb->mode == QPNP_LABIBB_LCD_MODE) {
+ rc = qpnp_lab_pfm_disable(labibb);
+ if (rc < 0) {
+ pr_err("Error in disabling PFM, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
labibb->lab_vreg.vreg_enabled = 0;
labibb->ibb_vreg.vreg_enabled = 0;
@@ -1646,9 +1747,17 @@ static irqreturn_t lab_vreg_ok_handler(int irq, void *_labibb)
struct qpnp_labibb *labibb = _labibb;
int rc;
- rc = qpnp_skip_swire_command(labibb);
- if (rc)
- pr_err("Failed in 'qpnp_skip_swire_command' rc=%d\n", rc);
+ if (labibb->skip_2nd_swire_cmd && labibb->lab_dig_major < 2) {
+ rc = qpnp_skip_swire_command(labibb);
+ if (rc < 0)
+ pr_err("Failed in 'qpnp_skip_swire_command' rc=%d\n",
+ rc);
+ } else if (labibb->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE &&
+ labibb->mode == QPNP_LABIBB_LCD_MODE) {
+ rc = qpnp_lab_pfm_enable(labibb);
+ if (rc < 0)
+ pr_err("Failed to config PFM, rc=%d\n", rc);
+ }
return IRQ_HANDLED;
}
@@ -1663,6 +1772,23 @@ static int qpnp_lab_regulator_get_voltage(struct regulator_dev *rdev)
return labibb->lab_vreg.curr_volt;
}
+static bool is_lab_vreg_ok_irq_available(struct qpnp_labibb *labibb)
+{
+ /*
+ * LAB VREG_OK interrupt is used only to skip 2nd SWIRE command in
+ * dig_major < 2 targets. For pmicobalt, it is used to enable PFM in
+ * LCD mode.
+ */
+ if (labibb->skip_2nd_swire_cmd && labibb->lab_dig_major < 2)
+ return true;
+
+ if (labibb->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE &&
+ labibb->mode == QPNP_LABIBB_LCD_MODE)
+ return true;
+
+ return false;
+}
+
static struct regulator_ops qpnp_lab_ops = {
.enable = qpnp_lab_regulator_enable,
.disable = qpnp_lab_regulator_disable,
@@ -1784,16 +1910,16 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb,
}
if (of_find_property(of_node,
- "qpnp,qpnp-lab-current-sense", NULL)) {
+ "qcom,qpnp-lab-current-sense", NULL)) {
config_current_sense = true;
rc = of_property_read_string(of_node,
- "qpnp,qpnp-lab-current-sense",
+ "qcom,qpnp-lab-current-sense",
&current_sense_str);
if (!rc) {
val = qpnp_labibb_get_matching_idx(
current_sense_str);
} else {
- pr_err("qpnp,qpnp-lab-current-sense configured incorrectly rc = %d\n",
+ pr_err("qcom,qpnp-lab-current-sense configured incorrectly rc = %d\n",
rc);
return rc;
}
@@ -1811,19 +1937,6 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb,
}
}
- if (labibb->skip_2nd_swire_cmd) {
- rc = devm_request_threaded_irq(labibb->dev,
- labibb->lab_vreg.lab_vreg_ok_irq, NULL,
- lab_vreg_ok_handler,
- IRQF_ONESHOT | IRQF_TRIGGER_RISING,
- "lab-vreg-ok", labibb);
- if (rc) {
- pr_err("Failed to register 'lab-vreg-ok' irq rc=%d\n",
- rc);
- return rc;
- }
- }
-
val = (labibb->standalone) ? 0 : LAB_IBB_EN_RDY_EN;
rc = qpnp_labibb_sec_write(labibb, labibb->lab_base,
REG_LAB_IBB_EN_RDY, &val, 1);
@@ -1901,6 +2014,19 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb,
labibb->lab_vreg.vreg_enabled = 1;
}
+ if (is_lab_vreg_ok_irq_available(labibb)) {
+ rc = devm_request_threaded_irq(labibb->dev,
+ labibb->lab_vreg.lab_vreg_ok_irq, NULL,
+ lab_vreg_ok_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "lab-vreg-ok", labibb);
+ if (rc) {
+ pr_err("Failed to register 'lab-vreg-ok' irq rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
rc = qpnp_labibb_read(labibb, &val,
labibb->lab_base + REG_LAB_MODULE_RDY, 1);
if (rc) {
@@ -1955,7 +2081,6 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb,
return -EINVAL;
}
- mutex_init(&(labibb->lab_vreg.lab_mutex));
return 0;
}
@@ -1966,18 +2091,37 @@ static int qpnp_ibb_dt_init(struct qpnp_labibb *labibb,
u32 i, tmp;
u8 val;
- if (labibb->mode == QPNP_LABIBB_LCD_MODE)
- val = REG_LAB_IBB_LCD_MODE;
- else
- val = REG_LAB_IBB_AMOLED_MODE;
+ /*
+ * Do not configure LCD_AMOLED_SEL for pmicobalt as it will be done by
+ * GPIO selector. Override the labibb->mode with what was configured
+ * by the bootloader.
+ */
+ if (labibb->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE) {
+ rc = qpnp_labibb_read(labibb, &val,
+ labibb->ibb_base + REG_IBB_LCD_AMOLED_SEL, 1);
+ if (rc) {
+ pr_err("qpnp_labibb_read register %x failed rc = %d\n",
+ REG_IBB_LCD_AMOLED_SEL, rc);
+ return rc;
+ }
- rc = qpnp_labibb_sec_write(labibb, labibb->ibb_base,
- REG_LAB_LCD_AMOLED_SEL, &val, 1);
+ if (val == REG_LAB_IBB_AMOLED_MODE)
+ labibb->mode = QPNP_LABIBB_AMOLED_MODE;
+ else
+ labibb->mode = QPNP_LABIBB_LCD_MODE;
+ } else {
+ if (labibb->mode == QPNP_LABIBB_LCD_MODE)
+ val = REG_LAB_IBB_LCD_MODE;
+ else
+ val = REG_LAB_IBB_AMOLED_MODE;
- if (rc) {
- pr_err("qpnp_labibb_sec_write register %x failed rc = %d\n",
- REG_IBB_LCD_AMOLED_SEL, rc);
- return rc;
+ rc = qpnp_labibb_sec_write(labibb, labibb->ibb_base,
+ REG_LAB_LCD_AMOLED_SEL, &val, 1);
+ if (rc) {
+ pr_err("qpnp_labibb_sec_write register %x failed rc = %d\n",
+ REG_IBB_LCD_AMOLED_SEL, rc);
+ return rc;
+ }
}
rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-lab-pwrdn-delay",
@@ -2482,6 +2626,13 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb,
return rc;
}
+ /*
+ * For pmicobalt, override swire_control with what was configured
+ * before by the bootloader.
+ */
+ if (labibb->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE)
+ labibb->swire_control = val & IBB_ENABLE_CTL_SWIRE_RDY;
+
if (ibb_enable_ctl &
(IBB_ENABLE_CTL_SWIRE_RDY | IBB_ENABLE_CTL_MODULE_EN)) {
/* SWIRE_RDY or IBB_MODULE_EN enabled */
@@ -2659,14 +2810,13 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb,
return -EINVAL;
}
- mutex_init(&(labibb->ibb_vreg.ibb_mutex));
return 0;
}
static int qpnp_lab_register_irq(struct device_node *child,
struct qpnp_labibb *labibb)
{
- if (labibb->skip_2nd_swire_cmd) {
+ if (is_lab_vreg_ok_irq_available(labibb)) {
labibb->lab_vreg.lab_vreg_ok_irq =
of_irq_get_byname(child, "lab-vreg-ok");
if (labibb->lab_vreg.lab_vreg_ok_irq < 0) {
@@ -2684,7 +2834,7 @@ static int qpnp_labibb_check_ttw_supported(struct qpnp_labibb *labibb)
u8 val;
switch (labibb->pmic_rev_id->pmic_subtype) {
- case PMI8996:
+ case PMI8996_SUBTYPE:
rc = qpnp_labibb_read(labibb, &val,
labibb->ibb_base + REG_IBB_REVISION4, 1);
if (rc) {
@@ -2702,7 +2852,7 @@ static int qpnp_labibb_check_ttw_supported(struct qpnp_labibb *labibb)
/* FORCE_LAB_ON in TTW is not required for PMI8996 */
labibb->ttw_force_lab_on = false;
break;
- case PMI8950:
+ case PMI8950_SUBTYPE:
/* TTW supported for all revisions */
break;
default:
@@ -2721,7 +2871,7 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
unsigned int base;
struct device_node *child, *revid_dev_node;
const char *mode_name;
- u8 type;
+ u8 type, revision;
int rc = 0;
labibb = devm_kzalloc(&pdev->dev,
@@ -2739,6 +2889,9 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
labibb->dev = &(pdev->dev);
labibb->pdev = pdev;
+ mutex_init(&(labibb->lab_vreg.lab_mutex));
+ mutex_init(&(labibb->ibb_vreg.ibb_mutex));
+
revid_dev_node = of_parse_phandle(labibb->dev->of_node,
"qcom,pmic-revid", 0);
if (!revid_dev_node) {
@@ -2753,19 +2906,19 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
}
rc = of_property_read_string(labibb->dev->of_node,
- "qpnp,qpnp-labibb-mode", &mode_name);
+ "qcom,qpnp-labibb-mode", &mode_name);
if (!rc) {
if (strcmp("lcd", mode_name) == 0) {
labibb->mode = QPNP_LABIBB_LCD_MODE;
} else if (strcmp("amoled", mode_name) == 0) {
labibb->mode = QPNP_LABIBB_AMOLED_MODE;
} else {
- pr_err("Invalid device property in qpnp,qpnp-labibb-mode: %s\n",
+ pr_err("Invalid device property in qcom,qpnp-labibb-mode: %s\n",
mode_name);
return -EINVAL;
}
} else {
- pr_err("qpnp_labibb: qpnp,qpnp-labibb-mode is missing.\n");
+ pr_err("qpnp_labibb: qcom,qpnp-labibb-mode is missing.\n");
return rc;
}
@@ -2783,15 +2936,17 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
labibb->dev->of_node, "qcom,labibb-ttw-force-lab-on");
labibb->swire_control = of_property_read_bool(labibb->dev->of_node,
- "qpnp,swire-control");
+ "qcom,swire-control");
if (labibb->swire_control && labibb->mode != QPNP_LABIBB_AMOLED_MODE) {
pr_err("Invalid mode for SWIRE control\n");
return -EINVAL;
}
+
if (labibb->swire_control) {
labibb->skip_2nd_swire_cmd =
of_property_read_bool(labibb->dev->of_node,
"qcom,skip-2nd-swire-cmd");
+
rc = of_property_read_u32(labibb->dev->of_node,
"qcom,swire-2nd-cmd-delay",
&labibb->swire_2nd_cmd_delay);
@@ -2811,6 +2966,7 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
pr_err("no child nodes\n");
return -ENXIO;
}
+
for_each_available_child_of_node(pdev->dev.of_node, child) {
rc = of_property_read_u32(child, "reg", &base);
if (rc < 0) {
@@ -2820,6 +2976,13 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
return rc;
}
+ rc = qpnp_labibb_read(labibb, &revision, base + REG_REVISION_2,
+ 1);
+ if (rc) {
+ pr_err("Reading REVISION_2 failed rc=%d\n", rc);
+ goto fail_registration;
+ }
+
rc = qpnp_labibb_read(labibb, &type,
base + REG_PERPH_TYPE, 1);
if (rc) {
@@ -2830,6 +2993,7 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
switch (type) {
case QPNP_LAB_TYPE:
labibb->lab_base = base;
+ labibb->lab_dig_major = revision;
rc = qpnp_lab_register_irq(child, labibb);
if (rc) {
pr_err("Failed to register LAB IRQ rc=%d\n",
@@ -2843,6 +3007,7 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
case QPNP_IBB_TYPE:
labibb->ibb_base = base;
+ labibb->ibb_dig_major = revision;
rc = register_qpnp_ibb_regulator(labibb, child);
if (rc)
goto fail_registration;
diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c
index 72fc3c32db49..b6d831b84e1d 100644
--- a/drivers/regulator/s2mps11.c
+++ b/drivers/regulator/s2mps11.c
@@ -305,7 +305,7 @@ static struct regulator_ops s2mps11_buck_ops = {
.enable_mask = S2MPS11_ENABLE_MASK \
}
-#define regulator_desc_s2mps11_buck6_10(num, min, step) { \
+#define regulator_desc_s2mps11_buck67810(num, min, step) { \
.name = "BUCK"#num, \
.id = S2MPS11_BUCK##num, \
.ops = &s2mps11_buck_ops, \
@@ -321,6 +321,22 @@ static struct regulator_ops s2mps11_buck_ops = {
.enable_mask = S2MPS11_ENABLE_MASK \
}
+#define regulator_desc_s2mps11_buck9 { \
+ .name = "BUCK9", \
+ .id = S2MPS11_BUCK9, \
+ .ops = &s2mps11_buck_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = MIN_3000_MV, \
+ .uV_step = STEP_25_MV, \
+ .n_voltages = S2MPS11_BUCK9_N_VOLTAGES, \
+ .ramp_delay = S2MPS11_RAMP_DELAY, \
+ .vsel_reg = S2MPS11_REG_B9CTRL2, \
+ .vsel_mask = S2MPS11_BUCK9_VSEL_MASK, \
+ .enable_reg = S2MPS11_REG_B9CTRL1, \
+ .enable_mask = S2MPS11_ENABLE_MASK \
+}
+
static const struct regulator_desc s2mps11_regulators[] = {
regulator_desc_s2mps11_ldo(1, STEP_25_MV),
regulator_desc_s2mps11_ldo(2, STEP_50_MV),
@@ -365,11 +381,11 @@ static const struct regulator_desc s2mps11_regulators[] = {
regulator_desc_s2mps11_buck1_4(3),
regulator_desc_s2mps11_buck1_4(4),
regulator_desc_s2mps11_buck5,
- regulator_desc_s2mps11_buck6_10(6, MIN_600_MV, STEP_6_25_MV),
- regulator_desc_s2mps11_buck6_10(7, MIN_600_MV, STEP_6_25_MV),
- regulator_desc_s2mps11_buck6_10(8, MIN_600_MV, STEP_6_25_MV),
- regulator_desc_s2mps11_buck6_10(9, MIN_3000_MV, STEP_25_MV),
- regulator_desc_s2mps11_buck6_10(10, MIN_750_MV, STEP_12_5_MV),
+ regulator_desc_s2mps11_buck67810(6, MIN_600_MV, STEP_6_25_MV),
+ regulator_desc_s2mps11_buck67810(7, MIN_600_MV, STEP_6_25_MV),
+ regulator_desc_s2mps11_buck67810(8, MIN_600_MV, STEP_6_25_MV),
+ regulator_desc_s2mps11_buck9,
+ regulator_desc_s2mps11_buck67810(10, MIN_750_MV, STEP_12_5_MV),
};
static struct regulator_ops s2mps14_reg_ops;
diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c
index 58f5d3b8e981..27343e1c43ef 100644
--- a/drivers/regulator/s5m8767.c
+++ b/drivers/regulator/s5m8767.c
@@ -202,9 +202,10 @@ static int s5m8767_get_register(struct s5m8767_info *s5m8767, int reg_id,
}
}
- if (i < s5m8767->num_regulators)
- *enable_ctrl =
- s5m8767_opmode_reg[reg_id][mode] << S5M8767_ENCTRL_SHIFT;
+ if (i >= s5m8767->num_regulators)
+ return -EINVAL;
+
+ *enable_ctrl = s5m8767_opmode_reg[reg_id][mode] << S5M8767_ENCTRL_SHIFT;
return 0;
}
@@ -937,8 +938,12 @@ static int s5m8767_pmic_probe(struct platform_device *pdev)
else
regulators[id].vsel_mask = 0xff;
- s5m8767_get_register(s5m8767, id, &enable_reg,
+ ret = s5m8767_get_register(s5m8767, id, &enable_reg,
&enable_val);
+ if (ret) {
+ dev_err(s5m8767->dev, "error reading registers\n");
+ return ret;
+ }
regulators[id].enable_reg = enable_reg;
regulators[id].enable_mask = S5M8767_ENCTRL_MASK;
regulators[id].enable_val = enable_val;
diff --git a/drivers/rtc/rtc-ds1685.c b/drivers/rtc/rtc-ds1685.c
index 05a51ef52703..d5c1b057a739 100644
--- a/drivers/rtc/rtc-ds1685.c
+++ b/drivers/rtc/rtc-ds1685.c
@@ -187,9 +187,9 @@ ds1685_rtc_end_data_access(struct ds1685_priv *rtc)
* Only use this where you are certain another lock will not be held.
*/
static inline void
-ds1685_rtc_begin_ctrl_access(struct ds1685_priv *rtc, unsigned long flags)
+ds1685_rtc_begin_ctrl_access(struct ds1685_priv *rtc, unsigned long *flags)
{
- spin_lock_irqsave(&rtc->lock, flags);
+ spin_lock_irqsave(&rtc->lock, *flags);
ds1685_rtc_switch_to_bank1(rtc);
}
@@ -1304,7 +1304,7 @@ ds1685_rtc_sysfs_ctrl_regs_store(struct device *dev,
{
struct ds1685_priv *rtc = dev_get_drvdata(dev);
u8 reg = 0, bit = 0, tmp;
- unsigned long flags = 0;
+ unsigned long flags;
long int val = 0;
const struct ds1685_rtc_ctrl_regs *reg_info =
ds1685_rtc_sysfs_ctrl_regs_lookup(attr->attr.name);
@@ -1325,7 +1325,7 @@ ds1685_rtc_sysfs_ctrl_regs_store(struct device *dev,
bit = reg_info->bit;
/* Safe to spinlock during a write. */
- ds1685_rtc_begin_ctrl_access(rtc, flags);
+ ds1685_rtc_begin_ctrl_access(rtc, &flags);
tmp = rtc->read(rtc, reg);
rtc->write(rtc, reg, (val ? (tmp | bit) : (tmp & ~(bit))));
ds1685_rtc_end_ctrl_access(rtc, flags);
diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c
index 097325d96db5..b1b4746a0eab 100644
--- a/drivers/rtc/rtc-hym8563.c
+++ b/drivers/rtc/rtc-hym8563.c
@@ -144,7 +144,7 @@ static int hym8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
* it does not seem to carry it over a subsequent write/read.
* So we'll limit ourself to 100 years, starting at 2000 for now.
*/
- buf[6] = tm->tm_year - 100;
+ buf[6] = bin2bcd(tm->tm_year - 100);
/*
* CTL1 only contains TEST-mode bits apart from stop,
diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c
index 7184a0eda793..725dccae24e7 100644
--- a/drivers/rtc/rtc-max77686.c
+++ b/drivers/rtc/rtc-max77686.c
@@ -465,7 +465,7 @@ static int max77686_rtc_probe(struct platform_device *pdev)
info->virq = regmap_irq_get_virq(max77686->rtc_irq_data,
MAX77686_RTCIRQ_RTCA1);
- if (!info->virq) {
+ if (info->virq <= 0) {
ret = -ENXIO;
goto err_rtc;
}
diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c
index bd911bafb809..17341feadad1 100644
--- a/drivers/rtc/rtc-rx8025.c
+++ b/drivers/rtc/rtc-rx8025.c
@@ -65,7 +65,6 @@
static const struct i2c_device_id rx8025_id[] = {
{ "rx8025", 0 },
- { "rv8803", 1 },
{ }
};
MODULE_DEVICE_TABLE(i2c, rx8025_id);
diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c
index f64c282275b3..e1b86bb01062 100644
--- a/drivers/rtc/rtc-vr41xx.c
+++ b/drivers/rtc/rtc-vr41xx.c
@@ -272,12 +272,13 @@ static irqreturn_t rtclong1_interrupt(int irq, void *dev_id)
}
static const struct rtc_class_ops vr41xx_rtc_ops = {
- .release = vr41xx_rtc_release,
- .ioctl = vr41xx_rtc_ioctl,
- .read_time = vr41xx_rtc_read_time,
- .set_time = vr41xx_rtc_set_time,
- .read_alarm = vr41xx_rtc_read_alarm,
- .set_alarm = vr41xx_rtc_set_alarm,
+ .release = vr41xx_rtc_release,
+ .ioctl = vr41xx_rtc_ioctl,
+ .read_time = vr41xx_rtc_read_time,
+ .set_time = vr41xx_rtc_set_time,
+ .read_alarm = vr41xx_rtc_read_alarm,
+ .set_alarm = vr41xx_rtc_set_alarm,
+ .alarm_irq_enable = vr41xx_rtc_alarm_irq_enable,
};
static int rtc_probe(struct platform_device *pdev)
diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c
index d4c285688ce9..3ddc85e6efd6 100644
--- a/drivers/scsi/53c700.c
+++ b/drivers/scsi/53c700.c
@@ -1122,7 +1122,7 @@ process_script_interrupt(__u32 dsps, __u32 dsp, struct scsi_cmnd *SCp,
} else {
struct scsi_cmnd *SCp;
- SCp = scsi_host_find_tag(SDp->host, SCSI_NO_TAG);
+ SCp = SDp->current_cmnd;
if(unlikely(SCp == NULL)) {
sdev_printk(KERN_ERR, SDp,
"no saved request for untagged cmd\n");
@@ -1826,7 +1826,7 @@ NCR_700_queuecommand_lck(struct scsi_cmnd *SCp, void (*done)(struct scsi_cmnd *)
slot->tag, slot);
} else {
slot->tag = SCSI_NO_TAG;
- /* must populate current_cmnd for scsi_host_find_tag to work */
+ /* save current command for reselection */
SCp->device->current_cmnd = SCp;
}
/* sanity check: some of the commands generated by the mid-layer
diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h
index d044f3f273be..467773033a20 100644
--- a/drivers/scsi/aacraid/aacraid.h
+++ b/drivers/scsi/aacraid/aacraid.h
@@ -29,6 +29,7 @@ enum {
#define AAC_INT_MODE_MSI (1<<1)
#define AAC_INT_MODE_AIF (1<<2)
#define AAC_INT_MODE_SYNC (1<<3)
+#define AAC_INT_MODE_MSIX (1<<16)
#define AAC_INT_ENABLE_TYPE1_INTX 0xfffffffb
#define AAC_INT_ENABLE_TYPE1_MSIX 0xfffffffa
diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c
index 0e954e37f0b5..0d351cd3191b 100644
--- a/drivers/scsi/aacraid/comminit.c
+++ b/drivers/scsi/aacraid/comminit.c
@@ -37,6 +37,7 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
+#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/mm.h>
#include <scsi/scsi_host.h>
@@ -47,6 +48,20 @@ struct aac_common aac_config = {
.irq_mod = 1
};
+static inline int aac_is_msix_mode(struct aac_dev *dev)
+{
+ u32 status;
+
+ status = src_readl(dev, MUnit.OMR);
+ return (status & AAC_INT_MODE_MSIX);
+}
+
+static inline void aac_change_to_intx(struct aac_dev *dev)
+{
+ aac_src_access_devreg(dev, AAC_DISABLE_MSIX);
+ aac_src_access_devreg(dev, AAC_ENABLE_INTX);
+}
+
static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long commsize, unsigned long commalign)
{
unsigned char *base;
@@ -425,6 +440,15 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev)
dev->comm_interface = AAC_COMM_PRODUCER;
dev->raw_io_interface = dev->raw_io_64 = 0;
+
+ /*
+ * Enable INTX mode, if not done already Enabled
+ */
+ if (aac_is_msix_mode(dev)) {
+ aac_change_to_intx(dev);
+ dev_info(&dev->pdev->dev, "Changed firmware to INTX mode");
+ }
+
if ((!aac_adapter_sync_cmd(dev, GET_ADAPTER_PROPERTIES,
0, 0, 0, 0, 0, 0,
status+0, status+1, status+2, status+3, NULL)) &&
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
index 4cbf54928640..8c758c36fc70 100644
--- a/drivers/scsi/aacraid/commsup.c
+++ b/drivers/scsi/aacraid/commsup.c
@@ -611,10 +611,10 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
}
return -EFAULT;
}
- /* We used to udelay() here but that absorbed
- * a CPU when a timeout occured. Not very
- * useful. */
- cpu_relax();
+ /*
+ * Allow other processes / CPUS to use core
+ */
+ schedule();
}
} else if (down_interruptible(&fibptr->event_wait)) {
/* Do nothing ... satisfy
@@ -1970,6 +1970,10 @@ int aac_command_thread(void *data)
if (difference <= 0)
difference = 1;
set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ break;
+
schedule_timeout(difference);
if (kthread_should_stop())
diff --git a/drivers/scsi/device_handler/Kconfig b/drivers/scsi/device_handler/Kconfig
index e5647d59224f..0b331c9c0a8f 100644
--- a/drivers/scsi/device_handler/Kconfig
+++ b/drivers/scsi/device_handler/Kconfig
@@ -13,13 +13,13 @@ menuconfig SCSI_DH
config SCSI_DH_RDAC
tristate "LSI RDAC Device Handler"
- depends on SCSI_DH
+ depends on SCSI_DH && SCSI
help
If you have a LSI RDAC select y. Otherwise, say N.
config SCSI_DH_HP_SW
tristate "HP/COMPAQ MSA Device Handler"
- depends on SCSI_DH
+ depends on SCSI_DH && SCSI
help
If you have a HP/COMPAQ MSA device that requires START_STOP to
be sent to start it and cannot upgrade the firmware then select y.
@@ -27,13 +27,13 @@ config SCSI_DH_HP_SW
config SCSI_DH_EMC
tristate "EMC CLARiiON Device Handler"
- depends on SCSI_DH
+ depends on SCSI_DH && SCSI
help
If you have a EMC CLARiiON select y. Otherwise, say N.
config SCSI_DH_ALUA
tristate "SPC-3 ALUA Device Handler"
- depends on SCSI_DH
+ depends on SCSI_DH && SCSI
help
SCSI Device handler for generic SPC-3 Asymmetric Logical Unit
Access (ALUA).
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index db9446c612da..b0d92b84bcdc 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -2855,7 +2855,7 @@ lpfc_online(struct lpfc_hba *phba)
}
vports = lpfc_create_vport_work_array(phba);
- if (vports != NULL)
+ if (vports != NULL) {
for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
struct Scsi_Host *shost;
shost = lpfc_shost_from_vport(vports[i]);
@@ -2872,7 +2872,8 @@ lpfc_online(struct lpfc_hba *phba)
}
spin_unlock_irq(shost->host_lock);
}
- lpfc_destroy_vport_work_array(phba, vports);
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
lpfc_unblock_mgmt_io(phba);
return 0;
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index 97a1c1c33b05..00ce3e269a43 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -6282,12 +6282,13 @@ out:
}
for (i = 0; i < ioc->sge_count; i++) {
- if (kbuff_arr[i])
+ if (kbuff_arr[i]) {
dma_free_coherent(&instance->pdev->dev,
le32_to_cpu(kern_sge32[i].length),
kbuff_arr[i],
le32_to_cpu(kern_sge32[i].phys_addr));
kbuff_arr[i] = NULL;
+ }
}
megasas_return_cmd(instance, cmd);
diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c
index 5d0ec42a9317..634254a52301 100644
--- a/drivers/scsi/qla1280.c
+++ b/drivers/scsi/qla1280.c
@@ -4214,7 +4214,7 @@ static struct scsi_host_template qla1280_driver_template = {
.eh_bus_reset_handler = qla1280_eh_bus_reset,
.eh_host_reset_handler = qla1280_eh_adapter_reset,
.bios_param = qla1280_biosparam,
- .can_queue = 0xfffff,
+ .can_queue = MAX_OUTSTANDING_COMMANDS,
.this_id = -1,
.sg_tablesize = SG_ALL,
.use_clustering = ENABLE_CLUSTERING,
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index da2e068ee47d..93cbefa75b26 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -227,6 +227,7 @@ static struct {
{"PIONEER", "CD-ROM DRM-624X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
{"Promise", "VTrak E610f", NULL, BLIST_SPARSELUN | BLIST_NO_RSOC},
{"Promise", "", NULL, BLIST_SPARSELUN},
+ {"QEMU", "QEMU CD-ROM", NULL, BLIST_SKIP_VPD_PAGES},
{"QNAP", "iSCSI Storage", NULL, BLIST_MAX_1024},
{"SYNOLOGY", "iSCSI Storage", NULL, BLIST_MAX_1024},
{"QUANTUM", "XP34301", "1071", BLIST_NOTQ},
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 984ddcb4786d..1b9c049bd5c5 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -1127,7 +1127,6 @@ static int scsi_eh_action(struct scsi_cmnd *scmd, int rtn)
*/
void scsi_eh_finish_cmd(struct scsi_cmnd *scmd, struct list_head *done_q)
{
- scmd->device->host->host_failed--;
scmd->eh_eflags = 0;
list_move_tail(&scmd->eh_entry, done_q);
}
@@ -2226,6 +2225,9 @@ int scsi_error_handler(void *data)
else
scsi_unjam_host(shost);
+ /* All scmds have been handled */
+ shost->host_failed = 0;
+
/*
* Note - if the above fails completely, the action is to take
* individual devices offline and flush the queue of any
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index dd8ad2a44510..cf5b99e1f12b 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -910,9 +910,12 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
}
/*
- * If we finished all bytes in the request we are done now.
+ * special case: failed zero length commands always need to
+ * drop down into the retry code. Otherwise, if we finished
+ * all bytes in the request we are done now.
*/
- if (!scsi_end_request(req, error, good_bytes, 0))
+ if (!(blk_rq_bytes(req) == 0 && error) &&
+ !scsi_end_request(req, error, good_bytes, 0))
return;
/*
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 7808ef94ff07..87b35b78d879 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -314,6 +314,7 @@ static void scsi_target_destroy(struct scsi_target *starget)
struct Scsi_Host *shost = dev_to_shost(dev->parent);
unsigned long flags;
+ BUG_ON(starget->state == STARGET_DEL);
starget->state = STARGET_DEL;
transport_destroy_device(dev);
spin_lock_irqsave(shost->host_lock, flags);
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index d2a0ae4971ed..d47624000edf 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -1193,18 +1193,18 @@ static void __scsi_remove_target(struct scsi_target *starget)
void scsi_remove_target(struct device *dev)
{
struct Scsi_Host *shost = dev_to_shost(dev->parent);
- struct scsi_target *starget, *last_target = NULL;
+ struct scsi_target *starget;
unsigned long flags;
restart:
spin_lock_irqsave(shost->host_lock, flags);
list_for_each_entry(starget, &shost->__targets, siblings) {
if (starget->state == STARGET_DEL ||
- starget == last_target)
+ starget->state == STARGET_REMOVE)
continue;
if (starget->dev.parent == dev || &starget->dev == dev) {
kref_get(&starget->reap_ref);
- last_target = starget;
+ starget->state = STARGET_REMOVE;
spin_unlock_irqrestore(shost->host_lock, flags);
__scsi_remove_target(starget);
scsi_target_reap(starget);
diff --git a/drivers/sensors/sensors_ssc.c b/drivers/sensors/sensors_ssc.c
index 4707f74404f2..0910ef34e777 100644
--- a/drivers/sensors/sensors_ssc.c
+++ b/drivers/sensors/sensors_ssc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -69,6 +69,8 @@ static void slpi_loader_do(struct platform_device *pdev)
{
struct slpi_loader_private *priv = NULL;
+ int ret;
+ const char *firmware_name = NULL;
if (!pdev) {
dev_err(&pdev->dev, "%s: Platform device null\n", __func__);
@@ -81,6 +83,13 @@ static void slpi_loader_do(struct platform_device *pdev)
goto fail;
}
+ ret = of_property_read_string(pdev->dev.of_node,
+ "qcom,firmware-name", &firmware_name);
+ if (ret < 0) {
+ pr_err("can't get fw name.\n");
+ goto fail;
+ }
+
priv = platform_get_drvdata(pdev);
if (!priv) {
dev_err(&pdev->dev,
@@ -88,7 +97,7 @@ static void slpi_loader_do(struct platform_device *pdev)
goto fail;
}
- priv->pil_h = subsystem_get("slpi");
+ priv->pil_h = subsystem_get_with_fwname("slpi", firmware_name);
if (IS_ERR(priv->pil_h)) {
dev_err(&pdev->dev, "%s: pil get failed,\n",
__func__);
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 8c43effadc70..d3f967319d21 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -768,6 +768,15 @@ config MSM_SERVICE_NOTIFIER
like audio, the identifier for which is provided by the service
locator.
+config MSM_QBT1000
+ bool "QBT1000 Ultrasonic Fingerprint Sensor"
+ help
+ This driver provides services for configuring the fingerprint
+ sensor hardware and for communicating with the trusted app which
+ uses it. It enables clocks and provides commands for loading
+ trusted apps, unloading them and marshalling buffers to the
+ trusted fingerprint app.
+
config MSM_RPM_RBCPR_STATS_V2_LOG
tristate "MSM Resource Power Manager RPBCPR Stat Driver"
depends on DEBUG_FS
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 0105e03b082d..f56c6bf1539a 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -93,6 +93,7 @@ obj-$(CONFIG_MSM_KERNEL_PROTECT) += kernel_protect.o
obj-$(CONFIG_MSM_RTB) += msm_rtb-hotplug.o
obj-$(CONFIG_QCOM_REMOTEQDSS) += remoteqdss.o
obj-$(CONFIG_MSM_SERVICE_LOCATOR) += service-locator.o
+obj-$(CONFIG_MSM_QBT1000) += qbt1000.o
obj-$(CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG) += rpm_rbcpr_stats_v2.o
obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o rpm_master_stat.o system_stats.o
obj-$(CONFIG_MSM_RPM_LOG) += rpm_log.o
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index 9cfca014c8ad..382245eb90b6 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -3948,7 +3948,6 @@ int glink_core_register_transport(struct glink_transport_if *if_ptr,
xprt_ptr->edge, xprt_ptr->name);
if (IS_ERR_OR_NULL(xprt_ptr->tx_task)) {
GLINK_ERR("%s: unable to run thread\n", __func__);
- glink_core_deinit_xprt_qos_cfg(xprt_ptr);
kfree(xprt_ptr);
return -ENOMEM;
}
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index bd7fc82afda8..feeed645fc47 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -131,6 +131,9 @@ module_param(qmi_timeout, ulong, 0600);
* Registers: WCSS_HM_A_PMM_PMM
* Base Address: 0x18880000
*/
+#define WCSS_HM_A_PMM_ROOT_CLK_ENABLE 0x80010
+#define PMM_TCXO_CLK_ENABLE BIT(13)
+
#define PMM_COMMON_IDLEREQ_CSR_OFFSET 0x80120
#define PMM_COMMON_IDLEREQ_CSR_SW_WNOC_IDLEREQ_SET BIT(16)
#define PMM_COMMON_IDLEREQ_CSR_WNOC_IDLEACK BIT(26)
@@ -1318,7 +1321,7 @@ static int icnss_hw_reset_rf_reset_cmd(struct icnss_priv *priv)
if (ret) {
icnss_pr_err("RESET: RF reset command failed, state: 0x%lx\n",
priv->state);
- icnss_hw_wsi_cmd_error_recovery(priv);
+ return ret;
}
icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
@@ -1332,8 +1335,28 @@ static int icnss_hw_reset_rf_reset_cmd(struct icnss_priv *priv)
static int icnss_hw_reset_switch_to_cxo(struct icnss_priv *priv)
{
+ u32 rdata;
+
icnss_pr_dbg("RESET: Switch to CXO, state: 0x%lx\n", priv->state);
+ rdata = icnss_hw_read_reg(priv->mem_base_va,
+ WCSS_HM_A_PMM_ROOT_CLK_ENABLE);
+
+ icnss_pr_dbg("RESET: PMM_TCXO_CLK_ENABLE : 0x%05lx\n",
+ rdata & PMM_TCXO_CLK_ENABLE);
+
+ if ((rdata & PMM_TCXO_CLK_ENABLE) == 0) {
+ icnss_pr_dbg("RESET: Set PMM_TCXO_CLK_ENABLE to 1\n");
+
+ icnss_hw_write_reg_field(priv->mem_base_va,
+ WCSS_HM_A_PMM_ROOT_CLK_ENABLE,
+ PMM_TCXO_CLK_ENABLE, 1);
+ icnss_hw_poll_reg_field(priv->mem_base_va,
+ WCSS_HM_A_PMM_ROOT_CLK_ENABLE,
+ PMM_TCXO_CLK_ENABLE, 1, 10,
+ ICNSS_HW_REG_RETRY);
+ }
+
icnss_hw_write_reg_field(priv->mem_base_va,
WCSS_CLK_CTL_NOC_CFG_RCGR_OFFSET,
WCSS_CLK_CTL_NOC_CFG_RCGR_SRC_SEL, 0);
@@ -1389,7 +1412,7 @@ static int icnss_hw_reset_xo_disable_cmd(struct icnss_priv *priv)
if (ret) {
icnss_pr_err("RESET: XO disable command failed, state: 0x%lx\n",
priv->state);
- icnss_hw_wsi_cmd_error_recovery(priv);
+ return ret;
}
icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
@@ -1406,6 +1429,7 @@ static int icnss_hw_reset(struct icnss_priv *priv)
u32 rdata;
u32 rdata1;
int i;
+ int ret = 0;
if (test_bit(HW_ONLY_TOP_LEVEL_RESET, &quirks))
goto top_level_reset;
@@ -1457,11 +1481,35 @@ static int icnss_hw_reset(struct icnss_priv *priv)
icnss_hw_reset_wlan_rfactrl_power_down(priv);
- icnss_hw_reset_rf_reset_cmd(priv);
+ ret = icnss_hw_reset_rf_reset_cmd(priv);
+ if (ret)
+ goto top_level_reset;
icnss_hw_reset_switch_to_cxo(priv);
- icnss_hw_reset_xo_disable_cmd(priv);
+ for (i = 0; i < ICNSS_HW_REG_RETRY; i++) {
+ rdata = icnss_hw_read_reg(priv->mem_base_va, SR_PMM_SR_MSB);
+ usleep_range(5, 10);
+ rdata1 = icnss_hw_read_reg(priv->mem_base_va, SR_PMM_SR_MSB);
+
+ icnss_pr_dbg("RESET: SR_PMM_SR_MSB: 0x%08x/0x%08x, XO: 0x%05lx/0x%05lx, AHB: 0x%05lx/0x%05lx\n",
+ rdata, rdata1,
+ rdata & SR_PMM_SR_MSB_XO_CLOCK_MASK,
+ rdata1 & SR_PMM_SR_MSB_XO_CLOCK_MASK,
+ rdata & SR_PMM_SR_MSB_AHB_CLOCK_MASK,
+ rdata1 & SR_PMM_SR_MSB_AHB_CLOCK_MASK);
+
+ if ((rdata & SR_PMM_SR_MSB_AHB_CLOCK_MASK) !=
+ (rdata1 & SR_PMM_SR_MSB_AHB_CLOCK_MASK) &&
+ (rdata & SR_PMM_SR_MSB_XO_CLOCK_MASK) !=
+ (rdata1 & SR_PMM_SR_MSB_XO_CLOCK_MASK))
+ break;
+ usleep_range(5, 10);
+ }
+
+ ret = icnss_hw_reset_xo_disable_cmd(priv);
+ if (ret)
+ goto top_level_reset;
icnss_hw_write_reg_field(priv->mpm_config_va, MPM_WCSSAON_CONFIG_OFFSET,
MPM_WCSSAON_CONFIG_FORCE_ACTIVE, 0);
@@ -2607,10 +2655,10 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
icnss_call_driver_remove(priv);
out:
- icnss_remove_msa_permissions(priv);
-
ret = icnss_hw_power_off(priv);
+ icnss_remove_msa_permissions(priv);
+
kfree(data);
return ret;
@@ -4231,6 +4279,11 @@ static int icnss_get_vbatt_info(struct icnss_priv *priv)
struct qpnp_vadc_chip *vadc_dev = NULL;
int ret = 0;
+ if (test_bit(VBATT_DISABLE, &quirks)) {
+ icnss_pr_dbg("VBATT feature is disabled\n");
+ return ret;
+ }
+
adc_tm_dev = qpnp_get_adc_tm(&priv->pdev->dev, "icnss");
if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) {
icnss_pr_err("adc_tm_dev probe defer\n");
diff --git a/drivers/soc/qcom/memshare/msm_memshare.c b/drivers/soc/qcom/memshare/msm_memshare.c
index 00cc5e12709b..dcca82fc25c6 100644
--- a/drivers/soc/qcom/memshare/msm_memshare.c
+++ b/drivers/soc/qcom/memshare/msm_memshare.c
@@ -39,6 +39,7 @@ static DECLARE_DELAYED_WORK(work_recv_msg, mem_share_svc_recv_msg);
static struct workqueue_struct *mem_share_svc_workqueue;
static uint64_t bootup_request;
static void *memshare_ramdump_dev[MAX_CLIENTS];
+static struct device *memshare_dev[MAX_CLIENTS];
/* Memshare Driver Structure */
struct memshare_driver {
@@ -145,9 +146,14 @@ static int mem_share_configure_ramdump(void)
}
snprintf(client_name, 18, "memshare_%s", clnt);
-
- memshare_ramdump_dev[num_clients] = create_ramdump_device(client_name,
- NULL);
+ if (memshare_dev[num_clients]) {
+ memshare_ramdump_dev[num_clients] =
+ create_ramdump_device(client_name,
+ memshare_dev[num_clients]);
+ } else {
+ pr_err("memshare:%s: invalid memshare device\n", __func__);
+ return -ENODEV;
+ }
if (IS_ERR_OR_NULL(memshare_ramdump_dev[num_clients])) {
pr_err("memshare: %s: Unable to create memshare ramdump device.\n",
__func__);
@@ -957,6 +963,8 @@ static int memshare_child_probe(struct platform_device *pdev)
* memshare clients
*/
+ memshare_dev[num_clients] = &pdev->dev;
+
if (!memblock[num_clients].file_created) {
rc = mem_share_configure_ramdump();
if (rc)
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c b/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c
index e4c8f1f446df..a876484859eb 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
*
* This program is Mree software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -133,7 +133,7 @@ static ssize_t bus_floor_vote_store_api(struct device *dev,
return 0;
}
- if (sscanf(buf, "%s %llu", name, &vote_khz) != 2) {
+ if (sscanf(buf, "%9s %llu", name, &vote_khz) != 2) {
pr_err("%s:return error", __func__);
return -EINVAL;
}
diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c
index 31a5ae89174e..bf6b11194111 100644
--- a/drivers/soc/qcom/pil-q6v5-mss.c
+++ b/drivers/soc/qcom/pil-q6v5-mss.c
@@ -277,7 +277,8 @@ static int pil_mss_loadable_init(struct modem_data *drv,
q6->restart_reg_sec = true;
}
- q6->restart_reg = devm_ioremap_resource(&pdev->dev, res);
+ q6->restart_reg = devm_ioremap(&pdev->dev,
+ res->start, resource_size(res));
if (!q6->restart_reg)
return -ENOMEM;
diff --git a/drivers/soc/qcom/pil-q6v5.c b/drivers/soc/qcom/pil-q6v5.c
index f8895e8a7b3d..5752aecb82bd 100644
--- a/drivers/soc/qcom/pil-q6v5.c
+++ b/drivers/soc/qcom/pil-q6v5.c
@@ -388,7 +388,7 @@ static int __pil_q6v55_reset(struct pil_desc *pil)
mb();
udelay(1);
- if (drv->qdsp6v62_1_2) {
+ if (drv->qdsp6v62_1_2 || drv->qdsp6v62_1_5) {
for (i = BHS_CHECK_MAX_LOOPS; i > 0; i--) {
if (readl_relaxed(drv->reg_base + QDSP6V62SS_BHS_STATUS)
& QDSP6v55_BHS_EN_REST_ACK)
@@ -488,7 +488,8 @@ static int __pil_q6v55_reset(struct pil_desc *pil)
*/
udelay(1);
}
- } else if (drv->qdsp6v61_1_1 || drv->qdsp6v62_1_2) {
+ } else if (drv->qdsp6v61_1_1 || drv->qdsp6v62_1_2 ||
+ drv->qdsp6v62_1_5) {
/* Deassert QDSP6 compiler memory clamp */
val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
val &= ~QDSP6v55_CLAMP_QMC_MEM;
@@ -501,7 +502,13 @@ static int __pil_q6v55_reset(struct pil_desc *pil)
/* Turn on L1, L2, ETB and JU memories 1 at a time */
val = readl_relaxed(drv->reg_base +
QDSP6V6SS_MEM_PWR_CTL);
- for (i = 28; i >= 0; i--) {
+
+ if (drv->qdsp6v62_1_5)
+ i = 29;
+ else
+ i = 28;
+
+ for ( ; i >= 0; i--) {
val |= BIT(i);
writel_relaxed(val, drv->reg_base +
QDSP6V6SS_MEM_PWR_CTL);
@@ -663,6 +670,9 @@ struct q6v5_data *pil_q6v5_init(struct platform_device *pdev)
drv->qdsp6v62_1_2 = of_property_read_bool(pdev->dev.of_node,
"qcom,qdsp6v62-1-2");
+ drv->qdsp6v62_1_5 = of_property_read_bool(pdev->dev.of_node,
+ "qcom,qdsp6v62-1-5");
+
drv->non_elf_image = of_property_read_bool(pdev->dev.of_node,
"qcom,mba-image-is-not-elf");
diff --git a/drivers/soc/qcom/pil-q6v5.h b/drivers/soc/qcom/pil-q6v5.h
index 6a59b06f7b6c..9e8b8511e69b 100644
--- a/drivers/soc/qcom/pil-q6v5.h
+++ b/drivers/soc/qcom/pil-q6v5.h
@@ -62,6 +62,7 @@ struct q6v5_data {
bool qdsp6v56_1_10;
bool qdsp6v61_1_1;
bool qdsp6v62_1_2;
+ bool qdsp6v62_1_5;
bool non_elf_image;
bool restart_reg_sec;
bool override_acc;
diff --git a/drivers/soc/qcom/qbt1000.c b/drivers/soc/qcom/qbt1000.c
new file mode 100644
index 000000000000..dd543daca1b4
--- /dev/null
+++ b/drivers/soc/qcom/qbt1000.c
@@ -0,0 +1,1263 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "qbt1000:%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/cdev.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/mutex.h>
+#include <linux/atomic.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/input.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <uapi/linux/qbt1000.h>
+#include <soc/qcom/scm.h>
+#include "qseecom_kernel.h"
+
+#define QBT1000_DEV "qbt1000"
+#define QBT1000_IN_DEV_NAME "qbt1000_key_input"
+#define QBT1000_IN_DEV_VERSION 0x0100
+#define MAX_FW_EVENTS 128
+#define FP_APP_CMD_RX_IPC 132
+#define FW_MAX_IPC_MSG_DATA_SIZE 0x500
+#define IPC_MSG_ID_CBGE_REQUIRED 29
+
+/*
+ * shared buffer size - init with max value,
+ * user space will provide new value upon tz app load
+ */
+static uint32_t g_app_buf_size = SZ_256K;
+static char const *const FP_APP_NAME = "fingerpr";
+
+struct finger_detect_gpio {
+ int gpio;
+ int active_low;
+ int irq;
+ struct work_struct work;
+ unsigned int key_code;
+ int power_key_enabled;
+ int last_gpio_state;
+ int event_reported;
+};
+
+struct fw_event_desc {
+ enum qbt1000_fw_event ev;
+};
+
+struct fw_ipc_info {
+ int gpio;
+ int irq;
+};
+
+struct qbt1000_drvdata {
+ struct class *qbt1000_class;
+ struct cdev qbt1000_cdev;
+ struct device *dev;
+ char *qbt1000_node;
+ struct clk **clocks;
+ unsigned clock_count;
+ uint8_t clock_state;
+ unsigned root_clk_idx;
+ unsigned frequency;
+ atomic_t available;
+ struct mutex mutex;
+ struct mutex fw_events_mutex;
+ struct input_dev *in_dev;
+ struct fw_ipc_info fw_ipc;
+ struct finger_detect_gpio fd_gpio;
+ DECLARE_KFIFO(fw_events, struct fw_event_desc, MAX_FW_EVENTS);
+ wait_queue_head_t read_wait_queue;
+ struct qseecom_handle *fp_app_handle;
+};
+
+/*
+* struct fw_ipc_cmd -
+* used to store IPC commands to/from firmware
+* @status - indicates whether sending/getting the IPC message was successful
+* @msg_type - the type of IPC message
+* @msg_len - the length of the message data
+* @resp_needed - whether a response is needed for this message
+* @msg_data - any extra data associated with the message
+*/
+struct fw_ipc_cmd {
+ uint32_t status;
+ uint32_t msg_type;
+ uint32_t msg_len;
+ uint32_t resp_needed;
+ uint8_t msg_data[FW_MAX_IPC_MSG_DATA_SIZE];
+};
+
+/*
+* struct ipc_msg_type_to_fw_event -
+* entry in mapping between an IPC message type to a firmware event
+* @msg_type - IPC message type, as reported by firmware
+* @fw_event - corresponding firmware event code to report to driver client
+*/
+struct ipc_msg_type_to_fw_event {
+ uint32_t msg_type;
+ enum qbt1000_fw_event fw_event;
+};
+
+/* mapping between firmware IPC message types to HLOS firmware events */
+struct ipc_msg_type_to_fw_event g_msg_to_event[] = {
+ {IPC_MSG_ID_CBGE_REQUIRED, FW_EVENT_CBGE_REQUIRED}
+};
+
+/**
+ * get_cmd_rsp_buffers() - Function sets cmd & rsp buffer pointers and
+ * aligns buffer lengths
+ * @hdl: index of qseecom_handle
+ * @cmd: req buffer - set to qseecom_handle.sbuf
+ * @cmd_len: ptr to req buffer len
+ * @rsp: rsp buffer - set to qseecom_handle.sbuf + offset
+ * @rsp_len: ptr to rsp buffer len
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int get_cmd_rsp_buffers(struct qseecom_handle *hdl,
+ void **cmd,
+ uint32_t *cmd_len,
+ void **rsp,
+ uint32_t *rsp_len)
+{
+ /* 64 bytes alignment for QSEECOM */
+ *cmd_len = ALIGN(*cmd_len, 64);
+ *rsp_len = ALIGN(*rsp_len, 64);
+
+ if ((*rsp_len + *cmd_len) > g_app_buf_size) {
+ pr_err("buffer too small to hold cmd=%d and rsp=%d\n",
+ *cmd_len, *rsp_len);
+ return -ENOMEM;
+ }
+
+ *cmd = hdl->sbuf;
+ *rsp = hdl->sbuf + *cmd_len;
+ return 0;
+}
+
+/**
+ * clocks_on() - Function votes for SPI and AHB clocks to be on and sets
+ * the clk rate to predetermined value for SPI.
+ * @drvdata: ptr to driver data
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int clocks_on(struct qbt1000_drvdata *drvdata)
+{
+ int rc = 0;
+ int index;
+
+ if (!drvdata->clock_state) {
+ for (index = 0; index < drvdata->clock_count; index++) {
+ pr_debug("set clock rate at idx:%d, freq: %u\n",
+ index, drvdata->frequency);
+ if (index == drvdata->root_clk_idx) {
+ rc = clk_set_rate(drvdata->clocks[index],
+ drvdata->frequency);
+ if (rc) {
+ pr_err("failure set clock rate at idx:%d\n",
+ index);
+ goto unprepare;
+ }
+ }
+
+ rc = clk_prepare_enable(drvdata->clocks[index]);
+ if (rc) {
+ pr_err("failure to prepare clk at idx:%d\n",
+ index);
+ goto unprepare;
+ }
+
+
+ }
+ drvdata->clock_state = 1;
+ }
+ goto end;
+
+unprepare:
+ for (--index; index >= 0; index--)
+ clk_disable_unprepare(drvdata->clocks[index]);
+
+end:
+ return rc;
+}
+
+/**
+ * clocks_off() - Function votes for SPI and AHB clocks to be off
+ * @drvdata: ptr to driver data
+ *
+ * Return: None
+ */
+static void clocks_off(struct qbt1000_drvdata *drvdata)
+{
+ int index;
+
+ if (drvdata->clock_state) {
+ for (index = 0; index < drvdata->clock_count; index++)
+ clk_disable_unprepare(drvdata->clocks[index]);
+ drvdata->clock_state = 0;
+ }
+}
+
+/**
+ * send_tz_cmd() - Function sends a command to TZ
+ *
+ * @drvdata: pointer to driver data
+ * @app_handle: handle to tz app
+ * @is_user_space: 1 if the cmd buffer is in user space, 0
+ * otherwise
+ * @cmd: command buffer to send
+ * @cmd_len: length of the command buffer
+ * @rsp: output, will be set to location of response buffer
+ * @rsp_len: max size of response
+ *
+ * Return: 0 on success.
+ */
+static int send_tz_cmd(struct qbt1000_drvdata *drvdata,
+ struct qseecom_handle *app_handle,
+ int is_user_space,
+ void *cmd, uint32_t cmd_len,
+ void **rsp, uint32_t rsp_len)
+{
+ int rc = 0;
+ void *aligned_cmd;
+ void *aligned_rsp;
+ uint32_t aligned_cmd_len;
+ uint32_t aligned_rsp_len;
+
+ /* init command and response buffers and align lengths */
+ aligned_cmd_len = cmd_len;
+ aligned_rsp_len = rsp_len;
+
+ rc = get_cmd_rsp_buffers(app_handle,
+ (void **)&aligned_cmd,
+ &aligned_cmd_len,
+ (void **)&aligned_rsp,
+ &aligned_rsp_len);
+
+ if (rc != 0)
+ goto end;
+
+ if (aligned_cmd - cmd + cmd_len > g_app_buf_size) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ if (is_user_space) {
+ rc = copy_from_user(aligned_cmd, (void __user *)cmd,
+ cmd_len);
+ if (rc != 0) {
+ pr_err("failure to copy user space buf %d\n", rc);
+ rc = -EFAULT;
+ goto end;
+ }
+ } else
+ memcpy(aligned_cmd, cmd, cmd_len);
+
+
+ /* vote for clocks before sending TZ command */
+ rc = clocks_on(drvdata);
+ if (rc != 0) {
+ pr_err("failure to enable clocks %d\n", rc);
+ goto end;
+ }
+
+ /* send cmd to TZ */
+ rc = qseecom_send_command(app_handle,
+ aligned_cmd,
+ aligned_cmd_len,
+ aligned_rsp,
+ aligned_rsp_len);
+
+ /* un-vote for clocks */
+ clocks_off(drvdata);
+
+ if (rc != 0) {
+ pr_err("failure to send tz cmd %d\n", rc);
+ goto end;
+ }
+
+ *rsp = aligned_rsp;
+
+end:
+ return rc;
+}
+
+/**
+ * qbt1000_open() - Function called when user space opens device.
+ * Successful if driver not currently open.
+ * @inode: ptr to inode object
+ * @file: ptr to file object
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int qbt1000_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+
+ struct qbt1000_drvdata *drvdata = container_of(inode->i_cdev,
+ struct qbt1000_drvdata,
+ qbt1000_cdev);
+ file->private_data = drvdata;
+
+ pr_debug("qbt1000_open begin\n");
+ /* disallowing concurrent opens */
+ if (!atomic_dec_and_test(&drvdata->available)) {
+ atomic_inc(&drvdata->available);
+ rc = -EBUSY;
+ }
+
+ pr_debug("qbt1000_open end : %d\n", rc);
+ return rc;
+}
+
+/**
+ * qbt1000_release() - Function called when user space closes device.
+
+ * @inode: ptr to inode object
+ * @file: ptr to file object
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int qbt1000_release(struct inode *inode, struct file *file)
+{
+ struct qbt1000_drvdata *drvdata = file->private_data;
+
+ atomic_inc(&drvdata->available);
+ return 0;
+}
+
+/**
+ * qbt1000_ioctl() - Function called when user space calls ioctl.
+ * @file: struct file - not used
+ * @cmd: cmd identifier:QBT1000_LOAD_APP,QBT1000_UNLOAD_APP,
+ * QBT1000_SEND_TZCMD
+ * @arg: ptr to relevant structe: either qbt1000_app or
+ * qbt1000_send_tz_cmd depending on which cmd is passed
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg)
+{
+ int rc = 0;
+ void __user *priv_arg = (void __user *)arg;
+ struct qbt1000_drvdata *drvdata;
+
+ drvdata = file->private_data;
+
+ mutex_lock(&drvdata->mutex);
+
+ pr_debug("qbt1000_ioctl %d\n", cmd);
+
+ switch (cmd) {
+ case QBT1000_LOAD_APP:
+ {
+ struct qbt1000_app app;
+ struct qseecom_handle *app_handle;
+
+ if (copy_from_user(&app, priv_arg,
+ sizeof(app)) != 0) {
+ rc = -EFAULT;
+ pr_err("failed copy from user space-LOAD\n");
+ goto end;
+ }
+
+ if (!app.app_handle) {
+ dev_err(drvdata->dev, "%s: LOAD app_handle is null\n",
+ __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ pr_debug("app %s load before\n", app.name);
+
+ /* start the TZ app */
+ rc = qseecom_start_app(&app_handle, app.name, app.size);
+ if (rc == 0) {
+ g_app_buf_size = app.size;
+ rc = qseecom_set_bandwidth(app_handle,
+ app.high_band_width == 1 ? true : false);
+ if (rc != 0) {
+ /* log error, allow to continue */
+ pr_err("App %s failed to set bw\n", app.name);
+ }
+ } else {
+ pr_err("app %s failed to load\n", app.name);
+ goto end;
+ }
+
+ /* copy the app handle to user */
+ rc = copy_to_user((void __user *)app.app_handle, &app_handle,
+ sizeof(*app.app_handle));
+
+ if (rc != 0) {
+ dev_err(drvdata->dev,
+ "%s: Failed copy 2us LOAD rc:%d\n",
+ __func__, rc);
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ pr_debug("app %s load after\n", app.name);
+
+ if (!strcmp(app.name, FP_APP_NAME))
+ drvdata->fp_app_handle = app_handle;
+
+ break;
+ }
+ case QBT1000_UNLOAD_APP:
+ {
+ struct qbt1000_app app;
+ struct qseecom_handle *app_handle;
+
+ if (copy_from_user(&app, priv_arg,
+ sizeof(app)) != 0) {
+ rc = -ENOMEM;
+ pr_err("failed copy from user space-UNLOAD\n");
+ goto end;
+ }
+
+ if (!app.app_handle) {
+ dev_err(drvdata->dev, "%s: UNLOAD app_handle is null\n",
+ __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ rc = copy_from_user(&app_handle, app.app_handle,
+ sizeof(app_handle));
+
+ if (rc != 0) {
+ dev_err(drvdata->dev,
+ "%s: Failed copy from user space-UNLOAD handle rc:%d\n",
+ __func__, rc);
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ /* if the app hasn't been loaded already, return err */
+ if (!app_handle) {
+ pr_err("app not loaded\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (drvdata->fp_app_handle == app_handle)
+ drvdata->fp_app_handle = 0;
+
+ /* set bw & shutdown the TZ app */
+ qseecom_set_bandwidth(app_handle,
+ app.high_band_width == 1 ? true : false);
+ rc = qseecom_shutdown_app(&app_handle);
+ if (rc != 0) {
+ pr_err("app failed to shutdown\n");
+ goto end;
+ }
+
+ /* copy the app handle (should be null) to user */
+ rc = copy_to_user((void __user *)app.app_handle, &app_handle,
+ sizeof(*app.app_handle));
+
+ if (rc != 0) {
+ dev_err(drvdata->dev,
+ "%s: Failed copy 2us UNLOAD rc:%d\n",
+ __func__, rc);
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ break;
+ }
+ case QBT1000_SEND_TZCMD:
+ {
+ struct qbt1000_send_tz_cmd tzcmd;
+ void *rsp_buf;
+
+ if (copy_from_user(&tzcmd, priv_arg,
+ sizeof(tzcmd))
+ != 0) {
+ rc = -EFAULT;
+ pr_err("failed copy from user space %d\n", rc);
+ goto end;
+ }
+
+ if (tzcmd.req_buf_len > g_app_buf_size ||
+ tzcmd.rsp_buf_len > g_app_buf_size) {
+ rc = -ENOMEM;
+ pr_err("invalid cmd buf len, req=%d, rsp=%d\n",
+ tzcmd.req_buf_len, tzcmd.rsp_buf_len);
+ goto end;
+ }
+
+ /* if the app hasn't been loaded already, return err */
+ if (!tzcmd.app_handle) {
+ pr_err("app not loaded\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ rc = send_tz_cmd(drvdata,
+ tzcmd.app_handle, 1,
+ tzcmd.req_buf, tzcmd.req_buf_len,
+ &rsp_buf, tzcmd.rsp_buf_len);
+
+ if (rc < 0) {
+ pr_err("failure sending command to tz\n");
+ goto end;
+ }
+
+ /* copy rsp buf back to user space buffer */
+ rc = copy_to_user((void __user *)tzcmd.rsp_buf,
+ rsp_buf, tzcmd.rsp_buf_len);
+ if (rc != 0) {
+ pr_err("failed copy 2us rc:%d bytes %d:\n",
+ rc, tzcmd.rsp_buf_len);
+ rc = -EFAULT;
+ goto end;
+ }
+
+ break;
+ }
+ case QBT1000_SET_FINGER_DETECT_KEY:
+ {
+ struct qbt1000_set_finger_detect_key set_fd_key;
+
+ if (copy_from_user(&set_fd_key, priv_arg,
+ sizeof(set_fd_key))
+ != 0) {
+ rc = -EFAULT;
+ pr_err("failed copy from user space %d\n", rc);
+ goto end;
+ }
+
+ drvdata->fd_gpio.key_code = set_fd_key.key_code;
+
+ break;
+ }
+ case QBT1000_CONFIGURE_POWER_KEY:
+ {
+ struct qbt1000_configure_power_key power_key;
+
+ if (copy_from_user(&power_key, priv_arg,
+ sizeof(power_key))
+ != 0) {
+ rc = -EFAULT;
+ pr_err("failed copy from user space %d\n", rc);
+ goto end;
+ }
+
+ drvdata->fd_gpio.power_key_enabled = power_key.enable;
+
+ break;
+ }
+ default:
+ pr_err("invalid cmd %d\n", cmd);
+ rc = -ENOIOCTLCMD;
+ goto end;
+ }
+
+end:
+ mutex_unlock(&drvdata->mutex);
+ return rc;
+}
+
+static int get_events_fifo_len_locked(struct qbt1000_drvdata *drvdata)
+{
+ int len;
+
+ mutex_lock(&drvdata->fw_events_mutex);
+ len = kfifo_len(&drvdata->fw_events);
+ mutex_unlock(&drvdata->fw_events_mutex);
+
+ return len;
+}
+
+static ssize_t qbt1000_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct fw_event_desc fw_event;
+ struct qbt1000_drvdata *drvdata = filp->private_data;
+
+ if (cnt < sizeof(fw_event.ev))
+ return -EINVAL;
+
+ mutex_lock(&drvdata->fw_events_mutex);
+
+ while (kfifo_len(&drvdata->fw_events) == 0) {
+ mutex_unlock(&drvdata->fw_events_mutex);
+
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ pr_debug("fw_events fifo: empty, waiting\n");
+
+ if (wait_event_interruptible(drvdata->read_wait_queue,
+ (get_events_fifo_len_locked(drvdata) > 0)))
+ return -ERESTARTSYS;
+
+ mutex_lock(&drvdata->fw_events_mutex);
+ }
+
+ if (!kfifo_get(&drvdata->fw_events, &fw_event)) {
+ pr_debug("fw_events fifo: unexpectedly empty\n");
+
+ mutex_unlock(&drvdata->fw_events_mutex);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&drvdata->fw_events_mutex);
+
+ pr_debug("fw_event: %d\n", (int)fw_event.ev);
+ return copy_to_user(ubuf, &fw_event.ev, sizeof(fw_event.ev));
+}
+
+static unsigned int qbt1000_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct qbt1000_drvdata *drvdata = filp->private_data;
+ unsigned int mask = 0;
+
+ poll_wait(filp, &drvdata->read_wait_queue, wait);
+
+ if (kfifo_len(&drvdata->fw_events) > 0)
+ mask |= (POLLIN | POLLRDNORM);
+
+ return mask;
+}
+
+static const struct file_operations qbt1000_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = qbt1000_ioctl,
+ .open = qbt1000_open,
+ .release = qbt1000_release,
+ .read = qbt1000_read,
+ .poll = qbt1000_poll
+};
+
+static int qbt1000_dev_register(struct qbt1000_drvdata *drvdata)
+{
+ dev_t dev_no;
+ int ret = 0;
+ size_t node_size;
+ char *node_name = QBT1000_DEV;
+ struct device *dev = drvdata->dev;
+ struct device *device;
+
+ node_size = strlen(node_name) + 1;
+
+ drvdata->qbt1000_node = devm_kzalloc(dev, node_size, GFP_KERNEL);
+ if (!drvdata->qbt1000_node) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ strlcpy(drvdata->qbt1000_node, node_name, node_size);
+
+ ret = alloc_chrdev_region(&dev_no, 0, 1, drvdata->qbt1000_node);
+ if (ret) {
+ pr_err("alloc_chrdev_region failed %d\n", ret);
+ goto err_alloc;
+ }
+
+ cdev_init(&drvdata->qbt1000_cdev, &qbt1000_fops);
+
+ drvdata->qbt1000_cdev.owner = THIS_MODULE;
+ ret = cdev_add(&drvdata->qbt1000_cdev, dev_no, 1);
+ if (ret) {
+ pr_err("cdev_add failed %d\n", ret);
+ goto err_cdev_add;
+ }
+
+ drvdata->qbt1000_class = class_create(THIS_MODULE,
+ drvdata->qbt1000_node);
+ if (IS_ERR(drvdata->qbt1000_class)) {
+ ret = PTR_ERR(drvdata->qbt1000_class);
+ pr_err("class_create failed %d\n", ret);
+ goto err_class_create;
+ }
+
+ device = device_create(drvdata->qbt1000_class, NULL,
+ drvdata->qbt1000_cdev.dev, drvdata,
+ drvdata->qbt1000_node);
+ if (IS_ERR(device)) {
+ ret = PTR_ERR(device);
+ pr_err("device_create failed %d\n", ret);
+ goto err_dev_create;
+ }
+
+ return 0;
+err_dev_create:
+ class_destroy(drvdata->qbt1000_class);
+err_class_create:
+ cdev_del(&drvdata->qbt1000_cdev);
+err_cdev_add:
+ unregister_chrdev_region(drvdata->qbt1000_cdev.dev, 1);
+err_alloc:
+ return ret;
+}
+
+/**
+ * qbt1000_create_input_device() - Function allocates an input
+ * device, configures it for key events and registers it
+ *
+ * @drvdata: ptr to driver data
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int qbt1000_create_input_device(struct qbt1000_drvdata *drvdata)
+{
+ int rc = 0;
+
+ drvdata->in_dev = input_allocate_device();
+ if (drvdata->in_dev == NULL) {
+ dev_err(drvdata->dev, "%s: input_allocate_device() failed\n",
+ __func__);
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ drvdata->in_dev->name = QBT1000_IN_DEV_NAME;
+ drvdata->in_dev->phys = NULL;
+ drvdata->in_dev->id.bustype = BUS_HOST;
+ drvdata->in_dev->id.vendor = 0x0001;
+ drvdata->in_dev->id.product = 0x0001;
+ drvdata->in_dev->id.version = QBT1000_IN_DEV_VERSION;
+
+ drvdata->in_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ drvdata->in_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ drvdata->in_dev->keybit[BIT_WORD(KEY_HOMEPAGE)] |=
+ BIT_MASK(KEY_HOMEPAGE);
+ drvdata->in_dev->keybit[BIT_WORD(KEY_CAMERA)] |=
+ BIT_MASK(KEY_CAMERA);
+ drvdata->in_dev->keybit[BIT_WORD(KEY_POWER)] |=
+ BIT_MASK(KEY_POWER);
+
+ input_set_abs_params(drvdata->in_dev, ABS_X,
+ 0,
+ 1000,
+ 0, 0);
+ input_set_abs_params(drvdata->in_dev, ABS_Y,
+ 0,
+ 1000,
+ 0, 0);
+
+ rc = input_register_device(drvdata->in_dev);
+ if (rc) {
+ dev_err(drvdata->dev, "%s: input_reg_dev() failed %d\n",
+ __func__, rc);
+ goto end;
+ }
+
+end:
+ if (rc)
+ input_free_device(drvdata->in_dev);
+ return rc;
+}
+
+static void purge_finger_events(struct qbt1000_drvdata *drvdata)
+{
+ int i, fifo_len;
+ struct fw_event_desc fw_event;
+
+ fifo_len = kfifo_len(&drvdata->fw_events);
+
+ for (i = 0; i < fifo_len; i++) {
+ if (!kfifo_get(&drvdata->fw_events, &fw_event))
+ pr_err("fw events fifo: could not remove oldest item\n");
+ else if (fw_event.ev != FW_EVENT_FINGER_DOWN
+ && fw_event.ev != FW_EVENT_FINGER_UP)
+ kfifo_put(&drvdata->fw_events, fw_event);
+ }
+}
+
+static void qbt1000_gpio_report_event(struct qbt1000_drvdata *drvdata)
+{
+ int state;
+ struct fw_event_desc fw_event;
+
+ state = (__gpio_get_value(drvdata->fd_gpio.gpio) ? 1 : 0)
+ ^ drvdata->fd_gpio.active_low;
+
+ if (drvdata->fd_gpio.event_reported
+ && state == drvdata->fd_gpio.last_gpio_state)
+ return;
+
+ pr_debug("gpio %d: report state %d\n", drvdata->fd_gpio.gpio, state);
+
+ drvdata->fd_gpio.event_reported = 1;
+ drvdata->fd_gpio.last_gpio_state = state;
+
+ if (drvdata->fd_gpio.key_code) {
+ input_event(drvdata->in_dev, EV_KEY,
+ drvdata->fd_gpio.key_code, !!state);
+ input_sync(drvdata->in_dev);
+ }
+
+ if (state && drvdata->fd_gpio.power_key_enabled) {
+ input_event(drvdata->in_dev, EV_KEY, KEY_POWER, 1);
+ input_sync(drvdata->in_dev);
+ input_event(drvdata->in_dev, EV_KEY, KEY_POWER, 0);
+ input_sync(drvdata->in_dev);
+ }
+
+ fw_event.ev = (state ? FW_EVENT_FINGER_DOWN : FW_EVENT_FINGER_UP);
+
+ mutex_lock(&drvdata->fw_events_mutex);
+
+ if (kfifo_is_full(&drvdata->fw_events)) {
+ struct fw_event_desc dummy_fw_event;
+
+ pr_warn("fw events fifo: full, dropping oldest item\n");
+ if (!kfifo_get(&drvdata->fw_events, &dummy_fw_event))
+ pr_err("fw events fifo: could not remove oldest item\n");
+ }
+
+ purge_finger_events(drvdata);
+
+ if (!kfifo_put(&drvdata->fw_events, fw_event))
+ pr_err("fw events fifo: error adding item\n");
+
+ mutex_unlock(&drvdata->fw_events_mutex);
+ wake_up_interruptible(&drvdata->read_wait_queue);
+}
+
+static void qbt1000_gpio_work_func(struct work_struct *work)
+{
+ struct qbt1000_drvdata *drvdata =
+ container_of(work, struct qbt1000_drvdata, fd_gpio.work);
+
+ qbt1000_gpio_report_event(drvdata);
+
+ pm_relax(drvdata->dev);
+}
+
+static irqreturn_t qbt1000_gpio_isr(int irq, void *dev_id)
+{
+ struct qbt1000_drvdata *drvdata = dev_id;
+
+ if (irq != drvdata->fd_gpio.irq) {
+ pr_warn("invalid irq %d (expected %d)\n",
+ irq, drvdata->fd_gpio.irq);
+ return IRQ_HANDLED;
+ }
+
+ pm_stay_awake(drvdata->dev);
+ schedule_work(&drvdata->fd_gpio.work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * qbt1000_ipc_irq_handler() - function processes IPC
+ * interrupts on its own thread
+ * @irq: the interrupt that occurred
+ * @dev_id: pointer to the qbt1000_drvdata
+ *
+ * Return: IRQ_HANDLED when complete
+ */
+static irqreturn_t qbt1000_ipc_irq_handler(int irq, void *dev_id)
+{
+ struct fw_ipc_cmd *rx_cmd;
+ int i;
+ uint32_t rxipc = FP_APP_CMD_RX_IPC;
+ struct qbt1000_drvdata *drvdata = (struct qbt1000_drvdata *)dev_id;
+ int rc = 0;
+
+ pm_stay_awake(drvdata->dev);
+
+ mutex_lock(&drvdata->mutex);
+
+ if (irq != drvdata->fw_ipc.irq) {
+ pr_warn("invalid irq %d (expected %d)\n",
+ irq, drvdata->fw_ipc.irq);
+ goto end;
+ }
+
+ pr_debug("firmware interrupt received (irq %d)\n", irq);
+
+ if (!drvdata->fp_app_handle)
+ goto end;
+
+ /*
+ * send the TZ command to fetch the message from firmware
+ * TZ will process the message if it can
+ */
+ rc = send_tz_cmd(drvdata, drvdata->fp_app_handle, 0,
+ &rxipc, sizeof(rxipc),
+ (void *)&rx_cmd, sizeof(*rx_cmd));
+
+ if (rc < 0) {
+ pr_err("failure sending tz cmd %d\n", rxipc);
+ goto end;
+ }
+
+ if (rx_cmd->status != 0) {
+ pr_err("tz command failed to complete\n");
+ goto end;
+ }
+
+ /*
+ * given the IPC message type, search for a corresponding event for the
+ * driver client. If found, add to the events FIFO
+ */
+ for (i = 0; i < ARRAY_SIZE(g_msg_to_event); i++) {
+ if (g_msg_to_event[i].msg_type == rx_cmd->msg_type) {
+ enum qbt1000_fw_event ev = g_msg_to_event[i].fw_event;
+ struct fw_event_desc fw_ev_desc;
+
+ mutex_lock(&drvdata->fw_events_mutex);
+ pr_debug("fw events: add %d\n", (int) ev);
+ fw_ev_desc.ev = ev;
+
+ if (!kfifo_put(&drvdata->fw_events, fw_ev_desc))
+ pr_err("fw events: fifo full, drop event %d\n",
+ (int) ev);
+
+ mutex_unlock(&drvdata->fw_events_mutex);
+ wake_up_interruptible(&drvdata->read_wait_queue);
+ break;
+ }
+ }
+
+end:
+ mutex_unlock(&drvdata->mutex);
+ pm_relax(drvdata->dev);
+ return IRQ_HANDLED;
+}
+
+static int setup_fd_gpio_irq(struct platform_device *pdev,
+ struct qbt1000_drvdata *drvdata)
+{
+ int rc = 0;
+ int irq;
+ const char *desc = "qbt_finger_detect";
+
+ rc = devm_gpio_request_one(&pdev->dev, drvdata->fd_gpio.gpio,
+ GPIOF_IN, desc);
+
+ if (rc < 0) {
+ pr_err("failed to request gpio %d, error %d\n",
+ drvdata->fd_gpio.gpio, rc);
+ goto end;
+ }
+
+ irq = gpio_to_irq(drvdata->fd_gpio.gpio);
+ if (irq < 0) {
+ rc = irq;
+ pr_err("unable to get irq number for gpio %d, error %d\n",
+ drvdata->fd_gpio.gpio, rc);
+ goto end;
+ }
+
+ drvdata->fd_gpio.irq = irq;
+ INIT_WORK(&drvdata->fd_gpio.work, qbt1000_gpio_work_func);
+
+ rc = devm_request_any_context_irq(&pdev->dev, drvdata->fd_gpio.irq,
+ qbt1000_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ desc, drvdata);
+
+ if (rc < 0) {
+ pr_err("unable to claim irq %d; error %d\n",
+ drvdata->fd_gpio.irq, rc);
+ goto end;
+ }
+
+end:
+ return rc;
+}
+
+static int setup_ipc_irq(struct platform_device *pdev,
+ struct qbt1000_drvdata *drvdata)
+{
+ int rc = 0;
+ const char *desc = "qbt_ipc";
+
+ drvdata->fw_ipc.irq = gpio_to_irq(drvdata->fw_ipc.gpio);
+ if (drvdata->fw_ipc.irq < 0) {
+ rc = drvdata->fw_ipc.irq;
+ pr_err("no irq for gpio %d, error=%d\n",
+ drvdata->fw_ipc.gpio, rc);
+ goto end;
+ }
+
+ rc = devm_gpio_request_one(&pdev->dev, drvdata->fw_ipc.gpio,
+ GPIOF_IN, desc);
+
+ if (rc < 0) {
+ pr_err("failed to request gpio %d, error %d\n",
+ drvdata->fw_ipc.gpio, rc);
+ goto end;
+ }
+
+ rc = devm_request_threaded_irq(&pdev->dev,
+ drvdata->fw_ipc.irq,
+ NULL,
+ qbt1000_ipc_irq_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ desc,
+ drvdata);
+
+ if (rc < 0) {
+ pr_err("failed to register for ipc irq %d, rc = %d\n",
+ drvdata->fw_ipc.irq, rc);
+ goto end;
+ }
+
+end:
+ return rc;
+}
+
+/**
+ * qbt1000_read_device_tree() - Function reads device tree
+ * properties into driver data
+ * @pdev: ptr to platform device object
+ * @drvdata: ptr to driver data
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int qbt1000_read_device_tree(struct platform_device *pdev,
+ struct qbt1000_drvdata *drvdata)
+{
+ int rc = 0;
+ uint8_t clkcnt = 0;
+ int index = 0;
+ uint32_t rate;
+ const char *clock_name;
+ int gpio;
+ enum of_gpio_flags flags;
+
+ /* obtain number of clocks from hw config */
+ clkcnt = of_property_count_strings(pdev->dev.of_node, "clock-names");
+ if (IS_ERR_VALUE(drvdata->clock_count)) {
+ pr_err("failed to get clock names\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ /* sanity check for max clock count */
+ if (clkcnt > 16) {
+ pr_err("invalid clock count %d\n", clkcnt);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ /* alloc mem for clock array - auto free if probe fails */
+ drvdata->clock_count = clkcnt;
+ pr_debug("clock count %d\n", clkcnt);
+ drvdata->clocks = devm_kzalloc(&pdev->dev,
+ sizeof(struct clk *) * drvdata->clock_count, GFP_KERNEL);
+ if (!drvdata->clocks) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ /* load clock names */
+ for (index = 0; index < drvdata->clock_count; index++) {
+ of_property_read_string_index(pdev->dev.of_node,
+ "clock-names",
+ index, &clock_name);
+ pr_debug("getting clock %s\n", clock_name);
+ drvdata->clocks[index] = devm_clk_get(&pdev->dev, clock_name);
+ if (IS_ERR(drvdata->clocks[index])) {
+ rc = PTR_ERR(drvdata->clocks[index]);
+ if (rc != -EPROBE_DEFER)
+ pr_err("failed to get %s\n", clock_name);
+ goto end;
+ }
+
+ if (!strcmp(clock_name, "iface_clk")) {
+ pr_debug("root index %d\n", index);
+ drvdata->root_clk_idx = index;
+ }
+ }
+
+ /* read clock frequency */
+ if (of_property_read_u32(pdev->dev.of_node,
+ "clock-frequency", &rate) == 0) {
+ pr_debug("clk frequency %d\n", rate);
+ drvdata->frequency = rate;
+ }
+
+ /* read IPC gpio */
+ drvdata->fw_ipc.gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,ipc-gpio", 0);
+ if (drvdata->fw_ipc.gpio < 0) {
+ rc = drvdata->fw_ipc.gpio;
+ pr_err("ipc gpio not found, error=%d\n", rc);
+ goto end;
+ }
+
+ /* read finger detect GPIO configuration */
+ gpio = of_get_named_gpio_flags(pdev->dev.of_node,
+ "qcom,finger-detect-gpio", 0, &flags);
+ if (gpio < 0) {
+ pr_err("failed to get gpio flags\n");
+ rc = gpio;
+ goto end;
+ }
+
+ drvdata->fd_gpio.gpio = gpio;
+ drvdata->fd_gpio.active_low = flags & OF_GPIO_ACTIVE_LOW;
+
+end:
+ return rc;
+}
+
+/**
+ * qbt1000_probe() - Function loads hardware config from device tree
+ * @pdev: ptr to platform device object
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int qbt1000_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct qbt1000_drvdata *drvdata;
+ int rc = 0;
+
+ pr_debug("qbt1000_probe begin\n");
+ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ drvdata->dev = &pdev->dev;
+ platform_set_drvdata(pdev, drvdata);
+
+ rc = qbt1000_read_device_tree(pdev, drvdata);
+ if (rc < 0)
+ goto end;
+
+ atomic_set(&drvdata->available, 1);
+
+ mutex_init(&drvdata->mutex);
+ mutex_init(&drvdata->fw_events_mutex);
+
+ rc = qbt1000_dev_register(drvdata);
+ if (rc < 0)
+ goto end;
+
+ INIT_KFIFO(drvdata->fw_events);
+ init_waitqueue_head(&drvdata->read_wait_queue);
+
+ rc = qbt1000_create_input_device(drvdata);
+ if (rc < 0)
+ goto end;
+
+ rc = setup_fd_gpio_irq(pdev, drvdata);
+ if (rc < 0)
+ goto end;
+
+ rc = setup_ipc_irq(pdev, drvdata);
+ if (rc < 0)
+ goto end;
+
+ rc = device_init_wakeup(&pdev->dev, 1);
+ if (rc < 0)
+ goto end;
+
+end:
+ pr_debug("qbt1000_probe end : %d\n", rc);
+ return rc;
+}
+
+static int qbt1000_remove(struct platform_device *pdev)
+{
+ struct qbt1000_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ input_unregister_device(drvdata->in_dev);
+
+ clocks_off(drvdata);
+ mutex_destroy(&drvdata->mutex);
+ mutex_destroy(&drvdata->fw_events_mutex);
+
+ device_destroy(drvdata->qbt1000_class, drvdata->qbt1000_cdev.dev);
+ class_destroy(drvdata->qbt1000_class);
+ cdev_del(&drvdata->qbt1000_cdev);
+ unregister_chrdev_region(drvdata->qbt1000_cdev.dev, 1);
+
+ device_init_wakeup(&pdev->dev, 0);
+
+ return 0;
+}
+
+static int qbt1000_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int rc = 0;
+ struct qbt1000_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ /*
+ * Returning an error code if driver currently making a TZ call.
+ * Note: The purpose of this driver is to ensure that the clocks are on
+ * while making a TZ call. Hence the clock check to determine if the
+ * driver will allow suspend to occur.
+ */
+ if (!mutex_trylock(&drvdata->mutex))
+ return -EBUSY;
+
+ if (drvdata->clock_state)
+ rc = -EBUSY;
+ else {
+ enable_irq_wake(drvdata->fd_gpio.irq);
+ enable_irq_wake(drvdata->fw_ipc.irq);
+ }
+
+ mutex_unlock(&drvdata->mutex);
+
+ return rc;
+}
+
+static int qbt1000_resume(struct platform_device *pdev)
+{
+ struct qbt1000_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ disable_irq_wake(drvdata->fd_gpio.irq);
+ disable_irq_wake(drvdata->fw_ipc.irq);
+
+ return 0;
+}
+
+static const struct of_device_id qbt1000_match[] = {
+ { .compatible = "qcom,qbt1000" },
+ {}
+};
+
+static struct platform_driver qbt1000_plat_driver = {
+ .probe = qbt1000_probe,
+ .remove = qbt1000_remove,
+ .suspend = qbt1000_suspend,
+ .resume = qbt1000_resume,
+ .driver = {
+ .name = "qbt1000",
+ .owner = THIS_MODULE,
+ .of_match_table = qbt1000_match,
+ },
+};
+
+module_platform_driver(qbt1000_plat_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Qualcomm Technologies, Inc. QBT1000 driver");
diff --git a/drivers/soc/qcom/qdsp6v2/apr.c b/drivers/soc/qcom/qdsp6v2/apr.c
index ee9b054dcc24..128ea434dcc8 100644
--- a/drivers/soc/qcom/qdsp6v2/apr.c
+++ b/drivers/soc/qcom/qdsp6v2/apr.c
@@ -54,6 +54,28 @@ struct apr_reset_work {
struct work_struct work;
};
+static bool apr_cf_debug;
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_apr_debug;
+static ssize_t apr_debug_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ char cmd;
+
+ if (copy_from_user(&cmd, ubuf, 1))
+ return -EFAULT;
+
+ apr_cf_debug = (cmd == '1') ? true : false;
+
+ return cnt;
+}
+
+static const struct file_operations apr_debug_ops = {
+ .write = apr_debug_write,
+};
+#endif
+
#define APR_PKT_INFO(x...) \
do { \
if (apr_pkt_ctx) \
@@ -343,8 +365,13 @@ int apr_send_pkt(void *handle, uint32_t *buf)
hdr->dest_domain = svc->dest_domain;
hdr->dest_svc = svc->id;
- APR_PKT_INFO("Tx: dest_svc[%d], opcode[0x%X], size[%d]",
- hdr->dest_svc, hdr->opcode, hdr->pkt_size);
+ if (unlikely(apr_cf_debug)) {
+ APR_PKT_INFO(
+ "Tx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X]",
+ (hdr->src_domain << 8) | hdr->src_svc,
+ (hdr->dest_domain << 8) | hdr->dest_svc, hdr->opcode,
+ hdr->token);
+ }
rc = apr_tal_write(clnt->handle, buf,
(struct apr_pkt_priv *)&svc->pkt_owner,
@@ -538,8 +565,6 @@ void apr_cb_func(void *buf, int len, void *priv)
return;
}
hdr = buf;
- APR_PKT_INFO("Rx: dest_svc[%d], opcode[0x%X], size[%d]",
- hdr->dest_svc, hdr->opcode, hdr->pkt_size);
ver = hdr->hdr_field;
ver = (ver & 0x000F);
@@ -631,9 +656,28 @@ void apr_cb_func(void *buf, int len, void *priv)
data.dest_port = hdr->dest_port;
data.token = hdr->token;
data.msg_type = msg_type;
+ data.payload = NULL;
if (data.payload_size > 0)
data.payload = (char *)hdr + hdr_size;
+ if (unlikely(apr_cf_debug)) {
+ if (hdr->opcode == APR_BASIC_RSP_RESULT && data.payload) {
+ uint32_t *ptr = data.payload;
+
+ APR_PKT_INFO(
+ "Rx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X] rc[0x%X]",
+ (hdr->src_domain << 8) | hdr->src_svc,
+ (hdr->dest_domain << 8) | hdr->dest_svc,
+ hdr->opcode, hdr->token, ptr[1]);
+ } else {
+ APR_PKT_INFO(
+ "Rx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X]",
+ (hdr->src_domain << 8) | hdr->src_svc,
+ (hdr->dest_domain << 8) | hdr->dest_svc, hdr->opcode,
+ hdr->token);
+ }
+ }
+
temp_port = ((data.dest_port >> 8) * 8) + (data.dest_port & 0xFF);
pr_debug("port = %d t_port = %d\n", data.src_port, temp_port);
if (c_svc->port_cnt && c_svc->port_fn[temp_port])
@@ -910,3 +954,14 @@ static int __init apr_late_init(void)
return ret;
}
late_initcall(apr_late_init);
+
+#ifdef CONFIG_DEBUG_FS
+static int __init apr_debug_init(void)
+{
+ debugfs_apr_debug = debugfs_create_file("msm_apr_debug",
+ S_IFREG | S_IRUGO, NULL, NULL,
+ &apr_debug_ops);
+ return 0;
+}
+device_initcall(apr_debug_init);
+#endif
diff --git a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c
index e8969a5e533b..45ac48eb2241 100644
--- a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c
+++ b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c
@@ -35,6 +35,7 @@
struct apr_tx_buf {
struct list_head list;
+ struct apr_pkt_priv pkt_priv;
char buf[APR_MAX_BUF];
};
@@ -67,29 +68,28 @@ static char *svc_names[APR_DEST_MAX][APR_CLIENT_MAX] = {
static struct apr_svc_ch_dev
apr_svc_ch[APR_DL_MAX][APR_DEST_MAX][APR_CLIENT_MAX];
-static int apr_get_free_buf(int len, void **buf)
+static struct apr_tx_buf *apr_get_free_buf(int len)
{
struct apr_tx_buf *tx_buf;
unsigned long flags;
- if (!buf || len > APR_MAX_BUF) {
+ if (len > APR_MAX_BUF) {
pr_err("%s: buf too large [%d]\n", __func__, len);
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
spin_lock_irqsave(&buf_list.lock, flags);
if (list_empty(&buf_list.list)) {
spin_unlock_irqrestore(&buf_list.lock, flags);
pr_err("%s: No buf available\n", __func__);
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
}
tx_buf = list_first_entry(&buf_list.list, struct apr_tx_buf, list);
list_del(&tx_buf->list);
spin_unlock_irqrestore(&buf_list.lock, flags);
- *buf = tx_buf->buf;
- return 0;
+ return tx_buf;
}
static void apr_buf_add_tail(const void *buf)
@@ -130,16 +130,22 @@ int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data,
{
int rc = 0, retries = 0;
void *pkt_data = NULL;
+ struct apr_tx_buf *tx_buf;
+ struct apr_pkt_priv *pkt_priv_ptr = pkt_priv;
if (!apr_ch->handle || !pkt_priv)
return -EINVAL;
if (pkt_priv->pkt_owner == APR_PKT_OWNER_DRIVER) {
- rc = apr_get_free_buf(len, &pkt_data);
- if (rc)
+ tx_buf = apr_get_free_buf(len);
+ if (IS_ERR_OR_NULL(tx_buf)) {
+ rc = -EINVAL;
goto exit;
-
- memcpy(pkt_data, data, len);
+ }
+ memcpy(tx_buf->buf, data, len);
+ memcpy(&tx_buf->pkt_priv, pkt_priv, sizeof(tx_buf->pkt_priv));
+ pkt_priv_ptr = &tx_buf->pkt_priv;
+ pkt_data = tx_buf->buf;
} else {
pkt_data = data;
}
@@ -148,7 +154,7 @@ int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data,
if (rc == -EAGAIN)
udelay(50);
- rc = __apr_tal_write(apr_ch, pkt_data, pkt_priv, len);
+ rc = __apr_tal_write(apr_ch, pkt_data, pkt_priv_ptr, len);
} while (rc == -EAGAIN && retries++ < APR_MAXIMUM_NUM_OF_RETRIES);
if (rc < 0) {
@@ -180,6 +186,28 @@ void apr_tal_notify_rx(void *handle, const void *priv, const void *pkt_priv,
glink_rx_done(apr_ch->handle, ptr, true);
}
+static void apr_tal_notify_tx_abort(void *handle, const void *priv,
+ const void *pkt_priv)
+{
+ struct apr_pkt_priv *apr_pkt_priv_ptr =
+ (struct apr_pkt_priv *)pkt_priv;
+ struct apr_tx_buf *list_node;
+
+ if (!apr_pkt_priv_ptr) {
+ pr_err("%s: Invalid pkt_priv\n", __func__);
+ return;
+ }
+
+ pr_debug("%s: tx_abort received for apr_pkt_priv_ptr:%pK\n",
+ __func__, apr_pkt_priv_ptr);
+
+ if (apr_pkt_priv_ptr->pkt_owner == APR_PKT_OWNER_DRIVER) {
+ list_node = container_of(apr_pkt_priv_ptr,
+ struct apr_tx_buf, pkt_priv);
+ apr_buf_add_tail(list_node->buf);
+ }
+}
+
void apr_tal_notify_tx_done(void *handle, const void *priv,
const void *pkt_priv, const void *ptr)
{
@@ -315,6 +343,7 @@ struct apr_svc_ch_dev *apr_tal_open(uint32_t clnt, uint32_t dest, uint32_t dl,
open_cfg.notify_state = apr_tal_notify_state;
open_cfg.notify_rx_intent_req = apr_tal_notify_rx_intent_req;
open_cfg.notify_remote_rx_intent = apr_tal_notify_remote_rx_intent;
+ open_cfg.notify_tx_abort = apr_tal_notify_tx_abort;
open_cfg.priv = apr_ch;
open_cfg.transport = "smem";
diff --git a/drivers/soc/qcom/scm.c b/drivers/soc/qcom/scm.c
index 714c848ec9c0..b4713ac1b68b 100644
--- a/drivers/soc/qcom/scm.c
+++ b/drivers/soc/qcom/scm.c
@@ -56,9 +56,16 @@ DEFINE_MUTEX(scm_lmh_lock);
#define SMC_ATOMIC_MASK 0x80000000
#define IS_CALL_AVAIL_CMD 1
-#define SCM_BUF_LEN(__cmd_size, __resp_size) \
- (sizeof(struct scm_command) + sizeof(struct scm_response) + \
- __cmd_size + __resp_size)
+#define SCM_BUF_LEN(__cmd_size, __resp_size) ({ \
+ size_t x = __cmd_size + __resp_size; \
+ size_t y = sizeof(struct scm_command) + sizeof(struct scm_response); \
+ size_t result; \
+ if (x < __cmd_size || (x + y) < x) \
+ result = 0; \
+ else \
+ result = x + y; \
+ result; \
+ })
/**
* struct scm_command - one SCM command buffer
* @len: total available memory for command and response
@@ -356,8 +363,7 @@ int scm_call_noalloc(u32 svc_id, u32 cmd_id, const void *cmd_buf,
int ret;
size_t len = SCM_BUF_LEN(cmd_len, resp_len);
- if (cmd_len > scm_buf_len || resp_len > scm_buf_len ||
- len > scm_buf_len)
+ if (len == 0)
return -EINVAL;
if (!IS_ALIGNED((unsigned long)scm_buf, PAGE_SIZE))
@@ -780,7 +786,7 @@ int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
int ret;
size_t len = SCM_BUF_LEN(cmd_len, resp_len);
- if (cmd_len > len || resp_len > len)
+ if (len == 0 || PAGE_ALIGN(len) < len)
return -EINVAL;
cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);
diff --git a/drivers/soc/qcom/secure_buffer.c b/drivers/soc/qcom/secure_buffer.c
index ac004a597ea2..95d50fe01ee1 100644
--- a/drivers/soc/qcom/secure_buffer.c
+++ b/drivers/soc/qcom/secure_buffer.c
@@ -52,7 +52,7 @@ struct mem_prot_info {
struct dest_vm_and_perm_info {
u32 vm;
u32 perm;
- u32 *ctx;
+ u64 ctx;
u32 ctx_size;
};
@@ -209,7 +209,7 @@ populate_dest_info(int *dest_vmids, int nelements, int *dest_perms,
for (i = 0; i < nelements; i++) {
dest_info[i].vm = dest_vmids[i];
dest_info[i].perm = dest_perms[i];
- dest_info[i].ctx = NULL;
+ dest_info[i].ctx = 0x0;
dest_info[i].ctx_size = 0;
}
diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c
index 24018c544b06..2b708732760f 100644
--- a/drivers/soc/qcom/service-locator.c
+++ b/drivers/soc/qcom/service-locator.c
@@ -149,7 +149,7 @@ static void service_locator_recv_msg(struct work_struct *work)
do {
pr_debug("Notified about a Receive event\n");
ret = qmi_recv_msg(service_locator.clnt_handle);
- if (ret != -ENOMSG)
+ if (ret < 0)
pr_err("Error receiving message rc:%d. Retrying...\n",
ret);
} while (ret == 0);
diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c
index 8cba88742cb8..a244bc168136 100644
--- a/drivers/soc/qcom/service-notifier.c
+++ b/drivers/soc/qcom/service-notifier.c
@@ -162,7 +162,7 @@ static void root_service_clnt_recv_msg(struct work_struct *work)
data->instance_id);
} while ((ret = qmi_recv_msg(data->clnt_handle)) == 0);
- pr_info("Notified about a Receive event (instance-id: %d)\n",
+ pr_debug("Notified about a Receive event (instance-id: %d)\n",
data->instance_id);
}
@@ -227,7 +227,8 @@ static void root_service_service_ind_cb(struct qmi_handle *handle,
struct qmi_client_info *data = (struct qmi_client_info *)ind_cb_priv;
struct service_notif_info *service_notif;
struct msg_desc ind_desc;
- struct qmi_servreg_notif_state_updated_ind_msg_v01 ind_msg;
+ struct qmi_servreg_notif_state_updated_ind_msg_v01 ind_msg = {
+ QMI_STATE_MIN_VAL, "", 0xFFFF };
int rc;
ind_desc.msg_id = SERVREG_NOTIF_STATE_UPDATED_IND_MSG;
diff --git a/drivers/soc/qcom/sysmon.c b/drivers/soc/qcom/sysmon.c
index 8a12341a6f91..f8d0f10b1173 100644
--- a/drivers/soc/qcom/sysmon.c
+++ b/drivers/soc/qcom/sysmon.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-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
@@ -206,7 +206,7 @@ int sysmon_send_shutdown_no_qmi(struct subsys_desc *dest_desc)
return -ENODEV;
mutex_lock(&ss->lock);
- ret = sysmon_send_msg(ss, tx_buf, ARRAY_SIZE(tx_buf));
+ ret = sysmon_send_msg(ss, tx_buf, strlen(tx_buf));
if (ret) {
pr_err("Message sending failed %d\n", ret);
goto out;
@@ -257,7 +257,7 @@ int sysmon_get_reason_no_qmi(struct subsys_desc *dest_desc,
return -ENODEV;
mutex_lock(&ss->lock);
- ret = sysmon_send_msg(ss, tx_buf, ARRAY_SIZE(tx_buf));
+ ret = sysmon_send_msg(ss, tx_buf, strlen(tx_buf));
if (ret) {
pr_err("Message sending failed %d\n", ret);
goto out;
diff --git a/drivers/soc/qcom/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c
index 97d922fa5724..6bc815862541 100644
--- a/drivers/soc/qcom/wcd-dsp-glink.c
+++ b/drivers/soc/qcom/wcd-dsp-glink.c
@@ -161,8 +161,8 @@ static void wdsp_glink_notify_rx(void *handle, const void *priv,
wpriv->rsp_cnt = ++rsp_cnt;
mutex_unlock(&wpriv->rsp_mutex);
- complete(&wpriv->rsp_complete);
glink_rx_done(handle, ptr, true);
+ complete(&wpriv->rsp_complete);
}
/*
diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c
index 534c58937a56..4a65c5bda146 100644
--- a/drivers/soc/rockchip/pm_domains.c
+++ b/drivers/soc/rockchip/pm_domains.c
@@ -419,6 +419,7 @@ static int rockchip_pm_domain_probe(struct platform_device *pdev)
if (error) {
dev_err(dev, "failed to handle node %s: %d\n",
node->name, error);
+ of_node_put(node);
goto err_out;
}
}
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index b25dc71b0ea9..73c8ea0b1360 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -111,7 +111,7 @@ static const struct lpss_config lpss_platforms[] = {
.reg_general = -1,
.reg_ssp = 0x20,
.reg_cs_ctrl = 0x24,
- .reg_capabilities = 0xfc,
+ .reg_capabilities = -1,
.rx_threshold = 1,
.tx_threshold_lo = 32,
.tx_threshold_hi = 56,
diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index 79a8bc4f6cec..035767c02072 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -265,7 +265,10 @@ static inline u32 rx_max(struct rockchip_spi *rs)
static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
{
u32 ser;
- struct rockchip_spi *rs = spi_master_get_devdata(spi->master);
+ struct spi_master *master = spi->master;
+ struct rockchip_spi *rs = spi_master_get_devdata(master);
+
+ pm_runtime_get_sync(rs->dev);
ser = readl_relaxed(rs->regs + ROCKCHIP_SPI_SER) & SER_MASK;
@@ -290,6 +293,8 @@ static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
ser &= ~(1 << spi->chip_select);
writel_relaxed(ser, rs->regs + ROCKCHIP_SPI_SER);
+
+ pm_runtime_put_sync(rs->dev);
}
static int rockchip_spi_prepare_message(struct spi_master *master,
diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
index 64318fcfacf2..5044c6198332 100644
--- a/drivers/spi/spi-ti-qspi.c
+++ b/drivers/spi/spi-ti-qspi.c
@@ -94,6 +94,7 @@ struct ti_qspi {
#define QSPI_FLEN(n) ((n - 1) << 0)
#define QSPI_WLEN_MAX_BITS 128
#define QSPI_WLEN_MAX_BYTES 16
+#define QSPI_WLEN_MASK QSPI_WLEN(QSPI_WLEN_MAX_BITS)
/* STATUS REGISTER */
#define BUSY 0x01
@@ -224,16 +225,16 @@ static inline int ti_qspi_poll_wc(struct ti_qspi *qspi)
return -ETIMEDOUT;
}
-static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t)
+static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t,
+ int count)
{
- int wlen, count, xfer_len;
+ int wlen, xfer_len;
unsigned int cmd;
const u8 *txbuf;
u32 data;
txbuf = t->tx_buf;
cmd = qspi->cmd | QSPI_WR_SNGL;
- count = t->len;
wlen = t->bits_per_word >> 3; /* in bytes */
xfer_len = wlen;
@@ -293,9 +294,10 @@ static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t)
return 0;
}
-static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t)
+static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t,
+ int count)
{
- int wlen, count;
+ int wlen;
unsigned int cmd;
u8 *rxbuf;
@@ -312,7 +314,6 @@ static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t)
cmd |= QSPI_RD_SNGL;
break;
}
- count = t->len;
wlen = t->bits_per_word >> 3; /* in bytes */
while (count) {
@@ -343,12 +344,13 @@ static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t)
return 0;
}
-static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t)
+static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t,
+ int count)
{
int ret;
if (t->tx_buf) {
- ret = qspi_write_msg(qspi, t);
+ ret = qspi_write_msg(qspi, t, count);
if (ret) {
dev_dbg(qspi->dev, "Error while writing\n");
return ret;
@@ -356,7 +358,7 @@ static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t)
}
if (t->rx_buf) {
- ret = qspi_read_msg(qspi, t);
+ ret = qspi_read_msg(qspi, t, count);
if (ret) {
dev_dbg(qspi->dev, "Error while reading\n");
return ret;
@@ -373,7 +375,8 @@ static int ti_qspi_start_transfer_one(struct spi_master *master,
struct spi_device *spi = m->spi;
struct spi_transfer *t;
int status = 0, ret;
- int frame_length;
+ unsigned int frame_len_words, transfer_len_words;
+ int wlen;
/* setup device control reg */
qspi->dc = 0;
@@ -385,30 +388,38 @@ static int ti_qspi_start_transfer_one(struct spi_master *master,
if (spi->mode & SPI_CS_HIGH)
qspi->dc |= QSPI_CSPOL(spi->chip_select);
- frame_length = (m->frame_length << 3) / spi->bits_per_word;
-
- frame_length = clamp(frame_length, 0, QSPI_FRAME);
+ frame_len_words = 0;
+ list_for_each_entry(t, &m->transfers, transfer_list)
+ frame_len_words += t->len / (t->bits_per_word >> 3);
+ frame_len_words = min_t(unsigned int, frame_len_words, QSPI_FRAME);
/* setup command reg */
qspi->cmd = 0;
qspi->cmd |= QSPI_EN_CS(spi->chip_select);
- qspi->cmd |= QSPI_FLEN(frame_length);
+ qspi->cmd |= QSPI_FLEN(frame_len_words);
ti_qspi_write(qspi, qspi->dc, QSPI_SPI_DC_REG);
mutex_lock(&qspi->list_lock);
list_for_each_entry(t, &m->transfers, transfer_list) {
- qspi->cmd |= QSPI_WLEN(t->bits_per_word);
+ qspi->cmd = ((qspi->cmd & ~QSPI_WLEN_MASK) |
+ QSPI_WLEN(t->bits_per_word));
+
+ wlen = t->bits_per_word >> 3;
+ transfer_len_words = min(t->len / wlen, frame_len_words);
- ret = qspi_transfer_msg(qspi, t);
+ ret = qspi_transfer_msg(qspi, t, transfer_len_words * wlen);
if (ret) {
dev_dbg(qspi->dev, "transfer message failed\n");
mutex_unlock(&qspi->list_lock);
return -EINVAL;
}
- m->actual_length += t->len;
+ m->actual_length += transfer_len_words * wlen;
+ frame_len_words -= transfer_len_words;
+ if (frame_len_words == 0)
+ break;
}
mutex_unlock(&qspi->list_lock);
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index b02e48185355..0ae654a921f8 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spmi.h>
+#include <linux/syscore_ops.h>
/* PMIC Arbiter configuration registers */
#define PMIC_ARB_VERSION 0x0000
@@ -159,6 +160,7 @@ struct spmi_pmic_arb {
u16 last_apid;
struct apid_data apid_data[PMIC_ARB_MAX_PERIPHS];
};
+static struct spmi_pmic_arb *the_pa;
/**
* pmic_arb_ver: version dependent functionality.
@@ -525,7 +527,7 @@ static void cleanup_irq(struct spmi_pmic_arb *pa, u16 apid, int id)
irq_mask, ppid);
}
-static void periph_interrupt(struct spmi_pmic_arb *pa, u16 apid)
+static void periph_interrupt(struct spmi_pmic_arb *pa, u16 apid, bool show)
{
unsigned int irq;
u32 status;
@@ -542,22 +544,32 @@ static void periph_interrupt(struct spmi_pmic_arb *pa, u16 apid)
cleanup_irq(pa, apid, id);
continue;
}
- generic_handle_irq(irq);
+ if (show) {
+ struct irq_desc *desc;
+ const char *name = "null";
+
+ desc = irq_to_desc(irq);
+ if (desc == NULL)
+ name = "stray irq";
+ else if (desc->action && desc->action->name)
+ name = desc->action->name;
+
+ pr_warn("spmi_show_resume_irq: %d triggered [0x%01x, 0x%02x, 0x%01x] %s\n",
+ irq, sid, per, id, name);
+ } else {
+ generic_handle_irq(irq);
+ }
}
}
-static void pmic_arb_chained_irq(struct irq_desc *desc)
+static void __pmic_arb_chained_irq(struct spmi_pmic_arb *pa, bool show)
{
- struct spmi_pmic_arb *pa = irq_desc_get_handler_data(desc);
- struct irq_chip *chip = irq_desc_get_chip(desc);
void __iomem *intr = pa->intr;
int first = pa->min_apid >> 5;
int last = pa->max_apid >> 5;
u32 status, enable;
int i, id, apid;
- chained_irq_enter(chip, desc);
-
for (i = first; i <= last; ++i) {
status = readl_relaxed(intr +
pa->ver_ops->owner_acc_status(pa->ee, i));
@@ -568,10 +580,18 @@ static void pmic_arb_chained_irq(struct irq_desc *desc)
enable = readl_relaxed(intr +
pa->ver_ops->acc_enable(apid));
if (enable & SPMI_PIC_ACC_ENABLE_BIT)
- periph_interrupt(pa, apid);
+ periph_interrupt(pa, apid, show);
}
}
+}
+
+static void pmic_arb_chained_irq(struct irq_desc *desc)
+{
+ struct spmi_pmic_arb *pa = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ chained_irq_enter(chip, desc);
+ __pmic_arb_chained_irq(pa, false);
chained_irq_exit(chip, desc);
}
@@ -988,6 +1008,16 @@ static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
.xlate = qpnpint_irq_domain_dt_translate,
};
+static void spmi_pmic_arb_resume(void)
+{
+ if (spmi_show_resume_irq())
+ __pmic_arb_chained_irq(the_pa, true);
+}
+
+static struct syscore_ops spmi_pmic_arb_syscore_ops = {
+ .resume = spmi_pmic_arb_resume,
+};
+
static int spmi_pmic_arb_probe(struct platform_device *pdev)
{
struct spmi_pmic_arb *pa;
@@ -1005,6 +1035,12 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
pa->spmic = ctrl;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
+ if (!res) {
+ dev_err(&pdev->dev, "core resource not specified\n");
+ err = -EINVAL;
+ goto err_put_ctrl;
+ }
+
pa->core_size = resource_size(res);
if (pa->core_size <= 0x800) {
dev_err(&pdev->dev, "core_size is smaller than 0x800. Failing Probe\n");
@@ -1147,6 +1183,8 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
if (err)
goto err_domain_remove;
+ the_pa = pa;
+ register_syscore_ops(&spmi_pmic_arb_syscore_ops);
return 0;
err_domain_remove:
@@ -1161,8 +1199,11 @@ static int spmi_pmic_arb_remove(struct platform_device *pdev)
{
struct spmi_controller *ctrl = platform_get_drvdata(pdev);
struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl);
+
spmi_controller_remove(ctrl);
irq_set_chained_handler_and_data(pa->irq, NULL, NULL);
+ unregister_syscore_ops(&spmi_pmic_arb_syscore_ops);
+ the_pa = NULL;
irq_domain_remove(pa->domain);
spmi_controller_put(ctrl);
return 0;
diff --git a/drivers/staging/android/fiq_debugger/Kconfig b/drivers/staging/android/fiq_debugger/Kconfig
index 56f7f999377e..60fc224d4efc 100644
--- a/drivers/staging/android/fiq_debugger/Kconfig
+++ b/drivers/staging/android/fiq_debugger/Kconfig
@@ -42,6 +42,15 @@ config FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE
If enabled, this puts the fiq debugger into console mode by default.
Otherwise, the fiq debugger will start out in debug mode.
+config FIQ_DEBUGGER_UART_OVERLAY
+ bool "Install uart DT overlay"
+ depends on FIQ_DEBUGGER
+ select OF_OVERLAY
+ default n
+ help
+ If enabled, fiq debugger is calling fiq_debugger_uart_overlay()
+ that will apply overlay uart_overlay@0 to disable proper uart.
+
config FIQ_WATCHDOG
bool
select FIQ_DEBUGGER
diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c
index 7f056831dbff..b132cff14f01 100644
--- a/drivers/staging/android/fiq_debugger/fiq_debugger.c
+++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c
@@ -39,6 +39,10 @@
#include <asm/fiq_glue.h>
#endif
+#ifdef CONFIG_FIQ_DEBUGGER_UART_OVERLAY
+#include <linux/of.h>
+#endif
+
#include <linux/uaccess.h>
#include "fiq_debugger.h"
@@ -119,11 +123,13 @@ static bool initial_console_enable;
#endif
static bool fiq_kgdb_enable;
+static bool fiq_debugger_disable;
module_param_named(no_sleep, initial_no_sleep, bool, 0644);
module_param_named(debug_enable, initial_debug_enable, bool, 0644);
module_param_named(console_enable, initial_console_enable, bool, 0644);
module_param_named(kgdb_enable, fiq_kgdb_enable, bool, 0644);
+module_param_named(disable, fiq_debugger_disable, bool, 0644);
#ifdef CONFIG_FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON
static inline
@@ -1201,11 +1207,41 @@ static struct platform_driver fiq_debugger_driver = {
},
};
+#if defined(CONFIG_FIQ_DEBUGGER_UART_OVERLAY)
+int fiq_debugger_uart_overlay(void)
+{
+ struct device_node *onp = of_find_node_by_path("/uart_overlay@0");
+ int ret;
+
+ if (!onp) {
+ pr_err("serial_debugger: uart overlay not found\n");
+ return -ENODEV;
+ }
+
+ ret = of_overlay_create(onp);
+ if (ret < 0) {
+ pr_err("serial_debugger: fail to create overlay: %d\n", ret);
+ of_node_put(onp);
+ return ret;
+ }
+
+ pr_info("serial_debugger: uart overlay applied\n");
+ return 0;
+}
+#endif
+
static int __init fiq_debugger_init(void)
{
+ if (fiq_debugger_disable) {
+ pr_err("serial_debugger: disabled\n");
+ return -ENODEV;
+ }
#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE)
fiq_debugger_tty_init();
#endif
+#if defined(CONFIG_FIQ_DEBUGGER_UART_OVERLAY)
+ fiq_debugger_uart_overlay();
+#endif
return platform_driver_register(&fiq_debugger_driver);
}
diff --git a/drivers/staging/android/ion/ion_heap.c b/drivers/staging/android/ion/ion_heap.c
index b8bf80f02f4c..43d3f92cd418 100644
--- a/drivers/staging/android/ion/ion_heap.c
+++ b/drivers/staging/android/ion/ion_heap.c
@@ -2,7 +2,7 @@
* drivers/staging/android/ion/ion_heap.c
*
* Copyright (C) 2011 Google, Inc.
- * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016, 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
@@ -325,8 +325,9 @@ struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data)
switch (heap_data->type) {
case ION_HEAP_TYPE_SYSTEM_CONTIG:
- heap = ion_system_contig_heap_create(heap_data);
- break;
+ pr_err("%s: Heap type is disabled: %d\n", __func__,
+ heap_data->type);
+ return ERR_PTR(-EINVAL);
case ION_HEAP_TYPE_SYSTEM:
heap = ion_system_heap_create(heap_data);
break;
@@ -366,7 +367,8 @@ void ion_heap_destroy(struct ion_heap *heap)
switch (heap->type) {
case ION_HEAP_TYPE_SYSTEM_CONTIG:
- ion_system_contig_heap_destroy(heap);
+ pr_err("%s: Heap type is disabled: %d\n", __func__,
+ heap->type);
break;
case ION_HEAP_TYPE_SYSTEM:
ion_system_heap_destroy(heap);
diff --git a/drivers/staging/comedi/drivers/das1800.c b/drivers/staging/comedi/drivers/das1800.c
index 940781183fac..3be10963f98b 100644
--- a/drivers/staging/comedi/drivers/das1800.c
+++ b/drivers/staging/comedi/drivers/das1800.c
@@ -567,14 +567,17 @@ static int das1800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
struct comedi_isadma_desc *desc;
int i;
- outb(0x0, dev->iobase + DAS1800_STATUS); /* disable conversions */
- outb(0x0, dev->iobase + DAS1800_CONTROL_B); /* disable interrupts and dma */
- outb(0x0, dev->iobase + DAS1800_CONTROL_A); /* disable and clear fifo and stop triggering */
-
- for (i = 0; i < 2; i++) {
- desc = &dma->desc[i];
- if (desc->chan)
- comedi_isadma_disable(desc->chan);
+ /* disable and stop conversions */
+ outb(0x0, dev->iobase + DAS1800_STATUS);
+ outb(0x0, dev->iobase + DAS1800_CONTROL_B);
+ outb(0x0, dev->iobase + DAS1800_CONTROL_A);
+
+ if (dma) {
+ for (i = 0; i < 2; i++) {
+ desc = &dma->desc[i];
+ if (desc->chan)
+ comedi_isadma_disable(desc->chan);
+ }
}
return 0;
@@ -934,13 +937,14 @@ static void das1800_ai_setup_dma(struct comedi_device *dev,
{
struct das1800_private *devpriv = dev->private;
struct comedi_isadma *dma = devpriv->dma;
- struct comedi_isadma_desc *desc = &dma->desc[0];
+ struct comedi_isadma_desc *desc;
unsigned int bytes;
if ((devpriv->irq_dma_bits & DMA_ENABLED) == 0)
return;
dma->cur_dma = 0;
+ desc = &dma->desc[0];
/* determine a dma transfer size to fill buffer in 0.3 sec */
bytes = das1800_ai_transfer_size(dev, s, desc->maxsize, 300000000);
diff --git a/drivers/staging/iio/accel/sca3000_core.c b/drivers/staging/iio/accel/sca3000_core.c
index 02e930c55570..e4839ee4ca61 100644
--- a/drivers/staging/iio/accel/sca3000_core.c
+++ b/drivers/staging/iio/accel/sca3000_core.c
@@ -595,7 +595,7 @@ static ssize_t sca3000_read_frequency(struct device *dev,
goto error_ret_mut;
ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
mutex_unlock(&st->lock);
- if (ret)
+ if (ret < 0)
goto error_ret;
val = ret;
if (base_freq > 0)
diff --git a/drivers/staging/rdma/hfi1/TODO b/drivers/staging/rdma/hfi1/TODO
index 05de0dad8762..4c6f1d7d2eaf 100644
--- a/drivers/staging/rdma/hfi1/TODO
+++ b/drivers/staging/rdma/hfi1/TODO
@@ -3,4 +3,4 @@ July, 2015
- Remove unneeded file entries in sysfs
- Remove software processing of IB protocol and place in library for use
by qib, ipath (if still present), hfi1, and eventually soft-roce
-
+- Replace incorrect uAPI
diff --git a/drivers/staging/rdma/hfi1/file_ops.c b/drivers/staging/rdma/hfi1/file_ops.c
index aae9826ec62b..c851e51b1dc3 100644
--- a/drivers/staging/rdma/hfi1/file_ops.c
+++ b/drivers/staging/rdma/hfi1/file_ops.c
@@ -62,6 +62,8 @@
#include <linux/cred.h>
#include <linux/uio.h>
+#include <rdma/ib.h>
+
#include "hfi.h"
#include "pio.h"
#include "device.h"
@@ -214,6 +216,10 @@ static ssize_t hfi1_file_write(struct file *fp, const char __user *data,
int uctxt_required = 1;
int must_be_root = 0;
+ /* FIXME: This interface cannot continue out of staging */
+ if (WARN_ON_ONCE(!ib_safe_file_access(fp)))
+ return -EACCES;
+
if (count < sizeof(cmd)) {
ret = -EINVAL;
goto bail;
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index 560c5c72daeb..65c7033e0df0 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -879,14 +879,6 @@ __cpufreq_cooling_register(struct device_node *np,
goto free_power_table;
}
- snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
- cpufreq_dev->id);
-
- cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev,
- &cpufreq_cooling_ops);
- if (IS_ERR(cool_dev))
- goto remove_idr;
-
/* Fill freq-table in descending order of frequencies */
for (i = 0, freq = -1; i <= cpufreq_dev->max_level; i++) {
freq = find_next_max(table, freq);
@@ -899,6 +891,14 @@ __cpufreq_cooling_register(struct device_node *np,
pr_debug("%s: freq:%u KHz\n", __func__, freq);
}
+ snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
+ cpufreq_dev->id);
+
+ cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev,
+ &cpufreq_cooling_ops);
+ if (IS_ERR(cool_dev))
+ goto remove_idr;
+
cpufreq_dev->clipped_freq = cpufreq_dev->freq_table[0];
cpufreq_dev->cool_dev = cool_dev;
diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c
index e845841ab036..7106288efae3 100644
--- a/drivers/thermal/rockchip_thermal.c
+++ b/drivers/thermal/rockchip_thermal.c
@@ -545,15 +545,14 @@ static int rockchip_configure_from_dt(struct device *dev,
thermal->chip->tshut_temp);
thermal->tshut_temp = thermal->chip->tshut_temp;
} else {
+ if (shut_temp > INT_MAX) {
+ dev_err(dev, "Invalid tshut temperature specified: %d\n",
+ shut_temp);
+ return -ERANGE;
+ }
thermal->tshut_temp = shut_temp;
}
- if (thermal->tshut_temp > INT_MAX) {
- dev_err(dev, "Invalid tshut temperature specified: %d\n",
- thermal->tshut_temp);
- return -ERANGE;
- }
-
if (of_property_read_u32(np, "rockchip,hw-tshut-mode", &tshut_mode)) {
dev_warn(dev,
"Missing tshut mode property, using default (%s)\n",
diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c
index 0dde34e3a7c5..545c60c826a1 100644
--- a/drivers/thunderbolt/eeprom.c
+++ b/drivers/thunderbolt/eeprom.c
@@ -444,6 +444,7 @@ int tb_drom_read(struct tb_switch *sw)
return tb_drom_parse_entries(sw);
err:
kfree(sw->drom);
+ sw->drom = NULL;
return -EIO;
}
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index c3fe026d3168..9aff37186246 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -2045,7 +2045,9 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm)
}
}
spin_unlock(&gsm_mux_lock);
- WARN_ON(i == MAX_MUX);
+ /* open failed before registering => nothing to do */
+ if (i == MAX_MUX)
+ return;
/* In theory disconnecting DLCI 0 is sufficient but for some
modems this is apparently not the case. */
diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c
index bbc4ce66c2c1..644ddb841d9f 100644
--- a/drivers/tty/n_hdlc.c
+++ b/drivers/tty/n_hdlc.c
@@ -600,7 +600,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
add_wait_queue(&tty->read_wait, &wait);
for (;;) {
- if (test_bit(TTY_OTHER_DONE, &tty->flags)) {
+ if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
ret = -EIO;
break;
}
@@ -828,7 +828,7 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp,
/* set bits for operations that won't block */
if (n_hdlc->rx_buf_list.head)
mask |= POLLIN | POLLRDNORM; /* readable */
- if (test_bit(TTY_OTHER_DONE, &tty->flags))
+ if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
mask |= POLLHUP;
if (tty_hung_up_p(filp))
mask |= POLLHUP;
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index cf000b331eed..84e71bd19082 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -1955,18 +1955,6 @@ static inline int input_available_p(struct tty_struct *tty, int poll)
return ldata->commit_head - ldata->read_tail >= amt;
}
-static inline int check_other_done(struct tty_struct *tty)
-{
- int done = test_bit(TTY_OTHER_DONE, &tty->flags);
- if (done) {
- /* paired with cmpxchg() in check_other_closed(); ensures
- * read buffer head index is not stale
- */
- smp_mb__after_atomic();
- }
- return done;
-}
-
/**
* copy_from_read_buf - copy read data directly
* @tty: terminal device
@@ -2171,7 +2159,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
struct n_tty_data *ldata = tty->disc_data;
unsigned char __user *b = buf;
DEFINE_WAIT_FUNC(wait, woken_wake_function);
- int c, done;
+ int c;
int minimum, time;
ssize_t retval = 0;
long timeout;
@@ -2239,32 +2227,35 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
((minimum - (b - buf)) >= 1))
ldata->minimum_to_wake = (minimum - (b - buf));
- done = check_other_done(tty);
-
if (!input_available_p(tty, 0)) {
- if (done) {
- retval = -EIO;
- break;
- }
- if (tty_hung_up_p(file))
- break;
- if (!timeout)
- break;
- if (file->f_flags & O_NONBLOCK) {
- retval = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
up_read(&tty->termios_rwsem);
+ tty_buffer_flush_work(tty->port);
+ down_read(&tty->termios_rwsem);
+ if (!input_available_p(tty, 0)) {
+ if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
+ retval = -EIO;
+ break;
+ }
+ if (tty_hung_up_p(file))
+ break;
+ if (!timeout)
+ break;
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ up_read(&tty->termios_rwsem);
- timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
- timeout);
+ timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
+ timeout);
- down_read(&tty->termios_rwsem);
- continue;
+ down_read(&tty->termios_rwsem);
+ continue;
+ }
}
if (ldata->icanon && !L_EXTPROC(tty)) {
@@ -2446,12 +2437,17 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file,
poll_wait(file, &tty->read_wait, wait);
poll_wait(file, &tty->write_wait, wait);
- if (check_other_done(tty))
- mask |= POLLHUP;
if (input_available_p(tty, 1))
mask |= POLLIN | POLLRDNORM;
+ else {
+ tty_buffer_flush_work(tty->port);
+ if (input_available_p(tty, 1))
+ mask |= POLLIN | POLLRDNORM;
+ }
if (tty->packet && tty->link->ctrl_status)
mask |= POLLPRI | POLLIN | POLLRDNORM;
+ if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
+ mask |= POLLHUP;
if (tty_hung_up_p(file))
mask |= POLLHUP;
if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) {
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 78e983677339..7865228f664f 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -59,7 +59,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
if (!tty->link)
return;
set_bit(TTY_OTHER_CLOSED, &tty->link->flags);
- tty_flip_buffer_push(tty->link->port);
+ wake_up_interruptible(&tty->link->read_wait);
wake_up_interruptible(&tty->link->write_wait);
if (tty->driver->subtype == PTY_TYPE_MASTER) {
set_bit(TTY_OTHER_CLOSED, &tty->flags);
@@ -247,9 +247,7 @@ static int pty_open(struct tty_struct *tty, struct file *filp)
goto out;
clear_bit(TTY_IO_ERROR, &tty->flags);
- /* TTY_OTHER_CLOSED must be cleared before TTY_OTHER_DONE */
clear_bit(TTY_OTHER_CLOSED, &tty->link->flags);
- clear_bit(TTY_OTHER_DONE, &tty->link->flags);
set_bit(TTY_THROTTLED, &tty->flags);
return 0;
diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c
index 88531a36b69c..ed489880e62b 100644
--- a/drivers/tty/serial/8250/8250_mid.c
+++ b/drivers/tty/serial/8250/8250_mid.c
@@ -14,6 +14,7 @@
#include <linux/pci.h>
#include <linux/dma/hsu.h>
+#include <linux/8250_pci.h>
#include "8250.h"
@@ -24,6 +25,7 @@
#define PCI_DEVICE_ID_INTEL_DNV_UART 0x19d8
/* Intel MID Specific registers */
+#define INTEL_MID_UART_DNV_FISR 0x08
#define INTEL_MID_UART_PS 0x30
#define INTEL_MID_UART_MUL 0x34
#define INTEL_MID_UART_DIV 0x38
@@ -31,6 +33,7 @@
struct mid8250;
struct mid8250_board {
+ unsigned int flags;
unsigned long freq;
unsigned int base_baud;
int (*setup)(struct mid8250 *, struct uart_port *p);
@@ -88,16 +91,16 @@ static int tng_setup(struct mid8250 *mid, struct uart_port *p)
static int dnv_handle_irq(struct uart_port *p)
{
struct mid8250 *mid = p->private_data;
- int ret;
-
- ret = hsu_dma_irq(&mid->dma_chip, 0);
- ret |= hsu_dma_irq(&mid->dma_chip, 1);
-
- /* For now, letting the HW generate separate interrupt for the UART */
- if (ret)
- return ret;
-
- return serial8250_handle_irq(p, serial_port_in(p, UART_IIR));
+ unsigned int fisr = serial_port_in(p, INTEL_MID_UART_DNV_FISR);
+ int ret = IRQ_NONE;
+
+ if (fisr & BIT(2))
+ ret |= hsu_dma_irq(&mid->dma_chip, 1);
+ if (fisr & BIT(1))
+ ret |= hsu_dma_irq(&mid->dma_chip, 0);
+ if (fisr & BIT(0))
+ ret |= serial8250_handle_irq(p, serial_port_in(p, UART_IIR));
+ return ret;
}
#define DNV_DMA_CHAN_OFFSET 0x80
@@ -106,12 +109,13 @@ static int dnv_setup(struct mid8250 *mid, struct uart_port *p)
{
struct hsu_dma_chip *chip = &mid->dma_chip;
struct pci_dev *pdev = to_pci_dev(p->dev);
+ unsigned int bar = FL_GET_BASE(mid->board->flags);
int ret;
chip->dev = &pdev->dev;
chip->irq = pdev->irq;
chip->regs = p->membase;
- chip->length = pci_resource_len(pdev, 0);
+ chip->length = pci_resource_len(pdev, bar);
chip->offset = DNV_DMA_CHAN_OFFSET;
/* Falling back to PIO mode if DMA probing fails */
@@ -217,6 +221,7 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct uart_8250_port uart;
struct mid8250 *mid;
+ unsigned int bar;
int ret;
ret = pcim_enable_device(pdev);
@@ -230,6 +235,7 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return -ENOMEM;
mid->board = (struct mid8250_board *)id->driver_data;
+ bar = FL_GET_BASE(mid->board->flags);
memset(&uart, 0, sizeof(struct uart_8250_port));
@@ -242,8 +248,8 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE;
uart.port.set_termios = mid8250_set_termios;
- uart.port.mapbase = pci_resource_start(pdev, 0);
- uart.port.membase = pcim_iomap(pdev, 0, 0);
+ uart.port.mapbase = pci_resource_start(pdev, bar);
+ uart.port.membase = pcim_iomap(pdev, bar, 0);
if (!uart.port.membase)
return -ENOMEM;
@@ -282,18 +288,21 @@ static void mid8250_remove(struct pci_dev *pdev)
}
static const struct mid8250_board pnw_board = {
+ .flags = FL_BASE0,
.freq = 50000000,
.base_baud = 115200,
.setup = pnw_setup,
};
static const struct mid8250_board tng_board = {
+ .flags = FL_BASE0,
.freq = 38400000,
.base_baud = 1843200,
.setup = tng_setup,
};
static const struct mid8250_board dnv_board = {
+ .flags = FL_BASE1,
.freq = 133333333,
.base_baud = 115200,
.setup = dnv_setup,
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 7cd6f9a90542..c1d4a8fa9be8 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -1401,6 +1401,9 @@ byt_set_termios(struct uart_port *p, struct ktermios *termios,
unsigned long m, n;
u32 reg;
+ /* Gracefully handle the B0 case: fall back to B9600 */
+ fuart = fuart ? fuart : 9600 * 16;
+
/* Get Fuart closer to Fref */
fuart *= rounddown_pow_of_two(fref / fuart);
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 94294558943c..7bbadd176c74 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -277,6 +277,13 @@ static bool atmel_use_dma_rx(struct uart_port *port)
return atmel_port->use_dma_rx;
}
+static bool atmel_use_fifo(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+ return atmel_port->fifo_size;
+}
+
static unsigned int atmel_get_lines_status(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
@@ -2169,7 +2176,12 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
mode |= ATMEL_US_USMODE_RS485;
} else if (termios->c_cflag & CRTSCTS) {
/* RS232 with hardware handshake (RTS/CTS) */
- mode |= ATMEL_US_USMODE_HWHS;
+ if (atmel_use_dma_rx(port) && !atmel_use_fifo(port)) {
+ dev_info(port->dev, "not enabling hardware flow control because DMA is used");
+ termios->c_cflag &= ~CRTSCTS;
+ } else {
+ mode |= ATMEL_US_USMODE_HWHS;
+ }
} else {
/* RS232 without hadware handshake */
mode |= ATMEL_US_USMODE_NORMAL;
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index fd9c47f2f29f..c8ab5370670d 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -1263,6 +1263,8 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
/* check to see if we need to change clock source */
if (ourport->baudclk != clk) {
+ clk_prepare_enable(clk);
+
s3c24xx_serial_setsource(port, clk_sel);
if (!IS_ERR(ourport->baudclk)) {
@@ -1270,8 +1272,6 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
ourport->baudclk = ERR_PTR(-EINVAL);
}
- clk_prepare_enable(clk);
-
ourport->baudclk = clk;
ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
}
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 51c7507b0444..63a06ab6ba03 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -38,7 +38,6 @@
#include <linux/major.h>
#include <linux/module.h>
#include <linux/mm.h>
-#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -116,8 +115,6 @@ struct sci_port {
struct timer_list rx_timer;
unsigned int rx_timeout;
#endif
-
- struct notifier_block freq_transition;
};
#define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS
@@ -1606,29 +1603,6 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
return ret;
}
-/*
- * Here we define a transition notifier so that we can update all of our
- * ports' baud rate when the peripheral clock changes.
- */
-static int sci_notifier(struct notifier_block *self,
- unsigned long phase, void *p)
-{
- struct sci_port *sci_port;
- unsigned long flags;
-
- sci_port = container_of(self, struct sci_port, freq_transition);
-
- if (phase == CPUFREQ_POSTCHANGE) {
- struct uart_port *port = &sci_port->port;
-
- spin_lock_irqsave(&port->lock, flags);
- port->uartclk = clk_get_rate(sci_port->iclk);
- spin_unlock_irqrestore(&port->lock, flags);
- }
-
- return NOTIFY_OK;
-}
-
static const struct sci_irq_desc {
const char *desc;
irq_handler_t handler;
@@ -2559,9 +2533,6 @@ static int sci_remove(struct platform_device *dev)
{
struct sci_port *port = platform_get_drvdata(dev);
- cpufreq_unregister_notifier(&port->freq_transition,
- CPUFREQ_TRANSITION_NOTIFIER);
-
uart_remove_one_port(&sci_uart_driver, &port->port);
sci_cleanup_single(port);
@@ -2714,16 +2685,6 @@ static int sci_probe(struct platform_device *dev)
if (ret)
return ret;
- sp->freq_transition.notifier_call = sci_notifier;
-
- ret = cpufreq_register_notifier(&sp->freq_transition,
- CPUFREQ_TRANSITION_NOTIFIER);
- if (unlikely(ret < 0)) {
- uart_remove_one_port(&sci_uart_driver, &sp->port);
- sci_cleanup_single(sp);
- return ret;
- }
-
#ifdef CONFIG_SH_STANDARD_BIOS
sh_bios_gdb_detach();
#endif
diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c
index 73190f5d2832..71d26c8e1b8f 100644
--- a/drivers/tty/serial/ucc_uart.c
+++ b/drivers/tty/serial/ucc_uart.c
@@ -1478,6 +1478,9 @@ static const struct of_device_id ucc_uart_match[] = {
.type = "serial",
.compatible = "ucc_uart",
},
+ {
+ .compatible = "fsl,t1040-ucc-uart",
+ },
{},
};
MODULE_DEVICE_TABLE(of, ucc_uart_match);
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index 3cd31e0d4bd9..fb31eecb708d 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -37,29 +37,6 @@
#define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF)
-/*
- * If all tty flip buffers have been processed by flush_to_ldisc() or
- * dropped by tty_buffer_flush(), check if the linked pty has been closed.
- * If so, wake the reader/poll to process
- */
-static inline void check_other_closed(struct tty_struct *tty)
-{
- unsigned long flags, old;
-
- /* transition from TTY_OTHER_CLOSED => TTY_OTHER_DONE must be atomic */
- for (flags = ACCESS_ONCE(tty->flags);
- test_bit(TTY_OTHER_CLOSED, &flags);
- ) {
- old = flags;
- __set_bit(TTY_OTHER_DONE, &flags);
- flags = cmpxchg(&tty->flags, old, flags);
- if (old == flags) {
- wake_up_interruptible(&tty->read_wait);
- break;
- }
- }
-}
-
/**
* tty_buffer_lock_exclusive - gain exclusive access to buffer
* tty_buffer_unlock_exclusive - release exclusive access
@@ -254,8 +231,6 @@ void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld)
if (ld && ld->ops->flush_buffer)
ld->ops->flush_buffer(tty);
- check_other_closed(tty);
-
atomic_dec(&buf->priority);
mutex_unlock(&buf->lock);
}
@@ -505,10 +480,8 @@ static void flush_to_ldisc(struct work_struct *work)
*/
count = smp_load_acquire(&head->commit) - head->read;
if (!count) {
- if (next == NULL) {
- check_other_closed(tty);
+ if (next == NULL)
break;
- }
buf->head = next;
tty_buffer_free(port, head);
continue;
@@ -597,3 +570,8 @@ bool tty_buffer_cancel_work(struct tty_port *port)
{
return cancel_work_sync(&port->buf.work);
}
+
+void tty_buffer_flush_work(struct tty_port *port)
+{
+ flush_work(&port->buf.work);
+}
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 6f0336fff501..41987a55a538 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -366,34 +366,22 @@ static void to_utf8(struct vc_data *vc, uint c)
static void do_compute_shiftstate(void)
{
- unsigned int i, j, k, sym, val;
+ unsigned int k, sym, val;
shift_state = 0;
memset(shift_down, 0, sizeof(shift_down));
- for (i = 0; i < ARRAY_SIZE(key_down); i++) {
-
- if (!key_down[i])
+ for_each_set_bit(k, key_down, min(NR_KEYS, KEY_CNT)) {
+ sym = U(key_maps[0][k]);
+ if (KTYP(sym) != KT_SHIFT && KTYP(sym) != KT_SLOCK)
continue;
- k = i * BITS_PER_LONG;
-
- for (j = 0; j < BITS_PER_LONG; j++, k++) {
-
- if (!test_bit(k, key_down))
- continue;
+ val = KVAL(sym);
+ if (val == KVAL(K_CAPSSHIFT))
+ val = KVAL(K_SHIFT);
- sym = U(key_maps[0][k]);
- if (KTYP(sym) != KT_SHIFT && KTYP(sym) != KT_SLOCK)
- continue;
-
- val = KVAL(sym);
- if (val == KVAL(K_CAPSSHIFT))
- val = KVAL(K_SHIFT);
-
- shift_down[val]++;
- shift_state |= (1 << val);
- }
+ shift_down[val]++;
+ shift_state |= BIT(val);
}
}
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 4462d167900c..136ebaaa9cc0 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -750,6 +750,7 @@ static void visual_init(struct vc_data *vc, int num, int init)
vc->vc_complement_mask = 0;
vc->vc_can_do_color = 0;
vc->vc_panic_force_write = false;
+ vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS;
vc->vc_sw->con_init(vc, init);
if (!vc->vc_complement_mask)
vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
@@ -3583,9 +3584,10 @@ static int do_register_con_driver(const struct consw *csw, int first, int last)
goto err;
desc = csw->con_startup();
-
- if (!desc)
+ if (!desc) {
+ retval = -ENODEV;
goto err;
+ }
retval = -EINVAL;
diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
index 61d538aa2346..4f4f06a5889f 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -21,6 +21,7 @@
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/mutex.h>
@@ -365,3 +366,4 @@ int otg_statemachine(struct otg_fsm *fsm)
return state_changed;
}
EXPORT_SYMBOL_GPL(otg_statemachine);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 2057d91d8336..dadd1e8dfe09 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -284,7 +284,7 @@ static int usb_probe_interface(struct device *dev)
struct usb_device *udev = interface_to_usbdev(intf);
const struct usb_device_id *id;
int error = -ENODEV;
- int lpm_disable_error;
+ int lpm_disable_error = -ENODEV;
dev_dbg(dev, "%s\n", __func__);
@@ -336,12 +336,14 @@ static int usb_probe_interface(struct device *dev)
* setting during probe, that should also be fine. usb_set_interface()
* will attempt to disable LPM, and fail if it can't disable it.
*/
- lpm_disable_error = usb_unlocked_disable_lpm(udev);
- if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
- dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.",
- __func__, driver->name);
- error = lpm_disable_error;
- goto err;
+ if (driver->disable_hub_initiated_lpm) {
+ lpm_disable_error = usb_unlocked_disable_lpm(udev);
+ if (lpm_disable_error) {
+ dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.",
+ __func__, driver->name);
+ error = lpm_disable_error;
+ goto err;
+ }
}
/* Carry out a deferred switch to altsetting 0 */
@@ -391,7 +393,8 @@ static int usb_unbind_interface(struct device *dev)
struct usb_interface *intf = to_usb_interface(dev);
struct usb_host_endpoint *ep, **eps = NULL;
struct usb_device *udev;
- int i, j, error, r, lpm_disable_error;
+ int i, j, error, r;
+ int lpm_disable_error = -ENODEV;
intf->condition = USB_INTERFACE_UNBINDING;
@@ -399,12 +402,13 @@ static int usb_unbind_interface(struct device *dev)
udev = interface_to_usbdev(intf);
error = usb_autoresume_device(udev);
- /* Hub-initiated LPM policy may change, so attempt to disable LPM until
+ /* If hub-initiated LPM policy may change, attempt to disable LPM until
* the driver is unbound. If LPM isn't disabled, that's fine because it
* wouldn't be enabled unless all the bound interfaces supported
* hub-initiated LPM.
*/
- lpm_disable_error = usb_unlocked_disable_lpm(udev);
+ if (driver->disable_hub_initiated_lpm)
+ lpm_disable_error = usb_unlocked_disable_lpm(udev);
/*
* Terminate all URBs for this interface unless the driver
@@ -505,7 +509,7 @@ int usb_driver_claim_interface(struct usb_driver *driver,
struct device *dev;
struct usb_device *udev;
int retval = 0;
- int lpm_disable_error;
+ int lpm_disable_error = -ENODEV;
if (!iface)
return -ENODEV;
@@ -526,12 +530,14 @@ int usb_driver_claim_interface(struct usb_driver *driver,
iface->condition = USB_INTERFACE_BOUND;
- /* Disable LPM until this driver is bound. */
- lpm_disable_error = usb_unlocked_disable_lpm(udev);
- if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
- dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.",
- __func__, driver->name);
- return -ENOMEM;
+ /* See the comment about disabling LPM in usb_probe_interface(). */
+ if (driver->disable_hub_initiated_lpm) {
+ lpm_disable_error = usb_unlocked_disable_lpm(udev);
+ if (lpm_disable_error) {
+ dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.",
+ __func__, driver->name);
+ return -ENOMEM;
+ }
}
/* Claimed interfaces are initially inactive (suspended) and
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 9eb1cff28bd4..b8b580e5ae6e 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -74,6 +74,15 @@ static void for_each_companion(struct pci_dev *pdev, struct usb_hcd *hcd,
if (companion->bus != pdev->bus ||
PCI_SLOT(companion->devfn) != slot)
continue;
+
+ /*
+ * Companion device should be either UHCI,OHCI or EHCI host
+ * controller, otherwise skip.
+ */
+ if (companion->class != CL_UHCI && companion->class != CL_OHCI &&
+ companion->class != CL_EHCI)
+ continue;
+
companion_hcd = pci_get_drvdata(companion);
if (!companion_hcd || !companion_hcd->self.root_hub)
continue;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 269c1ee2da44..5839111ab4e0 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -48,6 +48,11 @@ static void hub_event(struct work_struct *work);
/* synchronize hub-port add/remove and peering operations */
DEFINE_MUTEX(usb_port_peer_mutex);
+static bool skip_extended_resume_delay = 1;
+module_param(skip_extended_resume_delay, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(skip_extended_resume_delay,
+ "removes extra delay added to finish bus resume");
+
/* cycle leds on hubs that aren't blinking for attention */
static bool blinkenlights = 0;
module_param(blinkenlights, bool, S_IRUGO);
@@ -3434,7 +3439,9 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
/* drive resume for USB_RESUME_TIMEOUT msec */
dev_dbg(&udev->dev, "usb %sresume\n",
(PMSG_IS_AUTO(msg) ? "auto-" : ""));
- msleep(USB_RESUME_TIMEOUT);
+ if (!skip_extended_resume_delay)
+ usleep_range(USB_RESUME_TIMEOUT * 1000,
+ (USB_RESUME_TIMEOUT + 1) * 1000);
/* Virtual root hubs can trigger on GET_PORT_STATUS to
* stop resume signaling. Then finish the resume
@@ -3443,7 +3450,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
status = hub_port_status(hub, port1, &portstatus, &portchange);
/* TRSMRCY = 10 msec */
- msleep(10);
+ usleep_range(10000, 10500);
}
SuspendCleared:
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 6dc810bce295..944a6dca0fcb 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -44,6 +44,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Creative SB Audigy 2 NX */
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* USB3503 */
+ { USB_DEVICE(0x0424, 0x3503), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* Microsoft Wireless Laser Mouse 6000 Receiver */
{ USB_DEVICE(0x045e, 0x00e1), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -173,6 +176,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* MAYA44USB sound device */
{ USB_DEVICE(0x0a92, 0x0091), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* ASUS Base Station(T100) */
+ { USB_DEVICE(0x0b05, 0x17e0), .driver_info =
+ USB_QUIRK_IGNORE_REMOTE_WAKEUP },
+
/* Action Semiconductor flash disk */
{ USB_DEVICE(0x10d6, 0x2200), .driver_info =
USB_QUIRK_STRING_FETCH_255 },
@@ -188,26 +195,22 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x1908, 0x1315), .driver_info =
USB_QUIRK_HONOR_BNUMINTERFACES },
- /* INTEL VALUE SSD */
- { USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
-
- /* USB3503 */
- { USB_DEVICE(0x0424, 0x3503), .driver_info = USB_QUIRK_RESET_RESUME },
-
- /* ASUS Base Station(T100) */
- { USB_DEVICE(0x0b05, 0x17e0), .driver_info =
- USB_QUIRK_IGNORE_REMOTE_WAKEUP },
-
/* Protocol and OTG Electrical Test Device */
{ USB_DEVICE(0x1a0a, 0x0200), .driver_info =
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
+ /* Acer C120 LED Projector */
+ { USB_DEVICE(0x1de1, 0xc102), .driver_info = USB_QUIRK_NO_LPM },
+
/* Blackmagic Design Intensity Shuttle */
{ USB_DEVICE(0x1edb, 0xbd3b), .driver_info = USB_QUIRK_NO_LPM },
/* Blackmagic Design UltraStudio SDI */
{ USB_DEVICE(0x1edb, 0xbd4f), .driver_info = USB_QUIRK_NO_LPM },
+ /* INTEL VALUE SSD */
+ { USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
+
{ } /* terminating entry must be last */
};
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index a66d3cb62b65..a738a68d2292 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -44,6 +44,17 @@
#include <linux/usb/phy.h>
#include "hw.h"
+#ifdef CONFIG_MIPS
+/*
+ * There are some MIPS machines that can run in either big-endian
+ * or little-endian mode and that use the dwc2 register without
+ * a byteswap in both ways.
+ * Unlike other architectures, MIPS apparently does not require a
+ * barrier before the __raw_writel() to synchronize with DMA but does
+ * require the barrier after the __raw_writel() to serialize a set of
+ * writes. This set of operations was added specifically for MIPS and
+ * should only be used there.
+ */
static inline u32 dwc2_readl(const void __iomem *addr)
{
u32 value = __raw_readl(addr);
@@ -70,6 +81,22 @@ static inline void dwc2_writel(u32 value, void __iomem *addr)
pr_info("INFO:: wrote %08x to %p\n", value, addr);
#endif
}
+#else
+/* Normal architectures just use readl/write */
+static inline u32 dwc2_readl(const void __iomem *addr)
+{
+ return readl(addr);
+}
+
+static inline void dwc2_writel(u32 value, void __iomem *addr)
+{
+ writel(value, addr);
+
+#ifdef DWC2_LOG_WRITES
+ pr_info("info:: wrote %08x to %p\n", value, addr);
+#endif
+}
+#endif
/* Maximum number of Endpoints/HostChannels */
#define MAX_EPS_CHANNELS 16
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index dd5cb5577dca..2f1fb7e7aa54 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -128,12 +128,6 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, exynos);
- ret = dwc3_exynos_register_phys(exynos);
- if (ret) {
- dev_err(dev, "couldn't register PHYs\n");
- return ret;
- }
-
exynos->dev = dev;
exynos->clk = devm_clk_get(dev, "usbdrd30");
@@ -183,20 +177,29 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
goto err3;
}
+ ret = dwc3_exynos_register_phys(exynos);
+ if (ret) {
+ dev_err(dev, "couldn't register PHYs\n");
+ goto err4;
+ }
+
if (node) {
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
dev_err(dev, "failed to add dwc3 core\n");
- goto err4;
+ goto err5;
}
} else {
dev_err(dev, "no device node, failed to add dwc3 core\n");
ret = -ENODEV;
- goto err4;
+ goto err5;
}
return 0;
+err5:
+ platform_device_unregister(exynos->usb2_phy);
+ platform_device_unregister(exynos->usb3_phy);
err4:
regulator_disable(exynos->vdd10);
err3:
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 08006d84fb38..add035269ae7 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -1651,9 +1651,10 @@ static void dwc3_msm_notify_event(struct dwc3 *dwc, unsigned event)
/*
* Below sequence is used when controller is working without
- * having ssphy and only USB high speed is supported.
+ * having ssphy and only USB high/full speed is supported.
*/
- if (dwc->maximum_speed == USB_SPEED_HIGH) {
+ if (dwc->maximum_speed == USB_SPEED_HIGH ||
+ dwc->maximum_speed == USB_SPEED_FULL) {
dwc3_msm_write_reg(mdwc->base, QSCRATCH_GENERAL_CFG,
dwc3_msm_read_reg(mdwc->base,
QSCRATCH_GENERAL_CFG)
@@ -2072,6 +2073,11 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
clk_prepare_enable(mdwc->iface_clk);
clk_set_rate(mdwc->core_clk, mdwc->core_clk_rate);
clk_prepare_enable(mdwc->core_clk);
+
+ /* set Memory core: ON, Memory periphery: ON */
+ clk_set_flags(mdwc->core_clk, CLKFLAG_RETAIN_MEM);
+ clk_set_flags(mdwc->core_clk, CLKFLAG_RETAIN_PERIPH);
+
clk_prepare_enable(mdwc->utmi_clk);
if (mdwc->bus_aggr_clk)
clk_prepare_enable(mdwc->bus_aggr_clk);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 805c5e1931e1..2450cc52fa24 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -802,8 +802,8 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep)
dwc = dep->dwc;
if (!(dep->flags & DWC3_EP_ENABLED)) {
- dev_WARN_ONCE(dwc->dev, true, "%s is already disabled\n",
- dep->name);
+ dev_dbg(dwc->dev, "%s is already disabled\n", dep->name);
+ dbg_event(dep->number, "ALRDY DISABLED", dep->flags);
return 0;
}
@@ -2130,24 +2130,11 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
- pm_runtime_get_sync(dwc->dev);
- dbg_event(0xFF, "Stop gsync",
- atomic_read(&dwc->dev->power.usage_count));
- dwc3_gadget_disable_irq(dwc);
spin_lock_irqsave(&dwc->lock, flags);
-
- __dwc3_gadget_ep_disable(dwc->eps[0]);
- __dwc3_gadget_ep_disable(dwc->eps[1]);
-
dwc->gadget_driver = NULL;
-
spin_unlock_irqrestore(&dwc->lock, flags);
- pm_runtime_mark_last_busy(dwc->dev);
- pm_runtime_put_autosuspend(dwc->dev);
- dbg_event(0xFF, "Auto_susgsync", 0);
-
return 0;
}
@@ -2814,7 +2801,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT);
dwc3_usb3_phy_suspend(dwc, false);
- usb_gadget_vbus_draw(&dwc->gadget, 0);
+ usb_gadget_vbus_draw(&dwc->gadget, 100);
dwc3_reset_gadget(dwc);
dbg_event(0xFF, "BUS RST", 0);
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 5db4fe9e3cdf..be29dc4bef89 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1528,7 +1528,14 @@ static int android_setup(struct usb_gadget *gadget,
static void android_disconnect(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
- struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev);
+ struct gadget_info *gi;
+
+ if (!cdev) {
+ pr_err("%s: gadget is not connected\n", __func__);
+ return;
+ }
+
+ gi = container_of(cdev, struct gadget_info, cdev);
/* accessory HID support can be active while the
accessory function is not actually enabled,
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index fd2157c8e8c2..eb2409dda50d 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -710,26 +710,25 @@ static void ffs_user_copy_worker(struct work_struct *work)
work);
int ret = io_data->req->status ? io_data->req->status :
io_data->req->actual;
+ bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD;
ffs_log("enter: ret %d", ret);
if (io_data->read && ret > 0) {
use_mm(io_data->mm);
ret = copy_to_iter(io_data->buf, ret, &io_data->data);
- if (iov_iter_count(&io_data->data))
+ if (ret != io_data->req->actual && iov_iter_count(&io_data->data))
ret = -EFAULT;
unuse_mm(io_data->mm);
}
io_data->kiocb->ki_complete(io_data->kiocb, ret, ret);
- if (io_data->ffs->ffs_eventfd &&
- !(io_data->kiocb->ki_flags & IOCB_EVENTFD))
+ if (io_data->ffs->ffs_eventfd && !kiocb_has_eventfd)
eventfd_signal(io_data->ffs->ffs_eventfd, 1);
usb_ep_free_request(io_data->ep, io_data->req);
- io_data->kiocb->private = NULL;
if (io_data->read)
kfree(io_data->to_free);
kfree(io_data->buf);
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index c298c95d4ba0..738f20d935d6 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -497,7 +497,8 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port)
log_event_dbg("%s: Calling xdci_suspend", __func__);
ret = ipa_usb_xdci_suspend(gsi->d_port.out_channel_handle,
- gsi->d_port.in_channel_handle, gsi->prot_id);
+ gsi->d_port.in_channel_handle, gsi->prot_id,
+ true);
if (!ret) {
d_port->sm_state = STATE_SUSPENDED;
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index b135da661fc9..f3715d85aedc 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -2981,25 +2981,6 @@ void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
}
EXPORT_SYMBOL_GPL(fsg_common_set_inquiry_string);
-int fsg_common_run_thread(struct fsg_common *common)
-{
- common->state = FSG_STATE_IDLE;
- /* Tell the thread to start working */
- common->thread_task =
- kthread_create(fsg_main_thread, common, "file-storage");
- if (IS_ERR(common->thread_task)) {
- common->state = FSG_STATE_TERMINATED;
- return PTR_ERR(common->thread_task);
- }
-
- DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
-
- wake_up_process(common->thread_task);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(fsg_common_run_thread);
-
static void fsg_common_release(struct kref *ref)
{
struct fsg_common *common = container_of(ref, struct fsg_common, ref);
@@ -3009,6 +2990,7 @@ static void fsg_common_release(struct kref *ref)
if (common->state != FSG_STATE_TERMINATED) {
raise_exception(common, FSG_STATE_EXIT);
wait_for_completion(&common->thread_notifier);
+ common->thread_task = NULL;
}
for (i = 0; i < ARRAY_SIZE(common->luns); ++i) {
@@ -3054,9 +3036,21 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
if (ret)
return ret;
fsg_common_set_inquiry_string(fsg->common, NULL, NULL);
- ret = fsg_common_run_thread(fsg->common);
- if (ret)
+ }
+
+ if (!common->thread_task) {
+ common->state = FSG_STATE_IDLE;
+ common->thread_task =
+ kthread_create(fsg_main_thread, common, "file-storage");
+ if (IS_ERR(common->thread_task)) {
+ int ret = PTR_ERR(common->thread_task);
+ common->thread_task = NULL;
+ common->state = FSG_STATE_TERMINATED;
return ret;
+ }
+ DBG(common, "I/O thread pid: %d\n",
+ task_pid_nr(common->thread_task));
+ wake_up_process(common->thread_task);
}
fsg->gadget = gadget;
diff --git a/drivers/usb/gadget/function/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h
index 445df6775609..b6a9918eaefb 100644
--- a/drivers/usb/gadget/function/f_mass_storage.h
+++ b/drivers/usb/gadget/function/f_mass_storage.h
@@ -153,8 +153,6 @@ int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg);
void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
const char *pn);
-int fsg_common_run_thread(struct fsg_common *common);
-
void fsg_config_from_params(struct fsg_config *cfg,
const struct fsg_module_parameters *params,
unsigned int fsg_num_buffers);
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 98d5908c1e2f..8919cc26b98e 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -1141,6 +1141,7 @@ static void f_midi_free(struct usb_function *f)
kfree(midi->in_port[i]);
opts->func_inst.f = NULL;
kfree(midi);
+ opts->func_inst.f = NULL;
--opts->refcnt;
mutex_unlock(&opts->lock);
}
diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c
index 5e50fe245a59..33f7304eac84 100644
--- a/drivers/usb/gadget/function/f_mtp.c
+++ b/drivers/usb/gadget/function/f_mtp.c
@@ -43,6 +43,7 @@
#include "configfs.h"
#define MTP_RX_BUFFER_INIT_SIZE 1048576
+#define MTP_TX_BUFFER_INIT_SIZE 1048576
#define MTP_BULK_BUFFER_SIZE 16384
#define INTR_BUFFER_SIZE 28
#define MAX_INST_NAME_LEN 40
@@ -81,7 +82,7 @@
unsigned int mtp_rx_req_len = MTP_RX_BUFFER_INIT_SIZE;
module_param(mtp_rx_req_len, uint, S_IRUGO | S_IWUSR);
-unsigned int mtp_tx_req_len = MTP_BULK_BUFFER_SIZE;
+unsigned int mtp_tx_req_len = MTP_TX_BUFFER_INIT_SIZE;
module_param(mtp_tx_req_len, uint, S_IRUGO | S_IWUSR);
unsigned int mtp_tx_reqs = MTP_TX_REQ_MAX;
@@ -551,9 +552,6 @@ static int mtp_create_bulk_endpoints(struct mtp_dev *dev,
dev->ep_intr = ep;
retry_tx_alloc:
- if (mtp_tx_req_len > MTP_BULK_BUFFER_SIZE)
- mtp_tx_reqs = 4;
-
/* now allocate requests for our endpoints */
for (i = 0; i < mtp_tx_reqs; i++) {
req = mtp_request_new(dev->ep_in, mtp_tx_req_len);
@@ -753,8 +751,8 @@ static ssize_t mtp_write(struct file *fp, const char __user *buf,
break;
}
- if (count > MTP_BULK_BUFFER_SIZE)
- xfer = MTP_BULK_BUFFER_SIZE;
+ if (count > mtp_tx_req_len)
+ xfer = mtp_tx_req_len;
else
xfer = count;
if (xfer && copy_from_user(req->buf, buf, xfer)) {
@@ -850,8 +848,8 @@ static void send_file_work(struct work_struct *data)
break;
}
- if (count > MTP_BULK_BUFFER_SIZE)
- xfer = MTP_BULK_BUFFER_SIZE;
+ if (count > mtp_tx_req_len)
+ xfer = mtp_tx_req_len;
else
xfer = count;
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index dd73dfe5dcab..74e9f5b5a45d 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -863,8 +863,6 @@ static int eth_stop(struct net_device *net)
/*-------------------------------------------------------------------------*/
-static u8 host_ethaddr[ETH_ALEN];
-
static int get_ether_addr(const char *str, u8 *dev_addr)
{
if (str) {
@@ -895,17 +893,6 @@ static int get_ether_addr_str(u8 dev_addr[ETH_ALEN], char *str, int len)
return 18;
}
-static int get_host_ether_addr(u8 *str, u8 *dev_addr)
-{
- memcpy(dev_addr, str, ETH_ALEN);
- if (is_valid_ether_addr(dev_addr))
- return 0;
-
- random_ether_addr(dev_addr);
- memcpy(str, dev_addr, ETH_ALEN);
- return 1;
-}
-
static const struct net_device_ops eth_netdev_ops = {
.ndo_open = eth_open,
.ndo_stop = eth_stop,
@@ -963,11 +950,9 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
if (get_ether_addr(dev_addr, net->dev_addr))
dev_warn(&g->dev,
"using random %s ethernet address\n", "self");
-
- if (get_host_ether_addr(host_ethaddr, dev->host_mac))
- dev_warn(&g->dev, "using random %s ethernet address\n", "host");
- else
- dev_warn(&g->dev, "using previous %s ethernet address\n", "host");
+ if (get_ether_addr(host_addr, dev->host_mac))
+ dev_warn(&g->dev,
+ "using random %s ethernet address\n", "host");
if (ethaddr)
memcpy(ethaddr, dev->host_mac, ETH_ALEN);
diff --git a/drivers/usb/gadget/legacy/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c
index 4b158e2d1e57..64b2cbb0bc6b 100644
--- a/drivers/usb/gadget/legacy/acm_ms.c
+++ b/drivers/usb/gadget/legacy/acm_ms.c
@@ -133,10 +133,6 @@ static int acm_ms_do_config(struct usb_configuration *c)
if (status < 0)
goto put_msg;
- status = fsg_common_run_thread(opts->common);
- if (status)
- goto remove_acm;
-
status = usb_add_function(c, f_msg);
if (status)
goto remove_acm;
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index f454c7af489c..55386619a0f1 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -937,8 +937,11 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
struct usb_ep *ep = dev->gadget->ep0;
struct usb_request *req = dev->req;
- if ((retval = setup_req (ep, req, 0)) == 0)
- retval = usb_ep_queue (ep, req, GFP_ATOMIC);
+ if ((retval = setup_req (ep, req, 0)) == 0) {
+ spin_unlock_irq (&dev->lock);
+ retval = usb_ep_queue (ep, req, GFP_KERNEL);
+ spin_lock_irq (&dev->lock);
+ }
dev->state = STATE_DEV_CONNECTED;
/* assume that was SET_CONFIGURATION */
@@ -1456,8 +1459,11 @@ delegate:
w_length);
if (value < 0)
break;
+
+ spin_unlock (&dev->lock);
value = usb_ep_queue (gadget->ep0, dev->req,
- GFP_ATOMIC);
+ GFP_KERNEL);
+ spin_lock (&dev->lock);
if (value < 0) {
clean_req (gadget->ep0, dev->req);
break;
@@ -1480,11 +1486,14 @@ delegate:
if (value >= 0 && dev->state != STATE_DEV_SETUP) {
req->length = value;
req->zero = value < w_length;
- value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
+
+ spin_unlock (&dev->lock);
+ value = usb_ep_queue (gadget->ep0, req, GFP_KERNEL);
if (value < 0) {
DBG (dev, "ep_queue --> %d\n", value);
req->status = 0;
}
+ return value;
}
/* device stalls when value < 0 */
diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c
index bda3c519110f..99aa22c81770 100644
--- a/drivers/usb/gadget/legacy/mass_storage.c
+++ b/drivers/usb/gadget/legacy/mass_storage.c
@@ -132,10 +132,6 @@ static int msg_do_config(struct usb_configuration *c)
if (IS_ERR(f_msg))
return PTR_ERR(f_msg);
- ret = fsg_common_run_thread(opts->common);
- if (ret)
- goto put_func;
-
ret = usb_add_function(c, f_msg);
if (ret)
goto put_func;
diff --git a/drivers/usb/gadget/legacy/multi.c b/drivers/usb/gadget/legacy/multi.c
index 4fe794ddcd49..09c7c28f32f7 100644
--- a/drivers/usb/gadget/legacy/multi.c
+++ b/drivers/usb/gadget/legacy/multi.c
@@ -137,7 +137,6 @@ static struct usb_function *f_msg_rndis;
static int rndis_do_config(struct usb_configuration *c)
{
- struct fsg_opts *fsg_opts;
int ret;
if (gadget_is_otg(c->cdev->gadget)) {
@@ -169,11 +168,6 @@ static int rndis_do_config(struct usb_configuration *c)
goto err_fsg;
}
- fsg_opts = fsg_opts_from_func_inst(fi_msg);
- ret = fsg_common_run_thread(fsg_opts->common);
- if (ret)
- goto err_run;
-
ret = usb_add_function(c, f_msg_rndis);
if (ret)
goto err_run;
@@ -225,7 +219,6 @@ static struct usb_function *f_msg_multi;
static int cdc_do_config(struct usb_configuration *c)
{
- struct fsg_opts *fsg_opts;
int ret;
if (gadget_is_otg(c->cdev->gadget)) {
@@ -258,11 +251,6 @@ static int cdc_do_config(struct usb_configuration *c)
goto err_fsg;
}
- fsg_opts = fsg_opts_from_func_inst(fi_msg);
- ret = fsg_common_run_thread(fsg_opts->common);
- if (ret)
- goto err_run;
-
ret = usb_add_function(c, f_msg_multi);
if (ret)
goto err_run;
diff --git a/drivers/usb/gadget/legacy/nokia.c b/drivers/usb/gadget/legacy/nokia.c
index 8b3f6fb1825d..05d3f79e768d 100644
--- a/drivers/usb/gadget/legacy/nokia.c
+++ b/drivers/usb/gadget/legacy/nokia.c
@@ -152,7 +152,6 @@ static int nokia_bind_config(struct usb_configuration *c)
struct usb_function *f_ecm;
struct usb_function *f_obex2 = NULL;
struct usb_function *f_msg;
- struct fsg_opts *fsg_opts;
int status = 0;
int obex1_stat = -1;
int obex2_stat = -1;
@@ -222,12 +221,6 @@ static int nokia_bind_config(struct usb_configuration *c)
goto err_ecm;
}
- fsg_opts = fsg_opts_from_func_inst(fi_msg);
-
- status = fsg_common_run_thread(fsg_opts->common);
- if (status)
- goto err_msg;
-
status = usb_add_function(c, f_msg);
if (status)
goto err_msg;
diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index c148a4fdfe99..476ac5e511a4 100644
--- a/drivers/usb/gadget/udc/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
@@ -71,7 +71,7 @@ int usb_gadget_map_request(struct usb_gadget *gadget,
mapped = dma_map_sg(dev, req->sg, req->num_sgs,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
if (mapped == 0) {
- dev_err(&gadget->dev, "failed to map SGs\n");
+ dev_err(dev, "failed to map SGs\n");
return -EFAULT;
}
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 4031b372008e..c1c1024a054c 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -89,7 +89,7 @@ static int tegra_reset_usb_controller(struct platform_device *pdev)
if (!usb1_reset_attempted) {
struct reset_control *usb1_reset;
- usb1_reset = of_reset_control_get(phy_np, "usb");
+ usb1_reset = of_reset_control_get(phy_np, "utmi-pads");
if (IS_ERR(usb1_reset)) {
dev_warn(&pdev->dev,
"can't get utmi-pads reset from the PHY\n");
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 0f51d078416e..a7b055bc279a 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -1165,7 +1165,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_set_link_state(xhci, port_array, wIndex,
XDEV_RESUME);
spin_unlock_irqrestore(&xhci->lock, flags);
- msleep(20);
+ usleep_range(21000, 21500);
spin_lock_irqsave(&xhci->lock, flags);
xhci_set_link_state(xhci, port_array, wIndex,
XDEV_U0);
@@ -1409,7 +1409,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
if (need_usb2_u3_exit) {
spin_unlock_irqrestore(&xhci->lock, flags);
- msleep(20);
+ usleep_range(21000, 21500);
spin_lock_irqsave(&xhci->lock, flags);
}
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 2ac142e3cce9..29dc6ab252b1 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1789,6 +1789,8 @@ void xhci_free_command(struct xhci_hcd *xhci,
int xhci_sec_event_ring_cleanup(struct usb_hcd *hcd, unsigned intr_num)
{
int size;
+ u32 iman_reg;
+ u64 erdp_reg;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct device *dev = xhci_to_hcd(xhci)->self.controller;
@@ -1800,14 +1802,38 @@ int xhci_sec_event_ring_cleanup(struct usb_hcd *hcd, unsigned intr_num)
size =
sizeof(struct xhci_erst_entry)*(xhci->sec_erst[intr_num].num_entries);
- if (xhci->sec_erst[intr_num].entries)
+ if (xhci->sec_erst[intr_num].entries) {
+ /*
+ * disable irq, ack pending interrupt and clear EHB for xHC to
+ * generate interrupt again when new event ring is setup
+ */
+ iman_reg =
+ readl_relaxed(&xhci->sec_ir_set[intr_num]->irq_pending);
+ iman_reg &= ~IMAN_IE;
+ writel_relaxed(iman_reg,
+ &xhci->sec_ir_set[intr_num]->irq_pending);
+ iman_reg =
+ readl_relaxed(&xhci->sec_ir_set[intr_num]->irq_pending);
+ if (iman_reg & IMAN_IP)
+ writel_relaxed(iman_reg,
+ &xhci->sec_ir_set[intr_num]->irq_pending);
+ /* make sure IP gets cleared before clearing EHB */
+ mb();
+
+ erdp_reg = xhci_read_64(xhci,
+ &xhci->sec_ir_set[intr_num]->erst_dequeue);
+ xhci_write_64(xhci, erdp_reg | ERST_EHB,
+ &xhci->sec_ir_set[intr_num]->erst_dequeue);
+
dma_free_coherent(dev, size, xhci->sec_erst[intr_num].entries,
xhci->sec_erst[intr_num].erst_dma_addr);
- xhci->sec_erst[intr_num].entries = NULL;
+ xhci->sec_erst[intr_num].entries = NULL;
+ }
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed SEC ERST#%d",
intr_num);
if (xhci->sec_event_ring[intr_num])
xhci_ring_free(xhci, xhci->sec_event_ring[intr_num]);
+
xhci->sec_event_ring[intr_num] = NULL;
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Freed sec event ring");
@@ -1923,6 +1949,12 @@ no_bw:
kfree(xhci->rh_bw);
kfree(xhci->ext_caps);
+ xhci->usb2_ports = NULL;
+ xhci->usb3_ports = NULL;
+ xhci->port_array = NULL;
+ xhci->rh_bw = NULL;
+ xhci->ext_caps = NULL;
+
xhci->page_size = 0;
xhci->page_shift = 0;
xhci->bus_state[0].bus_suspended = 0;
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 6c47c26b5df7..de644e56aa3b 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -37,6 +37,7 @@
/* Device for a quirk */
#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73
#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000
+#define PCI_DEVICE_ID_FRESCO_LOGIC_FL1009 0x1009
#define PCI_DEVICE_ID_FRESCO_LOGIC_FL1400 0x1400
#define PCI_VENDOR_ID_ETRON 0x1b6f
@@ -48,6 +49,7 @@
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI 0xa12f
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f
#define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI 0x0aa8
+#define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8
static const char hcd_name[] = "xhci_hcd";
@@ -114,6 +116,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
}
+ if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
+ pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_FL1009)
+ xhci->quirks |= XHCI_BROKEN_STREAMS;
+
if (pdev->vendor == PCI_VENDOR_ID_NEC)
xhci->quirks |= XHCI_NEC_HOST;
@@ -156,7 +162,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
(pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI)) {
+ pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI)) {
xhci->quirks |= XHCI_PME_STUCK_QUIRK;
}
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 3e49861a09a2..c025dccdd8f1 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -186,6 +186,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
ret = clk_prepare_enable(clk);
if (ret)
goto put_hcd;
+ } else if (PTR_ERR(clk) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto put_hcd;
}
if (pdev->dev.parent)
@@ -241,10 +244,14 @@ static int xhci_plat_probe(struct platform_device *pdev)
if (ret)
goto disable_usb_phy;
+ device_wakeup_enable(&hcd->self.root_hub->dev);
+
ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED | IRQF_ONESHOT);
if (ret)
goto dealloc_usb2_hcd;
+ device_wakeup_enable(&xhci->shared_hcd->self.root_hub->dev);
+
ret = device_create_file(&pdev->dev, &dev_attr_config_imod);
if (ret)
dev_err(&pdev->dev, "%s: unable to create imod sysfs entry\n",
@@ -325,7 +332,7 @@ static int xhci_plat_runtime_suspend(struct device *dev)
dev_dbg(dev, "xhci-plat runtime suspend\n");
- return xhci_suspend(xhci, true);
+ return 0;
}
static int xhci_plat_runtime_resume(struct device *dev)
@@ -339,7 +346,7 @@ static int xhci_plat_runtime_resume(struct device *dev)
dev_dbg(dev, "xhci-plat runtime resume\n");
- ret = xhci_resume(xhci, false);
+ ret = 0;
pm_runtime_mark_last_busy(dev);
return ret;
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 2b63969c2bbf..34cd23724bed 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -289,6 +289,14 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
+
+ /*
+ * Writing the CMD_RING_ABORT bit should cause a cmd completion event,
+ * however on some host hw the CMD_RING_RUNNING bit is correctly cleared
+ * but the completion event in never sent. Use the cmd timeout timer to
+ * handle those cases. Use twice the time to cover the bit polling retry
+ */
+ mod_timer(&xhci->cmd_timer, jiffies + (2 * XHCI_CMD_DEFAULT_TIMEOUT));
xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
&xhci->op_regs->cmd_ring);
@@ -313,6 +321,7 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
xhci_err(xhci, "Stopped the command ring failed, "
"maybe the host is dead\n");
+ del_timer(&xhci->cmd_timer);
xhci->xhc_state |= XHCI_STATE_DYING;
xhci_quiesce(xhci);
xhci_halt(xhci);
@@ -1252,22 +1261,21 @@ void xhci_handle_command_timeout(unsigned long data)
int ret;
unsigned long flags;
u64 hw_ring_state;
- struct xhci_command *cur_cmd = NULL;
+ bool second_timeout = false;
xhci = (struct xhci_hcd *) data;
/* mark this command to be cancelled */
spin_lock_irqsave(&xhci->lock, flags);
if (xhci->current_cmd) {
- cur_cmd = xhci->current_cmd;
- cur_cmd->status = COMP_CMD_ABORT;
+ if (xhci->current_cmd->status == COMP_CMD_ABORT)
+ second_timeout = true;
+ xhci->current_cmd->status = COMP_CMD_ABORT;
}
-
/* Make sure command ring is running before aborting it */
hw_ring_state = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
if ((xhci->cmd_ring_state & CMD_RING_STATE_RUNNING) &&
(hw_ring_state & CMD_RING_RUNNING)) {
-
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_dbg(xhci, "Command timeout\n");
ret = xhci_abort_cmd_ring(xhci);
@@ -1279,6 +1287,15 @@ void xhci_handle_command_timeout(unsigned long data)
}
return;
}
+
+ /* command ring failed to restart, or host removed. Bail out */
+ if (second_timeout || xhci->xhc_state & XHCI_STATE_REMOVING) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_dbg(xhci, "command timed out twice, ring start fail?\n");
+ xhci_cleanup_command_queue(xhci);
+ return;
+ }
+
/* command timeout on stopped ring, ring can't be aborted */
xhci_dbg(xhci, "Command timeout on stopped ring\n");
xhci_handle_stopped_cmd_ring(xhci, xhci->current_cmd);
@@ -2727,7 +2744,8 @@ hw_died:
writel(irq_pending, &xhci->ir_set->irq_pending);
}
- if (xhci->xhc_state & XHCI_STATE_DYING) {
+ if (xhci->xhc_state & XHCI_STATE_DYING ||
+ xhci->xhc_state & XHCI_STATE_HALTED) {
xhci_dbg(xhci, "xHCI dying, ignoring interrupt. "
"Shouldn't IRQs be disabled?\n");
/* Clear the event handler busy flag (RW1C);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index b30831ef4014..a37b219a8dc5 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -692,20 +692,23 @@ void xhci_stop(struct usb_hcd *hcd)
u32 temp;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- if (xhci->xhc_state & XHCI_STATE_HALTED)
- return;
-
mutex_lock(&xhci->mutex);
- spin_lock_irq(&xhci->lock);
- xhci->xhc_state |= XHCI_STATE_HALTED;
- xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
- /* Make sure the xHC is halted for a USB3 roothub
- * (xhci_stop() could be called as part of failed init).
- */
- xhci_halt(xhci);
- xhci_reset(xhci);
- spin_unlock_irq(&xhci->lock);
+ if (!(xhci->xhc_state & XHCI_STATE_HALTED)) {
+ spin_lock_irq(&xhci->lock);
+
+ xhci->xhc_state |= XHCI_STATE_HALTED;
+ xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
+ xhci_halt(xhci);
+ xhci_reset(xhci);
+
+ spin_unlock_irq(&xhci->lock);
+ }
+
+ if (!usb_hcd_is_primary_hcd(hcd)) {
+ mutex_unlock(&xhci->mutex);
+ return;
+ }
xhci_cleanup_msix(xhci);
@@ -1116,8 +1119,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
/* Resume root hubs only when have pending events. */
status = readl(&xhci->op_regs->status);
if (status & STS_EINT) {
- usb_hcd_resume_root_hub(hcd);
usb_hcd_resume_root_hub(xhci->shared_hcd);
+ usb_hcd_resume_root_hub(hcd);
}
}
@@ -1132,10 +1135,10 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
/* Re-enable port polling. */
xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
- set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- usb_hcd_poll_rh_status(hcd);
set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
usb_hcd_poll_rh_status(xhci->shared_hcd);
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ usb_hcd_poll_rh_status(hcd);
return retval;
}
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 637f3f7cfce8..1a812eafe670 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -505,6 +505,7 @@ static struct scatterlist *
alloc_sglist(int nents, int max, int vary, struct usbtest_dev *dev, int pipe)
{
struct scatterlist *sg;
+ unsigned int n_size = 0;
unsigned i;
unsigned size = max;
unsigned maxpacket =
@@ -537,7 +538,8 @@ alloc_sglist(int nents, int max, int vary, struct usbtest_dev *dev, int pipe)
break;
case 1:
for (j = 0; j < size; j++)
- *buf++ = (u8) ((j % maxpacket) % 63);
+ *buf++ = (u8) (((j + n_size) % maxpacket) % 63);
+ n_size += size;
break;
}
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index ee9ff7028b92..00eed5d66fda 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -2401,7 +2401,8 @@ static void musb_restore_context(struct musb *musb)
musb_writew(musb_base, MUSB_INTRTXE, musb->intrtxe);
musb_writew(musb_base, MUSB_INTRRXE, musb->intrrxe);
musb_writeb(musb_base, MUSB_INTRUSBE, musb->context.intrusbe);
- musb_writeb(musb_base, MUSB_DEVCTL, musb->context.devctl);
+ if (musb->context.devctl & MUSB_DEVCTL_SESSION)
+ musb_writeb(musb_base, MUSB_DEVCTL, musb->context.devctl);
for (i = 0; i < musb->config->num_eps; ++i) {
struct musb_hw_ep *hw_ep;
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 795a45b1b25b..59a63a0b7985 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -594,14 +594,13 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, u8 epnum)
musb_writew(ep->regs, MUSB_TXCSR, 0);
/* scrub all previous state, clearing toggle */
- } else {
- csr = musb_readw(ep->regs, MUSB_RXCSR);
- if (csr & MUSB_RXCSR_RXPKTRDY)
- WARNING("rx%d, packet/%d ready?\n", ep->epnum,
- musb_readw(ep->regs, MUSB_RXCOUNT));
-
- musb_h_flush_rxfifo(ep, MUSB_RXCSR_CLRDATATOG);
}
+ csr = musb_readw(ep->regs, MUSB_RXCSR);
+ if (csr & MUSB_RXCSR_RXPKTRDY)
+ WARNING("rx%d, packet/%d ready?\n", ep->epnum,
+ musb_readw(ep->regs, MUSB_RXCOUNT));
+
+ musb_h_flush_rxfifo(ep, MUSB_RXCSR_CLRDATATOG);
/* target addr and (for multipoint) hub addr/port */
if (musb->is_multipoint) {
@@ -995,9 +994,15 @@ static void musb_bulk_nak_timeout(struct musb *musb, struct musb_hw_ep *ep,
if (is_in) {
dma = is_dma_capable() ? ep->rx_channel : NULL;
- /* clear nak timeout bit */
+ /*
+ * Need to stop the transaction by clearing REQPKT first
+ * then the NAK Timeout bit ref MUSBMHDRC USB 2.0 HIGH-SPEED
+ * DUAL-ROLE CONTROLLER Programmer's Guide, section 9.2.2
+ */
rx_csr = musb_readw(epio, MUSB_RXCSR);
rx_csr |= MUSB_RXCSR_H_WZC_BITS;
+ rx_csr &= ~MUSB_RXCSR_H_REQPKT;
+ musb_writew(epio, MUSB_RXCSR, rx_csr);
rx_csr &= ~MUSB_RXCSR_DATAERROR;
musb_writew(epio, MUSB_RXCSR, rx_csr);
@@ -1551,7 +1556,7 @@ static int musb_rx_dma_iso_cppi41(struct dma_controller *dma,
struct urb *urb,
size_t len)
{
- struct dma_channel *channel = hw_ep->tx_channel;
+ struct dma_channel *channel = hw_ep->rx_channel;
void __iomem *epio = hw_ep->regs;
dma_addr_t *buf;
u32 length, res;
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 2bc70d1cf6fa..b4d7c7d8bddf 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -21,6 +21,7 @@
#include <linux/power_supply.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/extcon.h>
#include <linux/usb/usbpd.h>
@@ -264,6 +265,16 @@ struct vdm_tx {
int size;
};
+struct rx_msg {
+ u8 type;
+ u8 len;
+ u32 payload[7];
+ struct list_head entry;
+};
+
+#define IS_DATA(m, t) ((m) && ((m)->len) && ((m)->type == (t)))
+#define IS_CTRL(m, t) ((m) && !((m)->len) && ((m)->type == (t)))
+
struct usbpd {
struct device dev;
struct workqueue_struct *wq;
@@ -274,10 +285,9 @@ struct usbpd {
struct extcon_dev *extcon;
enum usbpd_state current_state;
- bool hard_reset;
- u8 rx_msg_type;
- u8 rx_msg_len;
- u32 rx_payload[7];
+ bool hard_reset_recvd;
+ struct list_head rx_q;
+ spinlock_t rx_lock;
u32 received_pdos[7];
int src_cap_id;
@@ -457,14 +467,10 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos)
return 0;
}
-static int pd_eval_src_caps(struct usbpd *pd, const u32 *src_caps)
+static int pd_eval_src_caps(struct usbpd *pd)
{
union power_supply_propval val;
- u32 first_pdo = src_caps[0];
-
- /* save the PDOs so userspace can further evaluate */
- memcpy(&pd->received_pdos, src_caps, sizeof(pd->received_pdos));
- pd->src_cap_id++;
+ u32 first_pdo = pd->received_pdos[0];
if (PD_SRC_PDO_TYPE(first_pdo) != PD_SRC_PDO_TYPE_FIXED) {
usbpd_err(&pd->dev, "First src_cap invalid! %08x\n", first_pdo);
@@ -487,16 +493,12 @@ static int pd_eval_src_caps(struct usbpd *pd, const u32 *src_caps)
static void pd_send_hard_reset(struct usbpd *pd)
{
- int ret;
-
usbpd_dbg(&pd->dev, "send hard reset");
/* Force CC logic to source/sink to keep Rp/Rd unchanged */
set_power_role(pd, pd->current_pr);
pd->hard_reset_count++;
- ret = pd_phy_signal(HARD_RESET_SIG, 5); /* tHardResetComplete */
- if (!ret)
- pd->hard_reset = true;
+ pd_phy_signal(HARD_RESET_SIG, 5); /* tHardResetComplete */
pd->in_pr_swap = false;
}
@@ -522,13 +524,15 @@ static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type)
/* Force CC logic to source/sink to keep Rp/Rd unchanged */
set_power_role(pd, pd->current_pr);
- pd->hard_reset = true;
+ pd->hard_reset_recvd = true;
kick_sm(pd, 0);
}
static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type,
u8 *buf, size_t len)
{
+ struct rx_msg *rx_msg;
+ unsigned long flags;
u16 header;
if (type != SOP_MSG) {
@@ -571,16 +575,20 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type,
return;
}
- /* block until previous message has been consumed by usbpd_sm */
- if (pd->rx_msg_type)
- flush_work(&pd->sm_work);
+ rx_msg = kzalloc(sizeof(*rx_msg), GFP_KERNEL);
+ if (!rx_msg)
+ return;
- pd->rx_msg_type = PD_MSG_HDR_TYPE(header);
- pd->rx_msg_len = PD_MSG_HDR_COUNT(header);
- memcpy(&pd->rx_payload, buf, len);
+ rx_msg->type = PD_MSG_HDR_TYPE(header);
+ rx_msg->len = PD_MSG_HDR_COUNT(header);
+ memcpy(&rx_msg->payload, buf, len);
+
+ spin_lock_irqsave(&pd->rx_lock, flags);
+ list_add_tail(&rx_msg->entry, &pd->rx_q);
+ spin_unlock_irqrestore(&pd->rx_lock, flags);
usbpd_dbg(&pd->dev, "received message: type(%d) len(%d)\n",
- pd->rx_msg_type, pd->rx_msg_len);
+ rx_msg->type, rx_msg->len);
kick_sm(pd, 0);
}
@@ -611,6 +619,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
FRAME_FILTER_EN_HARD_RESET
};
union power_supply_propval val = {0};
+ unsigned long flags;
int ret;
usbpd_dbg(&pd->dev, "%s -> %s\n",
@@ -642,8 +651,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val);
- pd->rx_msg_len = 0;
- pd->rx_msg_type = 0;
pd->rx_msgid = -1;
if (!pd->in_pr_swap) {
@@ -753,40 +760,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
break;
- case PE_SRC_TRANSITION_TO_DEFAULT:
- pd->hard_reset = false;
-
- if (pd->vconn_enabled)
- regulator_disable(pd->vconn);
- regulator_disable(pd->vbus);
-
- if (pd->current_dr != DR_DFP) {
- extcon_set_cable_state_(pd->extcon, EXTCON_USB, 0);
- pd->current_dr = DR_DFP;
- pd_phy_update_roles(pd->current_dr, pd->current_pr);
- }
-
- msleep(SRC_RECOVER_TIME);
-
- ret = regulator_enable(pd->vbus);
- if (ret)
- usbpd_err(&pd->dev, "Unable to enable vbus\n");
-
- if (pd->vconn_enabled) {
- ret = regulator_enable(pd->vconn);
- if (ret) {
- usbpd_err(&pd->dev, "Unable to enable vconn\n");
- pd->vconn_enabled = false;
- }
- }
-
- val.intval = 0;
- power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
-
- usbpd_set_state(pd, PE_SRC_STARTUP);
- break;
-
case PE_SRC_HARD_RESET:
case PE_SNK_HARD_RESET:
/* hard reset may sleep; handle it in the workqueue */
@@ -842,8 +815,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
/* Reset protocol layer */
pd->tx_msgid = 0;
pd->rx_msgid = -1;
- pd->rx_msg_len = 0;
- pd->rx_msg_type = 0;
if (!pd->in_pr_swap) {
if (pd->pd_phy_opened) {
@@ -872,10 +843,10 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
/* fall-through */
case PE_SNK_WAIT_FOR_CAPABILITIES:
- if (pd->rx_msg_len && pd->rx_msg_type)
- kick_sm(pd, 0);
- else
+ spin_lock_irqsave(&pd->rx_lock, flags);
+ if (list_empty(&pd->rx_q))
kick_sm(pd, SINK_WAIT_CAP_TIME);
+ spin_unlock_irqrestore(&pd->rx_lock, flags);
break;
case PE_SNK_EVALUATE_CAPABILITY:
@@ -883,7 +854,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->hard_reset_count = 0;
/* evaluate PDOs and select one */
- ret = pd_eval_src_caps(pd, pd->rx_payload);
+ ret = pd_eval_src_caps(pd);
if (ret < 0) {
usbpd_err(&pd->dev, "Invalid src_caps received. Skipping request\n");
break;
@@ -971,6 +942,13 @@ int usbpd_register_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr)
return -EINVAL;
}
+ /* require connect/disconnect callbacks be implemented */
+ if (!hdlr->connect || !hdlr->disconnect) {
+ usbpd_err(&pd->dev, "SVID 0x%04x connect/disconnect must be non-NULL\n",
+ hdlr->svid);
+ return -EINVAL;
+ }
+
usbpd_dbg(&pd->dev, "registered handler for SVID 0x%04x\n", hdlr->svid);
list_add_tail(&hdlr->entry, &pd->svid_handlers);
@@ -981,8 +959,8 @@ int usbpd_register_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr)
for (i = 0; i < pd->num_svids; i++) {
if (pd->discovered_svids[i] == hdlr->svid) {
- if (hdlr->connect)
- hdlr->connect(hdlr);
+ hdlr->connect(hdlr);
+ hdlr->discovered = true;
break;
}
}
@@ -1037,13 +1015,13 @@ int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd,
}
EXPORT_SYMBOL(usbpd_send_svdm);
-static void handle_vdm_rx(struct usbpd *pd)
+static void handle_vdm_rx(struct usbpd *pd, struct rx_msg *rx_msg)
{
- u32 vdm_hdr = pd->rx_payload[0];
- u32 *vdos = &pd->rx_payload[1];
+ u32 vdm_hdr = rx_msg->payload[0];
+ u32 *vdos = &rx_msg->payload[1];
u16 svid = VDM_HDR_SVID(vdm_hdr);
u16 *psvid;
- u8 i, num_vdos = pd->rx_msg_len - 1; /* num objects minus header */
+ u8 i, num_vdos = rx_msg->len - 1; /* num objects minus header */
u8 cmd = SVDM_HDR_CMD(vdm_hdr);
u8 cmd_type = SVDM_HDR_CMD_TYPE(vdm_hdr);
struct usbpd_svid_handler *handler;
@@ -1194,8 +1172,10 @@ static void handle_vdm_rx(struct usbpd *pd)
svid = pd->discovered_svids[i];
if (svid) {
handler = find_svid_handler(pd, svid);
- if (handler && handler->connect)
+ if (handler) {
handler->connect(handler);
+ handler->discovered = true;
+ }
}
}
@@ -1300,10 +1280,14 @@ static void reset_vdm_state(struct usbpd *pd)
{
struct usbpd_svid_handler *handler;
- pd->vdm_state = VDM_NONE;
- list_for_each_entry(handler, &pd->svid_handlers, entry)
- if (handler->disconnect)
+ list_for_each_entry(handler, &pd->svid_handlers, entry) {
+ if (handler->discovered) {
handler->disconnect(handler);
+ handler->discovered = false;
+ }
+ }
+
+ pd->vdm_state = VDM_NONE;
kfree(pd->vdm_tx_retry);
pd->vdm_tx_retry = NULL;
kfree(pd->discovered_svids);
@@ -1368,14 +1352,27 @@ static void vconn_swap(struct usbpd *pd)
}
}
+static inline void rx_msg_cleanup(struct usbpd *pd)
+{
+ struct rx_msg *msg, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pd->rx_lock, flags);
+ list_for_each_entry_safe(msg, tmp, &pd->rx_q, entry) {
+ list_del(&msg->entry);
+ kfree(msg);
+ }
+ spin_unlock_irqrestore(&pd->rx_lock, flags);
+}
+
/* Handles current state and determines transitions */
static void usbpd_sm(struct work_struct *w)
{
struct usbpd *pd = container_of(w, struct usbpd, sm_work);
union power_supply_propval val = {0};
int ret;
- enum usbpd_control_msg_type ctrl_recvd = 0;
- enum usbpd_data_msg_type data_recvd = 0;
+ struct rx_msg *rx_msg = NULL;
+ unsigned long flags;
usbpd_dbg(&pd->dev, "handle state %s\n",
usbpd_state_strings[pd->current_state]);
@@ -1383,10 +1380,12 @@ static void usbpd_sm(struct work_struct *w)
hrtimer_cancel(&pd->timer);
pd->sm_queued = false;
- if (pd->rx_msg_len)
- data_recvd = pd->rx_msg_type;
- else
- ctrl_recvd = pd->rx_msg_type;
+ spin_lock_irqsave(&pd->rx_lock, flags);
+ if (!list_empty(&pd->rx_q)) {
+ rx_msg = list_first_entry(&pd->rx_q, struct rx_msg, entry);
+ list_del(&rx_msg->entry);
+ }
+ spin_unlock_irqrestore(&pd->rx_lock, flags);
/* Disconnect? */
if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE && !pd->in_pr_swap) {
@@ -1403,13 +1402,14 @@ static void usbpd_sm(struct work_struct *w)
pd->in_pr_swap = false;
pd->pd_connected = false;
pd->in_explicit_contract = false;
- pd->hard_reset = false;
+ pd->hard_reset_recvd = false;
pd->caps_count = 0;
pd->hard_reset_count = 0;
pd->src_cap_id = 0;
pd->requested_voltage = 0;
pd->requested_current = 0;
memset(&pd->received_pdos, 0, sizeof(pd->received_pdos));
+ rx_msg_cleanup(pd);
val.intval = 0;
power_supply_set_property(pd->usb_psy,
@@ -1456,24 +1456,29 @@ static void usbpd_sm(struct work_struct *w)
}
/* Hard reset? */
- if (pd->hard_reset) {
+ if (pd->hard_reset_recvd) {
+ pd->hard_reset_recvd = false;
+
val.intval = 1;
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
pd->in_pr_swap = false;
+ rx_msg_cleanup(pd);
reset_vdm_state(pd);
- if (pd->current_pr == PR_SINK)
+ if (pd->current_pr == PR_SINK) {
usbpd_set_state(pd, PE_SNK_TRANSITION_TO_DEFAULT);
- else
- usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT);
+ } else {
+ pd->current_state = PE_SRC_TRANSITION_TO_DEFAULT;
+ kick_sm(pd, PS_HARD_RESET_TIME);
+ }
goto sm_done;
}
/* Soft reset? */
- if (ctrl_recvd == MSG_SOFT_RESET) {
+ if (IS_CTRL(rx_msg, MSG_SOFT_RESET)) {
usbpd_dbg(&pd->dev, "Handle soft reset\n");
if (pd->current_pr == PR_SRC)
@@ -1553,10 +1558,10 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_SRC_SEND_CAPABILITIES_WAIT:
- if (data_recvd == MSG_REQUEST) {
- pd->rdo = pd->rx_payload[0];
+ if (IS_DATA(rx_msg, MSG_REQUEST)) {
+ pd->rdo = rx_msg->payload[0];
usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY);
- } else if (data_recvd || ctrl_recvd) {
+ } else if (rx_msg) {
usbpd_err(&pd->dev, "Unexpected message received\n");
usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
} else {
@@ -1565,7 +1570,7 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_SRC_READY:
- if (ctrl_recvd == MSG_GET_SOURCE_CAP) {
+ if (IS_CTRL(rx_msg, MSG_GET_SOURCE_CAP)) {
ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES,
default_src_caps,
ARRAY_SIZE(default_src_caps), SOP_MSG);
@@ -1574,7 +1579,7 @@ static void usbpd_sm(struct work_struct *w)
usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
break;
}
- } else if (ctrl_recvd == MSG_GET_SINK_CAP) {
+ } else if (IS_CTRL(rx_msg, MSG_GET_SINK_CAP)) {
ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES,
default_snk_caps,
ARRAY_SIZE(default_snk_caps), SOP_MSG);
@@ -1582,10 +1587,10 @@ static void usbpd_sm(struct work_struct *w)
usbpd_err(&pd->dev, "Error sending Sink Caps\n");
usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
}
- } else if (data_recvd == MSG_REQUEST) {
- pd->rdo = pd->rx_payload[0];
+ } else if (IS_DATA(rx_msg, MSG_REQUEST)) {
+ pd->rdo = rx_msg->payload[0];
usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY);
- } else if (ctrl_recvd == MSG_DR_SWAP) {
+ } else if (IS_CTRL(rx_msg, MSG_DR_SWAP)) {
if (pd->vdm_state == MODE_ENTERED) {
usbpd_set_state(pd, PE_SRC_HARD_RESET);
break;
@@ -1600,7 +1605,7 @@ static void usbpd_sm(struct work_struct *w)
dr_swap(pd);
kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
- } else if (ctrl_recvd == MSG_PR_SWAP) {
+ } else if (IS_CTRL(rx_msg, MSG_PR_SWAP)) {
/* lock in current mode */
set_power_role(pd, pd->current_pr);
@@ -1615,7 +1620,7 @@ static void usbpd_sm(struct work_struct *w)
pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF;
kick_sm(pd, SRC_TRANSITION_TIME);
break;
- } else if (ctrl_recvd == MSG_VCONN_SWAP) {
+ } else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP)) {
ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
if (ret) {
usbpd_err(&pd->dev, "Error sending Accept\n");
@@ -1625,13 +1630,45 @@ static void usbpd_sm(struct work_struct *w)
vconn_swap(pd);
} else {
- if (data_recvd == MSG_VDM)
- handle_vdm_rx(pd);
+ if (IS_DATA(rx_msg, MSG_VDM))
+ handle_vdm_rx(pd, rx_msg);
else
handle_vdm_tx(pd);
}
break;
+ case PE_SRC_TRANSITION_TO_DEFAULT:
+ if (pd->vconn_enabled)
+ regulator_disable(pd->vconn);
+ regulator_disable(pd->vbus);
+
+ if (pd->current_dr != DR_DFP) {
+ extcon_set_cable_state_(pd->extcon, EXTCON_USB, 0);
+ pd->current_dr = DR_DFP;
+ pd_phy_update_roles(pd->current_dr, pd->current_pr);
+ }
+
+ msleep(SRC_RECOVER_TIME);
+
+ ret = regulator_enable(pd->vbus);
+ if (ret)
+ usbpd_err(&pd->dev, "Unable to enable vbus\n");
+
+ if (pd->vconn_enabled) {
+ ret = regulator_enable(pd->vconn);
+ if (ret) {
+ usbpd_err(&pd->dev, "Unable to enable vconn\n");
+ pd->vconn_enabled = false;
+ }
+ }
+
+ val.intval = 0;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+
+ usbpd_set_state(pd, PE_SRC_STARTUP);
+ break;
+
case PE_SRC_HARD_RESET:
val.intval = 1;
power_supply_set_property(pd->usb_psy,
@@ -1639,11 +1676,11 @@ static void usbpd_sm(struct work_struct *w)
pd_send_hard_reset(pd);
pd->in_explicit_contract = false;
+ rx_msg_cleanup(pd);
reset_vdm_state(pd);
- usleep_range(PS_HARD_RESET_TIME * USEC_PER_MSEC,
- (PS_HARD_RESET_TIME + 5) * USEC_PER_MSEC);
- usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT);
+ pd->current_state = PE_SRC_TRANSITION_TO_DEFAULT;
+ kick_sm(pd, PS_HARD_RESET_TIME);
break;
case PE_SNK_STARTUP:
@@ -1651,7 +1688,7 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_SNK_WAIT_FOR_CAPABILITIES:
- if (data_recvd == MSG_SOURCE_CAPABILITIES) {
+ if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) {
val.intval = 0;
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_PD_IN_HARD_RESET,
@@ -1661,6 +1698,11 @@ static void usbpd_sm(struct work_struct *w)
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_PD_ACTIVE, &val);
+ /* save the PDOs so userspace can further evaluate */
+ memcpy(&pd->received_pdos, rx_msg->payload,
+ sizeof(pd->received_pdos));
+ pd->src_cap_id++;
+
usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY);
} else if (pd->hard_reset_count < 3) {
usbpd_set_state(pd, PE_SNK_HARD_RESET);
@@ -1688,7 +1730,7 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_SNK_SELECT_CAPABILITY:
- if (ctrl_recvd == MSG_ACCEPT) {
+ if (IS_CTRL(rx_msg, MSG_ACCEPT)) {
/* prepare for voltage increase/decrease */
val.intval = pd->requested_voltage;
power_supply_set_property(pd->usb_psy,
@@ -1708,13 +1750,14 @@ static void usbpd_sm(struct work_struct *w)
pd->selected_pdo = pd->requested_pdo;
usbpd_set_state(pd, PE_SNK_TRANSITION_SINK);
- } else if (ctrl_recvd == MSG_REJECT || ctrl_recvd == MSG_WAIT) {
+ } else if (IS_CTRL(rx_msg, MSG_REJECT) ||
+ IS_CTRL(rx_msg, MSG_WAIT)) {
if (pd->in_explicit_contract)
usbpd_set_state(pd, PE_SNK_READY);
else
usbpd_set_state(pd,
PE_SNK_WAIT_FOR_CAPABILITIES);
- } else if (pd->rx_msg_type) {
+ } else if (rx_msg) {
usbpd_err(&pd->dev, "Invalid response to sink request\n");
usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
} else {
@@ -1724,7 +1767,7 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_SNK_TRANSITION_SINK:
- if (ctrl_recvd == MSG_PS_RDY) {
+ if (IS_CTRL(rx_msg, MSG_PS_RDY)) {
val.intval = pd->requested_voltage;
power_supply_set_property(pd->usb_psy,
pd->requested_voltage >= pd->current_voltage ?
@@ -1745,9 +1788,14 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_SNK_READY:
- if (data_recvd == MSG_SOURCE_CAPABILITIES) {
+ if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) {
+ /* save the PDOs so userspace can further evaluate */
+ memcpy(&pd->received_pdos, rx_msg->payload,
+ sizeof(pd->received_pdos));
+ pd->src_cap_id++;
+
usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY);
- } else if (ctrl_recvd == MSG_GET_SINK_CAP) {
+ } else if (IS_CTRL(rx_msg, MSG_GET_SINK_CAP)) {
ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES,
default_snk_caps,
ARRAY_SIZE(default_snk_caps), SOP_MSG);
@@ -1755,7 +1803,7 @@ static void usbpd_sm(struct work_struct *w)
usbpd_err(&pd->dev, "Error sending Sink Caps\n");
usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
}
- } else if (ctrl_recvd == MSG_GET_SOURCE_CAP) {
+ } else if (IS_CTRL(rx_msg, MSG_GET_SOURCE_CAP)) {
ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES,
default_src_caps,
ARRAY_SIZE(default_src_caps), SOP_MSG);
@@ -1764,7 +1812,7 @@ static void usbpd_sm(struct work_struct *w)
usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
break;
}
- } else if (ctrl_recvd == MSG_DR_SWAP) {
+ } else if (IS_CTRL(rx_msg, MSG_DR_SWAP)) {
if (pd->vdm_state == MODE_ENTERED) {
usbpd_set_state(pd, PE_SNK_HARD_RESET);
break;
@@ -1779,7 +1827,7 @@ static void usbpd_sm(struct work_struct *w)
dr_swap(pd);
kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
- } else if (ctrl_recvd == MSG_PR_SWAP) {
+ } else if (IS_CTRL(rx_msg, MSG_PR_SWAP)) {
/* lock in current mode */
set_power_role(pd, pd->current_pr);
@@ -1794,7 +1842,7 @@ static void usbpd_sm(struct work_struct *w)
pd->in_pr_swap = true;
usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF);
break;
- } else if (ctrl_recvd == MSG_VCONN_SWAP) {
+ } else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP)) {
/*
* if VCONN is connected to VBUS, make sure we are
* not in high voltage contract, otherwise reject.
@@ -1821,16 +1869,14 @@ static void usbpd_sm(struct work_struct *w)
vconn_swap(pd);
} else {
- if (data_recvd == MSG_VDM)
- handle_vdm_rx(pd);
+ if (IS_DATA(rx_msg, MSG_VDM))
+ handle_vdm_rx(pd, rx_msg);
else
handle_vdm_tx(pd);
}
break;
case PE_SNK_TRANSITION_TO_DEFAULT:
- pd->hard_reset = false;
-
val.intval = 0;
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
@@ -1870,7 +1916,7 @@ static void usbpd_sm(struct work_struct *w)
case PE_SRC_SEND_SOFT_RESET:
case PE_SNK_SEND_SOFT_RESET:
- if (ctrl_recvd == MSG_ACCEPT) {
+ if (IS_CTRL(rx_msg, MSG_ACCEPT)) {
usbpd_set_state(pd, pd->current_pr == PR_SRC ?
PE_SRC_SEND_CAPABILITIES :
PE_SNK_WAIT_FOR_CAPABILITIES);
@@ -1907,7 +1953,7 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_DRS_SEND_DR_SWAP:
- if (ctrl_recvd == MSG_ACCEPT)
+ if (IS_CTRL(rx_msg, MSG_ACCEPT))
dr_swap(pd);
usbpd_set_state(pd, pd->current_pr == PR_SRC ?
@@ -1915,7 +1961,7 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_PRS_SRC_SNK_SEND_SWAP:
- if (ctrl_recvd != MSG_ACCEPT) {
+ if (!IS_CTRL(rx_msg, MSG_ACCEPT)) {
pd->current_state = PE_SRC_READY;
break;
}
@@ -1950,14 +1996,14 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_PRS_SRC_SNK_WAIT_SOURCE_ON:
- if (ctrl_recvd == MSG_PS_RDY)
+ if (IS_CTRL(rx_msg, MSG_PS_RDY))
usbpd_set_state(pd, PE_SNK_STARTUP);
else
usbpd_set_state(pd, PE_ERROR_RECOVERY);
break;
case PE_PRS_SNK_SRC_SEND_SWAP:
- if (ctrl_recvd != MSG_ACCEPT) {
+ if (!IS_CTRL(rx_msg, MSG_ACCEPT)) {
pd->current_state = PE_SNK_READY;
break;
}
@@ -1967,7 +2013,7 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_PRS_SNK_SRC_TRANSITION_TO_OFF:
- if (ctrl_recvd != MSG_PS_RDY) {
+ if (!IS_CTRL(rx_msg, MSG_PS_RDY)) {
usbpd_set_state(pd, PE_ERROR_RECOVERY);
break;
}
@@ -1997,7 +2043,7 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_VCS_WAIT_FOR_VCONN:
- if (ctrl_recvd == MSG_PS_RDY) {
+ if (IS_CTRL(rx_msg, MSG_PS_RDY)) {
/*
* hopefully redundant check but in case not enabled
* avoids unbalanced regulator disable count
@@ -2022,10 +2068,9 @@ static void usbpd_sm(struct work_struct *w)
break;
}
- /* Rx message should have been consumed now */
- pd->rx_msg_type = pd->rx_msg_len = 0;
-
sm_done:
+ kfree(rx_msg);
+
if (!pd->sm_queued)
pm_relax(&pd->dev);
}
@@ -2117,11 +2162,11 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
* During hard reset when VBUS goes to 0 the CC logic
* will report this as a disconnection. In those cases
* it can be ignored, however the downside is that
- * pd->hard_reset can be momentarily true even when a
- * non-PD capable source is attached, and can't be
- * distinguished from a physical disconnect. In that
- * case, allow for the common case of disconnecting
- * from an SDP.
+ * we can also happen to be in the SNK_Transition_to_default
+ * state due to a hard reset attempt even with a non-PD
+ * capable source, in which a physical disconnect may get
+ * masked. In that case, allow for the common case of
+ * disconnecting from an SDP.
*
* The less common case is a PD-capable SDP which will
* result in a hard reset getting treated like a
@@ -2662,6 +2707,8 @@ struct usbpd *usbpd_create(struct device *parent)
pd->current_dr = DR_NONE;
list_add_tail(&pd->instance, &_usbpd);
+ spin_lock_init(&pd->rx_lock);
+ INIT_LIST_HEAD(&pd->rx_q);
INIT_LIST_HEAD(&pd->svid_handlers);
/* force read initial power_supply values */
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index bdc0f2f24f19..a2b43a6e7fa7 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -108,6 +108,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */
{ USB_DEVICE(0x10C4, 0x8281) }, /* Nanotec Plug & Drive */
{ USB_DEVICE(0x10C4, 0x8293) }, /* Telegesis ETRX2USB */
+ { USB_DEVICE(0x10C4, 0x82F4) }, /* Starizona MicroTouch */
{ USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */
{ USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */
{ USB_DEVICE(0x10C4, 0x8382) }, /* Cygnal Integrated Products, Inc. */
@@ -117,6 +118,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */
{ USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */
{ USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */
+ { USB_DEVICE(0x10C4, 0x84B6) }, /* Starizona Hyperion */
{ USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */
{ USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */
{ USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */
@@ -140,6 +142,8 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0xF004) }, /* Elan Digital Systems USBcount50 */
{ USB_DEVICE(0x10C5, 0xEA61) }, /* Silicon Labs MobiData GPRS USB Modem */
{ USB_DEVICE(0x10CE, 0xEA6A) }, /* Silicon Labs MobiData GPRS USB Modem 100EU */
+ { USB_DEVICE(0x12B8, 0xEC60) }, /* Link G4 ECU */
+ { USB_DEVICE(0x12B8, 0xEC62) }, /* Link G4+ ECU */
{ USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */
{ USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */
{ USB_DEVICE(0x166A, 0x0201) }, /* Clipsal 5500PACA C-Bus Pascal Automation Controller */
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index c0866971db2b..1947ea0e0988 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -2856,14 +2856,16 @@ static int edge_startup(struct usb_serial *serial)
/* not set up yet, so do it now */
edge_serial->interrupt_read_urb =
usb_alloc_urb(0, GFP_KERNEL);
- if (!edge_serial->interrupt_read_urb)
- return -ENOMEM;
+ if (!edge_serial->interrupt_read_urb) {
+ response = -ENOMEM;
+ break;
+ }
edge_serial->interrupt_in_buffer =
kmalloc(buffer_size, GFP_KERNEL);
if (!edge_serial->interrupt_in_buffer) {
- usb_free_urb(edge_serial->interrupt_read_urb);
- return -ENOMEM;
+ response = -ENOMEM;
+ break;
}
edge_serial->interrupt_in_endpoint =
endpoint->bEndpointAddress;
@@ -2891,14 +2893,16 @@ static int edge_startup(struct usb_serial *serial)
/* not set up yet, so do it now */
edge_serial->read_urb =
usb_alloc_urb(0, GFP_KERNEL);
- if (!edge_serial->read_urb)
- return -ENOMEM;
+ if (!edge_serial->read_urb) {
+ response = -ENOMEM;
+ break;
+ }
edge_serial->bulk_in_buffer =
kmalloc(buffer_size, GFP_KERNEL);
if (!edge_serial->bulk_in_buffer) {
- usb_free_urb(edge_serial->read_urb);
- return -ENOMEM;
+ response = -ENOMEM;
+ break;
}
edge_serial->bulk_in_endpoint =
endpoint->bEndpointAddress;
@@ -2924,9 +2928,22 @@ static int edge_startup(struct usb_serial *serial)
}
}
- if (!interrupt_in_found || !bulk_in_found || !bulk_out_found) {
- dev_err(ddev, "Error - the proper endpoints were not found!\n");
- return -ENODEV;
+ if (response || !interrupt_in_found || !bulk_in_found ||
+ !bulk_out_found) {
+ if (!response) {
+ dev_err(ddev, "expected endpoints not found\n");
+ response = -ENODEV;
+ }
+
+ usb_free_urb(edge_serial->interrupt_read_urb);
+ kfree(edge_serial->interrupt_in_buffer);
+
+ usb_free_urb(edge_serial->read_urb);
+ kfree(edge_serial->bulk_in_buffer);
+
+ kfree(edge_serial);
+
+ return response;
}
/* start interrupt read for this edgeport this interrupt will
@@ -2949,16 +2966,9 @@ static void edge_disconnect(struct usb_serial *serial)
{
struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
- /* stop reads and writes on all ports */
- /* free up our endpoint stuff */
if (edge_serial->is_epic) {
usb_kill_urb(edge_serial->interrupt_read_urb);
- usb_free_urb(edge_serial->interrupt_read_urb);
- kfree(edge_serial->interrupt_in_buffer);
-
usb_kill_urb(edge_serial->read_urb);
- usb_free_urb(edge_serial->read_urb);
- kfree(edge_serial->bulk_in_buffer);
}
}
@@ -2971,6 +2981,16 @@ static void edge_release(struct usb_serial *serial)
{
struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+ if (edge_serial->is_epic) {
+ usb_kill_urb(edge_serial->interrupt_read_urb);
+ usb_free_urb(edge_serial->interrupt_read_urb);
+ kfree(edge_serial->interrupt_in_buffer);
+
+ usb_kill_urb(edge_serial->read_urb);
+ usb_free_urb(edge_serial->read_urb);
+ kfree(edge_serial->bulk_in_buffer);
+ }
+
kfree(edge_serial);
}
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index e07b15ed5814..7faa901ee47f 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -2376,6 +2376,10 @@ static void keyspan_release(struct usb_serial *serial)
s_priv = usb_get_serial_data(serial);
+ /* Make sure to unlink the URBs submitted in attach. */
+ usb_kill_urb(s_priv->instat_urb);
+ usb_kill_urb(s_priv->indat_urb);
+
usb_free_urb(s_priv->instat_urb);
usb_free_urb(s_priv->indat_urb);
usb_free_urb(s_priv->glocont_urb);
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 78b4f64c6b00..06c7dbc1c802 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -2007,6 +2007,7 @@ static void mos7720_release(struct usb_serial *serial)
urblist_entry)
usb_unlink_urb(urbtrack->urb);
spin_unlock_irqrestore(&mos_parport->listlock, flags);
+ parport_del_port(mos_parport->pp);
kref_put(&mos_parport->ref_count, destroy_mos_parport);
}
diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c
index 31a8b47f1ac6..c6596cbcc4b6 100644
--- a/drivers/usb/serial/mxuport.c
+++ b/drivers/usb/serial/mxuport.c
@@ -1259,6 +1259,15 @@ static int mxuport_attach(struct usb_serial *serial)
return 0;
}
+static void mxuport_release(struct usb_serial *serial)
+{
+ struct usb_serial_port *port0 = serial->port[0];
+ struct usb_serial_port *port1 = serial->port[1];
+
+ usb_serial_generic_close(port1);
+ usb_serial_generic_close(port0);
+}
+
static int mxuport_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct mxuport_port *mxport = usb_get_serial_port_data(port);
@@ -1361,6 +1370,7 @@ static struct usb_serial_driver mxuport_device = {
.probe = mxuport_probe,
.port_probe = mxuport_port_probe,
.attach = mxuport_attach,
+ .release = mxuport_release,
.calc_num_ports = mxuport_calc_num_ports,
.open = mxuport_open,
.close = mxuport_close,
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index c6f497f16526..d96d423d00e6 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -375,18 +375,22 @@ static void option_instat_callback(struct urb *urb);
#define HAIER_PRODUCT_CE81B 0x10f8
#define HAIER_PRODUCT_CE100 0x2009
-/* Cinterion (formerly Siemens) products */
-#define SIEMENS_VENDOR_ID 0x0681
-#define CINTERION_VENDOR_ID 0x1e2d
+/* Gemalto's Cinterion products (formerly Siemens) */
+#define SIEMENS_VENDOR_ID 0x0681
+#define CINTERION_VENDOR_ID 0x1e2d
+#define CINTERION_PRODUCT_HC25_MDMNET 0x0040
#define CINTERION_PRODUCT_HC25_MDM 0x0047
-#define CINTERION_PRODUCT_HC25_MDMNET 0x0040
+#define CINTERION_PRODUCT_HC28_MDMNET 0x004A /* same for HC28J */
#define CINTERION_PRODUCT_HC28_MDM 0x004C
-#define CINTERION_PRODUCT_HC28_MDMNET 0x004A /* same for HC28J */
#define CINTERION_PRODUCT_EU3_E 0x0051
#define CINTERION_PRODUCT_EU3_P 0x0052
#define CINTERION_PRODUCT_PH8 0x0053
#define CINTERION_PRODUCT_AHXX 0x0055
#define CINTERION_PRODUCT_PLXX 0x0060
+#define CINTERION_PRODUCT_PH8_2RMNET 0x0082
+#define CINTERION_PRODUCT_PH8_AUDIO 0x0083
+#define CINTERION_PRODUCT_AHXX_2RMNET 0x0084
+#define CINTERION_PRODUCT_AHXX_AUDIO 0x0085
/* Olivetti products */
#define OLIVETTI_VENDOR_ID 0x0b3c
@@ -633,6 +637,10 @@ static const struct option_blacklist_info telit_le922_blacklist_usbcfg3 = {
.reserved = BIT(1) | BIT(2) | BIT(3),
};
+static const struct option_blacklist_info cinterion_rmnet2_blacklist = {
+ .reserved = BIT(4) | BIT(5),
+};
+
static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
@@ -1602,7 +1610,79 @@ static const struct usb_device_id option_ids[] = {
.driver_info = (kernel_ulong_t)&net_intf3_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&net_intf3_blacklist },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffe9, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff42, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff43, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff44, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff45, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff46, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff47, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff48, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff49, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4a, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4b, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4c, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4d, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4e, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4f, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff50, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff51, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff52, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff53, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff54, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff55, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff56, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff57, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff58, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff59, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5a, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5b, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5c, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5d, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5e, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5f, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff60, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff61, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff62, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff63, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff64, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff65, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff66, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff67, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff68, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff69, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6a, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6b, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6c, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6d, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6e, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6f, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff70, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff71, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff72, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff73, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff74, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff75, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff76, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff77, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff78, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff79, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7a, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7b, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7c, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7d, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7e, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7f, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff80, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff81, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff82, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff83, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff84, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff85, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff86, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff87, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff88, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff89, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8a, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8b, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8c, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8d, 0xff, 0xff, 0xff) },
@@ -1613,6 +1693,61 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff92, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff93, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff94, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff9f, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa0, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa1, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa2, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa3, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa4, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa5, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa6, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa7, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa8, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa9, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffaa, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffab, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffac, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffae, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffaf, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb0, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb1, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb2, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb3, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb4, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb5, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb6, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb7, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb8, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb9, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffba, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbb, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbc, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbd, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbe, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbf, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc0, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc1, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc2, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc3, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc4, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc5, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc6, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc7, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc8, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc9, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffca, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcb, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcc, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcd, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffce, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcf, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd0, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd1, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd2, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd3, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd4, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd5, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffe9, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffec, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffee, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfff6, 0xff, 0xff, 0xff) },
@@ -1712,7 +1847,13 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX, 0xff) },
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PLXX),
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
- { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) },
+ { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_2RMNET, 0xff),
+ .driver_info = (kernel_ulong_t)&cinterion_rmnet2_blacklist },
+ { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_AUDIO, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_2RMNET, 0xff) },
+ { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_AUDIO, 0xff) },
+ { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) },
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDMNET) },
{ USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC25_MDM) },
{ USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC25_MDMNET) },
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index 504f5bff79c0..b18974cbd995 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -141,6 +141,7 @@ static void qt2_release(struct usb_serial *serial)
serial_priv = usb_get_serial_data(serial);
+ usb_kill_urb(serial_priv->read_urb);
usb_free_urb(serial_priv->read_urb);
kfree(serial_priv->read_buffer);
kfree(serial_priv);
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 9baf081174ce..e26e32169a36 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -811,6 +811,7 @@ static int uas_slave_configure(struct scsi_device *sdev)
if (devinfo->flags & US_FL_BROKEN_FUA)
sdev->broken_fua = 1;
+ scsi_change_queue_depth(sdev, devinfo->qdepth - 2);
return 0;
}
diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c
index facaaf003f19..e40da7759a0e 100644
--- a/drivers/usb/usbip/usbip_common.c
+++ b/drivers/usb/usbip/usbip_common.c
@@ -741,6 +741,17 @@ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
if (!(size > 0))
return 0;
+ if (size > urb->transfer_buffer_length) {
+ /* should not happen, probably malicious packet */
+ if (ud->side == USBIP_STUB) {
+ usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+ return 0;
+ } else {
+ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+ return -EPIPE;
+ }
+ }
+
ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size);
if (ret != size) {
dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret);
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index 42ea4028cfe1..9868d8a5c1ed 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -2249,7 +2249,6 @@ config XEN_FBDEV_FRONTEND
select FB_SYS_IMAGEBLIT
select FB_SYS_FOPS
select FB_DEFERRED_IO
- select INPUT_XEN_KBDDEV_FRONTEND if INPUT_MISC
select XEN_XENBUS_FRONTEND
default y
help
diff --git a/drivers/video/fbdev/da8xx-fb.c b/drivers/video/fbdev/da8xx-fb.c
index 0081725c6b5b..d00510029c93 100644
--- a/drivers/video/fbdev/da8xx-fb.c
+++ b/drivers/video/fbdev/da8xx-fb.c
@@ -209,8 +209,7 @@ static struct fb_videomode known_lcd_panels[] = {
.lower_margin = 2,
.hsync_len = 0,
.vsync_len = 0,
- .sync = FB_SYNC_CLK_INVERT |
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .sync = FB_SYNC_CLK_INVERT,
},
/* Sharp LK043T1DG01 */
[1] = {
@@ -224,7 +223,7 @@ static struct fb_videomode known_lcd_panels[] = {
.lower_margin = 2,
.hsync_len = 41,
.vsync_len = 10,
- .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .sync = 0,
.flag = 0,
},
[2] = {
@@ -239,7 +238,7 @@ static struct fb_videomode known_lcd_panels[] = {
.lower_margin = 10,
.hsync_len = 10,
.vsync_len = 10,
- .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .sync = 0,
.flag = 0,
},
[3] = {
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index 1e93a5b2e9ba..5a24a1995af9 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -178,6 +178,7 @@ enum mdss_hw_capabilities {
MDSS_CAPS_CWB_SUPPORTED,
MDSS_CAPS_MDP_VOTE_CLK_NOT_SUPPORTED,
MDSS_CAPS_AVR_SUPPORTED,
+ MDSS_CAPS_SEC_DETACH_SMMU,
MDSS_CAPS_MAX,
};
@@ -221,6 +222,7 @@ struct mdss_smmu_client {
bool domain_attached;
bool handoff_pending;
void __iomem *mmu_base;
+ int domain;
};
struct mdss_mdp_qseed3_lut_tbl {
@@ -327,6 +329,7 @@ struct mdss_data_type {
u32 wfd_mode;
u32 has_no_lut_read;
atomic_t sd_client_count;
+ atomic_t sc_client_count;
u8 has_wb_ad;
u8 has_non_scalar_rgb;
bool has_src_split;
@@ -519,6 +522,8 @@ struct mdss_data_type {
u32 max_dest_scaler_input_width;
u32 max_dest_scaler_output_width;
struct mdss_mdp_destination_scaler *ds;
+ u32 sec_disp_en;
+ u32 sec_cam_en;
};
extern struct mdss_data_type *mdss_res;
@@ -579,6 +584,14 @@ static inline int mdss_get_sd_client_cnt(void)
return atomic_read(&mdss_res->sd_client_count);
}
+static inline int mdss_get_sc_client_cnt(void)
+{
+ if (!mdss_res)
+ return 0;
+ else
+ return atomic_read(&mdss_res->sc_client_count);
+}
+
static inline void mdss_set_quirk(struct mdss_data_type *mdata,
enum mdss_hw_quirk bit)
{
diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c
index 79980acc2201..8663797f1730 100644
--- a/drivers/video/fbdev/msm/mdss_debug.c
+++ b/drivers/video/fbdev/msm/mdss_debug.c
@@ -39,6 +39,9 @@
#define PANEL_CMD_MIN_TX_COUNT 2
#define PANEL_DATA_NODE_LEN 80
+/* Hex number + whitespace */
+#define NEXT_VALUE_OFFSET 3
+
#define INVALID_XIN_ID 0xFF
static char panel_reg[2] = {DEFAULT_READ_PANEL_POWER_MODE_REG, 0x00};
@@ -81,7 +84,7 @@ static ssize_t panel_debug_base_offset_write(struct file *file,
buf[count] = 0; /* end of string */
- if (sscanf(buf, "%x %d", &off, &cnt) != 2)
+ if (sscanf(buf, "%x %u", &off, &cnt) != 2)
return -EFAULT;
if (off > dbg->max_offset)
@@ -129,7 +132,7 @@ static ssize_t panel_debug_base_reg_write(struct file *file,
struct mdss_debug_base *dbg = file->private_data;
char buf[PANEL_TX_MAX_BUF] = {0x0};
char reg[PANEL_TX_MAX_BUF] = {0x0};
- u32 len = 0, step = 0, value = 0;
+ u32 len = 0, value = 0;
char *bufp;
struct mdss_data_type *mdata = mdss_res;
@@ -152,13 +155,21 @@ static ssize_t panel_debug_base_reg_write(struct file *file,
buf[count] = 0; /* end of string */
bufp = buf;
- while (sscanf(bufp, "%x%n", &value, &step) > 0) {
+ /* End of a hex value in given string */
+ bufp[NEXT_VALUE_OFFSET - 1] = 0;
+ while (kstrtouint(bufp, 16, &value) == 0) {
reg[len++] = value;
if (len >= PANEL_TX_MAX_BUF) {
pr_err("wrong input reg len\n");
return -EFAULT;
}
- bufp += step;
+ bufp += NEXT_VALUE_OFFSET;
+ if ((bufp >= (buf + count)) || (bufp < buf)) {
+ pr_warn("%s,buffer out-of-bounds\n", __func__);
+ break;
+ }
+ /* End of a hex value in given string */
+ bufp[NEXT_VALUE_OFFSET - 1] = 0;
}
if (len < PANEL_CMD_MIN_TX_COUNT) {
pr_err("wrong input reg len\n");
@@ -203,6 +214,7 @@ static ssize_t panel_debug_base_reg_read(struct file *file,
struct mdss_panel_data *panel_data = ctl->panel_data;
struct mdss_dsi_ctrl_pdata *ctrl_pdata = container_of(panel_data,
struct mdss_dsi_ctrl_pdata, panel_data);
+ int rc = -EFAULT;
if (!dbg)
return -ENODEV;
@@ -221,7 +233,8 @@ static ssize_t panel_debug_base_reg_read(struct file *file,
if (!rx_buf || !panel_reg_buf) {
pr_err("not enough memory to hold panel reg dump\n");
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto read_reg_fail;
}
if (mdata->debug_inf.debug_enable_clock)
@@ -260,8 +273,7 @@ static ssize_t panel_debug_base_reg_read(struct file *file,
read_reg_fail:
kfree(rx_buf);
kfree(panel_reg_buf);
- return -EFAULT;
-
+ return rc;
}
static const struct file_operations panel_off_fops = {
@@ -739,11 +751,11 @@ static ssize_t mdss_debug_factor_write(struct file *file,
if (strnchr(buf, count, '/')) {
/* Parsing buf as fraction */
- if (sscanf(buf, "%d/%d", &numer, &denom) != 2)
+ if (sscanf(buf, "%u/%u", &numer, &denom) != 2)
return -EFAULT;
} else {
/* Parsing buf as percentage */
- if (sscanf(buf, "%d", &numer) != 1)
+ if (kstrtouint(buf, 0, &numer))
return -EFAULT;
denom = 100;
}
@@ -1051,7 +1063,7 @@ static ssize_t mdss_debug_perf_bw_limit_write(struct file *file,
if (strnchr(buf, count, ' ')) {
/* Parsing buf */
- if (sscanf(buf, "%d %d", &mode, &val) != 2)
+ if (sscanf(buf, "%u %u", &mode, &val) != 2)
return -EFAULT;
}
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
index 516cbdc9192b..29fc4e6fd65b 100644
--- a/drivers/video/fbdev/msm/mdss_dp.c
+++ b/drivers/video/fbdev/msm/mdss_dp.c
@@ -45,6 +45,11 @@
#define VDDA_UA_ON_LOAD 100000 /* uA units */
#define VDDA_UA_OFF_LOAD 100 /* uA units */
+struct mdss_dp_attention_node {
+ u32 vdo;
+ struct list_head list;
+};
+
#define DEFAULT_VIDEO_RESOLUTION HDMI_VFRMT_640x480p60_4_3
static u32 supported_modes[] = {
HDMI_VFRMT_640x480p60_4_3,
@@ -57,6 +62,11 @@ static u32 supported_modes[] = {
HDMI_VFRMT_4096x2160p60_256_135, HDMI_EVFRMT_4096x2160p24_16_9
};
+static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv);
+static void mdss_dp_mainlink_push_idle(struct mdss_panel_data *pdata);
+static inline void mdss_dp_link_retraining(struct mdss_dp_drv_pdata *dp);
+static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp_drv);
+
static void mdss_dp_put_dt_clk_data(struct device *dev,
struct dss_module_power *module_power)
{
@@ -898,14 +908,10 @@ static int dp_audio_info_setup(struct platform_device *pdev,
}
mdss_dp_audio_setup_sdps(&dp_ctrl->ctrl_io);
- mdss_dp_audio_set_sample_rate(&dp_ctrl->ctrl_io,
- dp_ctrl->link_rate, params->sample_rate_hz);
mdss_dp_config_audio_acr_ctrl(&dp_ctrl->ctrl_io, dp_ctrl->link_rate);
mdss_dp_set_safe_to_exit_level(&dp_ctrl->ctrl_io, dp_ctrl->lane_cnt);
mdss_dp_audio_enable(&dp_ctrl->ctrl_io, true);
- dp_ctrl->wait_for_audio_comp = true;
-
return rc;
} /* dp_audio_info_setup */
@@ -928,17 +934,6 @@ static int dp_get_audio_edid_blk(struct platform_device *pdev,
return rc;
} /* dp_get_audio_edid_blk */
-static void dp_audio_codec_teardown_done(struct platform_device *pdev)
-{
- struct mdss_dp_drv_pdata *dp = platform_get_drvdata(pdev);
-
- if (!dp)
- pr_err("invalid input\n");
-
- pr_debug("audio codec teardown done\n");
- complete_all(&dp->audio_comp);
-}
-
static int mdss_dp_init_ext_disp(struct mdss_dp_drv_pdata *dp)
{
int ret = 0;
@@ -960,8 +955,6 @@ static int mdss_dp_init_ext_disp(struct mdss_dp_drv_pdata *dp)
dp_get_audio_edid_blk;
dp->ext_audio_data.codec_ops.cable_status =
dp_get_cable_status;
- dp->ext_audio_data.codec_ops.teardown_done =
- dp_audio_codec_teardown_done;
if (!dp->pdev->dev.of_node) {
pr_err("%s cannot find dp dev.of_node\n", __func__);
@@ -1042,12 +1035,10 @@ static int dp_init_panel_info(struct mdss_dp_drv_pdata *dp_drv, u32 vic)
return 0;
} /* dp_init_panel_info */
-static inline void mdss_dp_set_audio_switch_node(
- struct mdss_dp_drv_pdata *dp, int val)
+static inline void mdss_dp_ack_state(struct mdss_dp_drv_pdata *dp, int val)
{
if (dp && dp->ext_audio_data.intf_ops.notify)
- dp->ext_audio_data.intf_ops.notify(dp->ext_pdev,
- val);
+ dp->ext_audio_data.intf_ops.notify(dp->ext_pdev, val);
}
/**
@@ -1148,7 +1139,7 @@ static void mdss_dp_configure_source_params(struct mdss_dp_drv_pdata *dp,
mdss_dp_fill_link_cfg(dp);
mdss_dp_mainlink_ctrl(&dp->ctrl_io, true);
mdss_dp_config_ctrl(dp);
- mdss_dp_sw_mvid_nvid(&dp->ctrl_io);
+ mdss_dp_sw_config_msa(&dp->ctrl_io, dp->link_rate, &dp->dp_cc_io);
mdss_dp_timing_cfg(&dp->ctrl_io, &dp->panel_data.panel_info);
}
@@ -1158,19 +1149,27 @@ static void mdss_dp_configure_source_params(struct mdss_dp_drv_pdata *dp,
*
* Initiates training of the DP main link and checks the state of the main
* link after the training is complete.
+ *
+ * Return: error code. -EINVAL if any invalid data or -EAGAIN if retraining
+ * is required.
*/
-static void mdss_dp_train_main_link(struct mdss_dp_drv_pdata *dp)
+static int mdss_dp_train_main_link(struct mdss_dp_drv_pdata *dp)
{
+ int ret = 0;
int ready = 0;
pr_debug("enter\n");
+ ret = mdss_dp_link_train(dp);
+ if (ret)
+ goto end;
- mdss_dp_link_train(dp);
mdss_dp_wait4train(dp);
ready = mdss_dp_mainlink_ready(dp, BIT(0));
pr_debug("main link %s\n", ready ? "READY" : "NOT READY");
+end:
+ return ret;
}
static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv)
@@ -1180,33 +1179,43 @@ static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv)
struct lane_mapping ln_map;
/* wait until link training is completed */
- mutex_lock(&dp_drv->train_mutex);
-
pr_debug("enter\n");
- orientation = usbpd_get_plug_orientation(dp_drv->pd);
- pr_debug("plug orientation = %d\n", orientation);
+ do {
+ if (ret == -EAGAIN) {
+ mdss_dp_mainlink_push_idle(&dp_drv->panel_data);
+ mdss_dp_off_irq(dp_drv);
+ }
- ret = mdss_dp_get_lane_mapping(dp_drv, orientation, &ln_map);
- if (ret)
- goto exit;
+ mutex_lock(&dp_drv->train_mutex);
- mdss_dp_phy_share_lane_config(&dp_drv->phy_io,
- orientation, dp_drv->dpcd.max_lane_count);
+ orientation = usbpd_get_plug_orientation(dp_drv->pd);
+ pr_debug("plug orientation = %d\n", orientation);
- ret = mdss_dp_enable_mainlink_clocks(dp_drv);
- if (ret)
- goto exit;
+ ret = mdss_dp_get_lane_mapping(dp_drv, orientation, &ln_map);
+ if (ret)
+ goto exit;
- mdss_dp_mainlink_reset(&dp_drv->ctrl_io);
+ mdss_dp_phy_share_lane_config(&dp_drv->phy_io,
+ orientation, dp_drv->dpcd.max_lane_count);
- reinit_completion(&dp_drv->idle_comp);
+ ret = mdss_dp_enable_mainlink_clocks(dp_drv);
+ if (ret)
+ goto exit;
- mdss_dp_configure_source_params(dp_drv, &ln_map);
+ mdss_dp_mainlink_reset(&dp_drv->ctrl_io);
- mdss_dp_train_main_link(dp_drv);
+ reinit_completion(&dp_drv->idle_comp);
+
+ mdss_dp_configure_source_params(dp_drv, &ln_map);
+
+ dp_drv->power_on = true;
+
+ ret = mdss_dp_train_main_link(dp_drv);
+
+ mutex_unlock(&dp_drv->train_mutex);
+ } while (ret == -EAGAIN);
- dp_drv->power_on = true;
pr_debug("end\n");
exit:
@@ -1275,12 +1284,18 @@ int mdss_dp_on_hpd(struct mdss_dp_drv_pdata *dp_drv)
mdss_dp_configure_source_params(dp_drv, &ln_map);
link_training:
- mdss_dp_train_main_link(dp_drv);
+ dp_drv->power_on = true;
+
+ if (-EAGAIN == mdss_dp_train_main_link(dp_drv)) {
+ mutex_unlock(&dp_drv->train_mutex);
+
+ mdss_dp_link_retraining(dp_drv);
+ return 0;
+ }
dp_drv->cont_splash = 0;
dp_drv->power_on = true;
- mdss_dp_set_audio_switch_node(dp_drv, true);
pr_debug("End-\n");
exit:
@@ -1303,17 +1318,29 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
return mdss_dp_on_hpd(dp_drv);
}
-static void mdss_dp_reset_test_data(struct mdss_dp_drv_pdata *dp)
+static inline void mdss_dp_reset_test_data(struct mdss_dp_drv_pdata *dp)
{
dp->test_data = (const struct dpcd_test_request){ 0 };
}
-static bool mdss_dp_is_link_training_requested(struct mdss_dp_drv_pdata *dp)
+static inline bool mdss_dp_is_link_status_updated(struct mdss_dp_drv_pdata *dp)
+{
+ return dp->link_status.link_status_updated;
+}
+
+static inline bool mdss_dp_is_downstream_port_status_changed(
+ struct mdss_dp_drv_pdata *dp)
+{
+ return dp->link_status.downstream_port_status_changed;
+}
+
+static inline bool mdss_dp_is_link_training_requested(
+ struct mdss_dp_drv_pdata *dp)
{
return (dp->test_data.test_requested == TEST_LINK_TRAINING);
}
-static bool mdss_dp_soft_hpd_reset(struct mdss_dp_drv_pdata *dp)
+static inline bool mdss_dp_soft_hpd_reset(struct mdss_dp_drv_pdata *dp)
{
return mdss_dp_is_link_training_requested(dp) &&
dp->alt_mode.dp_status.hpd_irq;
@@ -1386,6 +1413,7 @@ static int mdss_dp_off_hpd(struct mdss_dp_drv_pdata *dp_drv)
dp_drv->dp_initialized = false;
dp_drv->power_on = false;
+ mdss_dp_ack_state(dp_drv, false);
mutex_unlock(&dp_drv->train_mutex);
pr_debug("DP off done\n");
@@ -1409,52 +1437,30 @@ int mdss_dp_off(struct mdss_panel_data *pdata)
return mdss_dp_off_hpd(dp);
}
-static void mdss_dp_send_cable_notification(
+static int mdss_dp_send_cable_notification(
struct mdss_dp_drv_pdata *dp, int val)
{
+ int ret = 0;
if (!dp) {
DEV_ERR("%s: invalid input\n", __func__);
- return;
+ ret = -EINVAL;
+ goto end;
}
if (dp && dp->ext_audio_data.intf_ops.hpd)
- dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev,
+ ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev,
dp->ext_audio_data.type, val);
-}
-
-static void mdss_dp_audio_codec_wait(struct mdss_dp_drv_pdata *dp)
-{
- const int audio_completion_timeout_ms = HZ * 3;
- int ret = 0;
- if (!dp->wait_for_audio_comp)
- return;
-
- reinit_completion(&dp->audio_comp);
- ret = wait_for_completion_timeout(&dp->audio_comp,
- audio_completion_timeout_ms);
- if (ret <= 0)
- pr_warn("audio codec teardown timed out\n");
-
- dp->wait_for_audio_comp = false;
+end:
+ return ret;
}
-static void mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, bool enable)
+static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, bool enable)
{
- if (enable) {
- mdss_dp_send_cable_notification(dp, enable);
- } else {
- mdss_dp_set_audio_switch_node(dp, enable);
- mdss_dp_audio_codec_wait(dp);
- mdss_dp_send_cable_notification(dp, enable);
- }
-
- pr_debug("notify state %s done\n",
- enable ? "ENABLE" : "DISABLE");
+ return mdss_dp_send_cable_notification(dp, enable);
}
-
static int mdss_dp_edid_init(struct mdss_panel_data *pdata)
{
struct mdss_dp_drv_pdata *dp_drv = NULL;
@@ -1558,6 +1564,7 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata)
goto edid_error;
}
+ mdss_dp_update_cable_status(dp_drv, true);
mdss_dp_notify_clients(dp_drv, true);
dp_drv->dp_initialized = true;
@@ -1610,22 +1617,18 @@ end:
return rc;
}
-static void mdss_dp_hdcp_cb(void *ptr, enum hdcp_states status)
+static void mdss_dp_hdcp_cb_work(struct work_struct *work)
{
- struct mdss_dp_drv_pdata *dp = ptr;
+ struct mdss_dp_drv_pdata *dp;
+ struct delayed_work *dw = to_delayed_work(work);
struct hdcp_ops *ops;
int rc = 0;
- if (!dp) {
- pr_debug("invalid input\n");
- return;
- }
+ dp = container_of(dw, struct mdss_dp_drv_pdata, hdcp_cb_work);
ops = dp->hdcp.ops;
- mutex_lock(&dp->train_mutex);
-
- switch (status) {
+ switch (dp->hdcp_status) {
case HDCP_STATE_AUTHENTICATED:
pr_debug("hdcp authenticated\n");
dp->hdcp.auth_state = true;
@@ -1648,8 +1651,20 @@ static void mdss_dp_hdcp_cb(void *ptr, enum hdcp_states status)
default:
break;
}
+}
- mutex_unlock(&dp->train_mutex);
+static void mdss_dp_hdcp_cb(void *ptr, enum hdcp_states status)
+{
+ struct mdss_dp_drv_pdata *dp = ptr;
+
+ if (!dp) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ dp->hdcp_status = status;
+
+ queue_delayed_work(dp->workq, &dp->hdcp_cb_work, HZ/4);
}
static int mdss_dp_hdcp_init(struct mdss_panel_data *pdata)
@@ -1687,19 +1702,19 @@ static int mdss_dp_hdcp_init(struct mdss_panel_data *pdata)
hdcp_init_data.sec_access = true;
hdcp_init_data.client_id = HDCP_CLIENT_DP;
- dp_drv->hdcp.data = hdcp_1x_init(&hdcp_init_data);
- if (IS_ERR_OR_NULL(dp_drv->hdcp.data)) {
+ dp_drv->hdcp.hdcp1 = hdcp_1x_init(&hdcp_init_data);
+ if (IS_ERR_OR_NULL(dp_drv->hdcp.hdcp1)) {
pr_err("Error hdcp init\n");
rc = -EINVAL;
goto error;
}
- dp_drv->panel_data.panel_info.hdcp_1x_data = dp_drv->hdcp.data;
+ dp_drv->panel_data.panel_info.hdcp_1x_data = dp_drv->hdcp.hdcp1;
pr_debug("HDCP 1.3 initialized\n");
dp_drv->hdcp.hdcp2 = dp_hdcp2p2_init(&hdcp_init_data);
- if (!IS_ERR_OR_NULL(dp_drv->hdcp.data))
+ if (!IS_ERR_OR_NULL(dp_drv->hdcp.hdcp2))
pr_debug("HDCP 2.2 initialized\n");
dp_drv->hdcp.feature_enabled = true;
@@ -1874,8 +1889,20 @@ static void mdss_dp_update_hdcp_info(struct mdss_dp_drv_pdata *dp)
}
/* update internal data about hdcp */
- dp->hdcp.data = fd;
- dp->hdcp.ops = ops;
+ if (dp->hdcp.hdcp2_present || dp->hdcp.hdcp1_present) {
+ dp->hdcp.data = fd;
+ dp->hdcp.ops = ops;
+ } else {
+ dp->hdcp.data = NULL;
+ dp->hdcp.ops = NULL;
+ }
+}
+
+static inline bool dp_is_hdcp_enabled(struct mdss_dp_drv_pdata *dp_drv)
+{
+ return dp_drv->hdcp.feature_enabled &&
+ (dp_drv->hdcp.hdcp1_present || dp_drv->hdcp.hdcp2_present) &&
+ dp_drv->hdcp.ops;
}
static int mdss_dp_event_handler(struct mdss_panel_data *pdata,
@@ -1904,13 +1931,17 @@ static int mdss_dp_event_handler(struct mdss_panel_data *pdata,
if (dp->hdcp.ops && dp->hdcp.ops->authenticate)
rc = dp->hdcp.ops->authenticate(dp->hdcp.data);
+
+ mdss_dp_ack_state(dp, true);
break;
case MDSS_EVENT_PANEL_OFF:
rc = mdss_dp_off(pdata);
break;
case MDSS_EVENT_BLANK:
- if (dp->hdcp.ops && dp->hdcp.ops->off)
+ if (dp_is_hdcp_enabled(dp) && dp->hdcp.ops->off) {
+ flush_delayed_work(&dp->hdcp_cb_work);
dp->hdcp.ops->off(dp->hdcp.data);
+ }
mdss_dp_mainlink_push_idle(pdata);
break;
@@ -2023,6 +2054,12 @@ static int mdss_retrieve_dp_ctrl_resources(struct platform_device *pdev,
return rc;
}
+ if (msm_dss_ioremap_byname(pdev, &dp_drv->dp_cc_io, "dp_mmss_cc")) {
+ pr_err("%d unable to remap dp MMSS_CC resources\n",
+ __LINE__);
+ return rc;
+ }
+
if (msm_dss_ioremap_byname(pdev, &dp_drv->qfprom_io,
"qfprom_physical"))
pr_warn("unable to remap dp qfprom resources\n");
@@ -2099,6 +2136,9 @@ static void mdss_dp_event_work(struct work_struct *work)
case EV_IDLE_PATTERNS_SENT:
mdss_dp_idle_patterns_sent(dp);
break;
+ case EV_USBPD_ATTENTION:
+ mdss_dp_handle_attention(dp);
+ break;
case EV_USBPD_DISCOVER_MODES:
usbpd_send_svdm(dp->pd, USB_C_DP_SID, USBPD_SVDM_DISCOVER_MODES,
SVDM_CMD_TYPE_INITIATOR, 0x0, 0x0, 0x0);
@@ -2187,6 +2227,11 @@ irqreturn_t dp_isr(int irq, void *ptr)
dp_aux_native_handler(dp, isr1);
}
+ if (dp->hdcp.ops && dp->hdcp.ops->isr) {
+ if (dp->hdcp.ops->isr(dp->hdcp.data))
+ pr_err("dp_hdcp_isr failed\n");
+ }
+
return IRQ_HANDLED;
}
@@ -2201,6 +2246,8 @@ static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp)
}
INIT_WORK(&dp->work, mdss_dp_event_work);
+ INIT_DELAYED_WORK(&dp->hdcp_cb_work, mdss_dp_hdcp_cb_work);
+ INIT_LIST_HEAD(&dp->attention_head);
return 0;
}
@@ -2281,7 +2328,7 @@ end:
* This function will send the test response to the sink but only after
* any previous link training has been completed.
*/
-static void mdss_dp_send_test_response(struct mdss_dp_drv_pdata *dp)
+static inline void mdss_dp_send_test_response(struct mdss_dp_drv_pdata *dp)
{
mutex_lock(&dp->train_mutex);
mdss_dp_aux_send_test_response(dp);
@@ -2303,14 +2350,20 @@ static int mdss_dp_hpd_irq_notify_clients(struct mdss_dp_drv_pdata *dp)
int ret = 0;
if (dp->hpd_irq_toggled) {
- mdss_dp_notify_clients(dp, false);
-
- reinit_completion(&dp->irq_comp);
- ret = wait_for_completion_timeout(&dp->irq_comp,
- irq_comp_timeout);
- if (ret <= 0) {
- pr_warn("irq_comp timed out\n");
- return -EINVAL;
+ dp->hpd_irq_clients_notified = true;
+
+ ret = mdss_dp_notify_clients(dp, false);
+
+ if (!IS_ERR_VALUE(ret) && ret) {
+ reinit_completion(&dp->irq_comp);
+ ret = wait_for_completion_timeout(&dp->irq_comp,
+ irq_comp_timeout);
+ if (ret <= 0) {
+ pr_warn("irq_comp timed out\n");
+ ret = -EINVAL;
+ } else {
+ ret = 0;
+ }
}
}
@@ -2318,40 +2371,132 @@ static int mdss_dp_hpd_irq_notify_clients(struct mdss_dp_drv_pdata *dp)
}
/**
+ * mdss_dp_link_retraining() - initiates link retraining
+ * @dp: Display Port Driver data
+ *
+ * This function will initiate link retraining by first notifying
+ * DP clients and triggering DP shutdown, and then enabling DP after
+ * notification is done successfully.
+ */
+static inline void mdss_dp_link_retraining(struct mdss_dp_drv_pdata *dp)
+{
+ if (mdss_dp_hpd_irq_notify_clients(dp))
+ return;
+
+ mdss_dp_on_irq(dp);
+}
+
+/**
+ * mdss_dp_process_link_status_update() - processes link status updates
+ * @dp: Display Port Driver data
+ *
+ * This function will check for changes in the link status, e.g. clock
+ * recovery done on all lanes, and trigger link training if there is a
+ * failure/error on the link.
+ *
+ * The function will return 0 if the a link status update has been processed,
+ * otherwise it will return -EINVAL.
+ */
+static int mdss_dp_process_link_status_update(struct mdss_dp_drv_pdata *dp)
+{
+ if (!mdss_dp_is_link_status_updated(dp) ||
+ (mdss_dp_aux_channel_eq_done(dp) &&
+ mdss_dp_aux_clock_recovery_done(dp)))
+ return -EINVAL;
+
+ pr_info("channel_eq_done = %d, clock_recovery_done = %d\n",
+ mdss_dp_aux_channel_eq_done(dp),
+ mdss_dp_aux_clock_recovery_done(dp));
+
+ mdss_dp_link_retraining(dp);
+
+ return 0;
+}
+
+/**
+ * mdss_dp_process_link_training_request() - processes new training requests
+ * @dp: Display Port Driver data
+ *
+ * This function will handle new link training requests that are initiated by
+ * the sink. In particular, it will update the requested lane count and link
+ * link rate, and then trigger the link retraining procedure.
+ *
+ * The function will return 0 if a link training request has been processed,
+ * otherwise it will return -EINVAL.
+ */
+static int mdss_dp_process_link_training_request(struct mdss_dp_drv_pdata *dp)
+{
+ if (!mdss_dp_is_link_training_requested(dp))
+ return -EINVAL;
+
+ mdss_dp_send_test_response(dp);
+
+ pr_info("%s link rate = 0x%x, lane count = 0x%x\n",
+ mdss_dp_get_test_name(TEST_LINK_TRAINING),
+ dp->test_data.test_link_rate,
+ dp->test_data.test_lane_count);
+ dp->dpcd.max_lane_count =
+ dp->test_data.test_lane_count;
+ dp->link_rate = dp->test_data.test_link_rate;
+
+ mdss_dp_link_retraining(dp);
+
+ return 0;
+}
+
+/**
+ * mdss_dp_process_downstream_port_status_change() - process port status changes
+ * @dp: Display Port Driver data
+ *
+ * This function will handle downstream port updates that are initiated by
+ * the sink. If the downstream port status has changed, the EDID is read via
+ * AUX.
+ *
+ * The function will return 0 if a downstream port update has been
+ * processed, otherwise it will return -EINVAL.
+ */
+static int mdss_dp_process_downstream_port_status_change(
+ struct mdss_dp_drv_pdata *dp)
+{
+ if (!mdss_dp_is_downstream_port_status_changed(dp))
+ return -EINVAL;
+
+ return mdss_dp_edid_read(dp);
+}
+
+/**
* mdss_dp_process_hpd_irq_high() - handle HPD IRQ transition to HIGH
* @dp: Display Port Driver data
*
- * This function will handle the HPD IRQ state transitions from HIGH to HIGH
- * or LOW to HIGH, indicating the start of a new test request.
+ * This function will handle the HPD IRQ state transitions from LOW to HIGH
+ * (including cases when there are back to back HPD IRQ HIGH) indicating
+ * the start of a new link training request or sink status update.
*/
-static void mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp)
+static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp)
{
- pr_debug("enter: HPD IRQ High\n");
+ int ret = 0;
dp->hpd_irq_on = true;
mdss_dp_aux_parse_sink_status_field(dp);
- if (mdss_dp_is_link_training_requested(dp)) {
- mdss_dp_send_test_response(dp);
-
- pr_info("%s requested: link rate = 0x%x, lane count = 0x%x\n",
- mdss_dp_get_test_name(TEST_LINK_TRAINING),
- dp->test_data.test_link_rate,
- dp->test_data.test_lane_count);
- dp->dpcd.max_lane_count =
- dp->test_data.test_lane_count;
- dp->link_rate = dp->test_data.test_link_rate;
+ ret = mdss_dp_process_link_training_request(dp);
+ if (!ret)
+ goto exit;
- if (mdss_dp_hpd_irq_notify_clients(dp))
- return;
+ ret = mdss_dp_process_link_status_update(dp);
+ if (!ret)
+ goto exit;
- mdss_dp_on_irq(dp);
- }
+ ret = mdss_dp_process_downstream_port_status_change(dp);
+ if (!ret)
+ goto exit;
+ pr_debug("done\n");
+exit:
mdss_dp_reset_test_data(dp);
- pr_debug("done\n");
+ return ret;
}
/**
@@ -2361,11 +2506,15 @@ static void mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp)
* This function will handle the HPD IRQ state transitions from HIGH to LOW,
* indicating the end of a test request.
*/
-static void mdss_dp_process_hpd_irq_low(struct mdss_dp_drv_pdata *dp)
+static int mdss_dp_process_hpd_irq_low(struct mdss_dp_drv_pdata *dp)
{
+ if (!dp->hpd_irq_clients_notified)
+ return -EINVAL;
+
pr_debug("enter: HPD IRQ low\n");
dp->hpd_irq_on = false;
+ dp->hpd_irq_clients_notified = false;
mdss_dp_update_cable_status(dp, false);
mdss_dp_mainlink_push_idle(&dp->panel_data);
@@ -2374,6 +2523,7 @@ static void mdss_dp_process_hpd_irq_low(struct mdss_dp_drv_pdata *dp)
mdss_dp_reset_test_data(dp);
pr_debug("done\n");
+ return 0;
}
static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd,
@@ -2381,6 +2531,7 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd,
const u32 *vdos, int num_vdos)
{
struct mdss_dp_drv_pdata *dp_drv;
+ struct mdss_dp_attention_node *node;
dp_drv = container_of(hdlr, struct mdss_dp_drv_pdata, svid_handler);
if (!dp_drv->pd) {
@@ -2408,45 +2559,14 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd,
dp_send_events(dp_drv, EV_USBPD_DP_STATUS);
break;
case USBPD_SVDM_ATTENTION:
- dp_drv->alt_mode.dp_status.response = *vdos;
- mdss_dp_usbpd_ext_dp_status(&dp_drv->alt_mode.dp_status);
-
- dp_drv->hpd_irq_toggled = dp_drv->hpd_irq_on !=
- dp_drv->alt_mode.dp_status.hpd_irq;
-
- if (dp_drv->alt_mode.dp_status.hpd_irq) {
- mdss_dp_process_hpd_irq_high(dp_drv);
- break;
- }
-
- if (dp_drv->hpd_irq_toggled
- && !dp_drv->alt_mode.dp_status.hpd_irq) {
- mdss_dp_process_hpd_irq_low(dp_drv);
- break;
- }
-
- if (!dp_drv->alt_mode.dp_status.hpd_high) {
- pr_debug("Attention: HPD low\n");
- mdss_dp_update_cable_status(dp_drv, false);
- mdss_dp_notify_clients(dp_drv, false);
- pr_debug("Attention: Notified clients\n");
- break;
- }
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ node->vdo = *vdos;
- pr_debug("Attention: HPD high\n");
+ mutex_lock(&dp_drv->attention_lock);
+ list_add_tail(&node->list, &dp_drv->attention_head);
+ mutex_unlock(&dp_drv->attention_lock);
- mdss_dp_update_cable_status(dp_drv, true);
-
- dp_drv->alt_mode.current_state |= DP_STATUS_DONE;
-
- if (dp_drv->alt_mode.current_state & DP_CONFIGURE_DONE)
- mdss_dp_host_init(&dp_drv->panel_data);
- else
- dp_send_events(dp_drv, EV_USBPD_DP_CONFIGURE);
-
- if (dp_drv->alt_mode.dp_status.hpd_irq && dp_drv->power_on &&
- dp_drv->hdcp.ops && dp_drv->hdcp.ops->isr)
- dp_drv->hdcp.ops->isr(dp_drv->hdcp.data);
+ dp_send_events(dp_drv, EV_USBPD_ATTENTION);
break;
case DP_VDM_STATUS:
dp_drv->alt_mode.dp_status.response = *vdos;
@@ -2470,6 +2590,74 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd,
}
}
+static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv)
+{
+ dp_drv->hpd_irq_toggled = dp_drv->hpd_irq_on !=
+ dp_drv->alt_mode.dp_status.hpd_irq;
+
+ if (dp_drv->alt_mode.dp_status.hpd_irq) {
+ pr_debug("Attention: hpd_irq high\n");
+
+ if (dp_drv->power_on && dp_drv->hdcp.ops &&
+ dp_drv->hdcp.ops->cp_irq)
+ dp_drv->hdcp.ops->cp_irq(dp_drv->hdcp.data);
+
+ if (!mdss_dp_process_hpd_irq_high(dp_drv))
+ return;
+ } else if (dp_drv->hpd_irq_toggled) {
+ if (!mdss_dp_process_hpd_irq_low(dp_drv))
+ return;
+ }
+
+ if (!dp_drv->alt_mode.dp_status.hpd_high) {
+ pr_debug("Attention: HPD low\n");
+ mdss_dp_update_cable_status(dp_drv, false);
+ mdss_dp_notify_clients(dp_drv, false);
+ pr_debug("Attention: Notified clients\n");
+ return;
+ }
+
+ pr_debug("Attention: HPD high\n");
+
+ mdss_dp_update_cable_status(dp_drv, true);
+
+ dp_drv->alt_mode.current_state |= DP_STATUS_DONE;
+
+ if (dp_drv->alt_mode.current_state & DP_CONFIGURE_DONE)
+ mdss_dp_host_init(&dp_drv->panel_data);
+ else
+ dp_send_events(dp_drv, EV_USBPD_DP_CONFIGURE);
+
+ pr_debug("exit\n");
+}
+
+static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp)
+{
+ int i = 0;
+
+ while (!list_empty_careful(&dp->attention_head)) {
+ struct mdss_dp_attention_node *node;
+ u32 vdo;
+
+ pr_debug("processing item %d in the list\n", ++i);
+
+ mutex_lock(&dp->attention_lock);
+ node = list_first_entry(&dp->attention_head,
+ struct mdss_dp_attention_node, list);
+
+ vdo = node->vdo;
+ list_del(&node->list);
+ mutex_unlock(&dp->attention_lock);
+
+ kzfree(node);
+
+ dp->alt_mode.dp_status.response = vdo;
+ mdss_dp_usbpd_ext_dp_status(&dp->alt_mode.dp_status);
+ mdss_dp_process_attention(dp);
+ };
+
+}
+
static int mdss_dp_usbpd_setup(struct mdss_dp_drv_pdata *dp_drv)
{
int ret = 0;
@@ -2543,6 +2731,7 @@ static int mdss_dp_probe(struct platform_device *pdev)
dp_drv->mask2 = EDP_INTR_MASK2;
mutex_init(&dp_drv->emutex);
mutex_init(&dp_drv->pd_msg_mutex);
+ mutex_init(&dp_drv->attention_lock);
mutex_init(&dp_drv->hdcp_mutex);
spin_lock_init(&dp_drv->lock);
@@ -2621,10 +2810,8 @@ static int mdss_dp_probe(struct platform_device *pdev)
mdss_dp_device_register(dp_drv);
dp_drv->inited = true;
- dp_drv->wait_for_audio_comp = false;
dp_drv->hpd_irq_on = false;
mdss_dp_reset_test_data(dp_drv);
- init_completion(&dp_drv->audio_comp);
init_completion(&dp_drv->irq_comp);
pr_debug("done\n");
@@ -2662,13 +2849,6 @@ void *mdss_dp_get_hdcp_data(struct device *dev)
return dp_drv->hdcp.data;
}
-static inline bool dp_is_hdcp_enabled(struct mdss_dp_drv_pdata *dp_drv)
-{
- return dp_drv->hdcp.feature_enabled &&
- (dp_drv->hdcp.hdcp1_present || dp_drv->hdcp.hdcp2_present) &&
- dp_drv->hdcp.ops;
-}
-
static inline bool dp_is_stream_shareable(struct mdss_dp_drv_pdata *dp_drv)
{
bool ret = 0;
diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h
index beeb4d4b1a91..04abe9221acc 100644
--- a/drivers/video/fbdev/msm/mdss_dp.h
+++ b/drivers/video/fbdev/msm/mdss_dp.h
@@ -205,6 +205,7 @@ struct dp_alt_mode {
#define EV_USBPD_DP_CONFIGURE BIT(10)
#define EV_USBPD_CC_PIN_POLARITY BIT(11)
#define EV_USBPD_EXIT_MODE BIT(12)
+#define EV_USBPD_ATTENTION BIT(13)
/* dp state ctrl */
#define ST_TRAIN_PATTERN_1 BIT(0)
@@ -228,6 +229,18 @@ struct dp_alt_mode {
#define DP_LINK_RATE_MULTIPLIER 27000000
#define DP_MAX_PIXEL_CLK_KHZ 675000
+struct downstream_port_config {
+ /* Byte 02205h */
+ bool dfp_present;
+ u32 dfp_type;
+ bool format_conversion;
+ bool detailed_cap_info_available;
+ /* Byte 02207h */
+ u32 dfp_count;
+ bool msa_timing_par_ignored;
+ bool oui_support;
+};
+
struct dpcd_cap {
char major;
char minor;
@@ -240,6 +253,7 @@ struct dpcd_cap {
u32 flags;
u32 rx_port0_buf_size;
u32 training_read_interval;/* us */
+ struct downstream_port_config downstream_port;
};
struct dpcd_link_status {
@@ -393,6 +407,7 @@ struct mdss_dp_drv_pdata {
struct dss_io_data ctrl_io;
struct dss_io_data phy_io;
struct dss_io_data tcsr_reg_io;
+ struct dss_io_data dp_cc_io;
struct dss_io_data qfprom_io;
struct dss_io_data hdcp_io;
int base_size;
@@ -437,11 +452,11 @@ struct mdss_dp_drv_pdata {
struct completion train_comp;
struct completion idle_comp;
struct completion video_comp;
- struct completion audio_comp;
struct completion irq_comp;
struct mutex aux_mutex;
struct mutex train_mutex;
struct mutex pd_msg_mutex;
+ struct mutex attention_lock;
struct mutex hdcp_mutex;
bool cable_connected;
u32 s3d_mode;
@@ -463,13 +478,14 @@ struct mdss_dp_drv_pdata {
char delay_start;
u32 bpp;
struct dp_statistic dp_stat;
- bool wait_for_audio_comp;
bool hpd_irq_on;
bool hpd_irq_toggled;
+ bool hpd_irq_clients_notified;
/* event */
struct workqueue_struct *workq;
struct work_struct work;
+ struct delayed_work hdcp_cb_work;
u32 current_event;
spinlock_t event_lock;
spinlock_t lock;
@@ -480,9 +496,12 @@ struct mdss_dp_drv_pdata {
u32 vic;
u32 new_vic;
int fb_node;
+ int hdcp_status;
struct dpcd_test_request test_data;
struct dpcd_sink_count sink_count;
+
+ struct list_head attention_head;
};
enum dp_lane_count {
@@ -585,5 +604,7 @@ int mdss_dp_aux_set_sink_power_state(struct mdss_dp_drv_pdata *ep, char state);
void mdss_dp_aux_send_test_response(struct mdss_dp_drv_pdata *ep);
void *mdss_dp_get_hdcp_data(struct device *dev);
int mdss_dp_hdcp2p2_init(struct mdss_dp_drv_pdata *dp_drv);
+bool mdss_dp_aux_clock_recovery_done(struct mdss_dp_drv_pdata *ep);
+bool mdss_dp_aux_channel_eq_done(struct mdss_dp_drv_pdata *ep);
#endif /* MDSS_DP_H */
diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c
index 4d9a110cf6af..b64518194926 100644
--- a/drivers/video/fbdev/msm/mdss_dp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_dp_aux.c
@@ -514,7 +514,7 @@ char mdss_dp_gen_link_clk(struct mdss_panel_info *pinfo, char lane_cnt)
{
const u32 encoding_factx10 = 8;
const u32 ln_to_link_ratio = 10;
- u32 min_link_rate;
+ u32 min_link_rate, reminder = 0;
char calc_link_rate = 0;
pr_debug("clk_rate=%llu, bpp= %d, lane_cnt=%d\n",
@@ -527,13 +527,19 @@ char mdss_dp_gen_link_clk(struct mdss_panel_info *pinfo, char lane_cnt)
* Any changes in the section of code should
* consider this limitation.
*/
- min_link_rate = pinfo->clk_rate
- / (lane_cnt * encoding_factx10);
+ min_link_rate = (u32)div_u64(pinfo->clk_rate,
+ (lane_cnt * encoding_factx10));
min_link_rate /= ln_to_link_ratio;
min_link_rate = (min_link_rate * pinfo->bpp);
- min_link_rate = (u32)div_u64(min_link_rate * 10,
- DP_LINK_RATE_MULTIPLIER);
+ min_link_rate = (u32)div_u64_rem(min_link_rate * 10,
+ DP_LINK_RATE_MULTIPLIER, &reminder);
+ /*
+ * To avoid any fractional values,
+ * increment the min_link_rate
+ */
+ if (reminder)
+ min_link_rate += 1;
pr_debug("min_link_rate = %d\n", min_link_rate);
if (min_link_rate <= DP_LINK_RATE_162)
@@ -801,6 +807,8 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
cap = &ep->dpcd;
bp = rp->data;
+ memset(cap, 0, sizeof(*cap));
+
data = *bp++; /* byte 0 */
cap->major = (data >> 4) & 0x0f;
cap->minor = data & 0x0f;
@@ -819,8 +827,13 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
if (data & BIT(7))
cap->enhanced_frame++;
- if (data & 0x40)
+ if (data & 0x40) {
cap->flags |= DPCD_TPS3;
+ pr_debug("pattern 3 supported\n");
+ } else {
+ pr_debug("pattern 3 not supported\n");
+ }
+
data &= 0x0f;
cap->max_lane_count = data;
if (--rlen <= 0)
@@ -846,11 +859,36 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
if (--rlen <= 0)
return;
- bp += 3; /* skip 5, 6 and 7 */
- rlen -= 3;
+ data = *bp++; /* Byte 5: DOWN_STREAM_PORT_PRESENT */
+ cap->downstream_port.dfp_present = data & BIT(0);
+ cap->downstream_port.dfp_type = data & 0x6;
+ cap->downstream_port.format_conversion = data & BIT(3);
+ cap->downstream_port.detailed_cap_info_available = data & BIT(4);
+ pr_debug("dfp_present = %d, dfp_type = %d\n",
+ cap->downstream_port.dfp_present,
+ cap->downstream_port.dfp_type);
+ pr_debug("format_conversion = %d, detailed_cap_info_available = %d\n",
+ cap->downstream_port.format_conversion,
+ cap->downstream_port.detailed_cap_info_available);
+ if (--rlen <= 0)
+ return;
+
+ bp += 1; /* Skip Byte 6 */
+ rlen -= 1;
if (rlen <= 0)
return;
+ data = *bp++; /* Byte 7: DOWN_STREAM_PORT_COUNT */
+ cap->downstream_port.dfp_count = data & 0x7;
+ cap->downstream_port.msa_timing_par_ignored = data & BIT(6);
+ cap->downstream_port.oui_support = data & BIT(7);
+ pr_debug("dfp_count = %d, msa_timing_par_ignored = %d\n",
+ cap->downstream_port.dfp_count,
+ cap->downstream_port.msa_timing_par_ignored);
+ pr_debug("oui_support = %d\n", cap->downstream_port.oui_support);
+ if (--rlen <= 0)
+ return;
+
data = *bp++; /* byte 8 */
if (data & BIT(1)) {
cap->flags |= DPCD_PORT_0_EDID_PRESENTED;
@@ -861,7 +899,7 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
data = *bp++; /* byte 9 */
cap->rx_port0_buf_size = (data + 1) * 32;
- pr_debug("lane_buf_size=%d", cap->rx_port0_buf_size);
+ pr_debug("lane_buf_size=%d\n", cap->rx_port0_buf_size);
if (--rlen <= 0)
return;
@@ -1238,7 +1276,7 @@ static int dp_train_pattern_set_write(struct mdss_dp_drv_pdata *ep,
return dp_aux_write_buf(ep, 0x102, buf, 1, 0);
}
-static int dp_sink_clock_recovery_done(struct mdss_dp_drv_pdata *ep)
+bool mdss_dp_aux_clock_recovery_done(struct mdss_dp_drv_pdata *ep)
{
u32 mask;
u32 data;
@@ -1259,12 +1297,12 @@ static int dp_sink_clock_recovery_done(struct mdss_dp_drv_pdata *ep)
pr_debug("data=%x mask=%x\n", data, mask);
data &= mask;
if (data == mask) /* all done */
- return 1;
+ return true;
- return 0;
+ return false;
}
-static int dp_sink_channel_eq_done(struct mdss_dp_drv_pdata *ep)
+bool mdss_dp_aux_channel_eq_done(struct mdss_dp_drv_pdata *ep)
{
u32 mask;
u32 data;
@@ -1293,9 +1331,9 @@ static int dp_sink_channel_eq_done(struct mdss_dp_drv_pdata *ep)
data &= mask;
if (data == mask)/* all done */
- return 1;
+ return true;
- return 0;
+ return false;
}
void dp_sink_train_set_adjust(struct mdss_dp_drv_pdata *ep)
@@ -1431,6 +1469,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep)
int tries, old_v_level;
int ret = 0;
int usleep_time;
+ int const maximum_retries = 5;
pr_debug("Entered++");
@@ -1446,7 +1485,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep)
usleep_range(usleep_time, usleep_time);
dp_link_status_read(ep, 6);
- if (dp_sink_clock_recovery_done(ep)) {
+ if (mdss_dp_aux_clock_recovery_done(ep)) {
ret = 0;
break;
}
@@ -1458,7 +1497,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep)
if (old_v_level == ep->v_level) {
tries++;
- if (tries >= 5) {
+ if (tries >= maximum_retries) {
ret = -1;
break; /* quit */
}
@@ -1480,6 +1519,7 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep)
int ret = 0;
int usleep_time;
char pattern;
+ int const maximum_retries = 5;
pr_debug("Entered++");
@@ -1499,13 +1539,13 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep)
dp_link_status_read(ep, 6);
- if (dp_sink_channel_eq_done(ep)) {
+ if (mdss_dp_aux_channel_eq_done(ep)) {
ret = 0;
break;
}
tries++;
- if (tries > 4) {
+ if (tries > maximum_retries) {
ret = -1;
break;
}
@@ -1518,47 +1558,27 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep)
static int dp_link_rate_down_shift(struct mdss_dp_drv_pdata *ep)
{
- u32 prate, lrate;
- int rate, lane, max_lane;
- int changed = 0;
-
- rate = ep->link_rate;
- lane = ep->lane_cnt;
- max_lane = ep->dpcd.max_lane_count;
-
- prate = ep->pixel_rate;
- prate /= 1000; /* avoid using 64 biits */
- prate *= ep->bpp;
- prate /= 8; /* byte */
-
- if (rate > DP_LINK_RATE_162 && rate <= DP_LINK_RATE_MAX) {
- rate -= 4; /* reduce rate */
- changed++;
- }
+ int ret = 0;
- if (changed) {
- if (lane >= 1 && lane < max_lane)
- lane <<= 1; /* increase lane */
+ if (!ep)
+ return -EINVAL;
- lrate = 270000000; /* 270M */
- lrate /= 1000; /* avoid using 64 bits */
- lrate *= rate;
- lrate /= 10; /* byte, 10 bits --> 8 bits */
- lrate *= lane;
+ switch (ep->link_rate) {
+ case DP_LINK_RATE_540:
+ ep->link_rate = DP_LINK_RATE_270;
+ break;
+ case DP_LINK_RATE_270:
+ ep->link_rate = DP_LINK_RATE_162;
+ break;
+ case DP_LINK_RATE_162:
+ default:
+ ret = -EINVAL;
+ break;
+ };
- pr_debug("new lrate=%u prate=%u rate=%d lane=%d p=%d b=%d\n",
- lrate, prate, rate, lane, ep->pixel_rate, ep->bpp);
+ pr_debug("new rate=%d\n", ep->link_rate);
- if (lrate > prate) {
- ep->link_rate = rate;
- ep->lane_cnt = lane;
- pr_debug("new rate=%d %d\n", rate, lane);
- return 0;
- }
- }
-
- /* add calculation later */
- return -EINVAL;
+ return ret;
}
int mdss_dp_aux_set_sink_power_state(struct mdss_dp_drv_pdata *ep, char state)
@@ -1595,7 +1615,6 @@ int mdss_dp_link_train(struct mdss_dp_drv_pdata *dp)
mdss_dp_aux_set_sink_power_state(dp, SINK_POWER_ON);
-train_start:
dp->v_level = 0; /* start from default level */
dp->p_level = 0;
mdss_dp_config_ctrl(dp);
@@ -1605,11 +1624,12 @@ train_start:
ret = dp_start_link_train_1(dp);
if (ret < 0) {
- if (dp_link_rate_down_shift(dp) == 0) {
- goto train_start;
+ if (!dp_link_rate_down_shift(dp)) {
+ pr_debug("retry with lower rate\n");
+ return -EAGAIN;
} else {
pr_err("Training 1 failed\n");
- ret = -1;
+ ret = -EINVAL;
goto clear;
}
}
@@ -1618,21 +1638,23 @@ train_start:
ret = dp_start_link_train_2(dp);
if (ret < 0) {
- if (dp_link_rate_down_shift(dp) == 0) {
- goto train_start;
+ if (!dp_link_rate_down_shift(dp)) {
+ pr_debug("retry with lower rate\n");
+ return -EAGAIN;
} else {
pr_err("Training 2 failed\n");
- ret = -1;
+ ret = -EINVAL;
goto clear;
}
}
pr_debug("Training 2 completed successfully\n");
-
clear:
dp_clear_training_pattern(dp);
- if (ret != -1) {
+ if (ret != -EINVAL) {
+ mdss_dp_config_misc_settings(&dp->ctrl_io,
+ &dp->panel_data.panel_info);
mdss_dp_setup_tr_unit(&dp->ctrl_io, dp->link_rate,
dp->lane_cnt, dp->vic);
mdss_dp_state_ctrl(&dp->ctrl_io, ST_SEND_VIDEO);
diff --git a/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c
index 3891806b09bb..79cd94cfbe88 100644
--- a/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c
+++ b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c
@@ -53,7 +53,6 @@ struct dp_hdcp2p2_ctrl {
struct kthread_work send_msg;
struct kthread_work recv_msg;
struct kthread_work link;
- struct kthread_work poll;
char *msg_buf;
uint32_t send_msg_len; /* length of all parameters in msg */
uint32_t timeout;
@@ -67,26 +66,6 @@ struct dp_hdcp2p2_ctrl {
bool polling;
};
-static inline char *dp_hdcp_cmd_to_str(uint32_t cmd)
-{
- switch (cmd) {
- case HDMI_HDCP_WKUP_CMD_SEND_MESSAGE:
- return "HDMI_HDCP_WKUP_CMD_SEND_MESSAGE";
- case HDMI_HDCP_WKUP_CMD_RECV_MESSAGE:
- return "HDMI_HDCP_WKUP_CMD_RECV_MESSAGE";
- case HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS:
- return "HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS";
- case HDMI_HDCP_WKUP_CMD_STATUS_FAILED:
- return "DP_HDCP_WKUP_CMD_STATUS_FAIL";
- case HDMI_HDCP_WKUP_CMD_LINK_POLL:
- return "HDMI_HDCP_WKUP_CMD_LINK_POLL";
- case HDMI_HDCP_WKUP_CMD_AUTHENTICATE:
- return "HDMI_HDCP_WKUP_CMD_AUTHENTICATE";
- default:
- return "???";
- }
-}
-
static inline bool dp_hdcp2p2_is_valid_state(struct dp_hdcp2p2_ctrl *ctrl)
{
if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_AUTHENTICATE)
@@ -193,7 +172,7 @@ static int dp_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data)
queue_kthread_work(&ctrl->worker, &ctrl->status);
break;
case HDMI_HDCP_WKUP_CMD_LINK_POLL:
- queue_kthread_work(&ctrl->worker, &ctrl->poll);
+ ctrl->polling = true;
break;
case HDMI_HDCP_WKUP_CMD_AUTHENTICATE:
queue_kthread_work(&ctrl->worker, &ctrl->auth);
@@ -203,10 +182,11 @@ static int dp_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data)
}
exit:
mutex_unlock(&ctrl->wakeup_mutex);
+
return 0;
}
-static inline int dp_hdcp2p2_wakeup_lib(struct dp_hdcp2p2_ctrl *ctrl,
+static inline void dp_hdcp2p2_wakeup_lib(struct dp_hdcp2p2_ctrl *ctrl,
struct hdcp_lib_wakeup_data *data)
{
int rc = 0;
@@ -218,8 +198,6 @@ static inline int dp_hdcp2p2_wakeup_lib(struct dp_hdcp2p2_ctrl *ctrl,
pr_err("error sending %s to lib\n",
hdcp_lib_cmd_to_str(data->cmd));
}
-
- return rc;
}
static void dp_hdcp2p2_reset(struct dp_hdcp2p2_ctrl *ctrl)
@@ -339,8 +317,6 @@ static void dp_hdcp2p2_auth_failed(struct dp_hdcp2p2_ctrl *ctrl)
return;
}
- atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL);
-
/* notify DP about HDCP failure */
ctrl->init_data.notify_status(ctrl->init_data.cb_data,
HDCP_STATE_AUTH_FAIL);
@@ -349,8 +325,7 @@ static void dp_hdcp2p2_auth_failed(struct dp_hdcp2p2_ctrl *ctrl)
static int dp_hdcp2p2_aux_read_message(struct dp_hdcp2p2_ctrl *ctrl,
u8 *buf, int size, int offset, u32 timeout)
{
- int rc, max_size = 16, read_size, len = size;
- u8 *buf_start = buf;
+ int rc, max_size = 16, read_size;
if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
pr_err("hdcp is off\n");
@@ -378,8 +353,6 @@ static int dp_hdcp2p2_aux_read_message(struct dp_hdcp2p2_ctrl *ctrl,
size -= read_size;
} while (size > 0);
- print_hex_dump(KERN_DEBUG, "hdcp2p2: ", DUMP_PREFIX_NONE,
- 16, 1, buf_start, len, false);
return rc;
}
@@ -452,12 +425,9 @@ end:
static void dp_hdcp2p2_send_msg_work(struct kthread_work *work)
{
int rc = 0;
- int i;
- int sent_bytes = 0;
struct dp_hdcp2p2_ctrl *ctrl = container_of(work,
struct dp_hdcp2p2_ctrl, send_msg);
struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
- char *buf = NULL;
if (!ctrl) {
pr_err("invalid input\n");
@@ -474,20 +444,13 @@ static void dp_hdcp2p2_send_msg_work(struct kthread_work *work)
mutex_lock(&ctrl->msg_lock);
- /* Loop through number of parameters in the messages. */
- for (i = 0; i < ctrl->num_messages; i++) {
- buf = ctrl->msg_buf + sent_bytes;
-
- /* Forward the message to the sink */
- rc = dp_hdcp2p2_aux_write_message(ctrl, buf,
- (size_t)ctrl->msg_part[i].length,
- ctrl->msg_part[i].offset, ctrl->timeout);
- if (rc) {
- pr_err("Error sending msg to sink %d\n", rc);
- mutex_unlock(&ctrl->msg_lock);
- goto exit;
- }
- sent_bytes += ctrl->msg_part[i].length;
+ rc = dp_hdcp2p2_aux_write_message(ctrl, ctrl->msg_buf,
+ ctrl->send_msg_len, ctrl->msg_part->offset,
+ ctrl->timeout);
+ if (rc) {
+ pr_err("Error sending msg to sink %d\n", rc);
+ mutex_unlock(&ctrl->msg_lock);
+ goto exit;
}
cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS;
@@ -505,10 +468,9 @@ exit:
static int dp_hdcp2p2_get_msg_from_sink(struct dp_hdcp2p2_ctrl *ctrl)
{
- int i, rc = 0;
+ int rc = 0;
char *recvd_msg_buf = NULL;
struct hdcp_lib_wakeup_data cdata = { HDCP_LIB_WKUP_CMD_INVALID };
- int bytes_read = 0;
cdata.context = ctrl->lib_ctx;
@@ -518,17 +480,12 @@ static int dp_hdcp2p2_get_msg_from_sink(struct dp_hdcp2p2_ctrl *ctrl)
goto exit;
}
- for (i = 0; i < ctrl->num_messages; i++) {
- rc = dp_hdcp2p2_aux_read_message(
- ctrl, recvd_msg_buf + bytes_read,
- ctrl->msg_part[i].length,
- ctrl->msg_part[i].offset,
- ctrl->timeout);
- if (rc) {
- pr_err("error reading message %d\n", rc);
- goto exit;
- }
- bytes_read += ctrl->msg_part[i].length;
+ rc = dp_hdcp2p2_aux_read_message(ctrl, recvd_msg_buf,
+ ctrl->send_msg_len, ctrl->msg_part->offset,
+ ctrl->timeout);
+ if (rc) {
+ pr_err("error reading message %d\n", rc);
+ goto exit;
}
cdata.recvd_msg_buf = recvd_msg_buf;
@@ -550,7 +507,6 @@ exit:
static void dp_hdcp2p2_recv_msg_work(struct kthread_work *work)
{
- int rc = 0;
struct hdcp_lib_wakeup_data cdata = { HDCP_LIB_WKUP_CMD_INVALID };
struct dp_hdcp2p2_ctrl *ctrl = container_of(work,
struct dp_hdcp2p2_ctrl, recv_msg);
@@ -559,44 +515,24 @@ static void dp_hdcp2p2_recv_msg_work(struct kthread_work *work)
if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
pr_err("hdcp is off\n");
- goto exit;
+ return;
}
- if (ctrl->sink_rx_status & ctrl->abort_mask) {
- pr_err("reauth or Link fail triggered by sink\n");
-
- ctrl->sink_rx_status = 0;
- rc = -ENOLINK;
- cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
-
- goto exit;
- }
+ if (ctrl->rx_status) {
+ if (!ctrl->cp_irq_done) {
+ pr_debug("waiting for CP_IRQ\n");
+ ctrl->polling = true;
+ return;
+ }
- if (ctrl->rx_status && !ctrl->sink_rx_status) {
- pr_debug("Recv msg for RxStatus, but no CP_IRQ yet\n");
- ctrl->polling = true;
- goto exit;
+ if (ctrl->rx_status & ctrl->sink_rx_status) {
+ ctrl->cp_irq_done = false;
+ ctrl->sink_rx_status = 0;
+ ctrl->rx_status = 0;
+ }
}
dp_hdcp2p2_get_msg_from_sink(ctrl);
-
- return;
-exit:
- if (rc)
- dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
-}
-
-static void dp_hdcp2p2_poll_work(struct kthread_work *work)
-{
- struct dp_hdcp2p2_ctrl *ctrl = container_of(work,
- struct dp_hdcp2p2_ctrl, poll);
-
- if (ctrl->cp_irq_done) {
- ctrl->cp_irq_done = false;
- dp_hdcp2p2_get_msg_from_sink(ctrl);
- } else {
- ctrl->polling = true;
- }
}
static void dp_hdcp2p2_auth_status_work(struct kthread_work *work)
@@ -645,44 +581,45 @@ static void dp_hdcp2p2_link_work(struct kthread_work *work)
if (rc) {
pr_err("failed to read rx status\n");
- cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ cdata.cmd = HDCP_LIB_WKUP_CMD_LINK_FAILED;
+ atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL);
goto exit;
}
if (ctrl->sink_rx_status & ctrl->abort_mask) {
- pr_err("reauth or Link fail triggered by sink\n");
+ if (ctrl->sink_rx_status & BIT(3))
+ pr_err("reauth_req set by sink\n");
+
+ if (ctrl->sink_rx_status & BIT(4))
+ pr_err("link failure reported by sink\n");
ctrl->sink_rx_status = 0;
ctrl->rx_status = 0;
rc = -ENOLINK;
- cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+
+ cdata.cmd = HDCP_LIB_WKUP_CMD_LINK_FAILED;
+ atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL);
goto exit;
}
- /* if polling, get message from sink else let polling start */
if (ctrl->polling && (ctrl->sink_rx_status & ctrl->rx_status)) {
ctrl->sink_rx_status = 0;
ctrl->rx_status = 0;
- rc = dp_hdcp2p2_get_msg_from_sink(ctrl);
+ dp_hdcp2p2_get_msg_from_sink(ctrl);
ctrl->polling = false;
} else {
ctrl->cp_irq_done = true;
}
exit:
- dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
-
- if (rc) {
- dp_hdcp2p2_auth_failed(ctrl);
- return;
- }
+ if (rc)
+ dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
}
static void dp_hdcp2p2_auth_work(struct kthread_work *work)
{
- int rc = 0;
struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
struct dp_hdcp2p2_ctrl *ctrl = container_of(work,
struct dp_hdcp2p2_ctrl, auth);
@@ -694,12 +631,10 @@ static void dp_hdcp2p2_auth_work(struct kthread_work *work)
else
cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
- rc = dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
- if (rc)
- dp_hdcp2p2_auth_failed(ctrl);
+ dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
}
-static int dp_hdcp2p2_isr(void *input)
+static int dp_hdcp2p2_cp_irq(void *input)
{
struct dp_hdcp2p2_ctrl *ctrl = input;
@@ -748,7 +683,7 @@ void *dp_hdcp2p2_init(struct hdcp_init_data *init_data)
.authenticate = dp_hdcp2p2_authenticate,
.feature_supported = dp_hdcp2p2_feature_supported,
.off = dp_hdcp2p2_off,
- .isr = dp_hdcp2p2_isr
+ .cp_irq = dp_hdcp2p2_cp_irq,
};
static struct hdcp_client_ops client_ops = {
@@ -806,7 +741,6 @@ void *dp_hdcp2p2_init(struct hdcp_init_data *init_data)
init_kthread_work(&ctrl->recv_msg, dp_hdcp2p2_recv_msg_work);
init_kthread_work(&ctrl->status, dp_hdcp2p2_auth_status_work);
init_kthread_work(&ctrl->link, dp_hdcp2p2_link_work);
- init_kthread_work(&ctrl->poll, dp_hdcp2p2_poll_work);
ctrl->thread = kthread_run(kthread_worker_fn,
&ctrl->worker, "dp_hdcp2p2");
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c
index b1eb8e0c9579..86edc4492599 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.c
+++ b/drivers/video/fbdev/msm/mdss_dp_util.c
@@ -27,10 +27,6 @@
#define DP_LS_FREQ_162 162000000
#define DP_LS_FREQ_270 270000000
#define DP_LS_FREQ_540 540000000
-#define AUDIO_FREQ_32 32000
-#define AUDIO_FREQ_44_1 44100
-#define AUDIO_FREQ_48 48000
-#define DP_AUDIO_FREQ_COUNT 3
enum mdss_dp_pin_assignment {
PIN_ASSIGNMENT_A,
@@ -55,68 +51,12 @@ static const char *mdss_dp_pin_name(u8 pin)
}
}
-static const uint32_t naud_value[DP_AUDIO_FREQ_COUNT][DP_AUDIO_FREQ_COUNT] = {
- { 10125, 16875, 33750 },
- { 5625, 9375, 18750 },
- { 3375, 5625, 11250 }
-};
-
-static const uint32_t maud_rate[DP_AUDIO_FREQ_COUNT] = { 1024, 784, 512 };
-
-static const uint32_t audio_timing_rbr[DP_AUDIO_FREQ_COUNT] = {
- MMSS_DP_AUDIO_TIMING_RBR_32,
- MMSS_DP_AUDIO_TIMING_RBR_44,
- MMSS_DP_AUDIO_TIMING_RBR_48
-};
-
-static const uint32_t std_audio_freq_list[DP_AUDIO_FREQ_COUNT] = {
- AUDIO_FREQ_32,
- AUDIO_FREQ_44_1,
- AUDIO_FREQ_48
-};
-
struct mdss_hw mdss_dp_hw = {
.hw_ndx = MDSS_HW_EDP,
.ptr = NULL,
.irq_handler = dp_isr,
};
-static int mdss_dp_get_rate_index(uint32_t rate)
-{
- int index = 0;
-
- switch (rate) {
- case DP_LS_FREQ_162:
- case AUDIO_FREQ_32:
- index = 0;
- break;
- case DP_LS_FREQ_270:
- case AUDIO_FREQ_44_1:
- index = 1;
- break;
- case DP_LS_FREQ_540:
- case AUDIO_FREQ_48:
- index = 2;
- break;
- default:
- index = 0;
- pr_err("unsupported rate\n");
- break;
- }
-
- return index;
-}
-
-static bool match_std_freq(uint32_t audio_freq, uint32_t std_freq)
-{
- int quotient = audio_freq / std_freq;
-
- if (quotient & (quotient - 1))
- return false;
- else
- return true;
-}
-
/* DP retrieve ctrl HW version */
u32 mdss_dp_get_ctrl_hw_version(struct dss_io_data *ctrl_io)
{
@@ -313,10 +253,48 @@ void mdss_dp_timing_cfg(struct dss_io_data *ctrl_io,
writel_relaxed(data, ctrl_io->base + DP_ACTIVE_HOR_VER);
}
-void mdss_dp_sw_mvid_nvid(struct dss_io_data *ctrl_io)
+void mdss_dp_sw_config_msa(struct dss_io_data *ctrl_io,
+ char lrate, struct dss_io_data *dp_cc_io)
+{
+ u32 pixel_m, pixel_n;
+ u32 mvid, nvid;
+
+ pixel_m = readl_relaxed(dp_cc_io->base + MMSS_DP_PIXEL_M);
+ pixel_n = readl_relaxed(dp_cc_io->base + MMSS_DP_PIXEL_N);
+ pr_debug("pixel_m=0x%x, pixel_n=0x%x\n",
+ pixel_m, pixel_n);
+
+ mvid = (pixel_m & 0xFFFF) * 5;
+ nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);
+ if (lrate == DP_LINK_RATE_540)
+ nvid = nvid * 2;
+ pr_debug("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
+ writel_relaxed(mvid, ctrl_io->base + DP_SOFTWARE_MVID);
+ writel_relaxed(nvid, ctrl_io->base + DP_SOFTWARE_NVID);
+}
+
+void mdss_dp_config_misc_settings(struct dss_io_data *ctrl_io,
+ struct mdss_panel_info *pinfo)
{
- writel_relaxed(0x37, ctrl_io->base + DP_SOFTWARE_MVID);
- writel_relaxed(0x3c, ctrl_io->base + DP_SOFTWARE_NVID);
+ u32 bpp = pinfo->bpp;
+ u32 misc_val = 0x0;
+
+ switch (bpp) {
+ case 18:
+ misc_val |= (0x0 << 5);
+ break;
+ case 30:
+ misc_val |= (0x2 << 5);
+ break;
+ case 24:
+ default:
+ misc_val |= (0x1 << 5);
+ }
+
+ misc_val |= BIT(0); /* Configure clock to synchronous mode */
+
+ pr_debug("Misc settings = 0x%x\n", misc_val);
+ writel_relaxed(misc_val, ctrl_io->base + DP_MISC1_MISC0);
}
void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io, u8 link_rate,
@@ -327,8 +305,6 @@ void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io, u8 link_rate,
u32 valid_boundary2 = 0x0;
struct dp_vc_tu_mapping_table const *tu_entry = tu_table;
- writel_relaxed(0x21, ctrl_io->base + DP_MISC1_MISC0);
-
for (; tu_entry != tu_table + ARRAY_SIZE(tu_table); ++tu_entry) {
if ((tu_entry->vic == res) &&
(tu_entry->lanes == ln_cnt) &&
@@ -514,8 +490,14 @@ u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp)
pin_cfg = dp->alt_mode.dp_cap.dlink_pin_config;
for (pin = PIN_ASSIGNMENT_A; pin < PIN_ASSIGNMENT_MAX; pin++) {
- if (pin_cfg & BIT(pin))
- break;
+ if (pin_cfg & BIT(pin)) {
+ if (dp->alt_mode.dp_status.multi_func) {
+ if (pin == PIN_ASSIGNMENT_D)
+ break;
+ } else {
+ break;
+ }
+ }
}
if (pin == PIN_ASSIGNMENT_MAX)
@@ -866,52 +848,6 @@ void mdss_dp_audio_setup_sdps(struct dss_io_data *ctrl_io)
mdss_dp_audio_setup_isrc_sdp(ctrl_io);
}
-void mdss_dp_audio_set_sample_rate(struct dss_io_data *ctrl_io,
- char dp_link_rate, uint32_t audio_freq)
-{
- uint32_t link_rate;
- uint32_t default_audio_freq = AUDIO_FREQ_32;
- int i, multiplier = 1;
- uint32_t maud_index, lrate_index, register_index, value;
-
- link_rate = (uint32_t)dp_link_rate * DP_LINK_RATE_MULTIPLIER;
-
- pr_debug("link_rate = %u, audio_freq = %u\n", link_rate, audio_freq);
-
- for (i = 0; i < DP_AUDIO_FREQ_COUNT; i++) {
- if (audio_freq % std_audio_freq_list[i])
- continue;
-
- if (match_std_freq(audio_freq, std_audio_freq_list[i])) {
- default_audio_freq = std_audio_freq_list[i];
- multiplier = audio_freq / default_audio_freq;
- break;
- }
- }
-
- pr_debug("default_audio_freq = %u, multiplier = %d\n",
- default_audio_freq, multiplier);
-
- lrate_index = mdss_dp_get_rate_index(link_rate);
- maud_index = mdss_dp_get_rate_index(default_audio_freq);
-
- pr_debug("lrate_index = %u, maud_index = %u, maud = %u, naud = %u\n",
- lrate_index, maud_index,
- maud_rate[maud_index] * multiplier,
- naud_value[maud_index][lrate_index]);
-
- register_index = mdss_dp_get_rate_index(default_audio_freq);
- value = ((maud_rate[maud_index] * multiplier) << 16) |
- naud_value[maud_index][lrate_index];
-
- pr_debug("reg index = %d, offset = 0x%x, value = 0x%x\n",
- (int)register_index, audio_timing_rbr[register_index],
- value);
-
- writel_relaxed(value, ctrl_io->base +
- audio_timing_rbr[register_index]);
-}
-
void mdss_dp_set_safe_to_exit_level(struct dss_io_data *ctrl_io,
uint32_t lane_cnt)
{
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h
index 334c0071050d..4b28d98177be 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.h
+++ b/drivers/video/fbdev/msm/mdss_dp_util.h
@@ -164,6 +164,12 @@
#define TCSR_USB3_DP_PHYMODE 0x48
#define EDID_START_ADDRESS 0x50
+/* DP MMSS_CC registers */
+#define MMSS_DP_LINK_CMD_RCGR 0x0000
+#define MMSS_DP_LINK_CFG_RCGR 0x0004
+#define MMSS_DP_PIXEL_M 0x0048
+#define MMSS_DP_PIXEL_N 0x004C
+
/* DP HDCP 1.3 registers */
#define DP_HDCP_CTRL (0x0A0)
#define DP_HDCP_STATUS (0x0A4)
@@ -271,6 +277,8 @@ void mdss_dp_switch_usb3_phy_to_dp_mode(struct dss_io_data *tcsr_reg_io);
void mdss_dp_assert_phy_reset(struct dss_io_data *ctrl_io, bool assert);
void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io, u8 link_rate,
u8 ln_cnt, u32 res);
+void mdss_dp_config_misc_settings(struct dss_io_data *ctrl_io,
+ struct mdss_panel_info *pinfo);
void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io);
void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable);
void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable);
@@ -285,7 +293,8 @@ void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data);
int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv);
void mdss_dp_irq_enable(struct mdss_dp_drv_pdata *dp_drv);
void mdss_dp_irq_disable(struct mdss_dp_drv_pdata *dp_drv);
-void mdss_dp_sw_mvid_nvid(struct dss_io_data *ctrl_io);
+void mdss_dp_sw_config_msa(struct dss_io_data *ctrl_io,
+ char lrate, struct dss_io_data *dp_cc_io);
void mdss_dp_usbpd_ext_capabilities(struct usbpd_dp_capabilities *dp_cap);
void mdss_dp_usbpd_ext_dp_status(struct usbpd_dp_status *dp_status);
u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp);
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index 66cd99720afa..cf7a398c13ce 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -995,6 +995,7 @@ static void mdss_dsi_debugfs_cleanup(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
struct mdss_dsi_debugfs_info *dfs = ctrl->debugfs_info;
if (dfs && dfs->root)
debugfs_remove_recursive(dfs->root);
+ kfree(dfs);
pdata = pdata->next;
} while (pdata);
pr_debug("%s: Cleaned up mdss_dsi_debugfs_info\n", __func__);
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index 7091dc2f38b9..3536cb2d294d 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -468,6 +468,7 @@ struct mdss_dsi_ctrl_pdata {
bool cmd_sync_wait_trigger;
struct mdss_rect roi;
+ struct mdss_dsi_dual_pu_roi dual_roi;
struct pwm_device *pwm_bl;
u32 pclk_rate;
u32 byte_clk_rate;
diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c
index 18bcdca31bf6..c9168242da60 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_host.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_host.c
@@ -1108,6 +1108,11 @@ static int mdss_dsi_read_status(struct mdss_dsi_ctrl_pdata *ctrl)
rc = 1;
lenp = ctrl->status_valid_params ?: ctrl->status_cmds_rlen;
+ if (!lenp || !ctrl->status_cmds_rlen) {
+ pr_err("invalid dsi read params!\n");
+ return 0;
+ }
+
for (i = 0; i < ctrl->status_cmds.cmd_cnt; ++i) {
memset(&cmdreq, 0, sizeof(cmdreq));
cmdreq.cmds = ctrl->status_cmds.cmds + i;
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index 01fc01425a3a..7c36bb627043 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -25,6 +25,7 @@
#include "mdss_dsi.h"
#include "mdss_dba_utils.h"
+#include "mdss_debug.h"
#define DT_CMD_HDR 6
#define DEFAULT_MDP_TRANSFER_TIME 14000
@@ -446,28 +447,82 @@ static int mdss_dsi_roi_merge(struct mdss_dsi_ctrl_pdata *ctrl,
static char caset[] = {0x2a, 0x00, 0x00, 0x03, 0x00}; /* DTYPE_DCS_LWRITE */
static char paset[] = {0x2b, 0x00, 0x00, 0x05, 0x00}; /* DTYPE_DCS_LWRITE */
+/*
+ * Some panels can support multiple ROIs as part of the below commands
+ */
+static char caset_dual[] = {0x2a, 0x00, 0x00, 0x03, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00};/* DTYPE_DCS_LWRITE */
+static char paset_dual[] = {0x2b, 0x00, 0x00, 0x05, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00};/* DTYPE_DCS_LWRITE */
+
/* pack into one frame before sent */
static struct dsi_cmd_desc set_col_page_addr_cmd[] = {
{{DTYPE_DCS_LWRITE, 0, 0, 0, 1, sizeof(caset)}, caset}, /* packed */
{{DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(paset)}, paset},
};
+/* pack into one frame before sent */
+static struct dsi_cmd_desc set_dual_col_page_addr_cmd[] = { /*packed*/
+ {{DTYPE_DCS_LWRITE, 0, 0, 0, 1, sizeof(caset_dual)}, caset_dual},
+ {{DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(paset_dual)}, paset_dual},
+};
+
+
+static void __mdss_dsi_send_col_page_addr(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct mdss_rect *roi, bool dual_roi)
+{
+ if (dual_roi) {
+ struct mdss_rect *first, *second;
+
+ first = &ctrl->panel_data.panel_info.dual_roi.first_roi;
+ second = &ctrl->panel_data.panel_info.dual_roi.second_roi;
+
+ caset_dual[1] = (((first->x) & 0xFF00) >> 8);
+ caset_dual[2] = (((first->x) & 0xFF));
+ caset_dual[3] = (((first->x - 1 + first->w) & 0xFF00) >> 8);
+ caset_dual[4] = (((first->x - 1 + first->w) & 0xFF));
+ /* skip the MPU setting byte*/
+ caset_dual[6] = (((second->x) & 0xFF00) >> 8);
+ caset_dual[7] = (((second->x) & 0xFF));
+ caset_dual[8] = (((second->x - 1 + second->w) & 0xFF00) >> 8);
+ caset_dual[9] = (((second->x - 1 + second->w) & 0xFF));
+ set_dual_col_page_addr_cmd[0].payload = caset_dual;
+
+ paset_dual[1] = (((first->y) & 0xFF00) >> 8);
+ paset_dual[2] = (((first->y) & 0xFF));
+ paset_dual[3] = (((first->y - 1 + first->h) & 0xFF00) >> 8);
+ paset_dual[4] = (((first->y - 1 + first->h) & 0xFF));
+ /* skip the MPU setting byte */
+ paset_dual[6] = (((second->y) & 0xFF00) >> 8);
+ paset_dual[7] = (((second->y) & 0xFF));
+ paset_dual[8] = (((second->y - 1 + second->h) & 0xFF00) >> 8);
+ paset_dual[9] = (((second->y - 1 + second->h) & 0xFF));
+ set_dual_col_page_addr_cmd[1].payload = paset_dual;
+ } else {
+ caset[1] = (((roi->x) & 0xFF00) >> 8);
+ caset[2] = (((roi->x) & 0xFF));
+ caset[3] = (((roi->x - 1 + roi->w) & 0xFF00) >> 8);
+ caset[4] = (((roi->x - 1 + roi->w) & 0xFF));
+ set_col_page_addr_cmd[0].payload = caset;
+
+ paset[1] = (((roi->y) & 0xFF00) >> 8);
+ paset[2] = (((roi->y) & 0xFF));
+ paset[3] = (((roi->y - 1 + roi->h) & 0xFF00) >> 8);
+ paset[4] = (((roi->y - 1 + roi->h) & 0xFF));
+ set_col_page_addr_cmd[1].payload = paset;
+ }
+ pr_debug("%s Sending 2A 2B cmnd with dual_roi=%d\n", __func__,
+ dual_roi);
+
+}
static void mdss_dsi_send_col_page_addr(struct mdss_dsi_ctrl_pdata *ctrl,
struct mdss_rect *roi, int unicast)
{
struct dcs_cmd_req cmdreq;
+ struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info;
+ bool dual_roi = pinfo->dual_roi.enabled;
- caset[1] = (((roi->x) & 0xFF00) >> 8);
- caset[2] = (((roi->x) & 0xFF));
- caset[3] = (((roi->x - 1 + roi->w) & 0xFF00) >> 8);
- caset[4] = (((roi->x - 1 + roi->w) & 0xFF));
- set_col_page_addr_cmd[0].payload = caset;
-
- paset[1] = (((roi->y) & 0xFF00) >> 8);
- paset[2] = (((roi->y) & 0xFF));
- paset[3] = (((roi->y - 1 + roi->h) & 0xFF00) >> 8);
- paset[4] = (((roi->y - 1 + roi->h) & 0xFF));
- set_col_page_addr_cmd[1].payload = paset;
+ __mdss_dsi_send_col_page_addr(ctrl, roi, dual_roi);
memset(&cmdreq, 0, sizeof(cmdreq));
cmdreq.cmds_cnt = 2;
@@ -477,7 +532,9 @@ static void mdss_dsi_send_col_page_addr(struct mdss_dsi_ctrl_pdata *ctrl,
cmdreq.rlen = 0;
cmdreq.cb = NULL;
- cmdreq.cmds = set_col_page_addr_cmd;
+ /* Send default or dual roi 2A/2B cmd */
+ cmdreq.cmds = dual_roi ? set_dual_col_page_addr_cmd :
+ set_col_page_addr_cmd;
mdss_dsi_cmdlist_put(ctrl, &cmdreq);
}
@@ -1498,7 +1555,7 @@ static int mdss_dsi_parse_reset_seq(struct device_node *np,
static bool mdss_dsi_cmp_panel_reg_v2(struct mdss_dsi_ctrl_pdata *ctrl)
{
- int i, j;
+ int i, j = 0;
int len = 0, *lenp;
int group = 0;
@@ -1507,6 +1564,15 @@ static bool mdss_dsi_cmp_panel_reg_v2(struct mdss_dsi_ctrl_pdata *ctrl)
for (i = 0; i < ctrl->status_cmds.cmd_cnt; i++)
len += lenp[i];
+ for (i = 0; i < len; i++) {
+ pr_debug("[%i] return:0x%x status:0x%x\n",
+ i, (unsigned int)ctrl->return_buf[i],
+ (unsigned int)ctrl->status_value[j + i]);
+ MDSS_XLOG(ctrl->ndx, ctrl->return_buf[i],
+ ctrl->status_value[j + i]);
+ j += len;
+ }
+
for (j = 0; j < ctrl->groups; ++j) {
for (i = 0; i < len; ++i) {
if (ctrl->return_buf[i] !=
@@ -1827,20 +1893,28 @@ error:
pinfo->esd_check_enabled = false;
}
-static int mdss_dsi_parse_panel_features(struct device_node *np,
- struct mdss_dsi_ctrl_pdata *ctrl)
+static void mdss_dsi_parse_partial_update_caps(struct device_node *np,
+ struct mdss_dsi_ctrl_pdata *ctrl)
{
struct mdss_panel_info *pinfo;
-
- if (!np || !ctrl) {
- pr_err("%s: Invalid arguments\n", __func__);
- return -ENODEV;
- }
+ const char *data;
pinfo = &ctrl->panel_data.panel_info;
- pinfo->partial_update_supported = of_property_read_bool(np,
- "qcom,partial-update-enabled");
+ data = of_get_property(np, "qcom,partial-update-enabled", NULL);
+ if (data && !strcmp(data, "single_roi"))
+ pinfo->partial_update_supported =
+ PU_SINGLE_ROI;
+ else if (data && !strcmp(data, "dual_roi"))
+ pinfo->partial_update_supported =
+ PU_DUAL_ROI;
+ else if (data && !strcmp(data, "none"))
+ pinfo->partial_update_supported =
+ PU_NOT_SUPPORTED;
+ else
+ pinfo->partial_update_supported =
+ PU_NOT_SUPPORTED;
+
if (pinfo->mipi.mode == DSI_CMD_MODE) {
pinfo->partial_update_enabled = pinfo->partial_update_supported;
pr_info("%s: partial_update_enabled=%d\n", __func__,
@@ -1852,6 +1926,21 @@ static int mdss_dsi_parse_panel_features(struct device_node *np,
"qcom,partial-update-roi-merge");
}
}
+}
+
+static int mdss_dsi_parse_panel_features(struct device_node *np,
+ struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ struct mdss_panel_info *pinfo;
+
+ if (!np || !ctrl) {
+ pr_err("%s: Invalid arguments\n", __func__);
+ return -ENODEV;
+ }
+
+ pinfo = &ctrl->panel_data.panel_info;
+
+ mdss_dsi_parse_partial_update_caps(np, ctrl);
pinfo->dcs_cmd_by_left = of_property_read_bool(np,
"qcom,dcs-cmd-by-left");
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index fc8d3898351e..08e06c75522a 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -564,7 +564,7 @@ static ssize_t mdss_fb_get_panel_info(struct device *dev,
"min_fps=%d\nmax_fps=%d\npanel_name=%s\n"
"primary_panel=%d\nis_pluggable=%d\ndisplay_id=%s\n"
"is_cec_supported=%d\nis_pingpong_split=%d\n"
- "dfps_porch_mode=%d\n",
+ "dfps_porch_mode=%d\npu_roi_cnt=%d\ndual_dsi=%d",
pinfo->partial_update_enabled,
pinfo->roi_alignment.xstart_pix_align,
pinfo->roi_alignment.width_pix_align,
@@ -577,7 +577,8 @@ static ssize_t mdss_fb_get_panel_info(struct device *dev,
pinfo->panel_name, pinfo->is_prim_panel,
pinfo->is_pluggable, pinfo->display_id,
pinfo->is_cec_supported, is_pingpong_split(mfd),
- dfps_porch_mode);
+ dfps_porch_mode, pinfo->partial_update_enabled,
+ is_panel_split(mfd));
return ret;
}
@@ -615,8 +616,8 @@ static ssize_t mdss_fb_force_panel_dead(struct device *dev,
return len;
}
- if (sscanf(buf, "%d", &pdata->panel_info.panel_force_dead) != 1)
- pr_err("sccanf buf error!\n");
+ if (kstrtouint(buf, 0, &pdata->panel_info.panel_force_dead))
+ pr_err("kstrtouint buf error!\n");
return len;
}
@@ -729,8 +730,8 @@ static ssize_t mdss_fb_change_dfps_mode(struct device *dev,
}
pinfo = &pdata->panel_info;
- if (sscanf(buf, "%d", &dfps_mode) != 1) {
- pr_err("sccanf buf error!\n");
+ if (kstrtouint(buf, 0, &dfps_mode)) {
+ pr_err("kstrtouint buf error!\n");
return len;
}
@@ -1280,6 +1281,7 @@ static int mdss_fb_remove(struct platform_device *pdev)
return -EINVAL;
mdss_fb_unregister_input_handler(mfd);
+ mdss_panel_debugfs_cleanup(mfd->panel_info);
if (mdss_fb_suspend_sub(mfd))
pr_err("msm_fb_remove: can't stop the device %d\n",
@@ -2702,7 +2704,9 @@ static int mdss_fb_release_all(struct fb_info *info, bool release_all)
* enabling ahead of unblank. for some special cases like
* adb shell stop/start.
*/
+ mutex_lock(&mfd->bl_lock);
mdss_fb_set_backlight(mfd, 0);
+ mutex_unlock(&mfd->bl_lock);
ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info,
mfd->op_enable);
@@ -3279,6 +3283,7 @@ int mdss_fb_atomic_commit(struct fb_info *info,
mfd->msm_fb_backup.atomic_commit = true;
mfd->msm_fb_backup.disp_commit.l_roi = commit_v1->left_roi;
mfd->msm_fb_backup.disp_commit.r_roi = commit_v1->right_roi;
+ mfd->msm_fb_backup.disp_commit.flags = commit_v1->flags;
mutex_lock(&mfd->mdp_sync_pt_data.sync_mutex);
atomic_inc(&mfd->mdp_sync_pt_data.commit_cnt);
diff --git a/drivers/video/fbdev/msm/mdss_hdcp.h b/drivers/video/fbdev/msm/mdss_hdcp.h
index d373d22384e8..6e347a867366 100644
--- a/drivers/video/fbdev/msm/mdss_hdcp.h
+++ b/drivers/video/fbdev/msm/mdss_hdcp.h
@@ -55,6 +55,7 @@ struct hdcp_init_data {
struct hdcp_ops {
int (*isr)(void *ptr);
+ int (*cp_irq)(void *ptr);
int (*reauthenticate)(void *input);
int (*authenticate)(void *hdcp_ctrl);
bool (*feature_supported)(void *input);
diff --git a/drivers/video/fbdev/msm/mdss_hdcp_1x.c b/drivers/video/fbdev/msm/mdss_hdcp_1x.c
index 1e502cf750a6..a8182c2f0e76 100644
--- a/drivers/video/fbdev/msm/mdss_hdcp_1x.c
+++ b/drivers/video/fbdev/msm/mdss_hdcp_1x.c
@@ -153,6 +153,7 @@ struct hdcp_reg_set {
u32 sec_data12;
u32 reset;
+ u32 reset_bit;
};
#define HDCP_REG_SET_CLIENT_HDMI \
@@ -175,7 +176,7 @@ struct hdcp_reg_set {
HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA10, \
HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA11, \
HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA12, \
- HDMI_HDCP_RESET}
+ HDMI_HDCP_RESET, BIT(0)}
#define HDCP_REG_SET_CLIENT_DP \
{DP_HDCP_STATUS, 16, 14, 13, DP_HDCP_CTRL, \
@@ -193,7 +194,8 @@ struct hdcp_reg_set {
HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA9, \
HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA10, \
HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA11, \
- HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12, 0}
+ HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12, \
+ DP_SW_RESET, BIT(1)}
#define HDCP_HDMI_SINK_ADDR_MAP \
{{"bcaps", 0x40, 1}, {"bksv", 0x00, 5}, {"r0'", 0x08, 2}, \
@@ -1295,6 +1297,9 @@ static void hdcp_1x_int_work(struct work_struct *work)
return;
}
+ if (hdcp_ctrl->hdcp_state == HDCP_STATE_AUTHENTICATED)
+ hdcp1_set_enc(false);
+
mutex_lock(hdcp_ctrl->init_data.mutex);
hdcp_ctrl->hdcp_state = HDCP_STATE_AUTH_FAIL;
mutex_unlock(hdcp_ctrl->init_data.mutex);
@@ -1383,6 +1388,8 @@ error:
hdcp_ctrl->init_data.cb_data,
hdcp_ctrl->hdcp_state);
}
+
+ hdcp1_set_enc(true);
} else {
DEV_DBG("%s: %s: HDCP state changed during authentication\n",
__func__, HDCP_STATE_NAME);
@@ -1431,7 +1438,7 @@ int hdcp_1x_reauthenticate(void *input)
struct hdcp_reg_set *reg_set;
struct hdcp_int_set *isr;
u32 hdmi_hw_version;
- u32 ret = 0;
+ u32 ret = 0, reg;
if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -1462,15 +1469,17 @@ int hdcp_1x_reauthenticate(void *input)
/* Disable HDCP interrupts */
DSS_REG_W(io, isr->int_reg, DSS_REG_R(io, isr->int_reg) & ~HDCP_INT_EN);
- if (reg_set->reset)
- DSS_REG_W(io, reg_set->reset, BIT(0));
+ reg = DSS_REG_R(io, reg_set->reset);
+ DSS_REG_W(io, reg_set->reset, reg | reg_set->reset_bit);
/* Disable encryption and disable the HDCP block */
DSS_REG_W(io, reg_set->ctrl, 0);
+ DSS_REG_W(io, reg_set->reset, reg & ~reg_set->reset_bit);
+
if (!hdcp_1x_load_keys(input))
queue_delayed_work(hdcp_ctrl->init_data.workq,
- &hdcp_ctrl->hdcp_auth_work, HZ/2);
+ &hdcp_ctrl->hdcp_auth_work, HZ);
else
queue_work(hdcp_ctrl->init_data.workq,
&hdcp_ctrl->hdcp_int_work);
@@ -1485,6 +1494,7 @@ void hdcp_1x_off(void *input)
struct hdcp_reg_set *reg_set;
struct hdcp_int_set *isr;
int rc = 0;
+ u32 reg;
if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -1501,6 +1511,9 @@ void hdcp_1x_off(void *input)
return;
}
+ if (hdcp_ctrl->hdcp_state == HDCP_STATE_AUTHENTICATED)
+ hdcp1_set_enc(false);
+
/*
* Disable HDCP interrupts.
* Also, need to set the state to inactive here so that any ongoing
@@ -1527,12 +1540,15 @@ void hdcp_1x_off(void *input)
DEV_DBG("%s: %s: Deleted hdcp int work\n", __func__,
HDCP_STATE_NAME);
- if (reg_set->reset)
- DSS_REG_W(io, reg_set->reset, BIT(0));
+
+ reg = DSS_REG_R(io, reg_set->reset);
+ DSS_REG_W(io, reg_set->reset, reg | reg_set->reset_bit);
/* Disable encryption and disable the HDCP block */
DSS_REG_W(io, reg_set->ctrl, 0);
+ DSS_REG_W(io, reg_set->reset, reg & ~reg_set->reset_bit);
+
DEV_DBG("%s: %s: HDCP: Off\n", __func__, HDCP_STATE_NAME);
} /* hdcp_1x_off */
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
index 2047a047b537..b90ac82049c6 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
@@ -831,14 +831,10 @@ static const u8 *hdmi_edid_find_block(const u8 *in_buf, u32 start_offset,
u32 offset = start_offset;
u32 dbc_offset = in_buf[2];
- if (dbc_offset >= EDID_BLOCK_SIZE - EDID_DTD_LEN)
- return NULL;
- *len = 0;
-
/*
* * edid buffer 1, byte 2 being 4 means no non-DTD/Data block
* collection present.
- * * edid buffer 1, byte 2 being 0 menas no non-DTD/DATA block
+ * * edid buffer 1, byte 2 being 0 means no non-DTD/DATA block
* collection present and no DTD data present.
*/
if ((dbc_offset == 0) || (dbc_offset == 4)) {
@@ -858,8 +854,6 @@ static const u8 *hdmi_edid_find_block(const u8 *in_buf, u32 start_offset,
}
offset += 1 + block_len;
}
- DEV_WARN("%s: EDID: type=%d block not found in EDID block\n",
- __func__, type);
return NULL;
} /* hdmi_edid_find_block */
@@ -1602,7 +1596,6 @@ static void hdmi_edid_detail_desc(struct hdmi_edid_ctrl *edid_ctrl,
if (rc < 0)
rc = hdmi_set_resv_timing_info(&timing);
} else {
- DEV_ERR("%s: Invalid frame data\n", __func__);
rc = -EINVAL;
}
@@ -1611,7 +1604,6 @@ static void hdmi_edid_detail_desc(struct hdmi_edid_ctrl *edid_ctrl,
DEV_DBG("%s: DTD mode found: %d\n", __func__, *disp_mode);
} else {
*disp_mode = HDMI_VFRMT_UNKNOWN;
- DEV_ERR("%s: error adding mode from DTD: %d\n", __func__, rc);
}
} /* hdmi_edid_detail_desc */
@@ -1987,7 +1979,6 @@ static void hdmi_edid_get_display_mode(struct hdmi_edid_ctrl *edid_ctrl)
u32 has60hz_mode = false;
u32 has50hz_mode = false;
u32 desc_offset = 0;
- bool read_block0_res = false;
struct hdmi_edid_sink_data *sink_data = NULL;
if (!edid_ctrl) {
@@ -2004,12 +1995,6 @@ static void hdmi_edid_get_display_mode(struct hdmi_edid_ctrl *edid_ctrl)
hdmi_edid_find_block(data_buf+0x80, DBC_START_OFFSET,
VIDEO_DATA_BLOCK, &len) : NULL;
- if (num_of_cea_blocks && (len == 0 || len > MAX_DATA_BLOCK_SIZE)) {
- DEV_DBG("%s: fall back to block 0 res\n", __func__);
- svd = NULL;
- read_block0_res = true;
- }
-
sink_data = &edid_ctrl->sink_data;
sink_data->disp_multi_3d_mode_list_cnt = 0;
@@ -2059,20 +2044,21 @@ static void hdmi_edid_get_display_mode(struct hdmi_edid_ctrl *edid_ctrl)
edid_blk0+0x36+desc_offset,
&video_format);
- DEV_DBG("[%s:%d] Block-0 Adding vid fmt = [%s]\n",
- __func__, __LINE__,
- msm_hdmi_mode_2string(video_format));
+ if (video_format != HDMI_VFRMT_UNKNOWN) {
+ DEV_DBG("[%s:%d] Block-0 Adding vid fmt = [%s]\n",
+ __func__, __LINE__,
+ msm_hdmi_mode_2string(video_format));
- hdmi_edid_add_sink_video_format(edid_ctrl,
- video_format);
+ hdmi_edid_add_sink_video_format(edid_ctrl,
+ video_format);
- if (video_format == HDMI_VFRMT_640x480p60_4_3)
- has480p = true;
+ if (video_format == HDMI_VFRMT_640x480p60_4_3)
+ has480p = true;
- /* Make a note of the preferred video format */
- if (i == 0) {
- sink_data->preferred_video_format =
- video_format;
+ /* Make a note of the preferred video format */
+ if (i == 0)
+ sink_data->preferred_video_format =
+ video_format;
}
desc_offset += 0x12;
++i;
@@ -2088,28 +2074,32 @@ static void hdmi_edid_get_display_mode(struct hdmi_edid_ctrl *edid_ctrl)
* * EDID_BLOCK_SIZE = 0x80 Each page size in the EDID ROM
*/
desc_offset = edid_blk1[0x02];
- i = 0;
- while (!edid_blk1[desc_offset]) {
- hdmi_edid_detail_desc(edid_ctrl,
- edid_blk1+desc_offset,
- &video_format);
-
- DEV_DBG("[%s:%d] Block-1 Adding vid fmt = [%s]\n",
- __func__, __LINE__,
- msm_hdmi_mode_2string(video_format));
-
- hdmi_edid_add_sink_video_format(edid_ctrl,
- video_format);
- if (video_format == HDMI_VFRMT_640x480p60_4_3)
- has480p = true;
-
- /* Make a note of the preferred video format */
- if (i == 0) {
- sink_data->preferred_video_format =
- video_format;
+ if (desc_offset < (EDID_BLOCK_SIZE - EDID_DTD_LEN)) {
+ i = 0;
+ while (!edid_blk1[desc_offset]) {
+ hdmi_edid_detail_desc(edid_ctrl,
+ edid_blk1+desc_offset,
+ &video_format);
+
+ if (video_format != HDMI_VFRMT_UNKNOWN) {
+ DEV_DBG("%s Block-1 Adding vid fmt = [%s]\n",
+ __func__,
+ msm_hdmi_mode_2string(video_format));
+
+ hdmi_edid_add_sink_video_format(edid_ctrl,
+ video_format);
+ if (video_format == HDMI_VFRMT_640x480p60_4_3)
+ has480p = true;
+
+ /* Make a note of the preferred video format */
+ if (i == 0) {
+ sink_data->preferred_video_format =
+ video_format;
+ }
+ }
+ desc_offset += 0x12;
+ ++i;
}
- desc_offset += 0x12;
- ++i;
}
std_blk = 0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index a0637109c7b3..1dae41391795 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -47,6 +47,8 @@
#include <linux/msm-bus-board.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/rpm-smd.h>
+#include "soc/qcom/secure_buffer.h"
+#include <asm/cacheflush.h>
#include "mdss.h"
#include "mdss_fb.h"
@@ -64,6 +66,8 @@
#define RES_1080p (1088*1920)
#define RES_UHD (3840*2160)
+#define MDP_DEVICE_ID 0x1A
+
struct mdss_data_type *mdss_res;
static u32 mem_protect_sd_ctrl_id;
@@ -87,6 +91,7 @@ struct msm_mdp_interface mdp5 = {
#define MEM_PROTECT_SD_CTRL 0xF
#define MEM_PROTECT_SD_CTRL_FLAT 0x14
+#define MEM_PROTECT_SD_CTRL_SWITCH 0x18
static DEFINE_SPINLOCK(mdp_lock);
static DEFINE_SPINLOCK(mdss_mdp_intr_lock);
@@ -1329,7 +1334,9 @@ int mdss_iommu_ctrl(int enable)
if (mdata->iommu_ref_cnt == 0) {
rc = mdss_smmu_detach(mdata);
if (mdss_has_quirk(mdata,
- MDSS_QUIRK_MIN_BUS_VOTE))
+ MDSS_QUIRK_MIN_BUS_VOTE) &&
+ (!mdata->sec_disp_en ||
+ !mdata->sec_cam_en))
mdss_bus_scale_set_quota(MDSS_HW_RT,
0, 0);
}
@@ -1985,6 +1992,7 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
mdata->pixel_ram_size = 50 * 1024;
mdata->rects_per_sspp[MDSS_MDP_PIPE_TYPE_DMA] = 2;
+ mem_protect_sd_ctrl_id = MEM_PROTECT_SD_CTRL_SWITCH;
set_bit(MDSS_QOS_PER_PIPE_IB, mdata->mdss_qos_map);
set_bit(MDSS_QOS_REMAPPER, mdata->mdss_qos_map);
set_bit(MDSS_QOS_TS_PREFILL, mdata->mdss_qos_map);
@@ -2015,6 +2023,7 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
mdata->has_wb_ubwc = true;
set_bit(MDSS_CAPS_10_BIT_SUPPORTED, mdata->mdss_caps_map);
set_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map);
+ set_bit(MDSS_CAPS_SEC_DETACH_SMMU, mdata->mdss_caps_map);
break;
default:
mdata->max_target_zorder = 4; /* excluding base layer */
@@ -2605,7 +2614,7 @@ static ssize_t mdss_mdp_store_max_limit_bw(struct device *dev,
struct mdss_data_type *mdata = dev_get_drvdata(dev);
u32 data = 0;
- if (1 != sscanf(buf, "%d", &data)) {
+ if (kstrtouint(buf, 0, &data)) {
pr_info("Not able scan to bw_mode_bitmap\n");
} else {
mdata->bw_mode_bitmap = data;
@@ -4939,29 +4948,115 @@ static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on)
}
}
-int mdss_mdp_secure_display_ctrl(unsigned int enable)
+int mdss_mdp_secure_session_ctrl(unsigned int enable, u64 flags)
{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
struct sd_ctrl_req {
unsigned int enable;
} __attribute__ ((__packed__)) request;
unsigned int resp = -1;
int ret = 0;
+ uint32_t sid_info;
struct scm_desc desc;
- desc.args[0] = request.enable = enable;
- desc.arginfo = SCM_ARGS(1);
+ if (test_bit(MDSS_CAPS_SEC_DETACH_SMMU, mdata->mdss_caps_map)) {
+ /*
+ * Prepare syscall to hypervisor to switch the secure_vmid
+ * between secure and non-secure contexts
+ */
+ /* MDP secure SID */
+ sid_info = 0x1;
+ desc.arginfo = SCM_ARGS(4, SCM_VAL, SCM_RW, SCM_VAL, SCM_VAL);
+ desc.args[0] = MDP_DEVICE_ID;
+ desc.args[1] = SCM_BUFFER_PHYS(&sid_info);
+ desc.args[2] = sizeof(uint32_t);
+
- if (!is_scm_armv8()) {
- ret = scm_call(SCM_SVC_MP, MEM_PROTECT_SD_CTRL,
- &request, sizeof(request), &resp, sizeof(resp));
- } else {
- ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
+ pr_debug("Enable/Disable: %d, Flags %llx\n", enable, flags);
+ if (enable) {
+ if (flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION) {
+ desc.args[3] = VMID_CP_SEC_DISPLAY;
+ mdata->sec_disp_en = 1;
+ } else if (flags & MDP_SECURE_CAMERA_OVERLAY_SESSION) {
+ desc.args[3] = VMID_CP_CAMERA_PREVIEW;
+ mdata->sec_cam_en = 1;
+ } else {
+ return 0;
+ }
+
+ /* detach smmu contexts */
+ ret = mdss_smmu_detach(mdata);
+ if (ret) {
+ pr_err("Error while detaching smmu contexts ret = %d\n",
+ ret);
+ return -EINVAL;
+ }
+
+ /* let the driver think smmu is still attached */
+ mdata->iommu_attached = true;
+
+ dmac_flush_range(&sid_info, &sid_info + 1);
+ ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
mem_protect_sd_ctrl_id), &desc);
- resp = desc.ret[0];
- }
+ if (ret) {
+ pr_err("Error scm_call MEM_PROTECT_SD_CTRL(%u) ret=%dm resp=%x\n",
+ enable, ret, resp);
+ return -EINVAL;
+ }
+ resp = desc.ret[0];
- pr_debug("scm_call MEM_PROTECT_SD_CTRL(%u): ret=%d, resp=%x",
+ pr_debug("scm_call MEM_PROTECT_SD_CTRL(%u): ret=%d, resp=%x\n",
+ enable, ret, resp);
+ } else {
+ desc.args[3] = VMID_CP_PIXEL;
+ if (flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION)
+ mdata->sec_disp_en = 0;
+ else if (flags & MDP_SECURE_CAMERA_OVERLAY_SESSION)
+ mdata->sec_cam_en = 0;
+
+ dmac_flush_range(&sid_info, &sid_info + 1);
+ ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
+ mem_protect_sd_ctrl_id), &desc);
+ if (ret)
+ MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl",
+ "dsi0_phy", "dsi1_ctrl",
+ "dsi1_phy", "vbif", "vbif_nrt",
+ "dbg_bus", "vbif_dbg_bus",
+ "panic");
+ resp = desc.ret[0];
+
+ pr_debug("scm_call MEM_PROTECT_SD_CTRL(%u): ret=%d, resp=%x\n",
+ enable, ret, resp);
+
+ /* re-attach smmu contexts */
+ mdata->iommu_attached = false;
+ ret = mdss_smmu_attach(mdata);
+ if (ret) {
+ pr_err("Error while attaching smmu contexts ret = %d\n",
+ ret);
+ return -EINVAL;
+ }
+ }
+ MDSS_XLOG(enable);
+ } else {
+ desc.args[0] = request.enable = enable;
+ desc.arginfo = SCM_ARGS(1);
+
+ if (!is_scm_armv8()) {
+ ret = scm_call(SCM_SVC_MP, MEM_PROTECT_SD_CTRL,
+ &request,
+ sizeof(request),
+ &resp,
+ sizeof(resp));
+ } else {
+ ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
+ mem_protect_sd_ctrl_id), &desc);
+ resp = desc.ret[0];
+ }
+
+ pr_debug("scm_call MEM_PROTECT_SD_CTRL(%u): ret=%d, resp=%x\n",
enable, ret, resp);
+ }
if (ret)
return ret;
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index 8ac63aaaefce..e1c3841c82de 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -92,6 +92,8 @@
*/
#define ENABLE_PIXEL_EXT_ONLY 0x80000000
+/* Pipe flag to indicate this pipe contains secure camera buffer */
+#define MDP_SECURE_CAMERA_OVERLAY_SESSION 0x100000000
/**
* Destination Scaler control flags setting
*
@@ -109,6 +111,9 @@
* QSEED3 parameters needs to be updated.
* @DS_ENHANCER_UPDATE: Setting this bit indicates current Desitnation Scaler
* QSEED3 Detial enhancer parameters need to be updated.
+ * @DS_VALIDATE: Indicate destination data structure parameters are validated
+ * and can be used for programming the HW and perform a flush.
+ * @DS_DIRTY_UPDATE: Mark for dirty update for Power resume usecase.
*/
#define DS_ENABLE BIT(0)
#define DS_DUAL_MODE BIT(1)
@@ -116,6 +121,8 @@
#define DS_RIGHT BIT(3)
#define DS_SCALE_UPDATE BIT(4)
#define DS_ENHANCER_UPDATE BIT(5)
+#define DS_VALIDATE BIT(6)
+#define DS_DIRTY_UPDATE BIT(7)
/**
* Destination Scaler DUAL mode overfetch pixel count
@@ -230,9 +237,13 @@ enum mdss_mdp_csc_type {
MDSS_MDP_CSC_YUV2RGB_601L,
MDSS_MDP_CSC_YUV2RGB_601FR,
MDSS_MDP_CSC_YUV2RGB_709L,
+ MDSS_MDP_CSC_YUV2RGB_2020L,
+ MDSS_MDP_CSC_YUV2RGB_2020FR,
MDSS_MDP_CSC_RGB2YUV_601L,
MDSS_MDP_CSC_RGB2YUV_601FR,
MDSS_MDP_CSC_RGB2YUV_709L,
+ MDSS_MDP_CSC_RGB2YUV_2020L,
+ MDSS_MDP_CSC_RGB2YUV_2020FR,
MDSS_MDP_CSC_YUV2YUV,
MDSS_MDP_CSC_RGB2RGB,
MDSS_MDP_MAX_CSC
@@ -370,6 +381,8 @@ struct mdss_mdp_destination_scaler {
char __iomem *lut_base;
u16 src_width;
u16 src_height;
+ u16 last_mixer_width;
+ u16 last_mixer_height;
u32 flags;
struct mdp_scale_data_v2 scaler;
};
@@ -404,7 +417,7 @@ struct mdss_mdp_ctl_intfs_ops {
/* to update lineptr, [1..yres] - enable, 0 - disable */
int (*update_lineptr)(struct mdss_mdp_ctl *ctl, bool enable);
- int (*avr_ctrl_fnc)(struct mdss_mdp_ctl *);
+ int (*avr_ctrl_fnc)(struct mdss_mdp_ctl *, bool enable);
};
struct mdss_mdp_cwb {
@@ -551,6 +564,8 @@ struct mdss_mdp_mixer {
bool valid_roi;
bool roi_changed;
struct mdss_rect roi;
+ bool dsc_enabled;
+ bool dsc_merge_enabled;
u8 cursor_enabled;
u16 cursor_hotx;
@@ -616,7 +631,7 @@ struct mdss_mdp_img_data {
dma_addr_t addr;
unsigned long len;
u32 offset;
- u32 flags;
+ u64 flags;
u32 dir;
u32 domain;
bool mapped;
@@ -800,7 +815,7 @@ struct mdss_mdp_pipe {
struct file *file;
bool is_handed_off;
- u32 flags;
+ u64 flags;
u32 bwc_mode;
/* valid only when pipe's output is crossing both layer mixers */
@@ -816,6 +831,9 @@ struct mdss_mdp_pipe {
struct mdss_mdp_format_params *src_fmt;
struct mdss_mdp_plane_sizes src_planes;
+ /* flag to re-store roi in case of pu dual-roi validation error */
+ bool restore_roi;
+
/* compression ratio from the source format */
struct mult_factor comp_ratio;
@@ -905,6 +923,7 @@ struct mdss_overlay_private {
u32 splash_mem_addr;
u32 splash_mem_size;
u32 sd_enabled;
+ u32 sc_enabled;
struct sw_sync_timeline *vsync_timeline;
struct mdss_mdp_vsync_handler vsync_retire_handler;
@@ -1278,6 +1297,15 @@ static inline void mdss_update_sd_client(struct mdss_data_type *mdata,
atomic_add_unless(&mdss_res->sd_client_count, -1, 0);
}
+static inline void mdss_update_sc_client(struct mdss_data_type *mdata,
+ bool status)
+{
+ if (status)
+ atomic_inc(&mdata->sc_client_count);
+ else
+ atomic_add_unless(&mdss_res->sc_client_count, -1, 0);
+}
+
static inline int mdss_mdp_get_wb_ctl_support(struct mdss_data_type *mdata,
bool rotator_session)
{
@@ -1401,6 +1429,10 @@ static inline uint8_t pp_vig_csc_pipe_val(struct mdss_mdp_pipe *pipe)
return MDSS_MDP_CSC_YUV2RGB_601L;
case MDP_CSC_ITU_R_601_FR:
return MDSS_MDP_CSC_YUV2RGB_601FR;
+ case MDP_CSC_ITU_R_2020:
+ return MDSS_MDP_CSC_YUV2RGB_2020L;
+ case MDP_CSC_ITU_R_2020_FR:
+ return MDSS_MDP_CSC_YUV2RGB_2020FR;
case MDP_CSC_ITU_R_709:
default:
return MDSS_MDP_CSC_YUV2RGB_709L;
@@ -1491,6 +1523,7 @@ static inline bool mdss_mdp_is_map_needed(struct mdss_data_type *mdata,
struct mdss_mdp_img_data *data)
{
u32 is_secure_ui = data->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION;
+ u64 is_secure_camera = data->flags & MDP_SECURE_CAMERA_OVERLAY_SESSION;
/*
* For ULT Targets we need SMMU Map, to issue map call for secure Display.
@@ -1498,6 +1531,10 @@ static inline bool mdss_mdp_is_map_needed(struct mdss_data_type *mdata,
if (is_secure_ui && !mdss_has_quirk(mdata, MDSS_QUIRK_NEED_SECURE_MAP))
return false;
+ if (is_secure_camera && test_bit(MDSS_CAPS_SEC_DETACH_SMMU,
+ mdata->mdss_caps_map))
+ return false;
+
return true;
}
@@ -1554,7 +1591,7 @@ unsigned long mdss_mdp_get_clk_rate(u32 clk_idx, bool locked);
int mdss_mdp_vsync_clk_enable(int enable, bool locked);
void mdss_mdp_clk_ctrl(int enable);
struct mdss_data_type *mdss_mdp_get_mdata(void);
-int mdss_mdp_secure_display_ctrl(unsigned int enable);
+int mdss_mdp_secure_session_ctrl(unsigned int enable, u64 flags);
int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd);
int mdss_mdp_dfps_update_params(struct msm_fb_data_type *mfd,
@@ -1588,7 +1625,7 @@ int mdss_mdp_overlay_start(struct msm_fb_data_type *mfd);
void mdss_mdp_overlay_set_chroma_sample(
struct mdss_mdp_pipe *pipe);
int mdp_pipe_tune_perf(struct mdss_mdp_pipe *pipe,
- u32 flags);
+ u64 flags);
int mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe);
struct mdss_mdp_pipe *mdss_mdp_pipe_assign(struct mdss_data_type *mdata,
struct mdss_mdp_mixer *mixer, u32 ndx,
@@ -1706,6 +1743,7 @@ void mdss_mdp_pp_term(struct device *dev);
int mdss_mdp_pp_overlay_init(struct msm_fb_data_type *mfd);
int mdss_mdp_pp_resume(struct msm_fb_data_type *mfd);
+void mdss_mdp_pp_dest_scaler_resume(struct mdss_mdp_ctl *ctl);
int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl);
int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl);
@@ -1820,7 +1858,7 @@ struct mult_factor *mdss_mdp_get_comp_factor(u32 format,
int mdss_mdp_data_map(struct mdss_mdp_data *data, bool rotator, int dir);
void mdss_mdp_data_free(struct mdss_mdp_data *data, bool rotator, int dir);
int mdss_mdp_data_get_and_validate_size(struct mdss_mdp_data *data,
- struct msmfb_data *planes, int num_planes, u32 flags,
+ struct msmfb_data *planes, int num_planes, u64 flags,
struct device *dev, bool rotator, int dir,
struct mdp_layer_buffer *buffer);
u32 mdss_get_panel_framerate(struct msm_fb_data_type *mfd);
@@ -1831,7 +1869,7 @@ void mdss_mdp_intersect_rect(struct mdss_rect *res_rect,
const struct mdss_rect *sci_rect);
void mdss_mdp_crop_rect(struct mdss_rect *src_rect,
struct mdss_rect *dst_rect,
- const struct mdss_rect *sci_rect);
+ const struct mdss_rect *sci_rect, bool normalize);
void rect_copy_mdss_to_mdp(struct mdp_rect *user, struct mdss_rect *kernel);
void rect_copy_mdp_to_mdss(struct mdp_rect *user, struct mdss_rect *kernel);
bool mdss_rect_overlap_check(struct mdss_rect *rect1, struct mdss_rect *rect2);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index eb1e0b5c47a6..9ed44937efe6 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -728,7 +728,7 @@ int mdss_mdp_get_pipe_overlap_bw(struct mdss_mdp_pipe *pipe,
/* crop rectangles */
if (roi && !mixer->ctl->is_video_mode && !pipe->src_split_req)
- mdss_mdp_crop_rect(&src, &dst, roi);
+ mdss_mdp_crop_rect(&src, &dst, roi, true);
/*
* when doing vertical decimation lines will be skipped, hence there is
@@ -1108,7 +1108,7 @@ int mdss_mdp_perf_calc_pipe(struct mdss_mdp_pipe *pipe,
/* crop rectangles */
if (roi && !mixer->ctl->is_video_mode && !pipe->src_split_req)
- mdss_mdp_crop_rect(&src, &dst, roi);
+ mdss_mdp_crop_rect(&src, &dst, roi, true);
pr_debug("v_total=%d, xres=%d fps=%d\n", v_total, xres, fps);
pr_debug("src(w,h)(%d,%d) dst(w,h)(%d,%d) dst_y=%d bpp=%d yuv=%d\n",
@@ -2743,6 +2743,7 @@ static inline void __dsc_enable(struct mdss_mdp_mixer *mixer)
{
mdss_mdp_pingpong_write(mixer->pingpong_base,
MDSS_MDP_REG_PP_DSC_MODE, 1);
+ mixer->dsc_enabled = true;
}
static inline void __dsc_disable(struct mdss_mdp_mixer *mixer)
@@ -2762,6 +2763,13 @@ static inline void __dsc_disable(struct mdss_mdp_mixer *mixer)
return;
}
writel_relaxed(0, offset + MDSS_MDP_REG_DSC_COMMON_MODE);
+ mixer->dsc_enabled = false;
+ mixer->dsc_merge_enabled = false;
+}
+
+static bool __is_dsc_merge_enabled(u32 common_mode)
+{
+ return common_mode & BIT(1);
}
static void __dsc_config(struct mdss_mdp_mixer *mixer,
@@ -2774,6 +2782,7 @@ static void __dsc_config(struct mdss_mdp_mixer *mixer,
u32 initial_lines = dsc->initial_lines;
bool is_cmd_mode = !(mode & BIT(2));
+ mixer->dsc_merge_enabled = __is_dsc_merge_enabled(mode);
data = mdss_mdp_pingpong_read(mixer->pingpong_base,
MDSS_MDP_REG_PP_DCE_DATA_OUT_SWAP);
data |= BIT(18); /* endian flip */
@@ -2928,11 +2937,6 @@ static void __dsc_config_thresh(struct mdss_mdp_mixer *mixer,
}
}
-static bool __is_dsc_merge_enabled(u32 common_mode)
-{
- return common_mode & BIT(1);
-}
-
static bool __dsc_is_3d_mux_enabled(struct mdss_mdp_ctl *ctl,
struct mdss_panel_info *pinfo)
{
@@ -3291,8 +3295,19 @@ void mdss_mdp_ctl_dsc_setup(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_ctl *sctl;
struct mdss_panel_info *spinfo;
- if (!is_dsc_compression(pinfo))
+ /*
+ * Check for dynamic resolution switch from DSC On to DSC Off
+ * and disable DSC
+ */
+ if ((ctl->pending_mode_switch == SWITCH_RESOLUTION) &&
+ ctl->is_master &&
+ (!is_dsc_compression(pinfo))) {
+ if (ctl->mixer_left && ctl->mixer_left->dsc_enabled)
+ __dsc_disable(ctl->mixer_left);
+ if (ctl->mixer_right && ctl->mixer_right->dsc_enabled)
+ __dsc_disable(ctl->mixer_right);
return;
+ }
if (!ctl->is_master) {
pr_debug("skip slave ctl because master will program for both\n");
@@ -3508,7 +3523,9 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl)
if (is_dest_scaling_enable(ctl->mixer_left)) {
width = get_ds_input_width(ctl->mixer_left);
height = get_ds_input_height(ctl->mixer_left);
- if (ctl->panel_data->next && is_pingpong_split(ctl->mfd))
+ if (is_dual_lm_single_display(ctl->mfd) ||
+ (ctl->panel_data->next &&
+ is_pingpong_split(ctl->mfd)))
width *= 2;
} else {
width = get_panel_width(ctl);
@@ -3548,9 +3565,13 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl)
}
if (split_fb) {
- width = ctl->mfd->split_fb_left;
- width += (pinfo->lcdc.border_left +
- pinfo->lcdc.border_right);
+ if (is_dest_scaling_enable(ctl->mixer_left)) {
+ width = get_ds_input_width(ctl->mixer_left);
+ } else {
+ width = ctl->mfd->split_fb_left;
+ width += (pinfo->lcdc.border_left +
+ pinfo->lcdc.border_right);
+ }
} else if (width > max_mixer_width) {
width /= 2;
}
@@ -3576,8 +3597,12 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl)
return 0;
}
- if (split_fb)
- width = ctl->mfd->split_fb_right;
+ if (split_fb) {
+ if (is_dest_scaling_enable(ctl->mixer_left))
+ width = get_ds_input_width(ctl->mixer_left);
+ else
+ width = ctl->mfd->split_fb_right;
+ }
if (width < ctl->width) {
if (ctl->mixer_right == NULL) {
@@ -3693,6 +3718,30 @@ skip_intf_reconfig:
ctl->mixer_right->width = ctl->width / 2;
ctl->mixer_right->height = ctl->height;
}
+
+ /*
+ * If we are transitioning from DSC On + DSC Merge to DSC Off
+ * the 3D mux needs to be enabled
+ */
+ if (!is_dsc_compression(&pdata->panel_info) &&
+ ctl->mixer_left &&
+ ctl->mixer_left->dsc_enabled &&
+ ctl->mixer_left->dsc_merge_enabled) {
+ ctl->opmode |= MDSS_MDP_CTL_OP_PACK_3D_ENABLE |
+ MDSS_MDP_CTL_OP_PACK_3D_H_ROW_INT;
+ }
+
+ /*
+ * If we are transitioning from DSC Off to DSC On + DSC Merge
+ * the 3D mux needs to be disabled
+ */
+ if (is_dsc_compression(&pdata->panel_info) &&
+ ctl->mixer_left &&
+ !ctl->mixer_left->dsc_enabled &&
+ pdata->panel_info.dsc_enc_total != 1) {
+ ctl->opmode &= ~(MDSS_MDP_CTL_OP_PACK_3D_ENABLE |
+ MDSS_MDP_CTL_OP_PACK_3D_H_ROW_INT);
+ }
} else {
/*
* Handles MDP_SPLIT_MODE_NONE, MDP_DUAL_LM_DUAL_DISPLAY and
@@ -3707,7 +3756,6 @@ skip_intf_reconfig:
ctl->border_x_off = pdata->panel_info.lcdc.border_left;
ctl->border_y_off = pdata->panel_info.lcdc.border_top;
-
return ret;
}
@@ -4038,6 +4086,7 @@ static void mdss_mdp_ctl_restore_sub(struct mdss_mdp_ctl *ctl)
if (ctl->mfd && ctl->panel_data) {
ctl->mfd->ipc_resume = true;
mdss_mdp_pp_resume(ctl->mfd);
+ mdss_mdp_pp_dest_scaler_resume(ctl);
if (is_dsc_compression(&ctl->panel_data->panel_info)) {
/*
@@ -5593,7 +5642,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
if (ctl->ops.avr_ctrl_fnc) {
- ret = ctl->ops.avr_ctrl_fnc(ctl);
+ ret = ctl->ops.avr_ctrl_fnc(ctl, true);
if (ret) {
pr_err("error configuring avr ctrl registers ctl=%d err=%d\n",
ctl->num, ret);
@@ -5603,7 +5652,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
}
if (sctl && sctl->ops.avr_ctrl_fnc) {
- ret = sctl->ops.avr_ctrl_fnc(sctl);
+ ret = sctl->ops.avr_ctrl_fnc(sctl, true);
if (ret) {
pr_err("error configuring avr ctrl registers sctl=%d err=%d\n",
sctl->num, ret);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_debug.c b/drivers/video/fbdev/msm/mdss_mdp_debug.c
index 4c4fa9ea98d0..711d2d222c7d 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_debug.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_debug.c
@@ -1868,7 +1868,7 @@ static void __dump_pipe(struct seq_file *s, struct mdss_mdp_pipe *pipe)
int smps[4];
int i;
- seq_printf(s, "\nSSPP #%d type=%s ndx=%x flags=0x%08x play_cnt=%u xin_id=%d\n",
+ seq_printf(s, "\nSSPP #%d type=%s ndx=%x flags=0x%16llx play_cnt=%u xin_id=%d\n",
pipe->num, mdss_mdp_pipetype2str(pipe->type),
pipe->ndx, pipe->flags, pipe->play_cnt, pipe->xin_id);
seq_printf(s, "\tstage=%d alpha=0x%x transp=0x%x blend_op=%d\n",
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
index 4eb121f01aca..f08af5d6edd3 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
@@ -2029,8 +2029,22 @@ static void mdss_mdp_cmd_dsc_reconfig(struct mdss_mdp_ctl *ctl)
return;
pinfo = &ctl->panel_data->panel_info;
- if (pinfo->compression_mode != COMPRESSION_DSC)
- return;
+ if (pinfo->compression_mode != COMPRESSION_DSC) {
+ /*
+ * Check for a dynamic resolution switch from DSC On to
+ * DSC Off and call mdss_mdp_ctl_dsc_setup to disable DSC
+ */
+ if (ctl->pending_mode_switch == SWITCH_RESOLUTION) {
+ if (ctl->mixer_left && ctl->mixer_left->dsc_enabled)
+ changed = true;
+ if (is_split_lm(ctl->mfd) &&
+ ctl->mixer_right &&
+ ctl->mixer_right->dsc_enabled)
+ changed = true;
+ } else {
+ return;
+ }
+ }
changed = ctl->mixer_left->roi_changed;
if (is_split_lm(ctl->mfd))
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
index d316ab6d263a..048e5fce30c6 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
@@ -108,6 +108,8 @@ static void mdss_mdp_fetch_end_config(struct mdss_mdp_video_ctx *ctx,
static void early_wakeup_dfps_update_work(struct work_struct *work);
+static int mdss_mdp_video_avr_ctrl(struct mdss_mdp_ctl *ctl, bool enable);
+
static inline void mdp_video_write(struct mdss_mdp_video_ctx *ctx,
u32 reg, u32 val)
{
@@ -459,13 +461,15 @@ static int mdss_mdp_video_avr_trigger_setup(struct mdss_mdp_ctl *ctl)
}
static void mdss_mdp_video_avr_ctrl_setup(struct mdss_mdp_video_ctx *ctx,
- struct mdss_mdp_avr_info *avr_info, bool is_master)
+ struct mdss_mdp_avr_info *avr_info, bool is_master, bool enable)
{
u32 avr_ctrl = 0;
u32 avr_mode = 0;
- avr_ctrl = avr_info->avr_enabled;
- avr_mode = avr_info->avr_mode;
+ if (enable) {
+ avr_ctrl = avr_info->avr_enabled;
+ avr_mode = avr_info->avr_mode;
+ }
/* Enable avr_vsync_clear_en bit to clear avr in next vsync */
if (avr_mode == MDSS_MDP_AVR_ONE_SHOT)
@@ -1429,6 +1433,20 @@ static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl, int new_fps)
}
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
+
+ /*
+ * Need to disable AVR during DFPS update period.
+ * Next commit will restore the AVR settings.
+ */
+ if (test_bit(MDSS_CAPS_AVR_SUPPORTED,
+ mdata->mdss_caps_map) &&
+ ctl->avr_info.avr_enabled) {
+ mdss_mdp_video_avr_ctrl(ctl, false);
+ rc = mdss_mdp_video_dfps_wait4vsync(ctl);
+ if (rc < 0)
+ pr_err("Error in dfps_wait: %d\n", rc);
+ }
+
spin_lock_irqsave(&ctx->dfps_lock, flags);
if (mdata->mdp_rev < MDSS_MDP_HW_REV_105) {
@@ -2112,6 +2130,7 @@ static void early_wakeup_dfps_update_work(struct work_struct *work)
struct mdss_panel_info *pinfo;
struct msm_fb_data_type *mfd;
struct mdss_mdp_ctl *ctl;
+ struct mdss_data_type *mdata;
struct dynamic_fps_data data = {0};
int ret = 0;
int dfps;
@@ -2123,7 +2142,8 @@ static void early_wakeup_dfps_update_work(struct work_struct *work)
ctl = ctx->ctl;
- if (!ctl || !ctl->panel_data || !ctl->mfd || !ctl->mfd->fbi) {
+ if (!ctl || !ctl->panel_data || !ctl->mfd || !ctl->mfd->fbi ||
+ !ctl->mdata) {
pr_err("%s: invalid ctl\n", __func__);
return;
}
@@ -2131,6 +2151,7 @@ static void early_wakeup_dfps_update_work(struct work_struct *work)
pdata = ctl->panel_data;
pinfo = &ctl->panel_data->panel_info;
mfd = ctl->mfd;
+ mdata = ctl->mdata;
if (!pinfo->dynamic_fps || !ctl->ops.config_fps_fnc ||
!pdata->panel_info.default_fps) {
@@ -2138,6 +2159,17 @@ static void early_wakeup_dfps_update_work(struct work_struct *work)
return;
}
+ /*
+ * Bypass DFPS update when AVR is enabled because
+ * AVR will take control of the programmable fetch
+ */
+ if (test_bit(MDSS_CAPS_AVR_SUPPORTED,
+ mdata->mdss_caps_map) &&
+ ctl->avr_info.avr_enabled) {
+ pr_debug("Bypass DFPS update when AVR is enabled\n");
+ return;
+ }
+
/* get the default fps that was cached before any dfps update */
dfps = pdata->panel_info.default_fps;
@@ -2213,7 +2245,7 @@ static int mdss_mdp_video_early_wake_up(struct mdss_mdp_ctl *ctl)
return 0;
}
-static int mdss_mdp_video_avr_ctrl(struct mdss_mdp_ctl *ctl)
+static int mdss_mdp_video_avr_ctrl(struct mdss_mdp_ctl *ctl, bool enable)
{
struct mdss_mdp_video_ctx *ctx = NULL, *sctx = NULL;
@@ -2222,7 +2254,8 @@ static int mdss_mdp_video_avr_ctrl(struct mdss_mdp_ctl *ctl)
pr_err("invalid master ctx\n");
return -EINVAL;
}
- mdss_mdp_video_avr_ctrl_setup(ctx, &ctl->avr_info, ctl->is_master);
+ mdss_mdp_video_avr_ctrl_setup(ctx, &ctl->avr_info, ctl->is_master,
+ enable);
if (is_pingpong_split(ctl->mfd)) {
sctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[SLAVE_CTX];
@@ -2230,7 +2263,8 @@ static int mdss_mdp_video_avr_ctrl(struct mdss_mdp_ctl *ctl)
pr_err("invalid slave ctx\n");
return -EINVAL;
}
- mdss_mdp_video_avr_ctrl_setup(sctx, &ctl->avr_info, false);
+ mdss_mdp_video_avr_ctrl_setup(sctx, &ctl->avr_info, false,
+ enable);
}
return 0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index e26b3843d7b0..036e4e39efda 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_layer.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c
@@ -67,13 +67,89 @@ static inline void *u64_to_ptr(uint64_t address)
return (void *)(uintptr_t)address;
}
+static void mdss_mdp_disable_destination_scaler_setup(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_data_type *mdata = ctl->mdata;
+ struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info;
+
+ if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map)) {
+ if (ctl->mixer_left && ctl->mixer_right &&
+ ctl->mixer_left->ds && ctl->mixer_right->ds &&
+ ctl->mixer_left->ds->scaler.enable &&
+ ctl->mixer_right->ds->scaler.enable) {
+ /*
+ * DUAL mode disable
+ */
+ ctl->mixer_left->width = get_panel_width(ctl);
+ ctl->mixer_left->height = get_panel_yres(pinfo);
+ ctl->mixer_left->width /= 2;
+ ctl->mixer_right->width = ctl->mixer_left->width;
+ ctl->mixer_right->height = ctl->mixer_left->height;
+ ctl->mixer_left->roi = (struct mdss_rect) { 0, 0,
+ ctl->mixer_left->width,
+ ctl->mixer_left->height };
+ ctl->mixer_right->roi = (struct mdss_rect) { 0, 0,
+ ctl->mixer_right->width,
+ ctl->mixer_right->height };
+
+ /*
+ * Disable destination scaler by resetting the control
+ * flags and also need to disable in the QSEED3
+ * settings.
+ */
+ ctl->mixer_left->ds->flags = DS_SCALE_UPDATE |
+ DS_VALIDATE;
+ ctl->mixer_right->ds->flags = DS_SCALE_UPDATE |
+ DS_VALIDATE;
+ ctl->mixer_left->ds->scaler.enable = 0;
+ ctl->mixer_left->ds->scaler.detail_enhance.enable = 0;
+ ctl->mixer_right->ds->scaler.enable = 0;
+ ctl->mixer_right->ds->scaler.detail_enhance.enable = 0;
+
+ pr_debug("DS-Left+Right disable: left:%dx%d, right:%dx%d\n",
+ ctl->mixer_left->width,
+ ctl->mixer_left->height,
+ ctl->mixer_right->width,
+ ctl->mixer_right->height);
+ MDSS_XLOG(ctl->mixer_left->width,
+ ctl->mixer_left->height,
+ ctl->mixer_right->width,
+ ctl->mixer_right->height);
+ } else if (ctl->mixer_left && ctl->mixer_left->ds &&
+ ctl->mixer_left->ds->scaler.enable) {
+ /*
+ * Single DS disable
+ */
+ ctl->mixer_left->width = get_panel_width(ctl);
+ ctl->mixer_left->height = get_panel_yres(pinfo);
+ ctl->mixer_left->roi = (struct mdss_rect) { 0, 0,
+ ctl->mixer_left->width,
+ ctl->mixer_left->height };
+
+ ctl->mixer_left->ds->flags = DS_SCALE_UPDATE |
+ DS_VALIDATE;
+ ctl->mixer_left->ds->scaler.enable = 0;
+ ctl->mixer_left->ds->scaler.detail_enhance.enable = 0;
+
+ pr_debug("DS-left disable: wxh=%dx%d\n",
+ ctl->mixer_left->width,
+ ctl->mixer_left->height);
+ MDSS_XLOG(ctl->mixer_left->width,
+ ctl->mixer_left->height);
+ }
+ }
+}
+
static int __dest_scaler_data_setup(struct mdp_destination_scaler_data *ds_data,
struct mdss_mdp_destination_scaler *ds,
u32 max_input_width, u32 max_output_width)
{
struct mdp_scale_data_v2 *scale;
- ds->flags = (ds_data->flags & MDP_DESTSCALER_ENABLE) ? DS_ENABLE : 0;
+ if (ds_data->flags & MDP_DESTSCALER_ENABLE)
+ ds->flags |= DS_ENABLE;
+ else
+ ds->flags &= ~DS_ENABLE;
if (ds_data->flags & (MDP_DESTSCALER_SCALE_UPDATE |
MDP_DESTSCALER_ENHANCER_UPDATE)) {
@@ -101,8 +177,12 @@ static int __dest_scaler_data_setup(struct mdp_destination_scaler_data *ds_data,
ds->flags |= DS_SCALE_UPDATE;
if (ds_data->flags & MDP_DESTSCALER_ENHANCER_UPDATE)
ds->flags |= DS_ENHANCER_UPDATE;
- ds->src_width = scale->src_width[0];
- ds->src_height = scale->src_height[0];
+
+ /*
+ * Update with LM resolution
+ */
+ ds->src_width = ds_data->lm_width;
+ ds->src_height = ds_data->lm_height;
}
if (ds_data->flags == 0) {
@@ -110,6 +190,11 @@ static int __dest_scaler_data_setup(struct mdp_destination_scaler_data *ds_data,
ds_data->dest_scaler_ndx);
}
+ /*
+ * Confirm all check pass validation, and to be cleared in CTL after
+ * flush is issued.
+ */
+ ds->flags |= DS_VALIDATE;
return 0;
}
@@ -118,7 +203,7 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl,
{
struct mdss_data_type *mdata;
struct mdss_panel_info *pinfo;
-
+ u16 mxleft_w = 0, mxleft_h = 0, mxright_w = 0, mxright_h = 0;
mdata = ctl->mdata;
/*
@@ -134,7 +219,7 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl,
* height.
*/
pinfo = &ctl->panel_data->panel_info;
- if ((ds_data->lm_width > get_panel_xres(pinfo)) ||
+ if ((ds_data->lm_width > get_panel_width(ctl)) ||
(ds_data->lm_height > get_panel_yres(pinfo)) ||
(ds_data->lm_width == 0) ||
(ds_data->lm_height == 0)) {
@@ -142,14 +227,8 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl,
ds_data->lm_width, ds_data->lm_height);
return -EINVAL;
}
-
- ctl->width = ds_data->lm_width;
- ctl->height = ds_data->lm_height;
-
- ctl->mixer_left->width = ds_data->lm_width;
- ctl->mixer_left->height = ds_data->lm_height;
- pr_debug("Update mixer-left width/height: %dx%d\n",
- ds_data->lm_width, ds_data->lm_width);
+ mxleft_w = ds_data->lm_width;
+ mxleft_h = ds_data->lm_height;
}
if (ctl->mixer_right && ctl->mixer_right->ds) {
@@ -174,25 +253,51 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl,
* Split display both left and right should have the
* same width and height
*/
- ctl->mixer_right->width = ds_data->lm_width;
- ctl->mixer_right->height = ds_data->lm_height;
- pr_debug("Update mixer-right width/height: %dx%d\n",
- ds_data->lm_width, ds_data->lm_height);
+ mxright_w = ds_data->lm_width;
+ mxright_h = ds_data->lm_height;
if (ctl->mixer_left &&
- ((ctl->mixer_right->width !=
- ctl->mixer_left->width) ||
- (ctl->mixer_right->height !=
- ctl->mixer_left->height))) {
+ ((mxright_w != mxleft_w) ||
+ (mxright_h != mxleft_h))) {
pr_err("Mismatch width/heigth in LM for split display\n");
return -EINVAL;
}
+ }
+
+ /*
+ * Update mixer and control dimension after successful
+ * pre-validation
+ */
+ if (mxleft_w && mxleft_h) {
+ ctl->mixer_left->ds->last_mixer_width =
+ ctl->mixer_left->width;
+ ctl->mixer_left->ds->last_mixer_height =
+ ctl->mixer_left->height;
+
+ ctl->width = mxleft_w;
+ ctl->height = mxleft_h;
+ ctl->mixer_left->width = mxleft_w;
+ ctl->mixer_left->height = mxleft_h;
+ pr_debug("Update mixer-left width/height: %dx%d\n",
+ mxleft_w, mxleft_h);
+ }
+
+ if (mxright_w && mxright_h) {
+ ctl->mixer_right->ds->last_mixer_width =
+ ctl->mixer_right->width;
+ ctl->mixer_right->ds->last_mixer_height =
+ ctl->mixer_right->height;
+
+ ctl->mixer_right->width = mxright_w;
+ ctl->mixer_right->height = mxright_h;
+ pr_debug("Update mixer-right width/height: %dx%d\n",
+ mxright_w, mxright_h);
/*
* For split display, CTL width should be equal to
* whole panel size
*/
- ctl->width += ds_data->lm_width;
+ ctl->width += mxright_w;
}
pr_debug("Updated CTL width:%d, height:%d\n",
@@ -236,19 +341,23 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd,
MDSS_MDP_DS_OVERFETCH_SIZE,
mdata->max_dest_scaler_output_width);
if (ret)
- return ret;
+ goto reset_mixer;
ret = __dest_scaler_data_setup(&ds_data[1], ds_right,
mdata->max_dest_scaler_input_width -
MDSS_MDP_DS_OVERFETCH_SIZE,
mdata->max_dest_scaler_output_width);
if (ret)
- return ret;
+ goto reset_mixer;
ds_left->flags &= ~(DS_LEFT|DS_RIGHT);
ds_left->flags |= DS_DUAL_MODE;
ds_right->flags &= ~(DS_LEFT|DS_RIGHT);
ds_right->flags |= DS_DUAL_MODE;
+ MDSS_XLOG(ds_left->num, ds_left->src_width,
+ ds_left->src_height, ds_left->flags,
+ ds_right->num, ds_right->src_width,
+ ds_right->src_height, ds_right->flags);
break;
case DS_LEFT:
@@ -262,6 +371,11 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd,
ret = __dest_scaler_data_setup(&ds_data[0], ds_left,
mdata->max_dest_scaler_input_width,
mdata->max_dest_scaler_output_width);
+ if (ret)
+ goto reset_mixer;
+
+ MDSS_XLOG(ds_left->num, ds_left->src_width,
+ ds_left->src_height, ds_left->flags);
break;
case DS_RIGHT:
@@ -276,6 +390,11 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd,
ret = __dest_scaler_data_setup(&ds_data[0], ds_right,
mdata->max_dest_scaler_input_width,
mdata->max_dest_scaler_output_width);
+ if (ret)
+ goto reset_mixer;
+
+ MDSS_XLOG(ds_right->num, ds_right->src_width,
+ ds_right->src_height, ds_right->flags);
break;
}
@@ -312,6 +431,40 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd,
pr_err("Invalid Dest-scaler output width/height: %d/%d\n",
scaler_width, scaler_height);
ret = -EINVAL;
+ goto reset_mixer;
+ }
+
+ return ret;
+
+reset_mixer:
+ /* reverting mixer and control dimension */
+ if (ctl->mixer_left && ctl->mixer_left->ds &&
+ ctl->mixer_left->ds->last_mixer_width) {
+ ctl->width = ctl->mixer_left->ds->last_mixer_width;
+ ctl->height = ctl->mixer_left->ds->last_mixer_height;
+ ctl->mixer_left->width =
+ ctl->mixer_left->ds->last_mixer_width;
+ ctl->mixer_left->height =
+ ctl->mixer_left->ds->last_mixer_height;
+ if (ds_left)
+ ds_left->flags &= ~DS_ENABLE;
+ MDSS_XLOG(ctl->width, ctl->height,
+ ctl->mixer_left->width,
+ ctl->mixer_left->height);
+ }
+
+ if (ctl->mixer_right && ctl->mixer_right->ds &&
+ ctl->mixer_right->ds->last_mixer_width) {
+ ctl->width += ctl->mixer_right->ds->last_mixer_width;
+ ctl->mixer_right->width =
+ ctl->mixer_right->ds->last_mixer_width;
+ ctl->mixer_right->height =
+ ctl->mixer_right->ds->last_mixer_height;
+ if (ds_right)
+ ds_right->flags &= ~DS_ENABLE;
+ MDSS_XLOG(ctl->width, ctl->height,
+ ctl->mixer_right->width,
+ ctl->mixer_right->height);
}
return ret;
@@ -369,6 +522,56 @@ static void __update_avr_info(struct mdss_mdp_ctl *ctl,
}
/*
+ * __validate_dual_partial_update() - validation function for
+ * dual partial update ROIs
+ *
+ * - This function uses the commit structs "left_roi" and "right_roi"
+ * to pass the first and second ROI information for the multiple
+ * partial update feature.
+ * - Supports only SINGLE DSI with a max of 2 PU ROIs.
+ * - Not supported along with destination scalar.
+ * - Not supported when source-split is disabled.
+ * - Not supported with ping-pong split enabled.
+ */
+static int __validate_dual_partial_update(
+ struct mdss_mdp_ctl *ctl, struct mdp_layer_commit_v1 *commit)
+{
+ struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info;
+ struct mdss_data_type *mdata = ctl->mdata;
+ struct mdss_rect first_roi, second_roi;
+ int ret = 0;
+ struct mdp_destination_scaler_data *ds_data = commit->dest_scaler;
+
+ if (!mdata->has_src_split
+ || (is_panel_split(ctl->mfd))
+ || (is_pingpong_split(ctl->mfd))
+ || (ds_data && commit->dest_scaler_cnt &&
+ ds_data->flags & MDP_DESTSCALER_ENABLE)) {
+ pr_err("Invalid mode multi pu src_split:%d, split_mode:%d, ds_cnt:%d\n",
+ mdata->has_src_split, ctl->mfd->split_mode,
+ commit->dest_scaler_cnt);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ rect_copy_mdp_to_mdss(&commit->left_roi, &first_roi);
+ rect_copy_mdp_to_mdss(&commit->right_roi, &second_roi);
+
+ if (!is_valid_pu_dual_roi(pinfo, &first_roi, &second_roi))
+ ret = -EINVAL;
+
+ MDSS_XLOG(ctl->num, first_roi.x, first_roi.y, first_roi.w, first_roi.h,
+ second_roi.x, second_roi.y, second_roi.w, second_roi.h,
+ ret);
+ pr_debug("Multiple PU ROIs - roi0:{%d,%d,%d,%d}, roi1{%d,%d,%d,%d}, ret:%d\n",
+ first_roi.x, first_roi.y, first_roi.w, first_roi.h,
+ second_roi.x, second_roi.y, second_roi.w,
+ second_roi.h, ret);
+end:
+ return ret;
+}
+
+/*
* __layer_needs_src_split() - check needs source split configuration
* @layer: input layer
*
@@ -783,7 +986,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
{
int ret = 0;
u32 left_lm_w = left_lm_w_from_mfd(mfd);
- u32 flags;
+ u64 flags;
struct mdss_mdp_mixer *mixer = NULL;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
@@ -825,6 +1028,8 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
pipe->flags |= MDP_BWC_EN;
if (layer->flags & MDP_LAYER_PP)
pipe->flags |= MDP_OVERLAY_PP_CFG_EN;
+ if (layer->flags & MDP_LAYER_SECURE_CAMERA_SESSION)
+ pipe->flags |= MDP_SECURE_CAMERA_OVERLAY_SESSION;
pipe->scaler.enable = (layer->flags & SCALER_ENABLED);
pipe->is_fg = layer->flags & MDP_LAYER_FORGROUND;
@@ -847,6 +1052,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
pipe->is_handed_off = false;
pipe->async_update = (layer->flags & MDP_LAYER_ASYNC) ? true : false;
pipe->csc_coeff_set = layer->color_space;
+ pipe->restore_roi = false;
if (mixer->ctl) {
pipe->dst.x += mixer->ctl->border_x_off;
@@ -854,7 +1060,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
pr_debug("border{%d,%d}\n", mixer->ctl->border_x_off,
mixer->ctl->border_y_off);
}
- pr_debug("src{%d,%d,%d,%d}, dst{%d,%d,%d,%d}\n",
+ pr_debug("pipe:%d src{%d,%d,%d,%d}, dst{%d,%d,%d,%d}\n", pipe->num,
pipe->src.x, pipe->src.y, pipe->src.w, pipe->src.h,
pipe->dst.x, pipe->dst.y, pipe->dst.w, pipe->dst.h);
@@ -1195,7 +1401,7 @@ static struct mdss_mdp_data *__map_layer_buffer(struct msm_fb_data_type *mfd,
struct mdp_layer_buffer *buffer;
struct msmfb_data image;
int i, ret;
- u32 flags;
+ u64 flags;
struct mdss_mdp_validate_info_t *vitem;
for (i = 0; i < layer_count; i++) {
@@ -1221,7 +1427,8 @@ static struct mdss_mdp_data *__map_layer_buffer(struct msm_fb_data_type *mfd,
}
flags = (pipe->flags & (MDP_SECURE_OVERLAY_SESSION |
- MDP_SECURE_DISPLAY_OVERLAY_SESSION));
+ MDP_SECURE_DISPLAY_OVERLAY_SESSION |
+ MDP_SECURE_CAMERA_OVERLAY_SESSION));
if (buffer->planes[0].fd < 0) {
pr_err("invalid file descriptor for layer buffer\n");
@@ -1432,34 +1639,48 @@ end:
}
/*
- * __validate_secure_display() - validate secure display
+ * __validate_secure_session() - validate various secure sessions
*
* This function travers through used pipe list and checks if any pipe
- * is with secure display enabled flag. It fails if client tries to stage
- * unsecure content with secure display session.
+ * is with secure display, secure video and secure camera enabled flag.
+ * It fails if client tries to stage unsecure content with
+ * secure display session and secure camera with secure video sessions.
*
*/
-static int __validate_secure_display(struct mdss_overlay_private *mdp5_data)
+static int __validate_secure_session(struct mdss_overlay_private *mdp5_data)
{
struct mdss_mdp_pipe *pipe, *tmp;
uint32_t sd_pipes = 0, nonsd_pipes = 0;
+ uint32_t secure_vid_pipes = 0, secure_cam_pipes = 0;
mutex_lock(&mdp5_data->list_lock);
list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_used, list) {
if (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION)
sd_pipes++;
+ else if (pipe->flags & MDP_SECURE_OVERLAY_SESSION)
+ secure_vid_pipes++;
+ else if (pipe->flags & MDP_SECURE_CAMERA_OVERLAY_SESSION)
+ secure_cam_pipes++;
else
nonsd_pipes++;
}
mutex_unlock(&mdp5_data->list_lock);
- pr_debug("pipe count:: secure display:%d non-secure:%d\n",
- sd_pipes, nonsd_pipes);
+ pr_debug("pipe count:: secure display:%d non-secure:%d secure-vid:%d,secure-cam:%d\n",
+ sd_pipes, nonsd_pipes, secure_vid_pipes, secure_cam_pipes);
- if ((sd_pipes || mdss_get_sd_client_cnt()) && nonsd_pipes) {
+ if ((sd_pipes || mdss_get_sd_client_cnt()) &&
+ (nonsd_pipes || secure_vid_pipes ||
+ secure_cam_pipes)) {
pr_err("non-secure layer validation request during secure display session\n");
- pr_err(" secure client cnt:%d secure pipe cnt:%d non-secure pipe cnt:%d\n",
- mdss_get_sd_client_cnt(), sd_pipes, nonsd_pipes);
+ pr_err(" secure client cnt:%d secure pipe:%d non-secure pipe:%d, secure-vid:%d, secure-cam:%d\n",
+ mdss_get_sd_client_cnt(), sd_pipes, nonsd_pipes,
+ secure_vid_pipes, secure_cam_pipes);
+ return -EINVAL;
+ } else if (secure_cam_pipes && (secure_vid_pipes || sd_pipes)) {
+ pr_err(" incompatible layers during secure camera session\n");
+ pr_err("secure-camera cnt:%d secure video:%d secure display:%d\n",
+ secure_cam_pipes, secure_vid_pipes, sd_pipes);
return -EINVAL;
} else {
return 0;
@@ -1939,6 +2160,7 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
enum layer_pipe_q pipe_q_type;
enum layer_zorder_used zorder_used[MDSS_MDP_MAX_STAGE] = {0};
enum mdss_mdp_pipe_rect rect_num;
+ struct mdp_destination_scaler_data *ds_data;
ret = mutex_lock_interruptible(&mdp5_data->ov_lock);
if (ret)
@@ -2194,11 +2416,10 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
layer->z_order -= MDSS_MDP_STAGE_0;
}
+ ds_data = commit->dest_scaler;
if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) &&
- commit->dest_scaler &&
+ ds_data && (ds_data->flags & MDP_DESTSCALER_ENABLE) &&
commit->dest_scaler_cnt) {
- struct mdp_destination_scaler_data *ds_data =
- commit->dest_scaler;
/*
* Find out which DS block to use based on DS commit info
@@ -2217,8 +2438,7 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
}
ret = mdss_mdp_validate_destination_scaler(mfd,
- commit->dest_scaler,
- ds_mode);
+ ds_data, ds_mode);
if (ret) {
pr_err("fail to validate destination scaler\n");
layer->error_code = ret;
@@ -2236,7 +2456,7 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
validate_skip:
__handle_free_list(mdp5_data, validate_info_list, layer_count);
- ret = __validate_secure_display(mdp5_data);
+ ret = __validate_secure_session(mdp5_data);
validate_exit:
pr_debug("err=%d total_layer:%d left:%d right:%d rec0_rel_ndx=0x%x rec1_rel_ndx=0x%x rec0_destroy_ndx=0x%x rec1_destroy_ndx=0x%x processed=%d\n",
@@ -2250,16 +2470,20 @@ validate_exit:
mutex_lock(&mdp5_data->list_lock);
list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_used, list) {
if (IS_ERR_VALUE(ret)) {
- if ((pipe->ndx & rec_release_ndx[0]) ||
- (pipe->ndx & rec_release_ndx[1])) {
+ if (((pipe->ndx & rec_release_ndx[0]) &&
+ (pipe->multirect.num == 0)) ||
+ ((pipe->ndx & rec_release_ndx[1]) &&
+ (pipe->multirect.num == 1))) {
mdss_mdp_smp_unreserve(pipe);
pipe->params_changed = 0;
pipe->dirty = true;
if (!list_empty(&pipe->list))
list_del_init(&pipe->list);
mdss_mdp_pipe_destroy(pipe);
- } else if ((pipe->ndx & rec_destroy_ndx[0]) ||
- (pipe->ndx & rec_destroy_ndx[1])) {
+ } else if (((pipe->ndx & rec_destroy_ndx[0]) &&
+ (pipe->multirect.num == 0)) ||
+ ((pipe->ndx & rec_destroy_ndx[1]) &&
+ (pipe->multirect.num == 1))) {
/*
* cleanup/destroy list pipes should move back
* to destroy list. Next/current kickoff cycle
@@ -2472,6 +2696,8 @@ int mdss_mdp_layer_atomic_validate(struct msm_fb_data_type *mfd,
struct file *file, struct mdp_layer_commit_v1 *commit)
{
struct mdss_overlay_private *mdp5_data;
+ struct mdp_destination_scaler_data *ds_data;
+ struct mdss_panel_info *pinfo;
int rc = 0;
if (!mfd || !commit) {
@@ -2505,15 +2731,34 @@ int mdss_mdp_layer_atomic_validate(struct msm_fb_data_type *mfd,
}
}
- if (commit->dest_scaler && commit->dest_scaler_cnt) {
+ pinfo = mfd->panel_info;
+ if (pinfo->partial_update_enabled == PU_DUAL_ROI) {
+ if (commit->flags & MDP_COMMIT_PARTIAL_UPDATE_DUAL_ROI) {
+ rc = __validate_dual_partial_update(mdp5_data->ctl,
+ commit);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("Multiple pu pre-validate fail\n");
+ return rc;
+ }
+ }
+ } else {
+ if (commit->flags & MDP_COMMIT_PARTIAL_UPDATE_DUAL_ROI) {
+ pr_err("Multiple partial update not supported!\n");
+ return -EINVAL;
+ }
+ }
+
+ ds_data = commit->dest_scaler;
+ if (ds_data && commit->dest_scaler_cnt &&
+ (ds_data->flags & MDP_DESTSCALER_ENABLE)) {
rc = mdss_mdp_destination_scaler_pre_validate(mdp5_data->ctl,
- commit->dest_scaler,
- commit->dest_scaler_cnt);
+ ds_data, commit->dest_scaler_cnt);
if (IS_ERR_VALUE(rc)) {
pr_err("Destination scaler pre-validate failed\n");
return -EINVAL;
}
- }
+ } else
+ mdss_mdp_disable_destination_scaler_setup(mdp5_data->ctl);
rc = mdss_mdp_avr_validate(mfd, commit);
if (IS_ERR_VALUE(rc)) {
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index 965d4a6cfb5e..946e33033ab7 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -396,7 +396,7 @@ int mdss_mdp_overlay_req_check(struct msm_fb_data_type *mfd,
}
int mdp_pipe_tune_perf(struct mdss_mdp_pipe *pipe,
- u32 flags)
+ u64 flags)
{
struct mdss_data_type *mdata = pipe->mixer_left->ctl->mdata;
struct mdss_mdp_perf_params perf;
@@ -1188,11 +1188,10 @@ static void __overlay_pipe_cleanup(struct msm_fb_data_type *mfd,
list_move(&buf->buf_list, &mdp5_data->bufs_freelist);
/*
- * in case of secure UI, the buffer needs to be released as
- * soon as session is closed.
+ * free the buffers on the same cycle instead of waiting for
+ * next kickoff
*/
- if (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION)
- mdss_mdp_overlay_buf_free(mfd, buf);
+ mdss_mdp_overlay_buf_free(mfd, buf);
}
mdss_mdp_pipe_destroy(pipe);
@@ -1477,7 +1476,7 @@ static int __overlay_queue_pipes(struct msm_fb_data_type *mfd)
*/
if (mdss_get_sd_client_cnt() &&
!(pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION)) {
- pr_warn("Non secure pipe during secure display: %u: %08X, skip\n",
+ pr_warn("Non secure pipe during secure display: %u: %16llx, skip\n",
pipe->num, pipe->flags);
continue;
}
@@ -1861,12 +1860,109 @@ int mdss_mode_switch_post(struct msm_fb_data_type *mfd, u32 mode)
return rc;
}
+static void __restore_pipe(struct mdss_mdp_pipe *pipe)
+{
+
+ if (!pipe->restore_roi)
+ return;
+
+ pr_debug("restoring pipe:%d dst from:{%d,%d,%d,%d} to:{%d,%d,%d,%d}\n",
+ pipe->num, pipe->dst.x, pipe->dst.y,
+ pipe->dst.w, pipe->dst.h, pipe->layer.dst_rect.x,
+ pipe->layer.dst_rect.y, pipe->layer.dst_rect.w,
+ pipe->layer.dst_rect.h);
+ pr_debug("restoring pipe:%d src from:{%d,%d,%d,%d} to:{%d,%d,%d,%d}\n",
+ pipe->num, pipe->src.x, pipe->src.y,
+ pipe->src.w, pipe->src.h, pipe->layer.src_rect.x,
+ pipe->layer.src_rect.y, pipe->layer.src_rect.w,
+ pipe->layer.src_rect.h);
+
+ pipe->src.x = pipe->layer.src_rect.x;
+ pipe->src.y = pipe->layer.src_rect.y;
+ pipe->src.w = pipe->layer.src_rect.w;
+ pipe->src.h = pipe->layer.src_rect.h;
+
+ pipe->dst.x = pipe->layer.dst_rect.x;
+ pipe->dst.y = pipe->layer.dst_rect.y;
+ pipe->dst.w = pipe->layer.dst_rect.w;
+ pipe->dst.h = pipe->layer.dst_rect.h;
+}
+
+ /**
+ * __crop_adjust_pipe_rect() - Adjust pipe roi for dual partial
+ * update feature.
+ * @pipe: pipe to check against.
+ * @dual_roi: roi's for the dual partial roi.
+ *
+ * For dual PU ROI case, the layer mixer is configured
+ * by merging the two width aligned ROIs (first_roi and
+ * second_roi) vertically. So, the y-offset of all the
+ * pipes belonging to the second_roi needs to adjusted
+ * accordingly. Also the cropping of the pipe's src/dst
+ * rect has to be done with respect to the ROI the pipe
+ * is intersecting with, before the adjustment.
+ */
+static int __crop_adjust_pipe_rect(struct mdss_mdp_pipe *pipe,
+ struct mdss_dsi_dual_pu_roi *dual_roi)
+{
+ u32 adjust_h;
+ u32 roi_y_pos;
+ int ret = 0;
+
+ pipe->restore_roi = false;
+ if (mdss_rect_overlap_check(&pipe->dst, &dual_roi->first_roi)) {
+ mdss_mdp_crop_rect(&pipe->src, &pipe->dst,
+ &dual_roi->first_roi, false);
+ pipe->restore_roi = true;
+
+ } else if (mdss_rect_overlap_check(&pipe->dst, &dual_roi->second_roi)) {
+ mdss_mdp_crop_rect(&pipe->src, &pipe->dst,
+ &dual_roi->second_roi, false);
+ adjust_h = dual_roi->second_roi.y;
+ roi_y_pos = dual_roi->first_roi.y + dual_roi->first_roi.h;
+
+ if (adjust_h > roi_y_pos) {
+ adjust_h = adjust_h - roi_y_pos;
+ pipe->dst.y -= adjust_h;
+ } else {
+ pr_err("wrong y-pos adjust_y:%d roi_y_pos:%d\n",
+ adjust_h, roi_y_pos);
+ ret = -EINVAL;
+ }
+ pipe->restore_roi = true;
+
+ } else {
+ ret = -EINVAL;
+ }
+
+ pr_debug("crop/adjusted p:%d src:{%d,%d,%d,%d} dst:{%d,%d,%d,%d} r:%d\n",
+ pipe->num, pipe->src.x, pipe->src.y,
+ pipe->src.w, pipe->src.h, pipe->dst.x,
+ pipe->dst.y, pipe->dst.w, pipe->dst.h,
+ pipe->restore_roi);
+
+ if (ret) {
+ pr_err("dual roi error p%d dst{%d,%d,%d,%d}",
+ pipe->num, pipe->dst.x, pipe->dst.y, pipe->dst.w,
+ pipe->dst.h);
+ pr_err(" roi1{%d,%d,%d,%d} roi2{%d,%d,%d,%d}\n",
+ dual_roi->first_roi.x, dual_roi->first_roi.y,
+ dual_roi->first_roi.w, dual_roi->first_roi.h,
+ dual_roi->second_roi.x, dual_roi->second_roi.y,
+ dual_roi->second_roi.w, dual_roi->second_roi.h);
+ }
+
+ return ret;
+}
+
static void __validate_and_set_roi(struct msm_fb_data_type *mfd,
struct mdp_display_commit *commit)
{
struct mdss_mdp_pipe *pipe;
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info;
+ struct mdss_dsi_dual_pu_roi *dual_roi = &pinfo->dual_roi;
struct mdss_rect l_roi = {0}, r_roi = {0};
struct mdp_rect tmp_roi = {0};
bool skip_partial_update = true;
@@ -1881,6 +1977,39 @@ static void __validate_and_set_roi(struct msm_fb_data_type *mfd,
rect_copy_mdp_to_mdss(&commit->l_roi, &l_roi);
rect_copy_mdp_to_mdss(&commit->r_roi, &r_roi);
+ /*
+ * In case of dual partial update ROI, update the two ROIs to dual_roi
+ * struct and combine both the ROIs and assign it as a merged ROI in
+ * l_roi, as MDP would need only the merged ROI information for all
+ * LM settings.
+ */
+ if (pinfo->partial_update_enabled == PU_DUAL_ROI) {
+ if (commit->flags & MDP_COMMIT_PARTIAL_UPDATE_DUAL_ROI) {
+
+ if (!is_valid_pu_dual_roi(pinfo, &l_roi, &r_roi)) {
+ pr_err("Invalid dual roi - fall back to full screen update\n");
+ goto set_roi;
+ }
+
+ dual_roi->first_roi = (struct mdss_rect)
+ {l_roi.x, l_roi.y, l_roi.w, l_roi.h};
+ dual_roi->second_roi = (struct mdss_rect)
+ {r_roi.x, r_roi.y, r_roi.w, r_roi.h};
+ dual_roi->enabled = true;
+
+ l_roi.h += r_roi.h;
+ memset(&r_roi, 0, sizeof(struct mdss_rect));
+
+ pr_debug("Dual ROI - first_roi:{%d,%d,%d,%d}, second_roi:{%d,%d,%d,%d}\n",
+ dual_roi->first_roi.x, dual_roi->first_roi.y,
+ dual_roi->first_roi.w, dual_roi->first_roi.h,
+ dual_roi->second_roi.x, dual_roi->second_roi.y,
+ dual_roi->second_roi.w, dual_roi->second_roi.h);
+ } else {
+ dual_roi->enabled = false;
+ }
+ }
+
pr_debug("input: l_roi:-> %d %d %d %d r_roi:-> %d %d %d %d\n",
l_roi.x, l_roi.y, l_roi.w, l_roi.h,
r_roi.x, r_roi.y, r_roi.w, r_roi.h);
@@ -1926,12 +2055,24 @@ static void __validate_and_set_roi(struct msm_fb_data_type *mfd,
}
list_for_each_entry(pipe, &mdp5_data->pipes_used, list) {
+ pr_debug("pipe:%d src:{%d,%d,%d,%d} dst:{%d,%d,%d,%d}\n",
+ pipe->num, pipe->src.x, pipe->src.y,
+ pipe->src.w, pipe->src.h, pipe->dst.x,
+ pipe->dst.y, pipe->dst.w, pipe->dst.h);
+
+ if (dual_roi->enabled) {
+ if (__crop_adjust_pipe_rect(pipe, dual_roi)) {
+ skip_partial_update = true;
+ break;
+ }
+ }
+
if (!__is_roi_valid(pipe, &l_roi, &r_roi)) {
skip_partial_update = true;
- pr_err("error. invalid pu config for pipe%d: %d,%d,%d,%d\n",
- pipe->num,
- pipe->dst.x, pipe->dst.y,
- pipe->dst.w, pipe->dst.h);
+ pr_err("error. invalid pu config for pipe%d: %d,%d,%d,%d, dual_pu_roi:%d\n",
+ pipe->num, pipe->dst.x, pipe->dst.y,
+ pipe->dst.w, pipe->dst.h,
+ dual_roi->enabled);
break;
}
}
@@ -1946,17 +2087,143 @@ set_roi:
ctl->mixer_right->width,
ctl->mixer_right->height};
}
+
+ if (pinfo->partial_update_enabled == PU_DUAL_ROI) {
+ if (dual_roi->enabled) {
+ /* we failed pu validation, restore pipes */
+ list_for_each_entry(pipe,
+ &mdp5_data->pipes_used, list)
+ __restore_pipe(pipe);
+ }
+ dual_roi->enabled = false;
+ }
}
- pr_debug("after processing: %s l_roi:-> %d %d %d %d r_roi:-> %d %d %d %d\n",
+ pr_debug("after processing: %s l_roi:-> %d %d %d %d r_roi:-> %d %d %d %d, dual_pu_roi:%d\n",
(l_roi.w && l_roi.h && r_roi.w && r_roi.h) ? "left+right" :
((l_roi.w && l_roi.h) ? "left-only" : "right-only"),
l_roi.x, l_roi.y, l_roi.w, l_roi.h,
- r_roi.x, r_roi.y, r_roi.w, r_roi.h);
+ r_roi.x, r_roi.y, r_roi.w, r_roi.h,
+ dual_roi->enabled);
mdss_mdp_set_roi(ctl, &l_roi, &r_roi);
}
+/*
+ * Enables/disable secure (display or camera) sessions
+ */
+static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
+{
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
+ struct mdss_mdp_pipe *pipe;
+ int ret = 0;
+ int sd_in_pipe = 0;
+ int sc_in_pipe = 0;
+ u64 pipes_flags = 0;
+
+ list_for_each_entry(pipe, &mdp5_data->pipes_used, list) {
+ pipes_flags |= pipe->flags;
+ if (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION) {
+ sd_in_pipe = 1;
+ pr_debug("Secure pipe: %u : %16llx\n",
+ pipe->num, pipe->flags);
+ } else if (pipe->flags & MDP_SECURE_CAMERA_OVERLAY_SESSION) {
+ sc_in_pipe = 1;
+ pr_debug("Secure camera: %u: %16llx\n",
+ pipe->num, pipe->flags);
+ }
+ }
+
+ MDSS_XLOG(sd_in_pipe, sc_in_pipe, pipes_flags,
+ mdp5_data->sc_enabled, mdp5_data->sd_enabled);
+ pr_debug("sd:%d sd_in_pipe:%d sc:%d sc_in_pipe:%d flags:0x%llx\n",
+ mdp5_data->sd_enabled, sd_in_pipe,
+ mdp5_data->sc_enabled, sc_in_pipe, pipes_flags);
+
+ /*
+ * Return early in only two conditions:
+ * 1. All the features are already disabled and state remains
+ * disabled for the pipes.
+ * 2. One of the features is already enabled and state remains
+ * enabled for the pipes.
+ */
+ if (!sd_in_pipe && !mdp5_data->sd_enabled &&
+ !sc_in_pipe && !mdp5_data->sc_enabled)
+ return ret;
+ else if ((sd_in_pipe && mdp5_data->sd_enabled) ||
+ (sc_in_pipe && mdp5_data->sc_enabled))
+ return ret;
+
+ /* Secure Display */
+ if (!mdp5_data->sd_enabled && sd_in_pipe) {
+ if (!mdss_get_sd_client_cnt()) {
+ MDSS_XLOG(0x11);
+ /*wait for ping pong done */
+ if (ctl->ops.wait_pingpong)
+ mdss_mdp_display_wait4pingpong(ctl, true);
+ ret = mdss_mdp_secure_session_ctrl(1,
+ MDP_SECURE_DISPLAY_OVERLAY_SESSION);
+ if (ret) {
+ pr_err("secure display enable fail:%d", ret);
+ return ret;
+ }
+ }
+ mdp5_data->sd_enabled = 1;
+ mdss_update_sd_client(mdp5_data->mdata, true);
+ } else if (mdp5_data->sd_enabled && !sd_in_pipe) {
+ /* disable the secure display on last client */
+ if (mdss_get_sd_client_cnt() == 1) {
+ MDSS_XLOG(0x22);
+ if (ctl->ops.wait_pingpong)
+ mdss_mdp_display_wait4pingpong(ctl, true);
+ ret = mdss_mdp_secure_session_ctrl(0,
+ MDP_SECURE_DISPLAY_OVERLAY_SESSION);
+ if (ret) {
+ pr_err("secure display disable fail:%d\n", ret);
+ return ret;
+ }
+ }
+ mdss_update_sd_client(mdp5_data->mdata, false);
+ mdp5_data->sd_enabled = 0;
+ }
+
+ /* Secure Camera */
+ if (!mdp5_data->sc_enabled && sc_in_pipe) {
+ if (!mdss_get_sc_client_cnt()) {
+ MDSS_XLOG(0x33);
+ if (ctl->ops.wait_pingpong)
+ mdss_mdp_display_wait4pingpong(ctl, true);
+ ret = mdss_mdp_secure_session_ctrl(1,
+ MDP_SECURE_CAMERA_OVERLAY_SESSION);
+ if (ret) {
+ pr_err("secure camera enable fail:%d\n", ret);
+ return ret;
+ }
+ }
+ mdp5_data->sc_enabled = 1;
+ mdss_update_sc_client(mdp5_data->mdata, true);
+ } else if (mdp5_data->sc_enabled && !sc_in_pipe) {
+ /* disable the secure camera on last client */
+ if (mdss_get_sc_client_cnt() == 1) {
+ MDSS_XLOG(0x44);
+ if (ctl->ops.wait_pingpong)
+ mdss_mdp_display_wait4pingpong(ctl, true);
+ ret = mdss_mdp_secure_session_ctrl(0,
+ MDP_SECURE_CAMERA_OVERLAY_SESSION);
+ if (ret) {
+ pr_err("secure camera disable fail:%d\n", ret);
+ return ret;
+ }
+ }
+ mdss_update_sc_client(mdp5_data->mdata, false);
+ mdp5_data->sc_enabled = 0;
+ }
+
+ MDSS_XLOG(ret);
+ return ret;
+}
+
int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
struct mdp_display_commit *data)
{
@@ -1964,7 +2231,6 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
struct mdss_mdp_pipe *pipe, *tmp;
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
int ret = 0;
- int sd_in_pipe = 0;
struct mdss_mdp_commit_cb commit_cb;
if (!ctl)
@@ -1995,30 +2261,12 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
mutex_unlock(ctl->shared_lock);
return ret;
}
- mutex_lock(&mdp5_data->list_lock);
-
- /*
- * check if there is a secure display session
- */
- list_for_each_entry(pipe, &mdp5_data->pipes_used, list) {
- if (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION) {
- sd_in_pipe = 1;
- pr_debug("Secure pipe: %u : %08X\n",
- pipe->num, pipe->flags);
- }
- }
- /*
- * start secure display session if there is secure display session and
- * sd_enabled is not true.
- */
- if (!mdp5_data->sd_enabled && sd_in_pipe) {
- if (!mdss_get_sd_client_cnt())
- ret = mdss_mdp_secure_display_ctrl(1);
- if (!ret) {
- mdp5_data->sd_enabled = 1;
- mdss_update_sd_client(mdp5_data->mdata, true);
- }
+ mutex_lock(&mdp5_data->list_lock);
+ ret = __overlay_secure_ctrl(mfd);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("secure operation failed %d\n", ret);
+ goto commit_fail;
}
if (!ctl->shared_lock)
@@ -2108,19 +2356,6 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
}
mutex_lock(&mdp5_data->ov_lock);
- /*
- * If there is no secure display session and sd_enabled, disable the
- * secure display session
- */
- if (mdp5_data->sd_enabled && !sd_in_pipe && !ret) {
- /* disable the secure display on last client */
- if (mdss_get_sd_client_cnt() == 1)
- ret = mdss_mdp_secure_display_ctrl(0);
- if (!ret) {
- mdss_update_sd_client(mdp5_data->mdata, false);
- mdp5_data->sd_enabled = 0;
- }
- }
mdss_fb_update_notify_update(mfd);
commit_fail:
@@ -2272,7 +2507,7 @@ static int mdss_mdp_overlay_queue(struct msm_fb_data_type *mfd,
struct mdss_mdp_data *src_data;
struct mdp_layer_buffer buffer;
int ret;
- u32 flags;
+ u64 flags;
pipe = __overlay_find_pipe(mfd, req->id);
if (!pipe) {
@@ -2298,7 +2533,8 @@ static int mdss_mdp_overlay_queue(struct msm_fb_data_type *mfd,
pr_warn("Unexpected buffer queue to a solid fill pipe\n");
flags = (pipe->flags & (MDP_SECURE_OVERLAY_SESSION |
- MDP_SECURE_DISPLAY_OVERLAY_SESSION));
+ MDP_SECURE_DISPLAY_OVERLAY_SESSION |
+ MDP_SECURE_CAMERA_OVERLAY_SESSION));
mutex_lock(&mdp5_data->list_lock);
src_data = mdss_mdp_overlay_buf_alloc(mfd, pipe);
@@ -2958,7 +3194,7 @@ static ssize_t dynamic_fps_sysfs_wta_dfps(struct device *dev,
if (pdata->panel_info.dfps_update ==
DFPS_IMMEDIATE_MULTI_UPDATE_MODE_CLK_HFP) {
- if (sscanf(buf, "%d %d %d %d %d",
+ if (sscanf(buf, "%u %u %u %u %u",
&data.hfp, &data.hbp, &data.hpw,
&data.clk_rate, &data.fps) != 5) {
pr_err("could not read input\n");
@@ -5223,6 +5459,7 @@ static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd)
mdss_mdp_overlay_kickoff(mfd, NULL);
}
+ctl_stop:
/*
* If retire fences are still active wait for a vsync time
* for retire fence to be updated.
@@ -5253,7 +5490,6 @@ static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd)
flush_work(&mdp5_data->retire_work);
}
-ctl_stop:
mutex_lock(&mdp5_data->ov_lock);
/* set the correct pipe_mapped before ctl_stop */
mdss_mdp_mixer_update_pipe_map(mdp5_data->ctl,
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c
index 8f211a977aa4..bcf5309993b9 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c
@@ -1340,10 +1340,11 @@ static struct mdss_mdp_pipe *__pipe_lookup(struct mdss_mdp_pipe *pipe_list,
bool (*cmp)(struct mdss_mdp_pipe *, void *), void *data)
{
struct mdss_mdp_pipe *pipe;
- int i, j;
+ int i, j, max_rects;
for (i = 0, pipe = pipe_list; i < count; i++) {
- for (j = 0; j < pipe->multirect.max_rects; j++, pipe++)
+ max_rects = pipe->multirect.max_rects;
+ for (j = 0; j < max_rects; j++, pipe++)
if ((rect_num == pipe->multirect.num) &&
cmp(pipe, data))
return pipe;
@@ -1878,7 +1879,8 @@ static void mdss_mdp_pipe_stride_update(struct mdss_mdp_pipe *pipe)
if (pipe->multirect.mode == MDSS_MDP_PIPE_MULTIRECT_NONE) {
memcpy(&ystride, &pipe->src_planes.ystride,
sizeof(u32) * MAX_PLANES);
- if (pipe->flags & MDP_SECURE_OVERLAY_SESSION)
+ if (pipe->flags & (MDP_SECURE_OVERLAY_SESSION |
+ MDP_SECURE_CAMERA_OVERLAY_SESSION))
secure = 0xF;
} else {
if (pipe->multirect.num == MDSS_MDP_PIPE_RECT0) {
@@ -1891,12 +1893,14 @@ static void mdss_mdp_pipe_stride_update(struct mdss_mdp_pipe *pipe)
ystride[0] = rec0_pipe->src_planes.ystride[0];
ystride[2] = rec0_pipe->src_planes.ystride[2];
- if (rec0_pipe->flags & MDP_SECURE_OVERLAY_SESSION)
+ if (rec0_pipe->flags & (MDP_SECURE_OVERLAY_SESSION |
+ MDP_SECURE_CAMERA_OVERLAY_SESSION))
secure |= 0x5;
ystride[1] = rec1_pipe->src_planes.ystride[0];
ystride[3] = rec1_pipe->src_planes.ystride[2];
- if (rec1_pipe->flags & MDP_SECURE_OVERLAY_SESSION)
+ if (rec1_pipe->flags & (MDP_SECURE_OVERLAY_SESSION |
+ MDP_SECURE_CAMERA_OVERLAY_SESSION))
secure |= 0xA;
}
@@ -1998,7 +2002,7 @@ static int mdss_mdp_image_setup(struct mdss_mdp_pipe *pipe,
dst.x -= left_lm_w_from_mfd(pipe->mfd);
}
- mdss_mdp_crop_rect(&src, &dst, &roi);
+ mdss_mdp_crop_rect(&src, &dst, &roi, true);
if (mdata->has_src_split && is_right_mixer) {
/*
@@ -2320,11 +2324,13 @@ static int mdss_mdp_pipe_solidfill_setup(struct mdss_mdp_pipe *pipe)
}
format = MDSS_MDP_FMT_SOLID_FILL;
- secure = (pipe->flags & MDP_SECURE_OVERLAY_SESSION ? 0xF : 0x0);
+ secure = (pipe->flags & (MDP_SECURE_OVERLAY_SESSION |
+ MDP_SECURE_CAMERA_OVERLAY_SESSION)
+ ? 0xF : 0x0);
/* support ARGB color format only */
unpack = (C3_ALPHA << 24) | (C2_R_Cr << 16) |
- (C1_B_Cb << 8) | (C0_G_Y << 0);
+ (C0_G_Y << 8) | (C1_B_Cb << 0);
if (pipe->scaler.enable)
opmode |= (1 << 31);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c
index ee1cd8fd623e..f79212ea740d 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c
@@ -60,6 +60,30 @@ struct mdp_csc_cfg mdp_csc_8bit_convert[MDSS_MDP_MAX_CSC] = {
{ 0x10, 0xeb, 0x10, 0xf0, 0x10, 0xf0,},
{ 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,},
},
+ [MDSS_MDP_CSC_YUV2RGB_2020L] = {
+ 0,
+ {
+ 0x0256, 0x0000, 0x035e,
+ 0x0256, 0xffa0, 0xfeb2,
+ 0x0256, 0x044c, 0x0000,
+ },
+ { 0xfff0, 0xff80, 0xff80,},
+ { 0x0, 0x0, 0x0,},
+ { 0x10, 0xeb, 0x10, 0xf0, 0x10, 0xf0,},
+ { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,},
+ },
+ [MDSS_MDP_CSC_YUV2RGB_2020FR] = {
+ 0,
+ {
+ 0x0200, 0x0000, 0x02f3,
+ 0x0200, 0xffac, 0xfedb,
+ 0x0200, 0x03c3, 0x0000,
+ },
+ { 0x0000, 0xff80, 0xff80,},
+ { 0x0, 0x0, 0x0,},
+ { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,},
+ { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,},
+ },
[MDSS_MDP_CSC_RGB2YUV_601L] = {
0,
{
@@ -96,6 +120,30 @@ struct mdp_csc_cfg mdp_csc_8bit_convert[MDSS_MDP_MAX_CSC] = {
{ 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,},
{ 0x0010, 0x00eb, 0x0010, 0x00f0, 0x0010, 0x00f0,},
},
+ [MDSS_MDP_CSC_RGB2YUV_2020L] = {
+ 0,
+ {
+ 0x0073, 0x0129, 0x001a,
+ 0xffc1, 0xff5e, 0x00e0,
+ 0x00e0, 0xff32, 0xffee
+ },
+ { 0x0, 0x0, 0x0,},
+ { 0x0010, 0x0080, 0x0080,},
+ { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,},
+ { 0x0010, 0x00eb, 0x0010, 0x00f0, 0x0010, 0x00f0,},
+ },
+ [MDSS_MDP_CSC_RGB2YUV_2020FR] = {
+ 0,
+ {
+ 0x0086, 0x015b, 0x001e,
+ 0xffb9, 0xff47, 0x0100,
+ 0x0100, 0xff15, 0xffeb
+ },
+ { 0x0, 0x0, 0x0,},
+ { 0x0, 0x0080, 0x0080,},
+ { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,},
+ { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,},
+ },
[MDSS_MDP_CSC_YUV2YUV] = {
0,
{
@@ -159,6 +207,30 @@ struct mdp_csc_cfg mdp_csc_10bit_convert[MDSS_MDP_MAX_CSC] = {
{ 0x40, 0x3ac, 0x40, 0x3c0, 0x40, 0x3c0,},
{ 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
},
+ [MDSS_MDP_CSC_YUV2RGB_2020L] = {
+ 0,
+ {
+ 0x0256, 0x0000, 0x035e,
+ 0x0256, 0xffa0, 0xfeb2,
+ 0x0256, 0x044c, 0x0000,
+ },
+ { 0xffc0, 0xfe00, 0xfe00,},
+ { 0x0, 0x0, 0x0,},
+ { 0x40, 0x3ac, 0x40, 0x3c0, 0x40, 0x3c0,},
+ { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+ },
+ [MDSS_MDP_CSC_YUV2RGB_2020FR] = {
+ 0,
+ {
+ 0x0200, 0x0000, 0x02f3,
+ 0x0200, 0xffac, 0xfedb,
+ 0x0200, 0x03c3, 0x0000,
+ },
+ { 0x0000, 0xfe00, 0xfe00,},
+ { 0x0, 0x0, 0x0,},
+ { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+ { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+ },
[MDSS_MDP_CSC_RGB2YUV_601L] = {
0,
{
@@ -195,6 +267,30 @@ struct mdp_csc_cfg mdp_csc_10bit_convert[MDSS_MDP_MAX_CSC] = {
{ 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
{ 0x0040, 0x03ac, 0x0040, 0x03c0, 0x0040, 0x03c0,},
},
+ [MDSS_MDP_CSC_RGB2YUV_2020L] = {
+ 0,
+ {
+ 0x0073, 0x0129, 0x001a,
+ 0xffc1, 0xff5e, 0x00e0,
+ 0x00e0, 0xff32, 0xffee
+ },
+ { 0x0, 0x0, 0x0,},
+ { 0x0040, 0x0200, 0x0200,},
+ { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+ { 0x0040, 0x03ac, 0x0040, 0x03c0, 0x0040, 0x03c0,},
+ },
+ [MDSS_MDP_CSC_RGB2YUV_2020FR] = {
+ 0,
+ {
+ 0x0086, 0x015b, 0x001e,
+ 0xffb9, 0xff47, 0x0100,
+ 0x0100, 0xff15, 0xffeb
+ },
+ { 0x0, 0x0, 0x0,},
+ { 0x0, 0x0200, 0x0200,},
+ { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+ { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
+ },
[MDSS_MDP_CSC_YUV2YUV] = {
0,
{
@@ -2480,6 +2576,28 @@ static int pp_dest_scaler_setup(struct mdss_mdp_mixer *mixer)
if (!test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) || !ds)
return 0;
+ /*
+ * Non-validated DS data will be related to PM event. It is required
+ * to send out last setup to match the mixer and panel configuration.
+ */
+ if (!(ds->flags & DS_VALIDATE)) {
+ pr_debug("Apply old DS[%d] for non validate data\n", ds->num);
+ if (ds->flags & DS_ENABLE)
+ ds->flags |= (DS_SCALE_UPDATE | DS_ENHANCER_UPDATE);
+ ds->flags |= DS_VALIDATE;
+ }
+
+ /*
+ * If mark for dirty update, force update to scaler and detail
+ * enhancer.
+ */
+ if (ds->flags & DS_DIRTY_UPDATE) {
+ pr_debug("Scale dirty update requested\n");
+ ds->flags |= (DS_SCALE_UPDATE | DS_ENHANCER_UPDATE |
+ DS_VALIDATE);
+ ds->flags &= ~DS_DIRTY_UPDATE;
+ }
+
ds_offset = ds->ds_base;
op_mode = readl_relaxed(MDSS_MDP_REG_DEST_SCALER_OP_MODE +
ds_offset);
@@ -2519,12 +2637,37 @@ static int pp_dest_scaler_setup(struct mdss_mdp_mixer *mixer)
}
/* Destinations scaler shared the flush with DSPP in control */
- if (ds->flags & DS_ENABLE)
+ if (ds->flags & (DS_ENABLE | DS_VALIDATE)) {
+ pr_debug("FLUSH[%d]: flags:%X, op_mode:%x\n",
+ ds->num, ds->flags, op_mode);
ctl->flush_bits |= BIT(13 + ds->num);
+ }
+ ds->flags &= ~DS_VALIDATE;
return 0;
}
+void mdss_mdp_pp_dest_scaler_resume(struct mdss_mdp_ctl *ctl)
+{
+ if (!ctl || !ctl->mdata) {
+ pr_err("Invalid ctl\n");
+ return;
+ }
+
+ if (!test_bit(MDSS_CAPS_DEST_SCALER, ctl->mdata->mdss_caps_map))
+ return;
+
+ if (ctl->mixer_left && ctl->mixer_left->ds) {
+ ctl->mixer_left->ds->flags |= DS_DIRTY_UPDATE;
+ pr_debug("DS left mark dirty\n");
+ }
+
+ if (ctl->mixer_right && ctl->mixer_right->ds) {
+ ctl->mixer_right->ds->flags |= DS_DIRTY_UPDATE;
+ pr_debug("DS right mark dirty\n");
+ }
+}
+
int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl)
{
int ret = 0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_util.c b/drivers/video/fbdev/msm/mdss_mdp_util.c
index 8b0ebc3fdf05..199c2b66d90e 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_util.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_util.c
@@ -241,7 +241,7 @@ void mdss_mdp_intersect_rect(struct mdss_rect *res_rect,
void mdss_mdp_crop_rect(struct mdss_rect *src_rect,
struct mdss_rect *dst_rect,
- const struct mdss_rect *sci_rect)
+ const struct mdss_rect *sci_rect, bool normalize)
{
struct mdss_rect res;
mdss_mdp_intersect_rect(&res, dst_rect, sci_rect);
@@ -253,9 +253,17 @@ void mdss_mdp_crop_rect(struct mdss_rect *src_rect,
src_rect->w = res.w;
src_rect->h = res.h;
}
- *dst_rect = (struct mdss_rect)
- {(res.x - sci_rect->x), (res.y - sci_rect->y),
- res.w, res.h};
+
+ /* adjust dest rect based on the sci_rect starting */
+ if (normalize) {
+ *dst_rect = (struct mdss_rect) {(res.x - sci_rect->x),
+ (res.y - sci_rect->y), res.w, res.h};
+
+ /* return the actual cropped intersecting rect */
+ } else {
+ *dst_rect = (struct mdss_rect) {res.x, res.y,
+ res.w, res.h};
+ }
}
}
@@ -963,16 +971,17 @@ static int mdss_mdp_put_img(struct mdss_mdp_img_data *data, bool rotator,
data->srcp_dma_buf = NULL;
}
}
- } else if (data->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION) {
+ } else if ((data->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION) ||
+ (data->flags & MDP_SECURE_CAMERA_OVERLAY_SESSION)) {
/*
- * skip memory unmapping - secure display uses physical
- * address which does not require buffer unmapping
+ * skip memory unmapping - secure display and camera uses
+ * physical address which does not require buffer unmapping
*
* For LT targets in secure display usecase, srcp_dma_buf will
* be filled due to map call which will be unmapped above.
*
*/
- pr_debug("skip memory unmapping for secure display content\n");
+ pr_debug("skip memory unmapping for secure display/camera content\n");
} else {
return -ENOMEM;
}
@@ -1188,7 +1197,7 @@ err_unmap:
}
static int mdss_mdp_data_get(struct mdss_mdp_data *data,
- struct msmfb_data *planes, int num_planes, u32 flags,
+ struct msmfb_data *planes, int num_planes, u64 flags,
struct device *dev, bool rotator, int dir)
{
int i, rc = 0;
@@ -1201,7 +1210,7 @@ static int mdss_mdp_data_get(struct mdss_mdp_data *data,
rc = mdss_mdp_get_img(&planes[i], &data->p[i], dev, rotator,
dir);
if (rc) {
- pr_err("failed to get buf p=%d flags=%x\n", i, flags);
+ pr_err("failed to get buf p=%d flags=%llx\n", i, flags);
while (i > 0) {
i--;
mdss_mdp_put_img(&data->p[i], rotator, dir);
@@ -1251,7 +1260,7 @@ void mdss_mdp_data_free(struct mdss_mdp_data *data, bool rotator, int dir)
}
int mdss_mdp_data_get_and_validate_size(struct mdss_mdp_data *data,
- struct msmfb_data *planes, int num_planes, u32 flags,
+ struct msmfb_data *planes, int num_planes, u64 flags,
struct device *dev, bool rotator, int dir,
struct mdp_layer_buffer *buffer)
{
diff --git a/drivers/video/fbdev/msm/mdss_panel.c b/drivers/video/fbdev/msm/mdss_panel.c
index 16c2d4e6e92d..31cf74274131 100644
--- a/drivers/video/fbdev/msm/mdss_panel.c
+++ b/drivers/video/fbdev/msm/mdss_panel.c
@@ -455,10 +455,12 @@ int mdss_panel_debugfs_setup(struct mdss_panel_info *panel_info, struct dentry
return -ENOMEM;
}
+ debugfs_info->parent = parent;
debugfs_info->root = debugfs_create_dir(intf_str, parent);
if (IS_ERR_OR_NULL(debugfs_info->root)) {
pr_err("Debugfs create dir failed with error: %ld\n",
PTR_ERR(debugfs_info->root));
+ kfree(debugfs_info);
return -ENODEV;
}
@@ -503,6 +505,7 @@ int mdss_panel_debugfs_init(struct mdss_panel_info *panel_info,
intf_str);
if (rc) {
pr_err("error in initilizing panel debugfs\n");
+ mdss_panel_debugfs_cleanup(&pdata->panel_info);
return rc;
}
pdata = pdata->next;
@@ -516,13 +519,16 @@ void mdss_panel_debugfs_cleanup(struct mdss_panel_info *panel_info)
{
struct mdss_panel_data *pdata;
struct mdss_panel_debugfs_info *debugfs_info;
+ struct dentry *parent = NULL;
pdata = container_of(panel_info, struct mdss_panel_data, panel_info);
do {
debugfs_info = pdata->panel_info.debugfs_info;
- if (debugfs_info && debugfs_info->root)
- debugfs_remove_recursive(debugfs_info->root);
+ if (debugfs_info && !parent)
+ parent = debugfs_info->parent;
+ kfree(debugfs_info);
pdata = pdata->next;
} while (pdata);
+ debugfs_remove_recursive(parent);
pr_debug("Cleaned up mdss_panel_debugfs_info\n");
}
diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h
index be0491195263..0483e3d42873 100644
--- a/drivers/video/fbdev/msm/mdss_panel.h
+++ b/drivers/video/fbdev/msm/mdss_panel.h
@@ -137,6 +137,25 @@ enum {
SIM_HW_TE_MODE,
};
+
+/*
+ * enum partial_update_mode - Different modes for partial update feature
+ *
+ * @PU_NOT_SUPPORTED: Feature is not supported on target.
+ * @PU_SINGLE_ROI: Default mode, only one ROI is triggered to the
+ * panel(one on each DSI in case of split dsi)
+ * @PU_DUAL_ROI: Support for sending two roi's that are clubbed
+ * together as one big single ROI. This is only
+ * supported on certain panels that have this
+ * capability in their DDIC.
+ *
+ */
+enum {
+ PU_NOT_SUPPORTED = 0,
+ PU_SINGLE_ROI,
+ PU_DUAL_ROI,
+};
+
struct mdss_rect {
u16 x;
u16 y;
@@ -664,6 +683,50 @@ struct mdss_panel_roi_alignment {
u32 min_height;
};
+
+/*
+ * Nomeclature used to represent partial ROI in case of
+ * dual roi when the panel supports it. Region marked (XXX) is
+ * the extended roi to align with the second roi since LM output
+ * has to be rectangle.
+ *
+ * For single ROI, only the first ROI will be used in the struct.
+ * DSI driver will merge it based on the partial_update_roi_merge
+ * property.
+ *
+ * -------------------------------
+ * | DSI0 | DSI1 |
+ * -------------------------------
+ * | | |
+ * | | |
+ * | =========|=======----+ |
+ * | | | |XXXX| |
+ * | | First| Roi |XXXX| |
+ * | | | |XXXX| |
+ * | =========|=======----+ |
+ * | | |
+ * | | |
+ * | | |
+ * | +----================= |
+ * | |XXXX| | | |
+ * | |XXXX| Second Roi | |
+ * | |XXXX| | | |
+ * | +----====|============ |
+ * | | |
+ * | | |
+ * | | |
+ * | | |
+ * | | |
+ * ------------------------------
+ *
+ */
+
+struct mdss_dsi_dual_pu_roi {
+ struct mdss_rect first_roi;
+ struct mdss_rect second_roi;
+ bool enabled;
+};
+
struct mdss_panel_info {
u32 xres;
u32 yres;
@@ -689,6 +752,7 @@ struct mdss_panel_info {
u32 vic; /* video identification code */
u32 deep_color;
struct mdss_rect roi;
+ struct mdss_dsi_dual_pu_roi dual_roi;
int pwm_pmic_gpio;
int pwm_lpg_chan;
int pwm_period;
@@ -723,8 +787,8 @@ struct mdss_panel_info {
u32 cont_splash_enabled;
bool esd_rdy;
- bool partial_update_supported; /* value from dts if pu is supported */
- bool partial_update_enabled; /* is pu currently allowed */
+ u32 partial_update_supported; /* value from dts if pu is supported */
+ u32 partial_update_enabled; /* is pu currently allowed */
u32 dcs_cmd_by_left;
u32 partial_update_roi_merge;
struct ion_handle *splash_ihdl;
@@ -879,6 +943,7 @@ struct mdss_panel_data {
struct mdss_panel_debugfs_info {
struct dentry *root;
+ struct dentry *parent;
struct mdss_panel_info panel_info;
u32 override_flag;
struct mdss_panel_debugfs_info *next;
@@ -999,6 +1064,27 @@ static inline bool is_lm_configs_dsc_compatible(struct mdss_panel_info *pinfo,
return true;
}
+static inline bool is_valid_pu_dual_roi(struct mdss_panel_info *pinfo,
+ struct mdss_rect *first_roi, struct mdss_rect *second_roi)
+{
+ if ((first_roi->x != second_roi->x) || (first_roi->w != second_roi->w)
+ || (first_roi->y > second_roi->y)
+ || ((first_roi->y + first_roi->h) > second_roi->y)
+ || (is_dsc_compression(pinfo) &&
+ !is_lm_configs_dsc_compatible(pinfo,
+ first_roi->w, first_roi->h) &&
+ !is_lm_configs_dsc_compatible(pinfo,
+ second_roi->w, second_roi->h))) {
+ pr_err("Invalid multiple PU ROIs, roi0:{%d,%d,%d,%d}, roi1{%d,%d,%d,%d}\n",
+ first_roi->x, first_roi->y, first_roi->w,
+ first_roi->h, second_roi->x, second_roi->y,
+ second_roi->w, second_roi->h);
+ return false;
+ }
+
+ return true;
+}
+
int mdss_register_panel(struct platform_device *pdev,
struct mdss_panel_data *pdata);
diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c
index eab7bcaaa156..2239791fdad0 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.c
+++ b/drivers/video/fbdev/msm/mdss_smmu.c
@@ -162,12 +162,15 @@ end:
}
/*
- * mdss_smmu_v2_attach()
+ * mdss_smmu_attach_v2()
*
* Associates each configured VA range with the corresponding smmu context
* bank device. Enables the clks as smmu_v2 requires voting it before the usage.
* And iommu attach is done only once during the initial attach and it is never
* detached as smmu v2 uses a feature called 'retention'.
+ * Only detach the secure and non-secure contexts in case of secure display
+ * case and secure contexts for secure camera use cases for the platforms
+ * which have caps MDSS_CAPS_SEC_DETACH_SMMU enabled
*/
static int mdss_smmu_attach_v2(struct mdss_data_type *mdata)
{
@@ -191,7 +194,9 @@ static int mdss_smmu_attach_v2(struct mdss_data_type *mdata)
}
mdss_smmu->handoff_pending = false;
- if (!mdss_smmu->domain_attached) {
+ if (!mdss_smmu->domain_attached &&
+ mdss_smmu_is_valid_domain_condition(mdata,
+ i, true)) {
rc = arm_iommu_attach_device(mdss_smmu->dev,
mdss_smmu->mmu_mapping);
if (rc) {
@@ -229,10 +234,11 @@ err:
}
/*
- * mdss_smmu_v2_detach()
+ * mdss_smmu_detach_v2()
*
- * Only disables the clks as it is not required to detach the iommu mapped
- * VA range from the device in smmu_v2 as explained in the mdss_smmu_v2_attach
+ * Disables the clks only when it is not required to detach the iommu mapped
+ * VA range (as long as not in secure display use case)
+ * from the device in smmu_v2 as explained in the mdss_smmu_v2_attach
*/
static int mdss_smmu_detach_v2(struct mdss_data_type *mdata)
{
@@ -245,8 +251,24 @@ static int mdss_smmu_detach_v2(struct mdss_data_type *mdata)
continue;
mdss_smmu = mdss_smmu_get_cb(i);
- if (mdss_smmu && mdss_smmu->dev && !mdss_smmu->handoff_pending)
- mdss_smmu_enable_power(mdss_smmu, false);
+ if (mdss_smmu && mdss_smmu->dev) {
+ if (!mdss_smmu->handoff_pending &&
+ mdss_smmu->domain_attached &&
+ mdss_smmu_is_valid_domain_condition(mdata,
+ i, false)) {
+ /*
+ * if entering in secure display or
+ * secure camera use case(for secured contexts
+ * leave the smmu clocks on and only detach the
+ * smmu contexts
+ */
+ arm_iommu_detach_device(mdss_smmu->dev);
+ mdss_smmu->domain_attached = false;
+ pr_debug("iommu v2 domain[%i] detached\n", i);
+ } else {
+ mdss_smmu_enable_power(mdss_smmu, false);
+ }
+ }
}
mutex_unlock(&mdp_iommu_lock);
@@ -609,6 +631,7 @@ int mdss_smmu_probe(struct platform_device *pdev)
}
mdss_smmu = &mdata->mdss_smmu[smmu_domain.domain];
+ mdss_smmu->domain = smmu_domain.domain;
mp = &mdss_smmu->mp;
memset(mp, 0, sizeof(struct dss_module_power));
diff --git a/drivers/video/fbdev/msm/mdss_smmu.h b/drivers/video/fbdev/msm/mdss_smmu.h
index a987066cc773..f7e6e275c16a 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.h
+++ b/drivers/video/fbdev/msm/mdss_smmu.h
@@ -73,6 +73,38 @@ static inline bool mdss_smmu_is_valid_domain_type(struct mdss_data_type *mdata,
return true;
}
+static inline bool mdss_smmu_is_valid_domain_condition(
+ struct mdss_data_type *mdata,
+ int domain_type,
+ bool is_attach)
+{
+ if (is_attach) {
+ if (test_bit(MDSS_CAPS_SEC_DETACH_SMMU,
+ mdata->mdss_caps_map) &&
+ (mdata->sec_disp_en ||
+ (mdata->sec_cam_en &&
+ domain_type == MDSS_IOMMU_DOMAIN_SECURE))) {
+ pr_debug("SMMU attach not attempted, sd:%d, sc:%d\n",
+ mdata->sec_disp_en, mdata->sec_cam_en);
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ if (test_bit(MDSS_CAPS_SEC_DETACH_SMMU,
+ mdata->mdss_caps_map) &&
+ (mdata->sec_disp_en ||
+ (mdata->sec_cam_en &&
+ domain_type == MDSS_IOMMU_DOMAIN_SECURE))) {
+ pr_debug("SMMU detach attempted, sd:%d, sc:%d\n",
+ mdata->sec_disp_en, mdata->sec_cam_en);
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
static inline struct mdss_smmu_client *mdss_smmu_get_cb(u32 domain)
{
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
@@ -96,7 +128,7 @@ static inline int is_mdss_iommu_attached(void)
return mdata ? mdata->iommu_attached : false;
}
-static inline int mdss_smmu_get_domain_type(u32 flags, bool rotator)
+static inline int mdss_smmu_get_domain_type(u64 flags, bool rotator)
{
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
int type;
diff --git a/drivers/video/fbdev/msm/msm_ext_display.c b/drivers/video/fbdev/msm/msm_ext_display.c
index f6c6548bdaa5..ca01ee6345d2 100644
--- a/drivers/video/fbdev/msm/msm_ext_display.c
+++ b/drivers/video/fbdev/msm/msm_ext_display.c
@@ -38,15 +38,18 @@ struct msm_ext_disp {
struct switch_dev hdmi_sdev;
struct switch_dev audio_sdev;
bool ack_enabled;
- atomic_t ack_pending;
+ bool audio_session_on;
struct list_head display_list;
struct mutex lock;
+ struct completion hpd_comp;
};
static int msm_ext_disp_get_intf_data(struct msm_ext_disp *ext_disp,
enum msm_ext_disp_type type,
struct msm_ext_disp_init_data **data);
static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack);
+static int msm_ext_disp_update_audio_ops(struct msm_ext_disp *ext_disp,
+ enum msm_ext_disp_cable_state state);
static int msm_ext_disp_switch_dev_register(struct msm_ext_disp *ext_disp)
{
@@ -313,14 +316,14 @@ static void msm_ext_disp_remove_intf_data(struct msm_ext_disp *ext_disp,
}
}
-static void msm_ext_disp_send_cable_notification(struct msm_ext_disp *ext_disp,
+static int msm_ext_disp_send_cable_notification(struct msm_ext_disp *ext_disp,
enum msm_ext_disp_cable_state new_state)
{
int state = EXT_DISPLAY_CABLE_STATE_MAX;
if (!ext_disp) {
pr_err("Invalid params\n");
- return;
+ return -EINVAL;
}
state = ext_disp->hdmi_sdev.state;
@@ -330,6 +333,77 @@ static void msm_ext_disp_send_cable_notification(struct msm_ext_disp *ext_disp,
ext_disp->hdmi_sdev.state == state ?
"is same" : "switched to",
ext_disp->hdmi_sdev.state);
+
+ return ext_disp->hdmi_sdev.state == state ? 0 : 1;
+}
+
+static int msm_ext_disp_send_audio_notification(struct msm_ext_disp *ext_disp,
+ enum msm_ext_disp_cable_state new_state)
+{
+ int state = EXT_DISPLAY_CABLE_STATE_MAX;
+
+ if (!ext_disp) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ state = ext_disp->audio_sdev.state;
+ switch_set_state(&ext_disp->audio_sdev, !!new_state);
+
+ pr_debug("Audio state %s %d\n",
+ ext_disp->audio_sdev.state == state ?
+ "is same" : "switched to",
+ ext_disp->audio_sdev.state);
+
+ return ext_disp->audio_sdev.state == state ? 0 : 1;
+}
+
+static int msm_ext_disp_process_display(struct msm_ext_disp *ext_disp,
+ enum msm_ext_disp_cable_state state)
+{
+ int ret = msm_ext_disp_send_cable_notification(ext_disp, state);
+
+ /* positive ret value means audio node was switched */
+ if (IS_ERR_VALUE(ret) || !ret) {
+ pr_debug("not waiting for display\n");
+ goto end;
+ }
+
+ reinit_completion(&ext_disp->hpd_comp);
+ ret = wait_for_completion_timeout(&ext_disp->hpd_comp, HZ * 2);
+ if (!ret) {
+ pr_err("display timeout\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ ret = 0;
+end:
+ return ret;
+}
+
+static int msm_ext_disp_process_audio(struct msm_ext_disp *ext_disp,
+ enum msm_ext_disp_cable_state state)
+{
+ int ret = msm_ext_disp_send_audio_notification(ext_disp, state);
+
+ /* positive ret value means audio node was switched */
+ if (IS_ERR_VALUE(ret) || !ret || !ext_disp->ack_enabled) {
+ pr_debug("not waiting for audio\n");
+ goto end;
+ }
+
+ reinit_completion(&ext_disp->hpd_comp);
+ ret = wait_for_completion_timeout(&ext_disp->hpd_comp, HZ * 2);
+ if (!ret) {
+ pr_err("audio timeout\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ ret = 0;
+end:
+ return ret;
}
static int msm_ext_disp_hpd(struct platform_device *pdev,
@@ -337,7 +411,6 @@ static int msm_ext_disp_hpd(struct platform_device *pdev,
enum msm_ext_disp_cable_state state)
{
int ret = 0;
- struct msm_ext_disp_init_data *data = NULL;
struct msm_ext_disp *ext_disp = NULL;
if (!pdev) {
@@ -379,27 +452,28 @@ static int msm_ext_disp_hpd(struct platform_device *pdev,
goto end;
}
- ret = msm_ext_disp_get_intf_data(ext_disp, type, &data);
- if (ret)
- goto end;
-
if (state == EXT_DISPLAY_CABLE_CONNECT) {
- ext_disp->current_disp = data->type;
- } else if ((state == EXT_DISPLAY_CABLE_DISCONNECT) &&
- !ext_disp->ack_enabled) {
- if (ext_disp->ops) {
- ext_disp->ops->audio_info_setup = NULL;
- ext_disp->ops->get_audio_edid_blk = NULL;
- ext_disp->ops->cable_status = NULL;
- ext_disp->ops->get_intf_id = NULL;
- ext_disp->ops->teardown_done = NULL;
- }
+ ext_disp->current_disp = type;
+
+ ret = msm_ext_disp_process_display(ext_disp, state);
+ if (ret)
+ goto end;
+
+ msm_ext_disp_update_audio_ops(ext_disp, state);
+ if (ret)
+ goto end;
+
+ ret = msm_ext_disp_process_audio(ext_disp, state);
+ if (ret)
+ goto end;
+ } else {
+ msm_ext_disp_process_audio(ext_disp, state);
+ msm_ext_disp_update_audio_ops(ext_disp, state);
+ msm_ext_disp_process_display(ext_disp, state);
ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX;
}
- msm_ext_disp_send_cable_notification(ext_disp, state);
-
pr_debug("Hpd (%d) for display (%s)\n", state,
msm_ext_disp_name(type));
@@ -427,23 +501,18 @@ static int msm_ext_disp_get_intf_data_helper(struct platform_device *pdev,
goto end;
}
- mutex_lock(&ext_disp->lock);
-
if (ext_disp->current_disp == EXT_DISPLAY_TYPE_MAX) {
ret = -EINVAL;
pr_err("No display connected\n");
- goto error;
+ goto end;
}
ret = msm_ext_disp_get_intf_data(ext_disp, ext_disp->current_disp,
data);
- if (ret)
- goto error;
-error:
- mutex_unlock(&ext_disp->lock);
end:
return ret;
}
+
static int msm_ext_disp_cable_status(struct platform_device *pdev, u32 vote)
{
int ret = 0;
@@ -480,11 +549,21 @@ static int msm_ext_disp_audio_info_setup(struct platform_device *pdev,
{
int ret = 0;
struct msm_ext_disp_init_data *data = NULL;
+ struct msm_ext_disp *ext_disp = NULL;
ret = msm_ext_disp_get_intf_data_helper(pdev, &data);
if (ret || !data)
goto end;
+ ext_disp = platform_get_drvdata(pdev);
+ if (!ext_disp) {
+ pr_err("No drvdata found\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ ext_disp->audio_session_on = true;
+
ret = data->codec_ops.audio_info_setup(data->pdev, params);
end:
@@ -495,6 +574,7 @@ static void msm_ext_disp_teardown_done(struct platform_device *pdev)
{
int ret = 0;
struct msm_ext_disp_init_data *data = NULL;
+ struct msm_ext_disp *ext_disp = NULL;
ret = msm_ext_disp_get_intf_data_helper(pdev, &data);
if (ret || !data) {
@@ -502,7 +582,21 @@ static void msm_ext_disp_teardown_done(struct platform_device *pdev)
return;
}
- data->codec_ops.teardown_done(data->pdev);
+ ext_disp = platform_get_drvdata(pdev);
+ if (!ext_disp) {
+ pr_err("No drvdata found\n");
+ return;
+ }
+
+ if (data->codec_ops.teardown_done)
+ data->codec_ops.teardown_done(data->pdev);
+
+ ext_disp->audio_session_on = false;
+
+ pr_debug("%s tearing down audio\n",
+ msm_ext_disp_name(ext_disp->current_disp));
+
+ complete_all(&ext_disp->hpd_comp);
}
static int msm_ext_disp_get_intf_id(struct platform_device *pdev)
@@ -523,93 +617,78 @@ static int msm_ext_disp_get_intf_id(struct platform_device *pdev)
goto end;
}
- mutex_lock(&ext_disp->lock);
ret = ext_disp->current_disp;
- mutex_unlock(&ext_disp->lock);
end:
return ret;
}
+static int msm_ext_disp_update_audio_ops(struct msm_ext_disp *ext_disp,
+ enum msm_ext_disp_cable_state state)
+{
+ int ret = 0;
+ struct msm_ext_disp_audio_codec_ops *ops = ext_disp->ops;
+
+ if (!ops) {
+ pr_err("Invalid audio ops\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (state == EXT_DISPLAY_CABLE_CONNECT) {
+ ops->audio_info_setup = msm_ext_disp_audio_info_setup;
+ ops->get_audio_edid_blk = msm_ext_disp_get_audio_edid_blk;
+ ops->cable_status = msm_ext_disp_cable_status;
+ ops->get_intf_id = msm_ext_disp_get_intf_id;
+ ops->teardown_done = msm_ext_disp_teardown_done;
+ } else {
+ ops->audio_info_setup = NULL;
+ ops->get_audio_edid_blk = NULL;
+ ops->cable_status = NULL;
+ ops->get_intf_id = NULL;
+ ops->teardown_done = NULL;
+ }
+end:
+ return ret;
+}
+
static int msm_ext_disp_notify(struct platform_device *pdev,
- enum msm_ext_disp_cable_state new_state)
+ enum msm_ext_disp_cable_state state)
{
int ret = 0;
- int state = 0;
- bool switched;
- struct msm_ext_disp_init_data *data = NULL;
struct msm_ext_disp *ext_disp = NULL;
if (!pdev) {
pr_err("Invalid platform device\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto end;
}
ext_disp = platform_get_drvdata(pdev);
if (!ext_disp) {
pr_err("Invalid drvdata\n");
- return -EINVAL;
- }
-
- mutex_lock(&ext_disp->lock);
-
- if (state < EXT_DISPLAY_CABLE_DISCONNECT ||
- state >= EXT_DISPLAY_CABLE_STATE_MAX) {
- pr_err("Invalid state (%d)\n", state);
ret = -EINVAL;
goto end;
}
- state = ext_disp->audio_sdev.state;
- if (state == new_state)
- goto end;
-
- if (ext_disp->ack_enabled &&
- atomic_read(&ext_disp->ack_pending)) {
+ if (state < EXT_DISPLAY_CABLE_DISCONNECT ||
+ state >= EXT_DISPLAY_CABLE_STATE_MAX) {
+ pr_err("Invalid state (%d)\n", state);
ret = -EINVAL;
- pr_err("%s ack pending, not notifying %s\n",
- state ? "connect" : "disconnect",
- new_state ? "connect" : "disconnect");
goto end;
}
- ret = msm_ext_disp_get_intf_data(ext_disp, ext_disp->current_disp,
- &data);
- if (ret)
- goto end;
-
- if (new_state == EXT_DISPLAY_CABLE_CONNECT && ext_disp->ops) {
- ext_disp->ops->audio_info_setup =
- msm_ext_disp_audio_info_setup;
- ext_disp->ops->get_audio_edid_blk =
- msm_ext_disp_get_audio_edid_blk;
- ext_disp->ops->cable_status =
- msm_ext_disp_cable_status;
- ext_disp->ops->get_intf_id =
- msm_ext_disp_get_intf_id;
- ext_disp->ops->teardown_done =
- msm_ext_disp_teardown_done;
- }
-
- switch_set_state(&ext_disp->audio_sdev, (int)new_state);
- switched = ext_disp->audio_sdev.state != state;
-
- if (ext_disp->ack_enabled && switched)
- atomic_set(&ext_disp->ack_pending, 1);
-
- pr_debug("audio %s %s\n", switched ? "switched to" : "same as",
- ext_disp->audio_sdev.state ? "HDMI" : "SPKR");
+ pr_debug("%s notifying hpd (%d)\n",
+ msm_ext_disp_name(ext_disp->current_disp), state);
+ complete_all(&ext_disp->hpd_comp);
end:
- mutex_unlock(&ext_disp->lock);
-
return ret;
}
static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack)
{
u32 ack_hpd;
- u32 hpd;
int ret = 0;
struct msm_ext_disp *ext_disp = NULL;
@@ -624,10 +703,6 @@ static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack)
return -EINVAL;
}
- mutex_lock(&ext_disp->lock);
-
- hpd = ext_disp->current_disp != EXT_DISPLAY_TYPE_MAX;
-
if (ack & AUDIO_ACK_SET_ENABLE) {
ext_disp->ack_enabled = ack & AUDIO_ACK_ENABLE ?
true : false;
@@ -640,44 +715,14 @@ static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack)
if (!ext_disp->ack_enabled)
goto end;
- atomic_set(&ext_disp->ack_pending, 0);
-
ack_hpd = ack & AUDIO_ACK_CONNECT;
- pr_debug("acknowledging %s\n",
- ack_hpd ? "connect" : "disconnect");
-
- /**
- * If the ack feature is enabled and we receive an ack for
- * disconnect then we reset the current display state to
- * empty.
- */
- if (!ack_hpd) {
- if (ext_disp->ops) {
- ext_disp->ops->audio_info_setup = NULL;
- ext_disp->ops->get_audio_edid_blk = NULL;
- ext_disp->ops->cable_status = NULL;
- ext_disp->ops->get_intf_id = NULL;
- ext_disp->ops->teardown_done = NULL;
- }
-
- ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX;
- }
-
- if (ack_hpd != hpd) {
- pr_err("unbalanced audio state, ack %d, hpd %d\n",
- ack_hpd, hpd);
-
- mutex_unlock(&ext_disp->lock);
-
- ret = msm_ext_disp_notify(pdev, hpd);
-
- return ret;
- }
+ pr_debug("%s acknowledging audio (%d)\n",
+ msm_ext_disp_name(ext_disp->current_disp), ack_hpd);
+ if (!ext_disp->audio_session_on)
+ complete_all(&ext_disp->hpd_comp);
end:
- mutex_unlock(&ext_disp->lock);
-
return ret;
}
@@ -850,6 +895,7 @@ static int msm_ext_disp_probe(struct platform_device *pdev)
mutex_init(&ext_disp->lock);
INIT_LIST_HEAD(&ext_disp->display_list);
+ init_completion(&ext_disp->hpd_comp);
ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX;
return ret;
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 7d3e5d0e9aa4..8ab6238c9299 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -73,7 +73,7 @@ struct virtio_balloon {
/* The array of pfns we tell the Host about. */
unsigned int num_pfns;
- u32 pfns[VIRTIO_BALLOON_ARRAY_PFNS_MAX];
+ __virtio32 pfns[VIRTIO_BALLOON_ARRAY_PFNS_MAX];
/* Memory statistics */
int need_stats_update;
@@ -125,14 +125,16 @@ static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq)
wait_event(vb->acked, virtqueue_get_buf(vq, &len));
}
-static void set_page_pfns(u32 pfns[], struct page *page)
+static void set_page_pfns(struct virtio_balloon *vb,
+ __virtio32 pfns[], struct page *page)
{
unsigned int i;
/* Set balloon pfns pointing at this page.
* Note that the first pfn points at start of the page. */
for (i = 0; i < VIRTIO_BALLOON_PAGES_PER_PAGE; i++)
- pfns[i] = page_to_balloon_pfn(page) + i;
+ pfns[i] = cpu_to_virtio32(vb->vdev,
+ page_to_balloon_pfn(page) + i);
}
static void fill_balloon(struct virtio_balloon *vb, size_t num)
@@ -155,7 +157,7 @@ static void fill_balloon(struct virtio_balloon *vb, size_t num)
msleep(200);
break;
}
- set_page_pfns(vb->pfns + vb->num_pfns, page);
+ set_page_pfns(vb, vb->pfns + vb->num_pfns, page);
vb->num_pages += VIRTIO_BALLOON_PAGES_PER_PAGE;
if (!virtio_has_feature(vb->vdev,
VIRTIO_BALLOON_F_DEFLATE_ON_OOM))
@@ -171,10 +173,12 @@ static void fill_balloon(struct virtio_balloon *vb, size_t num)
static void release_pages_balloon(struct virtio_balloon *vb)
{
unsigned int i;
+ struct page *page;
/* Find pfns pointing at start of each page, get pages and free them. */
for (i = 0; i < vb->num_pfns; i += VIRTIO_BALLOON_PAGES_PER_PAGE) {
- struct page *page = balloon_pfn_to_page(vb->pfns[i]);
+ page = balloon_pfn_to_page(virtio32_to_cpu(vb->vdev,
+ vb->pfns[i]));
if (!virtio_has_feature(vb->vdev,
VIRTIO_BALLOON_F_DEFLATE_ON_OOM))
adjust_managed_page_count(page, 1);
@@ -197,7 +201,7 @@ static unsigned leak_balloon(struct virtio_balloon *vb, size_t num)
page = balloon_page_dequeue(vb_dev_info);
if (!page)
break;
- set_page_pfns(vb->pfns + vb->num_pfns, page);
+ set_page_pfns(vb, vb->pfns + vb->num_pfns, page);
vb->num_pages -= VIRTIO_BALLOON_PAGES_PER_PAGE;
}
@@ -465,13 +469,13 @@ static int virtballoon_migratepage(struct balloon_dev_info *vb_dev_info,
__count_vm_event(BALLOON_MIGRATE);
spin_unlock_irqrestore(&vb_dev_info->pages_lock, flags);
vb->num_pfns = VIRTIO_BALLOON_PAGES_PER_PAGE;
- set_page_pfns(vb->pfns, newpage);
+ set_page_pfns(vb, vb->pfns, newpage);
tell_host(vb, vb->inflate_vq);
/* balloon's page migration 2nd step -- deflate "page" */
balloon_page_delete(page);
vb->num_pfns = VIRTIO_BALLOON_PAGES_PER_PAGE;
- set_page_pfns(vb->pfns, page);
+ set_page_pfns(vb, vb->pfns, page);
tell_host(vb, vb->deflate_vq);
mutex_unlock(&vb->balloon_lock);
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index 12eab503efd1..cfab1d24e4bc 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -247,6 +247,19 @@ static enum bp_state update_schedule(enum bp_state state)
}
#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG
+static void release_memory_resource(struct resource *resource)
+{
+ if (!resource)
+ return;
+
+ /*
+ * No need to reset region to identity mapped since we now
+ * know that no I/O can be in this region
+ */
+ release_resource(resource);
+ kfree(resource);
+}
+
static struct resource *additional_memory_resource(phys_addr_t size)
{
struct resource *res;
@@ -268,20 +281,21 @@ static struct resource *additional_memory_resource(phys_addr_t size)
return NULL;
}
- return res;
-}
-
-static void release_memory_resource(struct resource *resource)
-{
- if (!resource)
- return;
+#ifdef CONFIG_SPARSEMEM
+ {
+ unsigned long limit = 1UL << (MAX_PHYSMEM_BITS - PAGE_SHIFT);
+ unsigned long pfn = res->start >> PAGE_SHIFT;
+
+ if (pfn > limit) {
+ pr_err("New System RAM resource outside addressable RAM (%lu > %lu)\n",
+ pfn, limit);
+ release_memory_resource(res);
+ return NULL;
+ }
+ }
+#endif
- /*
- * No need to reset region to identity mapped since we now
- * know that no I/O can be in this region
- */
- release_resource(resource);
- kfree(resource);
+ return res;
}
static enum bp_state reserve_additional_memory(void)
diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c
index 44367783f07a..83ec7b89d308 100644
--- a/drivers/xen/events/events_base.c
+++ b/drivers/xen/events/events_base.c
@@ -487,7 +487,8 @@ static void eoi_pirq(struct irq_data *data)
if (!VALID_EVTCHN(evtchn))
return;
- if (unlikely(irqd_is_setaffinity_pending(data))) {
+ if (unlikely(irqd_is_setaffinity_pending(data)) &&
+ likely(!irqd_irq_disabled(data))) {
int masked = test_and_set_mask(evtchn);
clear_evtchn(evtchn);
@@ -1370,7 +1371,8 @@ static void ack_dynirq(struct irq_data *data)
if (!VALID_EVTCHN(evtchn))
return;
- if (unlikely(irqd_is_setaffinity_pending(data))) {
+ if (unlikely(irqd_is_setaffinity_pending(data)) &&
+ likely(!irqd_irq_disabled(data))) {
int masked = test_and_set_mask(evtchn);
clear_evtchn(evtchn);
diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c
index 38272ad24551..f4edd6df3df2 100644
--- a/drivers/xen/evtchn.c
+++ b/drivers/xen/evtchn.c
@@ -316,7 +316,6 @@ static int evtchn_resize_ring(struct per_user_data *u)
{
unsigned int new_size;
evtchn_port_t *new_ring, *old_ring;
- unsigned int p, c;
/*
* Ensure the ring is large enough to capture all possible
@@ -346,20 +345,17 @@ static int evtchn_resize_ring(struct per_user_data *u)
/*
* Copy the old ring contents to the new ring.
*
- * If the ring contents crosses the end of the current ring,
- * it needs to be copied in two chunks.
+ * To take care of wrapping, a full ring, and the new index
+ * pointing into the second half, simply copy the old contents
+ * twice.
*
* +---------+ +------------------+
- * |34567 12| -> | 1234567 |
- * +-----p-c-+ +------------------+
+ * |34567 12| -> |34567 1234567 12|
+ * +-----p-c-+ +-------c------p---+
*/
- p = evtchn_ring_offset(u, u->ring_prod);
- c = evtchn_ring_offset(u, u->ring_cons);
- if (p < c) {
- memcpy(new_ring + c, u->ring + c, (u->ring_size - c) * sizeof(*u->ring));
- memcpy(new_ring + u->ring_size, u->ring, p * sizeof(*u->ring));
- } else
- memcpy(new_ring + c, u->ring + c, (p - c) * sizeof(*u->ring));
+ memcpy(new_ring, old_ring, u->ring_size * sizeof(*u->ring));
+ memcpy(new_ring + u->ring_size, old_ring,
+ u->ring_size * sizeof(*u->ring));
u->ring = new_ring;
u->ring_size = new_size;
diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c
index 70fa438000af..611f9c11da85 100644
--- a/drivers/xen/xen-acpi-processor.c
+++ b/drivers/xen/xen-acpi-processor.c
@@ -423,36 +423,7 @@ upload:
return 0;
}
-static int __init check_prereq(void)
-{
- struct cpuinfo_x86 *c = &cpu_data(0);
-
- if (!xen_initial_domain())
- return -ENODEV;
-
- if (!acpi_gbl_FADT.smi_command)
- return -ENODEV;
-
- if (c->x86_vendor == X86_VENDOR_INTEL) {
- if (!cpu_has(c, X86_FEATURE_EST))
- return -ENODEV;
- return 0;
- }
- if (c->x86_vendor == X86_VENDOR_AMD) {
- /* Copied from powernow-k8.h, can't include ../cpufreq/powernow
- * as we get compile warnings for the static functions.
- */
-#define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007
-#define USE_HW_PSTATE 0x00000080
- u32 eax, ebx, ecx, edx;
- cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
- if ((edx & USE_HW_PSTATE) != USE_HW_PSTATE)
- return -ENODEV;
- return 0;
- }
- return -ENODEV;
-}
/* acpi_perf_data is a pointer to percpu data. */
static struct acpi_processor_performance __percpu *acpi_perf_data;
@@ -509,10 +480,10 @@ struct notifier_block xen_acpi_processor_resume_nb = {
static int __init xen_acpi_processor_init(void)
{
unsigned int i;
- int rc = check_prereq();
+ int rc;
- if (rc)
- return rc;
+ if (!xen_initial_domain())
+ return -ENODEV;
nr_acpi_bits = get_max_acpi_id() + 1;
acpi_ids_done = kcalloc(BITS_TO_LONGS(nr_acpi_bits), sizeof(unsigned long), GFP_KERNEL);