summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/apei/ghes.c7
-rw-r--r--drivers/acpi/nfit.c16
-rw-r--r--drivers/acpi/video_detect.c20
-rw-r--r--drivers/android/binder.c10
-rw-r--r--drivers/ata/libata-core.c4
-rw-r--r--drivers/ata/sata_mv.c3
-rw-r--r--drivers/base/core.c8
-rw-r--r--drivers/base/firmware_class.c7
-rw-r--r--drivers/base/memory.c11
-rw-r--r--drivers/base/power/main.c12
-rw-r--r--drivers/base/power/power.h19
-rw-r--r--drivers/base/power/runtime.c8
-rw-r--r--drivers/base/power/wakeirq.c76
-rw-r--r--drivers/bcma/main.c4
-rw-r--r--drivers/block/loop.c34
-rw-r--r--drivers/block/zram/zram_drv.c8
-rw-r--r--drivers/bluetooth/ath3k.c2
-rw-r--r--drivers/bluetooth/btfm_slim_codec.c60
-rw-r--r--drivers/bluetooth/btfm_slim_wcn3990.c69
-rw-r--r--drivers/bluetooth/btusb.c1
-rw-r--r--drivers/bus/vexpress-config.c7
-rw-r--r--drivers/char/adsprpc.c6
-rw-r--r--drivers/char/diag/diagfwd_mhi.c3
-rw-r--r--drivers/char/diag/diagfwd_peripheral.c7
-rw-r--r--drivers/char/hw_random/msm-rng.c3
-rw-r--r--drivers/char/tpm/xen-tpmfront.c1
-rw-r--r--drivers/clk/bcm/clk-bcm2835.c4
-rw-r--r--drivers/clk/clk-wm831x.c2
-rw-r--r--drivers/clk/clk.c6
-rw-r--r--drivers/clk/clk.h2
-rw-r--r--drivers/clk/imx/clk-imx31.c4
-rw-r--r--drivers/clk/msm/clock-alpha-pll.c11
-rw-r--r--drivers/clk/msm/clock-cpu-8996.c2
-rw-r--r--drivers/clk/msm/clock-gcc-8996.c10
-rw-r--r--drivers/clk/qcom/Makefile2
-rw-r--r--drivers/clk/qcom/clk-branch.c3
-rw-r--r--drivers/clk/qcom/clk-cpu-osm.c2
-rw-r--r--drivers/clk/qcom/clk-debug.c277
-rw-r--r--drivers/clk/qcom/clk-debug.h120
-rw-r--r--drivers/clk/qcom/clk-dummy.c5
-rw-r--r--drivers/clk/qcom/clk-rcg.h3
-rw-r--r--drivers/clk/qcom/clk-rcg2.c70
-rw-r--r--drivers/clk/qcom/clk-smd-rpm.c3
-rw-r--r--drivers/clk/qcom/common.c239
-rw-r--r--drivers/clk/qcom/common.h100
-rw-r--r--drivers/clk/qcom/gcc-sdm660.c7
-rw-r--r--drivers/clk/qcom/mmcc-sdm660.c6
-rw-r--r--drivers/clk/ti/clk-3xxx.c20
-rw-r--r--drivers/clk/ti/clock.h9
-rw-r--r--drivers/clk/ti/dpll.c19
-rw-r--r--drivers/clk/ti/dpll3xxx.c67
-rw-r--r--drivers/clocksource/exynos_mct.c1
-rw-r--r--drivers/cpufreq/powernv-cpufreq.c8
-rw-r--r--drivers/crypto/caam/caamalg.c4
-rw-r--r--drivers/devfreq/governor_spdm_bw_hyp.c6
-rw-r--r--drivers/dma/ipu/ipu_irq.c2
-rw-r--r--drivers/dma/pl330.c11
-rw-r--r--drivers/esoc/esoc-mdm-4x.c23
-rw-r--r--drivers/esoc/esoc-mdm-drv.c4
-rw-r--r--drivers/esoc/esoc.h4
-rw-r--r--drivers/esoc/esoc_bus.c19
-rw-r--r--drivers/esoc/esoc_client.c14
-rw-r--r--drivers/esoc/esoc_dev.c17
-rw-r--r--drivers/firmware/efi/arm-init.c18
-rw-r--r--drivers/firmware/qcom/tz_log.c172
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v11_0.c6
-rw-r--r--drivers/gpu/drm/ast/ast_main.c7
-rw-r--r--drivers/gpu/drm/ast/ast_post.c48
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c2
-rw-r--r--drivers/gpu/drm/drm_dp_mst_topology.c2
-rw-r--r--drivers/gpu/drm/drm_edid.c3
-rw-r--r--drivers/gpu/drm/drm_modes.c7
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.c3
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c9
-rw-r--r--drivers/gpu/drm/i915/intel_display.c4
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c13
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c2
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c3
-rw-r--r--drivers/gpu/drm/msm/msm_gem_vma.c5
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c3
-rw-r--r--drivers/gpu/drm/msm/msm_iommu.c9
-rw-r--r--drivers/gpu/drm/msm/msm_iommu.h9
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/hw.c3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/base.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c9
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c7
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_cursor.c73
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h1
-rw-r--r--drivers/gpu/drm/radeon/si_dpm.c14
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c4
-rw-r--r--drivers/gpu/msm/adreno.c24
-rw-r--r--drivers/gpu/msm/adreno_compat.c26
-rw-r--r--drivers/gpu/msm/kgsl_iommu.c48
-rw-r--r--drivers/gpu/msm/kgsl_mmu.c14
-rw-r--r--drivers/gpu/msm/kgsl_mmu.h6
-rw-r--r--drivers/hid/hid-core.c1
-rw-r--r--drivers/hid/hid-corsair.c60
-rw-r--r--drivers/hid/hid-cypress.c3
-rw-r--r--drivers/hid/wacom_wac.c28
-rw-r--r--drivers/hv/hv.c10
-rw-r--r--drivers/hv/hv_fcopy.c4
-rw-r--r--drivers/hv/hv_kvp.c4
-rw-r--r--drivers/hv/hv_snapshot.c4
-rw-r--r--drivers/hwmon/amc6821.c4
-rw-r--r--drivers/hwmon/ds620.c2
-rw-r--r--drivers/hwmon/g762.c11
-rw-r--r--drivers/hwmon/nct7802.c8
-rw-r--r--drivers/hwmon/scpi-hwmon.c1
-rw-r--r--drivers/i2c/busses/i2c-msm-v2.c27
-rw-r--r--drivers/i2c/i2c-core.c2
-rw-r--r--drivers/i2c/i2c-dev.c2
-rw-r--r--drivers/iio/adc/qcom-rradc.c102
-rw-r--r--drivers/iio/adc/qcom-tadc.c244
-rw-r--r--drivers/iio/pressure/mpl115.c1
-rw-r--r--drivers/iio/pressure/mpl3115.c4
-rw-r--r--drivers/infiniband/core/cma.c6
-rw-r--r--drivers/infiniband/core/mad.c2
-rw-r--r--drivers/infiniband/core/multicast.c7
-rw-r--r--drivers/infiniband/core/umem.c2
-rw-r--r--drivers/infiniband/hw/mlx4/ah.c6
-rw-r--r--drivers/infiniband/hw/mlx4/main.c29
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c3
-rw-r--r--drivers/infiniband/hw/mlx5/mr.c28
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h20
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_cm.c29
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_ib.c12
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c68
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_multicast.c13
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c49
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.h1
-rw-r--r--drivers/input/joystick/xpad.c6
-rw-r--r--drivers/input/keyboard/gpio_keys.c77
-rw-r--r--drivers/input/misc/Kconfig10
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/drv260x.c1
-rw-r--r--drivers/input/misc/vl53L0/Makefile4
-rw-r--r--drivers/input/mouse/elan_i2c_core.c1
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h6
-rw-r--r--drivers/input/touchscreen/Kconfig22
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/elants_i2c.c4
-rw-r--r--drivers/input/touchscreen/gt9xx/Kconfig51
-rw-r--r--drivers/input/touchscreen/gt9xx/Makefile8
-rw-r--r--drivers/input/touchscreen/gt9xx/goodix_tool.c600
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.c2564
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.h220
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx_update.c1530
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c8
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h3
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c16
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c23
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c4
-rw-r--r--drivers/iommu/amd_iommu.c2
-rw-r--r--drivers/iommu/amd_iommu_v2.c4
-rw-r--r--drivers/iommu/arm-smmu.c2
-rw-r--r--drivers/iommu/dma-mapping-fast.c49
-rw-r--r--drivers/iommu/intel-iommu.c45
-rw-r--r--drivers/iommu/io-pgtable-arm.c6
-rw-r--r--drivers/irqchip/irq-bcm7038-l1.c26
-rw-r--r--drivers/irqchip/irq-gic-v3.c8
-rw-r--r--drivers/isdn/gigaset/ser-gigaset.c4
-rw-r--r--drivers/isdn/hardware/eicon/message.c3
-rw-r--r--drivers/leds/leds-qpnp.c118
-rw-r--r--drivers/md/Kconfig16
-rw-r--r--drivers/md/bcache/bcache.h4
-rw-r--r--drivers/md/bcache/btree.c40
-rw-r--r--drivers/md/bcache/btree.h3
-rw-r--r--drivers/md/bcache/request.c4
-rw-r--r--drivers/md/bcache/super.c2
-rw-r--r--drivers/md/dm-cache-target.c6
-rw-r--r--drivers/md/dm-crypt.c7
-rw-r--r--drivers/md/dm-flakey.c2
-rw-r--r--drivers/md/dm-stats.c1
-rw-r--r--drivers/md/dm-verity-target.c9
-rw-r--r--drivers/md/dm.c55
-rw-r--r--drivers/md/linear.c39
-rw-r--r--drivers/md/linear.h1
-rw-r--r--drivers/md/md.c2
-rw-r--r--drivers/md/persistent-data/dm-space-map-metadata.c14
-rw-r--r--drivers/md/raid5.c9
-rw-r--r--drivers/media/i2c/Kconfig1
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c6
-rw-r--r--drivers/media/pci/dm1105/Kconfig2
-rw-r--r--drivers/media/pci/solo6x10/solo6x10.h3
-rw-r--r--drivers/media/platform/am437x/am437x-vpfe.c2
-rw-r--r--drivers/media/platform/blackfin/ppi.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/camera/camera.c5
-rw-r--r--drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c46
-rw-r--r--drivers/media/platform/msm/camera_v2/common/cam_soc_api.c17
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c41
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp.c4
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp.h10
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp32.c4
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp40.c5
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp44.c5
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp46.c5
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.c137
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.h6
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp48.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c168
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h4
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c33
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c26
-rw-r--r--drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c4
-rw-r--r--drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c4
-rw-r--r--drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c62
-rw-r--r--drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h5
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c23
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c21
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c12
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_util.c15
-rw-r--r--drivers/media/platform/msm/vidc/msm_vdec.c1
-rw-r--r--drivers/media/platform/msm/vidc/msm_venc.c390
-rw-r--r--drivers/media/platform/msm/vidc/venus_hfi.c2
-rw-r--r--drivers/media/rc/ite-cir.c2
-rw-r--r--drivers/media/usb/siano/smsusb.c18
-rw-r--r--drivers/media/usb/uvc/uvc_queue.c2
-rw-r--r--drivers/misc/Kconfig8
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/mei/bus.c2
-rw-r--r--drivers/misc/mei/client.c2
-rw-r--r--drivers/misc/profiler.c461
-rw-r--r--drivers/misc/qpnp-misc.c138
-rw-r--r--drivers/misc/qseecom.c14
-rw-r--r--drivers/mmc/card/block.c8
-rw-r--r--drivers/mmc/card/mmc_test.c2
-rw-r--r--drivers/mmc/core/mmc.c4
-rw-r--r--drivers/mmc/host/mxs-mmc.c6
-rw-r--r--drivers/mmc/host/sdhci-msm.c20
-rw-r--r--drivers/mmc/host/sdhci-msm.h7
-rw-r--r--drivers/mmc/host/sdhci.c23
-rw-r--r--drivers/mtd/maps/pmcmsp-flash.c4
-rw-r--r--drivers/mtd/nand/Kconfig2
-rw-r--r--drivers/net/can/c_can/c_can_pci.c1
-rw-r--r--drivers/net/can/ti_hecc.c16
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb_core.c6
-rw-r--r--drivers/net/can/usb/usb_8dev.c9
-rw-r--r--drivers/net/ethernet/broadcom/bcmsysport.c25
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h8
-rw-r--r--drivers/net/ethernet/marvell/mvpp2.c59
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_rx.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c10
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/pci.h8
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/switchx2.c1
-rw-r--r--drivers/net/ethernet/msm/msm_rmnet_mhi.c174
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-main.c31
-rw-r--r--drivers/net/ethernet/renesas/ravb_main.c13
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c22
-rw-r--r--drivers/net/ethernet/ti/cpmac.c7
-rw-r--r--drivers/net/hyperv/netvsc_drv.c3
-rw-r--r--drivers/net/ieee802154/atusb.c31
-rw-r--r--drivers/net/ieee802154/fakelb.c14
-rw-r--r--drivers/net/loopback.c1
-rw-r--r--drivers/net/macvtap.c4
-rw-r--r--drivers/net/phy/bcm63xx.c21
-rw-r--r--drivers/net/tun.c10
-rw-r--r--drivers/net/usb/cdc_ether.c8
-rw-r--r--drivers/net/usb/qmi_wwan.c7
-rw-r--r--drivers/net/usb/r8152.c89
-rw-r--r--drivers/net/vrf.c8
-rw-r--r--drivers/net/wireless/ath/ath10k/Makefile1
-rw-r--r--drivers/net/wireless/ath/ath10k/ce.c6
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h33
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.c304
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.h15
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_rx.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c5
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.c230
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.h19
-rw-r--r--drivers/net/wireless/ath/ath10k/snoc.c92
-rw-r--r--drivers/net/wireless/ath/ath10k/snoc.h33
-rw-r--r--drivers/net/wireless/ath/ath10k/spectral.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c3
-rw-r--r--drivers/net/wireless/ath/ath5k/mac80211-ops.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_eeprom.h4
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.c44
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c27
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c7
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c7
-rw-r--r--drivers/net/wireless/ath/wil6210/pm.c13
-rw-r--r--drivers/net/wireless/ath/wil6210/sysfs.c111
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h7
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c61
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.h73
-rw-r--r--drivers/net/wireless/cnss_prealloc/cnss_prealloc.c1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/base.c8
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/core.c9
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/pci.c14
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/pci.h4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/ps.c36
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/usb.c19
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/usb.h3
-rw-r--r--drivers/net/xen-netfront.c3
-rw-r--r--drivers/nfc/nq-nci.c4
-rw-r--r--drivers/ntb/ntb_transport.c5
-rw-r--r--drivers/nvdimm/namespace_devs.c28
-rw-r--r--drivers/nvdimm/nd.h1
-rw-r--r--drivers/nvdimm/region_devs.c9
-rw-r--r--drivers/pci/host/pci-msm.c50
-rw-r--r--drivers/pci/hotplug/rpadlpar_core.c10
-rw-r--r--drivers/pci/pci.c4
-rw-r--r--drivers/pci/pcie/aspm.c19
-rw-r--r--drivers/pci/probe.c12
-rw-r--r--drivers/phy/phy-qcom-ufs.c8
-rw-r--r--drivers/pinctrl/intel/pinctrl-broxton.c2
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson.c2
-rw-r--r--drivers/pinctrl/qcom/pinctrl-lpi.c4
-rw-r--r--drivers/pinctrl/sh-pfc/pinctrl.c3
-rw-r--r--drivers/platform/goldfish/pdev_bus.c13
-rw-r--r--drivers/platform/msm/gsi/gsi.c16
-rw-r--r--drivers/platform/msm/gsi/gsi.h47
-rw-r--r--drivers/platform/msm/gsi/gsi_dbg.c52
-rw-r--r--drivers/platform/msm/gsi/gsi_reg.h5
-rw-r--r--drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c133
-rw-r--r--drivers/platform/msm/ipa/ipa_rm_dependency_graph.c4
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_dp.c4
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_flt.c8
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_intf.c26
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c3
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c53
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_intf.c26
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c3
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c50
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_utils.c19
-rw-r--r--drivers/platform/msm/mhi/mhi.h175
-rw-r--r--drivers/platform/msm/mhi/mhi_bhi.c496
-rw-r--r--drivers/platform/msm/mhi/mhi_bhi.h42
-rw-r--r--drivers/platform/msm/mhi/mhi_event.c105
-rw-r--r--drivers/platform/msm/mhi/mhi_iface.c478
-rw-r--r--drivers/platform/msm/mhi/mhi_init.c245
-rw-r--r--drivers/platform/msm/mhi/mhi_isr.c247
-rw-r--r--drivers/platform/msm/mhi/mhi_macros.h9
-rw-r--r--drivers/platform/msm/mhi/mhi_main.c1056
-rw-r--r--drivers/platform/msm/mhi/mhi_mmio_ops.c139
-rw-r--r--drivers/platform/msm/mhi/mhi_pm.c337
-rw-r--r--drivers/platform/msm/mhi/mhi_ring_ops.c33
-rw-r--r--drivers/platform/msm/mhi/mhi_ssr.c123
-rw-r--r--drivers/platform/msm/mhi/mhi_states.c266
-rw-r--r--drivers/platform/msm/mhi/mhi_sys.c88
-rw-r--r--drivers/platform/msm/mhi/mhi_sys.h9
-rw-r--r--drivers/platform/msm/mhi_uci/mhi_uci.c71
-rw-r--r--drivers/platform/msm/msm_11ad/msm_11ad.c105
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c9
-rw-r--r--drivers/platform/x86/intel_mid_powerbtn.c2
-rw-r--r--drivers/power/power_supply_sysfs.c1
-rw-r--r--drivers/power/qcom/apm.c14
-rw-r--r--drivers/power/supply/qcom/battery.c2
-rw-r--r--drivers/power/supply/qcom/fg-core.h3
-rw-r--r--drivers/power/supply/qcom/fg-memif.c94
-rw-r--r--drivers/power/supply/qcom/pmic-voter.c34
-rw-r--r--drivers/power/supply/qcom/pmic-voter.h47
-rw-r--r--drivers/power/supply/qcom/qpnp-fg-gen3.c140
-rw-r--r--drivers/power/supply/qcom/qpnp-qnovo.c352
-rw-r--r--drivers/power/supply/qcom/qpnp-smb2.c81
-rw-r--r--drivers/power/supply/qcom/smb-lib.c279
-rw-r--r--drivers/power/supply/qcom/smb-lib.h11
-rw-r--r--drivers/power/supply/qcom/smb-reg.h1
-rw-r--r--drivers/power/supply/qcom/smb138x-charger.c34
-rw-r--r--drivers/pwm/pwm-pca9685.c11
-rw-r--r--drivers/regulator/core.c8
-rw-r--r--drivers/regulator/cpr3-hmss-regulator.c8
-rw-r--r--drivers/regulator/cpr3-mmss-regulator.c8
-rw-r--r--drivers/regulator/cpr3-regulator.c16
-rw-r--r--drivers/regulator/cpr3-regulator.h8
-rw-r--r--drivers/regulator/spm-regulator.c20
-rw-r--r--drivers/regulator/stw481x-vmmc.c3
-rw-r--r--drivers/rtc/interface.c16
-rw-r--r--drivers/rtc/rtc-sun6i.c23
-rw-r--r--drivers/s390/block/dcssblk.c2
-rw-r--r--drivers/s390/char/vmlogrdr.c2
-rw-r--r--drivers/s390/cio/qdio_thinint.c8
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c17
-rw-r--r--drivers/s390/scsi/zfcp_dbf.h41
-rw-r--r--drivers/s390/scsi/zfcp_erp.c61
-rw-r--r--drivers/s390/scsi/zfcp_ext.h4
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c8
-rw-r--r--drivers/s390/scsi/zfcp_fsf.h3
-rw-r--r--drivers/s390/scsi/zfcp_reqlist.h30
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c61
-rw-r--r--drivers/scsi/aacraid/comminit.c8
-rw-r--r--drivers/scsi/aacraid/src.c21
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c9
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fusion.c9
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_scsih.c3
-rw-r--r--drivers/scsi/mvsas/mv_94xx.c2
-rw-r--r--drivers/scsi/mvsas/mv_sas.c4
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c16
-rw-r--r--drivers/scsi/scsi_dh.c22
-rw-r--r--drivers/scsi/scsi_lib.c26
-rw-r--r--drivers/scsi/scsi_sysfs.c4
-rw-r--r--drivers/scsi/sg.c7
-rw-r--r--drivers/scsi/storvsc_drv.c32
-rw-r--r--drivers/soc/qcom/glink_smem_native_xprt.c59
-rw-r--r--drivers/soc/qcom/icnss.c143
-rw-r--r--drivers/soc/qcom/ipc_router_mhi_xprt.c10
-rw-r--r--drivers/soc/qcom/mpm-of.c45
-rw-r--r--drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c3
-rw-r--r--drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c2
-rw-r--r--drivers/soc/qcom/peripheral-loader.c5
-rw-r--r--drivers/soc/qcom/peripheral-loader.h4
-rw-r--r--drivers/soc/qcom/pil-q6v5-mss.c8
-rw-r--r--drivers/soc/qcom/pil-q6v5.c3
-rw-r--r--drivers/soc/qcom/qmi_interface.c3
-rw-r--r--drivers/soc/qcom/qpnp-haptic.c972
-rw-r--r--drivers/soc/qcom/rpm-smd.c50
-rw-r--r--drivers/soc/qcom/secure_buffer.c3
-rw-r--r--drivers/soc/qcom/service-locator.c12
-rw-r--r--drivers/soc/qcom/service-notifier.c24
-rw-r--r--drivers/soc/qcom/spcom.c16
-rw-r--r--drivers/soc/qcom/subsys-pil-tz.c1
-rw-r--r--drivers/soundwire/swr-wcd-ctrl.c20
-rwxr-xr-xdrivers/soundwire/swr-wcd-ctrl.h7
-rwxr-xr-xdrivers/soundwire/swrm_registers.h3
-rw-r--r--drivers/spi/spi-orion.c83
-rw-r--r--drivers/ssb/pci.c1
-rw-r--r--drivers/staging/android/fiq_debugger/fiq_debugger.c86
-rw-r--r--drivers/staging/android/ion/ion.c2
-rw-r--r--drivers/staging/android/ion/ion_system_heap.c8
-rw-r--r--drivers/staging/comedi/drivers/dt282x.c65
-rw-r--r--drivers/staging/comedi/drivers/ni_mio_common.c8
-rw-r--r--drivers/staging/iio/adc/ad7606_core.c2
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_recv.c3
-rw-r--r--drivers/staging/rtl8712/rtl871x_recv.c7
-rw-r--r--drivers/target/iscsi/iscsi_target.c2
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.c1
-rw-r--r--drivers/target/target_core_device.c20
-rw-r--r--drivers/target/target_core_sbc.c8
-rw-r--r--drivers/target/target_core_tpg.c45
-rw-r--r--drivers/target/target_core_transport.c133
-rw-r--r--drivers/target/target_core_user.c2
-rw-r--r--drivers/target/target_core_xcopy.c2
-rw-r--r--drivers/thermal/msm_lmh_dcvs.c4
-rw-r--r--drivers/thermal/thermal_hwmon.c2
-rw-r--r--drivers/tty/n_hdlc.c143
-rw-r--r--drivers/tty/serial/8250/8250_pci.c13
-rw-r--r--drivers/tty/serial/atmel_serial.c11
-rw-r--r--drivers/tty/serial/msm_serial.c1
-rw-r--r--drivers/tty/serial/msm_serial_hs.c8
-rw-r--r--drivers/tty/serial/samsung.c6
-rw-r--r--drivers/tty/serial/sc16is7xx.c2
-rw-r--r--drivers/tty/sysrq.c7
-rw-r--r--drivers/tty/vt/keyboard.c2
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c1
-rw-r--r--drivers/usb/class/cdc-acm.c1
-rw-r--r--drivers/usb/core/config.c10
-rw-r--r--drivers/usb/core/hub.c134
-rw-r--r--drivers/usb/core/quirks.c4
-rw-r--r--drivers/usb/dwc3/core.h2
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c2
-rw-r--r--drivers/usb/dwc3/ep0.c51
-rw-r--r--drivers/usb/dwc3/gadget.c14
-rw-r--r--drivers/usb/dwc3/gadget.h14
-rw-r--r--drivers/usb/gadget/composite.c23
-rw-r--r--drivers/usb/gadget/function/Makefile2
-rw-r--r--drivers/usb/gadget/function/f_audio_source.c9
-rw-r--r--drivers/usb/gadget/function/f_fs.c13
-rw-r--r--drivers/usb/gadget/function/f_ncm.c58
-rw-r--r--drivers/usb/gadget/function/f_qc_rndis.c14
-rw-r--r--drivers/usb/gadget/function/f_qdss.c19
-rw-r--r--drivers/usb/gadget/function/f_qdss.h5
-rw-r--r--drivers/usb/gadget/function/f_uac2.c14
-rw-r--r--drivers/usb/gadget/function/u_data_ipa.c2
-rw-r--r--drivers/usb/gadget/function/u_qdss.c22
-rw-r--r--drivers/usb/gadget/function/uvc_video.c2
-rw-r--r--drivers/usb/gadget/legacy/inode.c17
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c8
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c10
-rw-r--r--drivers/usb/host/uhci-pci.c4
-rw-r--r--drivers/usb/host/xhci-dbg.c2
-rw-r--r--drivers/usb/host/xhci-hub.c37
-rw-r--r--drivers/usb/host/xhci-mem.c48
-rw-r--r--drivers/usb/host/xhci-pci.c9
-rw-r--r--drivers/usb/host/xhci-plat.c18
-rw-r--r--drivers/usb/host/xhci-ring.c230
-rw-r--r--drivers/usb/host/xhci.c29
-rw-r--r--drivers/usb/host/xhci.h8
-rw-r--r--drivers/usb/misc/iowarrior.c21
-rw-r--r--drivers/usb/musb/blackfin.c1
-rw-r--r--drivers/usb/musb/da8xx.c6
-rw-r--r--drivers/usb/musb/musb_core.h7
-rw-r--r--drivers/usb/musb/musb_dsps.c12
-rw-r--r--drivers/usb/musb/musb_host.c10
-rw-r--r--drivers/usb/musb/musbhsdma.h2
-rw-r--r--drivers/usb/pd/policy_engine.c9
-rw-r--r--drivers/usb/phy/class-dual-role.c21
-rw-r--r--drivers/usb/phy/phy-am335x-control.c2
-rw-r--r--drivers/usb/phy/phy-msm-ssusb-qmp.c178
-rw-r--r--drivers/usb/serial/ark3116.c13
-rw-r--r--drivers/usb/serial/ch341.c84
-rw-r--r--drivers/usb/serial/cp210x.c2
-rw-r--r--drivers/usb/serial/cyberjack.c10
-rw-r--r--drivers/usb/serial/digi_acceleport.c14
-rw-r--r--drivers/usb/serial/ftdi_sio.c31
-rw-r--r--drivers/usb/serial/garmin_gps.c1
-rw-r--r--drivers/usb/serial/io_edgeport.c5
-rw-r--r--drivers/usb/serial/io_ti.c30
-rw-r--r--drivers/usb/serial/iuu_phoenix.c11
-rw-r--r--drivers/usb/serial/keyspan_pda.c14
-rw-r--r--drivers/usb/serial/kl5kusb105.c44
-rw-r--r--drivers/usb/serial/kobil_sct.c12
-rw-r--r--drivers/usb/serial/mos7720.c51
-rw-r--r--drivers/usb/serial/mos7840.c14
-rw-r--r--drivers/usb/serial/omninet.c19
-rw-r--r--drivers/usb/serial/opticon.c2
-rw-r--r--drivers/usb/serial/option.c8
-rw-r--r--drivers/usb/serial/oti6858.c16
-rw-r--r--drivers/usb/serial/pl2303.c9
-rw-r--r--drivers/usb/serial/pl2303.h1
-rw-r--r--drivers/usb/serial/qcserial.c1
-rw-r--r--drivers/usb/serial/quatech2.c4
-rw-r--r--drivers/usb/serial/safe_serial.c5
-rw-r--r--drivers/usb/serial/spcp8x5.c22
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c7
-rw-r--r--drivers/usb/storage/unusual_devs.h7
-rw-r--r--drivers/video/fbdev/core/fbcmap.c27
-rw-r--r--drivers/video/fbdev/msm/mdss.h9
-rw-r--r--drivers/video/fbdev/msm/mdss_compat_utils.c1
-rw-r--r--drivers/video/fbdev/msm/mdss_compat_utils.h7
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.c46
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c34
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c49
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.h5
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_host.c61
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c7
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c22
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.h5
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.c27
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.h2
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h17
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c49
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_hwio.h13
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c115
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_video.c205
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_layer.c142
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c106
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.c28
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_util.c27
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.h23
-rw-r--r--drivers/video/fbdev/msm/mdss_rotator.c3
-rw-r--r--drivers/video/fbdev/msm/msm_mdss_io_8974.c24
-rw-r--r--drivers/vme/bridges/vme_ca91cx42.c2
-rw-r--r--drivers/w1/masters/ds2490.c142
-rw-r--r--drivers/w1/w1.c1
-rw-r--r--drivers/xen/gntdev.c2
561 files changed, 12691 insertions, 10528 deletions
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 8f8da9f92090..eac4f3b02df9 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -847,6 +847,8 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs)
if (ghes_read_estatus(ghes, 1)) {
ghes_clear_estatus(ghes);
continue;
+ } else {
+ ret = NMI_HANDLED;
}
sev = ghes_severity(ghes->estatus->error_severity);
@@ -858,12 +860,11 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs)
__process_error(ghes);
ghes_clear_estatus(ghes);
-
- ret = NMI_HANDLED;
}
#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
- irq_work_queue(&ghes_proc_irq_work);
+ if (ret == NMI_HANDLED)
+ irq_work_queue(&ghes_proc_irq_work);
#endif
atomic_dec(&ghes_in_nmi);
return ret;
diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c
index c097f477c74c..14c2a07c9f3f 100644
--- a/drivers/acpi/nfit.c
+++ b/drivers/acpi/nfit.c
@@ -965,7 +965,7 @@ static size_t sizeof_nfit_set_info(int num_mappings)
+ num_mappings * sizeof(struct nfit_set_info_map);
}
-static int cmp_map(const void *m0, const void *m1)
+static int cmp_map_compat(const void *m0, const void *m1)
{
const struct nfit_set_info_map *map0 = m0;
const struct nfit_set_info_map *map1 = m1;
@@ -974,6 +974,14 @@ static int cmp_map(const void *m0, const void *m1)
sizeof(u64));
}
+static int cmp_map(const void *m0, const void *m1)
+{
+ const struct nfit_set_info_map *map0 = m0;
+ const struct nfit_set_info_map *map1 = m1;
+
+ return map0->region_offset - map1->region_offset;
+}
+
/* Retrieve the nth entry referencing this spa */
static struct acpi_nfit_memory_map *memdev_from_spa(
struct acpi_nfit_desc *acpi_desc, u16 range_index, int n)
@@ -1029,6 +1037,12 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc,
sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map),
cmp_map, NULL);
nd_set->cookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0);
+
+ /* support namespaces created with the wrong sort order */
+ sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map),
+ cmp_map_compat, NULL);
+ nd_set->altcookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0);
+
ndr_desc->nd_set = nd_set;
devm_kfree(dev, info);
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index 80e55cb0827b..b48ecbfc4498 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -271,6 +271,26 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"),
},
},
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1123661 */
+ .callback = video_detect_force_native,
+ .ident = "Dell XPS 17 L702X",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L702X"),
+ },
+ },
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1204476 */
+ /* https://bugs.launchpad.net/ubuntu/+source/linux-lts-trusty/+bug/1416940 */
+ .callback = video_detect_force_native,
+ .ident = "HP Pavilion dv6",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv6 Notebook PC"),
+ },
+ },
+
{ },
};
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 0f6591a72cf8..d0334e50f2f9 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -2221,9 +2221,10 @@ static void binder_transaction(struct binder_proc *proc,
return_error = BR_FAILED_REPLY;
goto err_bad_offset;
}
- if (copy_from_user(sg_bufp,
- (const void __user *)(uintptr_t)
- bp->buffer, bp->length)) {
+ if (copy_from_user_preempt_disabled(
+ sg_bufp,
+ (const void __user *)(uintptr_t)
+ bp->buffer, bp->length)) {
binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
@@ -2511,7 +2512,8 @@ static int binder_thread_write(struct binder_proc *proc,
case BC_REPLY_SG: {
struct binder_transaction_data_sg tr;
- if (copy_from_user(&tr, ptr, sizeof(tr)))
+ if (copy_from_user_preempt_disabled(&tr, ptr,
+ sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
binder_transaction(proc, thread, &tr.transaction_data,
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index bd370c98f77d..b0b77b61c40c 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4139,10 +4139,10 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
{ "ST380013AS", "3.20", ATA_HORKAGE_MAX_SEC_1024 },
/*
- * Device times out with higher max sects.
+ * These devices time out with higher max sects.
* https://bugzilla.kernel.org/show_bug.cgi?id=121671
*/
- { "LITEON CX1-JB256-HP", NULL, ATA_HORKAGE_MAX_SEC_1024 },
+ { "LITEON CX1-JB*-HP", NULL, ATA_HORKAGE_MAX_SEC_1024 },
/* Devices we expect to fail diagnostics */
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index bd74ee555278..729f26322095 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -4121,6 +4121,9 @@ static int mv_platform_probe(struct platform_device *pdev)
host->iomap = NULL;
hpriv->base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
+ if (!hpriv->base)
+ return -ENOMEM;
+
hpriv->base -= SATAHC0_REG_BASE;
hpriv->clk = clk_get(&pdev->dev, NULL);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index bbe8e2efc677..3fa9096b27c2 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1116,7 +1116,13 @@ int device_add(struct device *dev)
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
- device_pm_add(dev);
+ if ((dev->pm_domain) || (dev->type && dev->type->pm)
+ || (dev->class && (dev->class->pm || dev->class->resume))
+ || (dev->bus && (dev->bus->pm || dev->bus->resume)) ||
+ (dev->driver && dev->driver->pm)) {
+ device_pm_add(dev);
+ }
+
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &dev_attr_dev);
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 1c6e4da01e69..f8112c356bc5 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -1108,13 +1108,14 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
timeout = MAX_JIFFY_OFFSET;
}
- retval = wait_for_completion_interruptible_timeout(&buf->completion,
+ timeout = wait_for_completion_interruptible_timeout(&buf->completion,
timeout);
- if (retval == -ERESTARTSYS || !retval) {
+ if (timeout == -ERESTARTSYS || !timeout) {
+ retval = timeout;
mutex_lock(&fw_lock);
fw_load_abort(fw_priv);
mutex_unlock(&fw_lock);
- } else if (retval > 0) {
+ } else if (timeout > 0) {
retval = 0;
}
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 25425d3f2575..48c0a1d0dd3a 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -388,30 +388,29 @@ static ssize_t show_valid_zones(struct device *dev,
{
struct memory_block *mem = to_memory_block(dev);
unsigned long start_pfn, end_pfn;
+ unsigned long valid_start, valid_end;
unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
- struct page *first_page;
struct zone *zone;
start_pfn = section_nr_to_pfn(mem->start_section_nr);
end_pfn = start_pfn + nr_pages;
- first_page = pfn_to_page(start_pfn);
/* The block contains more than one zone can not be offlined. */
- if (!test_pages_in_a_zone(start_pfn, end_pfn))
+ if (!test_pages_in_a_zone(start_pfn, end_pfn, &valid_start, &valid_end))
return sprintf(buf, "none\n");
- zone = page_zone(first_page);
+ zone = page_zone(pfn_to_page(valid_start));
if (zone_idx(zone) == ZONE_MOVABLE - 1) {
/*The mem block is the last memoryblock of this zone.*/
- if (end_pfn == zone_end_pfn(zone))
+ if (valid_end == zone_end_pfn(zone))
return sprintf(buf, "%s %s\n",
zone->name, (zone + 1)->name);
}
if (zone_idx(zone) == ZONE_MOVABLE) {
/*The mem block is the first memoryblock of ZONE_MOVABLE.*/
- if (start_pfn == zone->zone_start_pfn)
+ if (valid_start == zone->zone_start_pfn)
return sprintf(buf, "%s %s\n",
zone->name, (zone - 1)->name);
}
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 6c5bc3fadfcf..a88590bb0b10 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -162,6 +162,12 @@ void device_pm_move_before(struct device *deva, struct device *devb)
pr_debug("PM: Moving %s:%s before %s:%s\n",
deva->bus ? deva->bus->name : "No Bus", dev_name(deva),
devb->bus ? devb->bus->name : "No Bus", dev_name(devb));
+ if (!((devb->pm_domain) || (devb->type && devb->type->pm)
+ || (devb->class && (devb->class->pm || devb->class->resume))
+ || (devb->bus && (devb->bus->pm || devb->bus->resume)) ||
+ (devb->driver && devb->driver->pm))) {
+ device_pm_add(devb);
+ }
/* Delete deva from dpm_list and reinsert before devb. */
list_move_tail(&deva->power.entry, &devb->power.entry);
}
@@ -176,6 +182,12 @@ void device_pm_move_after(struct device *deva, struct device *devb)
pr_debug("PM: Moving %s:%s after %s:%s\n",
deva->bus ? deva->bus->name : "No Bus", dev_name(deva),
devb->bus ? devb->bus->name : "No Bus", dev_name(devb));
+ if (!((devb->pm_domain) || (devb->type && devb->type->pm)
+ || (devb->class && (devb->class->pm || devb->class->resume))
+ || (devb->bus && (devb->bus->pm || devb->bus->resume)) ||
+ (devb->driver && devb->driver->pm))) {
+ device_pm_add(devb);
+ }
/* Delete deva from dpm_list and reinsert after devb. */
list_move(&deva->power.entry, &devb->power.entry);
}
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 297beae64314..297aa5cf393e 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -20,14 +20,22 @@ static inline void pm_runtime_early_init(struct device *dev)
extern void pm_runtime_init(struct device *dev);
extern void pm_runtime_remove(struct device *dev);
+#define WAKE_IRQ_DEDICATED_ALLOCATED BIT(0)
+#define WAKE_IRQ_DEDICATED_MANAGED BIT(1)
+#define WAKE_IRQ_DEDICATED_MASK (WAKE_IRQ_DEDICATED_ALLOCATED | \
+ WAKE_IRQ_DEDICATED_MANAGED)
+
struct wake_irq {
struct device *dev;
+ unsigned int status;
int irq;
- bool dedicated_irq:1;
};
extern void dev_pm_arm_wake_irq(struct wake_irq *wirq);
extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq);
+extern void dev_pm_enable_wake_irq_check(struct device *dev,
+ bool can_change_status);
+extern void dev_pm_disable_wake_irq_check(struct device *dev);
#ifdef CONFIG_PM_SLEEP
@@ -102,6 +110,15 @@ static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
{
}
+static inline void dev_pm_enable_wake_irq_check(struct device *dev,
+ bool can_change_status)
+{
+}
+
+static inline void dev_pm_disable_wake_irq_check(struct device *dev)
+{
+}
+
#endif
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 9796a1a15ef6..3252429f96af 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -515,7 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
callback = RPM_GET_CALLBACK(dev, runtime_suspend);
- dev_pm_enable_wake_irq(dev);
+ dev_pm_enable_wake_irq_check(dev, true);
retval = rpm_callback(callback, dev);
if (retval)
goto fail;
@@ -554,7 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
return retval;
fail:
- dev_pm_disable_wake_irq(dev);
+ dev_pm_disable_wake_irq_check(dev);
__update_runtime_status(dev, RPM_ACTIVE);
dev->power.deferred_resume = false;
wake_up_all(&dev->power.wait_queue);
@@ -737,12 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags)
callback = RPM_GET_CALLBACK(dev, runtime_resume);
- dev_pm_disable_wake_irq(dev);
+ dev_pm_disable_wake_irq_check(dev);
retval = rpm_callback(callback, dev);
if (retval) {
__update_runtime_status(dev, RPM_SUSPENDED);
pm_runtime_cancel_pending(dev);
- dev_pm_enable_wake_irq(dev);
+ dev_pm_enable_wake_irq_check(dev, false);
} else {
no_callback:
__update_runtime_status(dev, RPM_ACTIVE);
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
index 0d77cd6fd8d1..404d94c6c8bc 100644
--- a/drivers/base/power/wakeirq.c
+++ b/drivers/base/power/wakeirq.c
@@ -110,8 +110,10 @@ void dev_pm_clear_wake_irq(struct device *dev)
dev->power.wakeirq = NULL;
spin_unlock_irqrestore(&dev->power.lock, flags);
- if (wirq->dedicated_irq)
+ if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED) {
free_irq(wirq->irq, wirq);
+ wirq->status &= ~WAKE_IRQ_DEDICATED_MASK;
+ }
kfree(wirq);
}
EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
@@ -179,7 +181,6 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
wirq->dev = dev;
wirq->irq = irq;
- wirq->dedicated_irq = true;
irq_set_status_flags(irq, IRQ_NOAUTOEN);
/*
@@ -195,6 +196,8 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
if (err)
goto err_free_irq;
+ wirq->status = WAKE_IRQ_DEDICATED_ALLOCATED;
+
return err;
err_free_irq:
@@ -210,9 +213,9 @@ EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq);
* dev_pm_enable_wake_irq - Enable device wake-up interrupt
* @dev: Device
*
- * Called from the bus code or the device driver for
- * runtime_suspend() to enable the wake-up interrupt while
- * the device is running.
+ * Optionally called from the bus code or the device driver for
+ * runtime_resume() to override the PM runtime core managed wake-up
+ * interrupt handling to enable the wake-up interrupt.
*
* Note that for runtime_suspend()) the wake-up interrupts
* should be unconditionally enabled unlike for suspend()
@@ -222,7 +225,7 @@ void dev_pm_enable_wake_irq(struct device *dev)
{
struct wake_irq *wirq = dev->power.wakeirq;
- if (wirq && wirq->dedicated_irq)
+ if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED))
enable_irq(wirq->irq);
}
EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
@@ -231,20 +234,73 @@ EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
* dev_pm_disable_wake_irq - Disable device wake-up interrupt
* @dev: Device
*
- * Called from the bus code or the device driver for
- * runtime_resume() to disable the wake-up interrupt while
- * the device is running.
+ * Optionally called from the bus code or the device driver for
+ * runtime_suspend() to override the PM runtime core managed wake-up
+ * interrupt handling to disable the wake-up interrupt.
*/
void dev_pm_disable_wake_irq(struct device *dev)
{
struct wake_irq *wirq = dev->power.wakeirq;
- if (wirq && wirq->dedicated_irq)
+ if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED))
disable_irq_nosync(wirq->irq);
}
EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);
/**
+ * dev_pm_enable_wake_irq_check - Checks and enables wake-up interrupt
+ * @dev: Device
+ * @can_change_status: Can change wake-up interrupt status
+ *
+ * Enables wakeirq conditionally. We need to enable wake-up interrupt
+ * lazily on the first rpm_suspend(). This is needed as the consumer device
+ * starts in RPM_SUSPENDED state, and the the first pm_runtime_get() would
+ * otherwise try to disable already disabled wakeirq. The wake-up interrupt
+ * starts disabled with IRQ_NOAUTOEN set.
+ *
+ * Should be only called from rpm_suspend() and rpm_resume() path.
+ * Caller must hold &dev->power.lock to change wirq->status
+ */
+void dev_pm_enable_wake_irq_check(struct device *dev,
+ bool can_change_status)
+{
+ struct wake_irq *wirq = dev->power.wakeirq;
+
+ if (!wirq || !((wirq->status & WAKE_IRQ_DEDICATED_MASK)))
+ return;
+
+ if (likely(wirq->status & WAKE_IRQ_DEDICATED_MANAGED)) {
+ goto enable;
+ } else if (can_change_status) {
+ wirq->status |= WAKE_IRQ_DEDICATED_MANAGED;
+ goto enable;
+ }
+
+ return;
+
+enable:
+ enable_irq(wirq->irq);
+}
+
+/**
+ * dev_pm_disable_wake_irq_check - Checks and disables wake-up interrupt
+ * @dev: Device
+ *
+ * Disables wake-up interrupt conditionally based on status.
+ * Should be only called from rpm_suspend() and rpm_resume() path.
+ */
+void dev_pm_disable_wake_irq_check(struct device *dev)
+{
+ struct wake_irq *wirq = dev->power.wakeirq;
+
+ if (!wirq || !((wirq->status & WAKE_IRQ_DEDICATED_MASK)))
+ return;
+
+ if (wirq->status & WAKE_IRQ_DEDICATED_MANAGED)
+ disable_irq_nosync(wirq->irq);
+}
+
+/**
* dev_pm_arm_wake_irq - Arm device wake-up
* @wirq: Device wake-up interrupt
*
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index 59d8d0d14824..327f9e374b44 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -640,8 +640,11 @@ static int bcma_device_probe(struct device *dev)
drv);
int err = 0;
+ get_device(dev);
if (adrv->probe)
err = adrv->probe(core);
+ if (err)
+ put_device(dev);
return err;
}
@@ -654,6 +657,7 @@ static int bcma_device_remove(struct device *dev)
if (adrv->remove)
adrv->remove(core);
+ put_device(dev);
return 0;
}
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 80cf8add46ff..cec36d5c24f5 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1108,9 +1108,12 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
if ((unsigned int) info->lo_encrypt_key_size > LO_KEY_SIZE)
return -EINVAL;
+ /* I/O need to be drained during transfer transition */
+ blk_mq_freeze_queue(lo->lo_queue);
+
err = loop_release_xfer(lo);
if (err)
- return err;
+ goto exit;
if (info->lo_encrypt_type) {
unsigned int type = info->lo_encrypt_type;
@@ -1125,12 +1128,14 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
err = loop_init_xfer(lo, xfer, info);
if (err)
- return err;
+ goto exit;
if (lo->lo_offset != info->lo_offset ||
lo->lo_sizelimit != info->lo_sizelimit)
- if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit))
- return -EFBIG;
+ if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit)) {
+ err = -EFBIG;
+ goto exit;
+ }
loop_config_discard(lo);
@@ -1148,13 +1153,6 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
(info->lo_flags & LO_FLAGS_AUTOCLEAR))
lo->lo_flags ^= LO_FLAGS_AUTOCLEAR;
- if ((info->lo_flags & LO_FLAGS_PARTSCAN) &&
- !(lo->lo_flags & LO_FLAGS_PARTSCAN)) {
- lo->lo_flags |= LO_FLAGS_PARTSCAN;
- lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN;
- loop_reread_partitions(lo, lo->lo_device);
- }
-
lo->lo_encrypt_key_size = info->lo_encrypt_key_size;
lo->lo_init[0] = info->lo_init[0];
lo->lo_init[1] = info->lo_init[1];
@@ -1167,7 +1165,17 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
/* update dio if lo_offset or transfer is changed */
__loop_update_dio(lo, lo->use_dio);
- return 0;
+ exit:
+ blk_mq_unfreeze_queue(lo->lo_queue);
+
+ if (!err && (info->lo_flags & LO_FLAGS_PARTSCAN) &&
+ !(lo->lo_flags & LO_FLAGS_PARTSCAN)) {
+ lo->lo_flags |= LO_FLAGS_PARTSCAN;
+ lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN;
+ loop_reread_partitions(lo, lo->lo_device);
+ }
+
+ return err;
}
static int
@@ -1657,7 +1665,7 @@ static int loop_queue_rq(struct blk_mq_hw_ctx *hctx,
blk_mq_start_request(bd->rq);
if (lo->lo_state != Lo_bound)
- return -EIO;
+ return BLK_MQ_RQ_QUEUE_ERROR;
if (lo->use_dio && !(cmd->rq->cmd_flags & (REQ_FLUSH |
REQ_DISCARD)))
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index eada042b6eab..76ac3179f25c 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -1391,8 +1391,14 @@ static ssize_t hot_remove_store(struct class *class,
return ret ? ret : count;
}
+/*
+ * NOTE: hot_add attribute is not the usual read-only sysfs attribute. In a
+ * sense that reading from this file does alter the state of your system -- it
+ * creates a new un-initialized zram device and returns back this device's
+ * device_id (or an error code if it fails to create a new device).
+ */
static struct class_attribute zram_control_class_attrs[] = {
- __ATTR_RO(hot_add),
+ __ATTR(hot_add, 0400, hot_add_show, NULL),
__ATTR_WO(hot_remove),
__ATTR_NULL,
};
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 0beaa52df66b..5df8e1234505 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -94,6 +94,7 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x04CA, 0x300f) },
{ USB_DEVICE(0x04CA, 0x3010) },
{ USB_DEVICE(0x04CA, 0x3014) },
+ { USB_DEVICE(0x04CA, 0x3018) },
{ USB_DEVICE(0x0930, 0x0219) },
{ USB_DEVICE(0x0930, 0x021c) },
{ USB_DEVICE(0x0930, 0x0220) },
@@ -160,6 +161,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x3018), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
diff --git a/drivers/bluetooth/btfm_slim_codec.c b/drivers/bluetooth/btfm_slim_codec.c
index 061177173ab8..1ed366fd3d71 100644
--- a/drivers/bluetooth/btfm_slim_codec.c
+++ b/drivers/bluetooth/btfm_slim_codec.c
@@ -92,6 +92,9 @@ static void btfm_slim_dai_shutdown(struct snd_pcm_substream *substream,
return;
}
+ if (dai->id == BTFM_FM_SLIM_TX)
+ goto out;
+
/* Search for dai->id matched port handler */
for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) &&
(ch->id != BTFM_SLIM_NUM_CODEC_DAIS) &&
@@ -105,6 +108,7 @@ static void btfm_slim_dai_shutdown(struct snd_pcm_substream *substream,
}
btfm_slim_disable_ch(btfmslim, ch, rxport, grp, nchan);
+out:
btfm_slim_hw_deinit(btfmslim);
}
@@ -167,6 +171,61 @@ int btfm_slim_dai_prepare(struct snd_pcm_substream *substream,
return ret;
}
+static int btfm_slim_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret = -EINVAL, i;
+ struct btfmslim *btfmslim = dai->dev->platform_data;
+ struct btfmslim_ch *ch;
+ uint8_t rxport, grp = false, nchan = 1;
+
+ BTFMSLIM_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name,
+ dai->id, dai->rate);
+
+ switch (dai->id) {
+ case BTFM_FM_SLIM_TX:
+ grp = true; nchan = 2;
+ ch = btfmslim->tx_chs;
+ rxport = 0;
+ break;
+ case BTFM_BT_SCO_SLIM_TX:
+ ch = btfmslim->tx_chs;
+ rxport = 0;
+ break;
+ case BTFM_BT_SCO_A2DP_SLIM_RX:
+ case BTFM_BT_SPLIT_A2DP_SLIM_RX:
+ ch = btfmslim->rx_chs;
+ rxport = 1;
+ break;
+ case BTFM_SLIM_NUM_CODEC_DAIS:
+ default:
+ BTFMSLIM_ERR("dai->id is invalid:%d", dai->id);
+ goto out;
+ }
+
+ if (dai->id != BTFM_FM_SLIM_TX) {
+ ret = 0;
+ goto out;
+ }
+
+ /* Search for dai->id matched port handler */
+ for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) &&
+ (ch->id != BTFM_SLIM_NUM_CODEC_DAIS) &&
+ (ch->id != dai->id); ch++, i++)
+ ;
+
+ if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) ||
+ (ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) {
+ BTFMSLIM_ERR("ch is invalid!!");
+ goto out;
+ }
+
+ btfm_slim_disable_ch(btfmslim, ch, rxport, grp, nchan);
+
+out:
+ return ret;
+}
+
/* This function will be called once during boot up */
static int btfm_slim_dai_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
@@ -306,6 +365,7 @@ static struct snd_soc_dai_ops btfmslim_dai_ops = {
.shutdown = btfm_slim_dai_shutdown,
.hw_params = btfm_slim_dai_hw_params,
.prepare = btfm_slim_dai_prepare,
+ .hw_free = btfm_slim_dai_hw_free,
.set_channel_map = btfm_slim_dai_set_channel_map,
.get_channel_map = btfm_slim_dai_get_channel_map,
};
diff --git a/drivers/bluetooth/btfm_slim_wcn3990.c b/drivers/bluetooth/btfm_slim_wcn3990.c
index 72e28da4bd3b..d61454ac6e84 100644
--- a/drivers/bluetooth/btfm_slim_wcn3990.c
+++ b/drivers/bluetooth/btfm_slim_wcn3990.c
@@ -39,6 +39,7 @@ int btfm_slim_chrk_hw_init(struct btfmslim *btfmslim)
{
int ret = 0;
uint8_t reg_val;
+ uint16_t reg;
BTFMSLIM_DBG("");
@@ -46,20 +47,20 @@ int btfm_slim_chrk_hw_init(struct btfmslim *btfmslim)
return -EINVAL;
/* Get SB_SLAVE_HW_REV_MSB value*/
- ret = btfm_slim_read(btfmslim, CHRK_SB_SLAVE_HW_REV_MSB, 1,
- &reg_val, IFD);
+ reg = CHRK_SB_SLAVE_HW_REV_MSB;
+ ret = btfm_slim_read(btfmslim, reg, 1, &reg_val, IFD);
if (ret) {
- BTFMSLIM_ERR("failed to read (%d)", ret);
+ BTFMSLIM_ERR("failed to read (%d) reg 0x%x", ret, reg);
goto error;
}
BTFMSLIM_DBG("Major Rev: 0x%x, Minor Rev: 0x%x",
(reg_val & 0xF0) >> 4, (reg_val & 0x0F));
/* Get SB_SLAVE_HW_REV_LSB value*/
- ret = btfm_slim_read(btfmslim, CHRK_SB_SLAVE_HW_REV_LSB, 1,
- &reg_val, IFD);
+ reg = CHRK_SB_SLAVE_HW_REV_LSB;
+ ret = btfm_slim_read(btfmslim, reg, 1, &reg_val, IFD);
if (ret) {
- BTFMSLIM_ERR("failed to read (%d)", ret);
+ BTFMSLIM_ERR("failed to read (%d) reg 0x%x", ret, reg);
goto error;
}
BTFMSLIM_DBG("Step Rev: 0x%x", reg_val);
@@ -80,41 +81,41 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num,
if (rxport) {
/* Port enable */
reg = CHRK_SB_PGD_PORT_RX_CFGN(port_num - 0x10);
- } else { /* txport */
- /* Multiple Channel Setting - only FM Tx will be multiple
- * channel
- */
- if (enable && (port_num == CHRK_SB_PGD_PORT_TX1_FM ||
- port_num == CHRK_SB_PGD_PORT_TX2_FM)) {
-
- reg_val = (0x1 << CHRK_SB_PGD_PORT_TX1_FM) |
- (0x1 << CHRK_SB_PGD_PORT_TX2_FM);
- reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num);
- ret = btfm_slim_write(btfmslim, reg, 1, &reg_val, IFD);
- if (ret) {
- BTFMSLIM_ERR("failed to write (%d)", ret);
- goto error;
- }
- }
+ goto enable_disable_rxport;
+ }
+ /* txport */
+ if (!enable)
+ goto enable_disable_txport;
- /* Enable Tx port hw auto recovery for underrun or
- * overrun error
- */
- reg_val = (enable) ? (CHRK_ENABLE_OVERRUN_AUTO_RECOVERY |
- CHRK_ENABLE_UNDERRUN_AUTO_RECOVERY) : 0x0;
+ /* Multiple Channel Setting - only for FM Tx */
+ if (port_num == CHRK_SB_PGD_PORT_TX1_FM ||
+ port_num == CHRK_SB_PGD_PORT_TX2_FM) {
- ret = btfm_slim_write(btfmslim,
- CHRK_SB_PGD_PORT_TX_OR_UR_CFGN(port_num), 1,
- &reg_val, IFD);
+ reg_val = (0x1 << CHRK_SB_PGD_PORT_TX1_FM) |
+ (0x1 << CHRK_SB_PGD_PORT_TX2_FM);
+ reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num);
+ ret = btfm_slim_write(btfmslim, reg, 1, &reg_val, IFD);
if (ret) {
- BTFMSLIM_ERR("failed to write (%d)", ret);
+ BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg);
goto error;
}
+ }
- /* Port enable */
- reg = CHRK_SB_PGD_PORT_TX_CFGN(port_num);
+ /* Enable Tx port hw auto recovery for underrun or overrun error */
+ reg_val = (CHRK_ENABLE_OVERRUN_AUTO_RECOVERY |
+ CHRK_ENABLE_UNDERRUN_AUTO_RECOVERY);
+ reg = CHRK_SB_PGD_PORT_TX_OR_UR_CFGN(port_num);
+ ret = btfm_slim_write(btfmslim, reg, 1, &reg_val, IFD);
+ if (ret) {
+ BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg);
+ goto error;
}
+enable_disable_txport:
+ /* Port enable */
+ reg = CHRK_SB_PGD_PORT_TX_CFGN(port_num);
+
+enable_disable_rxport:
if (enable)
/* Set water mark to 1 and enable the port */
reg_val = CHRK_SB_PGD_PORT_ENABLE | CHRK_SB_PGD_PORT_WM_LB;
@@ -123,7 +124,7 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num,
ret = btfm_slim_write(btfmslim, reg, 1, &reg_val, IFD);
if (ret)
- BTFMSLIM_ERR("failed to write (%d)", ret);
+ BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg);
error:
return ret;
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index c306b483de60..cd6b141b9825 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -208,6 +208,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x3018), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
diff --git a/drivers/bus/vexpress-config.c b/drivers/bus/vexpress-config.c
index 6575c0fe6a4e..27ea64fa4f9b 100644
--- a/drivers/bus/vexpress-config.c
+++ b/drivers/bus/vexpress-config.c
@@ -171,6 +171,7 @@ static int vexpress_config_populate(struct device_node *node)
{
struct device_node *bridge;
struct device *parent;
+ int ret;
bridge = of_parse_phandle(node, "arm,vexpress,config-bridge", 0);
if (!bridge)
@@ -181,7 +182,11 @@ static int vexpress_config_populate(struct device_node *node)
if (WARN_ON(!parent))
return -ENODEV;
- return of_platform_populate(node, NULL, NULL, parent);
+ ret = of_platform_populate(node, NULL, NULL, parent);
+
+ put_device(parent);
+
+ return ret;
}
static int __init vexpress_config_init(void)
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 7820f5625266..e0106a7e31fa 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -1435,6 +1435,12 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
int err = 0;
struct timespec invoket;
+ VERIFY(err, fl->sctx);
+ if (err)
+ goto bail;
+ VERIFY(err, fl->cid >= 0 && fl->cid < NUM_CHANNELS);
+ if (err)
+ goto bail;
if (fl->profile)
getnstimeofday(&invoket);
if (!kernel) {
diff --git a/drivers/char/diag/diagfwd_mhi.c b/drivers/char/diag/diagfwd_mhi.c
index df26e2522baf..8b0e1f32bdc5 100644
--- a/drivers/char/diag/diagfwd_mhi.c
+++ b/drivers/char/diag/diagfwd_mhi.c
@@ -665,8 +665,7 @@ static int diag_mhi_register_ch(int id, struct diag_mhi_ch_t *ch)
atomic_set(&(ch->opened), 0);
ctxt = SET_CH_CTXT(id, ch->type);
ch->client_info.mhi_client_cb = mhi_notifier;
- return mhi_register_channel(&ch->hdl, ch->chan, 0, &ch->client_info,
- (void *)(uintptr_t)ctxt);
+ return mhi_register_channel(&ch->hdl, NULL);
}
int diag_mhi_init()
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index 3f9f7a999fdf..b04008c8fec3 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -855,7 +855,7 @@ int diagfwd_channel_open(struct diagfwd_info *fwd_info)
__func__, fwd_info->peripheral, fwd_info->type);
return 0;
}
-
+ mutex_lock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]);
fwd_info->ch_open = 1;
diagfwd_buffers_init(fwd_info);
diagfwd_write_buffers_init(fwd_info);
@@ -873,7 +873,7 @@ int diagfwd_channel_open(struct diagfwd_info *fwd_info)
if (fwd_info->p_ops && fwd_info->p_ops->open)
fwd_info->p_ops->open(fwd_info->ctxt);
}
-
+ mutex_unlock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]);
return 0;
}
@@ -883,6 +883,7 @@ int diagfwd_channel_close(struct diagfwd_info *fwd_info)
if (!fwd_info)
return -EIO;
+ mutex_lock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]);
fwd_info->ch_open = 0;
if (fwd_info && fwd_info->c_ops && fwd_info->c_ops->close)
fwd_info->c_ops->close(fwd_info);
@@ -898,7 +899,7 @@ int diagfwd_channel_close(struct diagfwd_info *fwd_info)
}
DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "p: %d t: %d considered closed\n",
fwd_info->peripheral, fwd_info->type);
-
+ mutex_unlock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]);
return 0;
}
diff --git a/drivers/char/hw_random/msm-rng.c b/drivers/char/hw_random/msm-rng.c
index 96fb986402eb..296b23960815 100644
--- a/drivers/char/hw_random/msm-rng.c
+++ b/drivers/char/hw_random/msm-rng.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013,2015,2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -156,6 +156,7 @@ static int msm_rng_probe(struct platform_device *pdev)
rng->hwrng.init = msm_rng_init,
rng->hwrng.cleanup = msm_rng_cleanup,
rng->hwrng.read = msm_rng_read,
+ rng->hwrng.quality = 700;
ret = devm_hwrng_register(&pdev->dev, &rng->hwrng);
if (ret) {
diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c
index 3111f2778079..849f2e29c243 100644
--- a/drivers/char/tpm/xen-tpmfront.c
+++ b/drivers/char/tpm/xen-tpmfront.c
@@ -305,7 +305,6 @@ static int tpmfront_probe(struct xenbus_device *dev,
rv = setup_ring(dev, priv);
if (rv) {
chip = dev_get_drvdata(&dev->dev);
- tpm_chip_unregister(chip);
ring_free(priv);
return rv;
}
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index 6029313aa995..35ab89fe9d7b 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -1082,7 +1082,9 @@ static void bcm2835_pll_divider_off(struct clk_hw *hw)
cprman_write(cprman, data->cm_reg,
(cprman_read(cprman, data->cm_reg) &
~data->load_mask) | data->hold_mask);
- cprman_write(cprman, data->a2w_reg, A2W_PLL_CHANNEL_DISABLE);
+ cprman_write(cprman, data->a2w_reg,
+ cprman_read(cprman, data->a2w_reg) |
+ A2W_PLL_CHANNEL_DISABLE);
spin_unlock(&cprman->regs_lock);
}
diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c
index 43f9d15255f4..763aed2de893 100644
--- a/drivers/clk/clk-wm831x.c
+++ b/drivers/clk/clk-wm831x.c
@@ -247,7 +247,7 @@ static int wm831x_clkout_is_prepared(struct clk_hw *hw)
if (ret < 0) {
dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n",
ret);
- return true;
+ return false;
}
return (ret & WM831X_CLKOUT_ENA) != 0;
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index e8e48015d3af..4a9e034f939f 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2874,8 +2874,6 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry)
goto err_out;
}
- clk_debug_measure_add(core->hw, core->dentry);
-
ret = 0;
goto out;
@@ -3005,10 +3003,8 @@ static int __init clk_debug_init(void)
return -ENOMEM;
mutex_lock(&clk_debug_lock);
- hlist_for_each_entry(core, &clk_debug_list, debug_node) {
- clk_register_debug(core->hw, core->dentry);
+ hlist_for_each_entry(core, &clk_debug_list, debug_node)
clk_debug_create_one(core, rootdir);
- }
inited = 1;
mutex_unlock(&clk_debug_lock);
diff --git a/drivers/clk/clk.h b/drivers/clk/clk.h
index 25bf4bc85f5c..87af7af7aac2 100644
--- a/drivers/clk/clk.h
+++ b/drivers/clk/clk.h
@@ -23,8 +23,6 @@ void __clk_free_clk(struct clk *clk);
/* Debugfs API to print the enabled clocks */
void clock_debug_print_enabled(void);
-int clk_register_debug(struct clk_hw *hw, struct dentry *dentry);
-void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry);
void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f);
#else
diff --git a/drivers/clk/imx/clk-imx31.c b/drivers/clk/imx/clk-imx31.c
index 6a964144a5b5..6a49ba2b9671 100644
--- a/drivers/clk/imx/clk-imx31.c
+++ b/drivers/clk/imx/clk-imx31.c
@@ -157,10 +157,8 @@ static void __init _mx31_clocks_init(unsigned long fref)
}
}
-int __init mx31_clocks_init(void)
+int __init mx31_clocks_init(unsigned long fref)
{
- u32 fref = 26000000; /* default */
-
_mx31_clocks_init(fref);
clk_register_clkdev(clk[gpt_gate], "per", "imx-gpt.0");
diff --git a/drivers/clk/msm/clock-alpha-pll.c b/drivers/clk/msm/clock-alpha-pll.c
index b9a1167a790d..834315aa4993 100644
--- a/drivers/clk/msm/clock-alpha-pll.c
+++ b/drivers/clk/msm/clock-alpha-pll.c
@@ -612,13 +612,17 @@ static int alpha_pll_set_rate(struct clk *c, unsigned long rate)
return -EINVAL;
}
+ if (pll->no_irq_dis)
+ spin_lock(&c->lock);
+ else
+ spin_lock_irqsave(&c->lock, flags);
+
/*
* For PLLs that do not support dynamic programming (dynamic_update
* is not set), ensure PLL is off before changing rate. For
* optimization reasons, assume no downstream clock is actively
* using it.
*/
- spin_lock_irqsave(&c->lock, flags);
if (c->count && !pll->dynamic_update)
c->ops->disable(c);
@@ -644,7 +648,10 @@ static int alpha_pll_set_rate(struct clk *c, unsigned long rate)
if (c->count && !pll->dynamic_update)
c->ops->enable(c);
- spin_unlock_irqrestore(&c->lock, flags);
+ if (pll->no_irq_dis)
+ spin_unlock(&c->lock);
+ else
+ spin_unlock_irqrestore(&c->lock, flags);
return 0;
}
diff --git a/drivers/clk/msm/clock-cpu-8996.c b/drivers/clk/msm/clock-cpu-8996.c
index 7e03a599fccc..bcda6f31d6f5 100644
--- a/drivers/clk/msm/clock-cpu-8996.c
+++ b/drivers/clk/msm/clock-cpu-8996.c
@@ -238,6 +238,7 @@ static struct alpha_pll_clk perfcl_alt_pll = {
.post_div_config = 0x100, /* Div-2 */
.config_ctl_val = 0x4001051B,
.offline_bit_workaround = true,
+ .no_irq_dis = true,
.c = {
.always_on = true,
.parent = &alpha_xo_ao.c,
@@ -300,6 +301,7 @@ static struct alpha_pll_clk pwrcl_alt_pll = {
.post_div_config = 0x100, /* Div-2 */
.config_ctl_val = 0x4001051B,
.offline_bit_workaround = true,
+ .no_irq_dis = true,
.c = {
.always_on = true,
.dbg_name = "pwrcl_alt_pll",
diff --git a/drivers/clk/msm/clock-gcc-8996.c b/drivers/clk/msm/clock-gcc-8996.c
index a9e0b53c3b22..e93e9c494023 100644
--- a/drivers/clk/msm/clock-gcc-8996.c
+++ b/drivers/clk/msm/clock-gcc-8996.c
@@ -105,6 +105,8 @@ DEFINE_CLK_DUMMY(gcc_ce1_axi_m_clk, 0);
DEFINE_CLK_DUMMY(measure_only_bimc_hmss_axi_clk, 0);
DEFINE_CLK_RPM_SMD_XO_BUFFER(ln_bb_clk, ln_bb_a_clk, LN_BB_CLK_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(ln_bb_clk_pin, ln_bb_a_clk_pin,
+ LN_BB_CLK_PIN_ID);
static DEFINE_CLK_VOTER(mcd_ce1_clk, &ce1_clk.c, 85710000);
static DEFINE_CLK_VOTER(pnoc_keepalive_a_clk, &pnoc_a_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(pnoc_msmbus_clk, &pnoc_clk.c, LONG_MAX);
@@ -1340,9 +1342,11 @@ static struct rcg_clk pdm2_clk_src = {
},
};
-/* Frequency table might change later */
static struct clk_freq_tbl ftbl_qspi_ser_clk_src[] = {
- F( 192000000, gpll4_out_main, 2, 0, 0),
+ F( 75000000, gpll0_out_main, 8, 0, 0),
+ F( 150000000, gpll0_out_main, 4, 0, 0),
+ F( 256000000, gpll4_out_main, 1.5, 0, 0),
+ F( 300000000, gpll0_out_main, 2, 0, 0),
F_END
};
@@ -3387,6 +3391,8 @@ static struct clk_lookup msm_clocks_rpm_8996[] = {
CLK_LIST(ipa_clk),
CLK_LIST(ln_bb_clk),
CLK_LIST(ln_bb_a_clk),
+ CLK_LIST(ln_bb_clk_pin),
+ CLK_LIST(ln_bb_a_clk_pin),
CLK_LIST(mcd_ce1_clk),
CLK_LIST(pnoc_keepalive_a_clk),
CLK_LIST(pnoc_msmbus_clk),
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 317091a8003d..4c18181c047c 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -12,7 +12,7 @@ clk-qcom-y += clk-regmap-mux.o
clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
clk-qcom-y += clk-hfpll.o
clk-qcom-y += reset.o clk-voter.o
-clk-qcom-y += clk-dummy.o
+clk-qcom-y += clk-dummy.o clk-debug.o
clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o gdsc-regulator.o
obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c
index ca6010db8d78..3e9cd9909b86 100644
--- a/drivers/clk/qcom/clk-branch.c
+++ b/drivers/clk/qcom/clk-branch.c
@@ -21,6 +21,7 @@
#include <linux/regmap.h>
#include "clk-branch.h"
+#include "clk-debug.h"
#include "clk-regmap.h"
#include "common.h"
@@ -250,6 +251,7 @@ const struct clk_ops clk_branch2_ops = {
.is_enabled = clk_is_enabled_regmap,
.set_flags = clk_branch_set_flags,
.list_registers = clk_branch2_list_registers,
+ .debug_init = clk_debug_measure_add,
};
EXPORT_SYMBOL_GPL(clk_branch2_ops);
@@ -384,6 +386,7 @@ const struct clk_ops clk_gate2_ops = {
.is_enabled = clk_is_enabled_regmap,
.list_registers = clk_gate2_list_registers,
.set_flags = clk_gate2_set_flags,
+ .debug_init = clk_debug_measure_add,
};
EXPORT_SYMBOL_GPL(clk_gate2_ops);
diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c
index 6b00bee337a1..f82ddc3b008b 100644
--- a/drivers/clk/qcom/clk-cpu-osm.c
+++ b/drivers/clk/qcom/clk-cpu-osm.c
@@ -39,6 +39,7 @@
#include "common.h"
#include "clk-regmap.h"
#include "clk-rcg.h"
+#include "clk-debug.h"
enum {
LMH_LITE_CLK_SRC,
@@ -757,6 +758,7 @@ static struct clk_ops clk_ops_cpu_osm = {
.round_rate = clk_osm_round_rate,
.list_rate = clk_osm_list_rate,
.recalc_rate = clk_osm_recalc_rate,
+ .debug_init = clk_debug_measure_add,
};
static const struct parent_map gcc_parent_map_1[] = {
diff --git a/drivers/clk/qcom/clk-debug.c b/drivers/clk/qcom/clk-debug.c
new file mode 100644
index 000000000000..50d0d01188ed
--- /dev/null
+++ b/drivers/clk/qcom/clk-debug.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2013-2014, 2016-2017,
+ *
+ * The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+
+#include "clk-regmap.h"
+#include "clk-debug.h"
+#include "common.h"
+
+static struct clk_hw *measure;
+
+static DEFINE_SPINLOCK(clk_reg_lock);
+static DEFINE_MUTEX(clk_debug_lock);
+
+#define TCXO_DIV_4_HZ 4800000
+#define SAMPLE_TICKS_1_MS 0x1000
+#define SAMPLE_TICKS_14_MS 0x10000
+
+#define XO_DIV4_CNT_DONE BIT(25)
+#define CNT_EN BIT(20)
+#define MEASURE_CNT BM(24, 0)
+
+/* Sample clock for 'ticks' reference clock ticks. */
+static u32 run_measurement(unsigned ticks, struct regmap *regmap,
+ u32 ctl_reg, u32 status_reg)
+{
+ u32 regval;
+
+ /* Stop counters and set the XO4 counter start value. */
+ regmap_write(regmap, ctl_reg, ticks);
+
+ regmap_read(regmap, status_reg, &regval);
+
+ /* Wait for timer to become ready. */
+ while ((regval & XO_DIV4_CNT_DONE) != 0) {
+ cpu_relax();
+ regmap_read(regmap, status_reg, &regval);
+ }
+
+ /* Run measurement and wait for completion. */
+ regmap_write(regmap, ctl_reg, (CNT_EN|ticks));
+
+ regmap_read(regmap, status_reg, &regval);
+
+ while ((regval & XO_DIV4_CNT_DONE) == 0) {
+ cpu_relax();
+ regmap_read(regmap, status_reg, &regval);
+ }
+
+ /* Return measured ticks. */
+ regmap_read(regmap, status_reg, &regval);
+ regval &= MEASURE_CNT;
+
+ return regval;
+}
+
+/*
+ * Perform a hardware rate measurement for a given clock.
+ * FOR DEBUG USE ONLY: Measurements take ~15 ms!
+ */
+static unsigned long clk_debug_mux_measure_rate(struct clk_hw *hw)
+{
+ unsigned long flags, ret = 0;
+ u32 gcc_xo4_reg, multiplier;
+ u64 raw_count_short, raw_count_full;
+ struct clk_debug_mux *meas = to_clk_measure(hw);
+ struct measure_clk_data *data = meas->priv;
+
+ clk_prepare_enable(data->cxo);
+
+ spin_lock_irqsave(&clk_reg_lock, flags);
+
+ multiplier = meas->multiplier + 1;
+
+ /* Enable CXO/4 and RINGOSC branch. */
+ regmap_read(meas->regmap[GCC], data->xo_div4_cbcr, &gcc_xo4_reg);
+ gcc_xo4_reg |= BIT(0);
+ regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg);
+
+ /*
+ * The ring oscillator counter will not reset if the measured clock
+ * is not running. To detect this, run a short measurement before
+ * the full measurement. If the raw results of the two are the same
+ * then the clock must be off.
+ */
+
+ /* Run a short measurement. (~1 ms) */
+ raw_count_short = run_measurement(SAMPLE_TICKS_1_MS, meas->regmap[GCC],
+ data->ctl_reg, data->status_reg);
+
+ /* Run a full measurement. (~14 ms) */
+ raw_count_full = run_measurement(SAMPLE_TICKS_14_MS, meas->regmap[GCC],
+ data->ctl_reg, data->status_reg);
+
+ gcc_xo4_reg &= ~BIT(0);
+ regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg);
+
+ /* Return 0 if the clock is off. */
+ if (raw_count_full == raw_count_short)
+ ret = 0;
+ else {
+ /* Compute rate in Hz. */
+ raw_count_full = ((raw_count_full * 10) + 15) * TCXO_DIV_4_HZ;
+ do_div(raw_count_full, ((SAMPLE_TICKS_14_MS * 10) + 35));
+ ret = (raw_count_full * multiplier);
+ }
+
+ spin_unlock_irqrestore(&clk_reg_lock, flags);
+
+ clk_disable_unprepare(data->cxo);
+
+ return ret;
+}
+
+static u8 clk_debug_mux_get_parent(struct clk_hw *hw)
+{
+ struct clk_debug_mux *meas = to_clk_measure(hw);
+ int i, num_parents = clk_hw_get_num_parents(hw);
+
+ for (i = 0; i < num_parents; i++) {
+ if (!strcmp(meas->parent[i].parents,
+ hw->init->parent_names[i])) {
+ pr_debug("%s: Clock name %s index %d\n", __func__,
+ hw->init->name, i);
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+static int clk_debug_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_debug_mux *meas = to_clk_measure(hw);
+ unsigned long lsb = 0;
+ u32 regval = 0;
+ int dbg_cc = 0;
+
+ dbg_cc = meas->parent[index].dbg_cc;
+
+ if (dbg_cc != GCC) {
+ regmap_read(meas->regmap[dbg_cc], 0x0, &regval);
+
+ /* Clear & Set post divider bits */
+ if (meas->parent[index].post_div_mask) {
+ regval &= ~meas->parent[index].post_div_mask;
+ lsb = find_first_bit((unsigned long *)
+ &meas->parent[index].post_div_mask, 32);
+ regval |= (meas->parent[index].post_div_val << lsb) &
+ meas->parent[index].post_div_mask;
+ meas->multiplier = meas->parent[index].post_div_val;
+ }
+
+ if (meas->parent[index].mask)
+ regval &= ~meas->parent[index].mask <<
+ meas->parent[index].shift;
+ else
+ regval &= ~meas->mask;
+
+ regval |= (meas->parent[index].next_sel & meas->mask);
+
+ if (meas->parent[index].en_mask == 0xFF)
+ /* Skip en_mask */
+ regval = regval;
+ else if (meas->parent[index].en_mask)
+ regval |= meas->parent[index].en_mask;
+ else
+ regval |= meas->en_mask;
+
+ regmap_write(meas->regmap[dbg_cc], 0x0, regval);
+ }
+
+ /* update the debug sel for GCC */
+ regmap_read(meas->regmap[GCC], meas->debug_offset, &regval);
+
+ /* clear post divider bits */
+ regval &= ~BM(15, 12);
+ lsb = find_first_bit((unsigned long *)
+ &meas->parent[index].post_div_mask, 32);
+ regval |= (meas->parent[index].post_div_val << lsb) &
+ meas->parent[index].post_div_mask;
+ meas->multiplier = meas->parent[index].post_div_val;
+ regval &= ~meas->mask;
+ regval |= (meas->parent[index].sel & meas->mask);
+ regval |= meas->en_mask;
+
+ regmap_write(meas->regmap[GCC], meas->debug_offset, regval);
+
+ return 0;
+}
+
+const struct clk_ops clk_debug_mux_ops = {
+ .get_parent = clk_debug_mux_get_parent,
+ .set_parent = clk_debug_mux_set_parent,
+};
+EXPORT_SYMBOL(clk_debug_mux_ops);
+
+static int clk_debug_measure_get(void *data, u64 *val)
+{
+ struct clk_hw *hw = data, *par;
+ int ret = 0;
+ unsigned long meas_rate, sw_rate;
+
+ mutex_lock(&clk_debug_lock);
+
+ ret = clk_set_parent(measure->clk, hw->clk);
+ if (!ret) {
+ par = measure;
+ while (par && par != hw) {
+ if (par->init->ops->enable)
+ par->init->ops->enable(par);
+ par = clk_hw_get_parent(par);
+ }
+ *val = clk_debug_mux_measure_rate(measure);
+ }
+
+ meas_rate = clk_get_rate(hw->clk);
+ sw_rate = clk_get_rate(clk_hw_get_parent(measure)->clk);
+ if (sw_rate && meas_rate >= (sw_rate * 2))
+ *val *= DIV_ROUND_CLOSEST(meas_rate, sw_rate);
+
+ mutex_unlock(&clk_debug_lock);
+
+ return ret;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clk_measure_fops, clk_debug_measure_get,
+ NULL, "%lld\n");
+
+int clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry)
+{
+ if (IS_ERR_OR_NULL(measure)) {
+ pr_err_once("Please check if `measure` clk is registered!!!\n");
+ return 0;
+ }
+
+ if (clk_set_parent(measure->clk, hw->clk))
+ return 0;
+
+ debugfs_create_file("clk_measure", S_IRUGO, dentry, hw,
+ &clk_measure_fops);
+ return 0;
+}
+EXPORT_SYMBOL(clk_debug_measure_add);
+
+int clk_register_debug(struct clk_hw *hw)
+{
+ if (IS_ERR_OR_NULL(measure)) {
+ if (hw->init->flags & CLK_IS_MEASURE) {
+ measure = hw;
+ return 0;
+ }
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_register_debug);
+
diff --git a/drivers/clk/qcom/clk-debug.h b/drivers/clk/qcom/clk-debug.h
new file mode 100644
index 000000000000..40e8730a9466
--- /dev/null
+++ b/drivers/clk/qcom/clk-debug.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2014, 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __QCOM_CLK_DEBUG_H__
+#define __QCOM_CLK_DEBUG_H__
+
+#include "../clk.h"
+
+/* Debugfs Measure Clocks */
+
+/**
+ * struct measure_clk_data - Structure of clk measure
+ *
+ * @cxo: XO clock.
+ * @xo_div4_cbcr: offset of debug XO/4 div register.
+ * @ctl_reg: offset of debug control register.
+ * @status_reg: offset of debug status register.
+ *
+ */
+struct measure_clk_data {
+ struct clk *cxo;
+ u32 xo_div4_cbcr;
+ u32 ctl_reg;
+ u32 status_reg;
+};
+
+/**
+ * List of Debug clock controllers.
+ */
+enum debug_cc {
+ GCC,
+ MMCC,
+ GPU,
+ CPU,
+};
+
+/**
+ * struct clk_src - Struture of clock source for debug mux
+ *
+ * @parents: clock name to be used as parent for debug mux.
+ * @sel: debug mux index at global clock controller.
+ * @dbg_cc: indicates the clock controller for recursive debug clock
+ * controllers.
+ * @next_sel: indicates the debug mux index at recursive debug mux.
+ * @mask: indicates the mask required at recursive debug mux.
+ * @shift: indicates the shift required at recursive debug mux.
+ * @en_mask: indicates the enable bit mask at recursive debug mux.
+ * Incase the recursive debug mux does not have a enable bit,
+ * 0xFF should be used to indicate the same, otherwise global
+ * enable bit would be used.
+ * @post_div_mask: indicates the post div mask to be used at debug/recursive
+ * debug mux.
+ * @post_div_val: indicates the post div value to be used at debug/recursive
+ * debug mux.
+ */
+struct clk_src {
+ const char *parents;
+ int sel;
+ enum debug_cc dbg_cc;
+ int next_sel;
+ u32 mask;
+ u32 shift;
+ u32 en_mask;
+ u32 post_div_mask;
+ u32 post_div_val;
+};
+
+#define MUX_SRC_LIST(...) \
+ .parent = (struct clk_src[]){__VA_ARGS__}, \
+ .num_parents = ARRAY_SIZE(((struct clk_src[]){__VA_ARGS__}))
+
+/**
+ * struct clk_debug_mux - Struture of clock debug mux
+ *
+ * @parent: structure of clk_src
+ * @num_parents: number of parents
+ * @regmap: regmaps of debug mux
+ * @num_parent_regmap: number of regmap of debug mux
+ * @priv: private measure_clk_data to be used by debug mux
+ * @en_mask: indicates the enable bit mask at global clock
+ * controller debug mux.
+ * @mask: indicates the mask to be used at global clock
+ * controller debug mux.
+ * @debug_offset: debug mux offset.
+ * @hw: handle between common and hardware-specific interfaces.
+ * @multiplier: internally used by debug mux as post div multiplier.
+ */
+struct clk_debug_mux {
+ struct clk_src *parent;
+ int num_parents;
+ struct regmap **regmap;
+ int num_parent_regmap;
+ void *priv;
+ u32 en_mask;
+ u32 mask;
+ u32 debug_offset;
+ struct clk_hw hw;
+
+ /* internal */
+ u32 multiplier;
+};
+
+#define to_clk_measure(_hw) container_of((_hw), struct clk_debug_mux, hw)
+
+extern const struct clk_ops clk_debug_mux_ops;
+
+int clk_register_debug(struct clk_hw *hw);
+int clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry);
+
+#endif
diff --git a/drivers/clk/qcom/clk-dummy.c b/drivers/clk/qcom/clk-dummy.c
index e06a829e508c..c08136563e31 100644
--- a/drivers/clk/qcom/clk-dummy.c
+++ b/drivers/clk/qcom/clk-dummy.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,6 +17,8 @@
#include <linux/slab.h>
#include <linux/reset-controller.h>
+#include "clk-debug.h"
+
struct clk_dummy {
struct clk_hw hw;
struct reset_controller_dev reset;
@@ -65,6 +67,7 @@ struct clk_ops clk_dummy_ops = {
.round_rate = dummy_clk_round_rate,
.recalc_rate = dummy_clk_recalc_rate,
.set_flags = dummy_clk_set_flags,
+ .debug_init = clk_debug_measure_add,
};
EXPORT_SYMBOL_GPL(clk_dummy_ops);
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index 58fbd07e6f15..0bcada9aac5d 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013, 2016-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -192,5 +192,6 @@ extern const struct clk_ops clk_pixel_ops;
extern const struct clk_ops clk_gfx3d_ops;
extern const struct clk_ops clk_gfx3d_src_ops;
extern const struct clk_ops clk_dp_ops;
+extern const struct clk_ops clk_esc_ops;
#endif
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 632d0f4ac9c1..ff0c8327fabe 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -1349,3 +1349,73 @@ const struct clk_ops clk_gfx3d_src_ops = {
.list_registers = clk_rcg2_list_registers,
};
EXPORT_SYMBOL_GPL(clk_gfx3d_src_ops);
+
+static int clk_esc_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ unsigned long parent_rate, div;
+ u32 mask = BIT(rcg->hid_width) - 1;
+ struct clk_hw *p;
+ unsigned long rate = req->rate;
+
+ if (rate == 0)
+ return -EINVAL;
+
+ p = req->best_parent_hw;
+ req->best_parent_rate = parent_rate = clk_hw_round_rate(p, rate);
+
+ div = ((2 * parent_rate) / rate) - 1;
+ div = min_t(u32, div, mask);
+
+ req->rate = calc_rate(parent_rate, 0, 0, 0, div);
+
+ return 0;
+}
+
+static int clk_esc_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ struct freq_tbl f = { 0 };
+ unsigned long div;
+ int i, num_parents = clk_hw_get_num_parents(hw);
+ u32 mask = BIT(rcg->hid_width) - 1;
+ u32 cfg;
+
+ div = ((2 * parent_rate) / rate) - 1;
+ div = min_t(u32, div, mask);
+
+ f.pre_div = div;
+
+ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
+ cfg &= CFG_SRC_SEL_MASK;
+ cfg >>= CFG_SRC_SEL_SHIFT;
+
+ for (i = 0; i < num_parents; i++) {
+ if (cfg == rcg->parent_map[i].cfg) {
+ f.src = rcg->parent_map[i].src;
+ return clk_rcg2_configure(rcg, &f);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int clk_esc_set_rate_and_parent(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate, u8 index)
+{
+ return clk_esc_set_rate(hw, rate, parent_rate);
+}
+
+const struct clk_ops clk_esc_ops = {
+ .is_enabled = clk_rcg2_is_enabled,
+ .get_parent = clk_rcg2_get_parent,
+ .set_parent = clk_rcg2_set_parent,
+ .recalc_rate = clk_rcg2_recalc_rate,
+ .determine_rate = clk_esc_determine_rate,
+ .set_rate = clk_esc_set_rate,
+ .set_rate_and_parent = clk_esc_set_rate_and_parent,
+ .list_registers = clk_rcg2_list_registers,
+};
+EXPORT_SYMBOL(clk_esc_ops);
diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c
index 5345e9086627..8dcdf2929ca6 100644
--- a/drivers/clk/qcom/clk-smd-rpm.c
+++ b/drivers/clk/qcom/clk-smd-rpm.c
@@ -30,6 +30,7 @@
#include <dt-bindings/mfd/qcom-rpm.h>
#include "clk-voter.h"
+#include "clk-debug.h"
#define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773
#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370
@@ -463,6 +464,7 @@ static const struct clk_ops clk_smd_rpm_ops = {
.round_rate = clk_smd_rpm_round_rate,
.recalc_rate = clk_smd_rpm_recalc_rate,
.is_enabled = clk_smd_rpm_is_enabled,
+ .debug_init = clk_debug_measure_add,
};
static const struct clk_ops clk_smd_rpm_branch_ops = {
@@ -471,6 +473,7 @@ static const struct clk_ops clk_smd_rpm_branch_ops = {
.round_rate = clk_smd_rpm_round_rate,
.recalc_rate = clk_smd_rpm_recalc_rate,
.is_enabled = clk_smd_rpm_is_enabled,
+ .debug_init = clk_debug_measure_add,
};
/* msm8916 */
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index f6ce3106ab6f..8ecb207a97e5 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -1,5 +1,7 @@
/*
- * Copyright (c) 2013-2014, 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, 2016-2017,
+ *
+ * The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -287,239 +289,4 @@ int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc)
}
EXPORT_SYMBOL_GPL(qcom_cc_probe);
-/* Debugfs Support */
-static struct clk_hw *measure;
-
-DEFINE_SPINLOCK(clk_reg_lock);
-
-/* Sample clock for 'ticks' reference clock ticks. */
-static u32 run_measurement(unsigned ticks, struct regmap *regmap,
- u32 ctl_reg, u32 status_reg)
-{
- u32 regval;
-
- /* Stop counters and set the XO4 counter start value. */
- regmap_write(regmap, ctl_reg, ticks);
-
- regmap_read(regmap, status_reg, &regval);
-
- /* Wait for timer to become ready. */
- while ((regval & BIT(25)) != 0) {
- cpu_relax();
- regmap_read(regmap, status_reg, &regval);
- }
-
- /* Run measurement and wait for completion. */
- regmap_write(regmap, ctl_reg, (BIT(20)|ticks));
- regmap_read(regmap, ctl_reg, &regval);
-
- regmap_read(regmap, status_reg, &regval);
-
- while ((regval & BIT(25)) == 0) {
- cpu_relax();
- regmap_read(regmap, status_reg, &regval);
- }
-
- /* Return measured ticks. */
- regmap_read(regmap, status_reg, &regval);
- regval &= BM(24, 0);
-
- return regval;
-}
-
-/*
- * Perform a hardware rate measurement for a given clock.
- * FOR DEBUG USE ONLY: Measurements take ~15 ms!
- */
-static unsigned long clk_debug_mux_measure_rate(struct clk_hw *hw)
-{
- unsigned long flags, ret = 0;
- u32 gcc_xo4_reg, sample_ticks = 0x10000, multiplier;
- u64 raw_count_short, raw_count_full;
- struct clk_debug_mux *meas = to_clk_measure(hw);
- struct measure_clk_data *data = meas->priv;
-
- clk_prepare_enable(data->cxo);
-
- spin_lock_irqsave(&clk_reg_lock, flags);
-
- multiplier = meas->multiplier + 1;
-
- /* Enable CXO/4 and RINGOSC branch. */
- regmap_read(meas->regmap[GCC], data->xo_div4_cbcr, &gcc_xo4_reg);
- gcc_xo4_reg |= BIT(0);
- regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg);
-
- /*
- * The ring oscillator counter will not reset if the measured clock
- * is not running. To detect this, run a short measurement before
- * the full measurement. If the raw results of the two are the same
- * then the clock must be off.
- */
-
- /* Run a short measurement. (~1 ms) */
- raw_count_short = run_measurement(0x1000, meas->regmap[GCC],
- data->ctl_reg, data->status_reg);
-
- /* Run a full measurement. (~14 ms) */
- raw_count_full = run_measurement(sample_ticks, meas->regmap[GCC],
- data->ctl_reg, data->status_reg);
-
- gcc_xo4_reg &= ~BIT(0);
- regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg);
-
- /* Return 0 if the clock is off. */
- if (raw_count_full == raw_count_short)
- ret = 0;
- else {
- /* Compute rate in Hz. */
- raw_count_full = ((raw_count_full * 10) + 15) * 4800000;
- do_div(raw_count_full, ((sample_ticks * 10) + 35));
- ret = (raw_count_full * multiplier);
- }
-
- spin_unlock_irqrestore(&clk_reg_lock, flags);
-
- clk_disable_unprepare(data->cxo);
-
- return ret;
-}
-
-static u8 clk_debug_mux_get_parent(struct clk_hw *hw)
-{
- struct clk_debug_mux *meas = to_clk_measure(hw);
- int i, num_parents = clk_hw_get_num_parents(hw);
-
- for (i = 0; i < num_parents; i++) {
- if (!strcmp(meas->parent[i].parents,
- hw->init->parent_names[i])) {
- pr_debug("%s :Clock name %s index %d\n", __func__,
- hw->init->name, i);
- return i;
- }
- }
-
- return 0;
-}
-
-static int clk_debug_mux_set_parent(struct clk_hw *hw, u8 index)
-{
- struct clk_debug_mux *meas = to_clk_measure(hw);
- unsigned long lsb = 0;
- u32 regval = 0;
- int dbg_cc = 0;
-
- dbg_cc = meas->parent[index].dbg_cc;
-
- if (dbg_cc != GCC) {
- regmap_read(meas->regmap[dbg_cc], 0x0, &regval);
-
- /* Clear & Set post divider bits */
- if (meas->parent[index].post_div_mask) {
- regval &= ~meas->parent[index].post_div_mask;
- lsb = find_first_bit((unsigned long *)
- &meas->parent[index].post_div_mask, 32);
- regval |= (meas->parent[index].post_div_val << lsb) &
- meas->parent[index].post_div_mask;
- meas->multiplier = meas->parent[index].post_div_val;
- }
-
- if (meas->parent[index].mask)
- regval &= ~meas->parent[index].mask <<
- meas->parent[index].shift;
- else
- regval &= ~meas->mask;
-
- regval |= (meas->parent[index].next_sel & meas->mask);
-
- if (meas->parent[index].en_mask == 0xFF)
- /* Skip en_mask */
- regval = regval;
- else if (meas->parent[index].en_mask)
- regval |= meas->parent[index].en_mask;
- else
- regval |= meas->en_mask;
-
- regmap_write(meas->regmap[dbg_cc], 0x0, regval);
- }
-
- /* update the debug sel for GCC */
- regmap_read(meas->regmap[GCC], meas->debug_offset, &regval);
-
- /* clear post divider bits */
- regval &= ~BM(15, 12);
- lsb = find_first_bit((unsigned long *)
- &meas->parent[index].post_div_mask, 32);
- regval |= (meas->parent[index].post_div_val << lsb) &
- meas->parent[index].post_div_mask;
- meas->multiplier = meas->parent[index].post_div_val;
- regval &= ~meas->mask;
- regval |= (meas->parent[index].sel & meas->mask);
- regval |= meas->en_mask;
-
- regmap_write(meas->regmap[GCC], meas->debug_offset, regval);
-
- return 0;
-}
-
-const struct clk_ops clk_debug_mux_ops = {
- .get_parent = clk_debug_mux_get_parent,
- .set_parent = clk_debug_mux_set_parent,
-};
-EXPORT_SYMBOL_GPL(clk_debug_mux_ops);
-
-static int clk_debug_measure_get(void *data, u64 *val)
-{
- struct clk_hw *hw = data, *par;
- int ret = 0;
- unsigned long meas_rate, sw_rate;
-
- ret = clk_set_parent(measure->clk, hw->clk);
- if (!ret) {
- par = measure;
- while (par && par != hw) {
- if (par->init->ops->enable)
- par->init->ops->enable(par);
- par = clk_hw_get_parent(par);
- }
- *val = clk_debug_mux_measure_rate(measure);
- }
-
- meas_rate = clk_get_rate(hw->clk);
- sw_rate = clk_get_rate(clk_hw_get_parent(measure)->clk);
- if (sw_rate && meas_rate >= (sw_rate * 2))
- *val *= DIV_ROUND_CLOSEST(meas_rate, sw_rate);
-
- return ret;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(clk_measure_fops, clk_debug_measure_get,
- NULL, "%lld\n");
-
-void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry)
-{
- if (IS_ERR_OR_NULL(measure))
- return;
-
- if (clk_set_parent(measure->clk, hw->clk))
- return;
-
- debugfs_create_file("measure", S_IRUGO, dentry, hw,
- &clk_measure_fops);
-}
-EXPORT_SYMBOL_GPL(clk_debug_measure_add);
-
-int clk_register_debug(struct clk_hw *hw, struct dentry *dentry)
-{
- if (IS_ERR_OR_NULL(measure)) {
- if (hw->init->flags & CLK_IS_MEASURE)
- measure = hw;
- if (!IS_ERR_OR_NULL(measure))
- clk_debug_measure_add(hw, dentry);
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(clk_register_debug);
-
MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index 95408a47fef0..c532444a0d40 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -13,7 +13,7 @@
#ifndef __QCOM_CLK_COMMON_H__
#define __QCOM_CLK_COMMON_H__
-#include "../clk.h"
+#include "clk-debug.h"
struct platform_device;
struct regmap_config;
@@ -54,107 +54,9 @@ extern int qcom_cc_probe(struct platform_device *pdev,
const struct qcom_cc_desc *desc);
extern struct clk_ops clk_dummy_ops;
-/* Debugfs Measure Clocks */
-
-/**
- * struct measure_clk_data - Structure of clk measure
- *
- * @cxo: XO clock.
- * @xo_div4_cbcr: offset of debug XO/4 div register.
- * @ctl_reg: offset of debug control register.
- * @status_reg: offset of debug status register.
- *
- */
-struct measure_clk_data {
- struct clk *cxo;
- u32 xo_div4_cbcr;
- u32 ctl_reg;
- u32 status_reg;
-};
-
-/**
- * List of Debug clock controllers.
- */
-enum debug_cc {
- GCC,
- MMCC,
- GPU,
- CPU,
-};
-
-/**
- * struct clk_src - Struture of clock source for debug mux
- *
- * @parents: clock name to be used as parent for debug mux.
- * @sel: debug mux index at global clock controller.
- * @dbg_cc: indicates the clock controller for recursive debug clock
- * controllers.
- * @next_sel: indicates the debug mux index at recursive debug mux.
- * @mask: indicates the mask required at recursive debug mux.
- * @shift: indicates the shift required at recursive debug mux.
- * @en_mask: indicates the enable bit mask at recursive debug mux.
- * Incase the recursive debug mux does not have a enable bit,
- * 0xFF should be used to indicate the same, otherwise global
- * enable bit would be used.
- * @post_div_mask: indicates the post div mask to be used at debug/recursive
- * debug mux.
- * @post_div_val: indicates the post div value to be used at debug/recursive
- * debug mux.
- */
-struct clk_src {
- const char *parents;
- int sel;
- enum debug_cc dbg_cc;
- int next_sel;
- u32 mask;
- u32 shift;
- u32 en_mask;
- u32 post_div_mask;
- u32 post_div_val;
-};
-
-#define MUX_SRC_LIST(...) \
- .parent = (struct clk_src[]){__VA_ARGS__}, \
- .num_parents = ARRAY_SIZE(((struct clk_src[]){__VA_ARGS__}))
-
-/**
- * struct clk_debug_mux - Struture of clock debug mux
- *
- * @parent: structure of clk_src
- * @num_parents: number of parents
- * @regmap: regmaps of debug mux
- * @num_parent_regmap: number of regmap of debug mux
- * @priv: private measure_clk_data to be used by debug mux
- * @en_mask: indicates the enable bit mask at global clock
- * controller debug mux.
- * @mask: indicates the mask to be used at global clock
- * controller debug mux.
- * @debug_offset: Start of debug mux offset.
- * @hw: handle between common and hardware-specific interfaces.
- * @multiplier: internally used by debug mux as post div multiplier.
- */
-struct clk_debug_mux {
- struct clk_src *parent;
- int num_parents;
- struct regmap **regmap;
- int num_parent_regmap;
- void *priv;
- u32 en_mask;
- u32 mask;
- u32 debug_offset;
- struct clk_hw hw;
-
- /* internal */
- u32 multiplier;
-};
-
#define BM(msb, lsb) (((((uint32_t)-1) << (31-msb)) >> (31-msb+lsb)) << lsb)
#define BVAL(msb, lsb, val) (((val) << lsb) & BM(msb, lsb))
-#define to_clk_measure(_hw) container_of((_hw), struct clk_debug_mux, hw)
-
-extern const struct clk_ops clk_debug_mux_ops;
-
#define WARN_CLK(core, name, cond, fmt, ...) do { \
clk_debug_print_hw(core, NULL); \
WARN(cond, "%s: " fmt, name, ##__VA_ARGS__); \
diff --git a/drivers/clk/qcom/gcc-sdm660.c b/drivers/clk/qcom/gcc-sdm660.c
index c282253da584..b55310e091af 100644
--- a/drivers/clk/qcom/gcc-sdm660.c
+++ b/drivers/clk/qcom/gcc-sdm660.c
@@ -27,6 +27,7 @@
#include "clk-alpha-pll.h"
#include "clk-branch.h"
+#include "clk-debug.h"
#include "common.h"
#include "clk-pll.h"
#include "clk-regmap.h"
@@ -3328,7 +3329,11 @@ static int clk_debug_660_probe(struct platform_device *pdev)
return PTR_ERR(clk);
}
- dev_info(&pdev->dev, "Registered debug mux successfully\n");
+ ret = clk_register_debug(&gcc_debug_mux.hw);
+ if (ret)
+ dev_err(&pdev->dev, "Could not register Measure clock\n");
+ else
+ dev_info(&pdev->dev, "Registered debug mux successfully\n");
return ret;
}
diff --git a/drivers/clk/qcom/mmcc-sdm660.c b/drivers/clk/qcom/mmcc-sdm660.c
index 0bf7ef05ed06..910c36c65b6a 100644
--- a/drivers/clk/qcom/mmcc-sdm660.c
+++ b/drivers/clk/qcom/mmcc-sdm660.c
@@ -978,12 +978,11 @@ static struct clk_rcg2 esc0_clk_src = {
.mnd_width = 0,
.hid_width = 5,
.parent_map = mmcc_parent_map_1,
- .freq_tbl = ftbl_dp_aux_clk_src,
.clkr.hw.init = &(struct clk_init_data){
.name = "esc0_clk_src",
.parent_names = mmcc_parent_names_1,
.num_parents = 4,
- .ops = &clk_rcg2_ops,
+ .ops = &clk_esc_ops,
VDD_DIG_FMAX_MAP1(
LOWER, 19200000),
},
@@ -994,12 +993,11 @@ static struct clk_rcg2 esc1_clk_src = {
.mnd_width = 0,
.hid_width = 5,
.parent_map = mmcc_parent_map_1,
- .freq_tbl = ftbl_dp_aux_clk_src,
.clkr.hw.init = &(struct clk_init_data){
.name = "esc1_clk_src",
.parent_names = mmcc_parent_names_1,
.num_parents = 4,
- .ops = &clk_rcg2_ops,
+ .ops = &clk_esc_ops,
VDD_DIG_FMAX_MAP1(
LOWER, 19200000),
},
diff --git a/drivers/clk/ti/clk-3xxx.c b/drivers/clk/ti/clk-3xxx.c
index 8831e1a05367..11d8aa3ec186 100644
--- a/drivers/clk/ti/clk-3xxx.c
+++ b/drivers/clk/ti/clk-3xxx.c
@@ -22,13 +22,6 @@
#include "clock.h"
-/*
- * DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks
- * that are sourced by DPLL5, and both of these require this clock
- * to be at 120 MHz for proper operation.
- */
-#define DPLL5_FREQ_FOR_USBHOST 120000000
-
#define OMAP3430ES2_ST_DSS_IDLE_SHIFT 1
#define OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT 5
#define OMAP3430ES2_ST_SSI_IDLE_SHIFT 8
@@ -546,14 +539,21 @@ void __init omap3_clk_lock_dpll5(void)
struct clk *dpll5_clk;
struct clk *dpll5_m2_clk;
+ /*
+ * Errata sprz319f advisory 2.1 documents a USB host clock drift issue
+ * that can be worked around using specially crafted dpll5 settings
+ * with a dpll5_m2 divider set to 8. Set the dpll5 rate to 8x the USB
+ * host clock rate, its .set_rate handler() will detect that frequency
+ * and use the errata settings.
+ */
dpll5_clk = clk_get(NULL, "dpll5_ck");
- clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST);
+ clk_set_rate(dpll5_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST * 8);
clk_prepare_enable(dpll5_clk);
- /* Program dpll5_m2_clk divider for no division */
+ /* Program dpll5_m2_clk divider */
dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck");
clk_prepare_enable(dpll5_m2_clk);
- clk_set_rate(dpll5_m2_clk, DPLL5_FREQ_FOR_USBHOST);
+ clk_set_rate(dpll5_m2_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST);
clk_disable_unprepare(dpll5_m2_clk);
clk_disable_unprepare(dpll5_clk);
diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h
index 90f3f472ae1c..13c37f48d9d6 100644
--- a/drivers/clk/ti/clock.h
+++ b/drivers/clk/ti/clock.h
@@ -257,11 +257,20 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw,
unsigned long parent_rate);
+/*
+ * OMAP3_DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks
+ * that are sourced by DPLL5, and both of these require this clock
+ * to be at 120 MHz for proper operation.
+ */
+#define OMAP3_DPLL5_FREQ_FOR_USBHOST 120000000
+
unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate);
int omap3_dpll4_set_rate(struct clk_hw *clk, unsigned long rate,
unsigned long parent_rate);
int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate, u8 index);
+int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate);
void omap3_clk_lock_dpll5(void);
unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c
index 5519b386edc0..f9a5089ddc79 100644
--- a/drivers/clk/ti/dpll.c
+++ b/drivers/clk/ti/dpll.c
@@ -114,6 +114,18 @@ static const struct clk_ops omap3_dpll_ck_ops = {
.round_rate = &omap2_dpll_round_rate,
};
+static const struct clk_ops omap3_dpll5_ck_ops = {
+ .enable = &omap3_noncore_dpll_enable,
+ .disable = &omap3_noncore_dpll_disable,
+ .get_parent = &omap2_init_dpll_parent,
+ .recalc_rate = &omap3_dpll_recalc,
+ .set_rate = &omap3_dpll5_set_rate,
+ .set_parent = &omap3_noncore_dpll_set_parent,
+ .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent,
+ .determine_rate = &omap3_noncore_dpll_determine_rate,
+ .round_rate = &omap2_dpll_round_rate,
+};
+
static const struct clk_ops omap3_dpll_per_ck_ops = {
.enable = &omap3_noncore_dpll_enable,
.disable = &omap3_noncore_dpll_disable,
@@ -461,7 +473,12 @@ static void __init of_ti_omap3_dpll_setup(struct device_node *node)
.modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
};
- of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd);
+ if ((of_machine_is_compatible("ti,omap3630") ||
+ of_machine_is_compatible("ti,omap36xx")) &&
+ !strcmp(node->name, "dpll5_ck"))
+ of_ti_dpll_setup(node, &omap3_dpll5_ck_ops, &dd);
+ else
+ of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd);
}
CLK_OF_DECLARE(ti_omap3_dpll_clock, "ti,omap3-dpll-clock",
of_ti_omap3_dpll_setup);
diff --git a/drivers/clk/ti/dpll3xxx.c b/drivers/clk/ti/dpll3xxx.c
index f4dec00fb684..0e9119fae760 100644
--- a/drivers/clk/ti/dpll3xxx.c
+++ b/drivers/clk/ti/dpll3xxx.c
@@ -815,3 +815,70 @@ int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
return omap3_noncore_dpll_set_rate_and_parent(hw, rate, parent_rate,
index);
}
+
+/* Apply DM3730 errata sprz319 advisory 2.1. */
+static bool omap3_dpll5_apply_errata(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct omap3_dpll5_settings {
+ unsigned int rate, m, n;
+ };
+
+ static const struct omap3_dpll5_settings precomputed[] = {
+ /*
+ * From DM3730 errata advisory 2.1, table 35 and 36.
+ * The N value is increased by 1 compared to the tables as the
+ * errata lists register values while last_rounded_field is the
+ * real divider value.
+ */
+ { 12000000, 80, 0 + 1 },
+ { 13000000, 443, 5 + 1 },
+ { 19200000, 50, 0 + 1 },
+ { 26000000, 443, 11 + 1 },
+ { 38400000, 25, 0 + 1 }
+ };
+
+ const struct omap3_dpll5_settings *d;
+ struct clk_hw_omap *clk = to_clk_hw_omap(hw);
+ struct dpll_data *dd;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(precomputed); ++i) {
+ if (parent_rate == precomputed[i].rate)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(precomputed))
+ return false;
+
+ d = &precomputed[i];
+
+ /* Update the M, N and rounded rate values and program the DPLL. */
+ dd = clk->dpll_data;
+ dd->last_rounded_m = d->m;
+ dd->last_rounded_n = d->n;
+ dd->last_rounded_rate = div_u64((u64)parent_rate * d->m, d->n);
+ omap3_noncore_dpll_program(clk, 0);
+
+ return true;
+}
+
+/**
+ * omap3_dpll5_set_rate - set rate for omap3 dpll5
+ * @hw: clock to change
+ * @rate: target rate for clock
+ * @parent_rate: rate of the parent clock
+ *
+ * Set rate for the DPLL5 clock. Apply the sprz319 advisory 2.1 on OMAP36xx if
+ * the DPLL is used for USB host (detected through the requested rate).
+ */
+int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ if (rate == OMAP3_DPLL5_FREQ_FOR_USBHOST * 8) {
+ if (omap3_dpll5_apply_errata(hw, parent_rate))
+ return 0;
+ }
+
+ return omap3_noncore_dpll_set_rate(hw, rate, parent_rate);
+}
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index ff44082a0827..47f8aafe3344 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -482,6 +482,7 @@ static void exynos4_local_timer_stop(struct mct_clock_event_device *mevt)
if (mct_int_type == MCT_INT_SPI) {
if (evt->irq != -1)
disable_irq_nosync(evt->irq);
+ exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
} else {
disable_percpu_irq(mct_irqs[MCT_L0_IRQ]);
}
diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c
index cb501386eb6e..c4b0ef65988c 100644
--- a/drivers/cpufreq/powernv-cpufreq.c
+++ b/drivers/cpufreq/powernv-cpufreq.c
@@ -373,8 +373,14 @@ static int powernv_cpufreq_target_index(struct cpufreq_policy *policy,
if (unlikely(rebooting) && new_index != get_nominal_index())
return 0;
- if (!throttled)
+ if (!throttled) {
+ /* we don't want to be preempted while
+ * checking if the CPU frequency has been throttled
+ */
+ preempt_disable();
powernv_cpufreq_throttle_check(NULL);
+ preempt_enable();
+ }
freq_data.pstate_id = powernv_freqs[new_index].driver_data;
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index 2cde3796cb82..f3307fc38e79 100644
--- a/drivers/crypto/caam/caamalg.c
+++ b/drivers/crypto/caam/caamalg.c
@@ -702,7 +702,9 @@ copy_iv:
/* Will read cryptlen */
append_math_add(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
- aead_append_src_dst(desc, FIFOLD_TYPE_MSG1OUT2);
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_BOTH | KEY_VLF |
+ FIFOLD_TYPE_MSG1OUT2 | FIFOLD_TYPE_LASTBOTH);
+ append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | KEY_VLF);
/* Write ICV */
append_seq_store(desc, ctx->authsize, LDST_CLASS_2_CCB |
diff --git a/drivers/devfreq/governor_spdm_bw_hyp.c b/drivers/devfreq/governor_spdm_bw_hyp.c
index cd068a3097ce..f18b72af5fc4 100644
--- a/drivers/devfreq/governor_spdm_bw_hyp.c
+++ b/drivers/devfreq/governor_spdm_bw_hyp.c
@@ -1,5 +1,5 @@
/*
-*Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+*Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
*
*This program is free software; you can redistribute it and/or modify
*it under the terms of the GNU General Public License version 2 and
@@ -84,6 +84,10 @@ static irqreturn_t threaded_isr(int irq, void *dev_id)
(int)desc.arg[0], ext_status);
mutex_lock(&devfreqs_lock);
list_for_each_entry(data, &devfreqs, list) {
+ if (data == NULL || data->devfreq == NULL) {
+ pr_err("Spurious interrupts\n");
+ break;
+ }
if (data->spdm_client == desc.ret[0]) {
devfreq_monitor_suspend(data->devfreq);
mutex_lock(&data->devfreq->lock);
diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c
index dd184b50e5b4..284627806b88 100644
--- a/drivers/dma/ipu/ipu_irq.c
+++ b/drivers/dma/ipu/ipu_irq.c
@@ -272,7 +272,7 @@ static void ipu_irq_handler(struct irq_desc *desc)
u32 status;
int i, line;
- for (i = IPU_IRQ_NR_FN_BANKS; i < IPU_IRQ_NR_BANKS; i++) {
+ for (i = 0; i < IPU_IRQ_NR_BANKS; i++) {
struct ipu_irq_bank *bank = irq_bank + i;
raw_spin_lock(&bank_lock);
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 17ee758b419f..8250950aab8b 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -445,6 +445,9 @@ struct dma_pl330_chan {
/* for cyclic capability */
bool cyclic;
+
+ /* for runtime pm tracking */
+ bool active;
};
struct pl330_dmac {
@@ -1994,6 +1997,7 @@ static void pl330_tasklet(unsigned long data)
_stop(pch->thread);
spin_unlock(&pch->thread->dmac->lock);
power_down = true;
+ pch->active = false;
} else {
/* Make sure the PL330 Channel thread is active */
spin_lock(&pch->thread->dmac->lock);
@@ -2015,6 +2019,7 @@ static void pl330_tasklet(unsigned long data)
desc->status = PREP;
list_move_tail(&desc->node, &pch->work_list);
if (power_down) {
+ pch->active = true;
spin_lock(&pch->thread->dmac->lock);
_start(pch->thread);
spin_unlock(&pch->thread->dmac->lock);
@@ -2129,6 +2134,7 @@ static int pl330_terminate_all(struct dma_chan *chan)
unsigned long flags;
struct pl330_dmac *pl330 = pch->dmac;
LIST_HEAD(list);
+ bool power_down = false;
pm_runtime_get_sync(pl330->ddma.dev);
spin_lock_irqsave(&pch->lock, flags);
@@ -2139,6 +2145,8 @@ static int pl330_terminate_all(struct dma_chan *chan)
pch->thread->req[0].desc = NULL;
pch->thread->req[1].desc = NULL;
pch->thread->req_running = -1;
+ power_down = pch->active;
+ pch->active = false;
/* Mark all desc done */
list_for_each_entry(desc, &pch->submitted_list, node) {
@@ -2156,6 +2164,8 @@ static int pl330_terminate_all(struct dma_chan *chan)
list_splice_tail_init(&pch->completed_list, &pl330->desc_pool);
spin_unlock_irqrestore(&pch->lock, flags);
pm_runtime_mark_last_busy(pl330->ddma.dev);
+ if (power_down)
+ pm_runtime_put_autosuspend(pl330->ddma.dev);
pm_runtime_put_autosuspend(pl330->ddma.dev);
return 0;
@@ -2302,6 +2312,7 @@ static void pl330_issue_pending(struct dma_chan *chan)
* updated on work_list emptiness status.
*/
WARN_ON(list_empty(&pch->submitted_list));
+ pch->active = true;
pm_runtime_get_sync(pch->dmac->ddma.dev);
}
list_splice_tail_init(&pch->submitted_list, &pch->work_list);
diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c
index 7275a99873b8..1e5f35d8422d 100644
--- a/drivers/esoc/esoc-mdm-4x.c
+++ b/drivers/esoc/esoc-mdm-4x.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -434,11 +434,12 @@ static irqreturn_t mdm_status_change(int irq, void *dev_id)
{
int value;
struct esoc_clink *esoc;
+ struct device *dev;
struct mdm_ctrl *mdm = (struct mdm_ctrl *)dev_id;
- struct device *dev = mdm->dev;
if (!mdm)
return IRQ_HANDLED;
+ dev = mdm->dev;
esoc = mdm->esoc;
value = gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS));
if (value == 0 && mdm->ready) {
@@ -499,7 +500,7 @@ static void mdm_configure_debug(struct mdm_ctrl *mdm)
struct device_node *node = mdm->dev->of_node;
addr = of_iomap(node, 0);
- if (IS_ERR(addr)) {
+ if (IS_ERR_OR_NULL(addr)) {
dev_err(mdm->dev, "failed to get debug base addres\n");
return;
}
@@ -508,7 +509,7 @@ static void mdm_configure_debug(struct mdm_ctrl *mdm)
if (val == MDM_DBG_MODE) {
mdm->dbg_mode = true;
mdm->cti = coresight_cti_get(MDM_CTI_NAME);
- if (IS_ERR(mdm->cti)) {
+ if (IS_ERR_OR_NULL(mdm->cti)) {
dev_err(mdm->dev, "unable to get cti handle\n");
goto cti_get_err;
}
@@ -743,7 +744,7 @@ static int mdm9x25_setup_hw(struct mdm_ctrl *mdm,
mdm->dev = &pdev->dev;
mdm->pon_ops = pon_ops;
esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL);
- if (IS_ERR(esoc)) {
+ if (IS_ERR_OR_NULL(esoc)) {
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
@@ -813,7 +814,7 @@ static int mdm9x35_setup_hw(struct mdm_ctrl *mdm,
mdm->pon_ops = pon_ops;
node = pdev->dev.of_node;
esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL);
- if (IS_ERR(esoc)) {
+ if (IS_ERR_OR_NULL(esoc)) {
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
@@ -901,7 +902,7 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm,
mdm->pon_ops = pon_ops;
node = pdev->dev.of_node;
esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL);
- if (IS_ERR(esoc)) {
+ if (IS_ERR_OR_NULL(esoc)) {
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
@@ -936,6 +937,10 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm,
mdm->dual_interface = of_property_read_bool(node,
"qcom,mdm-dual-link");
esoc->link_name = MDM9x55_PCIE;
+ ret = of_property_read_string(node, "qcom,mdm-link-info",
+ &esoc->link_info);
+ if (ret)
+ dev_info(mdm->dev, "esoc link info missing\n");
esoc->clink_ops = clink_ops;
esoc->parent = mdm->dev;
esoc->owner = THIS_MODULE;
@@ -1001,11 +1006,11 @@ static int mdm_probe(struct platform_device *pdev)
struct mdm_ctrl *mdm;
match = of_match_node(mdm_dt_match, node);
- if (IS_ERR(match))
+ if (IS_ERR_OR_NULL(match))
return PTR_ERR(match);
mdm_ops = match->data;
mdm = devm_kzalloc(&pdev->dev, sizeof(*mdm), GFP_KERNEL);
- if (IS_ERR(mdm))
+ if (IS_ERR_OR_NULL(mdm))
return PTR_ERR(mdm);
return mdm_ops->config_hw(mdm, mdm_ops, pdev);
}
diff --git a/drivers/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c
index 9abe125f28b5..8697428eceb2 100644
--- a/drivers/esoc/esoc-mdm-drv.c
+++ b/drivers/esoc/esoc-mdm-drv.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -230,7 +230,7 @@ int esoc_ssr_probe(struct esoc_clink *esoc_clink, struct esoc_drv *drv)
struct esoc_eng *esoc_eng;
mdm_drv = devm_kzalloc(&esoc_clink->dev, sizeof(*mdm_drv), GFP_KERNEL);
- if (IS_ERR(mdm_drv))
+ if (IS_ERR_OR_NULL(mdm_drv))
return PTR_ERR(mdm_drv);
esoc_eng = &mdm_drv->cmd_eng;
esoc_eng->handle_clink_evt = mdm_handle_clink_evt;
diff --git a/drivers/esoc/esoc.h b/drivers/esoc/esoc.h
index efa2a1d5e5a6..755fb24bd60a 100644
--- a/drivers/esoc/esoc.h
+++ b/drivers/esoc/esoc.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -46,6 +46,7 @@ struct esoc_eng {
* struct esoc_clink: Representation of external esoc device
* @name: Name of the external esoc.
* @link_name: name of the physical link.
+ * @link_info: additional info about the physical link.
* @parent: parent device.
* @dev: device for userspace interface.
* @id: id of the external device.
@@ -62,6 +63,7 @@ struct esoc_eng {
struct esoc_clink {
const char *name;
const char *link_name;
+ const char *link_info;
struct device *parent;
struct device dev;
unsigned int id;
diff --git a/drivers/esoc/esoc_bus.c b/drivers/esoc/esoc_bus.c
index 110a5dabed10..f925607511ba 100644
--- a/drivers/esoc/esoc_bus.c
+++ b/drivers/esoc/esoc_bus.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -32,10 +32,19 @@ esoc_link_show(struct device *dev, struct device_attribute *attr,
to_esoc_clink(dev)->link_name);
}
+static ssize_t
+esoc_link_info_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, ESOC_LINK_LEN, "%s",
+ to_esoc_clink(dev)->link_info);
+}
+
static struct device_attribute esoc_clink_attrs[] = {
__ATTR_RO(esoc_name),
__ATTR_RO(esoc_link),
+ __ATTR_RO(esoc_link_info),
__ATTR_NULL,
};
@@ -129,7 +138,7 @@ struct esoc_clink *get_esoc_clink(int id)
struct device *dev;
dev = bus_find_device(&esoc_bus_type, NULL, &id, esoc_clink_match_id);
- if (IS_ERR(dev))
+ if (IS_ERR_OR_NULL(dev))
return NULL;
esoc_clink = to_esoc_clink(dev);
return esoc_clink;
@@ -143,7 +152,7 @@ struct esoc_clink *get_esoc_clink_by_node(struct device_node *node)
dev = bus_find_device(&esoc_bus_type, NULL, node,
esoc_clink_match_node);
- if (IS_ERR(dev))
+ if (IS_ERR_OR_NULL(dev))
return NULL;
esoc_clink = to_esoc_clink(dev);
return esoc_clink;
@@ -175,14 +184,14 @@ int esoc_clink_register_ssr(struct esoc_clink *esoc_clink)
len = strlen("esoc") + sizeof(esoc_clink->id);
subsys_name = kzalloc(len, GFP_KERNEL);
- if (IS_ERR(subsys_name))
+ if (IS_ERR_OR_NULL(subsys_name))
return PTR_ERR(subsys_name);
snprintf(subsys_name, len, "esoc%d", esoc_clink->id);
esoc_clink->subsys.name = subsys_name;
esoc_clink->dev.of_node = esoc_clink->np;
esoc_clink->subsys.dev = &esoc_clink->dev;
esoc_clink->subsys_dev = subsys_register(&esoc_clink->subsys);
- if (IS_ERR(esoc_clink->subsys_dev)) {
+ if (IS_ERR_OR_NULL(esoc_clink->subsys_dev)) {
dev_err(&esoc_clink->dev, "failed to register ssr node\n");
ret = PTR_ERR(esoc_clink->subsys_dev);
goto subsys_err;
diff --git a/drivers/esoc/esoc_client.c b/drivers/esoc/esoc_client.c
index e9932ea3e964..0fe7a83cc3c2 100644
--- a/drivers/esoc/esoc_client.c
+++ b/drivers/esoc/esoc_client.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -43,7 +43,7 @@ struct esoc_desc *devm_register_esoc_client(struct device *dev,
struct device_node *np = dev->of_node;
struct esoc_clink *esoc_clink;
struct esoc_desc *desc;
- char *esoc_name, *esoc_link;
+ char *esoc_name, *esoc_link, *esoc_link_info;
for (index = 0;; index++) {
esoc_prop = kasprintf(GFP_KERNEL, "esoc-%d", index);
@@ -85,16 +85,26 @@ struct esoc_desc *devm_register_esoc_client(struct device *dev,
kfree(esoc_name);
return ERR_PTR(-ENOMEM);
}
+ esoc_link_info = kasprintf(GFP_KERNEL, "%s",
+ esoc_clink->link_info);
+ if (IS_ERR_OR_NULL(esoc_link_info)) {
+ dev_err(dev, "unable to alloc link info name\n");
+ kfree(esoc_name);
+ kfree(esoc_link);
+ return ERR_PTR(-ENOMEM);
+ }
desc = devres_alloc(devm_esoc_desc_release,
sizeof(*desc), GFP_KERNEL);
if (IS_ERR_OR_NULL(desc)) {
kfree(esoc_name);
kfree(esoc_link);
+ kfree(esoc_link_info);
dev_err(dev, "unable to allocate esoc descriptor\n");
return ERR_PTR(-ENOMEM);
}
desc->name = esoc_name;
desc->link = esoc_link;
+ desc->link_info = esoc_link_info;
desc->priv = esoc_clink;
devres_add(dev, desc);
return desc;
diff --git a/drivers/esoc/esoc_dev.c b/drivers/esoc/esoc_dev.c
index 56b767b29093..26b8d0fe512b 100644
--- a/drivers/esoc/esoc_dev.c
+++ b/drivers/esoc/esoc_dev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -260,7 +260,16 @@ static int esoc_dev_open(struct inode *inode, struct file *file)
unsigned int minor = iminor(inode);
esoc_udev = esoc_udev_get_by_minor(minor);
+ if (!esoc_udev) {
+ pr_err("failed to get udev\n");
+ return -ENOMEM;
+ }
+
esoc_clink = get_esoc_clink(esoc_udev->clink->id);
+ if (!esoc_clink) {
+ pr_err("failed to get clink\n");
+ return -ENOMEM;
+ }
uhandle = kzalloc(sizeof(*uhandle), GFP_KERNEL);
if (!uhandle) {
@@ -306,12 +315,12 @@ int esoc_clink_add_device(struct device *dev, void *dummy)
struct esoc_clink *esoc_clink = to_esoc_clink(dev);
esoc_udev = get_free_esoc_udev(esoc_clink);
- if (IS_ERR(esoc_udev))
+ if (IS_ERR_OR_NULL(esoc_udev))
return PTR_ERR(esoc_udev);
esoc_udev->dev = device_create(esoc_class, &esoc_clink->dev,
MKDEV(esoc_major, esoc_clink->id),
esoc_clink, "esoc-%d", esoc_clink->id);
- if (IS_ERR(esoc_udev->dev)) {
+ if (IS_ERR_OR_NULL(esoc_udev->dev)) {
pr_err("failed to create user device\n");
goto dev_err;
}
@@ -358,7 +367,7 @@ int __init esoc_dev_init(void)
{
int ret = 0;
esoc_class = class_create(THIS_MODULE, "esoc-dev");
- if (IS_ERR(esoc_class)) {
+ if (IS_ERR_OR_NULL(esoc_class)) {
pr_err("coudn't create class");
return PTR_ERR(esoc_class);
}
diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
index 9e15d571b53c..a76c35fc0b92 100644
--- a/drivers/firmware/efi/arm-init.c
+++ b/drivers/firmware/efi/arm-init.c
@@ -203,7 +203,19 @@ void __init efi_init(void)
reserve_regions();
early_memunmap(memmap.map, params.mmap_size);
- memblock_mark_nomap(params.mmap & PAGE_MASK,
- PAGE_ALIGN(params.mmap_size +
- (params.mmap & ~PAGE_MASK)));
+
+ if (IS_ENABLED(CONFIG_ARM)) {
+ /*
+ * ARM currently does not allow ioremap_cache() to be called on
+ * memory regions that are covered by struct page. So remove the
+ * UEFI memory map from the linear mapping.
+ */
+ memblock_mark_nomap(params.mmap & PAGE_MASK,
+ PAGE_ALIGN(params.mmap_size +
+ (params.mmap & ~PAGE_MASK)));
+ } else {
+ memblock_reserve(params.mmap & PAGE_MASK,
+ PAGE_ALIGN(params.mmap_size +
+ (params.mmap & ~PAGE_MASK)));
+ }
}
diff --git a/drivers/firmware/qcom/tz_log.c b/drivers/firmware/qcom/tz_log.c
index bf3a24b3eb01..fa90330db93b 100644
--- a/drivers/firmware/qcom/tz_log.c
+++ b/drivers/firmware/qcom/tz_log.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2015,2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -58,6 +58,15 @@
*/
#define QSEE_VERSION_TZ_3_X 0x800000
/*
+ * TZ 4.X version info
+ */
+#define QSEE_VERSION_TZ_4_X 0x1000000
+
+#define TZBSP_AES_256_ENCRYPTED_KEY_SIZE 256
+#define TZBSP_NONCE_LEN 12
+#define TZBSP_TAG_LEN 16
+
+/*
* VMID Table
*/
struct tzdbg_vmid_t {
@@ -126,6 +135,26 @@ struct tzdbg_int_t {
};
/*
+ * Interrupt Info Table used in tz version >=4.X
+ */
+struct tzdbg_int_t_tz40 {
+ uint16_t int_info;
+ uint8_t avail;
+ uint8_t spare;
+ uint32_t int_num;
+ uint8_t int_desc[TZBSP_MAX_INT_DESC];
+ uint32_t int_count[TZBSP_MAX_CPU_COUNT]; /* uint32_t in TZ ver >= 4.x*/
+};
+
+/* warm boot reason for cores */
+struct tzbsp_diag_wakeup_info_t {
+ /* Wake source info : APCS_GICC_HPPIR */
+ uint32_t HPPIR;
+ /* Wake source info : APCS_GICC_AHPPIR */
+ uint32_t AHPPIR;
+};
+
+/*
* Log ring buffer position
*/
struct tzdbg_log_pos_t {
@@ -179,6 +208,10 @@ struct tzdbg_t {
* Ring Buffer Length
*/
uint32_t ring_len;
+
+ /* Offset for Wakeup info */
+ uint32_t wakeup_info_off;
+
/*
* VMID to EE Mapping
*/
@@ -193,6 +226,16 @@ struct tzdbg_t {
struct tzdbg_reset_info_t reset_info[TZBSP_MAX_CPU_COUNT];
uint32_t num_interrupts;
struct tzdbg_int_t int_info[TZBSP_DIAG_INT_NUM];
+
+ /* Wake up info */
+ struct tzbsp_diag_wakeup_info_t wakeup_info[TZBSP_MAX_CPU_COUNT];
+
+ uint8_t key[TZBSP_AES_256_ENCRYPTED_KEY_SIZE];
+
+ uint8_t nonce[TZBSP_NONCE_LEN];
+
+ uint8_t tag[TZBSP_TAG_LEN];
+
/*
* We need at least 2K for the ring buffer
*/
@@ -264,6 +307,7 @@ struct tzdbg {
struct tzdbg_stat stat[TZDBG_STATS_MAX];
uint32_t hyp_debug_rw_buf_size;
bool is_hyplog_enabled;
+ uint32_t tz_version;
};
static struct tzdbg tzdbg = {
@@ -337,31 +381,9 @@ static int _disp_tz_boot_stats(void)
int len = 0;
struct tzdbg_boot_info_t *ptr = NULL;
struct tzdbg_boot_info64_t *ptr_64 = NULL;
- int ret = 0;
- uint32_t smc_id = 0;
- uint32_t feature = 10;
- struct qseecom_command_scm_resp resp = {};
- struct scm_desc desc = {0};
-
- if (!is_scm_armv8()) {
- ret = scm_call(SCM_SVC_INFO, SCM_SVC_UTIL, &feature,
- sizeof(feature), &resp, sizeof(resp));
- } else {
- smc_id = TZ_INFO_GET_FEATURE_VERSION_ID;
- desc.arginfo = TZ_INFO_GET_FEATURE_VERSION_ID_PARAM_ID;
- desc.args[0] = feature;
- ret = scm_call2(smc_id, &desc);
- resp.result = desc.ret[0];
- }
- if (ret) {
- pr_err("%s: scm_call to register log buffer failed\n",
- __func__);
- return 0;
- }
- pr_info("qsee_version = 0x%x\n", resp.result);
-
- if (resp.result >= QSEE_VERSION_TZ_3_X) {
+ pr_info("qsee_version = 0x%x\n", tzdbg.tz_version);
+ if (tzdbg.tz_version >= QSEE_VERSION_TZ_3_X) {
ptr_64 = (struct tzdbg_boot_info64_t *)((unsigned char *)
tzdbg.diag_buf + tzdbg.diag_buf->boot_info_off);
} else {
@@ -370,7 +392,7 @@ static int _disp_tz_boot_stats(void)
}
for (i = 0; i < tzdbg.diag_buf->cpu_count; i++) {
- if (resp.result >= QSEE_VERSION_TZ_3_X) {
+ if (tzdbg.tz_version >= QSEE_VERSION_TZ_3_X) {
len += snprintf(tzdbg.disp_buf + len,
(debug_rw_buf_size - 1) - len,
" CPU #: %d\n"
@@ -460,6 +482,7 @@ static int _disp_tz_interrupt_stats(void)
int *num_int;
unsigned char *ptr;
struct tzdbg_int_t *tzdbg_ptr;
+ struct tzdbg_int_t_tz40 *tzdbg_ptr_tz40;
num_int = (uint32_t *)((unsigned char *)tzdbg.diag_buf +
(tzdbg.diag_buf->int_info_off - sizeof(uint32_t)));
@@ -468,9 +491,12 @@ static int _disp_tz_interrupt_stats(void)
int_info_size = ((tzdbg.diag_buf->ring_off -
tzdbg.diag_buf->int_info_off)/(*num_int));
- for (i = 0; i < (*num_int); i++) {
- tzdbg_ptr = (struct tzdbg_int_t *)ptr;
- len += snprintf(tzdbg.disp_buf + len,
+ pr_info("qsee_version = 0x%x\n", tzdbg.tz_version);
+
+ if (tzdbg.tz_version < QSEE_VERSION_TZ_4_X) {
+ for (i = 0; i < (*num_int); i++) {
+ tzdbg_ptr = (struct tzdbg_int_t *)ptr;
+ len += snprintf(tzdbg.disp_buf + len,
(debug_rw_buf_size - 1) - len,
" Interrupt Number : 0x%x\n"
" Type of Interrupt : 0x%x\n"
@@ -478,24 +504,53 @@ static int _disp_tz_interrupt_stats(void)
tzdbg_ptr->int_num,
(uint32_t)tzdbg_ptr->int_info,
(uint8_t *)tzdbg_ptr->int_desc);
- for (j = 0; j < tzdbg.diag_buf->cpu_count; j++) {
- len += snprintf(tzdbg.disp_buf + len,
+ for (j = 0; j < tzdbg.diag_buf->cpu_count; j++) {
+ len += snprintf(tzdbg.disp_buf + len,
(debug_rw_buf_size - 1) - len,
" int_count on CPU # %d : %u\n",
(uint32_t)j,
(uint32_t)tzdbg_ptr->int_count[j]);
- }
- len += snprintf(tzdbg.disp_buf + len, debug_rw_buf_size - 1,
- "\n");
+ }
+ len += snprintf(tzdbg.disp_buf + len,
+ debug_rw_buf_size - 1, "\n");
- if (len > (debug_rw_buf_size - 1)) {
- pr_warn("%s: Cannot fit all info into the buffer\n",
+ if (len > (debug_rw_buf_size - 1)) {
+ pr_warn("%s: Cannot fit all info into buf\n",
__func__);
- break;
+ break;
+ }
+ ptr += int_info_size;
}
+ } else {
+ for (i = 0; i < (*num_int); i++) {
+ tzdbg_ptr_tz40 = (struct tzdbg_int_t_tz40 *)ptr;
+ len += snprintf(tzdbg.disp_buf + len,
+ (debug_rw_buf_size - 1) - len,
+ " Interrupt Number : 0x%x\n"
+ " Type of Interrupt : 0x%x\n"
+ " Description of interrupt : %s\n",
+ tzdbg_ptr_tz40->int_num,
+ (uint32_t)tzdbg_ptr_tz40->int_info,
+ (uint8_t *)tzdbg_ptr_tz40->int_desc);
+ for (j = 0; j < tzdbg.diag_buf->cpu_count; j++) {
+ len += snprintf(tzdbg.disp_buf + len,
+ (debug_rw_buf_size - 1) - len,
+ " int_count on CPU # %d : %u\n",
+ (uint32_t)j,
+ (uint32_t)tzdbg_ptr_tz40->int_count[j]);
+ }
+ len += snprintf(tzdbg.disp_buf + len,
+ debug_rw_buf_size - 1, "\n");
- ptr += int_info_size;
+ if (len > (debug_rw_buf_size - 1)) {
+ pr_warn("%s: Cannot fit all info into buf\n",
+ __func__);
+ break;
+ }
+ ptr += int_info_size;
+ }
}
+
tzdbg.stat[TZDBG_INTERRUPT].data = tzdbg.disp_buf;
return len;
}
@@ -731,10 +786,16 @@ static ssize_t tzdbgfs_read(struct file *file, char __user *buf,
int len = 0;
int *tz_id = file->private_data;
- memcpy_fromio((void *)tzdbg.diag_buf, tzdbg.virt_iobase,
+ if (*tz_id == TZDBG_BOOT || *tz_id == TZDBG_RESET ||
+ *tz_id == TZDBG_INTERRUPT || *tz_id == TZDBG_GENERAL ||
+ *tz_id == TZDBG_VMID || *tz_id == TZDBG_LOG)
+ memcpy_fromio((void *)tzdbg.diag_buf, tzdbg.virt_iobase,
debug_rw_buf_size);
- memcpy_fromio((void *)tzdbg.hyp_diag_buf, tzdbg.hyp_virt_iobase,
+
+ if (*tz_id == TZDBG_HYP_GENERAL || *tz_id == TZDBG_HYP_LOG)
+ memcpy_fromio((void *)tzdbg.hyp_diag_buf, tzdbg.hyp_virt_iobase,
tzdbg.hyp_debug_rw_buf_size);
+
switch (*tz_id) {
case TZDBG_BOOT:
len = _disp_tz_boot_stats();
@@ -982,6 +1043,33 @@ static int __update_hypdbg_base(struct platform_device *pdev,
return 0;
}
+static void tzdbg_get_tz_version(void)
+{
+ uint32_t smc_id = 0;
+ uint32_t feature = 10;
+ struct qseecom_command_scm_resp resp = {0};
+ struct scm_desc desc = {0};
+ int ret = 0;
+
+ if (!is_scm_armv8()) {
+ ret = scm_call(SCM_SVC_INFO, SCM_SVC_UTIL, &feature,
+ sizeof(feature), &resp, sizeof(resp));
+ } else {
+ smc_id = TZ_INFO_GET_FEATURE_VERSION_ID;
+ desc.arginfo = TZ_INFO_GET_FEATURE_VERSION_ID_PARAM_ID;
+ desc.args[0] = feature;
+ ret = scm_call2(smc_id, &desc);
+ resp.result = desc.ret[0];
+ }
+
+ if (ret)
+ pr_err("%s: scm_call to get tz version failed\n",
+ __func__);
+ else
+ tzdbg.tz_version = resp.result;
+
+}
+
/*
* Driver functions
*/
@@ -1070,6 +1158,9 @@ static int tz_log_probe(struct platform_device *pdev)
goto err;
tzdbg_register_qsee_log_buf();
+
+ tzdbg_get_tz_version();
+
return 0;
err:
kfree(tzdbg.diag_buf);
@@ -1100,6 +1191,7 @@ static struct platform_driver tz_log_driver = {
.name = "tz_log",
.owner = THIS_MODULE,
.of_match_table = tzlog_match,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
index c161eeda417b..267749a94c5a 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
@@ -3704,9 +3704,15 @@ static void dce_v11_0_encoder_add(struct amdgpu_device *adev,
default:
encoder->possible_crtcs = 0x3;
break;
+ case 3:
+ encoder->possible_crtcs = 0x7;
+ break;
case 4:
encoder->possible_crtcs = 0xf;
break;
+ case 5:
+ encoder->possible_crtcs = 0x1f;
+ break;
case 6:
encoder->possible_crtcs = 0x3f;
break;
diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c
index e0b4586a26fd..9b8f0b975ca6 100644
--- a/drivers/gpu/drm/ast/ast_main.c
+++ b/drivers/gpu/drm/ast/ast_main.c
@@ -223,7 +223,8 @@ static int ast_get_dram_info(struct drm_device *dev)
ast_write32(ast, 0x10000, 0xfc600309);
do {
- ;
+ if (pci_channel_offline(dev->pdev))
+ return -EIO;
} while (ast_read32(ast, 0x10000) != 0x01);
data = ast_read32(ast, 0x10004);
@@ -429,7 +430,9 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags)
ast_detect_chip(dev, &need_post);
if (ast->chip != AST1180) {
- ast_get_dram_info(dev);
+ ret = ast_get_dram_info(dev);
+ if (ret)
+ goto out_free;
ast->vram_size = ast_get_vram_info(dev);
DRM_INFO("dram %d %d %d %08x\n", ast->mclk, ast->dram_type, ast->dram_bus_width, ast->vram_size);
}
diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c
index 810c51d92b99..30672a3df8a9 100644
--- a/drivers/gpu/drm/ast/ast_post.c
+++ b/drivers/gpu/drm/ast/ast_post.c
@@ -58,13 +58,9 @@ bool ast_is_vga_enabled(struct drm_device *dev)
/* TODO 1180 */
} else {
ch = ast_io_read8(ast, AST_IO_VGA_ENABLE_PORT);
- if (ch) {
- ast_open_key(ast);
- ch = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff);
- return ch & 0x04;
- }
+ return !!(ch & 0x01);
}
- return 0;
+ return false;
}
static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff };
@@ -375,8 +371,8 @@ void ast_post_gpu(struct drm_device *dev)
pci_write_config_dword(ast->dev->pdev, 0x04, reg);
ast_enable_vga(dev);
- ast_enable_mmio(dev);
ast_open_key(ast);
+ ast_enable_mmio(dev);
ast_set_def_ext_reg(dev);
if (ast->chip == AST2300 || ast->chip == AST2400)
@@ -1630,12 +1626,44 @@ static void ast_init_dram_2300(struct drm_device *dev)
temp |= 0x73;
ast_write32(ast, 0x12008, temp);
+ param.dram_freq = 396;
param.dram_type = AST_DDR3;
+ temp = ast_mindwm(ast, 0x1e6e2070);
if (temp & 0x01000000)
param.dram_type = AST_DDR2;
- param.dram_chipid = ast->dram_type;
- param.dram_freq = ast->mclk;
- param.vram_size = ast->vram_size;
+ switch (temp & 0x18000000) {
+ case 0:
+ param.dram_chipid = AST_DRAM_512Mx16;
+ break;
+ default:
+ case 0x08000000:
+ param.dram_chipid = AST_DRAM_1Gx16;
+ break;
+ case 0x10000000:
+ param.dram_chipid = AST_DRAM_2Gx16;
+ break;
+ case 0x18000000:
+ param.dram_chipid = AST_DRAM_4Gx16;
+ break;
+ }
+ switch (temp & 0x0c) {
+ default:
+ case 0x00:
+ param.vram_size = AST_VIDMEM_SIZE_8M;
+ break;
+
+ case 0x04:
+ param.vram_size = AST_VIDMEM_SIZE_16M;
+ break;
+
+ case 0x08:
+ param.vram_size = AST_VIDMEM_SIZE_32M;
+ break;
+
+ case 0x0c:
+ param.vram_size = AST_VIDMEM_SIZE_64M;
+ break;
+ }
if (param.dram_type == AST_DDR3) {
get_ddr3_info(ast, &param);
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 1ac29d703c12..ea443fafb934 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -265,7 +265,7 @@ mode_fixup(struct drm_atomic_state *state)
struct drm_connector *connector;
struct drm_connector_state *conn_state;
int i;
- bool ret;
+ int ret;
for_each_crtc_in_state(state, crtc, crtc_state, i) {
if (!crtc_state->mode_changed &&
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 7cb2815e815e..a3b96d691ac9 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -1812,7 +1812,7 @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr)
mgr->payloads[i].num_slots = req_payload.num_slots;
} else if (mgr->payloads[i].num_slots) {
mgr->payloads[i].num_slots = 0;
- drm_dp_destroy_payload_step1(mgr, port, port->vcpi.vcpi, &mgr->payloads[i]);
+ drm_dp_destroy_payload_step1(mgr, port, mgr->payloads[i].vcpi, &mgr->payloads[i]);
req_payload.payload_state = mgr->payloads[i].payload_state;
mgr->payloads[i].start_slot = 0;
}
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 8c9ac021608f..cc1e16fd7e76 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -144,6 +144,9 @@ static struct edid_quirk {
/* Panel in Samsung NP700G7A-S01PL notebook reports 6bpc */
{ "SEC", 0xd033, EDID_QUIRK_FORCE_8BPC },
+
+ /* Rotel RSX-1058 forwards sink's EDID but only does HDMI 1.1*/
+ { "ETR", 13896, EDID_QUIRK_FORCE_8BPC },
};
/*
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 39e30abddf08..71a10f08522e 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -1401,6 +1401,13 @@ drm_mode_create_from_cmdline_mode(struct drm_device *dev,
return NULL;
mode->type |= DRM_MODE_TYPE_USERDEF;
+ /* fix up 1368x768: GFT/CVT can't express 1366 width due to alignment */
+ if (cmd->xres == 1366 && mode->hdisplay == 1368) {
+ mode->hdisplay = 1366;
+ mode->hsync_start--;
+ mode->hsync_end--;
+ drm_mode_set_name(mode);
+ }
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
return mode;
}
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index 92e7e5795398..db98ab5cde3d 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -484,6 +484,9 @@ static const struct file_operations psb_gem_fops = {
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = psb_unlocked_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.mmap = drm_gem_mmap,
.poll = drm_poll,
.read = drm_read,
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 97d1ed20418b..63fea6a2869c 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -445,6 +445,7 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector)
struct drm_i915_private *dev_priv = crt->base.base.dev->dev_private;
struct edid *edid;
struct i2c_adapter *i2c;
+ bool ret = false;
BUG_ON(crt->base.type != INTEL_OUTPUT_ANALOG);
@@ -461,17 +462,17 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector)
*/
if (!is_digital) {
DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n");
- return true;
+ ret = true;
+ } else {
+ DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n");
}
-
- DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n");
} else {
DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [no valid EDID found]\n");
}
kfree(edid);
- return false;
+ return ret;
}
static enum drm_connector_status
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 909d1d71d130..4f5d07bb3511 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -3948,10 +3948,10 @@ static void page_flip_completed(struct intel_crtc *intel_crtc)
drm_crtc_vblank_put(&intel_crtc->base);
wake_up_all(&dev_priv->pending_flip_queue);
- queue_work(dev_priv->wq, &work->work);
-
trace_i915_flip_complete(intel_crtc->plane,
work->pending_flip_obj);
+
+ queue_work(dev_priv->wq, &work->work);
}
void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 3f802163f7d4..e7c18519274a 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -6803,7 +6803,18 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
static void vlv_init_display_clock_gating(struct drm_i915_private *dev_priv)
{
- I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE);
+ u32 val;
+
+ /*
+ * On driver load, a pipe may be active and driving a DSI display.
+ * Preserve DPOUNIT_CLOCK_GATE_DISABLE to avoid the pipe getting stuck
+ * (and never recovering) in this case. intel_dsi_post_disable() will
+ * clear it when we turn off the display.
+ */
+ val = I915_READ(DSPCLK_GATE_D);
+ val &= DPOUNIT_CLOCK_GATE_DISABLE;
+ val |= VRHUNIT_CLOCK_GATE_DISABLE;
+ I915_WRITE(DSPCLK_GATE_D, val);
/*
* Disable trickle feed and enable pnd deadline calculation
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index f821a81c53a6..532ff8677259 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -1683,7 +1683,7 @@ static struct drm_driver msm_driver = {
.debugfs_cleanup = msm_debugfs_cleanup,
#endif
.ioctls = msm_ioctls,
- .num_ioctls = DRM_MSM_NUM_IOCTLS,
+ .num_ioctls = ARRAY_SIZE(msm_ioctls),
.fops = &fops,
.name = "msm_drm",
.desc = "MSM Snapdragon DRM",
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index 63128d11767e..d1455fbc980e 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -750,7 +750,10 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev,
size = PAGE_ALIGN(size);
+ mutex_lock(&dev->struct_mutex);
ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj);
+ mutex_unlock(&dev->struct_mutex);
+
if (ret)
goto fail;
diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c
index 7ca96831a9b3..a227f1ba0573 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -17,7 +17,7 @@
#include "msm_drv.h"
#include "msm_gem.h"
-#include "msm_mmu.h"
+#include "msm_iommu.h"
static void
msm_gem_address_space_destroy(struct kref *kref)
@@ -149,6 +149,9 @@ static int iommu_aspace_map_vma(struct msm_gem_address_space *aspace,
if (flags & MSM_BO_PRIVILEGED)
iommu_flags |= IOMMU_PRIV;
+ if ((flags & MSM_BO_CACHED) && msm_iommu_coherent(aspace->mmu))
+ iommu_flags |= IOMMU_CACHE;
+
if (WARN_ON(drm_mm_node_allocated(&vma->node)))
return 0;
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 3176f301e7a8..14c0cfc58270 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -576,8 +576,7 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
if (submit->bos[i].flags & MSM_SUBMIT_BO_READ)
msm_gem_move_to_active(&msm_obj->base, gpu, false, submit->fence);
-
- if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE)
+ else if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE)
msm_gem_move_to_active(&msm_obj->base, gpu, true, submit->fence);
}
diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c
index 3c16222b8890..3af24646f4f1 100644
--- a/drivers/gpu/drm/msm/msm_iommu.c
+++ b/drivers/gpu/drm/msm/msm_iommu.c
@@ -16,6 +16,7 @@
*/
#include <linux/of_platform.h>
+#include <linux/of_address.h>
#include "msm_drv.h"
#include "msm_iommu.h"
@@ -126,6 +127,9 @@ static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, int cnt)
if (ret)
iommu->allow_dynamic = false;
+ /* Mark the GPU as I/O coherent if it is supported */
+ iommu->is_coherent = of_dma_is_coherent(mmu->dev->of_node);
+
/* Attach the device to the domain */
ret = _attach_iommu_device(mmu, iommu->domain, names, cnt);
if (ret)
@@ -312,6 +316,7 @@ struct msm_mmu *msm_iommu_new_dynamic(struct msm_mmu *base)
struct iommu_domain *domain;
struct msm_mmu *mmu;
int ret, val = 1;
+ struct msm_iommu *child_iommu;
/* Don't continue if the base domain didn't have the support we need */
if (!base || base_iommu->allow_dynamic == false)
@@ -339,5 +344,9 @@ struct msm_mmu *msm_iommu_new_dynamic(struct msm_mmu *base)
iommu_domain_set_attr(domain, DOMAIN_ATTR_CONTEXT_BANK,
&base_iommu->cb);
+ /* Mark the dynamic domain as I/O coherent if the base domain is */
+ child_iommu = to_msm_iommu(mmu);
+ child_iommu->is_coherent = base_iommu->is_coherent;
+
return mmu;
}
diff --git a/drivers/gpu/drm/msm/msm_iommu.h b/drivers/gpu/drm/msm/msm_iommu.h
index d005cfb9758f..3a67b60ad81d 100644
--- a/drivers/gpu/drm/msm/msm_iommu.h
+++ b/drivers/gpu/drm/msm/msm_iommu.h
@@ -25,6 +25,8 @@ struct msm_iommu {
struct clk *clocks[5];
int nr_clocks;
+
+ bool is_coherent;
};
#define to_msm_iommu(x) container_of(x, struct msm_iommu, base)
@@ -34,4 +36,11 @@ static inline bool msm_iommu_allow_dynamic(struct msm_mmu *mmu)
return iommu->allow_dynamic;
}
+
+static inline bool msm_iommu_coherent(struct msm_mmu *mmu)
+{
+ struct msm_iommu *iommu = to_msm_iommu(mmu);
+
+ return iommu->is_coherent;
+}
#endif
diff --git a/drivers/gpu/drm/nouveau/dispnv04/hw.c b/drivers/gpu/drm/nouveau/dispnv04/hw.c
index 956a833b8200..57c7389feee4 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/hw.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/hw.c
@@ -222,6 +222,7 @@ nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype)
uint32_t mpllP;
pci_read_config_dword(pci_get_bus_and_slot(0, 3), 0x6c, &mpllP);
+ mpllP = (mpllP >> 8) & 0xf;
if (!mpllP)
mpllP = 4;
@@ -232,7 +233,7 @@ nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype)
uint32_t clock;
pci_read_config_dword(pci_get_bus_and_slot(0, 5), 0x4c, &clock);
- return clock;
+ return clock / 1000;
}
ret = nouveau_hw_get_pllvals(dev, plltype, &pllvals);
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 4dca65a63b92..af224fafa21f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -333,6 +333,9 @@ get_fp_strap(struct drm_device *dev, struct nvbios *bios)
if (bios->major_version < 5 && bios->data[0x48] & 0x4)
return NVReadVgaCrtc5758(dev, 0, 0xf) & 0xf;
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_MAXWELL)
+ return nvif_rd32(device, 0x001800) & 0x0000000f;
+ else
if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA)
return (nvif_rd32(device, NV_PEXTDEV_BOOT_0) >> 24) & 0xf;
else
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
index bbc9824af6e0..ece9f4102c0e 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
@@ -1833,7 +1833,7 @@ nvf1_chipset = {
.fb = gk104_fb_new,
.fuse = gf100_fuse_new,
.gpio = gk104_gpio_new,
- .i2c = gf119_i2c_new,
+ .i2c = gk104_i2c_new,
.ibus = gk104_ibus_new,
.imem = nv50_instmem_new,
.ltc = gk104_ltc_new,
@@ -1941,7 +1941,7 @@ nv117_chipset = {
.fb = gm107_fb_new,
.fuse = gm107_fuse_new,
.gpio = gk104_gpio_new,
- .i2c = gf119_i2c_new,
+ .i2c = gk104_i2c_new,
.ibus = gk104_ibus_new,
.imem = nv50_instmem_new,
.ltc = gm107_ltc_new,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c
index c1590b746f13..eb58cd7bfbc9 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c
@@ -59,7 +59,7 @@ gt215_hda_eld(NV50_DISP_MTHD_V1)
);
}
for (i = 0; i < size; i++)
- nvkm_wr32(device, 0x61c440 + soff, (i << 8) | args->v0.data[0]);
+ nvkm_wr32(device, 0x61c440 + soff, (i << 8) | args->v0.data[i]);
for (; i < 0x60; i++)
nvkm_wr32(device, 0x61c440 + soff, (i << 8));
nvkm_mask(device, 0x61c448 + soff, 0x80000003, 0x80000003);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c
index e7cbc139c1d4..89976ff4b305 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c
@@ -59,6 +59,7 @@ gf100_fifo_gpfifo_engine_fini(struct nvkm_fifo_chan *base,
struct nvkm_gpuobj *inst = chan->base.inst;
int ret = 0;
+ mutex_lock(&subdev->mutex);
nvkm_wr32(device, 0x002634, chan->base.chid);
if (nvkm_msec(device, 2000,
if (nvkm_rd32(device, 0x002634) == chan->base.chid)
@@ -66,10 +67,12 @@ gf100_fifo_gpfifo_engine_fini(struct nvkm_fifo_chan *base,
) < 0) {
nvkm_error(subdev, "channel %d [%s] kick timeout\n",
chan->base.chid, chan->base.object.client->name);
- ret = -EBUSY;
- if (suspend)
- return ret;
+ ret = -ETIMEDOUT;
}
+ mutex_unlock(&subdev->mutex);
+
+ if (ret && suspend)
+ return ret;
if (offset) {
nvkm_kmap(inst);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c
index 0b817540a9e4..aa1692e5669f 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c
@@ -39,7 +39,9 @@ gk104_fifo_gpfifo_kick(struct gk104_fifo_chan *chan)
struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
struct nvkm_device *device = subdev->device;
struct nvkm_client *client = chan->base.object.client;
+ int ret = 0;
+ mutex_lock(&subdev->mutex);
nvkm_wr32(device, 0x002634, chan->base.chid);
if (nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x002634) & 0x00100000))
@@ -47,10 +49,10 @@ gk104_fifo_gpfifo_kick(struct gk104_fifo_chan *chan)
) < 0) {
nvkm_error(subdev, "channel %d [%s] kick timeout\n",
chan->base.chid, client->name);
- return -EBUSY;
+ ret = -ETIMEDOUT;
}
-
- return 0;
+ mutex_unlock(&subdev->mutex);
+ return ret;
}
static u32
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h
index 212800ecdce9..7d1d3c6b4b72 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h
@@ -12,6 +12,7 @@ struct nvbios_source {
bool rw;
bool ignore_checksum;
bool no_pcir;
+ bool require_checksum;
};
int nvbios_extend(struct nvkm_bios *, u32 length);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c
index b2557e87afdd..7deb81b6dbac 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c
@@ -86,9 +86,12 @@ shadow_image(struct nvkm_bios *bios, int idx, u32 offset, struct shadow *mthd)
nvbios_checksum(&bios->data[image.base], image.size)) {
nvkm_debug(subdev, "%08x: checksum failed\n",
image.base);
- if (mthd->func->rw)
+ if (!mthd->func->require_checksum) {
+ if (mthd->func->rw)
+ score += 1;
score += 1;
- score += 1;
+ } else
+ return 0;
} else {
score += 3;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c
index 8fecb5ff22a0..06572f8ce914 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c
@@ -99,6 +99,7 @@ nvbios_acpi_fast = {
.init = acpi_init,
.read = acpi_read_fast,
.rw = false,
+ .require_checksum = true,
};
const struct nvbios_source
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c
index 85b1464c0194..587c52f08d3f 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c
@@ -47,8 +47,10 @@ nvkm_ltc_tags_clear(struct nvkm_ltc *ltc, u32 first, u32 count)
BUG_ON((first > limit) || (limit >= ltc->num_tags));
+ mutex_lock(&ltc->subdev.mutex);
ltc->func->cbc_clear(ltc, first, limit);
ltc->func->cbc_wait(ltc);
+ mutex_unlock(&ltc->subdev.mutex);
}
int
diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c
index afaf346bd50e..8901228b5d5d 100644
--- a/drivers/gpu/drm/radeon/radeon_cursor.c
+++ b/drivers/gpu/drm/radeon/radeon_cursor.c
@@ -90,6 +90,9 @@ static void radeon_show_cursor(struct drm_crtc *crtc)
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct radeon_device *rdev = crtc->dev->dev_private;
+ if (radeon_crtc->cursor_out_of_bounds)
+ return;
+
if (ASIC_IS_DCE4(rdev)) {
WREG32(EVERGREEN_CUR_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset,
upper_32_bits(radeon_crtc->cursor_addr));
@@ -143,21 +146,25 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y)
int xorigin = 0, yorigin = 0;
int w = radeon_crtc->cursor_width;
+ radeon_crtc->cursor_x = x;
+ radeon_crtc->cursor_y = y;
+
if (ASIC_IS_AVIVO(rdev)) {
/* avivo cursor are offset into the total surface */
x += crtc->x;
y += crtc->y;
}
- DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y);
- if (x < 0) {
+ if (x < 0)
xorigin = min(-x, radeon_crtc->max_cursor_width - 1);
- x = 0;
- }
- if (y < 0) {
+ if (y < 0)
yorigin = min(-y, radeon_crtc->max_cursor_height - 1);
- y = 0;
+
+ if (!ASIC_IS_AVIVO(rdev)) {
+ x += crtc->x;
+ y += crtc->y;
}
+ DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y);
/* fixed on DCE6 and newer */
if (ASIC_IS_AVIVO(rdev) && !ASIC_IS_DCE6(rdev)) {
@@ -180,27 +187,31 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y)
if (i > 1) {
int cursor_end, frame_end;
- cursor_end = x - xorigin + w;
+ cursor_end = x + w;
frame_end = crtc->x + crtc->mode.crtc_hdisplay;
if (cursor_end >= frame_end) {
w = w - (cursor_end - frame_end);
if (!(frame_end & 0x7f))
w--;
- } else {
- if (!(cursor_end & 0x7f))
- w--;
+ } else if (cursor_end <= 0) {
+ goto out_of_bounds;
+ } else if (!(cursor_end & 0x7f)) {
+ w--;
}
if (w <= 0) {
- w = 1;
- cursor_end = x - xorigin + w;
- if (!(cursor_end & 0x7f)) {
- x--;
- WARN_ON_ONCE(x < 0);
- }
+ goto out_of_bounds;
}
}
}
+ if (x <= (crtc->x - w) || y <= (crtc->y - radeon_crtc->cursor_height) ||
+ x >= (crtc->x + crtc->mode.hdisplay) ||
+ y >= (crtc->y + crtc->mode.vdisplay))
+ goto out_of_bounds;
+
+ x += xorigin;
+ y += yorigin;
+
if (ASIC_IS_DCE4(rdev)) {
WREG32(EVERGREEN_CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y);
WREG32(EVERGREEN_CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin);
@@ -212,6 +223,9 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y)
WREG32(AVIVO_D1CUR_SIZE + radeon_crtc->crtc_offset,
((w - 1) << 16) | (radeon_crtc->cursor_height - 1));
} else {
+ x -= crtc->x;
+ y -= crtc->y;
+
if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)
y *= 2;
@@ -229,10 +243,20 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y)
yorigin * 256);
}
- radeon_crtc->cursor_x = x;
- radeon_crtc->cursor_y = y;
+ if (radeon_crtc->cursor_out_of_bounds) {
+ radeon_crtc->cursor_out_of_bounds = false;
+ if (radeon_crtc->cursor_bo)
+ radeon_show_cursor(crtc);
+ }
return 0;
+
+ out_of_bounds:
+ if (!radeon_crtc->cursor_out_of_bounds) {
+ radeon_hide_cursor(crtc);
+ radeon_crtc->cursor_out_of_bounds = true;
+ }
+ return 0;
}
int radeon_crtc_cursor_move(struct drm_crtc *crtc,
@@ -297,22 +321,23 @@ int radeon_crtc_cursor_set2(struct drm_crtc *crtc,
return ret;
}
- radeon_crtc->cursor_width = width;
- radeon_crtc->cursor_height = height;
-
radeon_lock_cursor(crtc, true);
- if (hot_x != radeon_crtc->cursor_hot_x ||
+ if (width != radeon_crtc->cursor_width ||
+ height != radeon_crtc->cursor_height ||
+ hot_x != radeon_crtc->cursor_hot_x ||
hot_y != radeon_crtc->cursor_hot_y) {
int x, y;
x = radeon_crtc->cursor_x + radeon_crtc->cursor_hot_x - hot_x;
y = radeon_crtc->cursor_y + radeon_crtc->cursor_hot_y - hot_y;
- radeon_cursor_move_locked(crtc, x, y);
-
+ radeon_crtc->cursor_width = width;
+ radeon_crtc->cursor_height = height;
radeon_crtc->cursor_hot_x = hot_x;
radeon_crtc->cursor_hot_y = hot_y;
+
+ radeon_cursor_move_locked(crtc, x, y);
}
radeon_show_cursor(crtc);
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 7a0666ac4e23..d8f8be608c19 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -330,6 +330,7 @@ struct radeon_crtc {
u16 lut_r[256], lut_g[256], lut_b[256];
bool enabled;
bool can_tile;
+ bool cursor_out_of_bounds;
uint32_t crtc_offset;
struct drm_gem_object *cursor_bo;
uint64_t cursor_addr;
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index 10191b935937..b6f16804e73b 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -3008,24 +3008,12 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev,
(rdev->pdev->device == 0x6817) ||
(rdev->pdev->device == 0x6806))
max_mclk = 120000;
- } else if (rdev->family == CHIP_VERDE) {
- if ((rdev->pdev->revision == 0x81) ||
- (rdev->pdev->revision == 0x83) ||
- (rdev->pdev->revision == 0x87) ||
- (rdev->pdev->device == 0x6820) ||
- (rdev->pdev->device == 0x6821) ||
- (rdev->pdev->device == 0x6822) ||
- (rdev->pdev->device == 0x6823) ||
- (rdev->pdev->device == 0x682A) ||
- (rdev->pdev->device == 0x682B)) {
- max_sclk = 75000;
- max_mclk = 80000;
- }
} else if (rdev->family == CHIP_OLAND) {
if ((rdev->pdev->revision == 0xC7) ||
(rdev->pdev->revision == 0x80) ||
(rdev->pdev->revision == 0x81) ||
(rdev->pdev->revision == 0x83) ||
+ (rdev->pdev->revision == 0x87) ||
(rdev->pdev->device == 0x6604) ||
(rdev->pdev->device == 0x6605)) {
max_sclk = 75000;
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 4ae8b56b1847..037c38bb5333 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -1621,7 +1621,6 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink)
struct ttm_buffer_object *bo;
int ret = -EBUSY;
int put_count;
- uint32_t swap_placement = (TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM);
spin_lock(&glob->lru_lock);
list_for_each_entry(bo, &glob->swap_lru, swap) {
@@ -1657,7 +1656,8 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink)
if (unlikely(ret != 0))
goto out;
- if ((bo->mem.placement & swap_placement) != swap_placement) {
+ if (bo->mem.mem_type != TTM_PL_SYSTEM ||
+ bo->ttm->caching_state != tt_cached) {
struct ttm_mem_reg evict_mem;
evict_mem = bo->mem;
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index b852330d5671..3faa5aaf9d03 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1720,6 +1720,30 @@ static int adreno_getproperty(struct kgsl_device *device,
status = 0;
}
break;
+ case KGSL_PROP_DEVICE_QTIMER:
+ {
+ struct kgsl_qtimer_prop qtimerprop = {0};
+ struct kgsl_memdesc *qtimer_desc =
+ kgsl_mmu_get_qtimer_global_entry(device);
+
+ if (sizebytes != sizeof(qtimerprop)) {
+ status = -EINVAL;
+ break;
+ }
+
+ if (qtimer_desc) {
+ qtimerprop.gpuaddr = qtimer_desc->gpuaddr;
+ qtimerprop.size = qtimer_desc->size;
+ }
+
+ if (copy_to_user(value, &qtimerprop,
+ sizeof(qtimerprop))) {
+ status = -EFAULT;
+ break;
+ }
+ status = 0;
+ }
+ break;
case KGSL_PROP_MMU_ENABLE:
{
/* Report MMU only if we can handle paged memory */
diff --git a/drivers/gpu/msm/adreno_compat.c b/drivers/gpu/msm/adreno_compat.c
index d86a0c60f0b4..5a8d587d4536 100644
--- a/drivers/gpu/msm/adreno_compat.c
+++ b/drivers/gpu/msm/adreno_compat.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -113,6 +113,30 @@ int adreno_getproperty_compat(struct kgsl_device *device,
status = 0;
}
break;
+ case KGSL_PROP_DEVICE_QTIMER:
+ {
+ struct kgsl_qtimer_prop qtimerprop = {0};
+ struct kgsl_memdesc *qtimer_desc =
+ kgsl_mmu_get_qtimer_global_entry(device);
+
+ if (sizebytes != sizeof(qtimerprop)) {
+ status = -EINVAL;
+ break;
+ }
+
+ if (qtimer_desc) {
+ qtimerprop.gpuaddr = qtimer_desc->gpuaddr;
+ qtimerprop.size = qtimer_desc->size;
+ }
+
+ if (copy_to_user(value, &qtimerprop,
+ sizeof(qtimerprop))) {
+ status = -EFAULT;
+ break;
+ }
+ status = 0;
+ }
+ break;
default:
/*
* Call the adreno_getproperty to check if the property type
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 6c667cb62896..af9fc1c15236 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -106,6 +106,7 @@ static struct kgsl_memdesc *kgsl_global_secure_pt_entry;
static int global_pt_count;
uint64_t global_pt_alloc;
static struct kgsl_memdesc gpu_qdss_desc;
+static struct kgsl_memdesc gpu_qtimer_desc;
void kgsl_print_global_pt_entries(struct seq_file *s)
{
@@ -261,6 +262,50 @@ static inline void kgsl_cleanup_qdss_desc(struct kgsl_mmu *mmu)
kgsl_sharedmem_free(&gpu_qdss_desc);
}
+struct kgsl_memdesc *kgsl_iommu_get_qtimer_global_entry(void)
+{
+ return &gpu_qtimer_desc;
+}
+
+static void kgsl_setup_qtimer_desc(struct kgsl_device *device)
+{
+ int result = 0;
+ uint32_t gpu_qtimer_entry[2];
+
+ if (!of_find_property(device->pdev->dev.of_node,
+ "qcom,gpu-qtimer", NULL))
+ return;
+
+ if (of_property_read_u32_array(device->pdev->dev.of_node,
+ "qcom,gpu-qtimer", gpu_qtimer_entry, 2)) {
+ KGSL_CORE_ERR("Failed to read gpu qtimer dts entry\n");
+ return;
+ }
+
+ gpu_qtimer_desc.flags = 0;
+ gpu_qtimer_desc.priv = 0;
+ gpu_qtimer_desc.physaddr = gpu_qtimer_entry[0];
+ gpu_qtimer_desc.size = gpu_qtimer_entry[1];
+ gpu_qtimer_desc.pagetable = NULL;
+ gpu_qtimer_desc.ops = NULL;
+ gpu_qtimer_desc.dev = device->dev->parent;
+ gpu_qtimer_desc.hostptr = NULL;
+
+ result = memdesc_sg_dma(&gpu_qtimer_desc, gpu_qtimer_desc.physaddr,
+ gpu_qtimer_desc.size);
+ if (result) {
+ KGSL_CORE_ERR("memdesc_sg_dma failed: %d\n", result);
+ return;
+ }
+
+ kgsl_mmu_add_global(device, &gpu_qtimer_desc, "gpu-qtimer");
+}
+
+static inline void kgsl_cleanup_qtimer_desc(struct kgsl_mmu *mmu)
+{
+ kgsl_iommu_remove_global(mmu, &gpu_qtimer_desc);
+ kgsl_sharedmem_free(&gpu_qtimer_desc);
+}
static inline void _iommu_sync_mmu_pc(bool lock)
{
@@ -1403,6 +1448,7 @@ static void kgsl_iommu_close(struct kgsl_mmu *mmu)
kgsl_iommu_remove_global(mmu, &iommu->setstate);
kgsl_sharedmem_free(&iommu->setstate);
kgsl_cleanup_qdss_desc(mmu);
+ kgsl_cleanup_qtimer_desc(mmu);
}
static int _setstate_alloc(struct kgsl_device *device,
@@ -1474,6 +1520,7 @@ static int kgsl_iommu_init(struct kgsl_mmu *mmu)
kgsl_iommu_add_global(mmu, &iommu->setstate, "setstate");
kgsl_setup_qdss_desc(device);
+ kgsl_setup_qtimer_desc(device);
done:
if (status)
@@ -2616,6 +2663,7 @@ struct kgsl_mmu_ops kgsl_iommu_ops = {
.mmu_remove_global = kgsl_iommu_remove_global,
.mmu_getpagetable = kgsl_iommu_getpagetable,
.mmu_get_qdss_global_entry = kgsl_iommu_get_qdss_global_entry,
+ .mmu_get_qtimer_global_entry = kgsl_iommu_get_qtimer_global_entry,
.probe = kgsl_iommu_probe,
};
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index 46bb6f4656fb..aa7157e882ac 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -619,6 +619,18 @@ struct kgsl_memdesc *kgsl_mmu_get_qdss_global_entry(struct kgsl_device *device)
}
EXPORT_SYMBOL(kgsl_mmu_get_qdss_global_entry);
+struct kgsl_memdesc *kgsl_mmu_get_qtimer_global_entry(
+ struct kgsl_device *device)
+{
+ struct kgsl_mmu *mmu = &device->mmu;
+
+ if (MMU_OP_VALID(mmu, mmu_get_qtimer_global_entry))
+ return mmu->mmu_ops->mmu_get_qtimer_global_entry();
+
+ return NULL;
+}
+EXPORT_SYMBOL(kgsl_mmu_get_qtimer_global_entry);
+
/*
* NOMMU defintions - NOMMU really just means that the MMU is kept in pass
* through and the GPU directly accesses physical memory. Used in debug mode and
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index bc448d424ccb..505fe591a53e 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -81,6 +81,7 @@ struct kgsl_mmu_ops {
struct kgsl_pagetable * (*mmu_getpagetable)(struct kgsl_mmu *mmu,
unsigned long name);
struct kgsl_memdesc* (*mmu_get_qdss_global_entry)(void);
+ struct kgsl_memdesc* (*mmu_get_qtimer_global_entry)(void);
};
struct kgsl_mmu_pt_ops {
@@ -231,6 +232,9 @@ int kgsl_mmu_unmap_offset(struct kgsl_pagetable *pagetable,
struct kgsl_memdesc *kgsl_mmu_get_qdss_global_entry(struct kgsl_device *device);
+struct kgsl_memdesc *kgsl_mmu_get_qtimer_global_entry(
+ struct kgsl_device *device);
+
int kgsl_mmu_sparse_dummy_map(struct kgsl_pagetable *pagetable,
struct kgsl_memdesc *memdesc, uint64_t offset, uint64_t size);
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 577183bea07c..34ea83d067af 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2011,7 +2011,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
- { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) },
diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c
index bcefb9ebb026..88be56321610 100644
--- a/drivers/hid/hid-corsair.c
+++ b/drivers/hid/hid-corsair.c
@@ -148,26 +148,36 @@ static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev)
struct usb_interface *usbif = to_usb_interface(dev->parent);
struct usb_device *usbdev = interface_to_usbdev(usbif);
int brightness;
- char data[8];
+ char *data;
+
+ data = kmalloc(8, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
K90_REQUEST_STATUS,
USB_DIR_IN | USB_TYPE_VENDOR |
USB_RECIP_DEVICE, 0, 0, data, 8,
USB_CTRL_SET_TIMEOUT);
- if (ret < 0) {
+ if (ret < 5) {
dev_warn(dev, "Failed to get K90 initial state (error %d).\n",
ret);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
brightness = data[4];
if (brightness < 0 || brightness > 3) {
dev_warn(dev,
"Read invalid backlight brightness: %02hhx.\n",
data[4]);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
- return brightness;
+ ret = brightness;
+out:
+ kfree(data);
+
+ return ret;
}
static enum led_brightness k90_record_led_get(struct led_classdev *led_cdev)
@@ -253,17 +263,22 @@ static ssize_t k90_show_macro_mode(struct device *dev,
struct usb_interface *usbif = to_usb_interface(dev->parent);
struct usb_device *usbdev = interface_to_usbdev(usbif);
const char *macro_mode;
- char data[8];
+ char *data;
+
+ data = kmalloc(2, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
K90_REQUEST_GET_MODE,
USB_DIR_IN | USB_TYPE_VENDOR |
USB_RECIP_DEVICE, 0, 0, data, 2,
USB_CTRL_SET_TIMEOUT);
- if (ret < 0) {
+ if (ret < 1) {
dev_warn(dev, "Failed to get K90 initial mode (error %d).\n",
ret);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
switch (data[0]) {
@@ -277,10 +292,15 @@ static ssize_t k90_show_macro_mode(struct device *dev,
default:
dev_warn(dev, "K90 in unknown mode: %02hhx.\n",
data[0]);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
- return snprintf(buf, PAGE_SIZE, "%s\n", macro_mode);
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", macro_mode);
+out:
+ kfree(data);
+
+ return ret;
}
static ssize_t k90_store_macro_mode(struct device *dev,
@@ -320,26 +340,36 @@ static ssize_t k90_show_current_profile(struct device *dev,
struct usb_interface *usbif = to_usb_interface(dev->parent);
struct usb_device *usbdev = interface_to_usbdev(usbif);
int current_profile;
- char data[8];
+ char *data;
+
+ data = kmalloc(8, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
K90_REQUEST_STATUS,
USB_DIR_IN | USB_TYPE_VENDOR |
USB_RECIP_DEVICE, 0, 0, data, 8,
USB_CTRL_SET_TIMEOUT);
- if (ret < 0) {
+ if (ret < 8) {
dev_warn(dev, "Failed to get K90 initial state (error %d).\n",
ret);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
current_profile = data[7];
if (current_profile < 1 || current_profile > 3) {
dev_warn(dev, "Read invalid current profile: %02hhx.\n",
data[7]);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
- return snprintf(buf, PAGE_SIZE, "%d\n", current_profile);
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", current_profile);
+out:
+ kfree(data);
+
+ return ret;
}
static ssize_t k90_store_current_profile(struct device *dev,
diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c
index 1b764d1745f3..1689568b597d 100644
--- a/drivers/hid/hid-cypress.c
+++ b/drivers/hid/hid-cypress.c
@@ -39,6 +39,9 @@ static __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
if (!(quirks & CP_RDESC_SWAPPED_MIN_MAX))
return rdesc;
+ if (*rsize < 4)
+ return rdesc;
+
for (i = 0; i < *rsize - 4; i++)
if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) {
rdesc[i] = 0x19;
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 5c02d7bbc7f2..35e3fd9fadf6 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -148,19 +148,21 @@ static int wacom_pl_irq(struct wacom_wac *wacom)
wacom->id[0] = STYLUS_DEVICE_ID;
}
- pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
- if (features->pressure_max > 255)
- pressure = (pressure << 1) | ((data[4] >> 6) & 1);
- pressure += (features->pressure_max + 1) / 2;
-
- input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
- input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
- input_report_abs(input, ABS_PRESSURE, pressure);
-
- input_report_key(input, BTN_TOUCH, data[4] & 0x08);
- input_report_key(input, BTN_STYLUS, data[4] & 0x10);
- /* Only allow the stylus2 button to be reported for the pen tool. */
- input_report_key(input, BTN_STYLUS2, (wacom->tool[0] == BTN_TOOL_PEN) && (data[4] & 0x20));
+ if (prox) {
+ pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
+ if (features->pressure_max > 255)
+ pressure = (pressure << 1) | ((data[4] >> 6) & 1);
+ pressure += (features->pressure_max + 1) / 2;
+
+ input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
+ input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
+ input_report_abs(input, ABS_PRESSURE, pressure);
+
+ input_report_key(input, BTN_TOUCH, data[4] & 0x08);
+ input_report_key(input, BTN_STYLUS, data[4] & 0x10);
+ /* Only allow the stylus2 button to be reported for the pen tool. */
+ input_report_key(input, BTN_STYLUS2, (wacom->tool[0] == BTN_TOOL_PEN) && (data[4] & 0x20));
+ }
if (!prox)
wacom->id[0] = 0;
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index 63194a9a7189..57c191798699 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -219,7 +219,7 @@ int hv_init(void)
/* See if the hypercall page is already set */
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
- virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC);
+ virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_RX);
if (!virtaddr)
goto cleanup;
@@ -422,7 +422,7 @@ int hv_synic_alloc(void)
goto err;
}
- for_each_online_cpu(cpu) {
+ for_each_present_cpu(cpu) {
hv_context.event_dpc[cpu] = kmalloc(size, GFP_ATOMIC);
if (hv_context.event_dpc[cpu] == NULL) {
pr_err("Unable to allocate event dpc\n");
@@ -461,6 +461,8 @@ int hv_synic_alloc(void)
pr_err("Unable to allocate post msg page\n");
goto err;
}
+
+ INIT_LIST_HEAD(&hv_context.percpu_list[cpu]);
}
return 0;
@@ -485,7 +487,7 @@ void hv_synic_free(void)
int cpu;
kfree(hv_context.hv_numa_map);
- for_each_online_cpu(cpu)
+ for_each_present_cpu(cpu)
hv_synic_free_cpu(cpu);
}
@@ -555,8 +557,6 @@ void hv_synic_init(void *arg)
rdmsrl(HV_X64_MSR_VP_INDEX, vp_index);
hv_context.vp_index[cpu] = (u32)vp_index;
- INIT_LIST_HEAD(&hv_context.percpu_list[cpu]);
-
/*
* Register the per-cpu clockevent source.
*/
diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c
index c37a71e13de0..1fb02dcbc500 100644
--- a/drivers/hv/hv_fcopy.c
+++ b/drivers/hv/hv_fcopy.c
@@ -61,6 +61,7 @@ static DECLARE_WORK(fcopy_send_work, fcopy_send_data);
static const char fcopy_devname[] = "vmbus/hv_fcopy";
static u8 *recv_buffer;
static struct hvutil_transport *hvt;
+static struct completion release_event;
/*
* This state maintains the version number registered by the daemon.
*/
@@ -312,12 +313,14 @@ static void fcopy_on_reset(void)
if (cancel_delayed_work_sync(&fcopy_timeout_work))
fcopy_respond_to_host(HV_E_FAIL);
+ complete(&release_event);
}
int hv_fcopy_init(struct hv_util_service *srv)
{
recv_buffer = srv->recv_buffer;
+ init_completion(&release_event);
/*
* When this driver loads, the user level daemon that
* processes the host requests may not yet be running.
@@ -339,4 +342,5 @@ void hv_fcopy_deinit(void)
fcopy_transaction.state = HVUTIL_DEVICE_DYING;
cancel_delayed_work_sync(&fcopy_timeout_work);
hvutil_transport_destroy(hvt);
+ wait_for_completion(&release_event);
}
diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c
index 2a3420c4ca59..ce4d3a935491 100644
--- a/drivers/hv/hv_kvp.c
+++ b/drivers/hv/hv_kvp.c
@@ -86,6 +86,7 @@ static DECLARE_WORK(kvp_sendkey_work, kvp_send_key);
static const char kvp_devname[] = "vmbus/hv_kvp";
static u8 *recv_buffer;
static struct hvutil_transport *hvt;
+static struct completion release_event;
/*
* Register the kernel component with the user-level daemon.
* As part of this registration, pass the LIC version number.
@@ -682,6 +683,7 @@ static void kvp_on_reset(void)
if (cancel_delayed_work_sync(&kvp_timeout_work))
kvp_respond_to_host(NULL, HV_E_FAIL);
kvp_transaction.state = HVUTIL_DEVICE_INIT;
+ complete(&release_event);
}
int
@@ -689,6 +691,7 @@ hv_kvp_init(struct hv_util_service *srv)
{
recv_buffer = srv->recv_buffer;
+ init_completion(&release_event);
/*
* When this driver loads, the user level daemon that
* processes the host requests may not yet be running.
@@ -711,4 +714,5 @@ void hv_kvp_deinit(void)
cancel_delayed_work_sync(&kvp_timeout_work);
cancel_work_sync(&kvp_sendkey_work);
hvutil_transport_destroy(hvt);
+ wait_for_completion(&release_event);
}
diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c
index 81882d4848bd..faad79ae318a 100644
--- a/drivers/hv/hv_snapshot.c
+++ b/drivers/hv/hv_snapshot.c
@@ -66,6 +66,7 @@ static int dm_reg_value;
static const char vss_devname[] = "vmbus/hv_vss";
static __u8 *recv_buffer;
static struct hvutil_transport *hvt;
+static struct completion release_event;
static void vss_send_op(struct work_struct *dummy);
static void vss_timeout_func(struct work_struct *dummy);
@@ -326,11 +327,13 @@ static void vss_on_reset(void)
if (cancel_delayed_work_sync(&vss_timeout_work))
vss_respond_to_host(HV_E_FAIL);
vss_transaction.state = HVUTIL_DEVICE_INIT;
+ complete(&release_event);
}
int
hv_vss_init(struct hv_util_service *srv)
{
+ init_completion(&release_event);
if (vmbus_proto_version < VERSION_WIN8_1) {
pr_warn("Integration service 'Backup (volume snapshot)'"
" not supported on this host version.\n");
@@ -360,4 +363,5 @@ void hv_vss_deinit(void)
cancel_delayed_work_sync(&vss_timeout_work);
cancel_work_sync(&vss_send_op_work);
hvutil_transport_destroy(hvt);
+ wait_for_completion(&release_event);
}
diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c
index 12e851a5af48..46b4e35fd555 100644
--- a/drivers/hwmon/amc6821.c
+++ b/drivers/hwmon/amc6821.c
@@ -188,8 +188,8 @@ static struct amc6821_data *amc6821_update_device(struct device *dev)
!data->valid) {
for (i = 0; i < TEMP_IDX_LEN; i++)
- data->temp[i] = i2c_smbus_read_byte_data(client,
- temp_reg[i]);
+ data->temp[i] = (int8_t)i2c_smbus_read_byte_data(
+ client, temp_reg[i]);
data->stat1 = i2c_smbus_read_byte_data(client,
AMC6821_REG_STAT1);
diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c
index edf550fc4eef..0043a4c02b85 100644
--- a/drivers/hwmon/ds620.c
+++ b/drivers/hwmon/ds620.c
@@ -166,7 +166,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
if (res)
return res;
- val = (val * 10 / 625) * 8;
+ val = (clamp_val(val, -128000, 128000) * 10 / 625) * 8;
mutex_lock(&data->update_lock);
data->temp[attr->index] = val;
diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c
index b96a2a9e4df7..628be9c95ff9 100644
--- a/drivers/hwmon/g762.c
+++ b/drivers/hwmon/g762.c
@@ -193,14 +193,17 @@ static inline unsigned int rpm_from_cnt(u8 cnt, u32 clk_freq, u16 p,
* Convert fan RPM value from sysfs into count value for fan controller
* register (FAN_SET_CNT).
*/
-static inline unsigned char cnt_from_rpm(u32 rpm, u32 clk_freq, u16 p,
+static inline unsigned char cnt_from_rpm(unsigned long rpm, u32 clk_freq, u16 p,
u8 clk_div, u8 gear_mult)
{
- if (!rpm) /* to stop the fan, set cnt to 255 */
+ unsigned long f1 = clk_freq * 30 * gear_mult;
+ unsigned long f2 = p * clk_div;
+
+ if (!rpm) /* to stop the fan, set cnt to 255 */
return 0xff;
- return clamp_val(((clk_freq * 30 * gear_mult) / (rpm * p * clk_div)),
- 0, 255);
+ rpm = clamp_val(rpm, f1 / (255 * f2), ULONG_MAX / f2);
+ return DIV_ROUND_CLOSEST(f1, rpm * f2);
}
/* helper to grab and cache data, at most one time per second */
diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c
index 3ce33d244cc0..12b94b094c0d 100644
--- a/drivers/hwmon/nct7802.c
+++ b/drivers/hwmon/nct7802.c
@@ -259,13 +259,15 @@ static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low,
ret = 0;
else if (ret)
ret = DIV_ROUND_CLOSEST(1350000U, ret);
+ else
+ ret = 1350000U;
abort:
mutex_unlock(&data->access_lock);
return ret;
}
static int nct7802_write_fan_min(struct nct7802_data *data, u8 reg_fan_low,
- u8 reg_fan_high, unsigned int limit)
+ u8 reg_fan_high, unsigned long limit)
{
int err;
@@ -326,8 +328,8 @@ static int nct7802_write_voltage(struct nct7802_data *data, int nr, int index,
int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr];
int err;
+ voltage = clamp_val(voltage, 0, 0x3ff * nct7802_vmul[nr]);
voltage = DIV_ROUND_CLOSEST(voltage, nct7802_vmul[nr]);
- voltage = clamp_val(voltage, 0, 0x3ff);
mutex_lock(&data->access_lock);
err = regmap_write(data->regmap,
@@ -402,7 +404,7 @@ static ssize_t store_temp(struct device *dev, struct device_attribute *attr,
if (err < 0)
return err;
- val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127);
+ val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000);
err = regmap_write(data->regmap, nr, val & 0xff);
return err ? : count;
diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c
index 7e20567bc369..6827169c82d4 100644
--- a/drivers/hwmon/scpi-hwmon.c
+++ b/drivers/hwmon/scpi-hwmon.c
@@ -272,6 +272,7 @@ static const struct of_device_id scpi_of_match[] = {
{.compatible = "arm,scpi-sensors"},
{},
};
+MODULE_DEVICE_TABLE(of, scpi_of_match);
static struct platform_driver scpi_hwmon_platdrv = {
.driver = {
diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c
index bf2a1dd7cf15..7f98d9f527b9 100644
--- a/drivers/i2c/busses/i2c-msm-v2.c
+++ b/drivers/i2c/busses/i2c-msm-v2.c
@@ -1151,12 +1151,20 @@ static void i2c_msm_dma_xfer_unprepare(struct i2c_msm_ctrl *ctrl)
buf_itr->dma_dir);
}
-static void i2c_msm_dma_callback_xfer_complete(void *dma_async_param)
+static void i2c_msm_dma_callback_tx_complete(void *dma_async_param)
{
struct i2c_msm_ctrl *ctrl = dma_async_param;
+
complete(&ctrl->xfer.complete);
}
+static void i2c_msm_dma_callback_rx_complete(void *dma_async_param)
+{
+ struct i2c_msm_ctrl *ctrl = dma_async_param;
+
+ complete(&ctrl->xfer.rx_complete);
+}
+
/*
* i2c_msm_dma_xfer_process: Queue transfers to DMA
* @pre 1)QUP is in run state. 2) i2c_msm_dma_xfer_prepare() was called.
@@ -1269,14 +1277,16 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl)
}
/* callback defined for tx dma desc */
- dma_desc_tx->callback = i2c_msm_dma_callback_xfer_complete;
+ dma_desc_tx->callback = i2c_msm_dma_callback_tx_complete;
dma_desc_tx->callback_param = ctrl;
dmaengine_submit(dma_desc_tx);
dma_async_issue_pending(tx->dma_chan);
/* queue the rx dma desc */
dma_desc_rx = dmaengine_prep_slave_sg(rx->dma_chan, sg_rx,
- sg_rx_itr - sg_rx, rx->dir, 0);
+ sg_rx_itr - sg_rx, rx->dir,
+ (SPS_IOVEC_FLAG_EOT |
+ SPS_IOVEC_FLAG_NWD));
if (dma_desc_rx < 0) {
dev_err(ctrl->dev,
"error dmaengine_prep_slave_sg rx:%ld\n",
@@ -1285,6 +1295,8 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl)
goto dma_xfer_end;
}
+ dma_desc_rx->callback = i2c_msm_dma_callback_rx_complete;
+ dma_desc_rx->callback_param = ctrl;
dmaengine_submit(dma_desc_rx);
dma_async_issue_pending(rx->dma_chan);
@@ -1297,6 +1309,8 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl)
}
ret = i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.complete);
+ if (!ret && ctrl->xfer.rx_cnt)
+ i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.rx_complete);
dma_xfer_end:
/* free scatter-gather lists */
@@ -2054,13 +2068,14 @@ static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl,
long time_left;
int ret = 0;
- time_left = wait_for_completion_timeout(complete, xfer->timeout);
+ time_left = wait_for_completion_timeout(complete,
+ xfer->timeout);
if (!time_left) {
xfer->err = I2C_MSM_ERR_TIMEOUT;
i2c_msm_dbg_dump_diag(ctrl, false, 0, 0);
ret = -EIO;
i2c_msm_prof_evnt_add(ctrl, MSM_ERR, I2C_MSM_COMPLT_FL,
- xfer->timeout, time_left, 0);
+ xfer->timeout, time_left, 0);
} else {
/* return an error if one detected by ISR */
if (xfer->err)
@@ -2327,6 +2342,8 @@ i2c_msm_frmwrk_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
xfer->tx_ovrhd_cnt = 0;
atomic_set(&xfer->event_cnt, 0);
init_completion(&xfer->complete);
+ init_completion(&xfer->rx_complete);
+
xfer->cur_buf.is_init = false;
xfer->cur_buf.msg_idx = 0;
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index d625167357cc..e4587411b447 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -1400,7 +1400,7 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
if (i2c_check_addr_validity(addr, info.flags)) {
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
- info.addr, node->full_name);
+ addr, node->full_name);
return ERR_PTR(-EINVAL);
}
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index 2413ec9f8207..94c837046786 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -329,7 +329,7 @@ static noinline int i2cdev_ioctl_smbus(struct i2c_client *client,
unsigned long arg)
{
struct i2c_smbus_ioctl_data data_arg;
- union i2c_smbus_data temp;
+ union i2c_smbus_data temp = {};
int datasize, res;
if (copy_from_user(&data_arg,
diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c
index 57145ea72e90..537cca877f66 100644
--- a/drivers/iio/adc/qcom-rradc.c
+++ b/drivers/iio/adc/qcom-rradc.c
@@ -38,6 +38,7 @@
#define FG_ADC_RR_FAKE_BATT_HIGH_MSB 0x5B
#define FG_ADC_RR_BATT_ID_CTRL 0x60
+#define FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV BIT(0)
#define FG_ADC_RR_BATT_ID_TRIGGER 0x61
#define FG_ADC_RR_BATT_ID_TRIGGER_CTL BIT(0)
#define FG_ADC_RR_BATT_ID_STS 0x62
@@ -163,11 +164,6 @@
#define FG_ADC_RR_DIE_TEMP_SLOPE 2
#define FG_ADC_RR_DIE_TEMP_OFFSET_MILLI_DEGC 25000
-#define FAB_ID_GF 0x30
-#define FAB_ID_SMIC 0x11
-#define FAB_ID_660_GF 0x0
-#define FAB_ID_660_TSMC 0x2
-#define FAB_ID_660_MX 0x3
#define FG_ADC_RR_CHG_TEMP_GF_OFFSET_UV 1303168
#define FG_ADC_RR_CHG_TEMP_GF_SLOPE_UV_PER_C 3784
#define FG_ADC_RR_CHG_TEMP_SMIC_OFFSET_UV 1338433
@@ -401,11 +397,11 @@ static int rradc_get_660_fab_coeff(struct rradc_chip *chip,
int64_t *offset, int64_t *slope)
{
switch (chip->pmic_fab_id->fab_id) {
- case FAB_ID_660_GF:
+ case PM660_FAB_ID_GF:
*offset = FG_ADC_RR_CHG_TEMP_660_GF_OFFSET_UV;
*slope = FG_RR_CHG_TEMP_660_GF_SLOPE_UV_PER_C;
break;
- case FAB_ID_660_TSMC:
+ case PM660_FAB_ID_TSMC:
*offset = FG_ADC_RR_CHG_TEMP_660_SMIC_OFFSET_UV;
*slope = FG_RR_CHG_TEMP_660_SMIC_SLOPE_UV_PER_C;
break;
@@ -421,11 +417,11 @@ static int rradc_get_8998_fab_coeff(struct rradc_chip *chip,
int64_t *offset, int64_t *slope)
{
switch (chip->pmic_fab_id->fab_id) {
- case FAB_ID_GF:
+ case PMI8998_FAB_ID_GF:
*offset = FG_ADC_RR_CHG_TEMP_GF_OFFSET_UV;
*slope = FG_ADC_RR_CHG_TEMP_GF_SLOPE_UV_PER_C;
break;
- case FAB_ID_SMIC:
+ case PMI8998_FAB_ID_SMIC:
*offset = FG_ADC_RR_CHG_TEMP_SMIC_OFFSET_UV;
*slope = FG_ADC_RR_CHG_TEMP_SMIC_SLOPE_UV_PER_C;
break;
@@ -753,6 +749,75 @@ static int rradc_read_channel_with_continuous_mode(struct rradc_chip *chip,
return rc;
}
+static int rradc_enable_batt_id_channel(struct rradc_chip *chip, bool enable)
+{
+ int rc = 0;
+
+ if (enable) {
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_CTRL,
+ FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV,
+ FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV);
+ if (rc < 0) {
+ pr_err("Enabling BATT ID channel failed:%d\n", rc);
+ return rc;
+ }
+ } else {
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_CTRL,
+ FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV, 0);
+ if (rc < 0) {
+ pr_err("Disabling BATT ID channel failed:%d\n", rc);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int rradc_do_batt_id_conversion(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 *data, u8 *buf)
+{
+ int rc = 0, ret = 0;
+
+ rc = rradc_enable_batt_id_channel(chip, true);
+ if (rc < 0) {
+ pr_err("Enabling BATT ID channel failed:%d\n", rc);
+ return rc;
+ }
+
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
+ FG_ADC_RR_BATT_ID_TRIGGER_CTL,
+ FG_ADC_RR_BATT_ID_TRIGGER_CTL);
+ if (rc < 0) {
+ pr_err("BATT_ID trigger set failed:%d\n", rc);
+ ret = rc;
+ rc = rradc_enable_batt_id_channel(chip, false);
+ if (rc < 0)
+ pr_err("Disabling BATT ID channel failed:%d\n", rc);
+ return ret;
+ }
+
+ rc = rradc_read_channel_with_continuous_mode(chip, prop, buf);
+ if (rc < 0) {
+ pr_err("Error reading in continuous mode:%d\n", rc);
+ ret = rc;
+ }
+
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
+ FG_ADC_RR_BATT_ID_TRIGGER_CTL, 0);
+ if (rc < 0) {
+ pr_err("BATT_ID trigger re-set failed:%d\n", rc);
+ ret = rc;
+ }
+
+ rc = rradc_enable_batt_id_channel(chip, false);
+ if (rc < 0) {
+ pr_err("Disabling BATT ID channel failed:%d\n", rc);
+ ret = rc;
+ }
+
+ return ret;
+}
+
static int rradc_do_conversion(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 *data)
{
@@ -765,24 +830,9 @@ static int rradc_do_conversion(struct rradc_chip *chip,
switch (prop->channel) {
case RR_ADC_BATT_ID:
- rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
- FG_ADC_RR_BATT_ID_TRIGGER_CTL,
- FG_ADC_RR_BATT_ID_TRIGGER_CTL);
- if (rc < 0) {
- pr_err("BATT_ID trigger set failed:%d\n", rc);
- goto fail;
- }
-
- rc = rradc_read_channel_with_continuous_mode(chip, prop, buf);
- if (rc < 0) {
- pr_err("Error reading in continuous mode:%d\n", rc);
- goto fail;
- }
-
- rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
- FG_ADC_RR_BATT_ID_TRIGGER_CTL, 0);
+ rc = rradc_do_batt_id_conversion(chip, prop, data, buf);
if (rc < 0) {
- pr_err("BATT_ID trigger re-set failed:%d\n", rc);
+ pr_err("Battery ID conversion failed:%d\n", rc);
goto fail;
}
break;
diff --git a/drivers/iio/adc/qcom-tadc.c b/drivers/iio/adc/qcom-tadc.c
index 9241288c1d43..054dfcc8556a 100644
--- a/drivers/iio/adc/qcom-tadc.c
+++ b/drivers/iio/adc/qcom-tadc.c
@@ -18,7 +18,12 @@
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/power_supply.h>
+#include <linux/pmic-voter.h>
+#define USB_PRESENT_VOTER "USB_PRESENT_VOTER"
+#define SLEEP_VOTER "SLEEP_VOTER"
+#define SHUTDOWN_VOTER "SHUTDOWN_VOTER"
#define TADC_REVISION1_REG 0x00
#define TADC_REVISION2_REG 0x01
#define TADC_REVISION3_REG 0x02
@@ -54,6 +59,7 @@
#define TADC_CH7_ADC_HI_REG(chip) (chip->tadc_base + 0x73)
#define TADC_CH8_ADC_LO_REG(chip) (chip->tadc_base + 0x74)
#define TADC_CH8_ADC_HI_REG(chip) (chip->tadc_base + 0x75)
+#define TADC_ADC_DIRECT_TST(chip) (chip->tadc_base + 0xE7)
/* TADC_CMP register definitions */
#define TADC_CMP_THR1_CMP_REG(chip) (chip->tadc_cmp_base + 0x51)
@@ -217,6 +223,11 @@ struct tadc_chip {
struct tadc_chan_data chans[TADC_NUM_CH];
struct completion eoc_complete;
struct mutex write_lock;
+ struct mutex conv_lock;
+ struct power_supply *usb_psy;
+ struct votable *tadc_disable_votable;
+ struct work_struct status_change_work;
+ struct notifier_block nb;
};
struct tadc_pt {
@@ -274,7 +285,7 @@ static bool tadc_is_reg_locked(struct tadc_chip *chip, u16 reg)
if ((reg & 0xFF00) == chip->tadc_cmp_base)
return true;
- if (reg == TADC_HWTRIG_CONV_CH_EN_REG(chip))
+ if (reg >= TADC_HWTRIG_CONV_CH_EN_REG(chip))
return true;
return false;
@@ -480,12 +491,22 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc)
{
unsigned long timeout, timeleft;
u8 val[TADC_NUM_CH * 2];
- int rc, i;
+ int rc = 0, i;
+ mutex_lock(&chip->conv_lock);
rc = tadc_read(chip, TADC_MBG_ERR_REG(chip), val, 1);
if (rc < 0) {
pr_err("Couldn't read mbg error status rc=%d\n", rc);
- return rc;
+ goto unlock;
+ }
+
+ reinit_completion(&chip->eoc_complete);
+
+ if (get_effective_result(chip->tadc_disable_votable)) {
+ /* leave it back in completed state */
+ complete_all(&chip->eoc_complete);
+ rc = -ENODATA;
+ goto unlock;
}
if (val[0] != 0) {
@@ -496,7 +517,7 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc)
rc = tadc_write(chip, TADC_CONV_REQ_REG(chip), channels);
if (rc < 0) {
pr_err("Couldn't write conversion request rc=%d\n", rc);
- return rc;
+ goto unlock;
}
timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS);
@@ -506,25 +527,34 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc)
rc = tadc_read(chip, TADC_SW_CH_CONV_REG(chip), val, 1);
if (rc < 0) {
pr_err("Couldn't read conversion status rc=%d\n", rc);
- return rc;
+ goto unlock;
}
+ /*
+ * check one last time if the channel we are requesting
+ * has completed conversion
+ */
if (val[0] != channels) {
- pr_err("Conversion timed out\n");
- return -ETIMEDOUT;
+ rc = -ETIMEDOUT;
+ goto unlock;
}
}
rc = tadc_read(chip, TADC_CH1_ADC_LO_REG(chip), val, ARRAY_SIZE(val));
if (rc < 0) {
pr_err("Couldn't read adc channels rc=%d\n", rc);
- return rc;
+ goto unlock;
}
for (i = 0; i < TADC_NUM_CH; i++)
adc[i] = (s16)(val[i * 2] | (u16)val[i * 2 + 1] << 8);
- return jiffies_to_msecs(timeout - timeleft);
+ pr_debug("Conversion time for channels 0x%x = %dms\n", channels,
+ jiffies_to_msecs(timeout - timeleft));
+
+unlock:
+ mutex_unlock(&chip->conv_lock);
+ return rc;
}
static int tadc_read_raw(struct iio_dev *indio_dev,
@@ -593,12 +623,17 @@ static int tadc_read_raw(struct iio_dev *indio_dev,
break;
default:
rc = tadc_do_conversion(chip, BIT(chan->channel), adc);
- if (rc >= 0)
- *val = adc[chan->channel];
+ if (rc < 0) {
+ if (rc != -ENODATA)
+ pr_err("Couldn't read battery current and voltage channels rc=%d\n",
+ rc);
+ return rc;
+ }
+ *val = adc[chan->channel];
break;
}
- if (rc < 0) {
+ if (rc < 0 && rc != -ENODATA) {
pr_err("Couldn't read channel %d\n", chan->channel);
return rc;
}
@@ -630,7 +665,7 @@ static int tadc_read_raw(struct iio_dev *indio_dev,
case TADC_BATT_P:
rc = tadc_do_conversion(chip,
BIT(TADC_BATT_I) | BIT(TADC_BATT_V), adc);
- if (rc < 0) {
+ if (rc < 0 && rc != -ENODATA) {
pr_err("Couldn't read battery current and voltage channels rc=%d\n",
rc);
return rc;
@@ -641,7 +676,7 @@ static int tadc_read_raw(struct iio_dev *indio_dev,
case TADC_INPUT_P:
rc = tadc_do_conversion(chip,
BIT(TADC_INPUT_I) | BIT(TADC_INPUT_V), adc);
- if (rc < 0) {
+ if (rc < 0 && rc != -ENODATA) {
pr_err("Couldn't read input current and voltage channels rc=%d\n",
rc);
return rc;
@@ -683,6 +718,7 @@ static int tadc_read_raw(struct iio_dev *indio_dev,
case TADC_DIE_TEMP:
case TADC_DIE_TEMP_THR1:
case TADC_DIE_TEMP_THR2:
+ case TADC_DIE_TEMP_THR3:
*val = chan_data->scale;
return IIO_VAL_INT;
case TADC_BATT_I:
@@ -821,15 +857,130 @@ static int tadc_write_raw(struct iio_dev *indio_dev,
return 0;
}
-
static irqreturn_t handle_eoc(int irq, void *dev_id)
{
struct tadc_chip *chip = dev_id;
- complete(&chip->eoc_complete);
+ complete_all(&chip->eoc_complete);
return IRQ_HANDLED;
}
+static int tadc_disable_vote_callback(struct votable *votable,
+ void *data, int disable, const char *client)
+{
+ struct tadc_chip *chip = data;
+ int rc;
+ int timeout;
+ unsigned long timeleft;
+
+ if (disable) {
+ timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS);
+ timeleft = wait_for_completion_timeout(&chip->eoc_complete,
+ timeout);
+ if (timeleft == 0)
+ pr_err("Timed out waiting for eoc, disabling hw conversions regardless\n");
+
+ rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip), 0x00);
+ if (rc < 0) {
+ pr_err("Couldn't disable hw conversions rc=%d\n", rc);
+ return rc;
+ }
+ rc = tadc_write(chip, TADC_ADC_DIRECT_TST(chip), 0x80);
+ if (rc < 0) {
+ pr_err("Couldn't enable direct test mode rc=%d\n", rc);
+ return rc;
+ }
+ } else {
+ rc = tadc_write(chip, TADC_ADC_DIRECT_TST(chip), 0x00);
+ if (rc < 0) {
+ pr_err("Couldn't disable direct test mode rc=%d\n", rc);
+ return rc;
+ }
+ rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip), 0x07);
+ if (rc < 0) {
+ pr_err("Couldn't enable hw conversions rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ pr_debug("client: %s disable: %d\n", client, disable);
+ return 0;
+}
+
+static void status_change_work(struct work_struct *work)
+{
+ struct tadc_chip *chip = container_of(work,
+ struct tadc_chip, status_change_work);
+ union power_supply_propval pval = {0, };
+ int rc;
+
+ if (!chip->usb_psy)
+ chip->usb_psy = power_supply_get_by_name("usb");
+
+ if (!chip->usb_psy) {
+ /* treat usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0);
+ return;
+ }
+
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't get present status rc=%d\n", rc);
+ /* treat usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0);
+ return;
+ }
+
+ /* disable if usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, !pval.intval, 0);
+}
+
+static int tadc_notifier_call(struct notifier_block *nb,
+ unsigned long ev, void *v)
+{
+ struct power_supply *psy = v;
+ struct tadc_chip *chip = container_of(nb, struct tadc_chip, nb);
+
+ if (ev != PSY_EVENT_PROP_CHANGED)
+ return NOTIFY_OK;
+
+ if ((strcmp(psy->desc->name, "usb") == 0))
+ schedule_work(&chip->status_change_work);
+
+ return NOTIFY_OK;
+}
+
+static int tadc_register_notifier(struct tadc_chip *chip)
+{
+ int rc;
+
+ chip->nb.notifier_call = tadc_notifier_call;
+ rc = power_supply_reg_notifier(&chip->nb);
+ if (rc < 0) {
+ pr_err("Couldn't register psy notifier rc = %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int tadc_suspend(struct device *dev)
+{
+ struct tadc_chip *chip = dev_get_drvdata(dev);
+
+ vote(chip->tadc_disable_votable, SLEEP_VOTER, true, 0);
+ return 0;
+}
+
+static int tadc_resume(struct device *dev)
+{
+ struct tadc_chip *chip = dev_get_drvdata(dev);
+
+ vote(chip->tadc_disable_votable, SLEEP_VOTER, false, 0);
+ return 0;
+}
+
static int tadc_set_therm_table(struct tadc_chan_data *chan_data, u32 beta,
u32 rtherm)
{
@@ -1009,6 +1160,12 @@ static int tadc_probe(struct platform_device *pdev)
chip->dev = &pdev->dev;
init_completion(&chip->eoc_complete);
+ /*
+ * set the completion in "completed" state so disable of the tadc
+ * can progress
+ */
+ complete_all(&chip->eoc_complete);
+
rc = of_property_read_u32(node, "reg", &chip->tadc_base);
if (rc < 0) {
pr_err("Couldn't read base address rc=%d\n", rc);
@@ -1017,6 +1174,8 @@ static int tadc_probe(struct platform_device *pdev)
chip->tadc_cmp_base = chip->tadc_base + 0x100;
mutex_init(&chip->write_lock);
+ mutex_init(&chip->conv_lock);
+ INIT_WORK(&chip->status_change_work, status_change_work);
chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
if (!chip->regmap) {
pr_err("Couldn't get regmap\n");
@@ -1035,17 +1194,36 @@ static int tadc_probe(struct platform_device *pdev)
return rc;
}
+ chip->tadc_disable_votable = create_votable("SMB_TADC_DISABLE",
+ VOTE_SET_ANY,
+ tadc_disable_vote_callback,
+ chip);
+ if (IS_ERR(chip->tadc_disable_votable)) {
+ rc = PTR_ERR(chip->tadc_disable_votable);
+ return rc;
+ }
+ /* assume usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0);
+ vote(chip->tadc_disable_votable, SHUTDOWN_VOTER, false, 0);
+ vote(chip->tadc_disable_votable, SLEEP_VOTER, false, 0);
+
+ rc = tadc_register_notifier(chip);
+ if (rc < 0) {
+ pr_err("Couldn't register notifier=%d\n", rc);
+ goto destroy_votable;
+ }
+
irq = of_irq_get_byname(node, "eoc");
if (irq < 0) {
pr_err("Couldn't get eoc irq rc=%d\n", irq);
- return irq;
+ goto destroy_votable;
}
rc = devm_request_threaded_irq(chip->dev, irq, NULL, handle_eoc,
IRQF_ONESHOT, "eoc", chip);
if (rc < 0) {
pr_err("Couldn't request irq %d rc=%d\n", irq, rc);
- return rc;
+ goto destroy_votable;
}
indio_dev->dev.parent = chip->dev;
@@ -1058,17 +1236,37 @@ static int tadc_probe(struct platform_device *pdev)
rc = devm_iio_device_register(chip->dev, indio_dev);
if (rc < 0) {
pr_err("Couldn't register IIO device rc=%d\n", rc);
- return rc;
+ goto destroy_votable;
}
+ platform_set_drvdata(pdev, chip);
return 0;
+
+destroy_votable:
+ destroy_votable(chip->tadc_disable_votable);
+ return rc;
}
static int tadc_remove(struct platform_device *pdev)
{
+ struct tadc_chip *chip = platform_get_drvdata(pdev);
+
+ destroy_votable(chip->tadc_disable_votable);
return 0;
}
+static void tadc_shutdown(struct platform_device *pdev)
+{
+ struct tadc_chip *chip = platform_get_drvdata(pdev);
+
+ vote(chip->tadc_disable_votable, SHUTDOWN_VOTER, true, 0);
+}
+
+static const struct dev_pm_ops tadc_pm_ops = {
+ .resume = tadc_resume,
+ .suspend = tadc_suspend,
+};
+
static const struct of_device_id tadc_match_table[] = {
{ .compatible = "qcom,tadc" },
{ }
@@ -1076,12 +1274,14 @@ static const struct of_device_id tadc_match_table[] = {
MODULE_DEVICE_TABLE(of, tadc_match_table);
static struct platform_driver tadc_driver = {
- .driver = {
+ .driver = {
.name = "qcom-tadc",
.of_match_table = tadc_match_table,
+ .pm = &tadc_pm_ops,
},
- .probe = tadc_probe,
- .remove = tadc_remove,
+ .probe = tadc_probe,
+ .remove = tadc_remove,
+ .shutdown = tadc_shutdown,
};
module_platform_driver(tadc_driver);
diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c
index a0d7deeac62f..3f90985d545e 100644
--- a/drivers/iio/pressure/mpl115.c
+++ b/drivers/iio/pressure/mpl115.c
@@ -136,6 +136,7 @@ static const struct iio_chan_spec mpl115_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type =
BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),
},
};
diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c
index 01b2e0b18878..0f5b8767ec2e 100644
--- a/drivers/iio/pressure/mpl3115.c
+++ b/drivers/iio/pressure/mpl3115.c
@@ -182,7 +182,7 @@ static const struct iio_chan_spec mpl3115_channels[] = {
{
.type = IIO_PRESSURE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.scan_index = 0,
.scan_type = {
.sign = 'u',
@@ -195,7 +195,7 @@ static const struct iio_chan_spec mpl3115_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.scan_index = 1,
.scan_type = {
.sign = 's',
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 17a15c56028c..3f5741a3e728 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -2578,7 +2578,8 @@ static int cma_bind_addr(struct rdma_cm_id *id, struct sockaddr *src_addr,
if (!src_addr || !src_addr->sa_family) {
src_addr = (struct sockaddr *) &id->route.addr.src_addr;
src_addr->sa_family = dst_addr->sa_family;
- if (dst_addr->sa_family == AF_INET6) {
+ if (IS_ENABLED(CONFIG_IPV6) &&
+ dst_addr->sa_family == AF_INET6) {
struct sockaddr_in6 *src_addr6 = (struct sockaddr_in6 *) src_addr;
struct sockaddr_in6 *dst_addr6 = (struct sockaddr_in6 *) dst_addr;
src_addr6->sin6_scope_id = dst_addr6->sin6_scope_id;
@@ -3348,6 +3349,9 @@ static int cma_accept_iw(struct rdma_id_private *id_priv,
struct iw_cm_conn_param iw_param;
int ret;
+ if (!conn_param)
+ return -EINVAL;
+
ret = cma_modify_qp_rtr(id_priv, conn_param);
if (ret)
return ret;
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 2281de122038..8d84c563ba75 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -1745,7 +1745,7 @@ find_mad_agent(struct ib_mad_port_private *port_priv,
if (!class)
goto out;
if (convert_mgmt_class(mad_hdr->mgmt_class) >=
- IB_MGMT_MAX_METHODS)
+ ARRAY_SIZE(class->method_table))
goto out;
method = class->method_table[convert_mgmt_class(
mad_hdr->mgmt_class)];
diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c
index 6aa648cb5381..2cd97977b988 100644
--- a/drivers/infiniband/core/multicast.c
+++ b/drivers/infiniband/core/multicast.c
@@ -517,8 +517,11 @@ static void join_handler(int status, struct ib_sa_mcmember_rec *rec,
process_join_error(group, status);
else {
int mgids_changed, is_mgid0;
- ib_find_pkey(group->port->dev->device, group->port->port_num,
- be16_to_cpu(rec->pkey), &pkey_index);
+
+ if (ib_find_pkey(group->port->dev->device,
+ group->port->port_num, be16_to_cpu(rec->pkey),
+ &pkey_index))
+ pkey_index = MCAST_INVALID_PKEY_INDEX;
spin_lock_irq(&group->port->lock);
if (group->state == MCAST_BUSY &&
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index 04f3c0db9126..0ae337bec4f2 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -134,6 +134,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
IB_ACCESS_REMOTE_ATOMIC | IB_ACCESS_MW_BIND));
if (access & IB_ACCESS_ON_DEMAND) {
+ put_pid(umem->pid);
ret = ib_umem_odp_get(context, umem);
if (ret) {
kfree(umem);
@@ -149,6 +150,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
page_list = (struct page **) __get_free_page(GFP_KERNEL);
if (!page_list) {
+ put_pid(umem->pid);
kfree(umem);
return ERR_PTR(-ENOMEM);
}
diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c
index c007c766c61e..fc21bdbb8b32 100644
--- a/drivers/infiniband/hw/mlx4/ah.c
+++ b/drivers/infiniband/hw/mlx4/ah.c
@@ -113,7 +113,9 @@ static struct ib_ah *create_iboe_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr
!(1 << ah->av.eth.stat_rate & dev->caps.stat_rate_support))
--ah->av.eth.stat_rate;
}
-
+ ah->av.eth.sl_tclass_flowlabel |=
+ cpu_to_be32((ah_attr->grh.traffic_class << 20) |
+ ah_attr->grh.flow_label);
/*
* HW requires multicast LID so we just choose one.
*/
@@ -121,7 +123,7 @@ static struct ib_ah *create_iboe_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr
ah->av.ib.dlid = cpu_to_be16(0xc000);
memcpy(ah->av.eth.dgid, ah_attr->grh.dgid.raw, 16);
- ah->av.eth.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 29);
+ ah->av.eth.sl_tclass_flowlabel |= cpu_to_be32(ah_attr->sl << 29);
return &ah->ibah;
}
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index 97d6878f9938..77ddf2fa8625 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -630,9 +630,11 @@ static int eth_link_query_port(struct ib_device *ibdev, u8 port,
if (err)
goto out;
- props->active_width = (((u8 *)mailbox->buf)[5] == 0x40) ?
- IB_WIDTH_4X : IB_WIDTH_1X;
- props->active_speed = IB_SPEED_QDR;
+ props->active_width = (((u8 *)mailbox->buf)[5] == 0x40) ||
+ (((u8 *)mailbox->buf)[5] == 0x20 /*56Gb*/) ?
+ IB_WIDTH_4X : IB_WIDTH_1X;
+ props->active_speed = (((u8 *)mailbox->buf)[5] == 0x20 /*56Gb*/) ?
+ IB_SPEED_FDR : IB_SPEED_QDR;
props->port_cap_flags = IB_PORT_CM_SUP | IB_PORT_IP_BASED_GIDS;
props->gid_tbl_len = mdev->dev->caps.gid_table_len[port];
props->max_msg_sz = mdev->dev->caps.max_msg_sz;
@@ -2401,14 +2403,19 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
goto err_steer_qp_release;
}
- bitmap_zero(ibdev->ib_uc_qpns_bitmap, ibdev->steer_qpn_count);
-
- err = mlx4_FLOW_STEERING_IB_UC_QP_RANGE(
- dev, ibdev->steer_qpn_base,
- ibdev->steer_qpn_base +
- ibdev->steer_qpn_count - 1);
- if (err)
- goto err_steer_free_bitmap;
+ if (dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_DMFS_IPOIB) {
+ bitmap_zero(ibdev->ib_uc_qpns_bitmap,
+ ibdev->steer_qpn_count);
+ err = mlx4_FLOW_STEERING_IB_UC_QP_RANGE(
+ dev, ibdev->steer_qpn_base,
+ ibdev->steer_qpn_base +
+ ibdev->steer_qpn_count - 1);
+ if (err)
+ goto err_steer_free_bitmap;
+ } else {
+ bitmap_fill(ibdev->ib_uc_qpns_bitmap,
+ ibdev->steer_qpn_count);
+ }
}
for (j = 1; j <= ibdev->dev->caps.num_ports; j++)
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index f350f2d61c15..1c8b7c22c822 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -1207,7 +1207,8 @@ int mlx4_ib_destroy_qp(struct ib_qp *qp)
if (is_qp0(dev, mqp))
mlx4_CLOSE_PORT(dev->dev, mqp->port);
- if (dev->qp1_proxy[mqp->port - 1] == mqp) {
+ if (mqp->mlx4_ib_qp_type == MLX4_IB_QPT_PROXY_GSI &&
+ dev->qp1_proxy[mqp->port - 1] == mqp) {
mutex_lock(&dev->qp1_proxy_lock[mqp->port - 1]);
dev->qp1_proxy[mqp->port - 1] = NULL;
mutex_unlock(&dev->qp1_proxy_lock[mqp->port - 1]);
diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c
index 6000f7aeede9..3399271c235b 100644
--- a/drivers/infiniband/hw/mlx5/mr.c
+++ b/drivers/infiniband/hw/mlx5/mr.c
@@ -614,6 +614,33 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev)
return 0;
}
+static void wait_for_async_commands(struct mlx5_ib_dev *dev)
+{
+ struct mlx5_mr_cache *cache = &dev->cache;
+ struct mlx5_cache_ent *ent;
+ int total = 0;
+ int i;
+ int j;
+
+ for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) {
+ ent = &cache->ent[i];
+ for (j = 0 ; j < 1000; j++) {
+ if (!ent->pending)
+ break;
+ msleep(50);
+ }
+ }
+ for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) {
+ ent = &cache->ent[i];
+ total += ent->pending;
+ }
+
+ if (total)
+ mlx5_ib_warn(dev, "aborted while there are %d pending mr requests\n", total);
+ else
+ mlx5_ib_warn(dev, "done with all pending requests\n");
+}
+
int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev)
{
int i;
@@ -627,6 +654,7 @@ int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev)
clean_keys(dev, i);
destroy_workqueue(dev->cache.wq);
+ wait_for_async_commands(dev);
del_timer_sync(&dev->delay_timer);
return 0;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index 69a151ae8261..07cfcc326863 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -63,6 +63,8 @@ enum ipoib_flush_level {
enum {
IPOIB_ENCAP_LEN = 4,
+ IPOIB_PSEUDO_LEN = 20,
+ IPOIB_HARD_LEN = IPOIB_ENCAP_LEN + IPOIB_PSEUDO_LEN,
IPOIB_UD_HEAD_SIZE = IB_GRH_BYTES + IPOIB_ENCAP_LEN,
IPOIB_UD_RX_SG = 2, /* max buffer needed for 4K mtu */
@@ -131,15 +133,21 @@ struct ipoib_header {
u16 reserved;
};
-struct ipoib_cb {
- struct qdisc_skb_cb qdisc_cb;
- u8 hwaddr[INFINIBAND_ALEN];
+struct ipoib_pseudo_header {
+ u8 hwaddr[INFINIBAND_ALEN];
};
-static inline struct ipoib_cb *ipoib_skb_cb(const struct sk_buff *skb)
+static inline void skb_add_pseudo_hdr(struct sk_buff *skb)
{
- BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct ipoib_cb));
- return (struct ipoib_cb *)skb->cb;
+ char *data = skb_push(skb, IPOIB_PSEUDO_LEN);
+
+ /*
+ * only the ipoib header is present now, make room for a dummy
+ * pseudo header and set skb field accordingly
+ */
+ memset(data, 0, IPOIB_PSEUDO_LEN);
+ skb_reset_mac_header(skb);
+ skb_pull(skb, IPOIB_HARD_LEN);
}
/* Used for all multicast joins (broadcast, IPv4 mcast and IPv6 mcast) */
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 8ca75af0e6d1..2018d24344de 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -63,6 +63,8 @@ MODULE_PARM_DESC(cm_data_debug_level,
#define IPOIB_CM_RX_DELAY (3 * 256 * HZ)
#define IPOIB_CM_RX_UPDATE_MASK (0x3)
+#define IPOIB_CM_RX_RESERVE (ALIGN(IPOIB_HARD_LEN, 16) - IPOIB_ENCAP_LEN)
+
static struct ib_qp_attr ipoib_cm_err_attr = {
.qp_state = IB_QPS_ERR
};
@@ -147,15 +149,15 @@ static struct sk_buff *ipoib_cm_alloc_rx_skb(struct net_device *dev,
struct sk_buff *skb;
int i;
- skb = dev_alloc_skb(IPOIB_CM_HEAD_SIZE + 12);
+ skb = dev_alloc_skb(ALIGN(IPOIB_CM_HEAD_SIZE + IPOIB_PSEUDO_LEN, 16));
if (unlikely(!skb))
return NULL;
/*
- * IPoIB adds a 4 byte header. So we need 12 more bytes to align the
+ * IPoIB adds a IPOIB_ENCAP_LEN byte header, this will align the
* IP header to a multiple of 16.
*/
- skb_reserve(skb, 12);
+ skb_reserve(skb, IPOIB_CM_RX_RESERVE);
mapping[0] = ib_dma_map_single(priv->ca, skb->data, IPOIB_CM_HEAD_SIZE,
DMA_FROM_DEVICE);
@@ -624,9 +626,9 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
if (wc->byte_len < IPOIB_CM_COPYBREAK) {
int dlen = wc->byte_len;
- small_skb = dev_alloc_skb(dlen + 12);
+ small_skb = dev_alloc_skb(dlen + IPOIB_CM_RX_RESERVE);
if (small_skb) {
- skb_reserve(small_skb, 12);
+ skb_reserve(small_skb, IPOIB_CM_RX_RESERVE);
ib_dma_sync_single_for_cpu(priv->ca, rx_ring[wr_id].mapping[0],
dlen, DMA_FROM_DEVICE);
skb_copy_from_linear_data(skb, small_skb->data, dlen);
@@ -663,8 +665,7 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
copied:
skb->protocol = ((struct ipoib_header *) skb->data)->proto;
- skb_reset_mac_header(skb);
- skb_pull(skb, IPOIB_ENCAP_LEN);
+ skb_add_pseudo_hdr(skb);
++dev->stats.rx_packets;
dev->stats.rx_bytes += skb->len;
@@ -1035,8 +1036,6 @@ static struct ib_qp *ipoib_cm_create_tx_qp(struct net_device *dev, struct ipoib_
tx_qp = ib_create_qp(priv->pd, &attr);
if (PTR_ERR(tx_qp) == -EINVAL) {
- ipoib_warn(priv, "can't use GFP_NOIO for QPs on device %s, using GFP_KERNEL\n",
- priv->ca->name);
attr.create_flags &= ~IB_QP_CREATE_USE_GFP_NOIO;
tx_qp = ib_create_qp(priv->pd, &attr);
}
@@ -1489,12 +1488,14 @@ static ssize_t set_mode(struct device *d, struct device_attribute *attr,
ret = ipoib_set_mode(dev, buf);
- rtnl_unlock();
-
- if (!ret)
- return count;
+ /* The assumption is that the function ipoib_set_mode returned
+ * with the rtnl held by it, if not the value -EBUSY returned,
+ * then no need to rtnl_unlock
+ */
+ if (ret != -EBUSY)
+ rtnl_unlock();
- return ret;
+ return (!ret || ret == -EBUSY) ? count : ret;
}
static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, show_mode, set_mode);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index 85de078fb0ce..8f8c3af9f4e8 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -130,16 +130,15 @@ static struct sk_buff *ipoib_alloc_rx_skb(struct net_device *dev, int id)
buf_size = IPOIB_UD_BUF_SIZE(priv->max_ib_mtu);
- skb = dev_alloc_skb(buf_size + IPOIB_ENCAP_LEN);
+ skb = dev_alloc_skb(buf_size + IPOIB_HARD_LEN);
if (unlikely(!skb))
return NULL;
/*
- * IB will leave a 40 byte gap for a GRH and IPoIB adds a 4 byte
- * header. So we need 4 more bytes to get to 48 and align the
- * IP header to a multiple of 16.
+ * the IP header will be at IPOIP_HARD_LEN + IB_GRH_BYTES, that is
+ * 64 bytes aligned
*/
- skb_reserve(skb, 4);
+ skb_reserve(skb, sizeof(struct ipoib_pseudo_header));
mapping = priv->rx_ring[id].mapping;
mapping[0] = ib_dma_map_single(priv->ca, skb->data, buf_size,
@@ -242,8 +241,7 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
skb_pull(skb, IB_GRH_BYTES);
skb->protocol = ((struct ipoib_header *) skb->data)->proto;
- skb_reset_mac_header(skb);
- skb_pull(skb, IPOIB_ENCAP_LEN);
+ skb_add_pseudo_hdr(skb);
++dev->stats.rx_packets;
dev->stats.rx_bytes += skb->len;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 5f7681b975d0..8efcff1beb8f 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -464,8 +464,7 @@ int ipoib_set_mode(struct net_device *dev, const char *buf)
priv->tx_wr.wr.send_flags &= ~IB_SEND_IP_CSUM;
ipoib_flush_paths(dev);
- rtnl_lock();
- return 0;
+ return (!rtnl_trylock()) ? -EBUSY : 0;
}
if (!strcmp(buf, "datagram\n")) {
@@ -474,8 +473,7 @@ int ipoib_set_mode(struct net_device *dev, const char *buf)
dev_set_mtu(dev, min(priv->mcast_mtu, dev->mtu));
rtnl_unlock();
ipoib_flush_paths(dev);
- rtnl_lock();
- return 0;
+ return (!rtnl_trylock()) ? -EBUSY : 0;
}
return -EINVAL;
@@ -628,6 +626,14 @@ void ipoib_mark_paths_invalid(struct net_device *dev)
spin_unlock_irq(&priv->lock);
}
+static void push_pseudo_header(struct sk_buff *skb, const char *daddr)
+{
+ struct ipoib_pseudo_header *phdr;
+
+ phdr = (struct ipoib_pseudo_header *)skb_push(skb, sizeof(*phdr));
+ memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN);
+}
+
void ipoib_flush_paths(struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
@@ -850,9 +856,11 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr,
ipoib_neigh_free(neigh);
goto err_drop;
}
- if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE)
+ if (skb_queue_len(&neigh->queue) <
+ IPOIB_MAX_PATH_REC_QUEUE) {
+ push_pseudo_header(skb, neigh->daddr);
__skb_queue_tail(&neigh->queue, skb);
- else {
+ } else {
ipoib_warn(priv, "queue length limit %d. Packet drop.\n",
skb_queue_len(&neigh->queue));
goto err_drop;
@@ -868,10 +876,12 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr,
if (!path->query && path_rec_start(dev, path))
goto err_path;
- if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE)
+ if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+ push_pseudo_header(skb, neigh->daddr);
__skb_queue_tail(&neigh->queue, skb);
- else
+ } else {
goto err_drop;
+ }
}
spin_unlock_irqrestore(&priv->lock, flags);
@@ -889,7 +899,7 @@ err_drop:
}
static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
- struct ipoib_cb *cb)
+ struct ipoib_pseudo_header *phdr)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_path *path;
@@ -897,16 +907,17 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
spin_lock_irqsave(&priv->lock, flags);
- path = __path_find(dev, cb->hwaddr + 4);
+ path = __path_find(dev, phdr->hwaddr + 4);
if (!path || !path->valid) {
int new_path = 0;
if (!path) {
- path = path_rec_create(dev, cb->hwaddr + 4);
+ path = path_rec_create(dev, phdr->hwaddr + 4);
new_path = 1;
}
if (path) {
if (skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+ push_pseudo_header(skb, phdr->hwaddr);
__skb_queue_tail(&path->queue, skb);
} else {
++dev->stats.tx_dropped;
@@ -934,10 +945,11 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
be16_to_cpu(path->pathrec.dlid));
spin_unlock_irqrestore(&priv->lock, flags);
- ipoib_send(dev, skb, path->ah, IPOIB_QPN(cb->hwaddr));
+ ipoib_send(dev, skb, path->ah, IPOIB_QPN(phdr->hwaddr));
return;
} else if ((path->query || !path_rec_start(dev, path)) &&
skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+ push_pseudo_header(skb, phdr->hwaddr);
__skb_queue_tail(&path->queue, skb);
} else {
++dev->stats.tx_dropped;
@@ -951,13 +963,15 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_neigh *neigh;
- struct ipoib_cb *cb = ipoib_skb_cb(skb);
+ struct ipoib_pseudo_header *phdr;
struct ipoib_header *header;
unsigned long flags;
+ phdr = (struct ipoib_pseudo_header *) skb->data;
+ skb_pull(skb, sizeof(*phdr));
header = (struct ipoib_header *) skb->data;
- if (unlikely(cb->hwaddr[4] == 0xff)) {
+ if (unlikely(phdr->hwaddr[4] == 0xff)) {
/* multicast, arrange "if" according to probability */
if ((header->proto != htons(ETH_P_IP)) &&
(header->proto != htons(ETH_P_IPV6)) &&
@@ -970,13 +984,13 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
/* Add in the P_Key for multicast*/
- cb->hwaddr[8] = (priv->pkey >> 8) & 0xff;
- cb->hwaddr[9] = priv->pkey & 0xff;
+ phdr->hwaddr[8] = (priv->pkey >> 8) & 0xff;
+ phdr->hwaddr[9] = priv->pkey & 0xff;
- neigh = ipoib_neigh_get(dev, cb->hwaddr);
+ neigh = ipoib_neigh_get(dev, phdr->hwaddr);
if (likely(neigh))
goto send_using_neigh;
- ipoib_mcast_send(dev, cb->hwaddr, skb);
+ ipoib_mcast_send(dev, phdr->hwaddr, skb);
return NETDEV_TX_OK;
}
@@ -985,16 +999,16 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
case htons(ETH_P_IP):
case htons(ETH_P_IPV6):
case htons(ETH_P_TIPC):
- neigh = ipoib_neigh_get(dev, cb->hwaddr);
+ neigh = ipoib_neigh_get(dev, phdr->hwaddr);
if (unlikely(!neigh)) {
- neigh_add_path(skb, cb->hwaddr, dev);
+ neigh_add_path(skb, phdr->hwaddr, dev);
return NETDEV_TX_OK;
}
break;
case htons(ETH_P_ARP):
case htons(ETH_P_RARP):
/* for unicast ARP and RARP should always perform path find */
- unicast_arp_send(skb, dev, cb);
+ unicast_arp_send(skb, dev, phdr);
return NETDEV_TX_OK;
default:
/* ethertype not supported by IPoIB */
@@ -1011,11 +1025,12 @@ send_using_neigh:
goto unref;
}
} else if (neigh->ah) {
- ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(cb->hwaddr));
+ ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(phdr->hwaddr));
goto unref;
}
if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+ push_pseudo_header(skb, phdr->hwaddr);
spin_lock_irqsave(&priv->lock, flags);
__skb_queue_tail(&neigh->queue, skb);
spin_unlock_irqrestore(&priv->lock, flags);
@@ -1048,7 +1063,6 @@ static int ipoib_hard_header(struct sk_buff *skb,
const void *daddr, const void *saddr, unsigned len)
{
struct ipoib_header *header;
- struct ipoib_cb *cb = ipoib_skb_cb(skb);
header = (struct ipoib_header *) skb_push(skb, sizeof *header);
@@ -1057,12 +1071,12 @@ static int ipoib_hard_header(struct sk_buff *skb,
/*
* we don't rely on dst_entry structure, always stuff the
- * destination address into skb->cb so we can figure out where
+ * destination address into skb hard header so we can figure out where
* to send the packet later.
*/
- memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN);
+ push_pseudo_header(skb, daddr);
- return sizeof *header;
+ return IPOIB_HARD_LEN;
}
static void ipoib_set_mcast_list(struct net_device *dev)
@@ -1638,7 +1652,7 @@ void ipoib_setup(struct net_device *dev)
dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
- dev->hard_header_len = IPOIB_ENCAP_LEN;
+ dev->hard_header_len = IPOIB_HARD_LEN;
dev->addr_len = INFINIBAND_ALEN;
dev->type = ARPHRD_INFINIBAND;
dev->tx_queue_len = ipoib_sendq_size * 2;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index 87799de90a1d..5580ab0b5781 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -563,8 +563,11 @@ void ipoib_mcast_join_task(struct work_struct *work)
if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags))
return;
- if (ib_query_port(priv->ca, priv->port, &port_attr) ||
- port_attr.state != IB_PORT_ACTIVE) {
+ if (ib_query_port(priv->ca, priv->port, &port_attr)) {
+ ipoib_dbg(priv, "ib_query_port() failed\n");
+ return;
+ }
+ if (port_attr.state != IB_PORT_ACTIVE) {
ipoib_dbg(priv, "port state is not ACTIVE (state = %d) suspending join task\n",
port_attr.state);
return;
@@ -753,9 +756,11 @@ void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb)
__ipoib_mcast_add(dev, mcast);
list_add_tail(&mcast->list, &priv->multicast_list);
}
- if (skb_queue_len(&mcast->pkt_queue) < IPOIB_MAX_MCAST_QUEUE)
+ if (skb_queue_len(&mcast->pkt_queue) < IPOIB_MAX_MCAST_QUEUE) {
+ /* put pseudoheader back on for next time */
+ skb_push(skb, sizeof(struct ipoib_pseudo_header));
skb_queue_tail(&mcast->pkt_queue, skb);
- else {
+ } else {
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
}
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 5f0f4fc58f43..e397f1b0af09 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -1787,17 +1787,24 @@ static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp)
if (unlikely(rsp->tag & SRP_TAG_TSK_MGMT)) {
spin_lock_irqsave(&ch->lock, flags);
ch->req_lim += be32_to_cpu(rsp->req_lim_delta);
+ if (rsp->tag == ch->tsk_mgmt_tag) {
+ ch->tsk_mgmt_status = -1;
+ if (be32_to_cpu(rsp->resp_data_len) >= 4)
+ ch->tsk_mgmt_status = rsp->data[3];
+ complete(&ch->tsk_mgmt_done);
+ } else {
+ shost_printk(KERN_ERR, target->scsi_host,
+ "Received tsk mgmt response too late for tag %#llx\n",
+ rsp->tag);
+ }
spin_unlock_irqrestore(&ch->lock, flags);
-
- ch->tsk_mgmt_status = -1;
- if (be32_to_cpu(rsp->resp_data_len) >= 4)
- ch->tsk_mgmt_status = rsp->data[3];
- complete(&ch->tsk_mgmt_done);
} else {
scmnd = scsi_host_find_tag(target->scsi_host, rsp->tag);
- if (scmnd) {
+ if (scmnd && scmnd->host_scribble) {
req = (void *)scmnd->host_scribble;
scmnd = srp_claim_req(ch, req, NULL, scmnd);
+ } else {
+ scmnd = NULL;
}
if (!scmnd) {
shost_printk(KERN_ERR, target->scsi_host,
@@ -2469,19 +2476,18 @@ srp_change_queue_depth(struct scsi_device *sdev, int qdepth)
}
static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun,
- u8 func)
+ u8 func, u8 *status)
{
struct srp_target_port *target = ch->target;
struct srp_rport *rport = target->rport;
struct ib_device *dev = target->srp_host->srp_dev->dev;
struct srp_iu *iu;
struct srp_tsk_mgmt *tsk_mgmt;
+ int res;
if (!ch->connected || target->qp_in_error)
return -1;
- init_completion(&ch->tsk_mgmt_done);
-
/*
* Lock the rport mutex to avoid that srp_create_ch_ib() is
* invoked while a task management function is being sent.
@@ -2504,10 +2510,16 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun,
tsk_mgmt->opcode = SRP_TSK_MGMT;
int_to_scsilun(lun, &tsk_mgmt->lun);
- tsk_mgmt->tag = req_tag | SRP_TAG_TSK_MGMT;
tsk_mgmt->tsk_mgmt_func = func;
tsk_mgmt->task_tag = req_tag;
+ spin_lock_irq(&ch->lock);
+ ch->tsk_mgmt_tag = (ch->tsk_mgmt_tag + 1) | SRP_TAG_TSK_MGMT;
+ tsk_mgmt->tag = ch->tsk_mgmt_tag;
+ spin_unlock_irq(&ch->lock);
+
+ init_completion(&ch->tsk_mgmt_done);
+
ib_dma_sync_single_for_device(dev, iu->dma, sizeof *tsk_mgmt,
DMA_TO_DEVICE);
if (srp_post_send(ch, iu, sizeof(*tsk_mgmt))) {
@@ -2516,13 +2528,15 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun,
return -1;
}
+ res = wait_for_completion_timeout(&ch->tsk_mgmt_done,
+ msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS));
+ if (res > 0 && status)
+ *status = ch->tsk_mgmt_status;
mutex_unlock(&rport->mutex);
- if (!wait_for_completion_timeout(&ch->tsk_mgmt_done,
- msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS)))
- return -1;
+ WARN_ON_ONCE(res < 0);
- return 0;
+ return res > 0 ? 0 : -1;
}
static int srp_abort(struct scsi_cmnd *scmnd)
@@ -2548,7 +2562,7 @@ static int srp_abort(struct scsi_cmnd *scmnd)
shost_printk(KERN_ERR, target->scsi_host,
"Sending SRP abort for tag %#x\n", tag);
if (srp_send_tsk_mgmt(ch, tag, scmnd->device->lun,
- SRP_TSK_ABORT_TASK) == 0)
+ SRP_TSK_ABORT_TASK, NULL) == 0)
ret = SUCCESS;
else if (target->rport->state == SRP_RPORT_LOST)
ret = FAST_IO_FAIL;
@@ -2566,14 +2580,15 @@ static int srp_reset_device(struct scsi_cmnd *scmnd)
struct srp_target_port *target = host_to_target(scmnd->device->host);
struct srp_rdma_ch *ch;
int i;
+ u8 status;
shost_printk(KERN_ERR, target->scsi_host, "SRP reset_device called\n");
ch = &target->ch[0];
if (srp_send_tsk_mgmt(ch, SRP_TAG_NO_REQ, scmnd->device->lun,
- SRP_TSK_LUN_RESET))
+ SRP_TSK_LUN_RESET, &status))
return FAILED;
- if (ch->tsk_mgmt_status)
+ if (status)
return FAILED;
for (i = 0; i < target->ch_count; i++) {
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h
index f6af531f9f32..109eea94d0f9 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.h
+++ b/drivers/infiniband/ulp/srp/ib_srp.h
@@ -168,6 +168,7 @@ struct srp_rdma_ch {
int max_ti_iu_len;
int comp_vector;
+ u64 tsk_mgmt_tag;
struct completion tsk_mgmt_done;
u8 tsk_mgmt_status;
bool connected;
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index aff42d5e2296..16f000a76de5 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -1238,6 +1238,12 @@ static int xpad_init_input(struct usb_xpad *xpad)
input_dev->name = xpad->name;
input_dev->phys = xpad->phys;
usb_to_input_id(xpad->udev, &input_dev->id);
+
+ if (xpad->xtype == XTYPE_XBOX360W) {
+ /* x360w controllers and the receiver have different ids */
+ input_dev->id.product = 0x02a1;
+ }
+
input_dev->dev.parent = &xpad->intf->dev;
input_set_drvdata(input_dev, xpad);
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index cf29f2756b84..c93dd193a496 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -3,6 +3,7 @@
*
* Copyright 2005 Phil Blundell
* Copyright 2010, 2011 David Jander <david@protonic.nl>
+ * Copyright (c) 2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -32,6 +33,7 @@
#include <linux/of_irq.h>
#include <linux/spinlock.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/syscore_ops.h>
struct gpio_button_data {
const struct gpio_keys_button *button;
@@ -57,6 +59,11 @@ struct gpio_keys_drvdata {
struct gpio_button_data data[0];
};
+static struct device *global_dev;
+static struct syscore_ops gpio_keys_syscore_pm_ops;
+
+static void gpio_keys_syscore_resume(void);
+
/*
* SYSFS interface for enabling/disabling keys and switches:
*
@@ -343,14 +350,14 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
const struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned int type = button->type ?: EV_KEY;
- int state = gpio_get_value_cansleep(button->gpio);
+ int state;
+ state = (__gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
if (state < 0) {
dev_err(input->dev.parent, "failed to get gpio state\n");
return;
}
- state = (state ? 1 : 0) ^ button->active_low;
if (type == EV_ABS) {
if (state)
input_event(input, type, button->code, button->value);
@@ -664,6 +671,8 @@ gpio_keys_get_devtree_pdata(struct device *dev)
pdata->nbuttons = nbuttons;
pdata->rep = !!of_get_property(node, "autorepeat", NULL);
+ pdata->name = of_get_property(node, "input-name", NULL);
+ pdata->use_syscore = of_property_read_bool(node, "use-syscore");
i = 0;
for_each_child_of_node(node, pp) {
@@ -710,7 +719,7 @@ gpio_keys_get_devtree_pdata(struct device *dev)
button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL);
if (of_property_read_u32(pp, "debounce-interval",
- &button->debounce_interval))
+ &button->debounce_interval))
button->debounce_interval = 5;
}
@@ -767,6 +776,7 @@ static int gpio_keys_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ global_dev = dev;
ddata->pdata = pdata;
ddata->input = input;
mutex_init(&ddata->disable_lock);
@@ -835,6 +845,11 @@ static int gpio_keys_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, wakeup);
+ if (pdata->use_syscore)
+ gpio_keys_syscore_pm_ops.resume = gpio_keys_syscore_resume;
+
+ register_syscore_ops(&gpio_keys_syscore_pm_ops);
+
return 0;
err_remove_group:
@@ -857,6 +872,7 @@ err_setup_key:
static int gpio_keys_remove(struct platform_device *pdev)
{
sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
+ unregister_syscore_ops(&gpio_keys_syscore_pm_ops);
device_init_wakeup(&pdev->dev, 0);
@@ -864,6 +880,41 @@ static int gpio_keys_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
+static void gpio_keys_syscore_resume(void)
+{
+ struct gpio_keys_drvdata *ddata = dev_get_drvdata(global_dev);
+ struct input_dev *input = ddata->input;
+ struct gpio_button_data *bdata = NULL;
+ int error = 0;
+ int i;
+
+ if (ddata->key_pinctrl) {
+ error = gpio_keys_pinctrl_configure(ddata, true);
+ if (error) {
+ dev_err(global_dev, "failed to put the pin in resume state\n");
+ return;
+ }
+ }
+
+ if (device_may_wakeup(global_dev)) {
+ for (i = 0; i < ddata->pdata->nbuttons; i++) {
+ bdata = &ddata->data[i];
+ if (bdata->button->wakeup)
+ disable_irq_wake(bdata->irq);
+ }
+ } else {
+ mutex_lock(&input->mutex);
+ if (input->users)
+ error = gpio_keys_open(input);
+ mutex_unlock(&input->mutex);
+ }
+
+ if (error)
+ return;
+
+ gpio_keys_report_state(ddata);
+}
+
static int gpio_keys_suspend(struct device *dev)
{
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
@@ -901,6 +952,11 @@ static int gpio_keys_resume(struct device *dev)
int error = 0;
int i;
+ if (ddata->pdata->use_syscore == true) {
+ dev_dbg(global_dev, "Using syscore resume, no need of this resume.\n");
+ return 0;
+ }
+
if (ddata->key_pinctrl) {
error = gpio_keys_pinctrl_configure(ddata, true);
if (error) {
@@ -928,6 +984,21 @@ static int gpio_keys_resume(struct device *dev)
gpio_keys_report_state(ddata);
return 0;
}
+
+#else
+
+static void gpio_keys_syscore_resume(void){}
+
+static int gpio_keys_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int gpio_keys_resume(struct device *dev)
+{
+ return 0;
+}
+
#endif
static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 5cfa1848e37c..dd98d8c8fd1f 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -834,4 +834,14 @@ config INPUT_DRV2667_HAPTICS
source "drivers/input/misc/ots_pat9125/Kconfig"
+config INPUT_STMVL53L0
+ tristate "STM VL53L0 Proximity support"
+ depends on INPUT && I2C
+ help
+ Say Y here if you want to use STMicroelectronics's proximity sensor
+ through I2C interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stmvl53l0.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index a5ab4b762d31..44c026abcb6f 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -80,3 +80,4 @@ obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o
obj-$(CONFIG_INPUT_PIXART_OTS_PAT9125_SWITCH) += ots_pat9125/
+obj-$(CONFIG_INPUT_STMVL53L0) += vl53L0/
diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index 2adfd86c869a..930424e55439 100644
--- a/drivers/input/misc/drv260x.c
+++ b/drivers/input/misc/drv260x.c
@@ -592,7 +592,6 @@ static int drv260x_probe(struct i2c_client *client,
}
haptics->input_dev->name = "drv260x:haptics";
- haptics->input_dev->dev.parent = client->dev.parent;
haptics->input_dev->close = drv260x_close;
input_set_drvdata(haptics->input_dev, haptics);
input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
diff --git a/drivers/input/misc/vl53L0/Makefile b/drivers/input/misc/vl53L0/Makefile
index 4a6be55094b6..f105e1c3c60f 100644
--- a/drivers/input/misc/vl53L0/Makefile
+++ b/drivers/input/misc/vl53L0/Makefile
@@ -9,12 +9,12 @@ FEATURE_USE_CCI := true
ifeq ($(FEATURE_USE_CCI), true)
ccflags-y += -Idrivers/input/misc/vl53L0/inc -DCAMERA_CCI
else
-ccflags-y += -Idrivers/input/misc/vl53L0/inc
+ccflags-y += -Idrivers/input/misc/vl53L0/inc
endif
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
ccflags-y += -Idrivers/media/platform/msm/camera_v2
ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci
-obj-$(CONFIG_STMVL53L0) += stmvl53l0.o
+obj-$(CONFIG_INPUT_STMVL53L0) += stmvl53l0.o
stmvl53l0-objs := stmvl53l0_module.o stmvl53l0_module-i2c.o stmvl53l0_module-cci.o src/vl53l0_api_calibration.o src/vl53l0_api_core.o src/vl53l0_api_histogram.o src/vl53l0_api_ranging.o src/vl53l0_api_strings.o src/vl53l0_api.o src/vl53l0_platform.o src/vl53l0_i2c_platform.o src/vl53l0_port_i2c.o src/vl53l010_api.o src/vl53l010_tuning.o
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index d15b33813021..ed1935f300a7 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -1232,6 +1232,7 @@ static const struct acpi_device_id elan_acpi_id[] = {
{ "ELAN0000", 0 },
{ "ELAN0100", 0 },
{ "ELAN0600", 0 },
+ { "ELAN0605", 0 },
{ "ELAN1000", 0 },
{ }
};
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 073246c7d163..0cdd95801a25 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -211,6 +211,12 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = {
DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"),
},
},
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "C15B"),
+ },
+ },
{ }
};
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 99de4002275e..1d4e8a4ce206 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1128,6 +1128,16 @@ config TOUCHSCREEN_FT5X06_GESTURE
If unsure, say N.
+config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
+ bool "Synaptics DSX firmware update extra sysfs attributes"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE
+ help
+ Say Y here to enable support for extra sysfs attributes
+ supporting firmware update in a development environment.
+ This does not affect the core or other subsystem attributes.
+
+ If unsure, say N.
+
config TOUCHSCREEN_ROHM_BU21023
tristate "ROHM BU21023/24 Dual touch support resistive touchscreens"
depends on I2C
@@ -1206,18 +1216,6 @@ config TOUCHSCREEN_IT7260_I2C
To compile this driver as a module, choose M here: the
module will be called it7258_ts_i2c.
-config TOUCHSCREEN_GT9XX
- bool "Goodix touchpanel GT9xx series"
- depends on I2C
- help
- Say Y here if you have a Goodix GT9xx touchscreen.
- Gt9xx controllers are multi touch controllers which can
- report 5 touches at a time.
-
- If unsure, say N.
-
-source "drivers/input/touchscreen/gt9xx/Kconfig"
-
config TOUCHSCREEN_ST
bool "STMicroelectronics Touchscreen Driver"
depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index a32132cffe92..2e0161cf95bc 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -98,5 +98,4 @@ obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o
obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o
obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o
-obj-$(CONFIG_TOUCHSCREEN_GT9XX) += gt9xx/
obj-$(CONFIG_TOUCHSCREEN_ST) += st/
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
index ac09855fa435..486f8fe242da 100644
--- a/drivers/input/touchscreen/elants_i2c.c
+++ b/drivers/input/touchscreen/elants_i2c.c
@@ -905,9 +905,9 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev)
case QUEUE_HEADER_NORMAL:
report_count = ts->buf[FW_HDR_COUNT];
- if (report_count > 3) {
+ if (report_count == 0 || report_count > 3) {
dev_err(&client->dev,
- "too large report count: %*ph\n",
+ "bad report count: %*ph\n",
HEADER_SIZE, ts->buf);
break;
}
diff --git a/drivers/input/touchscreen/gt9xx/Kconfig b/drivers/input/touchscreen/gt9xx/Kconfig
deleted file mode 100644
index 2e1b5ba567a0..000000000000
--- a/drivers/input/touchscreen/gt9xx/Kconfig
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# Goodix GT9xx Touchscreen driver
-#
-
-config GT9XX_TOUCHPANEL_DRIVER
- tristate "Goodix GT9xx touchpanel driver"
- depends on TOUCHSCREEN_GT9XX
- default n
- help
- This is the main file for touchpanel driver for Goodix GT9xx
- touchscreens.
-
- Say Y here if you have a Goodix GT9xx touchscreen connected
- to your system.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called gt9xx.
-
-config GT9XX_TOUCHPANEL_UPDATE
- tristate "Goodix GT9xx touchpanel auto update support"
- depends on GT9XX_TOUCHPANEL_DRIVER
- default n
- help
- This enables support for firmware update for Goodix GT9xx
- touchscreens.
-
- Say Y here if you have a Goodix GT9xx touchscreen connected
- to your system.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called gt9xx_update.
-
-config GT9XX_TOUCHPANEL_DEBUG
- tristate "Goodix GT9xx Tools for debuging"
- depends on GT9XX_TOUCHPANEL_DRIVER
- default n
- help
- This is application debug interface support for Goodix GT9xx
- touchscreens.
-
- Say Y here if you want to have a Android app debug interface
- to your system.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called gt9xx_tool.
diff --git a/drivers/input/touchscreen/gt9xx/Makefile b/drivers/input/touchscreen/gt9xx/Makefile
deleted file mode 100644
index 482d869a2d37..000000000000
--- a/drivers/input/touchscreen/gt9xx/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-#gt915 touchpanel driver
-
-
-obj-$(CONFIG_GT9XX_TOUCHPANEL_DRIVER) += gt9xx.o
-#gt915 update file
-obj-$(CONFIG_GT9XX_TOUCHPANEL_UPDATE) += gt9xx_update.o
-#debug tool
-obj-$(CONFIG_GT9XX_TOUCHPANEL_DEBUG) += goodix_tool.o
diff --git a/drivers/input/touchscreen/gt9xx/goodix_tool.c b/drivers/input/touchscreen/gt9xx/goodix_tool.c
deleted file mode 100644
index 02fa0cbbe392..000000000000
--- a/drivers/input/touchscreen/gt9xx/goodix_tool.c
+++ /dev/null
@@ -1,600 +0,0 @@
-/* drivers/input/touchscreen/goodix_tool.c
- *
- * 2010 - 2012 Goodix Technology.
- * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be a reference
- * to you, when you are integrating the GOODiX's CTP IC into your system,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * Version:1.6
- * V1.0:2012/05/01,create file.
- * V1.2:2012/06/08,modify some warning.
- * V1.4:2012/08/28,modified to support GT9XX
- * V1.6:new proc name
- */
-
-#include "gt9xx.h"
-#include <linux/mutex.h>
-#include <linux/proc_fs.h>
-#include <linux/debugfs.h>
-
-#define DATA_LENGTH_UINT 512
-#define CMD_HEAD_LENGTH (sizeof(struct st_cmd_head))
-static char procname[20] = {0};
-
-struct st_cmd_head {
- u8 wr; /* write read flag 0:R 1:W 2:PID 3: */
- u8 flag; /* 0:no need flag/int 1: need flag 2:need int */
- u8 flag_addr[2];/* flag address */
- u8 flag_val; /* flag val */
- u8 flag_relation; /* flag_val:flag 0:not equal 1:equal 2:> 3:< */
- u16 circle; /* polling cycle */
- u8 times; /* plling times */
- u8 retry; /* I2C retry times */
- u16 delay; /* delay before read or after write */
- u16 data_len; /* data length */
- u8 addr_len; /* address length */
- u8 addr[2]; /* address */
- u8 res[3]; /* reserved */
-} __packed;
-
-static struct st_cmd_head cmd_head;
-static u8 *cmd_data;
-
-static struct i2c_client *gt_client;
-
-static struct proc_dir_entry *goodix_proc_entry;
-
-static struct mutex lock;
-
-static s32 (*tool_i2c_read)(u8 *, u16);
-static s32 (*tool_i2c_write)(u8 *, u16);
-
-s32 data_length;
-s8 ic_type[16] = {0};
-
-static void tool_set_proc_name(char *procname)
-{
- char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May",
- "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
- char date[20] = {0};
- char month[4] = {0};
- int i = 0, n_month = 1, n_day = 0, n_year = 0, ret;
-
- ret = sscanf(date, "%s %d %d", month, &n_day, &n_year);
- if (!ret)
- return;
- for (i = 0; i < 12; ++i) {
- if (!memcmp(months[i], month, 3)) {
- n_month = i+1;
- break;
- }
- }
-
- snprintf(procname, 20, "gmnode%04d%02d%02d", n_year, n_month, n_day);
-}
-
-static s32 tool_i2c_read_no_extra(u8 *buf, u16 len)
-{
- s32 ret = -1;
- u8 i = 0;
- struct i2c_msg msgs[2] = {
- {
- .flags = !I2C_M_RD,
- .addr = gt_client->addr,
- .len = cmd_head.addr_len,
- .buf = &buf[0],
- },
- {
- .flags = I2C_M_RD,
- .addr = gt_client->addr,
- .len = len,
- .buf = &buf[GTP_ADDR_LENGTH],
- },
- };
-
- for (i = 0; i < cmd_head.retry; i++) {
- ret = i2c_transfer(gt_client->adapter, msgs, 2);
- if (ret > 0)
- break;
- }
-
- if (i == cmd_head.retry) {
- dev_err(&gt_client->dev, "I2C read retry limit over\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-static s32 tool_i2c_write_no_extra(u8 *buf, u16 len)
-{
- s32 ret = -1;
- u8 i = 0;
- struct i2c_msg msg = {
- .flags = !I2C_M_RD,
- .addr = gt_client->addr,
- .len = len,
- .buf = buf,
- };
-
- for (i = 0; i < cmd_head.retry; i++) {
- ret = i2c_transfer(gt_client->adapter, &msg, 1);
- if (ret > 0)
- break;
- }
-
- if (i == cmd_head.retry) {
- dev_err(&gt_client->dev, "I2C write retry limit over\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-static s32 tool_i2c_read_with_extra(u8 *buf, u16 len)
-{
- s32 ret = -1;
- u8 pre[2] = {0x0f, 0xff};
- u8 end[2] = {0x80, 0x00};
-
- tool_i2c_write_no_extra(pre, 2);
- ret = tool_i2c_read_no_extra(buf, len);
- tool_i2c_write_no_extra(end, 2);
-
- return ret;
-}
-
-static s32 tool_i2c_write_with_extra(u8 *buf, u16 len)
-{
- s32 ret = -1;
- u8 pre[2] = {0x0f, 0xff};
- u8 end[2] = {0x80, 0x00};
-
- tool_i2c_write_no_extra(pre, 2);
- ret = tool_i2c_write_no_extra(buf, len);
- tool_i2c_write_no_extra(end, 2);
-
- return ret;
-}
-
-static void register_i2c_func(void)
-{
- if (strcmp(ic_type, "GT8110") && strcmp(ic_type, "GT8105")
- && strcmp(ic_type, "GT801") && strcmp(ic_type, "GT800")
- && strcmp(ic_type, "GT801PLUS") && strcmp(ic_type, "GT811")
- && strcmp(ic_type, "GTxxx")) {
- tool_i2c_read = tool_i2c_read_with_extra;
- tool_i2c_write = tool_i2c_write_with_extra;
- pr_debug("I2C function: with pre and end cmd\n");
- } else {
- tool_i2c_read = tool_i2c_read_no_extra;
- tool_i2c_write = tool_i2c_write_no_extra;
- pr_info("I2C function: without pre and end cmd\n");
- }
-}
-
-static void unregister_i2c_func(void)
-{
- tool_i2c_read = NULL;
- tool_i2c_write = NULL;
- pr_info("I2C function: unregister i2c transfer function\n");
-}
-
-void uninit_wr_node(void)
-{
- cmd_data = NULL;
- unregister_i2c_func();
- proc_remove(goodix_proc_entry);
-}
-
-static u8 relation(u8 src, u8 dst, u8 rlt)
-{
- u8 ret = 0;
-
- switch (rlt) {
-
- case 0:
- ret = (src != dst) ? true : false;
- break;
-
- case 1:
- ret = (src == dst) ? true : false;
- pr_debug("equal:src:0x%02x dst:0x%02x ret:%d\n",
- src, dst, (s32)ret);
- break;
-
- case 2:
- ret = (src > dst) ? true : false;
- break;
-
- case 3:
- ret = (src < dst) ? true : false;
- break;
-
- case 4:
- ret = (src & dst) ? true : false;
- break;
-
- case 5:
- ret = (!(src | dst)) ? true : false;
- break;
-
- default:
- ret = false;
- break;
- }
-
- return ret;
-}
-
-/*
- * Function:
- * Comfirm function.
- * Input:
- * None.
- * Output:
- * Return write length.
- */
-static u8 comfirm(void)
-{
- s32 i = 0;
- u8 buf[32];
-
- memcpy(buf, cmd_head.flag_addr, cmd_head.addr_len);
-
- for (i = 0; i < cmd_head.times; i++) {
- if (tool_i2c_read(buf, 1) <= 0) {
- dev_err(&gt_client->dev, "Read flag data failed");
- return FAIL;
- }
- if (true == relation(buf[GTP_ADDR_LENGTH], cmd_head.flag_val,
- cmd_head.flag_relation)) {
- pr_debug("value at flag addr:0x%02x\n",
- buf[GTP_ADDR_LENGTH]);
- pr_debug("flag value:0x%02x\n", cmd_head.flag_val);
- break;
- }
-
- msleep(cmd_head.circle);
- }
-
- if (i >= cmd_head.times) {
- dev_err(&gt_client->dev, "Didn't get the flag to continue");
- return FAIL;
- }
-
- return SUCCESS;
-}
-
-#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
-static s32 fill_update_info(char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- u8 buf[4];
-
- buf[0] = show_len >> 8;
- buf[1] = show_len & 0xff;
- buf[2] = total_len >> 8;
- buf[3] = total_len & 0xff;
- return simple_read_from_buffer(user_buf, count, ppos,
- buf, sizeof(buf));
-}
-#else
-static s32 fill_update_info(char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- return -ENODEV;
-}
-#endif
-
-/*
- * Function:
- * Goodix tool write function.
- * Input:
- * standard proc write function param.
- * Output:
- * Return write length.
- */
-static ssize_t goodix_tool_write(struct file *filp, const char __user *userbuf,
- size_t count, loff_t *ppos)
-{
- s32 ret = 0;
-
- mutex_lock(&lock);
- ret = copy_from_user(&cmd_head, userbuf, CMD_HEAD_LENGTH);
- if (ret) {
- dev_err(&gt_client->dev, "copy_from_user failed");
- ret = -EFAULT;
- goto exit;
- }
-
- dev_dbg(&gt_client->dev,
- "wr: 0x%02x, flag:0x%02x, flag addr:0x%02x%02x\n", cmd_head.wr,
- cmd_head.flag, cmd_head.flag_addr[0], cmd_head.flag_addr[1]);
- dev_dbg(&gt_client->dev,
- "flag val:0x%02x, flag rel:0x%02x,\n", cmd_head.flag_val,
- cmd_head.flag_relation);
- dev_dbg(&gt_client->dev, "circle:%u, times:%u, retry:%u, delay:%u\n",
- (s32) cmd_head.circle, (s32) cmd_head.times,
- (s32) cmd_head.retry, (s32)cmd_head.delay);
- dev_dbg(&gt_client->dev,
- "data len:%u, addr len:%u, addr:0x%02x%02x, write len: %u\n",
- (s32)cmd_head.data_len, (s32)cmd_head.addr_len,
- cmd_head.addr[0], cmd_head.addr[1], (s32)count);
-
- if (cmd_head.data_len > (data_length - GTP_ADDR_LENGTH)) {
- dev_err(&gt_client->dev, "data len %u > data buff %d, rejected\n",
- cmd_head.data_len, (data_length - GTP_ADDR_LENGTH));
- ret = -EINVAL;
- goto exit;
- }
-
- if (cmd_head.wr == GTP_RW_WRITE) {
- ret = copy_from_user(&cmd_data[GTP_ADDR_LENGTH],
- &userbuf[CMD_HEAD_LENGTH], cmd_head.data_len);
- if (ret) {
- dev_err(&gt_client->dev, "copy_from_user failed");
- ret = -EFAULT;
- goto exit;
- }
-
- memcpy(&cmd_data[GTP_ADDR_LENGTH - cmd_head.addr_len],
- cmd_head.addr, cmd_head.addr_len);
-
- if (cmd_head.flag == GTP_NEED_FLAG) {
- if (comfirm() == FAIL) {
- dev_err(&gt_client->dev, "Confirm fail");
- ret = -EINVAL;
- goto exit;
- }
- } else if (cmd_head.flag == GTP_NEED_INTERRUPT) {
- /* Need interrupt! */
- }
- if (tool_i2c_write(
- &cmd_data[GTP_ADDR_LENGTH - cmd_head.addr_len],
- cmd_head.data_len + cmd_head.addr_len) <= 0) {
- dev_err(&gt_client->dev, "Write data failed");
- ret = -EIO;
- goto exit;
- }
-
- if (cmd_head.delay)
- msleep(cmd_head.delay);
-
- ret = cmd_head.data_len + CMD_HEAD_LENGTH;
- goto exit;
- } else if (cmd_head.wr == GTP_RW_WRITE_IC_TYPE) { /* Write ic type */
- ret = copy_from_user(&cmd_data[0],
- &userbuf[CMD_HEAD_LENGTH],
- cmd_head.data_len);
- if (ret) {
- dev_err(&gt_client->dev, "copy_from_user failed");
- ret = -EFAULT;
- goto exit;
- }
-
- if (cmd_head.data_len > sizeof(ic_type)) {
- dev_err(&gt_client->dev,
- "data len %u > data buff %zu, rejected\n",
- cmd_head.data_len, sizeof(ic_type));
- ret = -EINVAL;
- goto exit;
- }
- memcpy(ic_type, cmd_data, cmd_head.data_len);
-
- register_i2c_func();
-
- ret = cmd_head.data_len + CMD_HEAD_LENGTH;
- goto exit;
- } else if (cmd_head.wr == GTP_RW_NO_WRITE) {
- ret = cmd_head.data_len + CMD_HEAD_LENGTH;
- goto exit;
- } else if (cmd_head.wr == GTP_RW_DISABLE_IRQ) { /* disable irq! */
- gtp_irq_disable(i2c_get_clientdata(gt_client));
-
- #if GTP_ESD_PROTECT
- gtp_esd_switch(gt_client, SWITCH_OFF);
- #endif
- ret = CMD_HEAD_LENGTH;
- goto exit;
- } else if (cmd_head.wr == GTP_RW_ENABLE_IRQ) { /* enable irq! */
- gtp_irq_enable(i2c_get_clientdata(gt_client));
-
- #if GTP_ESD_PROTECT
- gtp_esd_switch(gt_client, SWITCH_ON);
- #endif
- ret = CMD_HEAD_LENGTH;
- goto exit;
- } else if (cmd_head.wr == GTP_RW_CHECK_RAWDIFF_MODE) {
- struct goodix_ts_data *ts = i2c_get_clientdata(gt_client);
-
- ret = copy_from_user(&cmd_data[GTP_ADDR_LENGTH],
- &userbuf[CMD_HEAD_LENGTH], cmd_head.data_len);
- if (ret) {
- dev_err(&gt_client->dev, "copy_from_user failed");
- goto exit;
- }
- if (cmd_data[GTP_ADDR_LENGTH]) {
- pr_debug("gtp enter rawdiff\n");
- ts->gtp_rawdiff_mode = true;
- } else {
- ts->gtp_rawdiff_mode = false;
- pr_debug("gtp leave rawdiff\n");
- }
- ret = CMD_HEAD_LENGTH;
- goto exit;
- } else if (cmd_head.wr == GTP_RW_ENTER_UPDATE_MODE) {
- /* Enter update mode! */
- if (gup_enter_update_mode(gt_client) == FAIL) {
- ret = -EBUSY;
- goto exit;
- }
- } else if (cmd_head.wr == GTP_RW_LEAVE_UPDATE_MODE) {
- /* Leave update mode! */
- gup_leave_update_mode(gt_client);
- } else if (cmd_head.wr == GTP_RW_UPDATE_FW) {
- /* Update firmware! */
- show_len = 0;
- total_len = 0;
- if (cmd_head.data_len + 1 > data_length) {
- dev_err(&gt_client->dev, "data len %u > data buff %d, rejected\n",
- cmd_head.data_len + 1, data_length);
- ret = -EINVAL;
- goto exit;
- }
- memset(cmd_data, 0, cmd_head.data_len + 1);
- memcpy(cmd_data, &userbuf[CMD_HEAD_LENGTH],
- cmd_head.data_len);
-
- if (gup_update_proc((void *)cmd_data) == FAIL) {
- ret = -EBUSY;
- goto exit;
- }
- }
- ret = CMD_HEAD_LENGTH;
-
-exit:
- memset(&cmd_head, 0, sizeof(cmd_head));
- cmd_head.wr = 0xFF;
-
- mutex_unlock(&lock);
- return ret;
-}
-
-/*
- * Function:
- * Goodix tool read function.
- * Input:
- * standard proc read function param.
- * Output:
- * Return read length.
- */
-static ssize_t goodix_tool_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- u16 data_len = 0;
- s32 ret;
- u8 buf[32];
-
- mutex_lock(&lock);
- if (cmd_head.wr & 0x1) {
- dev_err(&gt_client->dev, "command head wrong\n");
- ret = -EINVAL;
- goto exit;
- }
-
- switch (cmd_head.wr) {
- case GTP_RW_READ:
- if (cmd_head.flag == GTP_NEED_FLAG) {
- if (comfirm() == FAIL) {
- dev_err(&gt_client->dev, "Confirm fail");
- ret = -EINVAL;
- goto exit;
- }
- } else if (cmd_head.flag == GTP_NEED_INTERRUPT) {
- /* Need interrupt! */
- }
-
- memcpy(cmd_data, cmd_head.addr, cmd_head.addr_len);
-
- pr_debug("[CMD HEAD DATA] ADDR:0x%02x%02x.\n", cmd_data[0],
- cmd_data[1]);
- pr_debug("[CMD HEAD ADDR] ADDR:0x%02x%02x.\n", cmd_head.addr[0],
- cmd_head.addr[1]);
-
- if (cmd_head.delay)
- msleep(cmd_head.delay);
-
- data_len = cmd_head.data_len;
- if (data_len <= 0 || (data_len > data_length)) {
- dev_err(&gt_client->dev, "Invalid data length %d\n",
- data_len);
- ret = -EINVAL;
- goto exit;
- }
- if (data_len > count)
- data_len = count;
-
- if (tool_i2c_read(cmd_data, data_len) <= 0) {
- dev_err(&gt_client->dev, "Read data failed\n");
- ret = -EIO;
- goto exit;
- }
- ret = simple_read_from_buffer(user_buf, count, ppos,
- &cmd_data[GTP_ADDR_LENGTH], data_len);
- break;
- case GTP_RW_FILL_INFO:
- ret = fill_update_info(user_buf, count, ppos);
- break;
- case GTP_RW_READ_VERSION:
- /* Read driver version */
- data_len = scnprintf(buf, sizeof(buf), "%s\n",
- GTP_DRIVER_VERSION);
- ret = simple_read_from_buffer(user_buf, count, ppos,
- buf, data_len);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
-exit:
- mutex_unlock(&lock);
- return ret;
-}
-
-static const struct file_operations goodix_proc_fops = {
- .write = goodix_tool_write,
- .read = goodix_tool_read,
- .open = simple_open,
- .owner = THIS_MODULE,
-};
-
-s32 init_wr_node(struct i2c_client *client)
-{
- u8 i;
-
- gt_client = client;
- memset(&cmd_head, 0, sizeof(cmd_head));
- cmd_data = NULL;
-
- i = GTP_I2C_RETRY_5;
- while ((!cmd_data) && i) {
- cmd_data = devm_kzalloc(&client->dev,
- i * DATA_LENGTH_UINT, GFP_KERNEL);
- if (cmd_data)
- break;
- i--;
- }
- if (i) {
- data_length = i * DATA_LENGTH_UINT;
- dev_dbg(&client->dev, "Applied memory size:%d", data_length);
- }
-
- cmd_head.addr_len = 2;
- cmd_head.retry = GTP_I2C_RETRY_5;
-
- register_i2c_func();
-
- mutex_init(&lock);
- tool_set_proc_name(procname);
- goodix_proc_entry = proc_create(procname,
- S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP,
- goodix_proc_entry,
- &goodix_proc_fops);
- if (goodix_proc_entry == NULL) {
- dev_err(&client->dev, "Couldn't create proc entry");
- return FAIL;
- }
-
- return SUCCESS;
-}
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.c b/drivers/input/touchscreen/gt9xx/gt9xx.c
deleted file mode 100644
index ead935120624..000000000000
--- a/drivers/input/touchscreen/gt9xx/gt9xx.c
+++ /dev/null
@@ -1,2564 +0,0 @@
-/* drivers/input/touchscreen/gt9xx.c
- *
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
- *
- * 2010 - 2013 Goodix Technology.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be a reference
- * to you, when you are integrating the GOODiX's CTP IC into your system,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * Version: 1.8
- * Authors: andrew@goodix.com, meta@goodix.com
- * Release Date: 2013/04/25
- * Revision record:
- * V1.0:
- * first Release. By Andrew, 2012/08/31
- * V1.2:
- * modify gtp_reset_guitar,slot report,tracking_id & 0x0F.
- * By Andrew, 2012/10/15
- * V1.4:
- * modify gt9xx_update.c. By Andrew, 2012/12/12
- * V1.6:
- * 1. new heartbeat/esd_protect mechanism(add external watchdog)
- * 2. doze mode, sliding wakeup
- * 3. 3 more cfg_group(GT9 Sensor_ID: 0~5)
- * 3. config length verification
- * 4. names & comments
- * By Meta, 2013/03/11
- * V1.8:
- * 1. pen/stylus identification
- * 2. read double check & fixed config support
- * 2. new esd & slide wakeup optimization
- * By Meta, 2013/06/08
- */
-
-#include <linux/regulator/consumer.h>
-#include "gt9xx.h"
-
-#include <linux/of_gpio.h>
-#include <linux/irq.h>
-#include <linux/module.h>
-#include <linux/input/mt.h>
-#include <linux/debugfs.h>
-#include <linux/interrupt.h>
-
-#define GOODIX_DEV_NAME "Goodix-CTP"
-#define CFG_MAX_TOUCH_POINTS 5
-#define GOODIX_COORDS_ARR_SIZE 4
-#define MAX_BUTTONS 4
-
-#define GOODIX_VTG_MIN_UV 2600000
-#define GOODIX_VTG_MAX_UV 3300000
-#define GOODIX_I2C_VTG_MIN_UV 1800000
-#define GOODIX_I2C_VTG_MAX_UV 1800000
-#define GOODIX_VDD_LOAD_MIN_UA 0
-#define GOODIX_VDD_LOAD_MAX_UA 10000
-#define GOODIX_VIO_LOAD_MIN_UA 0
-#define GOODIX_VIO_LOAD_MAX_UA 10000
-
-#define RESET_DELAY_T3_US 200 /* T3: > 100us */
-#define RESET_DELAY_T4 20 /* T4: > 5ms */
-#define SLEEP_DELAY_US 5000
-#define WAKE_UP_DELAY_US 5000
-
-#define PHY_BUF_SIZE 32
-#define PROP_NAME_SIZE 24
-
-#define GTP_MAX_TOUCH 5
-#define GTP_ESD_CHECK_CIRCLE_MS 2000
-
-static void gtp_int_sync(struct goodix_ts_data *ts, int ms);
-static int gtp_i2c_test(struct i2c_client *client);
-static int goodix_power_off(struct goodix_ts_data *ts);
-static int goodix_power_on(struct goodix_ts_data *ts);
-
-#if defined(CONFIG_FB)
-static int fb_notifier_callback(struct notifier_block *self,
- unsigned long event, void *data);
-static int goodix_ts_suspend(struct device *dev);
-static int goodix_ts_resume(struct device *dev);
-#elif defined(CONFIG_HAS_EARLYSUSPEND)
-static void goodix_ts_early_suspend(struct early_suspend *h);
-static void goodix_ts_late_resume(struct early_suspend *h);
-#endif
-
-#if GTP_ESD_PROTECT
-static struct delayed_work gtp_esd_check_work;
-static struct workqueue_struct *gtp_esd_check_workqueue;
-static void gtp_esd_check_func(struct work_struct *work);
-static int gtp_init_ext_watchdog(struct i2c_client *client);
-#endif
-
-enum doze {
- DOZE_DISABLED = 0,
- DOZE_ENABLED = 1,
- DOZE_WAKEUP = 2,
-};
-static enum doze doze_status = DOZE_DISABLED;
-static s8 gtp_enter_doze(struct goodix_ts_data *ts);
-
-bool init_done;
-static u8 chip_gt9xxs; /* true if ic is gt9xxs, like gt915s */
-u8 grp_cfg_version;
-struct i2c_client *i2c_connect_client;
-
-#define GTP_DEBUGFS_DIR "ts_debug"
-#define GTP_DEBUGFS_FILE_SUSPEND "suspend"
-#define GTP_DEBUGFS_FILE_DATA "data"
-#define GTP_DEBUGFS_FILE_ADDR "addr"
-
-/*******************************************************
-Function:
- Read data from the i2c slave device.
-Input:
- client: i2c device.
- buf[0~1]: read start address.
- buf[2~len-1]: read data buffer.
- len: GTP_ADDR_LENGTH + read bytes count
-Output:
- numbers of i2c_msgs to transfer:
- 2: succeed, otherwise: failed
-*********************************************************/
-int gtp_i2c_read(struct i2c_client *client, u8 *buf, int len)
-{
- struct goodix_ts_data *ts = i2c_get_clientdata(client);
- int ret = -EIO;
- u8 retries;
- struct i2c_msg msgs[2] = {
- {
- .flags = !I2C_M_RD,
- .addr = client->addr,
- .len = GTP_ADDR_LENGTH,
- .buf = &buf[0],
- },
- {
- .flags = I2C_M_RD,
- .addr = client->addr,
- .len = len - GTP_ADDR_LENGTH,
- .buf = &buf[GTP_ADDR_LENGTH],
- },
- };
-
- for (retries = 0; retries < GTP_I2C_RETRY_5; retries++) {
- ret = i2c_transfer(client->adapter, msgs, 2);
- if (ret == 2)
- break;
- dev_err(&client->dev, "I2C retry: %d\n", retries + 1);
- }
- if (retries == GTP_I2C_RETRY_5) {
- if (ts->pdata->slide_wakeup)
- /* reset chip would quit doze mode */
- if (doze_status == DOZE_ENABLED)
- return ret;
-
- if (init_done)
- gtp_reset_guitar(ts, 10);
- else
- dev_warn(&client->dev,
- "gtp_reset_guitar exit init_done=%d:\n",
- init_done);
- }
- return ret;
-}
-
-/*******************************************************
-Function:
- Write data to the i2c slave device.
-Input:
- client: i2c device.
- buf[0~1]: write start address.
- buf[2~len-1]: data buffer
- len: GTP_ADDR_LENGTH + write bytes count
-Output:
- numbers of i2c_msgs to transfer:
- 1: succeed, otherwise: failed
-*********************************************************/
-int gtp_i2c_write(struct i2c_client *client, u8 *buf, int len)
-{
- struct goodix_ts_data *ts = i2c_get_clientdata(client);
- int ret = -EIO;
- u8 retries;
- struct i2c_msg msg = {
- .flags = !I2C_M_RD,
- .addr = client->addr,
- .len = len,
- .buf = buf,
- };
-
- for (retries = 0; retries < GTP_I2C_RETRY_5; retries++) {
- ret = i2c_transfer(client->adapter, &msg, 1);
- if (ret == 1)
- break;
- dev_err(&client->dev, "I2C retry: %d\n", retries + 1);
- }
- if (retries == GTP_I2C_RETRY_5) {
- if (ts->pdata->slide_wakeup)
- if (doze_status == DOZE_ENABLED)
- return ret;
-
- if (init_done)
- gtp_reset_guitar(ts, 10);
- else
- dev_warn(&client->dev,
- "gtp_reset_guitar exit init_done=%d:\n",
- init_done);
- }
- return ret;
-}
-
-/*******************************************************
-Function:
- i2c read twice, compare the results
-Input:
- client: i2c device
- addr: operate address
- rxbuf: read data to store, if compare successful
- len: bytes to read
-Output:
- FAIL: read failed
- SUCCESS: read successful
-*********************************************************/
-int gtp_i2c_read_dbl_check(struct i2c_client *client,
- u16 addr, u8 *rxbuf, int len)
-{
- u8 buf[16] = {0};
- u8 confirm_buf[16] = {0};
- u8 retry = 0;
-
- while (retry++ < GTP_I2C_RETRY_3) {
- memset(buf, 0xAA, 16);
- buf[0] = (u8)(addr >> 8);
- buf[1] = (u8)(addr & 0xFF);
- gtp_i2c_read(client, buf, len + 2);
-
- memset(confirm_buf, 0xAB, 16);
- confirm_buf[0] = (u8)(addr >> 8);
- confirm_buf[1] = (u8)(addr & 0xFF);
- gtp_i2c_read(client, confirm_buf, len + 2);
-
- if (!memcmp(buf, confirm_buf, len + 2))
- break;
- }
- if (retry < GTP_I2C_RETRY_3) {
- memcpy(rxbuf, confirm_buf + 2, len);
- return SUCCESS;
- }
- dev_err(&client->dev,
- "i2c read 0x%04X, %d bytes, double check failed!", addr, len);
- return FAIL;
-}
-
-/*******************************************************
-Function:
- Send config data.
-Input:
- client: i2c device.
-Output:
- result of i2c write operation.
- > 0: succeed, otherwise: failed
-*********************************************************/
-int gtp_send_cfg(struct goodix_ts_data *ts)
-{
- int ret = 0;
- int retry;
-
- if (ts->pdata->driver_send_cfg) {
- if (ts->fixed_cfg) {
- dev_dbg(&ts->client->dev,
- "Ic fixed config, no config sent!");
- ret = 2;
- } else {
- for (retry = 0; retry < GTP_I2C_RETRY_5; retry++) {
- ret = gtp_i2c_write(ts->client,
- ts->config_data,
- GTP_CONFIG_MAX_LENGTH +
- GTP_ADDR_LENGTH);
- if (ret > 0)
- break;
- }
- }
- }
-
- return ret;
-}
-
-/*******************************************************
-Function:
- Disable irq function
-Input:
- ts: goodix i2c_client private data
-Output:
- None.
-*********************************************************/
-void gtp_irq_disable(struct goodix_ts_data *ts)
-{
- unsigned long irqflags;
-
- spin_lock_irqsave(&ts->irq_lock, irqflags);
- if (!ts->irq_is_disabled) {
- ts->irq_is_disabled = true;
- disable_irq_nosync(ts->client->irq);
- }
- spin_unlock_irqrestore(&ts->irq_lock, irqflags);
-}
-
-/*******************************************************
-Function:
- Enable irq function
-Input:
- ts: goodix i2c_client private data
-Output:
- None.
-*********************************************************/
-void gtp_irq_enable(struct goodix_ts_data *ts)
-{
- unsigned long irqflags = 0;
-
- spin_lock_irqsave(&ts->irq_lock, irqflags);
- if (ts->irq_is_disabled) {
- enable_irq(ts->client->irq);
- ts->irq_is_disabled = false;
- }
- spin_unlock_irqrestore(&ts->irq_lock, irqflags);
-}
-
-/*******************************************************
-Function:
- Report touch point event
-Input:
- ts: goodix i2c_client private data
- id: trackId
- x: input x coordinate
- y: input y coordinate
- w: input pressure
-Output:
- None.
-*********************************************************/
-static void gtp_touch_down(struct goodix_ts_data *ts, int id, int x, int y,
- int w)
-{
- if (ts->pdata->change_x2y)
- swap(x, y);
-
- input_mt_slot(ts->input_dev, id);
- input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
- input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
- input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
- input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
- input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
-}
-
-/*******************************************************
-Function:
- Report touch release event
-Input:
- ts: goodix i2c_client private data
-Output:
- None.
-*********************************************************/
-static void gtp_touch_up(struct goodix_ts_data *ts, int id)
-{
- input_mt_slot(ts->input_dev, id);
- input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
-}
-
-
-
-/*******************************************************
-Function:
- Goodix touchscreen work function
-Input:
- work: work struct of goodix_workqueue
-Output:
- None.
-*********************************************************/
-static void goodix_ts_work_func(struct work_struct *work)
-{
- u8 end_cmd[3] = { GTP_READ_COOR_ADDR >> 8,
- GTP_READ_COOR_ADDR & 0xFF, 0};
- u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1] = {
- GTP_READ_COOR_ADDR >> 8,
- GTP_READ_COOR_ADDR & 0xFF};
- u8 touch_num = 0;
- u8 finger = 0;
- static u16 pre_touch;
- static u8 pre_key;
- static u8 pre_pen;
- u8 key_value = 0;
- u8 *coor_data = NULL;
- s32 input_x = 0;
- s32 input_y = 0;
- s32 input_w = 0;
- s32 id = 0;
- s32 i = 0;
- int ret = -1;
- struct goodix_ts_data *ts = NULL;
- u8 doze_buf[3] = {0x81, 0x4B};
-
- ts = container_of(work, struct goodix_ts_data, work);
-#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
- if (ts->enter_update)
- return;
-#endif
-
- if (ts->pdata->slide_wakeup) {
- if (doze_status == DOZE_ENABLED) {
- ret = gtp_i2c_read(ts->client, doze_buf, 3);
- if (ret > 0) {
- if (doze_buf[2] == 0xAA) {
- dev_dbg(&ts->client->dev,
- "Slide(0xAA) To Light up the screen!");
- doze_status = DOZE_WAKEUP;
- input_report_key(
- ts->input_dev, KEY_POWER, 1);
- input_sync(ts->input_dev);
- input_report_key(
- ts->input_dev, KEY_POWER, 0);
- input_sync(ts->input_dev);
- /* clear 0x814B */
- doze_buf[2] = 0x00;
- gtp_i2c_write(ts->client, doze_buf, 3);
- } else if (doze_buf[2] == 0xBB) {
- dev_dbg(&ts->client->dev,
- "Slide(0xBB) To Light up the screen!");
- doze_status = DOZE_WAKEUP;
- input_report_key(ts->input_dev,
- KEY_POWER, 1);
- input_sync(ts->input_dev);
- input_report_key(ts->input_dev,
- KEY_POWER, 0);
- input_sync(ts->input_dev);
- /* clear 0x814B*/
- doze_buf[2] = 0x00;
- gtp_i2c_write(ts->client, doze_buf, 3);
- } else if (0xC0 == (doze_buf[2] & 0xC0)) {
- dev_dbg(&ts->client->dev,
- "double click to light up the screen!");
- doze_status = DOZE_WAKEUP;
- input_report_key(ts->input_dev,
- KEY_POWER, 1);
- input_sync(ts->input_dev);
- input_report_key(ts->input_dev,
- KEY_POWER, 0);
- input_sync(ts->input_dev);
- /* clear 0x814B */
- doze_buf[2] = 0x00;
- gtp_i2c_write(ts->client, doze_buf, 3);
- } else {
- gtp_enter_doze(ts);
- }
- }
- if (ts->use_irq)
- gtp_irq_enable(ts);
-
- return;
- }
- }
-
- ret = gtp_i2c_read(ts->client, point_data, 12);
- if (ret < 0) {
- dev_err(&ts->client->dev,
- "I2C transfer error. errno:%d\n ", ret);
- goto exit_work_func;
- }
-
- finger = point_data[GTP_ADDR_LENGTH];
- if ((finger & 0x80) == 0)
- goto exit_work_func;
-
- touch_num = finger & 0x0f;
- if (touch_num > GTP_MAX_TOUCH)
- goto exit_work_func;
-
- if (touch_num > 1) {
- u8 buf[8 * GTP_MAX_TOUCH] = { (GTP_READ_COOR_ADDR + 10) >> 8,
- (GTP_READ_COOR_ADDR + 10) & 0xff };
-
- ret = gtp_i2c_read(ts->client, buf,
- 2 + 8 * (touch_num - 1));
- memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));
- }
-
-
- key_value = point_data[3 + 8 * touch_num];
-
- if (key_value || pre_key) {
- for (i = 0; i < ts->pdata->num_button; i++) {
- input_report_key(ts->input_dev,
- ts->pdata->button_map[i],
- key_value & (0x01<<i));
- }
- touch_num = 0;
- pre_touch = 0;
- }
-
- pre_key = key_value;
-
- if (ts->pdata->with_pen) {
- if (pre_pen && (touch_num == 0)) {
- dev_dbg(&ts->client->dev, "Pen touch UP(Slot)!");
- input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
- input_mt_slot(ts->input_dev, 5);
- input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
- pre_pen = 0;
- }
- }
-
- if (pre_touch || touch_num) {
- s32 pos = 0;
- u16 touch_index = 0;
-
- coor_data = &point_data[3];
- if (touch_num) {
- id = coor_data[pos] & 0x0F;
- if (ts->pdata->with_pen) {
- id = coor_data[pos];
- if (id == 128) {
- dev_dbg(&ts->client->dev,
- "Pen touch DOWN(Slot)!");
- input_x = coor_data[pos + 1]
- | (coor_data[pos + 2] << 8);
- input_y = coor_data[pos + 3]
- | (coor_data[pos + 4] << 8);
- input_w = coor_data[pos + 5]
- | (coor_data[pos + 6] << 8);
-
- input_report_key(ts->input_dev,
- BTN_TOOL_PEN, 1);
- input_mt_slot(ts->input_dev, 5);
- input_report_abs(ts->input_dev,
- ABS_MT_TRACKING_ID, 5);
- input_report_abs(ts->input_dev,
- ABS_MT_POSITION_X, input_x);
- input_report_abs(ts->input_dev,
- ABS_MT_POSITION_Y, input_y);
- input_report_abs(ts->input_dev,
- ABS_MT_TOUCH_MAJOR, input_w);
- dev_dbg(&ts->client->dev,
- "Pen/Stylus: (%d, %d)[%d]",
- input_x, input_y, input_w);
- pre_pen = 1;
- pre_touch = 0;
- }
- }
-
- touch_index |= (0x01<<id);
- }
-
- for (i = 0; i < GTP_MAX_TOUCH; i++) {
- if (ts->pdata->with_pen)
- if (pre_pen == 1)
- break;
-
- if (touch_index & (0x01<<i)) {
- input_x = coor_data[pos + 1] |
- coor_data[pos + 2] << 8;
- input_y = coor_data[pos + 3] |
- coor_data[pos + 4] << 8;
- input_w = coor_data[pos + 5] |
- coor_data[pos + 6] << 8;
-
- gtp_touch_down(ts, id,
- input_x, input_y, input_w);
- pre_touch |= 0x01 << i;
-
- pos += 8;
- id = coor_data[pos] & 0x0F;
- touch_index |= (0x01<<id);
- } else {
- gtp_touch_up(ts, i);
- pre_touch &= ~(0x01 << i);
- }
- }
- }
- input_sync(ts->input_dev);
-
-exit_work_func:
- if (!ts->gtp_rawdiff_mode) {
- ret = gtp_i2c_write(ts->client, end_cmd, 3);
- if (ret < 0)
- dev_warn(&ts->client->dev, "I2C write end_cmd error!\n");
-
- }
- if (ts->use_irq)
- gtp_irq_enable(ts);
-
- return;
-}
-
-/*******************************************************
-Function:
- External interrupt service routine for interrupt mode.
-Input:
- irq: interrupt number.
- dev_id: private data pointer
-Output:
- Handle Result.
- IRQ_HANDLED: interrupt handled successfully
-*********************************************************/
-static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
-{
- struct goodix_ts_data *ts = dev_id;
-
- gtp_irq_disable(ts);
-
- queue_work(ts->goodix_wq, &ts->work);
-
- return IRQ_HANDLED;
-}
-/*******************************************************
-Function:
- Synchronization.
-Input:
- ms: synchronization time in millisecond.
-Output:
- None.
-*******************************************************/
-void gtp_int_sync(struct goodix_ts_data *ts, int ms)
-{
- gpio_direction_output(ts->pdata->irq_gpio, 0);
- msleep(ms);
- gpio_direction_input(ts->pdata->irq_gpio);
-}
-
-/*******************************************************
-Function:
- Reset chip.
-Input:
- ms: reset time in millisecond, must >10ms
-Output:
- None.
-*******************************************************/
-void gtp_reset_guitar(struct goodix_ts_data *ts, int ms)
-{
- /* This reset sequence will selcet I2C slave address */
- gpio_direction_output(ts->pdata->reset_gpio, 0);
- msleep(ms);
-
- if (ts->client->addr == GTP_I2C_ADDRESS_HIGH)
- gpio_direction_output(ts->pdata->irq_gpio, 1);
- else
- gpio_direction_output(ts->pdata->irq_gpio, 0);
-
- usleep_range(RESET_DELAY_T3_US, RESET_DELAY_T3_US + 1);
- gpio_direction_output(ts->pdata->reset_gpio, 1);
- msleep(RESET_DELAY_T4);
-
- gpio_direction_input(ts->pdata->reset_gpio);
-
- gtp_int_sync(ts, 50);
-
-#if GTP_ESD_PROTECT
- gtp_init_ext_watchdog(ts->client);
-#endif
-}
-
-#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_FB)
-/*******************************************************
-Function:
- Enter doze mode for sliding wakeup.
-Input:
- ts: goodix tp private data
-Output:
- 1: succeed, otherwise failed
-*******************************************************/
-static s8 gtp_enter_doze(struct goodix_ts_data *ts)
-{
- int ret = -1;
- s8 retry = 0;
- u8 i2c_control_buf[3] = {
- (u8)(GTP_REG_SLEEP >> 8),
- (u8)GTP_REG_SLEEP, 8};
-
- if (ts->pdata->dbl_clk_wakeup)
- i2c_control_buf[2] = 0x09;
-
- gtp_irq_disable(ts);
-
- while (retry++ < GTP_I2C_RETRY_3) {
- i2c_control_buf[0] = 0x80;
- i2c_control_buf[1] = 0x46;
- ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
- if (ret < 0) {
- dev_err(&ts->client->dev,
- "failed to set doze flag into 0x8046, %d",
- retry);
- continue;
- }
- i2c_control_buf[0] = 0x80;
- i2c_control_buf[1] = 0x40;
- ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
- if (ret > 0) {
- doze_status = DOZE_ENABLED;
- dev_dbg(&ts->client->dev,
- "GTP has been working in doze mode!");
- gtp_irq_enable(ts);
- return ret;
- }
- msleep(20);
- }
- dev_err(&ts->client->dev, "GTP send doze cmd failed.\n");
- gtp_irq_enable(ts);
- return ret;
-}
-/**
- * gtp_enter_sleep - Enter sleep mode
- * @ts: driver private data
- *
- * Returns zero on success, else an error.
- */
-static u8 gtp_enter_sleep(struct goodix_ts_data *ts)
-{
- int ret = -1;
- s8 retry = 0;
- u8 i2c_control_buf[3] = {
- (u8)(GTP_REG_SLEEP >> 8),
- (u8)GTP_REG_SLEEP, 5};
-
- ret = gpio_direction_output(ts->pdata->irq_gpio, 0);
- if (ret)
- dev_err(&ts->client->dev,
- "GTP sleep: Cannot reconfig gpio %d.\n",
- ts->pdata->irq_gpio);
- if (ts->pdata->enable_power_off) {
- ret = gpio_direction_output(ts->pdata->reset_gpio, 0);
- if (ret)
- dev_err(&ts->client->dev,
- "GTP sleep: Cannot reconfig gpio %d.\n",
- ts->pdata->reset_gpio);
- ret = goodix_power_off(ts);
- if (ret) {
- dev_err(&ts->client->dev, "GTP power off failed.\n");
- return ret;
- }
- return 0;
- }
- usleep_range(SLEEP_DELAY_US, SLEEP_DELAY_US + 1);
- while (retry++ < GTP_I2C_RETRY_5) {
- ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
- if (ret == 1) {
- dev_dbg(&ts->client->dev, "GTP enter sleep!");
- return 0;
- }
- msleep(20);
- }
- dev_err(&ts->client->dev, "GTP send sleep cmd failed.\n");
- return ret;
-}
-
-/*******************************************************
-Function:
- Wakeup from sleep.
-Input:
- ts: private data.
-Output:
- Executive outcomes.
- >0: succeed, otherwise: failed.
-*******************************************************/
-static s8 gtp_wakeup_sleep(struct goodix_ts_data *ts)
-{
- u8 retry = 0;
- s8 ret = -1;
-
- if (ts->pdata->enable_power_off) {
- ret = gpio_direction_output(ts->pdata->irq_gpio, 0);
- if (ret)
- dev_err(&ts->client->dev,
- "GTP wakeup: Cannot reconfig gpio %d.\n",
- ts->pdata->irq_gpio);
- ret = gpio_direction_output(ts->pdata->reset_gpio, 0);
- if (ret)
- dev_err(&ts->client->dev,
- "GTP wakeup: Cannot reconfig gpio %d.\n",
- ts->pdata->reset_gpio);
- ret = goodix_power_on(ts);
- if (ret) {
- dev_err(&ts->client->dev, "GTP power on failed.\n");
- return 0;
- }
-
- gtp_reset_guitar(ts, 20);
-
- ret = gtp_send_cfg(ts);
- if (ret <= 0) {
- dev_err(&ts->client->dev,
- "GTP wakeup sleep failed.\n");
- return ret;
- }
-
- dev_dbg(&ts->client->dev,
- "Wakeup sleep send config success.");
- } else {
-err_retry:
- if (ts->pdata->slide_wakeup) { /* wakeup not by slide */
- if (doze_status != DOZE_WAKEUP)
- gtp_reset_guitar(ts, 10);
- else
- /* wakeup by slide */
- doze_status = DOZE_DISABLED;
- } else {
- if (chip_gt9xxs == 1) {
- gtp_reset_guitar(ts, 10);
- } else {
- ret = gpio_direction_output(
- ts->pdata->irq_gpio, 1);
- usleep_range(WAKE_UP_DELAY_US,
- WAKE_UP_DELAY_US + 1);
- }
- }
- ret = gtp_i2c_test(ts->client);
- if (ret == 2) {
- dev_dbg(&ts->client->dev, "GTP wakeup sleep.");
- if (!ts->pdata->slide_wakeup) {
- if (chip_gt9xxs == 0) {
- gtp_int_sync(ts, 25);
- msleep(20);
-#if GTP_ESD_PROTECT
- gtp_init_ext_watchdog(ts->client);
-#endif
- }
- }
- return ret;
- }
- gtp_reset_guitar(ts, 20);
- if (retry++ < GTP_I2C_RETRY_10)
- goto err_retry;
- dev_err(&ts->client->dev, "GTP wakeup sleep failed.\n");
- }
- return ret;
-}
-#endif /* !CONFIG_HAS_EARLYSUSPEND && !CONFIG_FB*/
-
-/*******************************************************
-Function:
- Initialize gtp.
-Input:
- ts: goodix private data
-Output:
- Executive outcomes.
- > =0: succeed, otherwise: failed
-*******************************************************/
-static int gtp_init_panel(struct goodix_ts_data *ts)
-{
- struct i2c_client *client = ts->client;
- unsigned char *config_data = NULL;
- int ret = -EIO;
- int i;
- u8 check_sum = 0;
- u8 opr_buf[16];
- u8 sensor_id = 0;
-
- if (ts->pdata->driver_send_cfg) {
- for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++)
- dev_dbg(&client->dev, "Config Groups(%d) Lengths: %zu",
- i, ts->pdata->config_data_len[i]);
-
- ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1);
- if (ret == SUCCESS) {
- if (opr_buf[0] != 0xBE) {
- ts->fw_error = 1;
- dev_err(&client->dev,
- "Firmware error, no config sent!");
- return -EINVAL;
- }
- }
-
- for (i = 1; i < GOODIX_MAX_CFG_GROUP; i++) {
- if (ts->pdata->config_data_len[i])
- break;
- }
-
- if (i == GOODIX_MAX_CFG_GROUP) {
- sensor_id = 0;
- } else {
- ret = gtp_i2c_read_dbl_check(ts->client,
- GTP_REG_SENSOR_ID, &sensor_id, 1);
- if (ret == SUCCESS) {
- if (sensor_id >= GOODIX_MAX_CFG_GROUP) {
- dev_err(&client->dev,
- "Invalid sensor_id(0x%02X), No Config Sent!",
- sensor_id);
- return -EINVAL;
- }
- } else {
- dev_err(&client->dev,
- "Failed to get sensor_id, No config sent!");
- return -EINVAL;
- }
- }
-
- dev_info(&client->dev, "Sensor ID selected: %d", sensor_id);
-
- if (ts->pdata->config_data_len[sensor_id] <
- GTP_CONFIG_MIN_LENGTH ||
- !ts->pdata->config_data[sensor_id]) {
- dev_err(&client->dev,
- "Sensor_ID(%d) matches with NULL or invalid config group!\n",
- sensor_id);
- return -EINVAL;
- }
-
- ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA,
- &opr_buf[0], 1);
- if (ret == SUCCESS) {
- if (opr_buf[0] < 90) {
- /* backup group config version */
- grp_cfg_version =
- ts->pdata->
- config_data[sensor_id][GTP_ADDR_LENGTH];
- ts->pdata->
- config_data[sensor_id][GTP_ADDR_LENGTH]
- = 0x00;
- ts->fixed_cfg = 0;
- } else {
- /* treated as fixed config, not send config */
- dev_warn(&client->dev,
- "Ic fixed config with config version(%d, 0x%02X)",
- opr_buf[0], opr_buf[0]);
- ts->fixed_cfg = 1;
- }
- } else {
- dev_err(&client->dev,
- "Failed to get ic config version!No config sent!");
- return -EINVAL;
- }
-
- config_data = ts->pdata->config_data[sensor_id];
- ts->config_data = ts->pdata->config_data[sensor_id];
- ts->gtp_cfg_len = ts->pdata->config_data_len[sensor_id];
-
-#if GTP_CUSTOM_CFG
- config_data[RESOLUTION_LOC] =
- (unsigned char)(GTP_MAX_WIDTH && 0xFF);
- config_data[RESOLUTION_LOC + 1] =
- (unsigned char)(GTP_MAX_WIDTH >> 8);
- config_data[RESOLUTION_LOC + 2] =
- (unsigned char)(GTP_MAX_HEIGHT && 0xFF);
- config_data[RESOLUTION_LOC + 3] =
- (unsigned char)(GTP_MAX_HEIGHT >> 8);
-
- if (GTP_INT_TRIGGER == 0)
- config_data[TRIGGER_LOC] &= 0xfe;
- else if (GTP_INT_TRIGGER == 1)
- config_data[TRIGGER_LOC] |= 0x01;
-#endif /* !GTP_CUSTOM_CFG */
-
- check_sum = 0;
- for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++)
- check_sum += config_data[i];
-
- config_data[ts->gtp_cfg_len] = (~check_sum) + 1;
-
- } else { /* DRIVER NOT SEND CONFIG */
- ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH;
- ret = gtp_i2c_read(ts->client, config_data,
- ts->gtp_cfg_len + GTP_ADDR_LENGTH);
- if (ret < 0) {
- dev_err(&client->dev,
- "Read Config Failed, Using DEFAULT Resolution & INT Trigger!\n");
- ts->abs_x_max = GTP_MAX_WIDTH;
- ts->abs_y_max = GTP_MAX_HEIGHT;
- ts->int_trigger_type = GTP_INT_TRIGGER;
- }
- } /* !DRIVER NOT SEND CONFIG */
-
- if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0)) {
- ts->abs_x_max = (config_data[RESOLUTION_LOC + 1] << 8)
- + config_data[RESOLUTION_LOC];
- ts->abs_y_max = (config_data[RESOLUTION_LOC + 3] << 8)
- + config_data[RESOLUTION_LOC + 2];
- ts->int_trigger_type = (config_data[TRIGGER_LOC]) & 0x03;
- }
- ret = gtp_send_cfg(ts);
- if (ret < 0)
- dev_err(&client->dev, "%s: Send config error.\n", __func__);
-
- msleep(20);
- return ret;
-}
-
-/*******************************************************
-Function:
- Read firmware version
-Input:
- client: i2c device
- version: buffer to keep ic firmware version
-Output:
- read operation return.
- 0: succeed, otherwise: failed
-*******************************************************/
-static int gtp_read_fw_version(struct i2c_client *client, u16 *version)
-{
- int ret = 0;
- u8 buf[GTP_FW_VERSION_BUFFER_MAXSIZE] = {
- GTP_REG_FW_VERSION >> 8, GTP_REG_FW_VERSION & 0xff };
-
- ret = gtp_i2c_read(client, buf, sizeof(buf));
- if (ret < 0) {
- dev_err(&client->dev, "GTP read version failed.\n");
- return -EIO;
- }
-
- if (version)
- *version = (buf[3] << 8) | buf[2];
-
- return ret;
-}
-/*
- * Function:
- * Read and check chip id.
- * Input:
- * client: i2c device
- * Output:
- * read operation return.
- * 0: succeed, otherwise: failed
- */
-static int gtp_check_product_id(struct i2c_client *client)
-{
- int ret = 0;
- char product_id[GTP_PRODUCT_ID_MAXSIZE];
- struct goodix_ts_data *ts = i2c_get_clientdata(client);
- /* 04 bytes are used for the Product-id in the register space.*/
- u8 buf[GTP_PRODUCT_ID_BUFFER_MAXSIZE] = {
- GTP_REG_PRODUCT_ID >> 8, GTP_REG_PRODUCT_ID & 0xff };
-
- ret = gtp_i2c_read(client, buf, sizeof(buf));
- if (ret < 0) {
- dev_err(&client->dev, "GTP read product_id failed.\n");
- return -EIO;
- }
-
- if (buf[5] == 0x00) {
- /* copy (GTP_PRODUCT_ID_MAXSIZE - 1) from buffer. Ex: 915 */
- strlcpy(product_id, &buf[2], GTP_PRODUCT_ID_MAXSIZE - 1);
- } else {
- if (buf[5] == 'S' || buf[5] == 's')
- chip_gt9xxs = 1;
- /* copy GTP_PRODUCT_ID_MAXSIZE from buffer. Ex: 915s */
- strlcpy(product_id, &buf[2], GTP_PRODUCT_ID_MAXSIZE);
- }
-
- dev_info(&client->dev, "Goodix Product ID = %s\n", product_id);
-
- ret = strcmp(product_id, ts->pdata->product_id);
- if (ret != 0)
- return -EINVAL;
-
- return ret;
-}
-
-/*******************************************************
-Function:
- I2c test Function.
-Input:
- client:i2c client.
-Output:
- Executive outcomes.
- 2: succeed, otherwise failed.
-*******************************************************/
-static int gtp_i2c_test(struct i2c_client *client)
-{
- u8 buf[3] = { GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff };
- int retry = GTP_I2C_RETRY_5;
- int ret = -EIO;
-
- while (retry--) {
- ret = gtp_i2c_read(client, buf, 3);
- if (ret > 0)
- return ret;
- dev_err(&client->dev, "GTP i2c test failed time %d.\n", retry);
- msleep(20);
- }
- return ret;
-}
-
-/*******************************************************
-Function:
- Request gpio(INT & RST) ports.
-Input:
- ts: private data.
-Output:
- Executive outcomes.
- = 0: succeed, != 0: failed
-*******************************************************/
-static int gtp_request_io_port(struct goodix_ts_data *ts)
-{
- struct i2c_client *client = ts->client;
- struct goodix_ts_platform_data *pdata = ts->pdata;
- int ret;
-
- if (gpio_is_valid(pdata->irq_gpio)) {
- ret = gpio_request(pdata->irq_gpio, "goodix_ts_irq_gpio");
- if (ret) {
- dev_err(&client->dev, "Unable to request irq gpio [%d]\n",
- pdata->irq_gpio);
- goto err_pwr_off;
- }
- ret = gpio_direction_input(pdata->irq_gpio);
- if (ret) {
- dev_err(&client->dev, "Unable to set direction for irq gpio [%d]\n",
- pdata->irq_gpio);
- goto err_free_irq_gpio;
- }
- } else {
- dev_err(&client->dev, "Invalid irq gpio [%d]!\n",
- pdata->irq_gpio);
- ret = -EINVAL;
- goto err_pwr_off;
- }
-
- if (gpio_is_valid(pdata->reset_gpio)) {
- ret = gpio_request(pdata->reset_gpio, "goodix_ts_reset_gpio");
- if (ret) {
- dev_err(&client->dev, "Unable to request reset gpio [%d]\n",
- pdata->reset_gpio);
- goto err_free_irq_gpio;
- }
-
- ret = gpio_direction_output(pdata->reset_gpio, 0);
- if (ret) {
- dev_err(&client->dev, "Unable to set direction for reset gpio [%d]\n",
- pdata->reset_gpio);
- goto err_free_reset_gpio;
- }
- } else {
- dev_err(&client->dev, "Invalid irq gpio [%d]!\n",
- pdata->reset_gpio);
- ret = -EINVAL;
- goto err_free_irq_gpio;
- }
- /* IRQ GPIO is an input signal, but we are setting it to output
- * direction and pulling it down, to comply with power up timing
- * requirements, mentioned in power up timing section of device
- * datasheet.
- */
- ret = gpio_direction_output(pdata->irq_gpio, 0);
- if (ret)
- dev_warn(&client->dev,
- "pull down interrupt gpio failed\n");
- ret = gpio_direction_output(pdata->reset_gpio, 0);
- if (ret)
- dev_warn(&client->dev,
- "pull down reset gpio failed\n");
-
- return ret;
-
-err_free_reset_gpio:
- if (gpio_is_valid(pdata->reset_gpio))
- gpio_free(pdata->reset_gpio);
-err_free_irq_gpio:
- if (gpio_is_valid(pdata->irq_gpio))
- gpio_free(pdata->irq_gpio);
-err_pwr_off:
- return ret;
-}
-
-/*******************************************************
-Function:
- Request interrupt.
-Input:
- ts: private data.
-Output:
- Executive outcomes.
- 0: succeed, -1: failed.
-*******************************************************/
-static int gtp_request_irq(struct goodix_ts_data *ts)
-{
- int ret;
- const u8 irq_table[] = GTP_IRQ_TAB;
-
- dev_dbg(&ts->client->dev, "INT trigger type:%x, irq=%d",
- ts->int_trigger_type,
- ts->client->irq);
-
- ret = request_threaded_irq(ts->client->irq, NULL,
- goodix_ts_irq_handler,
- irq_table[ts->int_trigger_type],
- ts->client->name, ts);
- if (ret) {
- ts->use_irq = false;
- return ret;
- }
- gtp_irq_disable(ts);
- ts->use_irq = true;
- return 0;
-}
-
-/*******************************************************
-Function:
- Request input device Function.
-Input:
- ts:private data.
-Output:
- Executive outcomes.
- 0: succeed, otherwise: failed.
-*******************************************************/
-static int gtp_request_input_dev(struct goodix_ts_data *ts)
-{
- int ret;
- char phys[PHY_BUF_SIZE];
- int index = 0;
-
- ts->input_dev = input_allocate_device();
- if (ts->input_dev == NULL) {
- dev_err(&ts->client->dev,
- "Failed to allocate input device.\n");
- return -ENOMEM;
- }
-
- ts->input_dev->evbit[0] =
- BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- set_bit(BTN_TOOL_FINGER, ts->input_dev->keybit);
- __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
- /* in case of "out of memory" */
- input_mt_init_slots(ts->input_dev, 10, 0);
-
- if (ts->pdata->have_touch_key) {
- for (index = 0; index < ts->pdata->num_button; index++) {
- input_set_capability(ts->input_dev,
- EV_KEY, ts->pdata->button_map[index]);
- }
- }
-
- if (ts->pdata->slide_wakeup)
- input_set_capability(ts->input_dev, EV_KEY, KEY_POWER);
-
- if (ts->pdata->with_pen) { /* pen support */
- __set_bit(BTN_TOOL_PEN, ts->input_dev->keybit);
- __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
- __set_bit(INPUT_PROP_POINTER, ts->input_dev->propbit);
- }
-
- if (ts->pdata->change_x2y)
- swap(ts->abs_x_max, ts->abs_y_max);
-
- input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X,
- 0, ts->abs_x_max, 0, 0);
- input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y,
- 0, ts->abs_y_max, 0, 0);
- input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR,
- 0, 255, 0, 0);
- input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR,
- 0, 255, 0, 0);
- input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID,
- 0, 255, 0, 0);
-
- snprintf(phys, PHY_BUF_SIZE, "input/ts");
- ts->input_dev->name = GOODIX_DEV_NAME;
- ts->input_dev->phys = phys;
- ts->input_dev->id.bustype = BUS_I2C;
- ts->input_dev->id.vendor = 0xDEAD;
- ts->input_dev->id.product = 0xBEEF;
- ts->input_dev->id.version = 10427;
-
- ret = input_register_device(ts->input_dev);
- if (ret) {
- dev_err(&ts->client->dev,
- "Register %s input device failed.\n",
- ts->input_dev->name);
- goto exit_free_inputdev;
- }
-
- return 0;
-
-exit_free_inputdev:
- input_free_device(ts->input_dev);
- ts->input_dev = NULL;
- return ret;
-}
-
-static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA)
-{
- return (regulator_count_voltages(reg) > 0) ?
- regulator_set_load(reg, load_uA) : 0;
-}
-
-/**
- * goodix_power_on - Turn device power ON
- * @ts: driver private data
- *
- * Returns zero on success, else an error.
- */
-static int goodix_power_on(struct goodix_ts_data *ts)
-{
- int ret;
-
- if (ts->power_on) {
- dev_info(&ts->client->dev,
- "Device already power on\n");
- return 0;
- }
-
- if (!IS_ERR(ts->avdd)) {
- ret = reg_set_optimum_mode_check(ts->avdd,
- GOODIX_VDD_LOAD_MAX_UA);
- if (ret < 0) {
- dev_err(&ts->client->dev,
- "Regulator avdd set_opt failed rc=%d\n", ret);
- goto err_set_opt_avdd;
- }
- ret = regulator_enable(ts->avdd);
- if (ret) {
- dev_err(&ts->client->dev,
- "Regulator avdd enable failed ret=%d\n", ret);
- goto err_enable_avdd;
- }
- }
-
- if (!IS_ERR(ts->vdd)) {
- ret = regulator_set_voltage(ts->vdd, GOODIX_VTG_MIN_UV,
- GOODIX_VTG_MAX_UV);
- if (ret) {
- dev_err(&ts->client->dev,
- "Regulator set_vtg failed vdd ret=%d\n", ret);
- goto err_set_vtg_vdd;
- }
- ret = reg_set_optimum_mode_check(ts->vdd,
- GOODIX_VDD_LOAD_MAX_UA);
- if (ret < 0) {
- dev_err(&ts->client->dev,
- "Regulator vdd set_opt failed rc=%d\n", ret);
- goto err_set_opt_vdd;
- }
- ret = regulator_enable(ts->vdd);
- if (ret) {
- dev_err(&ts->client->dev,
- "Regulator vdd enable failed ret=%d\n", ret);
- goto err_enable_vdd;
- }
- }
-
- if (!IS_ERR(ts->vcc_i2c)) {
- ret = regulator_set_voltage(ts->vcc_i2c, GOODIX_I2C_VTG_MIN_UV,
- GOODIX_I2C_VTG_MAX_UV);
- if (ret) {
- dev_err(&ts->client->dev,
- "Regulator set_vtg failed vcc_i2c ret=%d\n",
- ret);
- goto err_set_vtg_vcc_i2c;
- }
- ret = reg_set_optimum_mode_check(ts->vcc_i2c,
- GOODIX_VIO_LOAD_MAX_UA);
- if (ret < 0) {
- dev_err(&ts->client->dev,
- "Regulator vcc_i2c set_opt failed rc=%d\n",
- ret);
- goto err_set_opt_vcc_i2c;
- }
- ret = regulator_enable(ts->vcc_i2c);
- if (ret) {
- dev_err(&ts->client->dev,
- "Regulator vcc_i2c enable failed ret=%d\n",
- ret);
- regulator_disable(ts->vdd);
- goto err_enable_vcc_i2c;
- }
- }
-
- ts->power_on = true;
- return 0;
-
-err_enable_vcc_i2c:
-err_set_opt_vcc_i2c:
- if (!IS_ERR(ts->vcc_i2c))
- regulator_set_voltage(ts->vcc_i2c, 0, GOODIX_I2C_VTG_MAX_UV);
-err_set_vtg_vcc_i2c:
- if (!IS_ERR(ts->vdd))
- regulator_disable(ts->vdd);
-err_enable_vdd:
-err_set_opt_vdd:
- if (!IS_ERR(ts->vdd))
- regulator_set_voltage(ts->vdd, 0, GOODIX_VTG_MAX_UV);
-err_set_vtg_vdd:
- if (!IS_ERR(ts->avdd))
- regulator_disable(ts->avdd);
-err_enable_avdd:
-err_set_opt_avdd:
- ts->power_on = false;
- return ret;
-}
-
-/**
- * goodix_power_off - Turn device power OFF
- * @ts: driver private data
- *
- * Returns zero on success, else an error.
- */
-static int goodix_power_off(struct goodix_ts_data *ts)
-{
- int ret;
-
- if (!ts->power_on) {
- dev_info(&ts->client->dev,
- "Device already power off\n");
- return 0;
- }
-
- if (!IS_ERR(ts->vcc_i2c)) {
- ret = regulator_set_voltage(ts->vcc_i2c, 0,
- GOODIX_I2C_VTG_MAX_UV);
- if (ret < 0)
- dev_err(&ts->client->dev,
- "Regulator vcc_i2c set_vtg failed ret=%d\n",
- ret);
- ret = regulator_disable(ts->vcc_i2c);
- if (ret)
- dev_err(&ts->client->dev,
- "Regulator vcc_i2c disable failed ret=%d\n",
- ret);
- }
-
- if (!IS_ERR(ts->vdd)) {
- ret = regulator_set_voltage(ts->vdd, 0, GOODIX_VTG_MAX_UV);
- if (ret < 0)
- dev_err(&ts->client->dev,
- "Regulator vdd set_vtg failed ret=%d\n", ret);
- ret = regulator_disable(ts->vdd);
- if (ret)
- dev_err(&ts->client->dev,
- "Regulator vdd disable failed ret=%d\n", ret);
- }
-
- if (!IS_ERR(ts->avdd)) {
- ret = regulator_disable(ts->avdd);
- if (ret)
- dev_err(&ts->client->dev,
- "Regulator avdd disable failed ret=%d\n", ret);
- }
-
- ts->power_on = false;
- return 0;
-}
-
-/**
- * goodix_power_init - Initialize device power
- * @ts: driver private data
- *
- * Returns zero on success, else an error.
- */
-static int goodix_power_init(struct goodix_ts_data *ts)
-{
- int ret;
-
- ts->avdd = regulator_get(&ts->client->dev, "avdd");
- if (IS_ERR(ts->avdd)) {
- ret = PTR_ERR(ts->avdd);
- dev_info(&ts->client->dev,
- "Regulator get failed avdd ret=%d\n", ret);
- }
-
- ts->vdd = regulator_get(&ts->client->dev, "vdd");
- if (IS_ERR(ts->vdd)) {
- ret = PTR_ERR(ts->vdd);
- dev_info(&ts->client->dev,
- "Regulator get failed vdd ret=%d\n", ret);
- }
-
- ts->vcc_i2c = regulator_get(&ts->client->dev, "vcc-i2c");
- if (IS_ERR(ts->vcc_i2c)) {
- ret = PTR_ERR(ts->vcc_i2c);
- dev_info(&ts->client->dev,
- "Regulator get failed vcc_i2c ret=%d\n", ret);
- }
-
- return 0;
-}
-
-/**
- * goodix_power_deinit - Deinitialize device power
- * @ts: driver private data
- *
- * Returns zero on success, else an error.
- */
-static int goodix_power_deinit(struct goodix_ts_data *ts)
-{
- regulator_put(ts->vdd);
- regulator_put(ts->vcc_i2c);
- regulator_put(ts->avdd);
-
- return 0;
-}
-
-static ssize_t gtp_fw_name_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct goodix_ts_data *ts = dev_get_drvdata(dev);
-
- if (!strlen(ts->fw_name))
- return snprintf(buf, GTP_FW_NAME_MAXSIZE - 1,
- "No fw name has been given.");
- else
- return snprintf(buf, GTP_FW_NAME_MAXSIZE - 1,
- "%s\n", ts->fw_name);
-}
-
-static ssize_t gtp_fw_name_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct goodix_ts_data *ts = dev_get_drvdata(dev);
-
- if (size > GTP_FW_NAME_MAXSIZE - 1) {
- dev_err(dev, "FW name size exceeds the limit.");
- return -EINVAL;
- }
-
- strlcpy(ts->fw_name, buf, size);
- if (ts->fw_name[size-1] == '\n')
- ts->fw_name[size-1] = '\0';
-
- return size;
-}
-
-static ssize_t gtp_fw_upgrade_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct goodix_ts_data *ts = dev_get_drvdata(dev);
-
- return snprintf(buf, 2, "%d\n", ts->fw_loading);
-}
-
-static ssize_t gtp_fw_upgrade_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct goodix_ts_data *ts = dev_get_drvdata(dev);
- unsigned int val;
- int ret;
-
- if (size > 2)
- return -EINVAL;
-
- ret = kstrtouint(buf, 10, &val);
- if (ret)
- return ret;
-
- if (ts->gtp_is_suspend) {
- dev_err(&ts->client->dev,
- "Can't start fw upgrade. Device is in suspend state");
- return -EBUSY;
- }
-
- mutex_lock(&ts->input_dev->mutex);
- if (!ts->fw_loading && val) {
- disable_irq(ts->client->irq);
- ts->fw_loading = true;
- if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) {
- ret = gup_update_proc(NULL);
- if (ret == FAIL)
- dev_err(&ts->client->dev,
- "Fail to update GTP firmware\n");
- }
- ts->fw_loading = false;
- enable_irq(ts->client->irq);
- }
- mutex_unlock(&ts->input_dev->mutex);
-
- return size;
-}
-
-static ssize_t gtp_force_fw_upgrade_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct goodix_ts_data *ts = dev_get_drvdata(dev);
- unsigned int val;
- int ret;
-
- if (size > 2)
- return -EINVAL;
-
- ret = kstrtouint(buf, 10, &val);
- if (ret)
- return ret;
-
- if (ts->gtp_is_suspend) {
- dev_err(&ts->client->dev,
- "Can't start fw upgrade. Device is in suspend state.");
- return -EBUSY;
- }
-
- mutex_lock(&ts->input_dev->mutex);
- if (!ts->fw_loading && val) {
- disable_irq(ts->client->irq);
- ts->fw_loading = true;
- ts->force_update = true;
- if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) {
- ret = gup_update_proc(NULL);
- if (ret == FAIL)
- dev_err(&ts->client->dev,
- "Fail to force update GTP firmware.\n");
- }
- ts->force_update = false;
- ts->fw_loading = false;
- enable_irq(ts->client->irq);
- }
- mutex_unlock(&ts->input_dev->mutex);
-
- return size;
-}
-
-static DEVICE_ATTR(fw_name, (S_IRUGO | S_IWUSR | S_IWGRP),
- gtp_fw_name_show,
- gtp_fw_name_store);
-static DEVICE_ATTR(fw_upgrade, (S_IRUGO | S_IWUSR | S_IWGRP),
- gtp_fw_upgrade_show,
- gtp_fw_upgrade_store);
-static DEVICE_ATTR(force_fw_upgrade, (S_IRUGO | S_IWUSR | S_IWGRP),
- gtp_fw_upgrade_show,
- gtp_force_fw_upgrade_store);
-
-static struct attribute *gtp_attrs[] = {
- &dev_attr_fw_name.attr,
- &dev_attr_fw_upgrade.attr,
- &dev_attr_force_fw_upgrade.attr,
- NULL
-};
-
-static const struct attribute_group gtp_attr_grp = {
- .attrs = gtp_attrs,
-};
-
-static int gtp_debug_addr_is_valid(u16 addr)
-{
- if (addr < GTP_VALID_ADDR_START || addr > GTP_VALID_ADDR_END) {
- pr_err("GTP reg address is invalid: 0x%x\n", addr);
- return false;
- }
-
- return true;
-}
-
-static int gtp_debug_data_set(void *_data, u64 val)
-{
- struct goodix_ts_data *ts = _data;
-
- mutex_lock(&ts->input_dev->mutex);
- if (gtp_debug_addr_is_valid(ts->addr))
- dev_err(&ts->client->dev,
- "Writing to GTP registers not supported\n");
- mutex_unlock(&ts->input_dev->mutex);
-
- return 0;
-}
-
-static int gtp_debug_data_get(void *_data, u64 *val)
-{
- struct goodix_ts_data *ts = _data;
- int ret;
- u8 buf[3] = {0};
-
- mutex_lock(&ts->input_dev->mutex);
- buf[0] = ts->addr >> 8;
- buf[1] = ts->addr & 0x00ff;
-
- if (gtp_debug_addr_is_valid(ts->addr)) {
- ret = gtp_i2c_read(ts->client, buf, 3);
- if (ret < 0)
- dev_err(&ts->client->dev,
- "GTP read register 0x%x failed (%d)\n",
- ts->addr, ret);
- else
- *val = buf[2];
- }
- mutex_unlock(&ts->input_dev->mutex);
-
- return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, gtp_debug_data_get,
- gtp_debug_data_set, "%llx\n");
-
-static int gtp_debug_addr_set(void *_data, u64 val)
-{
- struct goodix_ts_data *ts = _data;
-
- if (gtp_debug_addr_is_valid(val)) {
- mutex_lock(&ts->input_dev->mutex);
- ts->addr = val;
- mutex_unlock(&ts->input_dev->mutex);
- }
-
- return 0;
-}
-
-static int gtp_debug_addr_get(void *_data, u64 *val)
-{
- struct goodix_ts_data *ts = _data;
-
- mutex_lock(&ts->input_dev->mutex);
- if (gtp_debug_addr_is_valid(ts->addr))
- *val = ts->addr;
- mutex_unlock(&ts->input_dev->mutex);
-
- return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, gtp_debug_addr_get,
- gtp_debug_addr_set, "%llx\n");
-
-static int gtp_debug_suspend_set(void *_data, u64 val)
-{
- struct goodix_ts_data *ts = _data;
-
- mutex_lock(&ts->input_dev->mutex);
- if (val)
- goodix_ts_suspend(&ts->client->dev);
- else
- goodix_ts_resume(&ts->client->dev);
- mutex_unlock(&ts->input_dev->mutex);
-
- return 0;
-}
-
-static int gtp_debug_suspend_get(void *_data, u64 *val)
-{
- struct goodix_ts_data *ts = _data;
-
- mutex_lock(&ts->input_dev->mutex);
- *val = ts->gtp_is_suspend;
- mutex_unlock(&ts->input_dev->mutex);
-
- return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, gtp_debug_suspend_get,
- gtp_debug_suspend_set, "%lld\n");
-
-static int gtp_debugfs_init(struct goodix_ts_data *data)
-{
- data->debug_base = debugfs_create_dir(GTP_DEBUGFS_DIR, NULL);
-
- if (IS_ERR_OR_NULL(data->debug_base)) {
- dev_err(&data->client->dev, "Failed to create debugfs dir\n");
- return -EINVAL;
- }
-
- if ((IS_ERR_OR_NULL(debugfs_create_file(GTP_DEBUGFS_FILE_SUSPEND,
- S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP,
- data->debug_base,
- data,
- &debug_suspend_fops)))) {
- dev_err(&data->client->dev, "Failed to create suspend file\n");
- debugfs_remove_recursive(data->debug_base);
- return -EINVAL;
- }
-
- if ((IS_ERR_OR_NULL(debugfs_create_file(GTP_DEBUGFS_FILE_DATA,
- S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP,
- data->debug_base,
- data,
- &debug_data_fops)))) {
- dev_err(&data->client->dev, "Failed to create data file\n");
- debugfs_remove_recursive(data->debug_base);
- return -EINVAL;
- }
-
- if ((IS_ERR_OR_NULL(debugfs_create_file(GTP_DEBUGFS_FILE_ADDR,
- S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP,
- data->debug_base,
- data,
- &debug_addr_fops)))) {
- dev_err(&data->client->dev, "Failed to create addr file\n");
- debugfs_remove_recursive(data->debug_base);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int goodix_ts_get_dt_coords(struct device *dev, char *name,
- struct goodix_ts_platform_data *pdata)
-{
- struct property *prop;
- struct device_node *np = dev->of_node;
- int rc;
- u32 coords[GOODIX_COORDS_ARR_SIZE];
-
- prop = of_find_property(np, name, NULL);
- if (!prop)
- return -EINVAL;
- if (!prop->value)
- return -ENODATA;
-
- rc = of_property_read_u32_array(np, name, coords,
- GOODIX_COORDS_ARR_SIZE);
- if (rc && (rc != -EINVAL)) {
- dev_err(dev, "Unable to read %s\n", name);
- return rc;
- }
-
- if (!strcmp(name, "goodix,panel-coords")) {
- pdata->panel_minx = coords[0];
- pdata->panel_miny = coords[1];
- pdata->panel_maxx = coords[2];
- pdata->panel_maxy = coords[3];
- } else if (!strcmp(name, "goodix,display-coords")) {
- pdata->x_min = coords[0];
- pdata->y_min = coords[1];
- pdata->x_max = coords[2];
- pdata->y_max = coords[3];
- } else {
- dev_err(dev, "unsupported property %s\n", name);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int goodix_parse_dt(struct device *dev,
- struct goodix_ts_platform_data *pdata)
-{
- int rc;
- struct device_node *np = dev->of_node;
- struct property *prop;
- u32 temp_val, num_buttons;
- u32 button_map[MAX_BUTTONS];
- char prop_name[PROP_NAME_SIZE];
- int i, read_cfg_num, temp;
-
- rc = goodix_ts_get_dt_coords(dev, "goodix,panel-coords", pdata);
- if (rc && (rc != -EINVAL))
- return rc;
-
- rc = goodix_ts_get_dt_coords(dev, "goodix,display-coords", pdata);
- if (rc)
- return rc;
-
- pdata->i2c_pull_up = of_property_read_bool(np,
- "goodix,i2c-pull-up");
-
- pdata->force_update = of_property_read_bool(np,
- "goodix,force-update");
-
- pdata->enable_power_off = of_property_read_bool(np,
- "goodix,enable-power-off");
-
- pdata->have_touch_key = of_property_read_bool(np,
- "goodix,have-touch-key");
-
- pdata->driver_send_cfg = of_property_read_bool(np,
- "goodix,driver-send-cfg");
-
- pdata->change_x2y = of_property_read_bool(np,
- "goodix,change-x2y");
-
- pdata->with_pen = of_property_read_bool(np,
- "goodix,with-pen");
-
- pdata->slide_wakeup = of_property_read_bool(np,
- "goodix,slide-wakeup");
-
- pdata->dbl_clk_wakeup = of_property_read_bool(np,
- "goodix,dbl_clk_wakeup");
-
- /* reset, irq gpio info */
- pdata->reset_gpio = of_get_named_gpio_flags(np, "reset-gpios",
- 0, &pdata->reset_gpio_flags);
- if (pdata->reset_gpio < 0)
- return pdata->reset_gpio;
-
- pdata->irq_gpio = of_get_named_gpio_flags(np, "interrupt-gpios",
- 0, &pdata->irq_gpio_flags);
- if (pdata->irq_gpio < 0)
- return pdata->irq_gpio;
-
- rc = of_property_read_string(np, "goodix,product-id",
- &pdata->product_id);
- if (rc && (rc != -EINVAL)) {
- dev_err(dev, "Failed to parse product_id.");
- return -EINVAL;
- }
-
- rc = of_property_read_string(np, "goodix,fw_name",
- &pdata->fw_name);
- if (rc && (rc != -EINVAL)) {
- dev_err(dev, "Failed to parse firmware name.\n");
- return -EINVAL;
- }
-
- prop = of_find_property(np, "goodix,button-map", NULL);
- if (prop) {
- num_buttons = prop->length / sizeof(temp_val);
- if (num_buttons > MAX_BUTTONS)
- return -EINVAL;
-
- rc = of_property_read_u32_array(np,
- "goodix,button-map", button_map,
- num_buttons);
- if (rc) {
- dev_err(dev, "Unable to read key codes\n");
- return rc;
- }
- pdata->num_button = num_buttons;
- memcpy(pdata->button_map, button_map,
- pdata->num_button * sizeof(u32));
- }
-
- read_cfg_num = 0;
- for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++) {
- temp = 0;
- snprintf(prop_name, sizeof(prop_name), "goodix,cfg-data%d", i);
- prop = of_find_property(np, prop_name, &temp);
- if (!prop || !prop->value) {
- pdata->config_data_len[i] = 0;
- pdata->config_data[i] = NULL;
- continue;
- }
- pdata->config_data_len[i] = temp;
- pdata->config_data[i] = devm_kzalloc(dev,
- GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH,
- GFP_KERNEL);
- if (!pdata->config_data[i]) {
- dev_err(dev,
- "Not enough memory for panel config data %d\n",
- i);
- return -ENOMEM;
- }
- pdata->config_data[i][0] = GTP_REG_CONFIG_DATA >> 8;
- pdata->config_data[i][1] = GTP_REG_CONFIG_DATA & 0xff;
- memcpy(&pdata->config_data[i][GTP_ADDR_LENGTH],
- prop->value, pdata->config_data_len[i]);
- read_cfg_num++;
- }
- dev_dbg(dev, "%d config data read from device tree\n", read_cfg_num);
-
- return 0;
-}
-
-/*******************************************************
-Function:
- I2c probe.
-Input:
- client: i2c device struct.
- id: device id.
-Output:
- Executive outcomes.
- 0: succeed.
-*******************************************************/
-
-static int goodix_ts_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct goodix_ts_platform_data *pdata;
- struct goodix_ts_data *ts;
- u16 version_info;
- int ret;
-
- dev_dbg(&client->dev, "GTP I2C Address: 0x%02x\n", client->addr);
- if (client->dev.of_node) {
- pdata = devm_kzalloc(&client->dev,
- sizeof(struct goodix_ts_platform_data), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- ret = goodix_parse_dt(&client->dev, pdata);
- if (ret)
- return ret;
- } else {
- pdata = client->dev.platform_data;
- }
-
- if (!pdata) {
- dev_err(&client->dev, "GTP invalid pdata\n");
- return -EINVAL;
- }
-
- i2c_connect_client = client;
-
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- dev_err(&client->dev, "GTP I2C not supported\n");
- return -ENODEV;
- }
-
- ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
- if (!ts)
- return -ENOMEM;
-
- memset(ts, 0, sizeof(*ts));
- ts->client = client;
- ts->pdata = pdata;
- /* For 2.6.39 & later use spin_lock_init(&ts->irq_lock)
- * For 2.6.39 & before, use ts->irq_lock = SPIN_LOCK_UNLOCKED
- */
- spin_lock_init(&ts->irq_lock);
- i2c_set_clientdata(client, ts);
- ts->gtp_rawdiff_mode = 0;
- ts->power_on = false;
-
- ret = gtp_request_io_port(ts);
- if (ret) {
- dev_err(&client->dev, "GTP request IO port failed.\n");
- goto exit_free_client_data;
- }
-
- ret = goodix_power_init(ts);
- if (ret) {
- dev_err(&client->dev, "GTP power init failed\n");
- goto exit_free_io_port;
- }
-
- ret = goodix_power_on(ts);
- if (ret) {
- dev_err(&client->dev, "GTP power on failed\n");
- goto exit_deinit_power;
- }
-
- gtp_reset_guitar(ts, 20);
-
- ret = gtp_i2c_test(client);
- if (ret != 2) {
- dev_err(&client->dev, "I2C communication ERROR\n");
- goto exit_power_off;
- }
-
- if (pdata->force_update)
- ts->force_update = true;
-
- if (pdata->fw_name)
- strlcpy(ts->fw_name, pdata->fw_name,
- strlen(pdata->fw_name) + 1);
-
- if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) {
- ret = gup_init_update_proc(ts);
- if (ret < 0) {
- dev_err(&client->dev,
- "GTP Create firmware update thread error\n");
- goto exit_power_off;
- }
- }
- ret = gtp_init_panel(ts);
- if (ret < 0) {
- dev_err(&client->dev, "GTP init panel failed\n");
- ts->abs_x_max = GTP_MAX_WIDTH;
- ts->abs_y_max = GTP_MAX_HEIGHT;
- ts->int_trigger_type = GTP_INT_TRIGGER;
- }
-
- ret = gtp_request_input_dev(ts);
- if (ret) {
- dev_err(&client->dev, "GTP request input dev failed\n");
- goto exit_free_inputdev;
- }
- input_set_drvdata(ts->input_dev, ts);
-
- mutex_init(&ts->lock);
-#if defined(CONFIG_FB)
- ts->fb_notif.notifier_call = fb_notifier_callback;
- ret = fb_register_client(&ts->fb_notif);
- if (ret)
- dev_err(&ts->client->dev,
- "Unable to register fb_notifier: %d\n",
- ret);
-#elif defined(CONFIG_HAS_EARLYSUSPEND)
- ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
- ts->early_suspend.suspend = goodix_ts_early_suspend;
- ts->early_suspend.resume = goodix_ts_late_resume;
- register_early_suspend(&ts->early_suspend);
-#endif
-
- ts->goodix_wq = create_singlethread_workqueue("goodix_wq");
- INIT_WORK(&ts->work, goodix_ts_work_func);
-
- ret = gtp_request_irq(ts);
- if (ret)
- dev_info(&client->dev, "GTP request irq failed %d\n", ret);
- else
- dev_info(&client->dev, "GTP works in interrupt mode\n");
-
- ret = gtp_read_fw_version(client, &version_info);
- if (ret != 2)
- dev_err(&client->dev, "GTP firmware version read failed\n");
-
- ret = gtp_check_product_id(client);
- if (ret != 0) {
- dev_err(&client->dev, "GTP Product id doesn't match\n");
- goto exit_free_irq;
- }
- if (ts->use_irq)
- gtp_irq_enable(ts);
-
-#ifdef CONFIG_GT9XX_TOUCHPANEL_DEBUG
- init_wr_node(client);
-#endif
-
-#if GTP_ESD_PROTECT
- gtp_esd_switch(client, SWITCH_ON);
-#endif
- ret = sysfs_create_group(&client->dev.kobj, &gtp_attr_grp);
- if (ret < 0) {
- dev_err(&client->dev, "sys file creation failed\n");
- goto exit_free_irq;
- }
-
- ret = gtp_debugfs_init(ts);
- if (ret != 0)
- goto exit_remove_sysfs;
-
- init_done = true;
- return 0;
-exit_free_irq:
- mutex_destroy(&ts->lock);
-#if defined(CONFIG_FB)
- if (fb_unregister_client(&ts->fb_notif))
- dev_err(&client->dev,
- "Error occurred while unregistering fb_notifier\n");
-#elif defined(CONFIG_HAS_EARLYSUSPEND)
- unregister_early_suspend(&ts->early_suspend);
-#endif
- if (ts->use_irq)
- free_irq(client->irq, ts);
- cancel_work_sync(&ts->work);
- flush_workqueue(ts->goodix_wq);
- destroy_workqueue(ts->goodix_wq);
-
- input_unregister_device(ts->input_dev);
- if (ts->input_dev) {
- input_free_device(ts->input_dev);
- ts->input_dev = NULL;
- }
-exit_remove_sysfs:
- sysfs_remove_group(&ts->input_dev->dev.kobj, &gtp_attr_grp);
-exit_free_inputdev:
- kfree(ts->config_data);
-exit_power_off:
- goodix_power_off(ts);
-exit_deinit_power:
- goodix_power_deinit(ts);
-exit_free_io_port:
- if (gpio_is_valid(pdata->reset_gpio))
- gpio_free(pdata->reset_gpio);
- if (gpio_is_valid(pdata->irq_gpio))
- gpio_free(pdata->irq_gpio);
-exit_free_client_data:
- i2c_set_clientdata(client, NULL);
- return ret;
-}
-
-/*******************************************************
-Function:
- Goodix touchscreen driver release function.
-Input:
- client: i2c device struct.
-Output:
- Executive outcomes. 0---succeed.
-*******************************************************/
-static int goodix_ts_remove(struct i2c_client *client)
-{
- struct goodix_ts_data *ts = i2c_get_clientdata(client);
-
- sysfs_remove_group(&ts->input_dev->dev.kobj, &gtp_attr_grp);
-
-#if defined(CONFIG_FB)
- if (fb_unregister_client(&ts->fb_notif))
- dev_err(&client->dev,
- "Error occurred while unregistering fb_notifier\n");
-#elif defined(CONFIG_HAS_EARLYSUSPEND)
- unregister_early_suspend(&ts->early_suspend);
-#endif
- mutex_destroy(&ts->lock);
-
-#ifdef CONFIG_GT9XX_TOUCHPANEL_DEBUG
- uninit_wr_node();
-#endif
-
-#if GTP_ESD_PROTECT
- cancel_work_sync(gtp_esd_check_workqueue);
- flush_workqueue(gtp_esd_check_workqueue);
- destroy_workqueue(gtp_esd_check_workqueue);
-#endif
-
- if (ts) {
- if (ts->use_irq)
- free_irq(client->irq, ts);
-
- cancel_work_sync(&ts->work);
- flush_workqueue(ts->goodix_wq);
- destroy_workqueue(ts->goodix_wq);
-
- input_unregister_device(ts->input_dev);
- if (ts->input_dev) {
- input_free_device(ts->input_dev);
- ts->input_dev = NULL;
- }
-
- if (gpio_is_valid(ts->pdata->reset_gpio))
- gpio_free(ts->pdata->reset_gpio);
- if (gpio_is_valid(ts->pdata->irq_gpio))
- gpio_free(ts->pdata->irq_gpio);
-
- goodix_power_off(ts);
- goodix_power_deinit(ts);
- i2c_set_clientdata(client, NULL);
- }
- debugfs_remove_recursive(ts->debug_base);
-
- return 0;
-}
-
-#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_FB)
-/*******************************************************
-Function:
- Early suspend function.
-Input:
- h: early_suspend struct.
-Output:
- None.
-*******************************************************/
-static int goodix_ts_suspend(struct device *dev)
-{
- struct goodix_ts_data *ts = dev_get_drvdata(dev);
- int ret = 0, i;
-
- if (ts->gtp_is_suspend) {
- dev_dbg(&ts->client->dev, "Already in suspend state\n");
- return 0;
- }
-
- mutex_lock(&ts->lock);
-
- if (ts->fw_loading) {
- dev_info(&ts->client->dev,
- "Fw upgrade in progress, can't go to suspend.");
- mutex_unlock(&ts->lock);
- return 0;
- }
-
-#if GTP_ESD_PROTECT
- gtp_esd_switch(ts->client, SWITCH_OFF);
-#endif
-
- if (ts->pdata->slide_wakeup) {
- ret = gtp_enter_doze(ts);
- } else {
- if (ts->use_irq)
- gtp_irq_disable(ts);
-
- for (i = 0; i < GTP_MAX_TOUCH; i++)
- gtp_touch_up(ts, i);
-
- input_sync(ts->input_dev);
-
- ret = gtp_enter_sleep(ts);
- if (ret < 0)
- dev_err(&ts->client->dev, "GTP early suspend failed.\n");
- }
- /* to avoid waking up while not sleeping,
- * delay 48 + 10ms to ensure reliability
- */
- msleep(58);
- mutex_unlock(&ts->lock);
- ts->gtp_is_suspend = 1;
-
- return ret;
-}
-
-/*******************************************************
-Function:
- Late resume function.
-Input:
- h: early_suspend struct.
-Output:
- None.
-*******************************************************/
-static int goodix_ts_resume(struct device *dev)
-{
- struct goodix_ts_data *ts = dev_get_drvdata(dev);
- int ret = 0;
-
- if (!ts->gtp_is_suspend) {
- dev_dbg(&ts->client->dev, "Already in awake state\n");
- return 0;
- }
-
- mutex_lock(&ts->lock);
- ret = gtp_wakeup_sleep(ts);
-
- if (ts->pdata->slide_wakeup)
- doze_status = DOZE_DISABLED;
-
- if (ret <= 0)
- dev_err(&ts->client->dev, "GTP resume failed\n");
-
- if (ts->use_irq)
- gtp_irq_enable(ts);
-
-#if GTP_ESD_PROTECT
- gtp_esd_switch(ts->client, SWITCH_ON);
-#endif
- mutex_unlock(&ts->lock);
- ts->gtp_is_suspend = 0;
-
- return ret;
-}
-
-#if defined(CONFIG_FB)
-static int fb_notifier_callback(struct notifier_block *self,
- unsigned long event, void *data)
-{
- struct fb_event *evdata = data;
- int *blank;
- struct goodix_ts_data *ts =
- container_of(self, struct goodix_ts_data, fb_notif);
-
- if (evdata && evdata->data && event == FB_EVENT_BLANK &&
- ts && ts->client) {
- blank = evdata->data;
- if (*blank == FB_BLANK_UNBLANK)
- goodix_ts_resume(&ts->client->dev);
- else if (*blank == FB_BLANK_POWERDOWN)
- goodix_ts_suspend(&ts->client->dev);
- }
-
- return 0;
-}
-#elif defined(CONFIG_HAS_EARLYSUSPEND)
-/*
- * Function:
- * Early suspend function.
- * Input:
- * h: early_suspend struct.
- * Output:
- * None.
- */
-static void goodix_ts_early_suspend(struct early_suspend *h)
-{
- struct goodix_ts_data *ts;
-
- ts = container_of(h, struct goodix_ts_data, early_suspend);
- goodix_ts_suspend(&ts->client->dev);
- return;
-}
-
-/*
- * Function:
- * Late resume function.
- * Input:
- * h: early_suspend struct.
- * Output:
- * None.
- */
-static void goodix_ts_late_resume(struct early_suspend *h)
-{
- struct goodix_ts_data *ts;
-
- ts = container_of(h, struct goodix_ts_data, early_suspend);
- goodix_ts_late_resume(ts);
-}
-#endif
-#endif /* !CONFIG_HAS_EARLYSUSPEND && !CONFIG_FB*/
-
-#if GTP_ESD_PROTECT
-/*
- * Function:
- * switch on & off esd delayed work
- * Input:
- * client: i2c device
- * on: SWITCH_ON / SWITCH_OFF
- * Output:
- * void
- */
-void gtp_esd_switch(struct i2c_client *client, int on)
-{
- struct goodix_ts_data *ts;
-
- ts = i2c_get_clientdata(client);
- if (on == SWITCH_ON) {
- /* switch on esd */
- if (!ts->esd_running) {
- ts->esd_running = 1;
- dev_dbg(&client->dev, "Esd started\n");
- queue_delayed_work(gtp_esd_check_workqueue,
- &gtp_esd_check_work, GTP_ESD_CHECK_CIRCLE);
- }
- } else {
- /* switch off esd */
- if (ts->esd_running) {
- ts->esd_running = 0;
- dev_dbg(&client->dev, "Esd cancelled\n");
- cancel_delayed_work_sync(&gtp_esd_check_work);
- }
- }
-}
-
-/*******************************************************
-Function:
- Initialize external watchdog for esd protect
-Input:
- client: i2c device.
-Output:
- result of i2c write operation.
- 1: succeed, otherwise: failed
-*********************************************************/
-static int gtp_init_ext_watchdog(struct i2c_client *client)
-{
- /* in case of recursively reset by calling gtp_i2c_write*/
- struct i2c_msg msg;
- u8 opr_buffer[4] = {0x80, 0x40, 0xAA, 0xAA};
- int ret;
- int retries = 0;
-
- msg.flags = !I2C_M_RD;
- msg.addr = client->addr;
- msg.len = 4;
- msg.buf = opr_buffer;
-
- while (retries < GTP_I2C_RETRY_5) {
- ret = i2c_transfer(client->adapter, &msg, 1);
- if (ret == 1)
- return 1;
- retries++;
- }
- if (retries == GTP_I2C_RETRY_5)
- dev_err(&client->dev, "init external watchdog failed!");
- return 0;
-}
-
-/*******************************************************
-Function:
- Esd protect function.
- Added external watchdog by meta, 2013/03/07
-Input:
- work: delayed work
-Output:
- None.
-*******************************************************/
-static void gtp_esd_check_func(struct work_struct *work)
-{
- s32 retry;
- s32 ret = -1;
- struct goodix_ts_data *ts = NULL;
- u8 test[4] = {0x80, 0x40};
-
- ts = i2c_get_clientdata(i2c_connect_client);
-
- if (ts->gtp_is_suspend) {
- dev_dbg(&ts->client->dev, "Esd terminated!\n");
- ts->esd_running = 0;
- return;
- }
-#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
- if (ts->enter_update)
- return;
-#endif
-
- for (retry = 0; retry < GTP_I2C_RETRY_3; retry++) {
- ret = gtp_i2c_read(ts->client, test, 4);
-
- if ((ret < 0)) {
- /* IC works abnormally..*/
- continue;
- } else {
- if ((test[2] == 0xAA) || (test[3] != 0xAA)) {
- /* IC works abnormally..*/
- retry = GTP_I2C_RETRY_3;
- break;
- }
- /* IC works normally, Write 0x8040 0xAA*/
- test[2] = 0xAA;
- gtp_i2c_write(ts->client, test, 3);
- break;
- }
- }
- if (retry == GTP_I2C_RETRY_3) {
- dev_err(&ts->client->dev,
- "IC Working ABNORMALLY, Resetting Guitar...\n");
- gtp_reset_guitar(ts, 50);
- }
-
- if (!ts->gtp_is_suspend)
- queue_delayed_work(gtp_esd_check_workqueue,
- &gtp_esd_check_work, GTP_ESD_CHECK_CIRCLE);
- else {
- dev_dbg(&ts->client->dev, "Esd terminated!\n");
- ts->esd_running = 0;
- }
-
- return;
-}
-#endif
-
-#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND))
-static const struct dev_pm_ops goodix_ts_dev_pm_ops = {
- .suspend = goodix_ts_suspend,
- .resume = goodix_ts_resume,
-};
-#else
-static const struct dev_pm_ops goodix_ts_dev_pm_ops = {
-};
-#endif
-
-static const struct i2c_device_id goodix_ts_id[] = {
- { GTP_I2C_NAME, 0 },
- { }
-};
-
-static const struct of_device_id goodix_match_table[] = {
- { .compatible = "goodix,gt9xx", },
- { },
-};
-
-static struct i2c_driver goodix_ts_driver = {
- .probe = goodix_ts_probe,
- .remove = goodix_ts_remove,
-#ifdef CONFIG_HAS_EARLYSUSPEND
- .suspend = goodix_ts_early_suspend,
- .resume = goodix_ts_late_resume,
-#endif
- .id_table = goodix_ts_id,
- .driver = {
- .name = GTP_I2C_NAME,
- .owner = THIS_MODULE,
- .of_match_table = goodix_match_table,
-#if CONFIG_PM
- .pm = &goodix_ts_dev_pm_ops,
-#endif
- },
-};
-
-/*******************************************************
-Function:
- Driver Install function.
-Input:
- None.
-Output:
- Executive Outcomes. 0---succeed.
-********************************************************/
-static int __init goodix_ts_init(void)
-{
- int ret;
-
-#if GTP_ESD_PROTECT
- INIT_DELAYED_WORK(&gtp_esd_check_work, gtp_esd_check_func);
- gtp_esd_check_workqueue = create_workqueue("gtp_esd_check");
-#endif
- ret = i2c_add_driver(&goodix_ts_driver);
- return ret;
-}
-
-/*******************************************************
-Function:
- Driver uninstall function.
-Input:
- None.
-Output:
- Executive Outcomes. 0---succeed.
-********************************************************/
-static void __exit goodix_ts_exit(void)
-{
- i2c_del_driver(&goodix_ts_driver);
-}
-
-module_init(goodix_ts_init);
-module_exit(goodix_ts_exit);
-
-MODULE_DESCRIPTION("GTP Series Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.h b/drivers/input/touchscreen/gt9xx/gt9xx.h
deleted file mode 100644
index 1e85e2fce276..000000000000
--- a/drivers/input/touchscreen/gt9xx/gt9xx.h
+++ /dev/null
@@ -1,220 +0,0 @@
-/* drivers/input/touchscreen/gt9xx.h
- *
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
- *
- * 2010 - 2013 Goodix Technology.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be a reference
- * to you, when you are integrating the GOODiX's CTP IC into your system,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- */
-
-#ifndef _GOODIX_GT9XX_H_
-#define _GOODIX_GT9XX_H_
-
-#include <linux/kernel.h>
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <linux/uaccess.h>
-
-#if defined(CONFIG_FB)
-#include <linux/notifier.h>
-#include <linux/fb.h>
-#elif defined(CONFIG_HAS_EARLYSUSPEND)
-#include <linux/earlysuspend.h>
-#define GOODIX_SUSPEND_LEVEL 1
-#endif
-
-#define MAX_BUTTONS 4
-#define GOODIX_MAX_CFG_GROUP 6
-#define GTP_FW_NAME_MAXSIZE 50
-
-struct goodix_ts_platform_data {
- int irq_gpio;
- u32 irq_gpio_flags;
- int reset_gpio;
- u32 reset_gpio_flags;
- const char *product_id;
- const char *fw_name;
- u32 x_max;
- u32 y_max;
- u32 x_min;
- u32 y_min;
- u32 panel_minx;
- u32 panel_miny;
- u32 panel_maxx;
- u32 panel_maxy;
- bool force_update;
- bool i2c_pull_up;
- bool enable_power_off;
- size_t config_data_len[GOODIX_MAX_CFG_GROUP];
- u8 *config_data[GOODIX_MAX_CFG_GROUP];
- u32 button_map[MAX_BUTTONS];
- u8 num_button;
- bool have_touch_key;
- bool driver_send_cfg;
- bool change_x2y;
- bool with_pen;
- bool slide_wakeup;
- bool dbl_clk_wakeup;
-};
-struct goodix_ts_data {
- spinlock_t irq_lock;
- struct i2c_client *client;
- struct input_dev *input_dev;
- struct goodix_ts_platform_data *pdata;
- struct hrtimer timer;
- struct workqueue_struct *goodix_wq;
- struct work_struct work;
- char fw_name[GTP_FW_NAME_MAXSIZE];
- struct delayed_work goodix_update_work;
- s32 irq_is_disabled;
- s32 use_irq;
- u16 abs_x_max;
- u16 abs_y_max;
- u16 addr;
- u8 max_touch_num;
- u8 int_trigger_type;
- u8 green_wake_mode;
- u8 chip_type;
- u8 *config_data;
- u8 enter_update;
- u8 gtp_is_suspend;
- u8 gtp_rawdiff_mode;
- u8 gtp_cfg_len;
- u8 fixed_cfg;
- u8 esd_running;
- u8 fw_error;
- bool power_on;
- struct mutex lock;
- bool fw_loading;
- bool force_update;
- struct regulator *avdd;
- struct regulator *vdd;
- struct regulator *vcc_i2c;
-#if defined(CONFIG_FB)
- struct notifier_block fb_notif;
-#elif defined(CONFIG_HAS_EARLYSUSPEND)
- struct early_suspend early_suspend;
-#endif
- struct dentry *debug_base;
-};
-
-extern u16 show_len;
-extern u16 total_len;
-
-/***************************PART1:ON/OFF define*******************************/
-#define GTP_CUSTOM_CFG 0
-#define GTP_ESD_PROTECT 0
-
-#define GTP_IRQ_TAB {\
- IRQ_TYPE_EDGE_RISING,\
- IRQ_TYPE_EDGE_FALLING,\
- IRQ_TYPE_LEVEL_LOW,\
- IRQ_TYPE_LEVEL_HIGH\
- }
-
-
-#define GTP_IRQ_TAB_RISING 0
-#define GTP_IRQ_TAB_FALLING 1
-#if GTP_CUSTOM_CFG
-#define GTP_MAX_HEIGHT 800
-#define GTP_MAX_WIDTH 480
-#define GTP_INT_TRIGGER GTP_IRQ_TAB_RISING
-#else
-#define GTP_MAX_HEIGHT 4096
-#define GTP_MAX_WIDTH 4096
-#define GTP_INT_TRIGGER GTP_IRQ_TAB_FALLING
-#endif
-
-#define GTP_PRODUCT_ID_MAXSIZE 5
-#define GTP_PRODUCT_ID_BUFFER_MAXSIZE 6
-#define GTP_FW_VERSION_BUFFER_MAXSIZE 4
-#define GTP_MAX_TOUCH 5
-#define GTP_ESD_CHECK_CIRCLE 2000 /* jiffy: ms */
-
-/***************************PART3:OTHER define*********************************/
-#define GTP_DRIVER_VERSION "V1.8.1<2013/09/01>"
-#define GTP_I2C_NAME "Goodix-TS"
-#define GTP_POLL_TIME 10 /* jiffy: ms*/
-#define GTP_ADDR_LENGTH 2
-#define GTP_CONFIG_MIN_LENGTH 186
-#define GTP_CONFIG_MAX_LENGTH 240
-#define FAIL 0
-#define SUCCESS 1
-#define SWITCH_OFF 0
-#define SWITCH_ON 1
-
-/* Registers define */
-#define GTP_READ_COOR_ADDR 0x814E
-#define GTP_REG_SLEEP 0x8040
-#define GTP_REG_SENSOR_ID 0x814A
-#define GTP_REG_CONFIG_DATA 0x8047
-#define GTP_REG_FW_VERSION 0x8144
-#define GTP_REG_PRODUCT_ID 0x8140
-
-#define GTP_I2C_RETRY_3 3
-#define GTP_I2C_RETRY_5 5
-#define GTP_I2C_RETRY_10 10
-
-#define RESOLUTION_LOC 3
-#define TRIGGER_LOC 8
-
-/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
-#define GTP_I2C_ADDRESS_HIGH 0x14
-#define GTP_I2C_ADDRESS_LOW 0x5D
-#define GTP_VALID_ADDR_START 0x8040
-#define GTP_VALID_ADDR_END 0x8177
-
-/* GTP CM_HEAD RW flags */
-#define GTP_RW_READ 0
-#define GTP_RW_WRITE 1
-#define GTP_RW_READ_IC_TYPE 2
-#define GTP_RW_WRITE_IC_TYPE 3
-#define GTP_RW_FILL_INFO 4
-#define GTP_RW_NO_WRITE 5
-#define GTP_RW_READ_ERROR 6
-#define GTP_RW_DISABLE_IRQ 7
-#define GTP_RW_READ_VERSION 8
-#define GTP_RW_ENABLE_IRQ 9
-#define GTP_RW_ENTER_UPDATE_MODE 11
-#define GTP_RW_LEAVE_UPDATE_MODE 13
-#define GTP_RW_UPDATE_FW 15
-#define GTP_RW_CHECK_RAWDIFF_MODE 17
-
-/* GTP need flag or interrupt */
-#define GTP_NO_NEED 0
-#define GTP_NEED_FLAG 1
-#define GTP_NEED_INTERRUPT 2
-
-/*****************************End of Part III********************************/
-
-void gtp_esd_switch(struct i2c_client *client, int on);
-
-int gtp_i2c_read_dbl_check(struct i2c_client *client, u16 addr,
- u8 *rxbuf, int len);
-int gtp_send_cfg(struct goodix_ts_data *ts);
-void gtp_reset_guitar(struct goodix_ts_data *ts, int ms);
-void gtp_irq_disable(struct goodix_ts_data *ts);
-void gtp_irq_enable(struct goodix_ts_data *ts);
-
-#ifdef CONFIG_GT9XX_TOUCHPANEL_DEBUG
-s32 init_wr_node(struct i2c_client *client);
-void uninit_wr_node(void);
-#endif
-
-u8 gup_init_update_proc(struct goodix_ts_data *ts);
-s32 gup_enter_update_mode(struct i2c_client *client);
-void gup_leave_update_mode(struct i2c_client *client);
-s32 gup_update_proc(void *dir);
-extern struct i2c_client *i2c_connect_client;
-#endif /* _GOODIX_GT9XX_H_ */
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_update.c b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
deleted file mode 100644
index 6bc243492272..000000000000
--- a/drivers/input/touchscreen/gt9xx/gt9xx_update.c
+++ /dev/null
@@ -1,1530 +0,0 @@
-/* drivers/input/touchscreen/gt9xx_update.c
- *
- * 2010 - 2012 Goodix Technology.
- * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be a reference
- * to you, when you are integrating the GOODiX's CTP IC into your system,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * Latest Version:1.6
- * Author: andrew@goodix.com
- * Revision Record:
- * V1.0:
- * first release. By Andrew, 2012/08/31
- * V1.2:
- * add force update,GT9110P pid map. By Andrew, 2012/10/15
- * V1.4:
- * 1. add config auto update function;
- * 2. modify enter_update_mode;
- * 3. add update file cal checksum.
- * By Andrew, 2012/12/12
- * V1.6:
- * 1. replace guitar_client with i2c_connect_client;
- * 2. support firmware header array update.
- * By Meta, 2013/03/11
- */
-#include "gt9xx.h"
-#include <linux/firmware.h>
-#include <linux/workqueue.h>
-#include <linux/kernel.h>
-
-#define FIRMWARE_NAME_LEN_MAX 256
-
-#define GUP_REG_HW_INFO 0x4220
-#define GUP_REG_FW_MSG 0x41E4
-#define GUP_REG_PID_VID 0x8140
-
-#define GOODIX_FIRMWARE_FILE_NAME "_goodix_update_.bin"
-#define GOODIX_CONFIG_FILE_NAME "_goodix_config_.cfg"
-
-#define FW_HEAD_LENGTH 14
-#define FW_SECTION_LENGTH 0x2000
-#define FW_DSP_ISP_LENGTH 0x1000
-#define FW_DSP_LENGTH 0x1000
-#define FW_BOOT_LENGTH 0x800
-
-#define PACK_SIZE 256
-#define MAX_FRAME_CHECK_TIME 5
-
-#define _bRW_MISCTL__SRAM_BANK 0x4048
-#define _bRW_MISCTL__MEM_CD_EN 0x4049
-#define _bRW_MISCTL__CACHE_EN 0x404B
-#define _bRW_MISCTL__TMR0_EN 0x40B0
-#define _rRW_MISCTL__SWRST_B0_ 0x4180
-#define _bWO_MISCTL__CPU_SWRST_PULSE 0x4184
-#define _rRW_MISCTL__BOOTCTL_B0_ 0x4190
-#define _rRW_MISCTL__BOOT_OPT_B0_ 0x4218
-#define _rRW_MISCTL__BOOT_CTL_ 0x5094
-
-#define FAIL 0
-#define SUCCESS 1
-
-#define RESET_DELAY_US 20000
-
-struct st_fw_head {
- u8 hw_info[4]; /* hardware info */
- u8 pid[8]; /* product id */
- u16 vid; /* version id */
-} __packed;
-
-struct st_update_msg {
- u8 force_update;
- u8 fw_flag;
- bool need_free;
- u8 *fw_data;
- u32 fw_len;
- struct st_fw_head ic_fw_msg;
-};
-
-static struct st_update_msg update_msg;
-u16 show_len;
-u16 total_len;
-u8 got_file_flag;
-u8 searching_file;
-/*******************************************************
-Function:
- Read data from the i2c slave device.
-Input:
- client: i2c device.
- buf[0~1]: read start address.
- buf[2~len-1]: read data buffer.
- len: GTP_ADDR_LENGTH + read bytes count
-Output:
- numbers of i2c_msgs to transfer:
- 2: succeed, otherwise: failed
-*********************************************************/
-static s32 gup_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
-{
- s32 ret = -1;
- u8 retries = 0;
- struct i2c_msg msgs[2] = {
- {
- .flags = !I2C_M_RD,
- .addr = client->addr,
- .len = GTP_ADDR_LENGTH,
- .buf = &buf[0],
- },
- {
- .flags = I2C_M_RD,
- .addr = client->addr,
- .len = len - GTP_ADDR_LENGTH,
- .buf = &buf[GTP_ADDR_LENGTH],
- },
- };
-
- while (retries < 5) {
- ret = i2c_transfer(client->adapter, msgs, 2);
- if (ret == 2)
- break;
- retries++;
- }
-
- if (retries == 5) {
- dev_err(&client->dev, "I2C read retry limit over.\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-/*******************************************************
-Function:
- Write data to the i2c slave device.
-Input:
- client: i2c device.
- buf[0~1]: write start address.
- buf[2~len-1]: data buffer
- len: GTP_ADDR_LENGTH + write bytes count
-Output:
- numbers of i2c_msgs to transfer:
- 1: succeed, otherwise: failed
-*********************************************************/
-s32 gup_i2c_write(struct i2c_client *client, u8 *buf, s32 len)
-{
- s32 ret = -1;
- u8 retries = 0;
- struct i2c_msg msg = {
- .flags = !I2C_M_RD,
- .addr = client->addr,
- .len = len,
- .buf = buf,
- };
-
- while (retries < 5) {
- ret = i2c_transfer(client->adapter, &msg, 1);
- if (ret == 1)
- break;
- retries++;
- }
-
- if (retries == 5) {
- dev_err(&client->dev, "I2C write retry limit over\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-static s32 gup_init_panel(struct goodix_ts_data *ts)
-{
- struct i2c_client *client = ts->client;
- u8 *config_data;
- s32 ret = 0;
- s32 i = 0;
- u8 check_sum = 0;
- u8 opr_buf[16];
- u8 sensor_id = 0;
-
- for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++)
- if (ts->pdata->config_data_len[i])
- break;
-
- if (i == GOODIX_MAX_CFG_GROUP) {
- sensor_id = 0;
- } else {
- ret = gtp_i2c_read_dbl_check(client, GTP_REG_SENSOR_ID,
- &sensor_id, 1);
- if (ret == SUCCESS) {
- if (sensor_id >= GOODIX_MAX_CFG_GROUP) {
- pr_err("Invalid sensor_id(0x%02X), No Config Sent",
- sensor_id);
- return -EINVAL;
- }
- } else {
- pr_err("Failed to get sensor_id, No config sent\n");
- return -EINVAL;
- }
- }
-
- pr_debug("Sensor ID selected: %d", sensor_id);
-
- if (ts->pdata->config_data_len[sensor_id] < GTP_CONFIG_MIN_LENGTH ||
- !ts->pdata->config_data_len[sensor_id]) {
- pr_err("Sensor_ID(%d) matches with NULL or INVALID CONFIG GROUP",
- sensor_id);
- return -EINVAL;
- }
-
- ret = gtp_i2c_read_dbl_check(client, GTP_REG_CONFIG_DATA,
- &opr_buf[0], 1);
- if (ret == SUCCESS) {
- pr_debug("CFG_GROUP%d Config Version: %d, IC Config Version: %d",
- sensor_id + 1,
- ts->pdata->config_data[sensor_id][0],
- opr_buf[0]);
-
- ts->pdata->config_data[sensor_id][0] = opr_buf[0];
- ts->fixed_cfg = 0;
- } else {
- pr_err("Failed to get ic config version. No config sent");
- return -EINVAL;
- }
-
- config_data = ts->pdata->config_data[sensor_id];
- ts->config_data = ts->pdata->config_data[sensor_id];
- ts->gtp_cfg_len = ts->pdata->config_data_len[sensor_id];
-
- pr_debug("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x\n",
- ts->abs_x_max, ts->abs_y_max, ts->int_trigger_type);
-
- config_data[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH;
- config_data[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8);
- config_data[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT;
- config_data[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8);
-
- if (GTP_INT_TRIGGER == 0) /* RISING */
- config_data[TRIGGER_LOC] &= 0xfe;
- else if (GTP_INT_TRIGGER == 1) /* FALLING */
- config_data[TRIGGER_LOC] |= 0x01;
-
- check_sum = 0;
- for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++)
- check_sum += config_data[i];
-
- config_data[ts->gtp_cfg_len] = (~check_sum) + 1;
-
- ret = gtp_send_cfg(ts);
- if (ret < 0)
- pr_err("Send config error\n");
-
- ts->config_data = NULL;
- ts->gtp_cfg_len = 0;
- msleep(20);
- return 0;
-}
-
-static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8 *msg, s32 len)
-{
- u8 i = 0;
-
- msg[0] = (addr >> 8) & 0xff;
- msg[1] = addr & 0xff;
-
- for (i = 0; i < 5; i++)
- if (gup_i2c_read(client, msg, GTP_ADDR_LENGTH + len) > 0)
- break;
-
- if (i >= 5) {
- pr_err("Read data from 0x%02x%02x failed\n", msg[0], msg[1]);
- return FAIL;
- }
-
- return SUCCESS;
-}
-
-static u8 gup_set_ic_msg(struct i2c_client *client, u16 addr, u8 val)
-{
- u8 i = 0;
- u8 msg[3] = {
- (addr >> 8) & 0xff,
- addr & 0xff,
- val,
- };
-
- for (i = 0; i < 5; i++)
- if (gup_i2c_write(client, msg, GTP_ADDR_LENGTH + 1) > 0)
- break;
-
- if (i >= 5) {
- pr_err("Set data to 0x%02x%02x failed\n", msg[0], msg[1]);
- return FAIL;
- }
-
- return SUCCESS;
-}
-
-static u8 gup_get_ic_fw_msg(struct i2c_client *client)
-{
- s32 ret = -1;
- u8 retry = 0;
- u8 buf[16];
- u8 i;
-
- /* step1:get hardware info */
- ret = gtp_i2c_read_dbl_check(client, GUP_REG_HW_INFO,
- &buf[GTP_ADDR_LENGTH], 4);
- if (ret == FAIL) {
- pr_err("get hw_info failed,exit");
- return FAIL;
- }
-
- /* buf[2~5]: 00 06 90 00 */
- /* hw_info: 00 90 06 00 */
- for (i = 0; i < 4; i++)
- update_msg.ic_fw_msg.hw_info[i] = buf[GTP_ADDR_LENGTH + 3 - i];
-
- pr_debug("IC Hardware info:%02x%02x%02x%02x\n",
- update_msg.ic_fw_msg.hw_info[0],
- update_msg.ic_fw_msg.hw_info[1],
- update_msg.ic_fw_msg.hw_info[2],
- update_msg.ic_fw_msg.hw_info[3]);
-
- /* step2:get firmware message */
- for (retry = 0; retry < 2; retry++) {
- ret = gup_get_ic_msg(client, GUP_REG_FW_MSG, buf, 1);
- if (ret == FAIL) {
- pr_err("Read firmware message fail\n");
- return ret;
- }
-
- update_msg.force_update = buf[GTP_ADDR_LENGTH];
- if ((update_msg.force_update != 0xBE) && (!retry)) {
- pr_info("The check sum in ic is error\n");
- pr_info("The IC will be updated by force\n");
- continue;
- }
- break;
- }
- pr_debug("IC force update flag:0x%x\n", update_msg.force_update);
-
- /* step3:get pid & vid */
- ret = gtp_i2c_read_dbl_check(client, GUP_REG_PID_VID,
- &buf[GTP_ADDR_LENGTH], 6);
- if (ret == FAIL) {
- pr_err("get pid & vid failed,exit");
- return FAIL;
- }
-
- memset(update_msg.ic_fw_msg.pid, 0, sizeof(update_msg.ic_fw_msg.pid));
- memcpy(update_msg.ic_fw_msg.pid, &buf[GTP_ADDR_LENGTH], 4);
- pr_debug("IC Product id:%s\n", update_msg.ic_fw_msg.pid);
-
- /* GT9XX PID MAPPING
- * |-----FLASH-----RAM-----|
- * |------918------918-----|
- * |------968------968-----|
- * |------913------913-----|
- * |------913P-----913P----|
- * |------927------927-----|
- * |------927P-----927P----|
- * |------9110-----9110----|
- * |------9110P----9111----|
- */
- if (update_msg.ic_fw_msg.pid[0] != 0) {
- if (!memcmp(update_msg.ic_fw_msg.pid, "9111", 4)) {
- pr_debug("IC Mapping Product id:%s\n",
- update_msg.ic_fw_msg.pid);
- memcpy(update_msg.ic_fw_msg.pid, "9110P", 5);
- }
- }
-
- update_msg.ic_fw_msg.vid = buf[GTP_ADDR_LENGTH + 4] +
- (buf[GTP_ADDR_LENGTH + 5] << 8);
- pr_debug("IC version id:%04x\n", update_msg.ic_fw_msg.vid);
-
- return SUCCESS;
-}
-
-s32 gup_enter_update_mode(struct i2c_client *client)
-{
- s32 ret = -1;
- u8 retry = 0;
- u8 rd_buf[3];
- struct goodix_ts_data *ts = i2c_get_clientdata(client);
-
- /* step1:RST output low last at least 2ms */
- gpio_direction_output(ts->pdata->reset_gpio, 0);
- usleep_range(RESET_DELAY_US, RESET_DELAY_US + 1);
-
- /* step2:select I2C slave addr,INT:0--0xBA;1--0x28. */
- gpio_direction_output(ts->pdata->irq_gpio,
- (client->addr == GTP_I2C_ADDRESS_HIGH));
- msleep(20);
-
- /* step3:RST output high reset guitar */
- gpio_direction_output(ts->pdata->reset_gpio, 1);
-
- /* 20121211 modify start */
- msleep(20);
- while (retry++ < 200) {
- /* step4:Hold ss51 & dsp */
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
- if (ret <= 0) {
- pr_debug("Hold ss51 & dsp I2C error,retry:%d\n", retry);
- continue;
- }
-
- /* step5:Confirm hold */
- ret = gup_get_ic_msg(client, _rRW_MISCTL__SWRST_B0_, rd_buf, 1);
- if (ret <= 0) {
- pr_debug("Hold ss51 & dsp I2C error,retry:%d\n", retry);
- continue;
- }
- if (rd_buf[GTP_ADDR_LENGTH] == 0x0C) {
- pr_debug("Hold ss51 & dsp confirm SUCCESS\n");
- break;
- }
- pr_debug("Hold ss51 & dsp confirm 0x4180 failed,value:%d\n",
- rd_buf[GTP_ADDR_LENGTH]);
- }
- if (retry >= 200) {
- pr_err("Enter update Hold ss51 failed\n");
- return FAIL;
- }
-
- /* step6:DSP_CK and DSP_ALU_CK PowerOn */
- ret = gup_set_ic_msg(client, 0x4010, 0x00);
-
- /* 20121211 modify end */
- return ret;
-}
-
-void gup_leave_update_mode(struct i2c_client *client)
-{
- struct goodix_ts_data *ts = i2c_get_clientdata(client);
-
- gpio_direction_input(ts->pdata->irq_gpio);
- pr_debug("reset chip");
- gtp_reset_guitar(ts, 20);
-}
-
-/* Get the correct nvram data
- * The correct conditions:
- * 1. the hardware info is the same
- * 2. the product id is the same
- * 3. the firmware version in update file is greater than the firmware
- * version in ic or the check sum in ic is wrong
-
- * Update Conditions:
- * 1. Same hardware info
- * 2. Same PID
- * 3. File PID > IC PID
-
- * Force Update Conditions:
- * 1. Wrong ic firmware checksum
- * 2. INVALID IC PID or VID
- * 3. IC PID == 91XX || File PID == 91XX
- */
-
-static u8 gup_enter_update_judge(struct i2c_client *client,
- struct st_fw_head *fw_head)
-{
- u16 u16_tmp;
- s32 i = 0;
-
- u16_tmp = fw_head->vid;
- fw_head->vid = (u16)(u16_tmp>>8) + (u16)(u16_tmp<<8);
-
- pr_debug("FILE HARDWARE INFO:%02x%02x%02x%02x\n", fw_head->hw_info[0],
- fw_head->hw_info[1], fw_head->hw_info[2], fw_head->hw_info[3]);
- pr_debug("FILE PID:%s\n", fw_head->pid);
- pr_debug("FILE VID:%04x\n", fw_head->vid);
-
- pr_debug("IC HARDWARE INFO:%02x%02x%02x%02x\n",
- update_msg.ic_fw_msg.hw_info[0],
- update_msg.ic_fw_msg.hw_info[1],
- update_msg.ic_fw_msg.hw_info[2],
- update_msg.ic_fw_msg.hw_info[3]);
- pr_debug("IC PID:%s\n", update_msg.ic_fw_msg.pid);
- pr_debug("IC VID:%04x\n", update_msg.ic_fw_msg.vid);
-
- /* First two conditions */
- if (!memcmp(fw_head->hw_info, update_msg.ic_fw_msg.hw_info,
- sizeof(update_msg.ic_fw_msg.hw_info))) {
- pr_debug("Get the same hardware info\n");
- if (update_msg.force_update != 0xBE) {
- pr_info("FW chksum error,need enter update\n");
- return SUCCESS;
- }
-
- /* 20130523 start */
- if (strlen(update_msg.ic_fw_msg.pid) < 3) {
- pr_info("Illegal IC pid, need enter update\n");
- return SUCCESS;
- }
- for (i = 0; i < 3; i++) {
- if ((update_msg.ic_fw_msg.pid[i] < 0x30) ||
- (update_msg.ic_fw_msg.pid[i] > 0x39)) {
- pr_info("Illegal IC pid, out of bound, need enter update\n");
- return SUCCESS;
- }
- }
- /* 20130523 end */
-
- if ((!memcmp(fw_head->pid, update_msg.ic_fw_msg.pid,
- (strlen(fw_head->pid) < 3 ? 3 : strlen(fw_head->pid)))) ||
- (!memcmp(update_msg.ic_fw_msg.pid, "91XX", 4)) ||
- (!memcmp(fw_head->pid, "91XX", 4))) {
- if (!memcmp(fw_head->pid, "91XX", 4))
- pr_debug("Force none same pid update mode\n");
- else
- pr_debug("Get the same pid\n");
-
- /* The third condition */
- if (fw_head->vid > update_msg.ic_fw_msg.vid) {
- pr_info("Need enter update");
- return SUCCESS;
- }
- pr_err("Don't meet the third condition\n");
- pr_err("File VID <= Ic VID, update aborted\n");
- } else {
- pr_err("File PID != Ic PID, update aborted\n");
- }
- } else {
- pr_err("Different Hardware, update aborted\n");
- }
-
- return FAIL;
-}
-
-static s8 gup_update_config(struct i2c_client *client,
- const struct firmware *cfg)
-{
- s32 ret = 0;
- s32 i = 0;
- s32 file_cfg_len = 0;
- u32 chip_cfg_len = 0;
- s32 count = 0;
- u8 *buf;
- u8 *file_config;
- u8 pid[8];
- u8 high, low;
-
- if (!cfg || !cfg->data) {
- pr_err("No need to upgrade config");
- return FAIL;
- }
-
- ret = gup_get_ic_msg(client, GUP_REG_PID_VID, pid, 6);
- if (ret == FAIL) {
- pr_err("Read product id & version id fail");
- return FAIL;
- }
- pid[5] = '\0';
- pr_debug("update cfg get pid:%s\n", &pid[GTP_ADDR_LENGTH]);
-
- chip_cfg_len = 186;
- if (!memcmp(&pid[GTP_ADDR_LENGTH], "968", 3) ||
- !memcmp(&pid[GTP_ADDR_LENGTH], "910", 3) ||
- !memcmp(&pid[GTP_ADDR_LENGTH], "960", 3)) {
- chip_cfg_len = 228;
- }
- pr_debug("config file ASCII len: %zu", cfg->size);
- pr_debug("need config binary len: %u", chip_cfg_len);
- if ((cfg->size + 5) < chip_cfg_len * 5) {
- pr_err("Config length error");
- return -EINVAL;
- }
-
- buf = devm_kzalloc(&client->dev, cfg->size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- file_config = devm_kzalloc(&client->dev, chip_cfg_len + GTP_ADDR_LENGTH,
- GFP_KERNEL);
- if (!file_config)
- return -ENOMEM;
-
- pr_debug("Delete illegal character");
- for (i = 0, count = 0; i < cfg->size; i++) {
- if (cfg->data[i] == ' ' || cfg->data[i] == '\r'
- || cfg->data[i] == '\n')
- continue;
- buf[count++] = cfg->data[i];
- }
-
- pr_debug("Ascii to hex");
- file_config[0] = GTP_REG_CONFIG_DATA >> 8;
- file_config[1] = GTP_REG_CONFIG_DATA & 0xff;
- for (i = 0, file_cfg_len = GTP_ADDR_LENGTH; i < count; i = i + 5) {
- if ((buf[i] == '0') && ((buf[i + 1] == 'x') ||
- (buf[i + 1] == 'X'))) {
- ret = hex2bin(&high, &buf[i + 2], 1);
- if (ret) {
- pr_err("Failed to convert high address from hex2bin");
- return ret;
- }
- ret = hex2bin(&low, &buf[i + 3], 1);
- if (ret) {
- pr_err("Failed to convert low address from hex2bin");
- return ret;
- }
-
- if ((high == 0xFF) || (low == 0xFF)) {
- ret = 0;
- pr_err("Illegal config file");
- return ret;
- }
- file_config[file_cfg_len++] = (high<<4) + low;
- } else {
- ret = 0;
- pr_err("Illegal config file");
- return ret;
- }
- }
-
- i = 0;
- while (i++ < 5) {
- ret = gup_i2c_write(client, file_config, file_cfg_len);
- if (ret > 0)
- break;
- pr_err("Send config i2c error");
- }
-
- return ret;
-}
-
-static s32 gup_get_firmware_file(struct i2c_client *client,
- struct st_update_msg *msg, u8 *path)
-{
- s32 ret;
- const struct firmware *fw = NULL;
-
- ret = request_firmware(&fw, path, &client->dev);
- if (ret < 0) {
- dev_info(&client->dev, "Cannot get firmware - %s (%d)\n",
- path, ret);
- return -EEXIST;
- }
-
- dev_dbg(&client->dev, "Config File: %s size: %zu", path, fw->size);
- msg->fw_data =
- devm_kzalloc(&client->dev, fw->size, GFP_KERNEL);
- if (!msg->fw_data) {
- release_firmware(fw);
- return -ENOMEM;
- }
-
- memcpy(msg->fw_data, fw->data, fw->size);
- msg->fw_len = fw->size;
- msg->need_free = true;
- release_firmware(fw);
- return 0;
-}
-
-static u8 gup_check_firmware_name(struct i2c_client *client,
- u8 **path_p)
-{
- u8 len;
- u8 *fname;
-
- if (!(*path_p)) {
- *path_p = GOODIX_FIRMWARE_FILE_NAME;
- return 0;
- }
-
- len = strnlen(*path_p, FIRMWARE_NAME_LEN_MAX);
- if (len >= FIRMWARE_NAME_LEN_MAX) {
- dev_err(&client->dev, "firmware name too long");
- return -EINVAL;
- }
-
- fname = strrchr(*path_p, '/');
- if (fname) {
- fname = fname + 1;
- *path_p = fname;
- }
- return 0;
-}
-
-static u8 gup_check_update_file(struct i2c_client *client,
- struct st_fw_head *fw_head, u8 *path)
-{
- s32 ret = 0;
- s32 i = 0;
- s32 fw_checksum = 0;
- u16 temp;
- const struct firmware *fw = NULL;
-
- ret = request_firmware(&fw, GOODIX_CONFIG_FILE_NAME, &client->dev);
- if (ret < 0) {
- dev_info(&client->dev, "Cannot get config file - %s (%d)\n",
- GOODIX_CONFIG_FILE_NAME, ret);
- } else {
- dev_dbg(&client->dev,
- "Update config File: %s", GOODIX_CONFIG_FILE_NAME);
- ret = gup_update_config(client, fw);
- if (ret <= 0)
- dev_err(&client->dev, "Update config failed");
- release_firmware(fw);
- }
-
- update_msg.need_free = false;
- update_msg.fw_len = 0;
-
- if (gup_check_firmware_name(client, &path))
- goto load_failed;
-
- if (gup_get_firmware_file(client, &update_msg, path))
- goto load_failed;
-
- memcpy(fw_head, update_msg.fw_data, FW_HEAD_LENGTH);
-
- /* check firmware legality */
- fw_checksum = 0;
- for (i = 0; i < FW_SECTION_LENGTH * 4 + FW_DSP_ISP_LENGTH +
- FW_DSP_LENGTH + FW_BOOT_LENGTH; i += 2) {
- temp = (update_msg.fw_data[FW_HEAD_LENGTH + i] << 8) +
- update_msg.fw_data[FW_HEAD_LENGTH + i + 1];
- fw_checksum += temp;
- }
-
- pr_debug("firmware checksum:%x", fw_checksum & 0xFFFF);
- if (fw_checksum & 0xFFFF) {
- dev_err(&client->dev, "Illegal firmware file");
- goto load_failed;
- }
-
- return SUCCESS;
-
-load_failed:
- if (update_msg.need_free) {
- devm_kfree(&client->dev, update_msg.fw_data);
- update_msg.need_free = false;
- }
- return FAIL;
-}
-
-static u8 gup_burn_proc(struct i2c_client *client, u8 *burn_buf, u16 start_addr,
- u16 total_length)
-{
- s32 ret = 0;
- u16 burn_addr = start_addr;
- u16 frame_length = 0;
- u16 burn_length = 0;
- u8 wr_buf[PACK_SIZE + GTP_ADDR_LENGTH];
- u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH];
- u8 retry = 0;
-
- pr_debug("Begin burn %dk data to addr 0x%x", (total_length / 1024),
- start_addr);
- while (burn_length < total_length) {
- pr_debug("B/T:%04d/%04d", burn_length, total_length);
- frame_length = ((total_length - burn_length) > PACK_SIZE)
- ? PACK_SIZE : (total_length - burn_length);
- wr_buf[0] = (u8)(burn_addr>>8);
- rd_buf[0] = wr_buf[0];
- wr_buf[1] = (u8)burn_addr;
- rd_buf[1] = wr_buf[1];
- memcpy(&wr_buf[GTP_ADDR_LENGTH], &burn_buf[burn_length],
- frame_length);
-
- for (retry = 0; retry < MAX_FRAME_CHECK_TIME; retry++) {
- ret = gup_i2c_write(client, wr_buf,
- GTP_ADDR_LENGTH + frame_length);
- if (ret <= 0) {
- pr_err("Write frame data i2c error\n");
- continue;
- }
- ret = gup_i2c_read(client, rd_buf, GTP_ADDR_LENGTH +
- frame_length);
- if (ret <= 0) {
- pr_err("Read back frame data i2c error\n");
- continue;
- }
-
- if (memcmp(&wr_buf[GTP_ADDR_LENGTH],
- &rd_buf[GTP_ADDR_LENGTH], frame_length)) {
- pr_err("Check frame data fail,not equal\n");
- continue;
- } else {
- break;
- }
- }
- if (retry >= MAX_FRAME_CHECK_TIME) {
- pr_err("Burn frame data time out,exit\n");
- return FAIL;
- }
- burn_length += frame_length;
- burn_addr += frame_length;
- }
- return SUCCESS;
-}
-
-static u8 gup_load_section_file(u8 *buf, u16 offset, u16 length)
-{
- if (!update_msg.fw_data ||
- update_msg.fw_len < FW_HEAD_LENGTH + offset + length) {
- pr_err(
- "<<-GTP->> cannot load section data. fw_len=%d read end=%d\n",
- update_msg.fw_len,
- FW_HEAD_LENGTH + offset + length);
- return FAIL;
- }
- memcpy(buf, &update_msg.fw_data[FW_HEAD_LENGTH + offset], length);
-
- return SUCCESS;
-}
-
-static u8 gup_recall_check(struct i2c_client *client, u8 *chk_src,
- u16 start_rd_addr, u16 chk_length)
-{
- u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH];
- s32 ret = 0;
- u16 recall_addr = start_rd_addr;
- u16 recall_length = 0;
- u16 frame_length = 0;
-
- while (recall_length < chk_length) {
- frame_length = ((chk_length - recall_length) > PACK_SIZE)
- ? PACK_SIZE : (chk_length - recall_length);
- ret = gup_get_ic_msg(client, recall_addr, rd_buf, frame_length);
- if (ret <= 0) {
- pr_err("recall i2c error,exit\n");
- return FAIL;
- }
-
- if (memcmp(&rd_buf[GTP_ADDR_LENGTH], &chk_src[recall_length],
- frame_length)) {
- pr_err("Recall frame data fail,not equal\n");
- return FAIL;
- }
-
- recall_length += frame_length;
- recall_addr += frame_length;
- }
- pr_debug("Recall check %dk firmware success\n", (chk_length/1024));
-
- return SUCCESS;
-}
-
-static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section,
- u16 start_addr, u8 bank_cmd)
-{
- s32 ret = 0;
- u8 rd_buf[5];
-
- /* step1:hold ss51 & dsp */
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
- if (ret <= 0) {
- pr_err("hold ss51 & dsp fail");
- return FAIL;
- }
-
- /* step2:set scramble */
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
- if (ret <= 0) {
- pr_err("set scramble fail");
- return FAIL;
- }
-
- /* step3:select bank */
- ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK,
- (bank_cmd >> 4)&0x0F);
- if (ret <= 0) {
- pr_err("select bank %d fail",
- (bank_cmd >> 4)&0x0F);
- return FAIL;
- }
-
- /* step4:enable accessing code */
- ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
- if (ret <= 0) {
- pr_err("enable accessing code fail");
- return FAIL;
- }
-
- /* step5:burn 8k fw section */
- ret = gup_burn_proc(client, fw_section, start_addr, FW_SECTION_LENGTH);
- if (ret == FAIL) {
- pr_err("burn fw_section fail");
- return FAIL;
- }
-
- /* step6:hold ss51 & release dsp */
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
- if (ret <= 0) {
- pr_err("hold ss51 & release dsp fail");
- return FAIL;
- }
- /* must delay */
- msleep(20);
-
- /* step7:send burn cmd to move data to flash from sram */
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, bank_cmd&0x0f);
- if (ret <= 0) {
- pr_err("send burn cmd fail");
- return FAIL;
- }
- pr_debug("Wait for the burn is complete");
- do {
- ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
- if (ret <= 0) {
- pr_err("Get burn state fail");
- return FAIL;
- }
- msleep(20);
- } while (rd_buf[GTP_ADDR_LENGTH]);
-
- /* step8:select bank */
- ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK,
- (bank_cmd >> 4)&0x0F);
- if (ret <= 0) {
- pr_err("select bank %d fail",
- (bank_cmd >> 4)&0x0F);
- return FAIL;
- }
-
- /* step9:enable accessing code */
- ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
- if (ret <= 0) {
- pr_err("enable accessing code fail");
- return FAIL;
- }
-
- /* step10:recall 8k fw section */
- ret = gup_recall_check(client, fw_section, start_addr,
- FW_SECTION_LENGTH);
- if (ret == FAIL) {
- pr_err("recall check 8k firmware fail");
- return FAIL;
- }
-
- /* step11:disable accessing code */
- ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x00);
- if (ret <= 0) {
- pr_err("disable accessing code fail");
- return FAIL;
- }
-
- return SUCCESS;
-}
-
-static u8 gup_burn_dsp_isp(struct i2c_client *client)
-{
- s32 ret = 0;
- u8 *fw_dsp_isp = NULL;
- u8 retry = 0;
-
- pr_debug("Begin burn dsp isp");
-
- /* step1:alloc memory */
- pr_debug("step1:alloc memory");
- while (retry++ < 5) {
- fw_dsp_isp = devm_kzalloc(&client->dev, FW_DSP_ISP_LENGTH,
- GFP_KERNEL);
- if (fw_dsp_isp == NULL) {
- continue;
- } else {
- break;
- }
- }
- if (retry == 5)
- return FAIL;
-
- /* step2:load dsp isp file data */
- pr_debug("step2:load dsp isp file data");
- ret = gup_load_section_file(fw_dsp_isp, (4 * FW_SECTION_LENGTH +
- FW_DSP_LENGTH + FW_BOOT_LENGTH), FW_DSP_ISP_LENGTH);
- if (ret == FAIL) {
- pr_err("load firmware dsp_isp fail");
- return FAIL;
- }
-
- /* step3:disable wdt,clear cache enable */
- pr_debug("step3:disable wdt,clear cache enable");
- ret = gup_set_ic_msg(client, _bRW_MISCTL__TMR0_EN, 0x00);
- if (ret <= 0) {
- pr_err("disable wdt fail");
- return FAIL;
- }
- ret = gup_set_ic_msg(client, _bRW_MISCTL__CACHE_EN, 0x00);
- if (ret <= 0) {
- pr_err("clear cache enable fail");
- return FAIL;
- }
-
- /* step4:hold ss51 & dsp */
- pr_debug("step4:hold ss51 & dsp");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
- if (ret <= 0) {
- pr_err("hold ss51 & dsp fail");
- return FAIL;
- }
-
- /* step5:set boot from sram */
- pr_debug("step5:set boot from sram");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOTCTL_B0_, 0x02);
- if (ret <= 0) {
- pr_err("set boot from sram fail");
- return FAIL;
- }
-
- /* step6:software reboot */
- pr_debug("step6:software reboot");
- ret = gup_set_ic_msg(client, _bWO_MISCTL__CPU_SWRST_PULSE, 0x01);
- if (ret <= 0) {
- pr_err("software reboot fail");
- return FAIL;
- }
-
- /* step7:select bank2 */
- pr_debug("step7:select bank2");
- ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x02);
- if (ret <= 0) {
- pr_err("select bank2 fail");
- return FAIL;
- }
-
- /* step8:enable accessing code */
- pr_debug("step8:enable accessing code");
- ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
- if (ret <= 0) {
- pr_err("enable accessing code fail");
- return FAIL;
- }
-
- /* step9:burn 4k dsp_isp */
- pr_debug("step9:burn 4k dsp_isp");
- ret = gup_burn_proc(client, fw_dsp_isp, 0xC000, FW_DSP_ISP_LENGTH);
- if (ret == FAIL) {
- pr_err("burn dsp_isp fail");
- return FAIL;
- }
-
- /* step10:set scramble */
- pr_debug("step10:set scramble");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
- if (ret <= 0) {
- pr_err("set scramble fail");
- return FAIL;
- }
-
- return SUCCESS;
-}
-
-static u8 gup_burn_fw_ss51(struct i2c_client *client)
-{
- u8 *fw_ss51 = NULL;
- u8 retry = 0;
- s32 ret = 0;
-
- pr_debug("Begin burn ss51 firmware");
-
- /* step1:alloc memory */
- pr_debug("step1:alloc memory");
- while (retry++ < 5) {
- fw_ss51 = devm_kzalloc(&client->dev, FW_SECTION_LENGTH,
- GFP_KERNEL);
- if (fw_ss51 == NULL) {
- continue;
- } else {
- break;
- }
- }
- if (retry == 5)
- return FAIL;
-
- /* step2:load ss51 firmware section 1 file data */
- pr_debug("step2:load ss51 firmware section 1 file data");
- ret = gup_load_section_file(fw_ss51, 0, FW_SECTION_LENGTH);
- if (ret == FAIL) {
- pr_err("load ss51 firmware section 1 fail");
- return FAIL;
- }
-
- /* step3:clear control flag */
- pr_debug("step3:clear control flag");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00);
- if (ret <= 0) {
- pr_err("clear control flag fail");
- return FAIL;
- }
-
- /* step4:burn ss51 firmware section 1 */
- pr_debug("step4:burn ss51 firmware section 1");
- ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x01);
- if (ret == FAIL) {
- pr_err("burn ss51 firmware section 1 fail");
- return FAIL;
- }
-
- /* step5:load ss51 firmware section 2 file data */
- pr_debug("step5:load ss51 firmware section 2 file data");
- ret = gup_load_section_file(fw_ss51, FW_SECTION_LENGTH,
- FW_SECTION_LENGTH);
- if (ret == FAIL) {
- pr_err("[burn_fw_ss51]load ss51 firmware section 2 fail\n");
- return FAIL;
- }
-
- /* step6:burn ss51 firmware section 2 */
- pr_debug("step6:burn ss51 firmware section 2");
- ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x02);
- if (ret == FAIL) {
- pr_err("burn ss51 firmware section 2 fail");
- return FAIL;
- }
-
- /* step7:load ss51 firmware section 3 file data */
- pr_debug("step7:load ss51 firmware section 3 file data");
- ret = gup_load_section_file(fw_ss51, 2*FW_SECTION_LENGTH,
- FW_SECTION_LENGTH);
- if (ret == FAIL) {
- pr_err("load ss51 firmware section 3 fail");
- return FAIL;
- }
-
- /* step8:burn ss51 firmware section 3 */
- pr_debug("step8:burn ss51 firmware section 3");
- ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x13);
- if (ret == FAIL) {
- pr_err("burn ss51 firmware section 3 fail");
- return FAIL;
- }
-
- /* step9:load ss51 firmware section 4 file data */
- pr_debug("step9:load ss51 firmware section 4 file data");
- ret = gup_load_section_file(fw_ss51, 3*FW_SECTION_LENGTH,
- FW_SECTION_LENGTH);
- if (ret == FAIL) {
- pr_err("load ss51 firmware section 4 fail");
- return FAIL;
- }
-
- /* step10:burn ss51 firmware section 4 */
- pr_debug("step10:burn ss51 firmware section 4");
- ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x14);
- if (ret == FAIL) {
- pr_err("burn ss51 firmware section 4 fail");
- return FAIL;
- }
-
- return SUCCESS;
-}
-
-static u8 gup_burn_fw_dsp(struct i2c_client *client)
-{
- s32 ret = 0;
- u8 *fw_dsp = NULL;
- u8 retry = 0;
- u8 rd_buf[5];
-
- pr_debug("Begin burn dsp firmware");
- /* step1:alloc memory */
- pr_debug("step1:alloc memory");
- while (retry++ < 5) {
- fw_dsp = devm_kzalloc(&client->dev, FW_DSP_LENGTH,
- GFP_KERNEL);
- if (fw_dsp == NULL) {
- continue;
- } else {
- break;
- }
- }
- if (retry == 5)
- return FAIL;
-
- /* step2:load firmware dsp */
- pr_debug("step2:load firmware dsp");
- ret = gup_load_section_file(fw_dsp, 4*FW_SECTION_LENGTH, FW_DSP_LENGTH);
- if (ret == FAIL) {
- pr_err("load firmware dsp fail");
- return ret;
- }
-
- /* step3:select bank3 */
- pr_debug("step3:select bank3");
- ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03);
- if (ret <= 0) {
- pr_err("select bank3 fail");
- return FAIL;
- }
-
- /* Step4:hold ss51 & dsp */
- pr_debug("step4:hold ss51 & dsp");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
- if (ret <= 0) {
- pr_err("hold ss51 & dsp fail");
- return FAIL;
- }
-
- /* step5:set scramble */
- pr_debug("step5:set scramble");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
- if (ret <= 0) {
- pr_err("set scramble fail");
- return FAIL;
- }
-
- /* step6:release ss51 & dsp */
- pr_debug("step6:release ss51 & dsp");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
- if (ret <= 0) {
- pr_err("release ss51 & dsp fail");
- return FAIL;
- }
- /* must delay */
- msleep(20);
-
- /* step7:burn 4k dsp firmware */
- pr_debug("step7:burn 4k dsp firmware");
- ret = gup_burn_proc(client, fw_dsp, 0x9000, FW_DSP_LENGTH);
- if (ret == FAIL) {
- pr_err("[burn_fw_dsp]burn fw_section fail\n");
- return ret;
- }
-
- /* step8:send burn cmd to move data to flash from sram */
- pr_debug("step8:send burn cmd to move data to flash from sram");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x05);
- if (ret <= 0) {
- pr_err("send burn cmd fail");
- return ret;
- }
- pr_debug("Wait for the burn is complete");
- do {
- ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
- if (ret <= 0) {
- pr_err("Get burn state fail");
- return ret;
- }
- msleep(20);
- } while (rd_buf[GTP_ADDR_LENGTH]);
-
- /* step9:recall check 4k dsp firmware */
- pr_debug("step9:recall check 4k dsp firmware");
- ret = gup_recall_check(client, fw_dsp, 0x9000, FW_DSP_LENGTH);
- if (ret == FAIL) {
- pr_err("recall check 4k dsp firmware fail");
- return ret;
- }
-
- return SUCCESS;
-}
-
-static u8 gup_burn_fw_boot(struct i2c_client *client)
-{
- s32 ret = 0;
- u8 *fw_boot = NULL;
- u8 retry = 0;
- u8 rd_buf[5];
-
- pr_debug("Begin burn bootloader firmware");
-
- /* step1:Alloc memory */
- pr_debug("step1:Alloc memory");
- while (retry++ < 5) {
- fw_boot = devm_kzalloc(&client->dev, FW_BOOT_LENGTH,
- GFP_KERNEL);
- if (fw_boot == NULL) {
- continue;
- } else {
- break;
- }
- }
- if (retry == 5)
- return FAIL;
-
- /* step2:load firmware bootloader */
- pr_debug("step2:load firmware bootloader");
- ret = gup_load_section_file(fw_boot, (4 * FW_SECTION_LENGTH +
- FW_DSP_LENGTH), FW_BOOT_LENGTH);
- if (ret == FAIL) {
- pr_err("load firmware dsp fail");
- return ret;
- }
-
- /* step3:hold ss51 & dsp */
- pr_debug("step3:hold ss51 & dsp");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
- if (ret <= 0) {
- pr_err("hold ss51 & dsp fail");
- return FAIL;
- }
-
- /* step4:set scramble */
- pr_debug("step4:set scramble");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
- if (ret <= 0) {
- pr_err("set scramble fail");
- return FAIL;
- }
-
- /* step5:release ss51 & dsp */
- pr_debug("step5:release ss51 & dsp");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
- if (ret <= 0) {
- pr_err("release ss51 & dsp fail");
- return FAIL;
- }
- /* must delay */
- msleep(20);
-
- /* step6:select bank3 */
- pr_debug("step6:select bank3");
- ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03);
- if (ret <= 0) {
- pr_err("select bank3 fail");
- return FAIL;
- }
-
- /* step7:burn 2k bootloader firmware */
- pr_debug("step7:burn 2k bootloader firmware");
- ret = gup_burn_proc(client, fw_boot, 0x9000, FW_BOOT_LENGTH);
- if (ret == FAIL) {
- pr_err("burn fw_section fail");
- return ret;
- }
-
- /* step7:send burn cmd to move data to flash from sram */
- pr_debug("step7:send burn cmd to flash data from sram");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x06);
- if (ret <= 0) {
- pr_err("send burn cmd fail");
- return ret;
- }
- pr_debug("Wait for the burn is complete");
- do {
- ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
- if (ret <= 0) {
- pr_err("Get burn state fail");
- return ret;
- }
- msleep(20);
- } while (rd_buf[GTP_ADDR_LENGTH]);
-
- /* step8:recall check 2k bootloader firmware */
- pr_debug("step8:recall check 2k bootloader firmware");
- ret = gup_recall_check(client, fw_boot, 0x9000, FW_BOOT_LENGTH);
- if (ret == FAIL) {
- pr_err("recall check 4k dsp firmware fail");
- return ret;
- }
-
- /* step9:enable download DSP code */
- pr_debug("step9:enable download DSP code ");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x99);
- if (ret <= 0) {
- pr_err("enable download DSP code fail");
- return FAIL;
- }
-
- /* step10:release ss51 & hold dsp */
- pr_debug("step10:release ss51 & hold dsp");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x08);
- if (ret <= 0) {
- pr_err("release ss51 & hold dsp fail");
- return FAIL;
- }
-
- return SUCCESS;
-}
-
-s32 gup_update_proc(void *dir)
-{
- s32 ret = 0;
- u8 retry = 0;
- struct st_fw_head fw_head;
- struct goodix_ts_data *ts = NULL;
-
- pr_debug("Begin update.");
-
- if (!i2c_connect_client) {
- pr_err("No i2c connect client for %s\n", __func__);
- return -EIO;
- }
-
- show_len = 1;
- total_len = 100;
-
- ts = i2c_get_clientdata(i2c_connect_client);
-
- if (searching_file) {
- /* exit .bin update file searching */
- searching_file = 0;
- pr_info("Exiting searching .bin update file.");
- /* wait for auto update quitted completely */
- while ((show_len != 200) && (show_len != 100))
- msleep(100);
- }
-
- ret = gup_check_update_file(i2c_connect_client, &fw_head, (u8 *)dir);
- if (ret == FAIL) {
- pr_err("check update file fail");
- goto file_fail;
- }
-
- /* gtp_reset_guitar(i2c_connect_client, 20); */
- ret = gup_get_ic_fw_msg(i2c_connect_client);
- if (ret == FAIL) {
- pr_err("get ic message fail");
- goto file_fail;
- }
-
- if (ts->force_update) {
- dev_dbg(&ts->client->dev, "Enter force update.");
- } else {
- ret = gup_enter_update_judge(ts->client, &fw_head);
- if (ret == FAIL) {
- dev_err(&ts->client->dev,
- "Check *.bin file fail.");
- goto file_fail;
- }
- }
-
- ts->enter_update = 1;
- gtp_irq_disable(ts);
-#if GTP_ESD_PROTECT
- gtp_esd_switch(ts->client, SWITCH_OFF);
-#endif
- ret = gup_enter_update_mode(i2c_connect_client);
- if (ret == FAIL) {
- pr_err("enter update mode fail");
- goto update_fail;
- }
-
- while (retry++ < 5) {
- show_len = 10;
- total_len = 100;
- ret = gup_burn_dsp_isp(i2c_connect_client);
- if (ret == FAIL) {
- pr_err("burn dsp isp fail");
- continue;
- }
-
- show_len += 10;
- ret = gup_burn_fw_ss51(i2c_connect_client);
- if (ret == FAIL) {
- pr_err("burn ss51 firmware fail");
- continue;
- }
-
- show_len += 40;
- ret = gup_burn_fw_dsp(i2c_connect_client);
- if (ret == FAIL) {
- pr_err("burn dsp firmware fail");
- continue;
- }
-
- show_len += 20;
- ret = gup_burn_fw_boot(i2c_connect_client);
- if (ret == FAIL) {
- pr_err("burn bootloader fw fail");
- continue;
- }
- show_len += 10;
- pr_info("UPDATE SUCCESS");
- break;
- }
- if (retry >= 5) {
- pr_err("retry timeout,UPDATE FAIL");
- goto update_fail;
- }
-
- pr_debug("leave update mode");
- gup_leave_update_mode(i2c_connect_client);
-
- msleep(100);
-
- if (ts->fw_error) {
- pr_info("firmware error auto update, resent config\n");
- gup_init_panel(ts);
- }
- show_len = 100;
- total_len = 100;
- ts->enter_update = 0;
- gtp_irq_enable(ts);
-
-#if GTP_ESD_PROTECT
- gtp_esd_switch(ts->client, SWITCH_ON);
-#endif
- if (update_msg.need_free) {
- devm_kfree(&ts->client->dev, update_msg.fw_data);
- update_msg.need_free = false;
- }
-
- return SUCCESS;
-
-update_fail:
- ts->enter_update = 0;
- gtp_irq_enable(ts);
-
-#if GTP_ESD_PROTECT
- gtp_esd_switch(ts->client, SWITCH_ON);
-#endif
-
-file_fail:
- show_len = 200;
- total_len = 100;
- if (update_msg.need_free) {
- devm_kfree(&ts->client->dev, update_msg.fw_data);
- update_msg.need_free = false;
- }
- return FAIL;
-}
-
-static void gup_update_work(struct work_struct *work)
-{
- if (gup_update_proc(NULL) == FAIL)
- pr_err("Goodix update work fail\n");
-}
-
-u8 gup_init_update_proc(struct goodix_ts_data *ts)
-{
- dev_dbg(&ts->client->dev, "Ready to run update work\n");
-
- INIT_DELAYED_WORK(&ts->goodix_update_work, gup_update_work);
- schedule_delayed_work(&ts->goodix_update_work,
- msecs_to_jiffies(3000));
-
- return 0;
-}
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
index 206941708141..3e85cb5e2ebc 100644
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
@@ -3666,7 +3666,7 @@ static int synaptics_rmi4_probe(struct platform_device *pdev)
return -EINVAL;
}
- rmi4_data = kzalloc(sizeof(*rmi4_data), GFP_KERNEL);
+ rmi4_data = devm_kzalloc(&pdev->dev, sizeof(*rmi4_data), GFP_KERNEL);
if (!rmi4_data) {
dev_err(&pdev->dev,
"%s: Failed to alloc mem for rmi4_data\n",
@@ -3684,6 +3684,12 @@ static int synaptics_rmi4_probe(struct platform_device *pdev)
rmi4_data->fingers_on_2d = false;
rmi4_data->update_coords = true;
+ rmi4_data->write_buf = devm_kzalloc(&pdev->dev, I2C_WRITE_BUF_MAX_LEN,
+ GFP_KERNEL);
+ if (!rmi4_data->write_buf)
+ return -ENOMEM;
+ rmi4_data->write_buf_len = I2C_WRITE_BUF_MAX_LEN;
+
rmi4_data->irq_enable = synaptics_rmi4_irq_enable;
rmi4_data->reset_device = synaptics_rmi4_reset_device;
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h
index 7d7e045d7917..a642092e2f63 100644
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h
@@ -101,6 +101,7 @@
#define PINCTRL_STATE_RELEASE "pmx_ts_release"
#define SYNA_FW_NAME_MAX_LEN 50
+#define I2C_WRITE_BUF_MAX_LEN 32
enum exp_fn {
RMI_DEV = 0,
@@ -277,6 +278,8 @@ struct synaptics_rmi4_data {
unsigned char no_sleep_setting;
unsigned char intr_mask[MAX_INTR_REGISTERS];
unsigned char *button_txrx_mapping;
+ unsigned char *write_buf;
+ unsigned short write_buf_len;
unsigned short num_of_intr_regs;
unsigned short f01_query_base_addr;
unsigned short f01_cmd_base_addr;
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
index 0ec16e606545..4787f2bcd768 100644
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
@@ -102,6 +102,7 @@
(fwu->config_data[2] == config_id[2]) && \
(fwu->config_data[3] == config_id[3]))
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static ssize_t fwu_sysfs_show_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count);
@@ -157,6 +158,7 @@ static ssize_t fwu_sysfs_config_id_show(struct device *dev,
static ssize_t fwu_sysfs_package_id_show(struct device *dev,
struct device_attribute *attr, char *buf);
+#endif
enum bl_version {
V5 = 5,
@@ -296,6 +298,7 @@ struct synaptics_rmi4_fwu_handle {
struct synaptics_rmi4_data *rmi4_data;
};
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static struct bin_attribute dev_attr_data = {
.attr = {
.name = "data",
@@ -305,9 +308,11 @@ static struct bin_attribute dev_attr_data = {
.read = fwu_sysfs_show_image,
.write = fwu_sysfs_store_image,
};
+#endif
static struct device_attribute attrs[] = {
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
__ATTR(force_update_fw, S_IWUSR | S_IWGRP,
NULL,
fwu_sysfs_force_reflash_store),
@@ -353,6 +358,7 @@ static struct device_attribute attrs[] = {
__ATTR(package_id, S_IRUGO,
fwu_sysfs_package_id_show,
synaptics_rmi4_store_error),
+#endif
};
static struct synaptics_rmi4_fwu_handle *fwu;
@@ -1220,6 +1226,7 @@ write_config:
return retval;
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static int fwu_start_write_config(void)
{
int retval;
@@ -1395,6 +1402,7 @@ exit:
return retval;
}
+#endif
static int fwu_do_lockdown(void)
{
@@ -1585,6 +1593,7 @@ int synaptics_dsx_fw_updater(unsigned char *fw_data)
}
EXPORT_SYMBOL(synaptics_dsx_fw_updater);
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static ssize_t fwu_sysfs_show_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count)
@@ -1972,6 +1981,7 @@ static ssize_t fwu_sysfs_package_id_show(struct device *dev,
(package_id[1] << 8) | package_id[0],
(package_id[3] << 8) | package_id[2]);
}
+#endif
static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data,
unsigned char intr_mask)
@@ -2045,6 +2055,7 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
fwu->do_lockdown = DO_LOCKDOWN;
fwu->initialized = true;
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj,
&dev_attr_data);
if (retval < 0) {
@@ -2053,6 +2064,7 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
__func__);
goto exit_free_fwu;
}
+#endif
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
@@ -2074,7 +2086,9 @@ exit_remove_attrs:
&attrs[attr_count].attr);
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
+#endif
exit_free_fwu:
kfree(fwu);
@@ -2096,7 +2110,9 @@ static void synaptics_rmi4_fwu_remove(struct synaptics_rmi4_data *rmi4_data)
&attrs[attr_count].attr);
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
+#endif
kfree(fwu->read_config_buf);
kfree(fwu);
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c
index 0b3fbaf9f462..0be7e0bc9045 100644
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c
@@ -134,18 +134,33 @@ static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data,
{
int retval;
unsigned char retry;
- unsigned char buf[length + 1];
struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
struct i2c_msg msg[] = {
{
.addr = i2c->addr,
.flags = 0,
.len = length + 1,
- .buf = buf,
}
};
mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
+ /*
+ * Reassign memory for write_buf in case length is greater than 32 bytes
+ */
+ if (rmi4_data->write_buf_len < length + 1) {
+ devm_kfree(rmi4_data->pdev->dev.parent, rmi4_data->write_buf);
+ rmi4_data->write_buf = devm_kzalloc(rmi4_data->pdev->dev.parent,
+ length + 1, GFP_KERNEL);
+ if (!rmi4_data->write_buf) {
+ rmi4_data->write_buf_len = 0;
+ retval = -ENOMEM;
+ goto exit;
+ }
+ rmi4_data->write_buf_len = length + 1;
+ }
+
+ /* Assign the write_buf of driver structure to i2c_msg buf */
+ msg[0].buf = rmi4_data->write_buf;
retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr);
if (retval != PAGE_SELECT_LEN) {
@@ -153,8 +168,8 @@ static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data,
goto exit;
}
- buf[0] = addr & MASK_8BIT;
- memcpy(&buf[1], &data[0], length);
+ rmi4_data->write_buf[0] = addr & MASK_8BIT;
+ memcpy(&rmi4_data->write_buf[1], &data[0], length);
for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
if (i2c_transfer(i2c->adapter, msg, 1) == 1) {
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c
index 52bd5cfc37c8..168318f85e53 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c
@@ -2218,10 +2218,12 @@ static int fwu_get_image_firmware_id(unsigned int *fw_id)
__func__);
return -ENOMEM;
}
- while (strptr[index] >= '0' && strptr[index] <= '9') {
+ while ((index < MAX_FIRMWARE_ID_LEN - 1) && strptr[index] >= '0'
+ && strptr[index] <= '9') {
firmware_id[index] = strptr[index];
index++;
}
+ firmware_id[index] = '\0';
retval = sstrtoul(firmware_id, 10, (unsigned long *)fw_id);
kfree(firmware_id);
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 5975d76ce755..a0ef57483ebb 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -926,7 +926,7 @@ again:
next_tail = (tail + sizeof(*cmd)) % CMD_BUFFER_SIZE;
left = (head - next_tail) % CMD_BUFFER_SIZE;
- if (left <= 2) {
+ if (left <= 0x20) {
struct iommu_cmd sync_cmd;
volatile u64 sem = 0;
int ret;
diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c
index 7caf2fa237f2..4831eb910fc7 100644
--- a/drivers/iommu/amd_iommu_v2.c
+++ b/drivers/iommu/amd_iommu_v2.c
@@ -809,8 +809,10 @@ int amd_iommu_init_device(struct pci_dev *pdev, int pasids)
goto out_free_domain;
group = iommu_group_get(&pdev->dev);
- if (!group)
+ if (!group) {
+ ret = -EINVAL;
goto out_free_domain;
+ }
ret = iommu_attach_group(dev_state->domain, group);
if (ret != 0)
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 03a691723349..51159711b1d8 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -2451,7 +2451,7 @@ static void arm_smmu_unassign_table(struct arm_smmu_domain *smmu_domain)
int ret;
int dest_vmids = VMID_HLOS;
int dest_perms = PERM_READ | PERM_WRITE | PERM_EXEC;
- int source_vmlist[2] = {VMID_HLOS, smmu_domain->secure_vmid};
+ int source_vmlist[2] = {smmu_domain->secure_vmid, VMID_HLOS};
struct arm_smmu_pte_info *pte_info, *temp;
if (!arm_smmu_is_master_side_secure(smmu_domain))
diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c
index 2acb9242bcf8..8c6364f03eac 100644
--- a/drivers/iommu/dma-mapping-fast.c
+++ b/drivers/iommu/dma-mapping-fast.c
@@ -25,6 +25,13 @@
#define FAST_PAGE_SIZE (1UL << FAST_PAGE_SHIFT)
#define FAST_PAGE_MASK (~(PAGE_SIZE - 1))
#define FAST_PTE_ADDR_MASK ((av8l_fast_iopte)0xfffffffff000)
+#define FAST_MAIR_ATTR_IDX_CACHE 1
+#define FAST_PTE_ATTRINDX_SHIFT 2
+#define FAST_PTE_ATTRINDX_MASK 0x7
+#define FAST_PTE_SH_SHIFT 8
+#define FAST_PTE_SH_MASK (((av8l_fast_iopte)0x3) << FAST_PTE_SH_SHIFT)
+#define FAST_PTE_SH_OS (((av8l_fast_iopte)2) << FAST_PTE_SH_SHIFT)
+#define FAST_PTE_SH_IS (((av8l_fast_iopte)3) << FAST_PTE_SH_SHIFT)
static pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot,
bool coherent)
@@ -56,6 +63,36 @@ static void fast_dmac_clean_range(struct dma_fast_smmu_mapping *mapping,
dmac_clean_range(start, end);
}
+static bool __fast_is_pte_coherent(av8l_fast_iopte *ptep)
+{
+ int attr_idx = (*ptep & (FAST_PTE_ATTRINDX_MASK <<
+ FAST_PTE_ATTRINDX_SHIFT)) >>
+ FAST_PTE_ATTRINDX_SHIFT;
+
+ if ((attr_idx == FAST_MAIR_ATTR_IDX_CACHE) &&
+ (((*ptep & FAST_PTE_SH_MASK) == FAST_PTE_SH_IS) ||
+ (*ptep & FAST_PTE_SH_MASK) == FAST_PTE_SH_OS))
+ return true;
+
+ return false;
+}
+
+static bool is_dma_coherent(struct device *dev, struct dma_attrs *attrs)
+{
+ bool is_coherent;
+
+ if (dma_get_attr(DMA_ATTR_FORCE_COHERENT, attrs))
+ is_coherent = true;
+ else if (dma_get_attr(DMA_ATTR_FORCE_NON_COHERENT, attrs))
+ is_coherent = false;
+ else if (is_device_dma_coherent(dev))
+ is_coherent = true;
+ else
+ is_coherent = false;
+
+ return is_coherent;
+}
+
/*
* Checks if the allocated range (ending at @end) covered the upcoming
* stale bit. We don't need to know exactly where the range starts since
@@ -315,7 +352,7 @@ static dma_addr_t fast_smmu_map_page(struct device *dev, struct page *page,
int nptes = len >> FAST_PAGE_SHIFT;
bool skip_sync = dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs);
int prot = __fast_dma_direction_to_prot(dir);
- bool is_coherent = is_device_dma_coherent(dev);
+ bool is_coherent = is_dma_coherent(dev, attrs);
prot = __get_iommu_pgprot(attrs, prot, is_coherent);
@@ -360,7 +397,7 @@ static void fast_smmu_unmap_page(struct device *dev, dma_addr_t iova,
int nptes = len >> FAST_PAGE_SHIFT;
struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK));
bool skip_sync = dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs);
- bool is_coherent = is_device_dma_coherent(dev);
+ bool is_coherent = is_dma_coherent(dev, attrs);
if (!skip_sync && !is_coherent)
__fast_dma_page_dev_to_cpu(page, offset, size, dir);
@@ -381,7 +418,7 @@ static void fast_smmu_sync_single_for_cpu(struct device *dev,
unsigned long offset = iova & ~FAST_PAGE_MASK;
struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK));
- if (!is_device_dma_coherent(dev))
+ if (!__fast_is_pte_coherent(pmd))
__fast_dma_page_dev_to_cpu(page, offset, size, dir);
}
@@ -394,7 +431,7 @@ static void fast_smmu_sync_single_for_device(struct device *dev,
unsigned long offset = iova & ~FAST_PAGE_MASK;
struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK));
- if (!is_device_dma_coherent(dev))
+ if (!__fast_is_pte_coherent(pmd))
__fast_dma_page_cpu_to_dev(page, offset, size, dir);
}
@@ -472,7 +509,7 @@ static void *fast_smmu_alloc(struct device *dev, size_t size,
struct sg_mapping_iter miter;
unsigned int count = ALIGN(size, SZ_4K) >> PAGE_SHIFT;
int prot = IOMMU_READ | IOMMU_WRITE; /* TODO: extract from attrs */
- bool is_coherent = is_device_dma_coherent(dev);
+ bool is_coherent = is_dma_coherent(dev, attrs);
pgprot_t remap_prot = __get_dma_pgprot(attrs, PAGE_KERNEL, is_coherent);
struct page **pages;
@@ -596,7 +633,7 @@ static int fast_smmu_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
unsigned long uaddr = vma->vm_start;
struct page **pages;
int i, nr_pages, ret = 0;
- bool coherent = is_device_dma_coherent(dev);
+ bool coherent = is_dma_coherent(dev, attrs);
vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot,
coherent);
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 59e9abd3345e..f0fc6f7b5d98 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -1993,6 +1993,25 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
if (context_present(context))
goto out_unlock;
+ /*
+ * For kdump cases, old valid entries may be cached due to the
+ * in-flight DMA and copied pgtable, but there is no unmapping
+ * behaviour for them, thus we need an explicit cache flush for
+ * the newly-mapped device. For kdump, at this point, the device
+ * is supposed to finish reset at its driver probe stage, so no
+ * in-flight DMA will exist, and we don't need to worry anymore
+ * hereafter.
+ */
+ if (context_copied(context)) {
+ u16 did_old = context_domain_id(context);
+
+ if (did_old >= 0 && did_old < cap_ndoms(iommu->cap))
+ iommu->flush.flush_context(iommu, did_old,
+ (((u16)bus) << 8) | devfn,
+ DMA_CCMD_MASK_NOBIT,
+ DMA_CCMD_DEVICE_INVL);
+ }
+
pgd = domain->pgd;
context_clear_entry(context);
@@ -3219,13 +3238,14 @@ static int __init init_dmars(void)
iommu_identity_mapping |= IDENTMAP_GFX;
#endif
+ check_tylersburg_isoch();
+
if (iommu_identity_mapping) {
ret = si_domain_init(hw_pass_through);
if (ret)
goto free_iommu;
}
- check_tylersburg_isoch();
/*
* If we copied translations from a previous kernel in the kdump
@@ -5020,6 +5040,25 @@ static void intel_iommu_remove_device(struct device *dev)
}
#ifdef CONFIG_INTEL_IOMMU_SVM
+#define MAX_NR_PASID_BITS (20)
+static inline unsigned long intel_iommu_get_pts(struct intel_iommu *iommu)
+{
+ /*
+ * Convert ecap_pss to extend context entry pts encoding, also
+ * respect the soft pasid_max value set by the iommu.
+ * - number of PASID bits = ecap_pss + 1
+ * - number of PASID table entries = 2^(pts + 5)
+ * Therefore, pts = ecap_pss - 4
+ * e.g. KBL ecap_pss = 0x13, PASID has 20 bits, pts = 15
+ */
+ if (ecap_pss(iommu->ecap) < 5)
+ return 0;
+
+ /* pasid_max is encoded as actual number of entries not the bits */
+ return find_first_bit((unsigned long *)&iommu->pasid_max,
+ MAX_NR_PASID_BITS) - 5;
+}
+
int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sdev)
{
struct device_domain_info *info;
@@ -5052,7 +5091,9 @@ int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sd
if (!(ctx_lo & CONTEXT_PASIDE)) {
context[1].hi = (u64)virt_to_phys(iommu->pasid_state_table);
- context[1].lo = (u64)virt_to_phys(iommu->pasid_table) | ecap_pss(iommu->ecap);
+ context[1].lo = (u64)virt_to_phys(iommu->pasid_table) |
+ intel_iommu_get_pts(iommu);
+
wmb();
/* CONTEXT_TT_MULTI_LEVEL and CONTEXT_TT_DEV_IOTLB are both
* extended to permit requests-with-PASID if the PASIDE bit
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 5f2b66286c0c..6a8a9492c771 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -89,6 +89,7 @@
#define ARM_LPAE_PTE_TYPE_TABLE 3
#define ARM_LPAE_PTE_TYPE_PAGE 3
+#define ARM_LPAE_PTE_SH_MASK (((arm_lpae_iopte)0x3) << 8)
#define ARM_LPAE_PTE_NSTABLE (((arm_lpae_iopte)1) << 63)
#define ARM_LPAE_PTE_XN (((arm_lpae_iopte)3) << 53)
#define ARM_LPAE_PTE_AF (((arm_lpae_iopte)1) << 10)
@@ -928,8 +929,9 @@ static bool __arm_lpae_is_iova_coherent(struct arm_lpae_io_pgtable *data,
ARM_LPAE_PTE_ATTRINDX_SHIFT)) >>
ARM_LPAE_PTE_ATTRINDX_SHIFT;
if ((attr_idx == ARM_LPAE_MAIR_ATTR_IDX_CACHE) &&
- ((*ptep & ARM_LPAE_PTE_SH_IS) ||
- (*ptep & ARM_LPAE_PTE_SH_OS)))
+ (((*ptep & ARM_LPAE_PTE_SH_MASK) == ARM_LPAE_PTE_SH_IS)
+ ||
+ (*ptep & ARM_LPAE_PTE_SH_MASK) == ARM_LPAE_PTE_SH_OS))
return true;
} else {
if (*ptep & ARM_LPAE_PTE_MEMATTR_OIWB)
diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c
index 0fea985ef1dc..d7af88534971 100644
--- a/drivers/irqchip/irq-bcm7038-l1.c
+++ b/drivers/irqchip/irq-bcm7038-l1.c
@@ -216,6 +216,31 @@ static int bcm7038_l1_set_affinity(struct irq_data *d,
return 0;
}
+static void bcm7038_l1_cpu_offline(struct irq_data *d)
+{
+ struct cpumask *mask = irq_data_get_affinity_mask(d);
+ int cpu = smp_processor_id();
+ cpumask_t new_affinity;
+
+ /* This CPU was not on the affinity mask */
+ if (!cpumask_test_cpu(cpu, mask))
+ return;
+
+ if (cpumask_weight(mask) > 1) {
+ /*
+ * Multiple CPU affinity, remove this CPU from the affinity
+ * mask
+ */
+ cpumask_copy(&new_affinity, mask);
+ cpumask_clear_cpu(cpu, &new_affinity);
+ } else {
+ /* Only CPU, put on the lowest online CPU */
+ cpumask_clear(&new_affinity);
+ cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity);
+ }
+ irq_set_affinity_locked(d, &new_affinity, false);
+}
+
static int __init bcm7038_l1_init_one(struct device_node *dn,
unsigned int idx,
struct bcm7038_l1_chip *intc)
@@ -267,6 +292,7 @@ static struct irq_chip bcm7038_l1_irq_chip = {
.irq_mask = bcm7038_l1_mask,
.irq_unmask = bcm7038_l1_unmask,
.irq_set_affinity = bcm7038_l1_set_affinity,
+ .irq_cpu_offline = bcm7038_l1_cpu_offline,
};
static int bcm7038_l1_map(struct irq_domain *d, unsigned int virq,
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index c40a9f3b0724..4ff2ee2609ca 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -804,6 +804,14 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
gic_write_irouter(val, reg);
/*
+ * It is possible that irq is disabled from SW perspective only,
+ * because kernel takes lazy disable approach. Therefore check irq
+ * descriptor if it should kept disabled.
+ */
+ if (irqd_irq_disabled(d))
+ enabled = 0;
+
+ /*
* If the interrupt was enabled, enabled it again. Otherwise,
* just wait for the distributor to have digested our changes.
*/
diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c
index 2a506fe0c8a4..74bf1a17ae7c 100644
--- a/drivers/isdn/gigaset/ser-gigaset.c
+++ b/drivers/isdn/gigaset/ser-gigaset.c
@@ -762,8 +762,10 @@ static int __init ser_gigaset_init(void)
driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
GIGASET_MODULENAME, GIGASET_DEVNAME,
&ops, THIS_MODULE);
- if (!driver)
+ if (!driver) {
+ rc = -ENOMEM;
goto error;
+ }
rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc);
if (rc != 0) {
diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c
index d7c286656a25..7b4ddf0a39ec 100644
--- a/drivers/isdn/hardware/eicon/message.c
+++ b/drivers/isdn/hardware/eicon/message.c
@@ -11304,7 +11304,8 @@ static void mixer_notify_update(PLCI *plci, byte others)
((CAPI_MSG *) msg)->header.ncci = 0;
((CAPI_MSG *) msg)->info.facility_req.Selector = SELECTOR_LINE_INTERCONNECT;
((CAPI_MSG *) msg)->info.facility_req.structs[0] = 3;
- PUT_WORD(&(((CAPI_MSG *) msg)->info.facility_req.structs[1]), LI_REQ_SILENT_UPDATE);
+ ((CAPI_MSG *) msg)->info.facility_req.structs[1] = LI_REQ_SILENT_UPDATE & 0xff;
+ ((CAPI_MSG *) msg)->info.facility_req.structs[2] = LI_REQ_SILENT_UPDATE >> 8;
((CAPI_MSG *) msg)->info.facility_req.structs[3] = 0;
w = api_put(notify_plci->appl, (CAPI_MSG *) msg);
if (w != _QUEUE_FULL)
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index 85a6be824485..817dfa3b2f53 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -897,9 +897,10 @@ static int qpnp_mpp_set(struct qpnp_led_data *led)
}
}
- if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
+ if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
pwm_enable(led->mpp_cfg->pwm_cfg->pwm_dev);
- else {
+ led->mpp_cfg->pwm_cfg->pwm_enabled = 1;
+ } else {
if (led->cdev.brightness < LED_MPP_CURRENT_MIN)
led->cdev.brightness = LED_MPP_CURRENT_MIN;
else {
@@ -950,6 +951,7 @@ static int qpnp_mpp_set(struct qpnp_led_data *led)
led->mpp_cfg->pwm_mode =
led->mpp_cfg->pwm_cfg->default_mode;
pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
+ led->mpp_cfg->pwm_cfg->pwm_enabled = 0;
}
rc = qpnp_led_masked_write(led,
LED_MPP_MODE_CTRL(led->base),
@@ -1606,7 +1608,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led)
dev_err(&led->pdev->dev, "pwm enable failed\n");
return rc;
}
-
+ led->kpdbl_cfg->pwm_cfg->pwm_enabled = 1;
set_bit(led->kpdbl_cfg->row_id, kpdbl_leds_in_use);
/* is_kpdbl_master_turn_on will be set to true when GPLED1
@@ -1642,6 +1644,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led)
"pwm enable failed\n");
return rc;
}
+ led->kpdbl_cfg->pwm_cfg->pwm_enabled = 1;
} else {
if (kpdbl_master) {
pwm_disable(kpdbl_master);
@@ -1660,6 +1663,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led)
is_kpdbl_master_turn_on = false;
} else {
pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
+ led->kpdbl_cfg->pwm_cfg->pwm_enabled = 0;
clear_bit(led->kpdbl_cfg->row_id, kpdbl_leds_in_use);
if (bitmap_weight(kpdbl_leds_in_use,
NUM_KPDBL_LEDS) == 1 && kpdbl_master &&
@@ -1727,20 +1731,17 @@ static int qpnp_rgb_set(struct qpnp_led_data *led)
"Failed to write led enable reg\n");
return rc;
}
-
- if (led->rgb_cfg->pwm_cfg->pwm_enabled) {
- pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
- led->rgb_cfg->pwm_cfg->pwm_enabled = 0;
- }
-
- rc = pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev);
- if (!rc)
+ if (!led->rgb_cfg->pwm_cfg->pwm_enabled) {
+ pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev);
led->rgb_cfg->pwm_cfg->pwm_enabled = 1;
+ }
} else {
led->rgb_cfg->pwm_cfg->mode =
led->rgb_cfg->pwm_cfg->default_mode;
- pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
- led->rgb_cfg->pwm_cfg->pwm_enabled = 0;
+ if (led->rgb_cfg->pwm_cfg->pwm_enabled) {
+ pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
+ led->rgb_cfg->pwm_cfg->pwm_enabled = 0;
+ }
rc = qpnp_led_masked_write(led,
RGB_LED_EN_CTL(led->base),
led->rgb_cfg->enable, RGB_LED_DISABLE);
@@ -2183,11 +2184,17 @@ static ssize_t pwm_us_store(struct device *dev,
previous_pwm_us = pwm_cfg->pwm_period_us;
pwm_cfg->pwm_period_us = pwm_us;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (ret) {
pwm_cfg->pwm_period_us = previous_pwm_us;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
qpnp_led_set(&led->cdev, led->cdev.brightness);
dev_err(&led->pdev->dev,
@@ -2237,12 +2244,18 @@ static ssize_t pause_lo_store(struct device *dev,
previous_pause_lo = pwm_cfg->lut_params.lut_pause_lo;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
pwm_cfg->lut_params.lut_pause_lo = pause_lo;
ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (ret) {
pwm_cfg->lut_params.lut_pause_lo = previous_pause_lo;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
qpnp_led_set(&led->cdev, led->cdev.brightness);
dev_err(&led->pdev->dev,
@@ -2292,12 +2305,18 @@ static ssize_t pause_hi_store(struct device *dev,
previous_pause_hi = pwm_cfg->lut_params.lut_pause_hi;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
pwm_cfg->lut_params.lut_pause_hi = pause_hi;
ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (ret) {
pwm_cfg->lut_params.lut_pause_hi = previous_pause_hi;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
qpnp_led_set(&led->cdev, led->cdev.brightness);
dev_err(&led->pdev->dev,
@@ -2348,12 +2367,18 @@ static ssize_t start_idx_store(struct device *dev,
previous_start_idx = pwm_cfg->duty_cycles->start_idx;
pwm_cfg->duty_cycles->start_idx = start_idx;
pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (ret) {
pwm_cfg->duty_cycles->start_idx = previous_start_idx;
pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
qpnp_led_set(&led->cdev, led->cdev.brightness);
dev_err(&led->pdev->dev,
@@ -2403,12 +2428,18 @@ static ssize_t ramp_step_ms_store(struct device *dev,
previous_ramp_step_ms = pwm_cfg->lut_params.ramp_step_ms;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
pwm_cfg->lut_params.ramp_step_ms = ramp_step_ms;
ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (ret) {
pwm_cfg->lut_params.ramp_step_ms = previous_ramp_step_ms;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
qpnp_led_set(&led->cdev, led->cdev.brightness);
dev_err(&led->pdev->dev,
@@ -2458,12 +2489,18 @@ static ssize_t lut_flags_store(struct device *dev,
previous_lut_flags = pwm_cfg->lut_params.flags;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
pwm_cfg->lut_params.flags = lut_flags;
ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (ret) {
pwm_cfg->lut_params.flags = previous_lut_flags;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
qpnp_led_set(&led->cdev, led->cdev.brightness);
dev_err(&led->pdev->dev,
@@ -2543,7 +2580,11 @@ static ssize_t duty_pcts_store(struct device *dev,
pwm_cfg->old_duty_pcts = previous_duty_pcts;
pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
+
ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (ret)
goto restore;
@@ -2558,7 +2599,10 @@ restore:
pwm_cfg->old_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
pwm_cfg->duty_cycles->duty_pcts = previous_duty_pcts;
pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
qpnp_led_set(&led->cdev, led->cdev.brightness);
return ret;
@@ -2588,7 +2632,10 @@ static void led_blink(struct qpnp_led_data *led,
led->kpdbl_cfg->pwm_mode =
pwm_cfg->default_mode;
}
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (led->id == QPNP_ID_RGB_RED || led->id == QPNP_ID_RGB_GREEN
|| led->id == QPNP_ID_RGB_BLUE) {
@@ -3541,8 +3588,11 @@ static int qpnp_get_config_kpdbl(struct qpnp_led_data *led,
}
rc = qpnp_get_config_pwm(led->kpdbl_cfg->pwm_cfg, led->pdev, node);
- if (rc < 0)
+ if (rc < 0) {
+ if (led->kpdbl_cfg->pwm_cfg->pwm_dev)
+ pwm_put(led->kpdbl_cfg->pwm_cfg->pwm_dev);
return rc;
+ }
rc = of_property_read_u32(node, "qcom,row-id", &val);
if (!rc)
@@ -3605,8 +3655,11 @@ static int qpnp_get_config_rgb(struct qpnp_led_data *led,
}
rc = qpnp_get_config_pwm(led->rgb_cfg->pwm_cfg, led->pdev, node);
- if (rc < 0)
+ if (rc < 0) {
+ if (led->rgb_cfg->pwm_cfg->pwm_dev)
+ pwm_put(led->rgb_cfg->pwm_cfg->pwm_dev);
return rc;
+ }
return 0;
}
@@ -3729,8 +3782,11 @@ static int qpnp_get_config_mpp(struct qpnp_led_data *led,
}
rc = qpnp_get_config_pwm(led->mpp_cfg->pwm_cfg, led->pdev, node);
- if (rc < 0)
+ if (rc < 0) {
+ if (led->mpp_cfg->pwm_cfg && led->mpp_cfg->pwm_cfg->pwm_dev)
+ pwm_put(led->mpp_cfg->pwm_cfg->pwm_dev);
goto err_config_mpp;
+ }
return 0;
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 7d5aa2c5c81d..36ca4e4cbfb7 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -475,6 +475,21 @@ config DM_VERITY
If unsure, say N.
+config DM_VERITY_HASH_PREFETCH_MIN_SIZE_128
+ bool "Prefetch size 128"
+
+config DM_VERITY_HASH_PREFETCH_MIN_SIZE
+ int "Verity hash prefetch minimum size"
+ depends on DM_VERITY
+ range 1 4096
+ default 128 if DM_VERITY_HASH_PREFETCH_MIN_SIZE_128
+ default 1
+ ---help---
+ This sets minimum number of hash blocks to prefetch for dm-verity.
+ For devices like eMMC, having larger prefetch size like 128 can improve
+ performance with increased memory consumption for keeping more hashes
+ in RAM.
+
config DM_VERITY_FEC
bool "Verity forward error correction support"
depends on DM_VERITY
@@ -527,6 +542,7 @@ config DM_ANDROID_VERITY
depends on ASYMMETRIC_KEY_TYPE
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
depends on MD_LINEAR
+ select DM_VERITY_HASH_PREFETCH_MIN_SIZE_128
---help---
This device-mapper target is virtually a VERITY target. This
target is setup by reading the metadata contents piggybacked
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
index 6b420a55c745..c3ea03c9a1a8 100644
--- a/drivers/md/bcache/bcache.h
+++ b/drivers/md/bcache/bcache.h
@@ -425,7 +425,7 @@ struct cache {
* until a gc finishes - otherwise we could pointlessly burn a ton of
* cpu
*/
- unsigned invalidate_needs_gc:1;
+ unsigned invalidate_needs_gc;
bool discard; /* Get rid of? */
@@ -593,8 +593,8 @@ struct cache_set {
/* Counts how many sectors bio_insert has added to the cache */
atomic_t sectors_to_gc;
+ wait_queue_head_t gc_wait;
- wait_queue_head_t moving_gc_wait;
struct keybuf moving_gc_keys;
/* Number of moving GC bios in flight */
struct semaphore moving_in_flight;
diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
index 22b9e34ceb75..5b815e64c1c9 100644
--- a/drivers/md/bcache/btree.c
+++ b/drivers/md/bcache/btree.c
@@ -1762,33 +1762,34 @@ static void bch_btree_gc(struct cache_set *c)
bch_moving_gc(c);
}
-static int bch_gc_thread(void *arg)
+static bool gc_should_run(struct cache_set *c)
{
- struct cache_set *c = arg;
struct cache *ca;
unsigned i;
- while (1) {
-again:
- bch_btree_gc(c);
+ for_each_cache(ca, c, i)
+ if (ca->invalidate_needs_gc)
+ return true;
- set_current_state(TASK_INTERRUPTIBLE);
- if (kthread_should_stop())
- break;
+ if (atomic_read(&c->sectors_to_gc) < 0)
+ return true;
- mutex_lock(&c->bucket_lock);
+ return false;
+}
- for_each_cache(ca, c, i)
- if (ca->invalidate_needs_gc) {
- mutex_unlock(&c->bucket_lock);
- set_current_state(TASK_RUNNING);
- goto again;
- }
+static int bch_gc_thread(void *arg)
+{
+ struct cache_set *c = arg;
- mutex_unlock(&c->bucket_lock);
+ while (1) {
+ wait_event_interruptible(c->gc_wait,
+ kthread_should_stop() || gc_should_run(c));
- try_to_freeze();
- schedule();
+ if (kthread_should_stop())
+ break;
+
+ set_gc_sectors(c);
+ bch_btree_gc(c);
}
return 0;
@@ -1796,11 +1797,10 @@ again:
int bch_gc_thread_start(struct cache_set *c)
{
- c->gc_thread = kthread_create(bch_gc_thread, c, "bcache_gc");
+ c->gc_thread = kthread_run(bch_gc_thread, c, "bcache_gc");
if (IS_ERR(c->gc_thread))
return PTR_ERR(c->gc_thread);
- set_task_state(c->gc_thread, TASK_INTERRUPTIBLE);
return 0;
}
diff --git a/drivers/md/bcache/btree.h b/drivers/md/bcache/btree.h
index 5c391fa01bed..9b80417cd547 100644
--- a/drivers/md/bcache/btree.h
+++ b/drivers/md/bcache/btree.h
@@ -260,8 +260,7 @@ void bch_initial_mark_key(struct cache_set *, int, struct bkey *);
static inline void wake_up_gc(struct cache_set *c)
{
- if (c->gc_thread)
- wake_up_process(c->gc_thread);
+ wake_up(&c->gc_wait);
}
#define MAP_DONE 0
diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
index 25fa8445bb24..2410df1c2a05 100644
--- a/drivers/md/bcache/request.c
+++ b/drivers/md/bcache/request.c
@@ -196,10 +196,8 @@ static void bch_data_insert_start(struct closure *cl)
struct data_insert_op *op = container_of(cl, struct data_insert_op, cl);
struct bio *bio = op->bio, *n;
- if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0) {
- set_gc_sectors(op->c);
+ if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0)
wake_up_gc(op->c);
- }
if (op->bypass)
return bch_data_invalidate(cl);
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index 3d5c0ba13181..7b5880b8874c 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -1489,6 +1489,7 @@ struct cache_set *bch_cache_set_alloc(struct cache_sb *sb)
mutex_init(&c->bucket_lock);
init_waitqueue_head(&c->btree_cache_wait);
init_waitqueue_head(&c->bucket_wait);
+ init_waitqueue_head(&c->gc_wait);
sema_init(&c->uuid_write_mutex, 1);
spin_lock_init(&c->btree_gc_time.lock);
@@ -1547,6 +1548,7 @@ static void run_cache_set(struct cache_set *c)
for_each_cache(ca, c, i)
c->nbuckets += ca->sb.nbuckets;
+ set_gc_sectors(c);
if (CACHE_SYNC(&c->sb)) {
LIST_HEAD(journal);
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index bb9b92ebbf8e..0da5efaad85c 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -248,7 +248,7 @@ struct cache {
/*
* Fields for converting from sectors to blocks.
*/
- uint32_t sectors_per_block;
+ sector_t sectors_per_block;
int sectors_per_block_shift;
spinlock_t lock;
@@ -3544,11 +3544,11 @@ static void cache_status(struct dm_target *ti, status_type_t type,
residency = policy_residency(cache->policy);
- DMEMIT("%u %llu/%llu %u %llu/%llu %u %u %u %u %u %u %lu ",
+ DMEMIT("%u %llu/%llu %llu %llu/%llu %u %u %u %u %u %u %lu ",
(unsigned)DM_CACHE_METADATA_BLOCK_SIZE,
(unsigned long long)(nr_blocks_metadata - nr_free_blocks_metadata),
(unsigned long long)nr_blocks_metadata,
- cache->sectors_per_block,
+ (unsigned long long)cache->sectors_per_block,
(unsigned long long) from_cblock(residency),
(unsigned long long) from_cblock(cache->cache_size),
(unsigned) atomic_read(&cache->stats.read_hit),
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index e540b7942eba..799b9a5ad4f5 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1500,12 +1500,15 @@ static int crypt_set_key(struct crypt_config *cc, char *key)
if (!cc->key_size && strcmp(key, "-"))
goto out;
+ /* clear the flag since following operations may invalidate previously valid key */
+ clear_bit(DM_CRYPT_KEY_VALID, &cc->flags);
+
if (cc->key_size && crypt_decode_key(cc->key, key, cc->key_size) < 0)
goto out;
- set_bit(DM_CRYPT_KEY_VALID, &cc->flags);
-
r = crypt_setkey_allcpus(cc);
+ if (!r)
+ set_bit(DM_CRYPT_KEY_VALID, &cc->flags);
out:
/* Hex key string not needed after here, so wipe it. */
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c
index 8e9e928dafba..78f403b45ab3 100644
--- a/drivers/md/dm-flakey.c
+++ b/drivers/md/dm-flakey.c
@@ -200,11 +200,13 @@ static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
if (!(fc->up_interval + fc->down_interval)) {
ti->error = "Total (up + down) interval is zero";
+ r = -EINVAL;
goto bad;
}
if (fc->up_interval + fc->down_interval < fc->up_interval) {
ti->error = "Interval overflow";
+ r = -EINVAL;
goto bad;
}
diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c
index 8289804ccd99..d5ea9f28ae70 100644
--- a/drivers/md/dm-stats.c
+++ b/drivers/md/dm-stats.c
@@ -175,6 +175,7 @@ static void dm_stat_free(struct rcu_head *head)
int cpu;
struct dm_stat *s = container_of(head, struct dm_stat, rcu_head);
+ kfree(s->histogram_boundaries);
kfree(s->program_id);
kfree(s->aux_data);
for_each_possible_cpu(cpu) {
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index 9d3d4b297201..c7e97cf6e7fb 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -501,6 +501,7 @@ static void verity_prefetch_io(struct work_struct *work)
container_of(work, struct dm_verity_prefetch_work, work);
struct dm_verity *v = pw->v;
int i;
+ sector_t prefetch_size;
for (i = v->levels - 2; i >= 0; i--) {
sector_t hash_block_start;
@@ -523,8 +524,14 @@ static void verity_prefetch_io(struct work_struct *work)
hash_block_end = v->hash_blocks - 1;
}
no_prefetch_cluster:
+ // for emmc, it is more efficient to send bigger read
+ prefetch_size = max((sector_t)CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE,
+ hash_block_end - hash_block_start + 1);
+ if ((hash_block_start + prefetch_size) >= (v->hash_start + v->hash_blocks)) {
+ prefetch_size = hash_block_end - hash_block_start + 1;
+ }
dm_bufio_prefetch(v->bufio, hash_block_start,
- hash_block_end - hash_block_start + 1);
+ prefetch_size);
}
kfree(pw);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index dd1cde5458b8..e9b34de2319e 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1467,11 +1467,62 @@ void dm_accept_partial_bio(struct bio *bio, unsigned n_sectors)
}
EXPORT_SYMBOL_GPL(dm_accept_partial_bio);
+/*
+ * Flush current->bio_list when the target map method blocks.
+ * This fixes deadlocks in snapshot and possibly in other targets.
+ */
+struct dm_offload {
+ struct blk_plug plug;
+ struct blk_plug_cb cb;
+};
+
+static void flush_current_bio_list(struct blk_plug_cb *cb, bool from_schedule)
+{
+ struct dm_offload *o = container_of(cb, struct dm_offload, cb);
+ struct bio_list list;
+ struct bio *bio;
+
+ INIT_LIST_HEAD(&o->cb.list);
+
+ if (unlikely(!current->bio_list))
+ return;
+
+ list = *current->bio_list;
+ bio_list_init(current->bio_list);
+
+ while ((bio = bio_list_pop(&list))) {
+ struct bio_set *bs = bio->bi_pool;
+ if (unlikely(!bs) || bs == fs_bio_set) {
+ bio_list_add(current->bio_list, bio);
+ continue;
+ }
+
+ spin_lock(&bs->rescue_lock);
+ bio_list_add(&bs->rescue_list, bio);
+ queue_work(bs->rescue_workqueue, &bs->rescue_work);
+ spin_unlock(&bs->rescue_lock);
+ }
+}
+
+static void dm_offload_start(struct dm_offload *o)
+{
+ blk_start_plug(&o->plug);
+ o->cb.callback = flush_current_bio_list;
+ list_add(&o->cb.list, &current->plug->cb_list);
+}
+
+static void dm_offload_end(struct dm_offload *o)
+{
+ list_del(&o->cb.list);
+ blk_finish_plug(&o->plug);
+}
+
static void __map_bio(struct dm_target_io *tio)
{
int r;
sector_t sector;
struct mapped_device *md;
+ struct dm_offload o;
struct bio *clone = &tio->clone;
struct dm_target *ti = tio->ti;
@@ -1484,7 +1535,11 @@ static void __map_bio(struct dm_target_io *tio)
*/
atomic_inc(&tio->io->io_count);
sector = clone->bi_iter.bi_sector;
+
+ dm_offload_start(&o);
r = ti->type->map(ti, clone);
+ dm_offload_end(&o);
+
if (r == DM_MAPIO_REMAPPED) {
/* the bio has been remapped so dispatch it */
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index b7fe7e9fc777..6ba3227e29b2 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -52,18 +52,26 @@ static inline struct dev_info *which_dev(struct mddev *mddev, sector_t sector)
return conf->disks + lo;
}
+/*
+ * In linear_congested() conf->raid_disks is used as a copy of
+ * mddev->raid_disks to iterate conf->disks[], because conf->raid_disks
+ * and conf->disks[] are created in linear_conf(), they are always
+ * consitent with each other, but mddev->raid_disks does not.
+ */
static int linear_congested(struct mddev *mddev, int bits)
{
struct linear_conf *conf;
int i, ret = 0;
- conf = mddev->private;
+ rcu_read_lock();
+ conf = rcu_dereference(mddev->private);
- for (i = 0; i < mddev->raid_disks && !ret ; i++) {
+ for (i = 0; i < conf->raid_disks && !ret ; i++) {
struct request_queue *q = bdev_get_queue(conf->disks[i].rdev->bdev);
ret |= bdi_congested(&q->backing_dev_info, bits);
}
+ rcu_read_unlock();
return ret;
}
@@ -143,6 +151,19 @@ static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks)
conf->disks[i-1].end_sector +
conf->disks[i].rdev->sectors;
+ /*
+ * conf->raid_disks is copy of mddev->raid_disks. The reason to
+ * keep a copy of mddev->raid_disks in struct linear_conf is,
+ * mddev->raid_disks may not be consistent with pointers number of
+ * conf->disks[] when it is updated in linear_add() and used to
+ * iterate old conf->disks[] earray in linear_congested().
+ * Here conf->raid_disks is always consitent with number of
+ * pointers in conf->disks[] array, and mddev->private is updated
+ * with rcu_assign_pointer() in linear_addr(), such race can be
+ * avoided.
+ */
+ conf->raid_disks = raid_disks;
+
return conf;
out:
@@ -195,15 +216,23 @@ static int linear_add(struct mddev *mddev, struct md_rdev *rdev)
if (!newconf)
return -ENOMEM;
+ /* newconf->raid_disks already keeps a copy of * the increased
+ * value of mddev->raid_disks, WARN_ONCE() is just used to make
+ * sure of this. It is possible that oldconf is still referenced
+ * in linear_congested(), therefore kfree_rcu() is used to free
+ * oldconf until no one uses it anymore.
+ */
mddev_suspend(mddev);
- oldconf = mddev->private;
+ oldconf = rcu_dereference(mddev->private);
mddev->raid_disks++;
- mddev->private = newconf;
+ WARN_ONCE(mddev->raid_disks != newconf->raid_disks,
+ "copied raid_disks doesn't match mddev->raid_disks");
+ rcu_assign_pointer(mddev->private, newconf);
md_set_array_sectors(mddev, linear_size(mddev, 0, 0));
set_capacity(mddev->gendisk, mddev->array_sectors);
mddev_resume(mddev);
revalidate_disk(mddev->gendisk);
- kfree(oldconf);
+ kfree_rcu(oldconf, rcu);
return 0;
}
diff --git a/drivers/md/linear.h b/drivers/md/linear.h
index b685ddd7d7f7..8d392e6098b3 100644
--- a/drivers/md/linear.h
+++ b/drivers/md/linear.h
@@ -10,6 +10,7 @@ struct linear_conf
{
struct rcu_head rcu;
sector_t array_sectors;
+ int raid_disks; /* a copy of mddev->raid_disks */
struct dev_info disks[0];
};
#endif
diff --git a/drivers/md/md.c b/drivers/md/md.c
index c1c7d4fb4b77..eff554a12fb4 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -6771,7 +6771,7 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
/* need to ensure recovery thread has run */
wait_event_interruptible_timeout(mddev->sb_wait,
!test_bit(MD_RECOVERY_NEEDED,
- &mddev->flags),
+ &mddev->recovery),
msecs_to_jiffies(5000));
if (cmd == STOP_ARRAY || cmd == STOP_ARRAY_RO) {
/* Need to flush page cache, and ensure no-one else opens
diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c
index 7e44005595c1..20557e2c60c6 100644
--- a/drivers/md/persistent-data/dm-space-map-metadata.c
+++ b/drivers/md/persistent-data/dm-space-map-metadata.c
@@ -775,17 +775,15 @@ int dm_sm_metadata_create(struct dm_space_map *sm,
memcpy(&smm->sm, &bootstrap_ops, sizeof(smm->sm));
r = sm_ll_new_metadata(&smm->ll, tm);
+ if (!r) {
+ if (nr_blocks > DM_SM_METADATA_MAX_BLOCKS)
+ nr_blocks = DM_SM_METADATA_MAX_BLOCKS;
+ r = sm_ll_extend(&smm->ll, nr_blocks);
+ }
+ memcpy(&smm->sm, &ops, sizeof(smm->sm));
if (r)
return r;
- if (nr_blocks > DM_SM_METADATA_MAX_BLOCKS)
- nr_blocks = DM_SM_METADATA_MAX_BLOCKS;
- r = sm_ll_extend(&smm->ll, nr_blocks);
- if (r)
- return r;
-
- memcpy(&smm->sm, &ops, sizeof(smm->sm));
-
/*
* Now we need to update the newly created data structures with the
* allocated blocks that they were built from.
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 10ce885445f6..7af976934441 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6980,6 +6980,15 @@ static int run(struct mddev *mddev)
stripe = (stripe | (stripe-1)) + 1;
mddev->queue->limits.discard_alignment = stripe;
mddev->queue->limits.discard_granularity = stripe;
+
+ /*
+ * We use 16-bit counter of active stripes in bi_phys_segments
+ * (minus one for over-loaded initialization)
+ */
+ blk_queue_max_hw_sectors(mddev->queue, 0xfffe * STRIPE_SECTORS);
+ blk_queue_max_discard_sectors(mddev->queue,
+ 0xfffe * STRIPE_SECTORS);
+
/*
* unaligned part of discard request will be ignored, so can't
* guarantee discard_zeroes_data
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 1700447afe43..ba3287d176af 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -619,6 +619,7 @@ config VIDEO_S5K6A3
config VIDEO_S5K4ECGX
tristate "Samsung S5K4ECGX sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ select CRC32
---help---
This is a V4L2 sensor-level driver for Samsung S5K4ECGX 5M
camera sensor with an embedded SoC image signal processor.
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
index c4307ad8594c..e543cbbf2ec4 100644
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -2168,11 +2168,12 @@ static int dvb_register(struct cx23885_tsport *port)
}
port->i2c_client_tuner = client_tuner;
break;
- case CX23885_BOARD_HAUPPAUGE_HVR5525:
- switch (port->nr) {
+ case CX23885_BOARD_HAUPPAUGE_HVR5525: {
struct m88rs6000t_config m88rs6000t_config;
struct a8293_platform_data a8293_pdata = {};
+ switch (port->nr) {
+
/* port b - satellite */
case 1:
/* attach frontend */
@@ -2267,6 +2268,7 @@ static int dvb_register(struct cx23885_tsport *port)
break;
}
break;
+ }
default:
printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
" isn't supported yet\n",
diff --git a/drivers/media/pci/dm1105/Kconfig b/drivers/media/pci/dm1105/Kconfig
index 173daf0c0847..14fa7e40f2a6 100644
--- a/drivers/media/pci/dm1105/Kconfig
+++ b/drivers/media/pci/dm1105/Kconfig
@@ -1,6 +1,6 @@
config DVB_DM1105
tristate "SDMC DM1105 based PCI cards"
- depends on DVB_CORE && PCI && I2C
+ depends on DVB_CORE && PCI && I2C && I2C_ALGOBIT
select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h
index 4ab6586c0467..f53e59e9c0ea 100644
--- a/drivers/media/pci/solo6x10/solo6x10.h
+++ b/drivers/media/pci/solo6x10/solo6x10.h
@@ -286,7 +286,10 @@ static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg)
static inline void solo_reg_write(struct solo_dev *solo_dev, int reg,
u32 data)
{
+ u16 val;
+
writel(data, solo_dev->reg_base + reg);
+ pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val);
}
static inline void solo_irq_on(struct solo_dev *dev, u32 mask)
diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c
index ba780c45f645..572bc043b62d 100644
--- a/drivers/media/platform/am437x/am437x-vpfe.c
+++ b/drivers/media/platform/am437x/am437x-vpfe.c
@@ -1576,7 +1576,7 @@ static int vpfe_s_fmt(struct file *file, void *priv,
return -EBUSY;
}
- ret = vpfe_try_fmt(file, priv, &format);
+ ret = __vpfe_get_format(vpfe, &format, &bpp);
if (ret)
return ret;
diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c
index cff63e511e6d..b8f3d9fa66e9 100644
--- a/drivers/media/platform/blackfin/ppi.c
+++ b/drivers/media/platform/blackfin/ppi.c
@@ -214,6 +214,8 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params)
if (params->dlen > 24 || params->dlen <= 0)
return -EINVAL;
pctrl = devm_pinctrl_get(ppi->dev);
+ if (IS_ERR(pctrl))
+ return PTR_ERR(pctrl);
pstate = pinctrl_lookup_state(pctrl,
pin_state[(params->dlen + 7) / 8 - 1]);
if (pinctrl_select_state(pctrl, pstate))
diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c
index 3985df780216..df0664b496ba 100644
--- a/drivers/media/platform/msm/camera_v2/camera/camera.c
+++ b/drivers/media/platform/msm/camera_v2/camera/camera.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -486,6 +486,9 @@ static long camera_v4l2_vidioc_private_ioctl(struct file *filep, void *fh,
if (WARN_ON(!k_ioctl || !pvdev))
return -EIO;
+ if (cmd != VIDIOC_MSM_CAMERA_PRIVATE_IOCTL_CMD)
+ return -EINVAL;
+
switch (k_ioctl->id) {
case MSM_CAMERA_PRIV_IOCTL_ID_RETURN_BUF: {
struct msm_camera_return_buf ptr, *tmp = NULL;
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
index a0f1d5148c94..9cdcabb762c0 100644
--- a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
+++ b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
@@ -342,8 +342,14 @@ static void cam_smmu_check_vaddr_in_range(int idx, void *vaddr)
mapping->ion_fd);
}
}
- pr_err("Cannot find vaddr:%pK in SMMU. %s uses invalid virtual address\n",
- vaddr, iommu_cb_set.cb_info[idx].name);
+ if (!strcmp(iommu_cb_set.cb_info[idx].name, "vfe"))
+ pr_err_ratelimited("Cannot find vaddr:%pK in SMMU.\n"
+ " %s uses invalid virtual address\n",
+ vaddr, iommu_cb_set.cb_info[idx].name);
+ else
+ pr_err("Cannot find vaddr:%pK in SMMU.\n"
+ " %s uses invalid virtual address\n",
+ vaddr, iommu_cb_set.cb_info[idx].name);
return;
}
@@ -894,8 +900,13 @@ static int cam_smmu_attach_sec_cpp(int idx)
rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_SECURE,
MSM_CAMERA_TZ_HW_BLOCK_CPP);
if (rc != 0) {
- pr_err("fail to set secure mode for cpp, rc %d", rc);
- return rc;
+ pr_err("secure mode TA notification for cpp unsuccessful, rc %d\n",
+ rc);
+ /*
+ * Although the TA notification failed, the flow should proceed
+ * without returning an error as at this point cpp had already
+ * entered the secure mode.
+ */
}
iommu_cb_set.cb_info[idx].state = CAM_SMMU_ATTACH;
@@ -910,8 +921,13 @@ static int cam_smmu_detach_sec_cpp(int idx)
rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_NON_SECURE,
MSM_CAMERA_TZ_HW_BLOCK_CPP);
if (rc != 0) {
- pr_err("fail to switch to non secure mode for cpp, rc %d", rc);
- return rc;
+ pr_err("secure mode TA notification for cpp unsuccessful, rc %d\n",
+ rc);
+ /*
+ * Although the TA notification failed, the flow should proceed
+ * without returning an error, as at this point cpp is in secure
+ * mode and should be switched to non-secure regardless
+ */
}
iommu_cb_set.cb_info[idx].state = CAM_SMMU_DETACH;
@@ -950,8 +966,13 @@ static int cam_smmu_attach_sec_vfe_ns_stats(int idx)
rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_SECURE,
MSM_CAMERA_TZ_HW_BLOCK_ISP);
if (rc != 0) {
- pr_err("fail to set secure mode for vfe, rc %d", rc);
- return rc;
+ pr_err("secure mode TA notification for vfe unsuccessful, rc %d\n",
+ rc);
+ /*
+ * Although the TA notification failed, the flow should proceed
+ * without returning an error as at this point vfe had already
+ * entered the secure mode
+ */
}
return 0;
@@ -964,8 +985,13 @@ static int cam_smmu_detach_sec_vfe_ns_stats(int idx)
rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_NON_SECURE,
MSM_CAMERA_TZ_HW_BLOCK_ISP);
if (rc != 0) {
- pr_err("fail to switch to non secure mode for vfe, rc %d", rc);
- return rc;
+ pr_err("secure mode TA notification for vfe unsuccessful, rc %d\n",
+ rc);
+ /*
+ * Although the TA notification failed, the flow should proceed
+ * without returning an error, as at this point vfe is in secure
+ * mode and should be switched to non-secure regardless
+ */
}
/*
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
index 34fffa8dd7ce..a0c606ce9a29 100644
--- a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
+++ b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -376,18 +376,17 @@ int msm_camera_clk_enable(struct device *dev,
if (clk_rate == 0) {
clk_rate =
clk_round_rate(clk_ptr[i], 0);
- if (clk_rate < 0) {
+ if (clk_rate <= 0) {
pr_err("%s round rate failed\n",
clk_info[i].clk_name);
goto cam_clk_set_err;
}
- rc = clk_set_rate(clk_ptr[i],
- clk_rate);
- if (rc < 0) {
- pr_err("%s set rate failed\n",
- clk_info[i].clk_name);
- goto cam_clk_set_err;
- }
+ }
+ rc = clk_set_rate(clk_ptr[i], clk_rate);
+ if (rc < 0) {
+ pr_err("%s set rate failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
}
}
rc = clk_prepare_enable(clk_ptr[i]);
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
index 8d66232dbda1..5dbed80f5b85 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
@@ -229,8 +229,8 @@ static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr,
mapped_info->paddr += accu_length;
accu_length += qbuf_buf->planes[i].length;
- CDBG("%s: plane: %d addr:%lu\n",
- __func__, i, (unsigned long)mapped_info->paddr);
+ CDBG("%s: plane: %d addr:%pK\n",
+ __func__, i, (void *)mapped_info->paddr);
}
buf_info->num_planes = qbuf_buf->num_planes;
@@ -312,8 +312,8 @@ static int msm_isp_map_buf(struct msm_isp_buf_mgr *buf_mgr,
pr_err_ratelimited("%s: cannot map address", __func__);
goto smmu_map_error;
}
- CDBG("%s: addr:%lu\n",
- __func__, (unsigned long)mapped_info->paddr);
+ CDBG("%s: addr:%pK\n",
+ __func__, (void *)mapped_info->paddr);
return rc;
smmu_map_error:
@@ -1282,6 +1282,7 @@ static int msm_isp_deinit_isp_buf_mgr(
int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr,
unsigned int cmd, void *arg)
{
+ int rc = -EINVAL;
switch (cmd) {
case VIDIOC_MSM_ISP_REQUEST_BUF: {
struct msm_isp_buf_request *buf_req = arg;
@@ -1290,7 +1291,7 @@ int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr,
memcpy(&buf_req_ver2, buf_req,
sizeof(struct msm_isp_buf_request));
buf_req_ver2.security_mode = NON_SECURE_MODE;
- buf_mgr->ops->request_buf(buf_mgr, &buf_req_ver2);
+ rc = buf_mgr->ops->request_buf(buf_mgr, &buf_req_ver2);
memcpy(buf_req, &buf_req_ver2,
sizeof(struct msm_isp_buf_request));
break;
@@ -1298,35 +1299,35 @@ int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr,
case VIDIOC_MSM_ISP_REQUEST_BUF_VER2: {
struct msm_isp_buf_request_ver2 *buf_req_ver2 = arg;
- buf_mgr->ops->request_buf(buf_mgr, buf_req_ver2);
+ rc = buf_mgr->ops->request_buf(buf_mgr, buf_req_ver2);
break;
}
case VIDIOC_MSM_ISP_ENQUEUE_BUF: {
struct msm_isp_qbuf_info *qbuf_info = arg;
- buf_mgr->ops->enqueue_buf(buf_mgr, qbuf_info);
+ rc = buf_mgr->ops->enqueue_buf(buf_mgr, qbuf_info);
break;
}
case VIDIOC_MSM_ISP_DEQUEUE_BUF: {
struct msm_isp_qbuf_info *qbuf_info = arg;
- buf_mgr->ops->dequeue_buf(buf_mgr, qbuf_info);
+ rc = buf_mgr->ops->dequeue_buf(buf_mgr, qbuf_info);
break;
}
case VIDIOC_MSM_ISP_RELEASE_BUF: {
struct msm_isp_buf_request *buf_req = arg;
- buf_mgr->ops->release_buf(buf_mgr, buf_req->handle);
+ rc = buf_mgr->ops->release_buf(buf_mgr, buf_req->handle);
break;
}
case VIDIOC_MSM_ISP_UNMAP_BUF: {
struct msm_isp_unmap_buf_req *unmap_req = arg;
- buf_mgr->ops->unmap_buf(buf_mgr, unmap_req->fd);
+ rc = buf_mgr->ops->unmap_buf(buf_mgr, unmap_req->fd);
break;
}
}
- return 0;
+ return rc;
}
static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr,
@@ -1335,14 +1336,15 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr,
struct msm_isp_buffer *bufs = NULL;
uint32_t i = 0, j = 0, k = 0, rc = 0;
char *print_buf = NULL, temp_buf[100];
- uint32_t start_addr = 0, end_addr = 0, print_buf_size = 2000;
+ uint32_t print_buf_size = 2000;
+ unsigned long start_addr = 0, end_addr = 0;
int buf_addr_delta = -1;
int temp_delta = 0;
uint32_t debug_stream_id = 0;
uint32_t debug_buf_idx = 0;
uint32_t debug_buf_plane = 0;
- uint32_t debug_start_addr = 0;
- uint32_t debug_end_addr = 0;
+ unsigned long debug_start_addr = 0;
+ unsigned long debug_end_addr = 0;
uint32_t debug_frame_id = 0;
enum msm_isp_buffer_state debug_state = MSM_ISP_BUFFER_STATE_UNUSED;
unsigned long flags;
@@ -1401,8 +1403,8 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr,
debug_stream_id, debug_frame_id);
pr_err("%s: nearby buf index %d, plane %d, state %d\n", __func__,
debug_buf_idx, debug_buf_plane, debug_state);
- pr_err("%s: buf address 0x%x -- 0x%x\n", __func__,
- debug_start_addr, debug_end_addr);
+ pr_err("%s: buf address %pK -- %pK\n", __func__,
+ (void *)debug_start_addr, (void *)debug_end_addr);
if (BUF_DEBUG_FULL) {
print_buf = kzalloc(print_buf_size, GFP_ATOMIC);
@@ -1437,9 +1439,10 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr,
mapped_info[k].len;
snprintf(temp_buf,
sizeof(temp_buf),
- " buf %d plane %d start_addr %x end_addr %x\n",
- j, k, start_addr,
- end_addr);
+ " buf %d plane %d start_addr %pK end_addr %pK\n",
+ j, k,
+ (void *)start_addr,
+ (void *)end_addr);
strlcat(print_buf, temp_buf,
print_buf_size);
}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
index bb3f0dca9d92..f2f3388b41c1 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
@@ -590,9 +590,9 @@ int vfe_hw_probe(struct platform_device *pdev)
(struct msm_vfe_hardware_info *) match_dev->data;
/* Cx ipeak support */
if (of_find_property(pdev->dev.of_node,
- "qcom,vfe_cx_ipeak", NULL)) {
+ "qcom,vfe-cx-ipeak", NULL)) {
vfe_dev->vfe_cx_ipeak = cx_ipeak_register(
- pdev->dev.of_node, "qcom,vfe_cx_ipeak");
+ pdev->dev.of_node, "qcom,vfe-cx-ipeak");
}
} else {
vfe_dev->hw_info = (struct msm_vfe_hardware_info *)
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
index e54342e3935a..45c2dd588bc9 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
@@ -162,6 +162,8 @@ struct msm_vfe_irq_ops {
void (*config_irq)(struct vfe_device *vfe_dev,
uint32_t irq_status0, uint32_t irq_status1,
enum msm_isp_irq_operation);
+ void (*preprocess_camif_irq)(struct vfe_device *vfe_dev,
+ uint32_t irq_status0);
};
struct msm_vfe_axi_ops {
@@ -282,7 +284,7 @@ struct msm_vfe_stats_ops {
void (*update_ping_pong_addr)(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info,
- uint32_t pingpong_status, dma_addr_t paddr);
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_size);
uint32_t (*get_frame_id)(struct vfe_device *vfe_dev);
uint32_t (*get_wm_mask)(uint32_t irq_status0, uint32_t irq_status1);
@@ -494,6 +496,7 @@ struct msm_vfe_src_info {
struct timeval time_stamp;
enum msm_vfe_dual_hw_type dual_hw_type;
struct msm_vfe_dual_hw_ms_info dual_hw_ms_info;
+ bool accept_frame;
};
struct msm_vfe_fetch_engine_info {
@@ -767,8 +770,8 @@ struct vfe_device {
struct msm_cam_clk_info *hvx_clk_info;
size_t num_hvx_clk;
size_t num_norm_clk;
+ bool hvx_clk_state;
enum cam_ahb_clk_vote ahb_vote;
- bool turbo_vote;
struct cx_ipeak_client *vfe_cx_ipeak;
/* Sync variables*/
@@ -809,6 +812,7 @@ struct vfe_device {
uint32_t is_split;
uint32_t dual_vfe_enable;
unsigned long page_fault_addr;
+ uint32_t vfe_hw_limit;
/* Debug variables */
int dump_reg;
@@ -830,6 +834,8 @@ struct vfe_device {
uint32_t recovery_irq1_mask;
/* Store the buf_idx for pd stats RDI stream */
uint8_t pd_buf_idx;
+ /* total bandwidth per vfe */
+ uint64_t total_bandwidth;
};
struct vfe_parent_device {
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
index bf18fc59585c..caf69af25601 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1305,7 +1305,7 @@ static void msm_vfe32_stats_enable_module(struct vfe_device *vfe_dev,
static void msm_vfe32_stats_update_ping_pong_addr(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info, uint32_t pingpong_status,
- dma_addr_t paddr)
+ dma_addr_t paddr, uint32_t buf_sz)
{
void __iomem *vfe_base = vfe_dev->vfe_base;
int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
index 8e549c338bdd..f5533fd9062e 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -2114,7 +2114,7 @@ static void msm_vfe40_stats_enable_module(struct vfe_device *vfe_dev,
static void msm_vfe40_stats_update_ping_pong_addr(
struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
- uint32_t pingpong_status, dma_addr_t paddr)
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_sz)
{
void __iomem *vfe_base = vfe_dev->vfe_base;
int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
@@ -2218,6 +2218,7 @@ struct msm_vfe_hardware_info vfe40_hw_info = {
.process_stats_irq = msm_isp_process_stats_irq,
.process_epoch_irq = msm_vfe40_process_epoch_irq,
.config_irq = msm_vfe40_config_irq,
+ .preprocess_camif_irq = msm_isp47_preprocess_camif_irq,
},
.axi_ops = {
.reload_wm = msm_vfe40_axi_reload_wm,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
index 957cbc292be3..c85bf1655b8c 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1720,7 +1720,7 @@ static void msm_vfe44_stats_update_cgc_override(struct vfe_device *vfe_dev,
static void msm_vfe44_stats_update_ping_pong_addr(
struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
- uint32_t pingpong_status, dma_addr_t paddr)
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_sz)
{
void __iomem *vfe_base = vfe_dev->vfe_base;
int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
@@ -1825,6 +1825,7 @@ struct msm_vfe_hardware_info vfe44_hw_info = {
.process_stats_irq = msm_isp_process_stats_irq,
.process_epoch_irq = msm_vfe44_process_epoch_irq,
.config_irq = msm_vfe44_config_irq,
+ .preprocess_camif_irq = msm_isp47_preprocess_camif_irq,
},
.axi_ops = {
.reload_wm = msm_vfe44_axi_reload_wm,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
index cc768db875db..72ce32940c29 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1798,7 +1798,7 @@ static void msm_vfe46_stats_enable_module(struct vfe_device *vfe_dev,
static void msm_vfe46_stats_update_ping_pong_addr(
struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
- uint32_t pingpong_status, dma_addr_t paddr)
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_sz)
{
void __iomem *vfe_base = vfe_dev->vfe_base;
int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
@@ -1902,6 +1902,7 @@ struct msm_vfe_hardware_info vfe46_hw_info = {
.process_stats_irq = msm_isp_process_stats_irq,
.process_epoch_irq = msm_vfe46_process_epoch_irq,
.config_irq = msm_vfe46_config_irq,
+ .preprocess_camif_irq = msm_isp47_preprocess_camif_irq,
},
.axi_ops = {
.reload_wm = msm_vfe46_axi_reload_wm,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
index d829aefe6c98..9c74695a6e32 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
@@ -331,7 +331,6 @@ int msm_vfe47_init_hardware(struct vfe_device *vfe_dev)
goto ahb_vote_fail;
}
vfe_dev->ahb_vote = CAM_AHB_SVS_VOTE;
- vfe_dev->turbo_vote = 0;
vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] =
vfe_dev->vfe_base;
@@ -387,6 +386,7 @@ void msm_vfe47_release_hardware(struct vfe_device *vfe_dev)
vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks(
vfe_dev, 0);
+ msm_vfe47_configure_hvx(vfe_dev, 0);
vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0);
}
@@ -489,7 +489,7 @@ void msm_vfe47_process_violation_status(
return;
}
- pr_err("%s: VFE pipeline violation status %d\n", __func__,
+ pr_err_ratelimited("%s: VFE pipeline violation status %d\n", __func__,
violation_status);
}
@@ -686,6 +686,15 @@ void msm_vfe47_process_epoch_irq(struct vfe_device *vfe_dev,
}
}
+void msm_isp47_preprocess_camif_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0)
+{
+ if (irq_status0 & BIT(1))
+ vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = false;
+ if (irq_status0 & BIT(0))
+ vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = true;
+}
+
void msm_vfe47_reg_update(struct vfe_device *vfe_dev,
enum msm_vfe_input_src frame_src)
{
@@ -1509,16 +1518,19 @@ void msm_vfe47_configure_hvx(struct vfe_device *vfe_dev,
pr_err("%s: no stream_clk\n", __func__);
return;
}
- rc = msm_camera_clk_enable(&vfe_dev->pdev->dev, vfe_dev->hvx_clk_info,
- vfe_dev->hvx_clk, vfe_dev->num_hvx_clk, is_stream_on);
- if (rc) {
- pr_err("%s: stream_clk enable failed, enable: %u\n",
- __func__,
- is_stream_on);
- return;
- }
- if (is_stream_on == 1) {
+ if (is_stream_on) {
/* Enable HVX */
+ if (!vfe_dev->hvx_clk_state) {
+ rc = msm_camera_clk_enable(&vfe_dev->pdev->dev,
+ vfe_dev->hvx_clk_info, vfe_dev->hvx_clk,
+ vfe_dev->num_hvx_clk, is_stream_on);
+ if (rc) {
+ pr_err("%s: stream_clk enable failed\n",
+ __func__);
+ return;
+ }
+ vfe_dev->hvx_clk_state = true;
+ }
val = msm_camera_io_r(vfe_dev->vfe_base + 0x50);
val |= (1 << 3);
msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x50);
@@ -1528,6 +1540,17 @@ void msm_vfe47_configure_hvx(struct vfe_device *vfe_dev,
msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x50);
} else {
/* Disable HVX */
+ if (!vfe_dev->hvx_clk_state)
+ return;
+ rc = msm_camera_clk_enable(&vfe_dev->pdev->dev,
+ vfe_dev->hvx_clk_info, vfe_dev->hvx_clk,
+ vfe_dev->num_hvx_clk, is_stream_on);
+ if (rc) {
+ pr_err("%s: stream_clk disable failed\n",
+ __func__);
+ return;
+ }
+ vfe_dev->hvx_clk_state = false;
val = msm_camera_io_r(vfe_dev->vfe_base + 0x50);
val &= 0xFFFFFFF7;
msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x50);
@@ -1896,7 +1919,7 @@ void msm_vfe47_update_ping_pong_addr(
if (buf_size < 0)
buf_size = 0;
- paddr32_max = (paddr + buf_size) & 0xFFFFFFC0;
+ paddr32_max = (paddr + buf_size) & 0xFFFFFFE0;
msm_camera_io_w(paddr32, vfe_base +
VFE47_PING_PONG_BASE(wm_idx, pingpong_bit));
@@ -2384,18 +2407,23 @@ void msm_vfe47_stats_enable_module(struct vfe_device *vfe_dev,
void msm_vfe47_stats_update_ping_pong_addr(
struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
- uint32_t pingpong_status, dma_addr_t paddr)
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_size)
{
void __iomem *vfe_base = vfe_dev->vfe_base;
int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
stream_info);
uint32_t paddr32 = (paddr & 0xFFFFFFFF);
+ uint32_t paddr32_max;
int stats_idx;
stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
msm_camera_io_w(paddr32, vfe_base +
VFE47_STATS_PING_PONG_BASE(stats_idx, pingpong_status));
+
+ paddr32_max = (paddr + buf_size) & 0xFFFFFFE0;
+ msm_camera_io_w(paddr32_max, vfe_base +
+ VFE47_STATS_PING_PONG_BASE(stats_idx, pingpong_status) + 0x4);
}
uint32_t msm_vfe47_stats_get_wm_mask(
@@ -2529,6 +2557,7 @@ int msm_vfe47_get_clks(struct vfe_device *vfe_dev)
vfe_dev->hvx_clk_info =
&vfe_dev->vfe_clk_info[vfe_dev->num_clk-1];
vfe_dev->hvx_clk = &vfe_dev->vfe_clk[vfe_dev->num_clk-1];
+ vfe_dev->hvx_clk_state = false;
}
for (i = 0; i < vfe_dev->num_clk; i++) {
@@ -2554,8 +2583,8 @@ void msm_vfe47_put_clks(struct vfe_device *vfe_dev)
int msm_vfe47_enable_clks(struct vfe_device *vfe_dev, int enable)
{
return msm_camera_clk_enable(&vfe_dev->pdev->dev,
- vfe_dev->vfe_clk_info,
- vfe_dev->vfe_clk, vfe_dev->num_norm_clk, enable);
+ vfe_dev->vfe_clk_info,
+ vfe_dev->vfe_clk, vfe_dev->num_norm_clk, enable);
}
int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate)
@@ -2563,31 +2592,53 @@ int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate)
int rc = 0;
int clk_idx = vfe_dev->hw_info->vfe_clk_idx;
int ret;
+ long clk_rate, prev_clk_rate;
+
+ clk_rate = clk_round_rate(vfe_dev->vfe_clk[clk_idx], *rate);
+ if (vfe_dev->msm_isp_vfe_clk_rate == clk_rate)
+ return rc;
+ prev_clk_rate = vfe_dev->msm_isp_vfe_clk_rate;
+ vfe_dev->msm_isp_vfe_clk_rate = clk_rate;
+ /*
+ * if cx_ipeak is supported vote first so that dsp throttling is
+ * reduced before we go to turbo
+ */
+ if ((vfe_dev->vfe_cx_ipeak) &&
+ (vfe_dev->msm_isp_vfe_clk_rate >=
+ vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL]
+ [vfe_dev->hw_info->vfe_clk_idx]) &&
+ prev_clk_rate <
+ vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL]
+ [vfe_dev->hw_info->vfe_clk_idx]) {
+ ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, true);
+ if (ret) {
+ pr_err("%s: cx_ipeak_update failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+ /*set vfe clock*/
rc = msm_camera_clk_set_rate(&vfe_dev->pdev->dev,
vfe_dev->vfe_clk[clk_idx], *rate);
if (rc < 0)
return rc;
- *rate = clk_round_rate(vfe_dev->vfe_clk[clk_idx], *rate);
- vfe_dev->msm_isp_vfe_clk_rate = *rate;
- if (vfe_dev->vfe_cx_ipeak) {
- if (vfe_dev->msm_isp_vfe_clk_rate >=
- vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_TURBO]
- [vfe_dev->hw_info->vfe_clk_idx] &&
- vfe_dev->turbo_vote == 0) {
- ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, true);
- if (ret)
- pr_debug("%s: cx_ipeak_update failed %d\n",
- __func__, ret);
- else
- vfe_dev->turbo_vote = 1;
- } else if (vfe_dev->turbo_vote == 1) {
- ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, false);
- if (ret)
- pr_debug("%s: cx_ipeak_update failed %d\n",
- __func__, ret);
- else
- vfe_dev->turbo_vote = 0;
+ /*
+ * if cx_ipeak is supported remove the vote for non-turbo clock and
+ * if voting done earlier
+ */
+ if ((vfe_dev->vfe_cx_ipeak) &&
+ (vfe_dev->msm_isp_vfe_clk_rate <
+ vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL]
+ [vfe_dev->hw_info->vfe_clk_idx]) &&
+ prev_clk_rate >=
+ vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL]
+ [vfe_dev->hw_info->vfe_clk_idx]) {
+ ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, false);
+ if (ret) {
+ pr_err("%s: cx_ipeak_update failed %d\n",
+ __func__, ret);
+ return ret;
}
}
if (vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg)
@@ -2742,6 +2793,8 @@ int msm_vfe47_enable_regulators(struct vfe_device *vfe_dev, int enable)
int msm_vfe47_get_platform_data(struct vfe_device *vfe_dev)
{
int rc = 0;
+ void __iomem *vfe_fuse_base;
+ uint32_t vfe_fuse_base_size;
vfe_dev->vfe_base = msm_camera_get_reg_base(vfe_dev->pdev, "vfe", 0);
if (!vfe_dev->vfe_base)
@@ -2766,7 +2819,18 @@ int msm_vfe47_get_platform_data(struct vfe_device *vfe_dev)
rc = -ENOMEM;
goto get_res_fail;
}
-
+ vfe_dev->vfe_hw_limit = 0;
+ vfe_fuse_base = msm_camera_get_reg_base(vfe_dev->pdev,
+ "vfe_fuse", 0);
+ vfe_fuse_base_size = msm_camera_get_res_size(vfe_dev->pdev,
+ "vfe_fuse");
+ if (vfe_fuse_base) {
+ if (vfe_fuse_base_size)
+ vfe_dev->vfe_hw_limit =
+ (msm_camera_io_r(vfe_fuse_base) >> 5) & 0x1;
+ msm_camera_put_reg_base(vfe_dev->pdev, vfe_fuse_base,
+ "vfe_fuse", 0);
+ }
rc = vfe_dev->hw_info->vfe_ops.platform_ops.get_regulators(vfe_dev);
if (rc)
goto get_regulator_fail;
@@ -2881,6 +2945,7 @@ struct msm_vfe_hardware_info vfe47_hw_info = {
.process_epoch_irq = msm_vfe47_process_epoch_irq,
.config_irq = msm_vfe47_config_irq,
.read_irq_status = msm_vfe47_read_irq_status,
+ .preprocess_camif_irq = msm_isp47_preprocess_camif_irq,
},
.axi_ops = {
.reload_wm = msm_vfe47_axi_reload_wm,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h
index 22a1a21bce9a..60afe3a80091 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014,2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -42,6 +42,8 @@ void msm_vfe47_process_reg_update(struct vfe_device *vfe_dev,
void msm_vfe47_process_epoch_irq(struct vfe_device *vfe_dev,
uint32_t irq_status0, uint32_t irq_status1,
struct msm_isp_timestamp *ts);
+void msm_isp47_preprocess_camif_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0);
void msm_vfe47_reg_update(struct vfe_device *vfe_dev,
enum msm_vfe_input_src frame_src);
long msm_vfe47_reset_hardware(struct vfe_device *vfe_dev,
@@ -148,7 +150,7 @@ void msm_vfe47_stats_enable_module(struct vfe_device *vfe_dev,
uint32_t stats_mask, uint8_t enable);
void msm_vfe47_stats_update_ping_pong_addr(
struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
- uint32_t pingpong_status, dma_addr_t paddr);
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_size);
uint32_t msm_vfe47_stats_get_wm_mask(
uint32_t irq_status0, uint32_t irq_status1);
uint32_t msm_vfe47_stats_get_comp_mask(
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c
index 5b4d6aa63055..e3d8ecb410ff 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c
@@ -237,6 +237,7 @@ static int msm_vfe48_get_clks(struct vfe_device *vfe_dev)
vfe_dev->hvx_clk_info =
&vfe_dev->vfe_clk_info[vfe_dev->num_clk-1];
vfe_dev->hvx_clk = &vfe_dev->vfe_clk[vfe_dev->num_clk-1];
+ vfe_dev->hvx_clk_state = false;
}
for (i = 0; i < vfe_dev->num_clk; i++) {
@@ -368,6 +369,7 @@ struct msm_vfe_hardware_info vfe48_hw_info = {
.process_stats_irq = msm_isp_process_stats_irq,
.process_epoch_irq = msm_vfe47_process_epoch_irq,
.config_irq = msm_vfe47_config_irq,
+ .preprocess_camif_irq = msm_isp47_preprocess_camif_irq,
},
.axi_ops = {
.reload_wm = msm_vfe47_axi_reload_wm,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
index 4e74851dc67d..b44b7573e0e6 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
@@ -948,6 +948,8 @@ static void msm_isp_update_pd_stats_idx(struct vfe_device *vfe_dev,
uint32_t pingpong_status = 0, pingpong_bit = 0;
struct msm_isp_buffer *done_buf = NULL;
int vfe_idx = -1;
+ /* initialize pd_buf_idx with an invalid index 0xF */
+ vfe_dev->pd_buf_idx = 0xF;
if (frame_src < VFE_RAW_0 || frame_src > VFE_RAW_2)
return;
@@ -1171,7 +1173,7 @@ void msm_isp_get_avtimer_ts(
rc = avcs_core_query_timer(&avtimer_tick);
if (rc < 0) {
- pr_err("%s: Error: Invalid AVTimer Tick, rc=%d\n",
+ pr_err_ratelimited("%s: Error: Invalid AVTimer Tick, rc=%d\n",
__func__, rc);
/* In case of error return zero AVTimer Tick Value */
time_stamp->vt_time.tv_sec = 0;
@@ -1734,7 +1736,7 @@ int msm_isp_print_ping_pong_address(struct vfe_device *vfe_dev,
}
temp = buf->mapped_info[0].paddr +
buf->mapped_info[0].len;
- pr_err("%s: stream %x ping bit %d uses buffer %pa-%pa, num_isp %d\n",
+ pr_err("%s: stream %x ping bit %d uses buffer %pK-%pK, num_isp %d\n",
__func__, stream_info->stream_src,
pingpong_bit,
&buf->mapped_info[0].paddr, &temp,
@@ -1743,10 +1745,10 @@ int msm_isp_print_ping_pong_address(struct vfe_device *vfe_dev,
for (i = 0; i < stream_info->num_planes; i++) {
for (k = 0; k < stream_info->num_isp; k++) {
pr_debug(
- "%s: stream_id %x ping-pong %d plane %d start_addr %lu addr_offset %x len %zx stride %d scanline %d\n"
+ "%s: stream_id %x ping-pong %d plane %d start_addr %pK addr_offset %x len %zx stride %d scanline %d\n"
, __func__, stream_info->stream_id,
- pingpong_bit, i, (unsigned long)
- buf->mapped_info[i].paddr,
+ pingpong_bit, i,
+ (void *)buf->mapped_info[i].paddr,
stream_info->
plane_cfg[k][i].plane_addr_offset,
buf->mapped_info[i].len,
@@ -2382,7 +2384,6 @@ static int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev)
struct msm_vfe_axi_stream *stream_info;
uint64_t total_pix_bandwidth = 0, total_rdi_bandwidth = 0;
uint32_t num_pix_streams = 0;
- uint64_t total_bandwidth = 0;
int vfe_idx;
for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
@@ -2401,10 +2402,10 @@ static int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev)
}
}
}
- total_bandwidth = total_pix_bandwidth + total_rdi_bandwidth;
+ vfe_dev->total_bandwidth = total_pix_bandwidth + total_rdi_bandwidth;
rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id,
- (total_bandwidth + vfe_dev->hw_info->min_ab),
- (total_bandwidth + vfe_dev->hw_info->min_ib));
+ (vfe_dev->total_bandwidth + vfe_dev->hw_info->min_ab),
+ (vfe_dev->total_bandwidth + vfe_dev->hw_info->min_ib));
if (rc < 0)
pr_err("%s: update failed\n", __func__);
@@ -2412,6 +2413,66 @@ static int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev)
return rc;
}
+int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg)
+{
+ int i, rc = 0;
+ uint64_t total_bandwidth = 0;
+ int vfe_idx;
+ unsigned long flags;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_dual_lpm_mode *ab_ib_vote = NULL;
+
+ ab_ib_vote = (struct msm_vfe_dual_lpm_mode *)arg;
+ if (!ab_ib_vote) {
+ pr_err("%s: ab_ib_vote is NULL !!!\n", __func__);
+ rc = -1;
+ return rc;
+ }
+ if (ab_ib_vote->lpm_mode) {
+ for (i = 0; i < ab_ib_vote->num_src; i++) {
+ stream_info =
+ msm_isp_get_stream_common_data(vfe_dev,
+ ab_ib_vote->stream_src[i]);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ if (stream_info->state == ACTIVE) {
+ vfe_idx =
+ msm_isp_get_vfe_idx_for_stream(vfe_dev,
+ stream_info);
+ total_bandwidth +=
+ stream_info->bandwidth[
+ vfe_idx];
+ stream_info->state = PAUSED;
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+ vfe_dev->total_bandwidth -= total_bandwidth;
+ rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id,
+ (vfe_dev->total_bandwidth - vfe_dev->hw_info->min_ab),
+ (vfe_dev->total_bandwidth - vfe_dev->hw_info->min_ib));
+ } else {
+ for (i = 0; i < ab_ib_vote->num_src; i++) {
+ stream_info =
+ msm_isp_get_stream_common_data(vfe_dev,
+ ab_ib_vote->stream_src[i]);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ if (stream_info->state == PAUSED) {
+ vfe_idx =
+ msm_isp_get_vfe_idx_for_stream(vfe_dev,
+ stream_info);
+ total_bandwidth +=
+ stream_info->bandwidth[
+ vfe_idx];
+ stream_info->state = ACTIVE;
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+ vfe_dev->total_bandwidth += total_bandwidth;
+ rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id,
+ (vfe_dev->total_bandwidth + vfe_dev->hw_info->min_ab),
+ (vfe_dev->total_bandwidth + vfe_dev->hw_info->min_ib));
+ }
+ return rc;
+}
static int msm_isp_init_stream_ping_pong_reg(
struct msm_vfe_axi_stream *stream_info)
{
@@ -2752,6 +2813,7 @@ static int __msm_isp_check_stream_state(struct msm_vfe_axi_stream *stream_info,
case RESUMING:
case RESUME_PENDING:
case ACTIVE:
+ case PAUSED:
if (cmd != 0)
return -EALREADY;
break;
@@ -2818,9 +2880,11 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev,
* those state transitions instead of directly forcing stream to
* be INACTIVE
*/
- while (stream_info->state != ACTIVE)
- __msm_isp_axi_stream_update(stream_info,
+ if (stream_info->state != PAUSED) {
+ while (stream_info->state != ACTIVE)
+ __msm_isp_axi_stream_update(stream_info,
&timestamp);
+ }
msm_isp_cfg_stream_scratch(stream_info, VFE_PING_FLAG);
msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG);
stream_info->undelivered_request_cnt = 0;
@@ -2833,8 +2897,15 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev,
vfe_dev->hw_info->vfe_ops.axi_ops.
clear_wm_irq_mask(vfe_dev, stream_info);
}
- init_completion(&stream_info->inactive_comp);
- stream_info->state = STOP_PENDING;
+ if (stream_info->state == ACTIVE) {
+ init_completion(&stream_info->inactive_comp);
+ stream_info->state = STOP_PENDING;
+ } else if (stream_info->state == PAUSED) {
+ /* don't wait for reg update */
+ stream_info->state = STOP_PENDING;
+ msm_isp_axi_stream_enable_cfg(stream_info);
+ stream_info->state = INACTIVE;
+ }
spin_unlock_irqrestore(&stream_info->lock, flags);
}
@@ -3262,6 +3333,7 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
int k;
uint32_t wm_mask = 0;
int vfe_idx;
+ uint32_t pingpong_bit = 0;
if (!vfe_dev || !stream_info) {
pr_err("%s %d failed: vfe_dev %pK stream_info %pK\n", __func__,
@@ -3280,27 +3352,40 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
}
frame_src = SRC_TO_INTF(stream_info->stream_src);
+ pingpong_status = vfe_dev->hw_info->
+ vfe_ops.axi_ops.get_pingpong_status(vfe_dev);
/*
* If PIX stream is active then RDI path uses SOF frame ID of PIX
* In case of standalone RDI streaming, SOF are used from
* individual intf.
*/
- if (((vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id <=
- vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id)) ||
- ((!vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id <=
- vfe_dev->axi_data.src_info[frame_src].frame_id)) ||
- stream_info->undelivered_request_cnt >= MAX_BUFFERS_IN_HW) {
- pr_debug("%s:%d invalid request_frame %d cur frame id %d pix %d\n",
+ /*
+ * If frame_id = 1 then no eof check is needed
+ */
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active &&
+ vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame == false) {
+ pr_debug("%s:%d invalid time to request frame %d\n",
+ __func__, __LINE__, frame_id);
+ goto error;
+ }
+ if ((vfe_dev->axi_data.src_info[VFE_PIX_0].active && (frame_id !=
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id + vfe_dev->
+ axi_data.src_info[VFE_PIX_0].sof_counter_step)) ||
+ ((!vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id !=
+ vfe_dev->axi_data.src_info[frame_src].frame_id + vfe_dev->
+ axi_data.src_info[frame_src].sof_counter_step))) {
+ pr_debug("%s:%d invalid frame id %d cur frame id %d pix %d\n",
__func__, __LINE__, frame_id,
vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id,
vfe_dev->axi_data.src_info[VFE_PIX_0].active);
-
- rc = msm_isp_return_empty_buffer(vfe_dev, stream_info,
- user_stream_id, frame_id, buf_index, frame_src);
- if (rc < 0)
- pr_err("%s:%d failed: return_empty_buffer src %d\n",
- __func__, __LINE__, frame_src);
- return 0;
+ goto error;
+ }
+ if (stream_info->undelivered_request_cnt >= MAX_BUFFERS_IN_HW) {
+ pr_debug("%s:%d invalid undelivered_request_cnt %d frame id %d\n",
+ __func__, __LINE__,
+ stream_info->undelivered_request_cnt,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ goto error;
}
if ((frame_src == VFE_PIX_0) && !stream_info->undelivered_request_cnt &&
MSM_VFE_STREAM_STOP_PERIOD !=
@@ -3322,6 +3407,25 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
}
spin_lock_irqsave(&stream_info->lock, flags);
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ if (stream_info->undelivered_request_cnt == 1) {
+ pingpong_status =
+ vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(
+ vfe_dev);
+ pingpong_bit = ((pingpong_status >>
+ stream_info->wm[vfe_idx][0]) & 0x1);
+ if (stream_info->sw_ping_pong_bit == !pingpong_bit) {
+ ISP_DBG("%s:Return Empty Buffer stream id 0x%X\n",
+ __func__, stream_info->stream_id);
+ rc = msm_isp_return_empty_buffer(vfe_dev, stream_info,
+ user_stream_id, frame_id, buf_index,
+ frame_src);
+ spin_unlock_irqrestore(&stream_info->lock,
+ flags);
+ return 0;
+ }
+ }
+
queue_req = &stream_info->request_queue_cmd[stream_info->request_q_idx];
if (queue_req->cmd_used) {
spin_unlock_irqrestore(&stream_info->lock, flags);
@@ -3351,7 +3455,6 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
stream_info->request_q_cnt++;
stream_info->undelivered_request_cnt++;
- vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
stream_cfg_cmd.axi_stream_handle = stream_info->stream_handle[vfe_idx];
stream_cfg_cmd.frame_skip_pattern = NO_SKIP;
stream_cfg_cmd.init_frame_drop = 0;
@@ -3380,9 +3483,6 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
}
stream_info->sw_ping_pong_bit = 0;
} else if (stream_info->undelivered_request_cnt == 2) {
- pingpong_status =
- vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(
- vfe_dev);
rc = msm_isp_cfg_ping_pong_address(
stream_info, pingpong_status);
if (rc) {
@@ -3408,6 +3508,14 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
spin_unlock_irqrestore(&stream_info->lock, flags);
return rc;
+error:
+ rc = msm_isp_return_empty_buffer(vfe_dev, stream_info,
+ user_stream_id, frame_id, buf_index, frame_src);
+ if (rc < 0)
+ pr_err("%s:%d failed: return_empty_buffer src %d\n",
+ __func__, __LINE__, frame_src);
+ return 0;
+
}
static int msm_isp_add_buf_queue(struct vfe_device *vfe_dev,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
index f9ae5fb74281..65009cb22286 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -160,4 +160,6 @@ static inline struct msm_vfe_axi_stream *msm_isp_vfe_get_stream(
int msm_isp_cfg_offline_ping_pong_address(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status,
uint32_t buf_idx);
+int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev,
+ void *arg);
#endif /* __MSM_ISP_AXI_UTIL_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
index f40af6e95272..f2cf4d477b3f 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
@@ -23,7 +23,8 @@ static inline void msm_isp_stats_cfg_wm_scratch(struct vfe_device *vfe_dev,
{
vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr(
vfe_dev, stream_info,
- pingpong_status, vfe_dev->buf_mgr->scratch_buf_stats_addr);
+ pingpong_status, vfe_dev->buf_mgr->scratch_buf_stats_addr,
+ SZ_32M);
}
static inline void msm_isp_stats_cfg_stream_scratch(
@@ -123,7 +124,8 @@ static int msm_isp_stats_cfg_ping_pong_address(
vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr(
vfe_dev, stream_info, pingpong_status,
buf->mapped_info[0].paddr +
- stream_info->buffer_offset[k]);
+ stream_info->buffer_offset[k],
+ buf->mapped_info[0].len);
}
stream_info->buf[pingpong_bit] = buf;
buf->pingpong_bit = pingpong_bit;
@@ -832,6 +834,12 @@ static int msm_isp_stats_update_cgc_override(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info;
int k;
+ if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) {
+ pr_err("%s invalid num_streams %d\n", __func__,
+ stream_cfg_cmd->num_streams);
+ return -EINVAL;
+ }
+
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
@@ -961,6 +969,11 @@ static int msm_isp_check_stream_cfg_cmd(struct vfe_device *vfe_dev,
int vfe_idx;
uint32_t stats_idx[MSM_ISP_STATS_MAX];
+ if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) {
+ pr_err("%s invalid num_streams %d\n", __func__,
+ stream_cfg_cmd->num_streams);
+ return -EINVAL;
+ }
memset(stats_idx, 0, sizeof(stats_idx));
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
@@ -1074,7 +1087,7 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl,
uint32_t comp_stats_mask[MAX_NUM_STATS_COMP_MASK] = {0};
uint32_t num_stats_comp_mask = 0;
struct msm_vfe_stats_stream *stream_info;
- struct msm_vfe_stats_shared_data *stats_data;
+ struct msm_vfe_stats_shared_data *stats_data = NULL;
int num_stream = 0;
struct msm_vfe_stats_stream *streams[MSM_ISP_STATS_MAX];
struct msm_isp_timestamp timestamp;
@@ -1136,10 +1149,12 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl,
comp_stats_mask[stream_info->composite_flag-1] |=
1 << idx;
- ISP_DBG("%s: stats_mask %x %x active streams %d\n",
+ ISP_DBG("%s: stats_mask %x %x\n",
__func__, comp_stats_mask[0],
- comp_stats_mask[1],
- stats_data->num_active_stream);
+ comp_stats_mask[1]);
+ if (stats_data)
+ ISP_DBG("%s: active_streams = %d\n", __func__,
+ stats_data->num_active_stream);
streams[num_stream++] = stream_info;
}
@@ -1231,6 +1246,12 @@ int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg)
int vfe_idx;
int k;
+ if (update_cmd->num_streams > MSM_ISP_STATS_MAX) {
+ pr_err("%s: Invalid num_streams %d\n",
+ __func__, update_cmd->num_streams);
+ return -EINVAL;
+ }
+
/*validate request*/
for (i = 0; i < update_cmd->num_streams; i++) {
update_info = (struct msm_vfe_axi_stream_cfg_update_info *)
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
index 86b500973538..f4e81c02208c 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
@@ -951,6 +951,11 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd,
rc = msm_isp_dual_hw_master_slave_sync(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
+ case VIDIOC_MSM_ISP_DUAL_HW_LPM_MODE:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_ab_ib_update_lpm_mode(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
case VIDIOC_MSM_ISP_FETCH_ENG_START:
case VIDIOC_MSM_ISP_MAP_BUF_START_FE:
mutex_lock(&vfe_dev->core_mutex);
@@ -1423,6 +1428,20 @@ static int msm_isp_send_hw_cmd(struct vfe_device *vfe_dev,
vfe_dev->vfe_ub_policy = *cfg_data;
break;
}
+ case GET_VFE_HW_LIMIT: {
+ uint32_t *hw_limit = NULL;
+
+ if (cmd_len < sizeof(uint32_t)) {
+ pr_err("%s:%d failed: invalid cmd len %u exp %zu\n",
+ __func__, __LINE__, cmd_len,
+ sizeof(uint32_t));
+ return -EINVAL;
+ }
+
+ hw_limit = (uint32_t *)cfg_data;
+ *hw_limit = vfe_dev->vfe_hw_limit;
+ break;
+ }
}
return 0;
}
@@ -2073,7 +2092,10 @@ irqreturn_t msm_isp_process_irq(int irq_num, void *data)
__func__, vfe_dev->pdev->id);
return IRQ_HANDLED;
}
-
+ if (vfe_dev->hw_info->vfe_ops.irq_ops.preprocess_camif_irq) {
+ vfe_dev->hw_info->vfe_ops.irq_ops.preprocess_camif_irq(
+ vfe_dev, irq_status0);
+ }
if (msm_isp_process_overflow_irq(vfe_dev,
&irq_status0, &irq_status1, 0)) {
/* if overflow initiated no need to handle the interrupts */
@@ -2120,7 +2142,7 @@ void msm_isp_do_tasklet(unsigned long data)
while (atomic_read(&vfe_dev->irq_cnt)) {
spin_lock_irqsave(&vfe_dev->tasklet_lock, flags);
- queue_cmd = list_first_entry(&vfe_dev->tasklet_q,
+ queue_cmd = list_first_entry_or_null(&vfe_dev->tasklet_q,
struct msm_vfe_tasklet_queue_cmd, list);
if (!queue_cmd) {
atomic_set(&vfe_dev->irq_cnt, 0);
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 1628c098622f..41d8ef577a27 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -73,7 +73,7 @@ static void msm_ispif_io_dump_reg(struct ispif_device *ispif)
static inline int msm_ispif_is_intf_valid(uint32_t csid_version,
- uint8_t intf_type)
+ enum msm_ispif_vfe_intf intf_type)
{
return ((csid_version <= CSID_VERSION_V22 && intf_type != VFE0) ||
(intf_type >= VFE_MAX)) ? false : true;
@@ -1662,7 +1662,7 @@ static long msm_ispif_subdev_fops_ioctl(struct file *file, unsigned int cmd,
static int ispif_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
struct ispif_device *ispif = v4l2_get_subdevdata(sd);
- int rc;
+ int rc = 0;
mutex_lock(&ispif->mutex);
if (0 == ispif->open_cnt) {
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c
index de27e585f63d..06e3ee4c353b 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -261,7 +261,7 @@ static int msm_jpeg_init_dev(struct platform_device *pdev)
goto fail_4;
}
- platform_set_drvdata(pdev, &msm_jpeg_device_p);
+ platform_set_drvdata(pdev, msm_jpeg_device_p);
JPEG_DBG("%s %s%d: success\n", __func__, MSM_JPEG_NAME, pdev->id);
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c
index 88d90d0a7c08..994ba93907e3 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c
+++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c
@@ -1759,7 +1759,7 @@ void msm_jpegdma_hw_put(struct msm_jpegdma_device *dma)
*/
static int msm_jpegdma_hw_attach_iommu(struct msm_jpegdma_device *dma)
{
- int ret;
+ int ret = -EINVAL;
mutex_lock(&dma->lock);
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 08aab077eec7..8402e31364b9 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -980,6 +980,7 @@ static int cpp_init_hardware(struct cpp_device *cpp_dev)
{
int rc = 0;
uint32_t vbif_version;
+ cpp_dev->turbo_vote = 0;
rc = msm_camera_regulator_enable(cpp_dev->cpp_vdd,
cpp_dev->num_reg, true);
@@ -1432,6 +1433,14 @@ static int cpp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
return -ENODEV;
}
+ if (cpp_dev->turbo_vote == 1) {
+ rc = cx_ipeak_update(cpp_dev->cpp_cx_ipeak, false);
+ if (rc)
+ pr_err("cx_ipeak_update failed");
+ else
+ cpp_dev->turbo_vote = 0;
+ }
+
cpp_dev->cpp_open_cnt--;
if (cpp_dev->cpp_open_cnt == 0) {
pr_debug("irq_status: 0x%x\n",
@@ -2955,6 +2964,38 @@ static int msm_cpp_validate_input(unsigned int cmd, void *arg,
return 0;
}
+unsigned long cpp_cx_ipeak_update(struct cpp_device *cpp_dev,
+ unsigned long clock, int idx)
+{
+ unsigned long clock_rate = 0;
+ int ret = 0;
+
+ if ((clock >= cpp_dev->hw_info.freq_tbl
+ [(cpp_dev->hw_info.freq_tbl_count) - 1]) &&
+ (cpp_dev->turbo_vote == 0)) {
+ ret = cx_ipeak_update(cpp_dev->cpp_cx_ipeak, true);
+ if (ret) {
+ pr_err("cx_ipeak voting failed setting clock below turbo");
+ clock = cpp_dev->hw_info.freq_tbl
+ [(cpp_dev->hw_info.freq_tbl_count) - 2];
+ } else {
+ cpp_dev->turbo_vote = 1;
+ }
+ clock_rate = msm_cpp_set_core_clk(cpp_dev, clock, idx);
+ } else if (clock < cpp_dev->hw_info.freq_tbl
+ [(cpp_dev->hw_info.freq_tbl_count) - 1]) {
+ clock_rate = msm_cpp_set_core_clk(cpp_dev, clock, idx);
+ if (cpp_dev->turbo_vote == 1) {
+ ret = cx_ipeak_update(cpp_dev->cpp_cx_ipeak, false);
+ if (ret)
+ pr_err("cx_ipeak unvoting failed");
+ else
+ cpp_dev->turbo_vote = 0;
+ }
+ }
+ return clock_rate;
+}
+
long msm_cpp_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
@@ -3337,9 +3378,15 @@ STREAM_BUFF_END:
mutex_unlock(&cpp_dev->mutex);
return -EINVAL;
}
- clock_rate = msm_cpp_set_core_clk(cpp_dev,
- clock_settings.clock_rate,
- msm_cpp_core_clk_idx);
+ if (cpp_dev->cpp_cx_ipeak) {
+ clock_rate = cpp_cx_ipeak_update(cpp_dev,
+ clock_settings.clock_rate,
+ msm_cpp_core_clk_idx);
+ } else {
+ clock_rate = msm_cpp_set_core_clk(cpp_dev,
+ clock_settings.clock_rate,
+ msm_cpp_core_clk_idx);
+ }
if (rc < 0) {
pr_err("Fail to set core clk\n");
mutex_unlock(&cpp_dev->mutex);
@@ -4391,6 +4438,15 @@ static int cpp_probe(struct platform_device *pdev)
}
}
+ if (of_find_property(pdev->dev.of_node, "qcom,cpp-cx-ipeak", NULL)) {
+ cpp_dev->cpp_cx_ipeak = cx_ipeak_register(
+ pdev->dev.of_node, "qcom,cpp-cx-ipeak");
+ if (cpp_dev->cpp_cx_ipeak)
+ CPP_DBG("Cx ipeak Registration Successful ");
+ else
+ pr_err("Cx ipeak Registration Unsuccessful");
+ }
+
rc = msm_camera_get_reset_info(pdev,
&cpp_dev->micro_iface_reset);
if (rc < 0) {
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
index e69b9d633a1f..a05448091e42 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -24,6 +24,7 @@
#include "cam_soc_api.h"
#include "cam_hw_ops.h"
#include <media/msmb_pproc.h>
+#include <soc/qcom/cx_ipeak.h>
/* hw version info:
31:28 Major version
@@ -284,6 +285,8 @@ struct cpp_device {
uint32_t micro_reset;
struct msm_cpp_payload_params payload_params;
struct msm_cpp_vbif_data *vbif_data;
+ bool turbo_vote;
+ struct cx_ipeak_client *cpp_cx_ipeak;
};
int msm_cpp_set_micro_clk(struct cpp_device *cpp_dev);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
index be266641a105..214dd6f3406d 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
@@ -1069,7 +1069,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
if (rc < 0) {
pr_err("%s:%d csiphy config_vreg failed\n",
__func__, __LINE__);
- goto csiphy_resource_fail;
+ goto csiphy_vreg_config_fail;
}
rc = msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
csiphy_dev->csiphy_vreg,
@@ -1089,7 +1089,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
if (rc < 0) {
pr_err("%s: csiphy clk enable failed\n", __func__);
csiphy_dev->ref_count--;
- goto csiphy_resource_fail;
+ goto csiphy_enable_clk_fail;
}
CDBG("%s:%d called\n", __func__, __LINE__);
@@ -1117,12 +1117,17 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
return 0;
+csiphy_enable_clk_fail:
+ msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 0);
top_vreg_enable_failed:
msm_camera_config_vreg(&csiphy_dev->pdev->dev,
csiphy_dev->csiphy_vreg,
csiphy_dev->regulator_count, NULL, 0,
&csiphy_dev->csiphy_reg_ptr[0], 0);
-csiphy_resource_fail:
+csiphy_vreg_config_fail:
if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
CAM_AHB_SUSPEND_VOTE) < 0)
pr_err("%s: failed to vote for AHB\n", __func__);
@@ -1169,7 +1174,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
if (rc < 0) {
pr_err("%s:%d csiphy config_vreg failed\n",
__func__, __LINE__);
- goto csiphy_resource_fail;
+ goto csiphy_vreg_config_fail;
}
rc = msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
csiphy_dev->csiphy_vreg,
@@ -1189,7 +1194,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
if (rc < 0) {
pr_err("%s: csiphy clk enable failed\n", __func__);
csiphy_dev->ref_count--;
- goto csiphy_resource_fail;
+ goto csiphy_enable_clk_fail;
}
CDBG("%s:%d clk enable success\n", __func__, __LINE__);
@@ -1213,12 +1218,18 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
csiphy_dev->hw_version);
csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
return 0;
+
+csiphy_enable_clk_fail:
+ msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 0);
top_vreg_enable_failed:
msm_camera_config_vreg(&csiphy_dev->pdev->dev,
csiphy_dev->csiphy_vreg,
csiphy_dev->regulator_count, NULL, 0,
&csiphy_dev->csiphy_reg_ptr[0], 0);
-csiphy_resource_fail:
+csiphy_vreg_config_fail:
if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
CAM_AHB_SUSPEND_VOTE) < 0)
pr_err("%s: failed to vote for AHB\n", __func__);
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
index ef0ef1512211..2fc3ce406c27 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
@@ -3969,6 +3969,18 @@ static int mpq_dmx_process_video_packet_framing(
mpq_dmx_update_decoder_stat(mpq_feed);
+ if (video_b_frame_events == 1) {
+ if (non_predicted_video_frame == 0) {
+ struct dmx_pts_dts_info *pts_dts;
+
+ pts_dts =
+ &meta_data.info.framing.pts_dts_info;
+ pts_dts->pts_exist = 0;
+ pts_dts->pts = 0;
+ pts_dts->dts_exist = 0;
+ pts_dts->dts = 0;
+ }
+ }
/*
* Write meta-data that includes the framing information
*/
@@ -3986,14 +3998,7 @@ static int mpq_dmx_process_video_packet_framing(
stream_buffer, &data, ret);
/* Trigger ES Data Event for VPTS */
- if (video_b_frame_events == 1) {
- if (non_predicted_video_frame == 1)
- feed->data_ready_cb.ts
- (&feed->feed.ts, &data);
- } else {
- feed->data_ready_cb.ts(&feed->feed.ts,
- &data);
- }
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
if (feed_data->video_buffer->mode ==
MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR)
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index 5c7b4df40d5d..e170c9ffafc7 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -539,6 +539,7 @@ static void *sde_rotator_get_userptr(void *alloc_ctx,
buf->ctx = ctx;
buf->rot_dev = rot_dev;
if (ctx->secure_camera) {
+ buf->buffer = NULL;
buf->handle = ion_import_dma_buf(iclient,
buf->fd);
if (IS_ERR_OR_NULL(buf->handle)) {
@@ -552,6 +553,7 @@ static void *sde_rotator_get_userptr(void *alloc_ctx,
buf->ctx->session_id,
buf->fd, &buf->handle);
} else {
+ buf->handle = NULL;
buf->buffer = dma_buf_get(buf->fd);
if (IS_ERR_OR_NULL(buf->buffer)) {
SDEDEV_ERR(rot_dev->dev,
@@ -578,6 +580,8 @@ error_buf_get:
static void sde_rotator_put_userptr(void *buf_priv)
{
struct sde_rotator_buf_handle *buf = buf_priv;
+ struct ion_client *iclient;
+ struct sde_rotator_device *rot_dev;
if (IS_ERR_OR_NULL(buf))
return;
@@ -588,6 +592,9 @@ static void sde_rotator_put_userptr(void *buf_priv)
return;
}
+ rot_dev = buf->ctx->rot_dev;
+ iclient = buf->rot_dev->mdata->iclient;
+
SDEDEV_DBG(buf->rot_dev->dev, "put dmabuf s:%d fd:%d buf:%pad\n",
buf->ctx->session_id,
buf->fd, &buf->buffer);
@@ -597,6 +604,11 @@ static void sde_rotator_put_userptr(void *buf_priv)
buf->buffer = NULL;
}
+ if (buf->handle) {
+ ion_free(iclient, buf->handle);
+ buf->handle = NULL;
+ }
+
kfree(buf_priv);
}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
index 0eaf1960ec27..882e3dcd6277 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
@@ -768,10 +768,17 @@ static int sde_mdp_get_img(struct sde_fb_data *img,
len = &data->len;
data->flags |= img->flags;
data->offset = img->offset;
- if (data->flags & SDE_ROT_EXT_DMA_BUF)
+
+ if ((data->flags & SDE_SECURE_CAMERA_SESSION) &&
+ IS_ERR_OR_NULL(img->handle)) {
+ SDEROT_ERR("error on ion_import_fb\n");
+ ret = PTR_ERR(img->handle);
+ img->handle = NULL;
+ return ret;
+ } else if (data->flags & SDE_ROT_EXT_DMA_BUF) {
data->srcp_dma_buf = img->buffer;
- else if (IS_ERR(data->srcp_dma_buf)) {
- SDEROT_ERR("error on ion_import_fd\n");
+ } else if (IS_ERR(data->srcp_dma_buf)) {
+ SDEROT_ERR("error on dma_buf\n");
ret = PTR_ERR(data->srcp_dma_buf);
data->srcp_dma_buf = NULL;
return ret;
@@ -845,8 +852,6 @@ static int sde_mdp_get_img(struct sde_fb_data *img,
ret = 0;
} while (0);
- if (!IS_ERR_OR_NULL(ihandle))
- ion_free(iclient, ihandle);
return ret;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index b7e2c297a1ad..24eb8fff905b 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -528,7 +528,6 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = {
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_NON_SECURE_OUTPUT2,
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index ce6736509d61..ca9d7fba4ee3 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -468,7 +468,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
- .name = "I Frame Quantization",
+ .name = "H264 I Frame Quantization",
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 1,
.maximum = 51,
@@ -479,7 +479,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
- .name = "P Frame Quantization",
+ .name = "H264 P Frame Quantization",
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 1,
.maximum = 51,
@@ -490,7 +490,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
- .name = "B Frame Quantization",
+ .name = "H264 B Frame Quantization",
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 1,
.maximum = 51,
@@ -500,6 +500,61 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.qmenu = NULL,
},
{
+ .id = V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP,
+ .name = "H263 I Frame Quantization",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 31,
+ .default_value = I_FRAME_QP,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP,
+ .name = "H263 P Frame Quantization",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 31,
+ .default_value = P_FRAME_QP,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP,
+ .name = "H263 B Frame Quantization",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 31,
+ .default_value = B_FRAME_QP,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP,
+ .name = "VPX I Frame Quantization",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 127,
+ .default_value = I_FRAME_QP,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP,
+ .name = "VPX P Frame Quantization",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 127,
+ .default_value = P_FRAME_QP,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ },
+ {
.id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
.name = "H264 Minimum QP",
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -522,6 +577,24 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.qmenu = NULL,
},
{
+ .id = V4L2_CID_MPEG_VIDEO_VPX_MIN_QP,
+ .name = "VPX Minimum QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 127,
+ .default_value = 0,
+ .step = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_MAX_QP,
+ .name = "VPX Maximum QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 127,
+ .default_value = 127,
+ .step = 1,
+ },
+ {
.id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP,
.name = "VP8 Minimum QP",
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -540,6 +613,24 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.step = 1,
},
{
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP,
+ .name = "MPEG4 Minimum QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 31,
+ .default_value = 1,
+ .step = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP,
+ .name = "MPEG4 Maximum QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 31,
+ .default_value = 31,
+ .step = 1,
+ },
+ {
.id = V4L2_CID_MPEG_VIDEO_MIN_QP_PACKED,
.name = "H264 Minimum QP PACKED",
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -940,6 +1031,36 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.step = 0,
},
{
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP,
+ .name = "Iframe initial QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 127,
+ .default_value = 1,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP,
+ .name = "Pframe initial QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 127,
+ .default_value = 1,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP,
+ .name = "Bframe initial QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 127,
+ .default_value = 1,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
.id = V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP,
.name = "Iframe initial QP",
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -1122,7 +1243,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.name = "Set frame level QP",
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 1,
- .maximum = 51,
+ .maximum = 127,
.default_value = 1,
.step = 1,
.qmenu = NULL,
@@ -2230,6 +2351,9 @@ unknown_value:
return -EINVAL;
}
+static int msm_venc_validate_qp_value(struct msm_vidc_inst *inst,
+ struct v4l2_ctrl *ctrl);
+
static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
{
int rc = 0;
@@ -2649,6 +2773,81 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
pdata = &quantization;
break;
}
+ case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: {
+ struct v4l2_ctrl *qpp, *qpb;
+
+ qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP);
+ qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP);
+
+ property_id = HAL_PARAM_VENC_SESSION_QP;
+ quantization.qpi = ctrl->val;
+ quantization.qpp = qpp->val;
+ quantization.qpb = qpb->val;
+ quantization.layer_id = 0;
+
+ pdata = &quantization;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: {
+ struct v4l2_ctrl *qpi, *qpb;
+
+ qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP);
+ qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP);
+
+ property_id = HAL_PARAM_VENC_SESSION_QP;
+ quantization.qpp = ctrl->val;
+ quantization.qpi = qpi->val;
+ quantization.qpb = qpb->val;
+ quantization.layer_id = 0;
+
+ pdata = &quantization;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: {
+ struct v4l2_ctrl *qpi, *qpp;
+
+ qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP);
+ qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP);
+
+ property_id = HAL_PARAM_VENC_SESSION_QP;
+ quantization.qpb = ctrl->val;
+ quantization.qpi = qpi->val;
+ quantization.qpp = qpp->val;
+ quantization.layer_id = 0;
+
+ pdata = &quantization;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP: {
+ struct v4l2_ctrl *qpp;
+
+ qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP);
+
+ property_id = HAL_PARAM_VENC_SESSION_QP;
+ quantization.qpi = ctrl->val;
+ quantization.qpp = qpp->val;
+ /* Bframes are not supported for VPX */
+ quantization.qpb = 0;
+ quantization.layer_id = 0;
+
+ pdata = &quantization;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: {
+ struct v4l2_ctrl *qpi;
+
+ qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP);
+
+ property_id = HAL_PARAM_VENC_SESSION_QP;
+ quantization.qpp = ctrl->val;
+ quantization.qpi = qpi->val;
+ /* Bframes are not supported for VPX */
+ quantization.qpb = 0;
+ quantization.layer_id = 0;
+
+ pdata = &quantization;
+ break;
+ }
case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: {
struct v4l2_ctrl *qp_max;
@@ -2689,8 +2888,85 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
pdata = &qp_range;
break;
}
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP: {
+ struct v4l2_ctrl *qp_max;
+
+ qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP);
+ if (ctrl->val >= qp_max->val) {
+ dprintk(VIDC_ERR,
+ "Bad range: Min QP (%d) > Max QP(%d)\n",
+ ctrl->val, qp_max->val);
+ rc = -ERANGE;
+ break;
+ }
+
+ property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
+ qp_range.layer_id = 0;
+ qp_range.max_qp = qp_max->val;
+ qp_range.min_qp = ctrl->val;
+
+ pdata = &qp_range;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP: {
+ struct v4l2_ctrl *qp_min;
+
+ qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP);
+ if (ctrl->val <= qp_min->val) {
+ dprintk(VIDC_ERR,
+ "Bad range: Max QP (%d) < Min QP(%d)\n",
+ ctrl->val, qp_min->val);
+ rc = -ERANGE;
+ break;
+ }
+
+ property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
+ qp_range.layer_id = 0;
+ qp_range.max_qp = ctrl->val;
+ qp_range.min_qp = qp_min->val;
+
+ pdata = &qp_range;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_VPX_MIN_QP: {
+ struct v4l2_ctrl *qp_max;
+
+ qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MAX_QP);
+ if (ctrl->val >= qp_max->val) {
+ dprintk(VIDC_ERR,
+ "Bad range: Min QP (%d) > Max QP(%d)\n",
+ ctrl->val, qp_max->val);
+ rc = -ERANGE;
+ break;
+ }
+ property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
+ qp_range.layer_id = 0;
+ qp_range.max_qp = qp_max->val;
+ qp_range.min_qp = ctrl->val;
+ pdata = &qp_range;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP: {
+ struct v4l2_ctrl *qp_min;
+
+ qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MIN_QP);
+ if (ctrl->val <= qp_min->val) {
+ dprintk(VIDC_ERR,
+ "Bad range: Max QP (%d) < Min QP(%d)\n",
+ ctrl->val, qp_min->val);
+ rc = -ERANGE;
+ break;
+ }
+ property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
+ qp_range.layer_id = 0;
+ qp_range.max_qp = ctrl->val;
+ qp_range.min_qp = qp_min->val;
+ pdata = &qp_range;
+ break;
+ }
case V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP: {
struct v4l2_ctrl *qp_max;
+
qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP);
property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
qp_range.layer_id = 0;
@@ -2701,6 +2977,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
}
case V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP: {
struct v4l2_ctrl *qp_min;
+
qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP);
property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
qp_range.layer_id = 0;
@@ -3222,6 +3499,14 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
pdata = &baselayerid;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_CONFIG_QP:
+ /* Sanity check for the QP boundaries as we are using
+ * same control to set dynamic QP for all the codecs
+ */
+ rc = msm_venc_validate_qp_value(inst, ctrl);
+ if (rc) {
+ dprintk(VIDC_ERR, "Invalid QP Config QP Range\n");
+ break;
+ }
property_id = HAL_CONFIG_VENC_FRAME_QP;
frameqp = ctrl->val;
pdata = &frameqp;
@@ -3380,7 +3665,6 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
}
v4l2_ctrl_lock(ctrl);
-#undef TRY_GET_CTRL
if (!rc && property_id) {
dprintk(VIDC_DBG, "Control: HAL property=%x,ctrl_value=%d\n",
@@ -3393,6 +3677,59 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
return rc;
}
+static int msm_venc_validate_qp_value(struct msm_vidc_inst *inst,
+ struct v4l2_ctrl *ctrl)
+{
+ int rc = 0, min, max;
+ struct v4l2_ctrl *temp_ctrl = NULL;
+ int qp_value = ctrl->val;
+
+#define VALIDATE_BOUNDARIES(__min, __max, __val) ({\
+ int __rc = __val >= __min && \
+ __val <= __max; \
+ if (!__rc) \
+ dprintk(VIDC_ERR, "QP beyond range: min(%d) max(%d) val(%d)", \
+ __min, __max, __val); \
+ __rc; \
+})
+
+ switch (inst->fmts[CAPTURE_PORT].fourcc) {
+ case V4L2_PIX_FMT_VP8:
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MAX_QP);
+ max = temp_ctrl->maximum;
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MIN_QP);
+ min = temp_ctrl->minimum;
+ if (!VALIDATE_BOUNDARIES(min, max, qp_value))
+ rc = -EINVAL;
+ break;
+ case V4L2_PIX_FMT_H263:
+ case V4L2_PIX_FMT_MPEG4:
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP);
+ max = temp_ctrl->maximum;
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP);
+ min = temp_ctrl->minimum;
+ if (!VALIDATE_BOUNDARIES(min, max, qp_value))
+ rc = -EINVAL;
+ break;
+ case V4L2_PIX_FMT_H264:
+ case V4L2_PIX_FMT_HEVC:
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MAX_QP);
+ max = temp_ctrl->maximum;
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MIN_QP);
+ min = temp_ctrl->minimum;
+ if (!VALIDATE_BOUNDARIES(min, max, qp_value))
+ rc = -EINVAL;
+ break;
+ default:
+ dprintk(VIDC_ERR, "%s Invalid Codec\n", __func__);
+ return -EINVAL;
+ }
+ return rc;
+#undef VALIDATE_BOUNDARIES
+}
+
+#undef TRY_GET_CTRL
+
static int try_set_ext_ctrl(struct msm_vidc_inst *inst,
struct v4l2_ext_controls *ctrl)
{
@@ -3408,6 +3745,7 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst,
struct hal_aspect_ratio sar;
struct hal_bitrate bitrate;
struct hal_frame_size blur_res;
+ struct v4l2_ctrl *temp_ctrl;
if (!inst || !inst->core || !inst->core->device || !ctrl) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
@@ -3470,6 +3808,48 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst,
property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP;
pdata = &quant;
break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP:
+ /* Sanity check for the QP boundaries as we are using
+ * same control to set Initial QP for all the codecs
+ */
+ temp_ctrl->id =
+ V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP;
+ temp_ctrl->val = control[i].value;
+ rc = msm_venc_validate_qp_value(inst, temp_ctrl);
+ if (rc) {
+ dprintk(VIDC_ERR, "Invalid Initial I QP\n");
+ break;
+ }
+ quant.qpi = control[i].value;
+ property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP;
+ pdata = &quant;
+ break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP:
+ temp_ctrl->id =
+ V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP;
+ temp_ctrl->val = control[i].value;
+ rc = msm_venc_validate_qp_value(inst, temp_ctrl);
+ if (rc) {
+ dprintk(VIDC_ERR, "Invalid Initial P QP\n");
+ break;
+ }
+ quant.qpp = control[i].value;
+ property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP;
+ pdata = &quant;
+ break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP:
+ temp_ctrl->id =
+ V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP;
+ temp_ctrl->val = control[i].value;
+ rc = msm_venc_validate_qp_value(inst, temp_ctrl);
+ if (rc) {
+ dprintk(VIDC_ERR, "Invalid Initial B QP\n");
+ break;
+ }
+ quant.qpb = control[i].value;
+ property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP;
+ pdata = &quant;
+ break;
case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_X_RANGE:
search_range.i_frame.x_subsampled = control[i].value;
property_id = HAL_PARAM_VENC_SEARCH_RANGE;
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 4739fc999c82..bc72c4a56c91 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -4472,7 +4472,7 @@ static int venus_hfi_get_fw_info(void *dev, struct hal_fw_info *fw_info)
struct venus_hfi_device *device = dev;
u32 smem_block_size = 0;
u8 *smem_table_ptr;
- char version[VENUS_VERSION_LENGTH];
+ char version[VENUS_VERSION_LENGTH] = "";
const u32 smem_image_index_venus = 14 * 128;
if (!device || !fw_info) {
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 0f301903aa6f..63165d324fff 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -263,6 +263,8 @@ static void ite_set_carrier_params(struct ite_dev *dev)
if (allowance > ITE_RXDCR_MAX)
allowance = ITE_RXDCR_MAX;
+
+ use_demodulator = true;
}
}
diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c
index c945e4c2fbd4..ec30a004f319 100644
--- a/drivers/media/usb/siano/smsusb.c
+++ b/drivers/media/usb/siano/smsusb.c
@@ -200,22 +200,30 @@ static int smsusb_start_streaming(struct smsusb_device_t *dev)
static int smsusb_sendrequest(void *context, void *buffer, size_t size)
{
struct smsusb_device_t *dev = (struct smsusb_device_t *) context;
- struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) buffer;
- int dummy;
+ struct sms_msg_hdr *phdr;
+ int dummy, ret;
if (dev->state != SMSUSB_ACTIVE) {
pr_debug("Device not active yet\n");
return -ENOENT;
}
+ phdr = kmalloc(size, GFP_KERNEL);
+ if (!phdr)
+ return -ENOMEM;
+ memcpy(phdr, buffer, size);
+
pr_debug("sending %s(%d) size: %d\n",
smscore_translate_msg(phdr->msg_type), phdr->msg_type,
phdr->msg_length);
smsendian_handle_tx_message((struct sms_msg_data *) phdr);
- smsendian_handle_message_header((struct sms_msg_hdr *)buffer);
- return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2),
- buffer, size, &dummy, 1000);
+ smsendian_handle_message_header((struct sms_msg_hdr *)phdr);
+ ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2),
+ phdr, size, &dummy, 1000);
+
+ kfree(phdr);
+ return ret;
}
static char *smsusb1_fw_lkup[] = {
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index cfb868a48b5f..ff6feff21e94 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -416,7 +416,7 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
nextbuf = NULL;
spin_unlock_irqrestore(&queue->irqlock, flags);
- buf->state = buf->error ? VB2_BUF_STATE_ERROR : UVC_BUF_STATE_DONE;
+ buf->state = buf->error ? UVC_BUF_STATE_ERROR : UVC_BUF_STATE_DONE;
vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused);
vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE);
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index ca092ef75cfe..12ffa4192605 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -532,6 +532,14 @@ config HDCP_QSEECOM
Environment (QSEE) via the QSEECOM Driver and also calls the APIs
exposed by the HDMI driver to communicate with the Receiver.
+config PROFILER
+ tristate "Qualcomm Technologies, Inc. trustzone Communicator driver"
+ help
+ Provides a communication interface between userspace and
+ trustzone using Secure Channel Manager (SCM) interface.
+ It exposes APIs for userspace to get system profiling
+ information.
+
config VEXPRESS_SYSCFG
bool "Versatile Express System Configuration driver"
depends on VEXPRESS_CONFIG
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 40b7e3b603e2..c2941afc961e 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_SRAM) += sram.o
obj-y += mic/
obj-$(CONFIG_GENWQE) += genwqe/
obj-$(CONFIG_QSEECOM) += qseecom.o
+obj-$(CONFIG_PROFILER) += profiler.o
obj-$(CONFIG_HDCP_QSEECOM) += hdcp.o
ifdef CONFIG_COMPAT
obj-$(CONFIG_QSEECOM) += compat_qseecom.o
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index e59838231703..be74a25708b2 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -399,7 +399,7 @@ bool mei_cldev_enabled(struct mei_cl_device *cldev)
EXPORT_SYMBOL_GPL(mei_cldev_enabled);
/**
- * mei_cldev_enable_device - enable me client device
+ * mei_cldev_enable - enable me client device
* create connection with me client
*
* @cldev: me client device
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 958af84884b5..2ff39fbc70d1 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -698,7 +698,7 @@ void mei_host_client_init(struct work_struct *work)
pm_runtime_mark_last_busy(dev->dev);
dev_dbg(dev->dev, "rpm: autosuspend\n");
- pm_runtime_autosuspend(dev->dev);
+ pm_request_autosuspend(dev->dev);
}
/**
diff --git a/drivers/misc/profiler.c b/drivers/misc/profiler.c
new file mode 100644
index 000000000000..a2887fcefbab
--- /dev/null
+++ b/drivers/misc/profiler.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "PROFILER: %s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/socinfo.h>
+#include <asm/cacheflush.h>
+#include <linux/delay.h>
+#include <soc/qcom/profiler.h>
+
+#include <linux/compat.h>
+
+#define PROFILER_DEV "profiler"
+
+static struct class *driver_class;
+static dev_t profiler_device_no;
+
+struct profiler_control {
+ struct device *pdev;
+ struct cdev cdev;
+};
+
+static struct profiler_control profiler;
+
+struct profiler_dev_handle {
+ bool released;
+ int abort;
+ atomic_t ioctl_count;
+};
+
+
+static int profiler_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id,
+ const void *req_buf, void *resp_buf)
+{
+ int ret = 0;
+ uint32_t qseos_cmd_id = 0;
+ struct scm_desc desc = {0};
+
+ if (!req_buf || !resp_buf) {
+ pr_err("Invalid buffer pointer\n");
+ return -EINVAL;
+ }
+ qseos_cmd_id = *(uint32_t *)req_buf;
+
+ switch (svc_id) {
+
+ case SCM_SVC_BW:
+ switch (qseos_cmd_id) {
+ case TZ_BW_SVC_START_ID:
+ case TZ_BW_SVC_GET_ID:
+ case TZ_BW_SVC_STOP_ID:
+ /* Send the command to TZ */
+ desc.arginfo = SCM_ARGS(4, SCM_RW, SCM_VAL,
+ SCM_RW, SCM_VAL);
+ desc.args[0] = virt_to_phys(&
+ (((struct tz_bw_svc_buf *)
+ req_buf)->bwreq));
+ desc.args[1] = ((struct tz_bw_svc_buf *)
+ req_buf)->req_size;
+ desc.args[2] = virt_to_phys(&
+ ((struct tz_bw_svc_buf *)
+ req_buf)->bwresp);
+ desc.args[3] = sizeof(struct tz_bw_svc_resp);
+
+ ret = scm_call2(SCM_SIP_FNID(SCM_SVC_INFO,
+ TZ_SVC_BW_PROF_ID), &desc);
+ break;
+ default:
+ pr_err("cmd_id %d is not supported by scm_call2.\n",
+ qseos_cmd_id);
+ ret = -EINVAL;
+ } /*end of switch (qsee_cmd_id) */
+ break;
+ default:
+ pr_err("svc_id 0x%x is not supported by armv8 scm_call2.\n",
+ svc_id);
+ ret = -EINVAL;
+ break;
+ } /*end of switch svc_id */
+ return ret;
+}
+
+
+static int profiler_scm_call(u32 svc_id, u32 tz_cmd_id, const void *cmd_buf,
+ size_t cmd_len, void *resp_buf, size_t resp_len)
+{
+ if (!is_scm_armv8())
+ return scm_call(svc_id, tz_cmd_id, cmd_buf, cmd_len,
+ resp_buf, resp_len);
+ else
+ return profiler_scm_call2(svc_id, tz_cmd_id, cmd_buf, resp_buf);
+}
+
+static int bw_profiling_command(void *req)
+{
+ struct tz_bw_svc_resp *bw_resp = NULL;
+ uint32_t cmd_id = 0;
+ int ret;
+
+ cmd_id = *(uint32_t *)req;
+ bw_resp = &((struct tz_bw_svc_buf *)req)->bwresp;
+ /* Flush buffers from cache to memory. */
+ dmac_flush_range(req, req +
+ PAGE_ALIGN(sizeof(union tz_bw_svc_req)));
+ dmac_flush_range((void *)bw_resp, ((void *)bw_resp) +
+ sizeof(struct tz_bw_svc_resp));
+ ret = profiler_scm_call(SCM_SVC_BW, TZ_SVC_BW_PROF_ID, req,
+ sizeof(struct tz_bw_svc_buf),
+ bw_resp, sizeof(struct tz_bw_svc_resp));
+ if (ret) {
+ pr_err("profiler_scm_call failed with err: %d\n", ret);
+ return -EINVAL;
+ }
+ /* Invalidate cache. */
+ dmac_inv_range((void *)bw_resp, ((void *)bw_resp) +
+ sizeof(struct tz_bw_svc_resp));
+ /* Verify cmd id and Check that request succeeded.*/
+ if ((bw_resp->status != E_BW_SUCCESS) ||
+ (cmd_id != bw_resp->cmd_id)) {
+ ret = -1;
+ pr_err("Status: %d,Cmd: %d\n",
+ bw_resp->status,
+ bw_resp->cmd_id);
+ }
+ return ret;
+}
+
+static int bw_profiling_start(struct tz_bw_svc_buf *bwbuf)
+{
+ struct tz_bw_svc_start_req *bwstartreq = NULL;
+
+ bwstartreq = (struct tz_bw_svc_start_req *) &bwbuf->bwreq;
+ /* Populate request data */
+ bwstartreq->cmd_id = TZ_BW_SVC_START_ID;
+ bwstartreq->version = TZ_BW_SVC_VERSION;
+ bwbuf->req_size = sizeof(struct tz_bw_svc_start_req);
+ return bw_profiling_command(bwbuf);
+}
+
+static int bw_profiling_get(void __user *argp, struct tz_bw_svc_buf *bwbuf)
+{
+ struct tz_bw_svc_get_req *bwgetreq = NULL;
+ int ret;
+ char *buf = NULL;
+ const int numberofregs = 3;
+ struct profiler_bw_cntrs_req cnt_buf;
+
+ bwgetreq = (struct tz_bw_svc_get_req *) &bwbuf->bwreq;
+ /* Allocate memory for get buffer */
+ buf = kzalloc(PAGE_ALIGN(numberofregs * sizeof(uint32_t)), GFP_KERNEL);
+ if (buf == NULL) {
+ ret = -ENOMEM;
+ pr_err(" Failed to allocate memory\n");
+ return ret;
+ }
+ /* Populate request data */
+ bwgetreq->cmd_id = TZ_BW_SVC_GET_ID;
+ bwgetreq->buf_ptr = (uint64_t) virt_to_phys(buf);
+ bwgetreq->buf_size = numberofregs * sizeof(uint32_t);
+ bwbuf->req_size = sizeof(struct tz_bw_svc_get_req);
+ dmac_flush_range(buf, ((void *)buf) + PAGE_ALIGN(bwgetreq->buf_size));
+ ret = bw_profiling_command(bwbuf);
+ if (ret) {
+ pr_err("bw_profiling_command failed\n");
+ return ret;
+ }
+ dmac_inv_range(buf, ((void *)buf) + PAGE_ALIGN(bwgetreq->buf_size));
+ cnt_buf.total = *(uint32_t *) (buf + 0 * sizeof(uint32_t));
+ cnt_buf.cpu = *(uint32_t *) (buf + 1 * sizeof(uint32_t));
+ cnt_buf.gpu = *(uint32_t *) (buf + 2 * sizeof(uint32_t));
+ if (copy_to_user(argp, &cnt_buf, sizeof(struct profiler_bw_cntrs_req)))
+ pr_err("copy_to_user failed\n");
+ /* Free memory for response */
+ if (buf != NULL) {
+ kfree(buf);
+ buf = NULL;
+ }
+ return ret;
+}
+
+static int bw_profiling_stop(struct tz_bw_svc_buf *bwbuf)
+{
+ struct tz_bw_svc_stop_req *bwstopreq = NULL;
+
+ bwstopreq = (struct tz_bw_svc_stop_req *) &bwbuf->bwreq;
+ /* Populate request data */
+ bwstopreq->cmd_id = TZ_BW_SVC_STOP_ID;
+ bwbuf->req_size = sizeof(struct tz_bw_svc_stop_req);
+ return bw_profiling_command(bwbuf);
+}
+
+
+static int profiler_get_bw_info(void __user *argp)
+{
+ int ret = 0;
+ struct tz_bw_svc_buf *bwbuf = NULL;
+ struct profiler_bw_cntrs_req cnt_buf;
+
+ ret = copy_from_user(&cnt_buf, argp,
+ sizeof(struct profiler_bw_cntrs_req));
+ if (ret)
+ return ret;
+ /* Allocate memory for request */
+ bwbuf = kzalloc(PAGE_ALIGN(sizeof(struct tz_bw_svc_buf)), GFP_KERNEL);
+ if (bwbuf == NULL)
+ return -ENOMEM;
+ switch (cnt_buf.cmd) {
+ case TZ_BW_SVC_START_ID:
+ ret = bw_profiling_start(bwbuf);
+ if (ret)
+ pr_err("bw_profiling_start Failed with ret: %d\n", ret);
+ break;
+ case TZ_BW_SVC_GET_ID:
+ ret = bw_profiling_get(argp, bwbuf);
+ if (ret)
+ pr_err("bw_profiling_get Failed with ret: %d\n", ret);
+ break;
+ case TZ_BW_SVC_STOP_ID:
+ ret = bw_profiling_stop(bwbuf);
+ if (ret)
+ pr_err("bw_profiling_stop Failed with ret: %d\n", ret);
+ break;
+ default:
+ pr_err("Invalid IOCTL: 0x%x\n", cnt_buf.cmd);
+ ret = -EINVAL;
+ }
+ /* Free memory for command */
+ if (bwbuf != NULL) {
+ kfree(bwbuf);
+ bwbuf = NULL;
+ }
+ return ret;
+}
+
+static int profiler_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ struct profiler_dev_handle *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ file->private_data = data;
+ data->abort = 0;
+ data->released = false;
+ atomic_set(&data->ioctl_count, 0);
+ return ret;
+}
+
+static int compat_get_profiler_bw_info(
+ struct compat_profiler_bw_cntrs_req __user *data32,
+ struct profiler_bw_cntrs_req __user *data)
+{
+ compat_uint_t total;
+ compat_uint_t cpu;
+ compat_uint_t gpu;
+ compat_uint_t cmd;
+ int err;
+
+ err = get_user(total, &data32->total);
+ err |= put_user(total, &data->total);
+ err |= get_user(gpu, &data32->gpu);
+ err |= put_user(gpu, &data->gpu);
+ err |= get_user(cpu, &data32->cpu);
+ err |= put_user(cpu, &data->cpu);
+ err |= get_user(cmd, &data32->cmd);
+ err |= put_user(cmd, &data->cmd);
+ return err;
+}
+
+static int compat_put_profiler_bw_info(
+ struct compat_profiler_bw_cntrs_req __user *data32,
+ struct profiler_bw_cntrs_req __user *data)
+{
+ compat_uint_t total;
+ compat_uint_t cpu;
+ compat_uint_t gpu;
+ compat_uint_t cmd;
+ int err;
+
+ err = get_user(total, &data->total);
+ err |= put_user(total, &data32->total);
+ err |= get_user(gpu, &data->gpu);
+ err |= put_user(gpu, &data32->gpu);
+ err |= get_user(cpu, &data->cpu);
+ err |= put_user(cpu, &data32->cpu);
+ err |= get_user(cmd, &data->cmd);
+ err |= put_user(cmd, &data32->cmd);
+ return err;
+}
+
+static unsigned int convert_cmd(unsigned int cmd)
+{
+ switch (cmd) {
+ case COMPAT_PROFILER_IOCTL_GET_BW_INFO:
+ return PROFILER_IOCTL_GET_BW_INFO;
+
+ default:
+ return cmd;
+ }
+}
+
+
+long profiler_ioctl(struct file *file, unsigned cmd, unsigned long arg)
+{
+ int ret = 0;
+ struct profiler_dev_handle *data = file->private_data;
+ void __user *argp = (void __user *) arg;
+
+ if (!data) {
+ pr_err("Invalid/uninitialized device handle\n");
+ return -EINVAL;
+ }
+
+ if (data->abort) {
+ pr_err("Aborting profiler driver\n");
+ return -ENODEV;
+ }
+
+ switch (cmd) {
+ case PROFILER_IOCTL_GET_BW_INFO:
+ atomic_inc(&data->ioctl_count);
+ ret = profiler_get_bw_info(argp);
+ if (ret)
+ pr_err("failed get system bandwidth info: %d\n", ret);
+ atomic_dec(&data->ioctl_count);
+ break;
+ default:
+ pr_err("Invalid IOCTL: 0x%x\n", cmd);
+ return -EINVAL;
+ }
+ return ret;
+}
+
+long compat_profiler_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ long ret;
+
+ switch (cmd) {
+ case COMPAT_PROFILER_IOCTL_GET_BW_INFO:{
+ struct compat_profiler_bw_cntrs_req __user *data32;
+ struct profiler_bw_cntrs_req __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (data == NULL)
+ return -EFAULT;
+ err = compat_get_profiler_bw_info(data32, data);
+ if (err)
+ return err;
+ ret = profiler_ioctl(file, convert_cmd(cmd),
+ (unsigned long)data);
+ err = compat_put_profiler_bw_info(data32, data);
+ return ret ? ret : err;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+
+static int profiler_release(struct inode *inode, struct file *file)
+{
+ pr_info("profiler release\n");
+ return 0;
+}
+
+static const struct file_operations profiler_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = profiler_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = compat_profiler_ioctl,
+#endif
+ .open = profiler_open,
+ .release = profiler_release
+};
+
+static int profiler_init(void)
+{
+ int rc;
+ struct device *class_dev;
+
+ rc = alloc_chrdev_region(&profiler_device_no, 0, 1, PROFILER_DEV);
+ if (rc < 0) {
+ pr_err("alloc_chrdev_region failed %d\n", rc);
+ return rc;
+ }
+
+ driver_class = class_create(THIS_MODULE, PROFILER_DEV);
+ if (IS_ERR(driver_class)) {
+ rc = -ENOMEM;
+ pr_err("class_create failed %d\n", rc);
+ goto exit_unreg_chrdev_region;
+ }
+
+ class_dev = device_create(driver_class, NULL, profiler_device_no, NULL,
+ PROFILER_DEV);
+ if (IS_ERR(class_dev)) {
+ pr_err("class_device_create failed %d\n", rc);
+ rc = -ENOMEM;
+ goto exit_destroy_class;
+ }
+
+ cdev_init(&profiler.cdev, &profiler_fops);
+ profiler.cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&profiler.cdev, MKDEV(MAJOR(profiler_device_no), 0), 1);
+ if (rc < 0) {
+ pr_err("%s: cdev_add failed %d\n", __func__, rc);
+ goto exit_destroy_device;
+ }
+
+ profiler.pdev = class_dev;
+ return 0;
+
+exit_destroy_device:
+ device_destroy(driver_class, profiler_device_no);
+exit_destroy_class:
+ class_destroy(driver_class);
+exit_unreg_chrdev_region:
+ unregister_chrdev_region(profiler_device_no, 1);
+ return rc;
+}
+
+static void profiler_exit(void)
+{
+ pr_info("Exiting from profiler\n");
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Qualcomm Technologies, Inc. trustzone Communicator");
+
+module_init(profiler_init);
+module_exit(profiler_exit);
diff --git a/drivers/misc/qpnp-misc.c b/drivers/misc/qpnp-misc.c
index 5b321b17c43e..c1570e32f749 100644
--- a/drivers/misc/qpnp-misc.c
+++ b/drivers/misc/qpnp-misc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014,2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014,2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,7 +14,7 @@
#include <linux/module.h>
#include <linux/err.h>
#include <linux/slab.h>
-#include <linux/spmi.h>
+#include <linux/regmap.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/qpnp-misc.h>
@@ -45,8 +45,7 @@ struct qpnp_misc_version {
* exclusion between probing and accessing misc
* driver information
* @dev: Device pointer to the misc device
- * @resource: Resource pointer that holds base address
- * @spmi: Spmi pointer which holds spmi information
+ * @regmap: Regmap pointer to the misc device
* @version: struct that holds the subtype and dig_major_rev
* of the chip.
*/
@@ -54,10 +53,10 @@ struct qpnp_misc_dev {
struct list_head list;
struct mutex mutex;
struct device *dev;
- struct resource *resource;
- struct spmi_device *spmi;
+ struct regmap *regmap;
struct qpnp_misc_version version;
+ u32 base;
u8 pwm_sel;
bool enable_gp_driver;
};
@@ -83,26 +82,29 @@ static struct qpnp_misc_version irq_support_version[] = {
{0x16, 0x00}, /* PMDCALIFORNIUM */
};
-static int qpnp_write_byte(struct spmi_device *spmi, u16 addr, u8 val)
+static int qpnp_write_byte(struct qpnp_misc_dev *mdev, u16 addr, u8 val)
{
int rc;
- rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, addr, &val, 1);
+ rc = regmap_write(mdev->regmap, mdev->base + addr, val);
if (rc)
- pr_err("SPMI write failed rc=%d\n", rc);
+ pr_err("regmap write failed rc=%d\n", rc);
return rc;
}
-static int qpnp_read_byte(struct spmi_device *spmi, u16 addr, u8 *val)
+static int qpnp_read_byte(struct qpnp_misc_dev *mdev, u16 addr, u8 *val)
{
+ unsigned int temp;
int rc;
- rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, addr, val, 1);
+ rc = regmap_read(mdev->regmap, mdev->base + addr, &temp);
if (rc) {
- pr_err("SPMI read failed rc=%d\n", rc);
+ pr_err("regmap read failed rc=%d\n", rc);
return rc;
}
+
+ *val = (u8)temp;
return rc;
}
@@ -128,6 +130,47 @@ static bool __misc_irqs_available(struct qpnp_misc_dev *dev)
return 1;
}
+int qpnp_misc_read_reg(struct device_node *node, u16 addr, u8 *val)
+{
+ struct qpnp_misc_dev *mdev = NULL;
+ struct qpnp_misc_dev *mdev_found = NULL;
+ int rc;
+ u8 temp;
+
+ if (IS_ERR_OR_NULL(node)) {
+ pr_err("Invalid device node pointer\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&qpnp_misc_dev_list_mutex);
+ list_for_each_entry(mdev, &qpnp_misc_dev_list, list) {
+ if (mdev->dev->of_node == node) {
+ mdev_found = mdev;
+ break;
+ }
+ }
+ mutex_unlock(&qpnp_misc_dev_list_mutex);
+
+ if (!mdev_found) {
+ /*
+ * No MISC device was found. This API should only
+ * be called by drivers which have specified the
+ * misc phandle in their device tree node.
+ */
+ pr_err("no probed misc device found\n");
+ return -EPROBE_DEFER;
+ }
+
+ rc = qpnp_read_byte(mdev, addr, &temp);
+ if (rc < 0) {
+ dev_err(mdev->dev, "Failed to read addr %x, rc=%d\n", addr, rc);
+ return rc;
+ }
+
+ *val = temp;
+ return 0;
+}
+
int qpnp_misc_irqs_available(struct device *consumer_dev)
{
struct device_node *misc_node = NULL;
@@ -168,16 +211,24 @@ int qpnp_misc_irqs_available(struct device *consumer_dev)
static int qpnp_misc_dt_init(struct qpnp_misc_dev *mdev)
{
+ struct device_node *node = mdev->dev->of_node;
u32 val;
+ int rc;
+
+ rc = of_property_read_u32(node, "reg", &mdev->base);
+ if (rc < 0 || !mdev->base) {
+ dev_err(mdev->dev, "Base address not defined or invalid\n");
+ return -EINVAL;
+ }
- if (!of_property_read_u32(mdev->dev->of_node, "qcom,pwm-sel", &val)) {
+ if (!of_property_read_u32(node, "qcom,pwm-sel", &val)) {
if (val > PWM_SEL_MAX) {
dev_err(mdev->dev, "Invalid value for pwm-sel\n");
return -EINVAL;
}
mdev->pwm_sel = (u8)val;
}
- mdev->enable_gp_driver = of_property_read_bool(mdev->dev->of_node,
+ mdev->enable_gp_driver = of_property_read_bool(node,
"qcom,enable-gp-driver");
WARN((mdev->pwm_sel > 0 && !mdev->enable_gp_driver),
@@ -197,18 +248,15 @@ static int qpnp_misc_config(struct qpnp_misc_dev *mdev)
switch (version_name) {
case PMDCALIFORNIUM:
if (mdev->pwm_sel > 0 && mdev->enable_gp_driver) {
- rc = qpnp_write_byte(mdev->spmi,
- mdev->resource->start + REG_PWM_SEL,
- mdev->pwm_sel);
+ rc = qpnp_write_byte(mdev, REG_PWM_SEL, mdev->pwm_sel);
if (rc < 0) {
dev_err(mdev->dev,
"Failed to write PWM_SEL reg\n");
return rc;
}
- rc = qpnp_write_byte(mdev->spmi,
- mdev->resource->start + REG_GP_DRIVER_EN,
- GP_DRIVER_EN_BIT);
+ rc = qpnp_write_byte(mdev, REG_GP_DRIVER_EN,
+ GP_DRIVER_EN_BIT);
if (rc < 0) {
dev_err(mdev->dev,
"Failed to write GP_DRIVER_EN reg\n");
@@ -223,35 +271,38 @@ static int qpnp_misc_config(struct qpnp_misc_dev *mdev)
return 0;
}
-static int qpnp_misc_probe(struct spmi_device *spmi)
+static int qpnp_misc_probe(struct platform_device *pdev)
{
- struct resource *resource;
struct qpnp_misc_dev *mdev = ERR_PTR(-EINVAL);
int rc;
- resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
- if (!resource) {
- pr_err("Unable to get spmi resource for MISC\n");
- return -EINVAL;
- }
-
- mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+ mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL);
if (!mdev)
return -ENOMEM;
- mdev->spmi = spmi;
- mdev->dev = &(spmi->dev);
- mdev->resource = resource;
+ mdev->dev = &pdev->dev;
+ mdev->regmap = dev_get_regmap(mdev->dev->parent, NULL);
+ if (!mdev->regmap) {
+ dev_err(mdev->dev, "Parent regmap is unavailable\n");
+ return -ENXIO;
+ }
+
+ rc = qpnp_misc_dt_init(mdev);
+ if (rc < 0) {
+ dev_err(mdev->dev,
+ "Error reading device tree properties, rc=%d\n", rc);
+ return rc;
+ }
+
- rc = qpnp_read_byte(spmi, resource->start + REG_SUBTYPE,
- &mdev->version.subtype);
+ rc = qpnp_read_byte(mdev, REG_SUBTYPE, &mdev->version.subtype);
if (rc < 0) {
dev_err(mdev->dev, "Failed to read subtype, rc=%d\n", rc);
return rc;
}
- rc = qpnp_read_byte(spmi, resource->start + REG_DIG_MAJOR_REV,
- &mdev->version.dig_major_rev);
+ rc = qpnp_read_byte(mdev, REG_DIG_MAJOR_REV,
+ &mdev->version.dig_major_rev);
if (rc < 0) {
dev_err(mdev->dev, "Failed to read dig_major_rev, rc=%d\n", rc);
return rc;
@@ -261,13 +312,6 @@ static int qpnp_misc_probe(struct spmi_device *spmi)
list_add_tail(&mdev->list, &qpnp_misc_dev_list);
mutex_unlock(&qpnp_misc_dev_list_mutex);
- rc = qpnp_misc_dt_init(mdev);
- if (rc < 0) {
- dev_err(mdev->dev,
- "Error reading device tree properties, rc=%d\n", rc);
- return rc;
- }
-
rc = qpnp_misc_config(mdev);
if (rc < 0) {
dev_err(mdev->dev,
@@ -279,7 +323,7 @@ static int qpnp_misc_probe(struct spmi_device *spmi)
return 0;
}
-static struct spmi_driver qpnp_misc_driver = {
+static struct platform_driver qpnp_misc_driver = {
.probe = qpnp_misc_probe,
.driver = {
.name = QPNP_MISC_DEV_NAME,
@@ -290,15 +334,15 @@ static struct spmi_driver qpnp_misc_driver = {
static int __init qpnp_misc_init(void)
{
- return spmi_driver_register(&qpnp_misc_driver);
+ return platform_driver_register(&qpnp_misc_driver);
}
static void __exit qpnp_misc_exit(void)
{
- return spmi_driver_unregister(&qpnp_misc_driver);
+ return platform_driver_unregister(&qpnp_misc_driver);
}
-module_init(qpnp_misc_init);
+subsys_initcall(qpnp_misc_init);
module_exit(qpnp_misc_exit);
MODULE_DESCRIPTION(QPNP_MISC_DEV_NAME);
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 78f03fc75761..9855bee67627 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -2333,7 +2333,13 @@ static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
ret);
goto loadapp_err;
}
-
+ if (load_img_req.mdt_len > len || load_img_req.img_len > len) {
+ pr_err("ion len %zu is smaller than mdt_len %u or img_len %u\n",
+ len, load_img_req.mdt_len,
+ load_img_req.img_len);
+ ret = -EINVAL;
+ goto loadapp_err;
+ }
/* Populate the structure for sending scm call to load image */
if (qseecom.qsee_version < QSEE_VERSION_40) {
load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
@@ -5149,6 +5155,12 @@ static int qseecom_load_external_elf(struct qseecom_dev_handle *data,
ret);
return ret;
}
+ if (load_img_req.mdt_len > len || load_img_req.img_len > len) {
+ pr_err("ion len %zu is smaller than mdt_len %u or img_len %u\n",
+ len, load_img_req.mdt_len,
+ load_img_req.img_len);
+ return ret;
+ }
/* Populate the structure for sending scm call to load image */
if (qseecom.qsee_version < QSEE_VERSION_40) {
load_req.qsee_cmd_id = QSEOS_LOAD_EXTERNAL_ELF_COMMAND;
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index ce0ecd1e9b7a..5d6c44b00bc2 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -875,6 +875,14 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
cmd.arg = idata->ic.arg;
cmd.flags = idata->ic.flags;
+ if (idata->ic.postsleep_max_us < idata->ic.postsleep_min_us) {
+ pr_err("%s: min value: %u must not be greater than max value: %u\n",
+ __func__, idata->ic.postsleep_min_us,
+ idata->ic.postsleep_max_us);
+ WARN_ON(1);
+ return -EPERM;
+ }
+
if (idata->buf_bytes) {
data.sg = &sg;
data.sg_len = 1;
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 9766dcc95602..12c66919f06f 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -791,7 +791,7 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test,
struct mmc_async_req *cur_areq = &test_areq[0].areq;
struct mmc_async_req *other_areq = &test_areq[1].areq;
int i;
- int ret;
+ int ret = RESULT_OK;
test_areq[0].test = test;
test_areq[1].test = test;
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 4567b7526469..542f1733d0dd 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2032,10 +2032,10 @@ reinit:
err = mmc_select_hs400(card);
if (err)
goto free_card;
- } else if (mmc_card_hs(card)) {
+ } else {
/* Select the desired bus width optionally */
err = mmc_select_bus_width(card);
- if (!IS_ERR_VALUE(err)) {
+ if (!IS_ERR_VALUE(err) && mmc_card_hs(card)) {
err = mmc_select_hs_ddr(card);
if (err)
goto free_card;
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index 44ecebd1ea8c..c8b8ac66ff7e 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -309,6 +309,9 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host)
cmd0 = BF_SSP(cmd->opcode, CMD0_CMD);
cmd1 = cmd->arg;
+ if (cmd->opcode == MMC_STOP_TRANSMISSION)
+ cmd0 |= BM_SSP_CMD0_APPEND_8CYC;
+
if (host->sdio_irq_en) {
ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN;
@@ -417,8 +420,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
ssp->base + HW_SSP_BLOCK_SIZE);
}
- if ((cmd->opcode == MMC_STOP_TRANSMISSION) ||
- (cmd->opcode == SD_IO_RW_EXTENDED))
+ if (cmd->opcode == SD_IO_RW_EXTENDED)
cmd0 |= BM_SSP_CMD0_APPEND_8CYC;
cmd1 = cmd->arg;
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 7274a6d2cce0..3e51c9a86250 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -3304,6 +3304,21 @@ static void sdhci_msm_cmdq_dump_debug_ram(struct sdhci_host *host)
pr_err("-------------------------\n");
}
+static void sdhci_msm_cache_debug_data(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ struct sdhci_msm_debug_data *cached_data = &msm_host->cached_data;
+
+ memcpy(&cached_data->copy_mmc, msm_host->mmc,
+ sizeof(struct mmc_host));
+ if (msm_host->mmc->card)
+ memcpy(&cached_data->copy_card, msm_host->mmc->card,
+ sizeof(struct mmc_card));
+ memcpy(&cached_data->copy_host, host,
+ sizeof(struct sdhci_host));
+}
+
void sdhci_msm_dump_vendor_regs(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -3316,6 +3331,7 @@ void sdhci_msm_dump_vendor_regs(struct sdhci_host *host)
u32 debug_reg[MAX_TEST_BUS] = {0};
u32 sts = 0;
+ sdhci_msm_cache_debug_data(host);
pr_info("----------- VENDOR REGISTER DUMP -----------\n");
if (host->cq_host)
sdhci_msm_cmdq_dump_debug_ram(host);
@@ -3897,8 +3913,8 @@ void sdhci_msm_pm_qos_cpu_init(struct sdhci_host *host,
group->req.type = PM_QOS_REQ_AFFINE_CORES;
cpumask_copy(&group->req.cpus_affine,
&msm_host->pdata->pm_qos_data.cpu_group_map.mask[i]);
- /* For initialization phase, set the performance mode latency */
- group->latency = latency[i].latency[SDHCI_PERFORMANCE_MODE];
+ /* We set default latency here for all pm_qos cpu groups. */
+ group->latency = PM_QOS_DEFAULT_VALUE;
pm_qos_add_request(&group->req, PM_QOS_CPU_DMA_LATENCY,
group->latency);
pr_info("%s (): voted for group #%d (mask=0x%lx) latency=%d (0x%p)\n",
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index c26636198a22..2e4f2179378e 100644
--- a/drivers/mmc/host/sdhci-msm.h
+++ b/drivers/mmc/host/sdhci-msm.h
@@ -171,6 +171,12 @@ struct sdhci_msm_ice_data {
int state;
};
+struct sdhci_msm_debug_data {
+ struct mmc_host copy_mmc;
+ struct mmc_card copy_card;
+ struct sdhci_host copy_host;
+};
+
struct sdhci_msm_host {
struct platform_device *pdev;
void __iomem *core_mem; /* MSM SDCC mapped address */
@@ -186,6 +192,7 @@ struct sdhci_msm_host {
atomic_t clks_on; /* Set if clocks are enabled */
struct sdhci_msm_pltfm_data *pdata;
struct mmc_host *mmc;
+ struct sdhci_msm_debug_data cached_data;
struct sdhci_pltfm_data sdhci_msm_pdata;
u32 curr_pwr_state;
u32 curr_io_level;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 083cb8cdf1ca..44633dc5d2be 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2507,7 +2507,27 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ sdhci_do_reset(host, SDHCI_RESET_CMD);
+ sdhci_do_reset(host, SDHCI_RESET_DATA);
+
err = -EIO;
+
+ if (cmd.opcode != MMC_SEND_TUNING_BLOCK_HS200)
+ goto out;
+
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = MMC_STOP_TRANSMISSION;
+ cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+ cmd.busy_timeout = 50;
+ mmc_wait_for_cmd(mmc, &cmd, 0);
+
+ spin_lock_irqsave(&host->lock, flags);
+
goto out;
}
@@ -3283,7 +3303,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
pr_err("%s: Card is consuming too much power!\n",
mmc_hostname(host->mmc));
- if (intmask & SDHCI_INT_CARD_INT) {
+ if ((intmask & SDHCI_INT_CARD_INT) &&
+ (host->ier & SDHCI_INT_CARD_INT)) {
sdhci_enable_sdio_irq_nolock(host, false);
host->thread_isr |= SDHCI_INT_CARD_INT;
result = IRQ_WAKE_THREAD;
diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c
index f9fa3fad728e..2051f28ddac6 100644
--- a/drivers/mtd/maps/pmcmsp-flash.c
+++ b/drivers/mtd/maps/pmcmsp-flash.c
@@ -139,15 +139,13 @@ static int __init init_msp_flash(void)
}
msp_maps[i].bankwidth = 1;
- msp_maps[i].name = kmalloc(7, GFP_KERNEL);
+ msp_maps[i].name = kstrndup(flash_name, 7, GFP_KERNEL);
if (!msp_maps[i].name) {
iounmap(msp_maps[i].virt);
kfree(msp_parts[i]);
goto cleanup_loop;
}
- msp_maps[i].name = strncpy(msp_maps[i].name, flash_name, 7);
-
for (j = 0; j < pcnt; j++) {
part_name[5] = '0' + i;
part_name[7] = '0' + j;
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 093fd917274f..132d0d04726c 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -531,7 +531,7 @@ config MTD_NAND_FSMC
Flexible Static Memory Controller (FSMC)
config MTD_NAND_XWAY
- tristate "Support for NAND on Lantiq XWAY SoC"
+ bool "Support for NAND on Lantiq XWAY SoC"
depends on LANTIQ && SOC_TYPE_XWAY
select MTD_NAND_PLATFORM
help
diff --git a/drivers/net/can/c_can/c_can_pci.c b/drivers/net/can/c_can/c_can_pci.c
index 7be393c96b1a..cf7c18947189 100644
--- a/drivers/net/can/c_can/c_can_pci.c
+++ b/drivers/net/can/c_can/c_can_pci.c
@@ -161,6 +161,7 @@ static int c_can_pci_probe(struct pci_dev *pdev,
dev->irq = pdev->irq;
priv->base = addr;
+ priv->device = &pdev->dev;
if (!c_can_pci_data->freq) {
dev_err(&pdev->dev, "no clock frequency defined\n");
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
index 680d1ff07a55..6749b1829469 100644
--- a/drivers/net/can/ti_hecc.c
+++ b/drivers/net/can/ti_hecc.c
@@ -948,7 +948,12 @@ static int ti_hecc_probe(struct platform_device *pdev)
netif_napi_add(ndev, &priv->napi, ti_hecc_rx_poll,
HECC_DEF_NAPI_WEIGHT);
- clk_enable(priv->clk);
+ err = clk_prepare_enable(priv->clk);
+ if (err) {
+ dev_err(&pdev->dev, "clk_prepare_enable() failed\n");
+ goto probe_exit_clk;
+ }
+
err = register_candev(ndev);
if (err) {
dev_err(&pdev->dev, "register_candev() failed\n");
@@ -981,7 +986,7 @@ static int ti_hecc_remove(struct platform_device *pdev)
struct ti_hecc_priv *priv = netdev_priv(ndev);
unregister_candev(ndev);
- clk_disable(priv->clk);
+ clk_disable_unprepare(priv->clk);
clk_put(priv->clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
iounmap(priv->base);
@@ -1006,7 +1011,7 @@ static int ti_hecc_suspend(struct platform_device *pdev, pm_message_t state)
hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_PDR);
priv->can.state = CAN_STATE_SLEEPING;
- clk_disable(priv->clk);
+ clk_disable_unprepare(priv->clk);
return 0;
}
@@ -1015,8 +1020,11 @@ static int ti_hecc_resume(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
struct ti_hecc_priv *priv = netdev_priv(dev);
+ int err;
- clk_enable(priv->clk);
+ err = clk_prepare_enable(priv->clk);
+ if (err)
+ return err;
hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_PDR);
priv->can.state = CAN_STATE_ERROR_ACTIVE;
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
index 5a2e341a6d1e..91be4575b524 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
@@ -872,23 +872,25 @@ lbl_free_candev:
static void peak_usb_disconnect(struct usb_interface *intf)
{
struct peak_usb_device *dev;
+ struct peak_usb_device *dev_prev_siblings;
/* unregister as many netdev devices as siblings */
- for (dev = usb_get_intfdata(intf); dev; dev = dev->prev_siblings) {
+ for (dev = usb_get_intfdata(intf); dev; dev = dev_prev_siblings) {
struct net_device *netdev = dev->netdev;
char name[IFNAMSIZ];
+ dev_prev_siblings = dev->prev_siblings;
dev->state &= ~PCAN_USB_STATE_CONNECTED;
strncpy(name, netdev->name, IFNAMSIZ);
unregister_netdev(netdev);
- free_candev(netdev);
kfree(dev->cmd_buf);
dev->next_siblings = NULL;
if (dev->adapter->dev_free)
dev->adapter->dev_free(dev);
+ free_candev(netdev);
dev_info(&intf->dev, "%s removed\n", name);
}
diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c
index a731720f1d13..449b2a47f9a8 100644
--- a/drivers/net/can/usb/usb_8dev.c
+++ b/drivers/net/can/usb/usb_8dev.c
@@ -954,8 +954,8 @@ static int usb_8dev_probe(struct usb_interface *intf,
for (i = 0; i < MAX_TX_URBS; i++)
priv->tx_contexts[i].echo_index = MAX_TX_URBS;
- priv->cmd_msg_buffer = kzalloc(sizeof(struct usb_8dev_cmd_msg),
- GFP_KERNEL);
+ priv->cmd_msg_buffer = devm_kzalloc(&intf->dev, sizeof(struct usb_8dev_cmd_msg),
+ GFP_KERNEL);
if (!priv->cmd_msg_buffer)
goto cleanup_candev;
@@ -969,7 +969,7 @@ static int usb_8dev_probe(struct usb_interface *intf,
if (err) {
netdev_err(netdev,
"couldn't register CAN device: %d\n", err);
- goto cleanup_cmd_msg_buffer;
+ goto cleanup_candev;
}
err = usb_8dev_cmd_version(priv, &version);
@@ -990,9 +990,6 @@ static int usb_8dev_probe(struct usb_interface *intf,
cleanup_unregister_candev:
unregister_netdev(priv->netdev);
-cleanup_cmd_msg_buffer:
- kfree(priv->cmd_msg_buffer);
-
cleanup_candev:
free_candev(netdev);
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c
index 858106352ce9..8860e74aa28f 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.c
+++ b/drivers/net/ethernet/broadcom/bcmsysport.c
@@ -732,11 +732,8 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv,
unsigned int c_index, last_c_index, last_tx_cn, num_tx_cbs;
unsigned int pkts_compl = 0, bytes_compl = 0;
struct bcm_sysport_cb *cb;
- struct netdev_queue *txq;
u32 hw_ind;
- txq = netdev_get_tx_queue(ndev, ring->index);
-
/* Compute how many descriptors have been processed since last call */
hw_ind = tdma_readl(priv, TDMA_DESC_RING_PROD_CONS_INDEX(ring->index));
c_index = (hw_ind >> RING_CONS_INDEX_SHIFT) & RING_CONS_INDEX_MASK;
@@ -767,9 +764,6 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv,
ring->c_index = c_index;
- if (netif_tx_queue_stopped(txq) && pkts_compl)
- netif_tx_wake_queue(txq);
-
netif_dbg(priv, tx_done, ndev,
"ring=%d c_index=%d pkts_compl=%d, bytes_compl=%d\n",
ring->index, ring->c_index, pkts_compl, bytes_compl);
@@ -781,16 +775,33 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv,
static unsigned int bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv,
struct bcm_sysport_tx_ring *ring)
{
+ struct netdev_queue *txq;
unsigned int released;
unsigned long flags;
+ txq = netdev_get_tx_queue(priv->netdev, ring->index);
+
spin_lock_irqsave(&ring->lock, flags);
released = __bcm_sysport_tx_reclaim(priv, ring);
+ if (released)
+ netif_tx_wake_queue(txq);
+
spin_unlock_irqrestore(&ring->lock, flags);
return released;
}
+/* Locked version of the per-ring TX reclaim, but does not wake the queue */
+static void bcm_sysport_tx_clean(struct bcm_sysport_priv *priv,
+ struct bcm_sysport_tx_ring *ring)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ring->lock, flags);
+ __bcm_sysport_tx_reclaim(priv, ring);
+ spin_unlock_irqrestore(&ring->lock, flags);
+}
+
static int bcm_sysport_tx_poll(struct napi_struct *napi, int budget)
{
struct bcm_sysport_tx_ring *ring =
@@ -1275,7 +1286,7 @@ static void bcm_sysport_fini_tx_ring(struct bcm_sysport_priv *priv,
napi_disable(&ring->napi);
netif_napi_del(&ring->napi);
- bcm_sysport_tx_reclaim(priv, ring);
+ bcm_sysport_tx_clean(priv, ring);
kfree(ring->cbs);
ring->cbs = NULL;
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
index bdbd80423b17..9ff2881f933d 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
@@ -900,9 +900,7 @@
static inline void dsaf_write_reg(void __iomem *base, u32 reg, u32 value)
{
- u8 __iomem *reg_addr = ACCESS_ONCE(base);
-
- writel(value, reg_addr + reg);
+ writel(value, base + reg);
}
#define dsaf_write_dev(a, reg, value) \
@@ -910,9 +908,7 @@ static inline void dsaf_write_reg(void __iomem *base, u32 reg, u32 value)
static inline u32 dsaf_read_reg(u8 __iomem *base, u32 reg)
{
- u8 __iomem *reg_addr = ACCESS_ONCE(base);
-
- return readl(reg_addr + reg);
+ return readl(base + reg);
}
#define dsaf_read_dev(a, reg) \
diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index a4beccf1fd46..0e67145bc418 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -772,6 +772,17 @@ struct mvpp2_rx_desc {
u32 reserved8;
};
+struct mvpp2_txq_pcpu_buf {
+ /* Transmitted SKB */
+ struct sk_buff *skb;
+
+ /* Physical address of transmitted buffer */
+ dma_addr_t phys;
+
+ /* Size transmitted */
+ size_t size;
+};
+
/* Per-CPU Tx queue control */
struct mvpp2_txq_pcpu {
int cpu;
@@ -787,11 +798,8 @@ struct mvpp2_txq_pcpu {
/* Number of Tx DMA descriptors reserved for each CPU */
int reserved_num;
- /* Array of transmitted skb */
- struct sk_buff **tx_skb;
-
- /* Array of transmitted buffers' physical addresses */
- dma_addr_t *tx_buffs;
+ /* Infos about transmitted buffers */
+ struct mvpp2_txq_pcpu_buf *buffs;
/* Index of last TX DMA descriptor that was inserted */
int txq_put_index;
@@ -981,10 +989,11 @@ static void mvpp2_txq_inc_put(struct mvpp2_txq_pcpu *txq_pcpu,
struct sk_buff *skb,
struct mvpp2_tx_desc *tx_desc)
{
- txq_pcpu->tx_skb[txq_pcpu->txq_put_index] = skb;
- if (skb)
- txq_pcpu->tx_buffs[txq_pcpu->txq_put_index] =
- tx_desc->buf_phys_addr;
+ struct mvpp2_txq_pcpu_buf *tx_buf =
+ txq_pcpu->buffs + txq_pcpu->txq_put_index;
+ tx_buf->skb = skb;
+ tx_buf->size = tx_desc->data_size;
+ tx_buf->phys = tx_desc->buf_phys_addr + tx_desc->packet_offset;
txq_pcpu->txq_put_index++;
if (txq_pcpu->txq_put_index == txq_pcpu->size)
txq_pcpu->txq_put_index = 0;
@@ -4403,17 +4412,16 @@ static void mvpp2_txq_bufs_free(struct mvpp2_port *port,
int i;
for (i = 0; i < num; i++) {
- dma_addr_t buf_phys_addr =
- txq_pcpu->tx_buffs[txq_pcpu->txq_get_index];
- struct sk_buff *skb = txq_pcpu->tx_skb[txq_pcpu->txq_get_index];
+ struct mvpp2_txq_pcpu_buf *tx_buf =
+ txq_pcpu->buffs + txq_pcpu->txq_get_index;
mvpp2_txq_inc_get(txq_pcpu);
- dma_unmap_single(port->dev->dev.parent, buf_phys_addr,
- skb_headlen(skb), DMA_TO_DEVICE);
- if (!skb)
+ dma_unmap_single(port->dev->dev.parent, tx_buf->phys,
+ tx_buf->size, DMA_TO_DEVICE);
+ if (!tx_buf->skb)
continue;
- dev_kfree_skb_any(skb);
+ dev_kfree_skb_any(tx_buf->skb);
}
}
@@ -4664,15 +4672,10 @@ static int mvpp2_txq_init(struct mvpp2_port *port,
for_each_present_cpu(cpu) {
txq_pcpu = per_cpu_ptr(txq->pcpu, cpu);
txq_pcpu->size = txq->size;
- txq_pcpu->tx_skb = kmalloc(txq_pcpu->size *
- sizeof(*txq_pcpu->tx_skb),
- GFP_KERNEL);
- if (!txq_pcpu->tx_skb)
- goto error;
-
- txq_pcpu->tx_buffs = kmalloc(txq_pcpu->size *
- sizeof(dma_addr_t), GFP_KERNEL);
- if (!txq_pcpu->tx_buffs)
+ txq_pcpu->buffs = kmalloc(txq_pcpu->size *
+ sizeof(struct mvpp2_txq_pcpu_buf),
+ GFP_KERNEL);
+ if (!txq_pcpu->buffs)
goto error;
txq_pcpu->count = 0;
@@ -4686,8 +4689,7 @@ static int mvpp2_txq_init(struct mvpp2_port *port,
error:
for_each_present_cpu(cpu) {
txq_pcpu = per_cpu_ptr(txq->pcpu, cpu);
- kfree(txq_pcpu->tx_skb);
- kfree(txq_pcpu->tx_buffs);
+ kfree(txq_pcpu->buffs);
}
dma_free_coherent(port->dev->dev.parent,
@@ -4706,8 +4708,7 @@ static void mvpp2_txq_deinit(struct mvpp2_port *port,
for_each_present_cpu(cpu) {
txq_pcpu = per_cpu_ptr(txq->pcpu, cpu);
- kfree(txq_pcpu->tx_skb);
- kfree(txq_pcpu->tx_buffs);
+ kfree(txq_pcpu->buffs);
}
if (txq->descs)
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index bbff8ec6713e..28a4b34310b2 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -502,8 +502,11 @@ void mlx4_en_recover_from_oom(struct mlx4_en_priv *priv)
return;
for (ring = 0; ring < priv->rx_ring_num; ring++) {
- if (mlx4_en_is_ring_empty(priv->rx_ring[ring]))
+ if (mlx4_en_is_ring_empty(priv->rx_ring[ring])) {
+ local_bh_disable();
napi_reschedule(&priv->rx_cq[ring]->napi);
+ local_bh_enable();
+ }
}
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 6cf6d93d8831..ba115ec7aa92 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -432,6 +432,13 @@ static int handle_hca_cap(struct mlx5_core_dev *dev)
MLX5_SET(cmd_hca_cap, set_hca_cap, pkey_table_size,
to_fw_pkey_sz(128));
+ /* Check log_max_qp from HCA caps to set in current profile */
+ if (MLX5_CAP_GEN_MAX(dev, log_max_qp) < profile[prof_sel].log_max_qp) {
+ mlx5_core_warn(dev, "log_max_qp value in current profile is %d, changing it to HCA capability limit (%d)\n",
+ profile[prof_sel].log_max_qp,
+ MLX5_CAP_GEN_MAX(dev, log_max_qp));
+ profile[prof_sel].log_max_qp = MLX5_CAP_GEN_MAX(dev, log_max_qp);
+ }
if (prof->mask & MLX5_PROF_MASK_QP_SIZE)
MLX5_SET(cmd_hca_cap, set_hca_cap, log_max_qp,
prof->log_max_qp);
@@ -505,7 +512,6 @@ static int mlx5_irq_set_affinity_hint(struct mlx5_core_dev *mdev, int i)
struct mlx5_priv *priv = &mdev->priv;
struct msix_entry *msix = priv->msix_arr;
int irq = msix[i + MLX5_EQ_VEC_COMP_BASE].vector;
- int numa_node = priv->numa_node;
int err;
if (!zalloc_cpumask_var(&priv->irq_info[i].mask, GFP_KERNEL)) {
@@ -513,7 +519,7 @@ static int mlx5_irq_set_affinity_hint(struct mlx5_core_dev *mdev, int i)
return -ENOMEM;
}
- cpumask_set_cpu(cpumask_local_spread(i, numa_node),
+ cpumask_set_cpu(cpumask_local_spread(i, priv->numa_node),
priv->irq_info[i].mask);
err = irq_set_affinity_hint(irq, priv->irq_info[i].mask);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h
index 142f33d978c5..a0fbe00dd690 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/pci.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/pci.h
@@ -206,21 +206,21 @@ MLXSW_ITEM32(pci, eqe, owner, 0x0C, 0, 1);
/* pci_eqe_cmd_token
* Command completion event - token
*/
-MLXSW_ITEM32(pci, eqe, cmd_token, 0x08, 16, 16);
+MLXSW_ITEM32(pci, eqe, cmd_token, 0x00, 16, 16);
/* pci_eqe_cmd_status
* Command completion event - status
*/
-MLXSW_ITEM32(pci, eqe, cmd_status, 0x08, 0, 8);
+MLXSW_ITEM32(pci, eqe, cmd_status, 0x00, 0, 8);
/* pci_eqe_cmd_out_param_h
* Command completion event - output parameter - higher part
*/
-MLXSW_ITEM32(pci, eqe, cmd_out_param_h, 0x0C, 0, 32);
+MLXSW_ITEM32(pci, eqe, cmd_out_param_h, 0x04, 0, 32);
/* pci_eqe_cmd_out_param_l
* Command completion event - output parameter - lower part
*/
-MLXSW_ITEM32(pci, eqe, cmd_out_param_l, 0x10, 0, 32);
+MLXSW_ITEM32(pci, eqe, cmd_out_param_l, 0x08, 0, 32);
#endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index 3be4a2355ead..cb165c2d4803 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -390,6 +390,7 @@ static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb,
dev_kfree_skb_any(skb_orig);
return NETDEV_TX_OK;
}
+ dev_consume_skb_any(skb_orig);
}
if (eth_skb_pad(skb)) {
diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
index d85960cfb694..fb2d9a82ce3d 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
@@ -313,6 +313,7 @@ static netdev_tx_t mlxsw_sx_port_xmit(struct sk_buff *skb,
dev_kfree_skb_any(skb_orig);
return NETDEV_TX_OK;
}
+ dev_consume_skb_any(skb_orig);
}
mlxsw_sx_txhdr_construct(skb, &tx_info);
len = skb->len;
diff --git a/drivers/net/ethernet/msm/msm_rmnet_mhi.c b/drivers/net/ethernet/msm/msm_rmnet_mhi.c
index 9117ea7d08c0..bf6502e27bdd 100644
--- a/drivers/net/ethernet/msm/msm_rmnet_mhi.c
+++ b/drivers/net/ethernet/msm/msm_rmnet_mhi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -27,13 +27,12 @@
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/of_device.h>
+#include <linux/rtnetlink.h>
#define RMNET_MHI_DRIVER_NAME "rmnet_mhi"
-#define RMNET_MHI_DEV_NAME "rmnet_mhi%d"
#define MHI_DEFAULT_MTU 8000
#define MHI_MAX_MRU 0xFFFF
#define MHI_NAPI_WEIGHT_VALUE 12
-#define MHI_RX_HEADROOM 64
#define WATCHDOG_TIMEOUT (30 * HZ)
#define RMNET_IPC_LOG_PAGES (100)
@@ -79,6 +78,8 @@ struct __packed mhi_skb_priv {
struct rmnet_mhi_private {
struct list_head node;
+ u32 dev_id;
+ const char *interface_name;
struct mhi_client_handle *tx_client_handle;
struct mhi_client_handle *rx_client_handle;
enum MHI_CLIENT_CHANNEL tx_channel;
@@ -87,6 +88,8 @@ struct rmnet_mhi_private {
struct sk_buff_head rx_buffers;
atomic_t rx_pool_len;
u32 mru;
+ u32 max_mru;
+ u32 max_mtu;
struct napi_struct napi;
gfp_t allocation_flags;
uint32_t tx_buffers_max;
@@ -110,6 +113,7 @@ struct rmnet_mhi_private {
};
static LIST_HEAD(rmnet_mhi_ctxt_list);
+static struct platform_driver rmnet_mhi_driver;
static int rmnet_mhi_process_fragment(struct rmnet_mhi_private *rmnet_mhi_ptr,
struct sk_buff *skb, int frag)
@@ -118,9 +122,9 @@ static int rmnet_mhi_process_fragment(struct rmnet_mhi_private *rmnet_mhi_ptr,
if (rmnet_mhi_ptr->frag_skb) {
/* Merge the new skb into the old fragment */
temp_skb = skb_copy_expand(rmnet_mhi_ptr->frag_skb,
- MHI_RX_HEADROOM,
- skb->len,
- GFP_ATOMIC);
+ 0,
+ skb->len,
+ GFP_ATOMIC);
if (!temp_skb) {
kfree(rmnet_mhi_ptr->frag_skb);
rmnet_mhi_ptr->frag_skb = NULL;
@@ -207,9 +211,8 @@ static int rmnet_alloc_rx(struct rmnet_mhi_private *rmnet_mhi_ptr,
return -ENOMEM;
}
skb_priv = (struct mhi_skb_priv *)(skb->cb);
- skb_priv->dma_size = cur_mru - MHI_RX_HEADROOM;
+ skb_priv->dma_size = cur_mru;
skb_priv->dma_addr = 0;
- skb_reserve(skb, MHI_RX_HEADROOM);
/* These steps must be in atomic context */
spin_lock_irqsave(&rmnet_mhi_ptr->alloc_lock, flags);
@@ -584,7 +587,10 @@ static int rmnet_mhi_stop(struct net_device *dev)
static int rmnet_mhi_change_mtu(struct net_device *dev, int new_mtu)
{
- if (0 > new_mtu || MHI_MAX_MTU < new_mtu)
+ struct rmnet_mhi_private *rmnet_mhi_ptr =
+ *(struct rmnet_mhi_private **)netdev_priv(dev);
+
+ if (new_mtu < 0 || rmnet_mhi_ptr->max_mtu < new_mtu)
return -EINVAL;
dev->mtu = new_mtu;
@@ -667,11 +673,11 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
switch (ext_cmd.extended_ioctl) {
case RMNET_IOCTL_SET_MRU:
- if (!ext_cmd.u.data || ext_cmd.u.data > MHI_MAX_MRU) {
- rmnet_log(rmnet_mhi_ptr,
- MSG_CRITICAL,
- "Can't set MRU, value %u is invalid\n",
- ext_cmd.u.data);
+ if (!ext_cmd.u.data ||
+ ext_cmd.u.data > rmnet_mhi_ptr->max_mru) {
+ rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL,
+ "Can't set MRU, value:%u is invalid max:%u\n",
+ ext_cmd.u.data, rmnet_mhi_ptr->max_mru);
return -EINVAL;
}
rmnet_log(rmnet_mhi_ptr,
@@ -688,7 +694,7 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
ext_cmd.u.data = 0;
break;
case RMNET_IOCTL_GET_DRIVER_NAME:
- strlcpy(ext_cmd.u.if_name, RMNET_MHI_DRIVER_NAME,
+ strlcpy(ext_cmd.u.if_name, rmnet_mhi_ptr->interface_name,
sizeof(ext_cmd.u.if_name));
break;
case RMNET_IOCTL_SET_SLEEP_STATE:
@@ -793,6 +799,9 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr)
int ret = 0;
struct rmnet_mhi_private **rmnet_mhi_ctxt = NULL;
int r = 0;
+ char ifalias[IFALIASZ];
+ char ifname[IFNAMSIZ];
+ struct mhi_client_handle *client_handle = NULL;
rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered.\n");
@@ -826,6 +835,7 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr)
} else {
rmnet_mhi_ptr->tx_enabled = 1;
}
+ client_handle = rmnet_mhi_ptr->tx_client_handle;
}
if (rmnet_mhi_ptr->rx_client_handle != NULL) {
rmnet_log(rmnet_mhi_ptr,
@@ -841,11 +851,32 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr)
} else {
rmnet_mhi_ptr->rx_enabled = 1;
}
+ /* Both tx & rx client handle contain same device info */
+ client_handle = rmnet_mhi_ptr->rx_client_handle;
+ }
+
+ if (!client_handle) {
+ ret = -EINVAL;
+ goto net_dev_alloc_fail;
}
+
+ snprintf(ifalias,
+ sizeof(ifalias),
+ "%s_%04x_%02u.%02u.%02u_%u",
+ rmnet_mhi_ptr->interface_name,
+ client_handle->dev_id,
+ client_handle->domain,
+ client_handle->bus,
+ client_handle->slot,
+ rmnet_mhi_ptr->dev_id);
+
+ snprintf(ifname, sizeof(ifname), "%s%%d",
+ rmnet_mhi_ptr->interface_name);
+
+ rtnl_lock();
rmnet_mhi_ptr->dev =
alloc_netdev(sizeof(struct rmnet_mhi_private *),
- RMNET_MHI_DEV_NAME,
- NET_NAME_PREDICTABLE, rmnet_mhi_setup);
+ ifname, NET_NAME_PREDICTABLE, rmnet_mhi_setup);
if (!rmnet_mhi_ptr->dev) {
rmnet_log(rmnet_mhi_ptr,
MSG_CRITICAL,
@@ -854,7 +885,9 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr)
goto net_dev_alloc_fail;
}
SET_NETDEV_DEV(rmnet_mhi_ptr->dev, &rmnet_mhi_ptr->pdev->dev);
+ dev_set_alias(rmnet_mhi_ptr->dev, ifalias, strlen(ifalias));
rmnet_mhi_ctxt = netdev_priv(rmnet_mhi_ptr->dev);
+ rtnl_unlock();
*rmnet_mhi_ctxt = rmnet_mhi_ptr;
ret = dma_set_mask(&(rmnet_mhi_ptr->dev->dev),
@@ -981,17 +1014,16 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info)
}
}
-static struct mhi_client_info_t rmnet_mhi_info = {rmnet_mhi_cb};
-
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
static void rmnet_mhi_create_debugfs(struct rmnet_mhi_private *rmnet_mhi_ptr)
{
- char node_name[15];
+ char node_name[32];
int i;
const umode_t mode = (S_IRUSR | S_IWUSR);
struct dentry *file;
+ struct mhi_client_handle *client_handle;
const struct {
char *name;
@@ -1047,8 +1079,20 @@ static void rmnet_mhi_create_debugfs(struct rmnet_mhi_private *rmnet_mhi_ptr)
},
};
- snprintf(node_name, sizeof(node_name), "%s%d",
- RMNET_MHI_DRIVER_NAME, rmnet_mhi_ptr->pdev->id);
+ /* Both tx & rx client handle contain same device info */
+ client_handle = rmnet_mhi_ptr->rx_client_handle;
+ if (!client_handle)
+ client_handle = rmnet_mhi_ptr->tx_client_handle;
+
+ snprintf(node_name,
+ sizeof(node_name),
+ "%s_%04x_%02u.%02u.%02u_%u",
+ rmnet_mhi_ptr->interface_name,
+ client_handle->dev_id,
+ client_handle->domain,
+ client_handle->bus,
+ client_handle->slot,
+ rmnet_mhi_ptr->dev_id);
if (IS_ERR_OR_NULL(dentry))
return;
@@ -1109,11 +1153,16 @@ static int rmnet_mhi_probe(struct platform_device *pdev)
int rc;
u32 channel;
struct rmnet_mhi_private *rmnet_mhi_ptr;
- char node_name[15];
+ struct mhi_client_handle *client_handle = NULL;
+ char node_name[32];
+ struct mhi_client_info_t client_info;
if (unlikely(!pdev->dev.of_node))
return -ENODEV;
+ if (!mhi_is_device_ready(&pdev->dev, "qcom,mhi"))
+ return -EPROBE_DEFER;
+
pdev->id = of_alias_get_id(pdev->dev.of_node, "mhi_rmnet");
if (unlikely(pdev->id < 0))
return -ENODEV;
@@ -1135,15 +1184,59 @@ static int rmnet_mhi_probe(struct platform_device *pdev)
}
rc = of_property_read_u32(pdev->dev.of_node,
+ "cell-index",
+ &rmnet_mhi_ptr->dev_id);
+ if (unlikely(rc)) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "failed to get valid 'cell-index'\n");
+ goto probe_fail;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mhi-max-mru",
+ &rmnet_mhi_ptr->max_mru);
+ if (likely(rc)) {
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO,
+ "max-mru not defined, setting to max %d\n",
+ MHI_MAX_MRU);
+ rmnet_mhi_ptr->max_mru = MHI_MAX_MRU;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mhi-max-mtu",
+ &rmnet_mhi_ptr->max_mtu);
+ if (likely(rc)) {
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO,
+ "max-mtu not defined, setting to max %d\n",
+ MHI_MAX_MTU);
+ rmnet_mhi_ptr->max_mtu = MHI_MAX_MTU;
+ }
+
+ rc = of_property_read_string(pdev->dev.of_node, "qcom,interface-name",
+ &rmnet_mhi_ptr->interface_name);
+ if (likely(rc)) {
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO,
+ "interface-name not defined, setting to default name %s\n",
+ RMNET_MHI_DRIVER_NAME);
+ rmnet_mhi_ptr->interface_name = rmnet_mhi_driver.driver.name;
+ }
+
+ client_info.dev = &pdev->dev;
+ client_info.node_name = "qcom,mhi";
+ client_info.mhi_client_cb = rmnet_mhi_cb;
+ client_info.user_data = rmnet_mhi_ptr;
+
+ rc = of_property_read_u32(pdev->dev.of_node,
"qcom,mhi-tx-channel",
&channel);
if (rc == 0) {
rmnet_mhi_ptr->tx_channel = channel;
+ client_info.chan = channel;
+ client_info.max_payload = rmnet_mhi_ptr->max_mtu;
+
rc = mhi_register_channel(&rmnet_mhi_ptr->tx_client_handle,
- rmnet_mhi_ptr->tx_channel,
- 0,
- &rmnet_mhi_info,
- rmnet_mhi_ptr);
+ &client_info);
if (unlikely(rc)) {
rmnet_log(rmnet_mhi_ptr,
MSG_CRITICAL,
@@ -1152,6 +1245,7 @@ static int rmnet_mhi_probe(struct platform_device *pdev)
rc);
goto probe_fail;
}
+ client_handle = rmnet_mhi_ptr->tx_client_handle;
}
rc = of_property_read_u32(pdev->dev.of_node,
@@ -1159,11 +1253,10 @@ static int rmnet_mhi_probe(struct platform_device *pdev)
&channel);
if (rc == 0) {
rmnet_mhi_ptr->rx_channel = channel;
+ client_info.max_payload = rmnet_mhi_ptr->max_mru;
+ client_info.chan = channel;
rc = mhi_register_channel(&rmnet_mhi_ptr->rx_client_handle,
- rmnet_mhi_ptr->rx_channel,
- 0,
- &rmnet_mhi_info,
- rmnet_mhi_ptr);
+ &client_info);
if (unlikely(rc)) {
rmnet_log(rmnet_mhi_ptr,
MSG_CRITICAL,
@@ -1172,22 +1265,31 @@ static int rmnet_mhi_probe(struct platform_device *pdev)
rc);
goto probe_fail;
}
-
+ /* overwriting tx_client_handle is ok because dev_id and
+ * bdf are same for both channels
+ */
+ client_handle = rmnet_mhi_ptr->rx_client_handle;
INIT_WORK(&rmnet_mhi_ptr->alloc_work, rmnet_mhi_alloc_work);
spin_lock_init(&rmnet_mhi_ptr->alloc_lock);
}
/* We must've have @ least one valid channel */
- if (!rmnet_mhi_ptr->rx_client_handle &&
- !rmnet_mhi_ptr->tx_client_handle) {
+ if (!client_handle) {
rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL,
"No registered channels\n");
rc = -ENODEV;
goto probe_fail;
}
- snprintf(node_name, sizeof(node_name), "%s%d",
- RMNET_MHI_DRIVER_NAME, pdev->id);
+ snprintf(node_name,
+ sizeof(node_name),
+ "%s_%04x_%02u.%02u.%02u_%u",
+ rmnet_mhi_ptr->interface_name,
+ client_handle->dev_id,
+ client_handle->domain,
+ client_handle->bus,
+ client_handle->slot,
+ rmnet_mhi_ptr->dev_id);
rmnet_mhi_ptr->rmnet_ipc_log =
ipc_log_context_create(RMNET_IPC_LOG_PAGES,
node_name, 0);
diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c
index 50d5604833ed..e0993eba5df3 100644
--- a/drivers/net/ethernet/neterion/vxge/vxge-main.c
+++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c
@@ -2223,8 +2223,6 @@ static irqreturn_t vxge_isr_napi(int irq, void *dev_id)
return IRQ_NONE;
}
-#ifdef CONFIG_PCI_MSI
-
static irqreturn_t vxge_tx_msix_handle(int irq, void *dev_id)
{
struct vxge_fifo *fifo = (struct vxge_fifo *)dev_id;
@@ -2442,16 +2440,13 @@ static void vxge_rem_msix_isr(struct vxgedev *vdev)
if (vdev->config.intr_type == MSI_X)
pci_disable_msix(vdev->pdev);
}
-#endif
static void vxge_rem_isr(struct vxgedev *vdev)
{
-#ifdef CONFIG_PCI_MSI
- if (vdev->config.intr_type == MSI_X) {
+ if (IS_ENABLED(CONFIG_PCI_MSI) &&
+ vdev->config.intr_type == MSI_X) {
vxge_rem_msix_isr(vdev);
- } else
-#endif
- if (vdev->config.intr_type == INTA) {
+ } else if (vdev->config.intr_type == INTA) {
synchronize_irq(vdev->pdev->irq);
free_irq(vdev->pdev->irq, vdev);
}
@@ -2460,11 +2455,10 @@ static void vxge_rem_isr(struct vxgedev *vdev)
static int vxge_add_isr(struct vxgedev *vdev)
{
int ret = 0;
-#ifdef CONFIG_PCI_MSI
int vp_idx = 0, intr_idx = 0, intr_cnt = 0, msix_idx = 0, irq_req = 0;
int pci_fun = PCI_FUNC(vdev->pdev->devfn);
- if (vdev->config.intr_type == MSI_X)
+ if (IS_ENABLED(CONFIG_PCI_MSI) && vdev->config.intr_type == MSI_X)
ret = vxge_enable_msix(vdev);
if (ret) {
@@ -2475,7 +2469,7 @@ static int vxge_add_isr(struct vxgedev *vdev)
vdev->config.intr_type = INTA;
}
- if (vdev->config.intr_type == MSI_X) {
+ if (IS_ENABLED(CONFIG_PCI_MSI) && vdev->config.intr_type == MSI_X) {
for (intr_idx = 0;
intr_idx < (vdev->no_of_vpath *
VXGE_HW_VPATH_MSIX_ACTIVE); intr_idx++) {
@@ -2576,9 +2570,8 @@ static int vxge_add_isr(struct vxgedev *vdev)
vdev->vxge_entries[intr_cnt].in_use = 1;
vdev->vxge_entries[intr_cnt].arg = &vdev->vpaths[0];
}
-INTA_MODE:
-#endif
+INTA_MODE:
if (vdev->config.intr_type == INTA) {
snprintf(vdev->desc[0], VXGE_INTR_STRLEN,
"%s:vxge:INTA", vdev->ndev->name);
@@ -3889,12 +3882,12 @@ static void vxge_device_config_init(struct vxge_hw_device_config *device_config,
if (max_mac_vpath > VXGE_MAX_MAC_ADDR_COUNT)
max_mac_vpath = VXGE_MAX_MAC_ADDR_COUNT;
-#ifndef CONFIG_PCI_MSI
- vxge_debug_init(VXGE_ERR,
- "%s: This Kernel does not support "
- "MSI-X. Defaulting to INTA", VXGE_DRIVER_NAME);
- *intr_type = INTA;
-#endif
+ if (!IS_ENABLED(CONFIG_PCI_MSI)) {
+ vxge_debug_init(VXGE_ERR,
+ "%s: This Kernel does not support "
+ "MSI-X. Defaulting to INTA", VXGE_DRIVER_NAME);
+ *intr_type = INTA;
+ }
/* Configure whether MSI-X or IRQL. */
switch (*intr_type) {
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index 467d41698fd5..549ad2018e7f 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -1330,6 +1330,19 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev)
buffer = PTR_ALIGN(priv->tx_align[q], DPTR_ALIGN) +
entry / NUM_TX_DESC * DPTR_ALIGN;
len = PTR_ALIGN(skb->data, DPTR_ALIGN) - skb->data;
+ /* Zero length DMA descriptors are problematic as they seem to
+ * terminate DMA transfers. Avoid them by simply using a length of
+ * DPTR_ALIGN (4) when skb data is aligned to DPTR_ALIGN.
+ *
+ * As skb is guaranteed to have at least ETH_ZLEN (60) bytes of
+ * data by the call to skb_put_padto() above this is safe with
+ * respect to both the length of the first DMA descriptor (len)
+ * overflowing the available data and the length of the second DMA
+ * descriptor (skb->len - len) being negative.
+ */
+ if (len == 0)
+ len = DPTR_ALIGN;
+
memcpy(buffer, skb->data, len);
dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE);
if (dma_mapping_error(ndev->dev.parent, dma_addr))
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index a5b869eb4678..4b100ef4af9f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -2939,12 +2939,6 @@ int stmmac_dvr_probe(struct device *device,
spin_lock_init(&priv->lock);
spin_lock_init(&priv->tx_lock);
- ret = register_netdev(ndev);
- if (ret) {
- pr_err("%s: ERROR %i registering the device\n", __func__, ret);
- goto error_netdev_register;
- }
-
/* If a specific clk_csr value is passed from the platform
* this means that the CSR Clock Range selection cannot be
* changed at run-time and it is fixed. Viceversa the driver'll try to
@@ -2969,11 +2963,21 @@ int stmmac_dvr_probe(struct device *device,
}
}
- return 0;
+ ret = register_netdev(ndev);
+ if (ret) {
+ netdev_err(priv->dev, "%s: ERROR %i registering the device\n",
+ __func__, ret);
+ goto error_netdev_register;
+ }
+
+ return ret;
-error_mdio_register:
- unregister_netdev(ndev);
error_netdev_register:
+ if (priv->pcs != STMMAC_PCS_RGMII &&
+ priv->pcs != STMMAC_PCS_TBI &&
+ priv->pcs != STMMAC_PCS_RTBI)
+ stmmac_mdio_unregister(ndev);
+error_mdio_register:
netif_napi_del(&priv->napi);
error_hw_init:
clk_disable_unprepare(priv->pclk);
diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c
index 77d26fe286c0..7e8bce46e6b4 100644
--- a/drivers/net/ethernet/ti/cpmac.c
+++ b/drivers/net/ethernet/ti/cpmac.c
@@ -549,7 +549,8 @@ fatal_error:
static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- int queue, len;
+ int queue;
+ unsigned int len;
struct cpmac_desc *desc;
struct cpmac_priv *priv = netdev_priv(dev);
@@ -559,7 +560,7 @@ static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(skb_padto(skb, ETH_ZLEN)))
return NETDEV_TX_OK;
- len = max(skb->len, ETH_ZLEN);
+ len = max_t(unsigned int, skb->len, ETH_ZLEN);
queue = skb_get_queue_mapping(skb);
netif_stop_subqueue(dev, queue);
@@ -1236,7 +1237,7 @@ int cpmac_init(void)
goto fail_alloc;
}
-#warning FIXME: unhardcode gpio&reset bits
+ /* FIXME: unhardcode gpio&reset bits */
ar7_gpio_disable(26);
ar7_gpio_disable(27);
ar7_device_reset(AR7_RESET_BIT_CPMAC_LO);
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index 409b48e1e589..e8a09ff9e724 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -40,6 +40,8 @@
#include "hyperv_net.h"
+/* Restrict GSO size to account for NVGRE */
+#define NETVSC_GSO_MAX_SIZE 62768
#define RING_SIZE_MIN 64
static int ring_size = 128;
@@ -1139,6 +1141,7 @@ static int netvsc_probe(struct hv_device *dev,
nvdev = hv_get_drvdata(dev);
netif_set_real_num_tx_queues(net, nvdev->num_chn);
netif_set_real_num_rx_queues(net, nvdev->num_chn);
+ netif_set_gso_max_size(net, NETVSC_GSO_MAX_SIZE);
ret = register_netdev(net);
if (ret != 0) {
diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c
index 199a94a9c8bc..3a429f1a8002 100644
--- a/drivers/net/ieee802154/atusb.c
+++ b/drivers/net/ieee802154/atusb.c
@@ -110,13 +110,26 @@ static int atusb_read_reg(struct atusb *atusb, uint8_t reg)
{
struct usb_device *usb_dev = atusb->usb_dev;
int ret;
+ uint8_t *buffer;
uint8_t value;
+ buffer = kmalloc(1, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
dev_dbg(&usb_dev->dev, "atusb: reg = 0x%x\n", reg);
ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0),
ATUSB_REG_READ, ATUSB_REQ_FROM_DEV,
- 0, reg, &value, 1, 1000);
- return ret >= 0 ? value : ret;
+ 0, reg, buffer, 1, 1000);
+
+ if (ret >= 0) {
+ value = buffer[0];
+ kfree(buffer);
+ return value;
+ } else {
+ kfree(buffer);
+ return ret;
+ }
}
static int atusb_write_subreg(struct atusb *atusb, uint8_t reg, uint8_t mask,
@@ -517,9 +530,13 @@ static struct ieee802154_ops atusb_ops = {
static int atusb_get_and_show_revision(struct atusb *atusb)
{
struct usb_device *usb_dev = atusb->usb_dev;
- unsigned char buffer[3];
+ unsigned char *buffer;
int ret;
+ buffer = kmalloc(3, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
/* Get a couple of the ATMega Firmware values */
ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0),
ATUSB_ID, ATUSB_REQ_FROM_DEV, 0, 0,
@@ -535,15 +552,20 @@ static int atusb_get_and_show_revision(struct atusb *atusb)
dev_info(&usb_dev->dev, "Please update to version 0.2 or newer");
}
+ kfree(buffer);
return ret;
}
static int atusb_get_and_show_build(struct atusb *atusb)
{
struct usb_device *usb_dev = atusb->usb_dev;
- char build[ATUSB_BUILD_SIZE + 1];
+ char *build;
int ret;
+ build = kmalloc(ATUSB_BUILD_SIZE + 1, GFP_KERNEL);
+ if (!build)
+ return -ENOMEM;
+
ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0),
ATUSB_BUILD, ATUSB_REQ_FROM_DEV, 0, 0,
build, ATUSB_BUILD_SIZE, 1000);
@@ -552,6 +574,7 @@ static int atusb_get_and_show_build(struct atusb *atusb)
dev_info(&usb_dev->dev, "Firmware: build %s\n", build);
}
+ kfree(build);
return ret;
}
diff --git a/drivers/net/ieee802154/fakelb.c b/drivers/net/ieee802154/fakelb.c
index 860d4aed8274..43617ded3773 100644
--- a/drivers/net/ieee802154/fakelb.c
+++ b/drivers/net/ieee802154/fakelb.c
@@ -30,7 +30,7 @@
static int numlbs = 2;
static LIST_HEAD(fakelb_phys);
-static DEFINE_SPINLOCK(fakelb_phys_lock);
+static DEFINE_MUTEX(fakelb_phys_lock);
static LIST_HEAD(fakelb_ifup_phys);
static DEFINE_RWLOCK(fakelb_ifup_phys_lock);
@@ -180,9 +180,9 @@ static int fakelb_add_one(struct device *dev)
if (err)
goto err_reg;
- spin_lock(&fakelb_phys_lock);
+ mutex_lock(&fakelb_phys_lock);
list_add_tail(&phy->list, &fakelb_phys);
- spin_unlock(&fakelb_phys_lock);
+ mutex_unlock(&fakelb_phys_lock);
return 0;
@@ -214,10 +214,10 @@ static int fakelb_probe(struct platform_device *pdev)
return 0;
err_slave:
- spin_lock(&fakelb_phys_lock);
+ mutex_lock(&fakelb_phys_lock);
list_for_each_entry_safe(phy, tmp, &fakelb_phys, list)
fakelb_del(phy);
- spin_unlock(&fakelb_phys_lock);
+ mutex_unlock(&fakelb_phys_lock);
return err;
}
@@ -225,10 +225,10 @@ static int fakelb_remove(struct platform_device *pdev)
{
struct fakelb_phy *phy, *tmp;
- spin_lock(&fakelb_phys_lock);
+ mutex_lock(&fakelb_phys_lock);
list_for_each_entry_safe(phy, tmp, &fakelb_phys, list)
fakelb_del(phy);
- spin_unlock(&fakelb_phys_lock);
+ mutex_unlock(&fakelb_phys_lock);
return 0;
}
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index dc7d970bd1c0..effcdbfb06e9 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -164,6 +164,7 @@ static void loopback_setup(struct net_device *dev)
{
dev->mtu = 64 * 1024;
dev->hard_header_len = ETH_HLEN; /* 14 */
+ dev->min_header_len = ETH_HLEN; /* 14 */
dev->addr_len = ETH_ALEN; /* 6 */
dev->type = ARPHRD_LOOPBACK; /* 0x0001*/
dev->flags = IFF_LOOPBACK;
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 159a68782bec..79de9608ac48 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -725,7 +725,7 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
ssize_t n;
if (q->flags & IFF_VNET_HDR) {
- vnet_hdr_len = q->vnet_hdr_sz;
+ vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz);
err = -EINVAL;
if (len < vnet_hdr_len)
@@ -865,7 +865,7 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q,
if (q->flags & IFF_VNET_HDR) {
struct virtio_net_hdr vnet_hdr;
- vnet_hdr_len = q->vnet_hdr_sz;
+ vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz);
if (iov_iter_count(iter) < vnet_hdr_len)
return -EINVAL;
diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c
index 86b28052bf06..9b709f78bb03 100644
--- a/drivers/net/phy/bcm63xx.c
+++ b/drivers/net/phy/bcm63xx.c
@@ -21,6 +21,23 @@ MODULE_DESCRIPTION("Broadcom 63xx internal PHY driver");
MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>");
MODULE_LICENSE("GPL");
+static int bcm63xx_config_intr(struct phy_device *phydev)
+{
+ int reg, err;
+
+ reg = phy_read(phydev, MII_BCM63XX_IR);
+ if (reg < 0)
+ return reg;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ reg &= ~MII_BCM63XX_IR_GMASK;
+ else
+ reg |= MII_BCM63XX_IR_GMASK;
+
+ err = phy_write(phydev, MII_BCM63XX_IR, reg);
+ return err;
+}
+
static int bcm63xx_config_init(struct phy_device *phydev)
{
int reg, err;
@@ -55,7 +72,7 @@ static struct phy_driver bcm63xx_driver[] = {
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
- .config_intr = bcm_phy_config_intr,
+ .config_intr = bcm63xx_config_intr,
.driver = { .owner = THIS_MODULE },
}, {
/* same phy as above, with just a different OUI */
@@ -68,7 +85,7 @@ static struct phy_driver bcm63xx_driver[] = {
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
- .config_intr = bcm_phy_config_intr,
+ .config_intr = bcm63xx_config_intr,
.driver = { .owner = THIS_MODULE },
} };
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index dd7b7d64c90a..a444294fb555 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1105,9 +1105,11 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
}
if (tun->flags & IFF_VNET_HDR) {
- if (len < tun->vnet_hdr_sz)
+ int vnet_hdr_sz = READ_ONCE(tun->vnet_hdr_sz);
+
+ if (len < vnet_hdr_sz)
return -EINVAL;
- len -= tun->vnet_hdr_sz;
+ len -= vnet_hdr_sz;
n = copy_from_iter(&gso, sizeof(gso), from);
if (n != sizeof(gso))
@@ -1119,7 +1121,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
if (tun16_to_cpu(tun, gso.hdr_len) > len)
return -EINVAL;
- iov_iter_advance(from, tun->vnet_hdr_sz - sizeof(gso));
+ iov_iter_advance(from, vnet_hdr_sz - sizeof(gso));
}
if ((tun->flags & TUN_TYPE_MASK) == IFF_TAP) {
@@ -1302,7 +1304,7 @@ static ssize_t tun_put_user(struct tun_struct *tun,
vlan_hlen = VLAN_HLEN;
if (tun->flags & IFF_VNET_HDR)
- vnet_hdr_sz = tun->vnet_hdr_sz;
+ vnet_hdr_sz = READ_ONCE(tun->vnet_hdr_sz);
total = skb->len + vlan_hlen + vnet_hdr_sz;
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 7cba2c3759df..8c408aa2f208 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -462,6 +462,7 @@ static const struct driver_info wwan_info = {
#define SAMSUNG_VENDOR_ID 0x04e8
#define LENOVO_VENDOR_ID 0x17ef
#define NVIDIA_VENDOR_ID 0x0955
+#define HP_VENDOR_ID 0x03f0
static const struct usb_device_id products[] = {
/* BLACKLIST !!
@@ -608,6 +609,13 @@ static const struct usb_device_id products[] = {
.driver_info = 0,
},
+/* HP lt2523 (Novatel E371) - handled by qmi_wwan */
+{
+ USB_DEVICE_AND_INTERFACE_INFO(HP_VENDOR_ID, 0x421d, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+ .driver_info = 0,
+},
+
/* AnyDATA ADU960S - handled by qmi_wwan */
{
USB_DEVICE_AND_INTERFACE_INFO(0x16d5, 0x650a, USB_CLASS_COMM,
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index a34f491224c1..09052f9e324f 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -485,6 +485,13 @@ static const struct usb_device_id products[] = {
USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&qmi_wwan_info,
},
+ { /* HP lt2523 (Novatel E371) */
+ USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d,
+ USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET,
+ USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&qmi_wwan_info,
+ },
{ /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */
USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7),
.driver_info = (unsigned long)&qmi_wwan_info,
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 2fb637ad594a..fbb1867ff25c 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -1645,7 +1645,7 @@ static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc)
u8 checksum = CHECKSUM_NONE;
u32 opts2, opts3;
- if (tp->version == RTL_VER_01)
+ if (!(tp->netdev->features & NETIF_F_RXCSUM))
goto return_result;
opts2 = le32_to_cpu(rx_desc->opts2);
@@ -3442,43 +3442,93 @@ static bool delay_autosuspend(struct r8152 *tp)
*/
if (!sw_linking && tp->rtl_ops.in_nway(tp))
return true;
+ else if (!skb_queue_empty(&tp->tx_queue))
+ return true;
else
return false;
}
-static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message)
+static int rtl8152_rumtime_suspend(struct r8152 *tp)
{
- struct r8152 *tp = usb_get_intfdata(intf);
struct net_device *netdev = tp->netdev;
int ret = 0;
- mutex_lock(&tp->control);
+ if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) {
+ u32 rcr = 0;
- if (PMSG_IS_AUTO(message)) {
- if (netif_running(netdev) && delay_autosuspend(tp)) {
+ if (delay_autosuspend(tp)) {
ret = -EBUSY;
goto out1;
}
- set_bit(SELECTIVE_SUSPEND, &tp->flags);
- } else {
- netif_device_detach(netdev);
+ if (netif_carrier_ok(netdev)) {
+ u32 ocp_data;
+
+ rcr = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
+ ocp_data = rcr & ~RCR_ACPT_ALL;
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+ rxdy_gated_en(tp, true);
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA,
+ PLA_OOB_CTRL);
+ if (!(ocp_data & RXFIFO_EMPTY)) {
+ rxdy_gated_en(tp, false);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, rcr);
+ ret = -EBUSY;
+ goto out1;
+ }
+ }
+
+ clear_bit(WORK_ENABLE, &tp->flags);
+ usb_kill_urb(tp->intr_urb);
+
+ rtl_runtime_suspend_enable(tp, true);
+
+ if (netif_carrier_ok(netdev)) {
+ napi_disable(&tp->napi);
+ rtl_stop_rx(tp);
+ rxdy_gated_en(tp, false);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, rcr);
+ napi_enable(&tp->napi);
+ }
}
+ set_bit(SELECTIVE_SUSPEND, &tp->flags);
+
+out1:
+ return ret;
+}
+
+static int rtl8152_system_suspend(struct r8152 *tp)
+{
+ struct net_device *netdev = tp->netdev;
+ int ret = 0;
+
+ netif_device_detach(netdev);
+
if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) {
clear_bit(WORK_ENABLE, &tp->flags);
usb_kill_urb(tp->intr_urb);
napi_disable(&tp->napi);
- if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
- rtl_stop_rx(tp);
- rtl_runtime_suspend_enable(tp, true);
- } else {
- cancel_delayed_work_sync(&tp->schedule);
- tp->rtl_ops.down(tp);
- }
+ cancel_delayed_work_sync(&tp->schedule);
+ tp->rtl_ops.down(tp);
napi_enable(&tp->napi);
}
-out1:
+
+ return ret;
+}
+
+static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct r8152 *tp = usb_get_intfdata(intf);
+ int ret;
+
+ mutex_lock(&tp->control);
+
+ if (PMSG_IS_AUTO(message))
+ ret = rtl8152_rumtime_suspend(tp);
+ else
+ ret = rtl8152_system_suspend(tp);
+
mutex_unlock(&tp->control);
return ret;
@@ -4173,6 +4223,11 @@ static int rtl8152_probe(struct usb_interface *intf,
NETIF_F_HIGHDMA | NETIF_F_FRAGLIST |
NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
+ if (tp->version == RTL_VER_01) {
+ netdev->features &= ~NETIF_F_RXCSUM;
+ netdev->hw_features &= ~NETIF_F_RXCSUM;
+ }
+
netdev->ethtool_ops = &ops;
netif_set_gso_max_size(netdev, RTL_LIMITED_TSO_SIZE);
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 903bda437839..d6b619667f1a 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -301,7 +301,9 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb,
.flowi4_tos = RT_TOS(ip4h->tos),
.flowi4_flags = FLOWI_FLAG_ANYSRC | FLOWI_FLAG_L3MDEV_SRC |
FLOWI_FLAG_SKIP_NH_OIF,
+ .flowi4_proto = ip4h->protocol,
.daddr = ip4h->daddr,
+ .saddr = ip4h->saddr,
};
if (vrf_send_v4_prep(skb, &fl4, vrf_dev))
@@ -410,6 +412,8 @@ static int vrf_finish_output6(struct net *net, struct sock *sk,
struct in6_addr *nexthop;
int ret;
+ nf_reset(skb);
+
skb->protocol = htons(ETH_P_IPV6);
skb->dev = dev;
@@ -521,6 +525,8 @@ static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *s
u32 nexthop;
int ret = -EINVAL;
+ nf_reset(skb);
+
/* Be paranoid, rather than too clever. */
if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
struct sk_buff *skb2;
@@ -919,6 +925,8 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev,
return -EINVAL;
vrf->tb_id = nla_get_u32(data[IFLA_VRF_TABLE]);
+ if (vrf->tb_id == RT_TABLE_UNSPEC)
+ return -EINVAL;
dev->priv_flags |= IFF_L3MDEV_MASTER;
diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile
index 25b23bf2c8e6..5fe8bc184868 100644
--- a/drivers/net/wireless/ath/ath10k/Makefile
+++ b/drivers/net/wireless/ath/ath10k/Makefile
@@ -26,6 +26,7 @@ ath10k_pci-y += pci.o \
ce.o
obj-$(CONFIG_ATH10K_TARGET_SNOC) += ath10k_snoc.o
ath10k_snoc-y += snoc.o \
+ qmi.o \
ce.o
ath10k_pci-$(CONFIG_ATH10K_AHB) += ahb.o
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index b8a3a1ecabaa..9cda1303c9e1 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -455,6 +455,9 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
u32 desc_flags = 0;
int ret = 0;
+ if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ return -ESHUTDOWN;
+
if (nbytes > ce_state->src_sz_max)
ath10k_warn(ar, "%s: send more we can (nbytes: %d, max: %d)\n",
__func__, nbytes, ce_state->src_sz_max);
@@ -942,6 +945,9 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar)
struct ath10k_ce_pipe *ce_state;
struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ return;
+
if (ar->target_version == ATH10K_HW_WCN3990)
intr_summary = 0xFFF;
else
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index d37ed66d767b..9acaffa51516 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -1536,7 +1536,6 @@ static void ath10k_core_restart(struct work_struct *work)
struct ath10k *ar = container_of(work, struct ath10k, restart_work);
set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags);
- ath10k_gen_set_base_mac_addr(ar, ar->base_mac_addr);
/* Place a barrier to make sure the compiler doesn't reorder
* CRASH_FLUSH and calling other functions.
@@ -2390,6 +2389,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
mutex_init(&ar->conf_mutex);
spin_lock_init(&ar->data_lock);
spin_lock_init(&ar->txqs_lock);
+ spin_lock_init(&ar->datapath_rx_stat_lock);
INIT_LIST_HEAD(&ar->txqs);
INIT_LIST_HEAD(&ar->peers);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 21c63d5d3ead..01d5ecc4f6b8 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -70,6 +70,20 @@
#define ATH10K_NAPI_BUDGET 64
#define ATH10K_NAPI_QUOTA_LIMIT 60
+#define ATH10K_RX_MCS_MIN 0
+#define ATH10K_RX_HT_MCS_MAX 32
+#define ATH10K_RX_VHT_RATEIDX_MAX 9
+#define ATH10K_RX_VHT_MCS_MAX 20 /* For 2x2 */
+#define ATH10K_RX_NSS_MIN 0
+#define ATH10K_RX_NSS_MAX 5
+
+enum ath10k_datapath_rx_band {
+ ATH10K_BAND_MIN,
+ ATH10K_BAND_2GHZ = ATH10K_BAND_MIN,
+ ATH10K_BAND_5GHZ,
+ ATH10K_BAND_MAX,
+};
+
struct ath10k;
enum ath10k_bus {
@@ -703,6 +717,20 @@ struct ath10k_fw_components {
struct ath10k_fw_file fw_file;
};
+struct datapath_rx_stats {
+ u32 no_of_packets;
+ u32 short_gi_pkts;
+ u32 ht_rate_indx[ATH10K_RX_HT_MCS_MAX + 1];
+ u32 vht_rate_indx[ATH10K_RX_VHT_MCS_MAX + 1];
+ u32 ht_rate_packets;
+ u32 vht_rate_packets;
+ u32 legacy_pkt;
+ u32 nss[ATH10K_RX_NSS_MAX + 1];
+ u32 num_pkts_40Mhz;
+ u32 num_pkts_80Mhz;
+ u32 band[ATH10K_BAND_MAX + 1];
+};
+
struct ath10k {
struct ath_common ath_common;
struct ieee80211_hw *hw;
@@ -729,7 +757,7 @@ struct ath10k {
u32 max_spatial_stream;
/* protected by conf_mutex */
bool ani_enabled;
-
+ bool sifs_burst_enabled;
bool p2p;
struct {
@@ -900,7 +928,10 @@ struct ath10k {
enum ath10k_spectral_mode mode;
struct ath10k_spec_scan config;
} spectral;
+ struct datapath_rx_stats *rx_stats;
#endif
+ /* prevent concurrency histogram for receiving data packet */
+ spinlock_t datapath_rx_stat_lock;
struct {
/* protected by conf_mutex */
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index c36c2481856b..ec8063e7986a 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -537,6 +537,230 @@ static const struct file_operations fops_fw_stats = {
.llseek = default_llseek,
};
+static inline int is_vht_rate_valid(u32 rate_indx)
+{
+ if ((rate_indx >= ATH10K_RX_MCS_MIN) &&
+ (rate_indx <= ATH10K_RX_VHT_RATEIDX_MAX))
+ return 1;
+ else
+ return 0;
+}
+
+void fill_datapath_stats(struct ath10k *ar, struct ieee80211_rx_status *status)
+{
+ struct datapath_rx_stats *stat_cnt = ar->rx_stats;
+
+ spin_lock_bh(&ar->datapath_rx_stat_lock);
+
+ stat_cnt->no_of_packets += 1;
+ if (!(stat_cnt->no_of_packets)) {
+ memset(stat_cnt, 0, sizeof(*stat_cnt));
+ stat_cnt->no_of_packets += 1;
+ }
+
+ if (status->flag & RX_FLAG_SHORT_GI)
+ stat_cnt->short_gi_pkts += 1;
+
+ if ((status->vht_nss >= ATH10K_RX_NSS_MIN) &&
+ (status->vht_nss < ATH10K_RX_NSS_MAX)) {
+ stat_cnt->nss[status->vht_nss] += 1;
+ if (status->flag & RX_FLAG_VHT) {
+ stat_cnt->vht_rate_packets += 1;
+ if (is_vht_rate_valid(status->rate_idx)) {
+ stat_cnt->vht_rate_indx[((status->vht_nss - 1) *
+ 10) + status->rate_idx] += 1;
+ } else {
+ /*if we get index other than (>=0 and <=9)*/
+ stat_cnt->vht_rate_indx[ATH10K_RX_VHT_MCS_MAX] += 1;
+ }
+ } else if (status->flag & RX_FLAG_HT) {
+ stat_cnt->ht_rate_packets += 1;
+ if ((status->rate_idx >= ATH10K_RX_MCS_MIN) &&
+ (status->rate_idx < ATH10K_RX_HT_MCS_MAX))
+ stat_cnt->ht_rate_indx[status->rate_idx] += 1;
+ else {
+ /*if we get index other than (>=0 and <=31)*/
+ stat_cnt->ht_rate_indx[ATH10K_RX_HT_MCS_MAX] += 1;
+ }
+ } else {
+ /* if pkt is other than HT and VHT */
+ stat_cnt->legacy_pkt += 1;
+ }
+ } else {
+ stat_cnt->nss[ATH10K_RX_NSS_MAX] += 1;
+ }
+
+ if (status->flag & RX_FLAG_40MHZ)
+ stat_cnt->num_pkts_40Mhz += 1;
+ if (status->vht_flag & RX_VHT_FLAG_80MHZ)
+ stat_cnt->num_pkts_80Mhz += 1;
+ if ((status->band >= ATH10K_BAND_MIN) &&
+ (status->band < ATH10K_BAND_MAX)) {
+ stat_cnt->band[status->band] += 1;
+ } else {
+ /*if band is other than 0,1 */
+ stat_cnt->band[ATH10K_BAND_MAX] += 1;
+ }
+
+ spin_unlock_bh(&ar->datapath_rx_stat_lock);
+}
+
+size_t get_datapath_stat(char *buf, struct ath10k *ar)
+{
+ u8 i;
+ struct datapath_rx_stats *stat_cnt = ar->rx_stats;
+ size_t j = 0;
+
+ spin_lock(&ar->datapath_rx_stat_lock);
+
+ j = snprintf(buf, ATH10K_DATAPATH_BUF_SIZE, "\nNo of packets: %u\t"
+ "No of short_gi packets: %u\n"
+ "\nHT Packets: %u \t VHT Packets: %u\n"
+ "\n40Mhz Packets: %u \t 80Mhz Packets: %u\n"
+ "\n2.4GHz: %u \t 5GHz: %u \t band-error: %u\n\n",
+ stat_cnt->no_of_packets,
+ stat_cnt->short_gi_pkts,
+ stat_cnt->ht_rate_packets,
+ stat_cnt->vht_rate_packets,
+ stat_cnt->num_pkts_40Mhz,
+ stat_cnt->num_pkts_80Mhz,
+ stat_cnt->band[ATH10K_BAND_2GHZ],
+ stat_cnt->band[ATH10K_BAND_5GHZ],
+ stat_cnt->band[ATH10K_BAND_MAX]);
+
+ for (i = 0; i <= ATH10K_RX_NSS_MAX; i++) {
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "NSS-%u: %u\t", i, stat_cnt->nss[i]);
+ }
+
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "\n\n----HT Rate index------\n");
+
+ for (i = ATH10K_RX_MCS_MIN; i < ATH10K_RX_HT_MCS_MAX;
+ i += 4) {
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "ht_rate_indx[%02u]: %10u\tht_rate_indx[%02u]: %10u\t"
+ "ht_rate_indx[%02u]: %10u\tht_rate_indx[%02u]: %10u\n",
+ i, stat_cnt->ht_rate_indx[i],
+ i + 1, stat_cnt->ht_rate_indx[i + 1],
+ i + 2, stat_cnt->ht_rate_indx[i + 2],
+ i + 3, stat_cnt->ht_rate_indx[i + 3]);
+ }
+
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "ht_rate_indx[OOB]: %10u\n",
+ stat_cnt->ht_rate_indx[ATH10K_RX_HT_MCS_MAX]);
+
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "\n----VHT Rate index------\n");
+
+ for (i = ATH10K_RX_MCS_MIN;
+ i <= ATH10K_RX_VHT_RATEIDX_MAX; i++) {
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "vht_rate_indx[%02u]: %10u\tvht_rate_indx[%02u]: %10u\n",
+ i, stat_cnt->vht_rate_indx[i],
+ i + 10, stat_cnt->vht_rate_indx[i + 10]);
+ }
+
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "vht_rate_indx[%02u]: %10u\n",
+ i + 10, stat_cnt->vht_rate_indx[i + 10]);
+
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "\nnumber of pkt other than HT and VHT(legacy) : %u\n"
+ "----------------------\n",
+ stat_cnt->legacy_pkt);
+
+ spin_unlock(&ar->datapath_rx_stat_lock);
+
+ return j;
+}
+
+static int ath10k_datapath_stats_open(struct inode *inode, struct file *file)
+{
+ struct ath10k *ar = inode->i_private;
+ int ret;
+
+ spin_lock(&ar->datapath_rx_stat_lock);
+
+ if (ar->state != ATH10K_STATE_ON) {
+ ret = -ENETDOWN;
+ goto err_unlock;
+ }
+
+ file->private_data = ar;
+
+ spin_unlock(&ar->datapath_rx_stat_lock);
+ return 0;
+
+err_unlock:
+ spin_unlock(&ar->datapath_rx_stat_lock);
+ return ret;
+}
+
+static ssize_t ath10k_datapath_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ size_t buf_len;
+ unsigned int ret;
+ void *buf = NULL;
+
+ buf = vmalloc(ATH10K_DATAPATH_BUF_SIZE);
+ if (!buf)
+ return 0;
+
+ buf_len = get_datapath_stat(buf, ar);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, buf_len);
+ vfree(buf);
+
+ return ret;
+}
+
+static ssize_t ath10k_datapath_stats_write(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u32 filter;
+ int ret;
+
+ if (kstrtouint_from_user(ubuf, count, 0, &filter))
+ return -EINVAL;
+
+ spin_lock(&ar->datapath_rx_stat_lock);
+
+ if (ar->state != ATH10K_STATE_ON) {
+ ret = count;
+ goto err_unlock;
+ }
+
+ if (!filter)
+ memset(ar->rx_stats, 0, sizeof(*ar->rx_stats));
+
+ ret = count;
+
+err_unlock:
+ spin_unlock(&ar->datapath_rx_stat_lock);
+ return ret;
+}
+
+static int ath10k_datapath_stats_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations fops_datapath_stats = {
+ .open = ath10k_datapath_stats_open,
+ .read = ath10k_datapath_stats_read,
+ .write = ath10k_datapath_stats_write,
+ .release = ath10k_datapath_stats_release,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
static ssize_t ath10k_debug_fw_reset_stats_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
@@ -1572,6 +1796,64 @@ static const struct file_operations fops_ani_enable = {
.llseek = default_llseek,
};
+static ssize_t ath10k_write_sifs_burst_enable(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ int ret;
+ u8 enable;
+
+ if (kstrtou8_from_user(user_buf, count, 0, &enable))
+ return -EINVAL;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->sifs_burst_enabled == enable) {
+ ret = count;
+ goto exit;
+ }
+
+ ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->burst_enable,
+ enable);
+ if (ret) {
+ ath10k_warn(ar, "sifs_burst_enable failed: %d\n", ret);
+ goto exit;
+ }
+ ar->sifs_burst_enabled = enable;
+
+ ret = count;
+
+exit:
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
+static ssize_t ath10k_read_sifs_burst_enable(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ char buf[32];
+ int len;
+ bool ret = false;
+
+ if (ar->sifs_burst_enabled)
+ ret = true;
+
+ len = scnprintf(buf, sizeof(buf), "%d\n", ret);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_sifs_burst_enable = {
+ .read = ath10k_read_sifs_burst_enable,
+ .write = ath10k_write_sifs_burst_enable,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
static const struct file_operations fops_cal_data = {
.open = ath10k_debug_cal_data_open,
.read = ath10k_debug_cal_data_read,
@@ -2343,7 +2625,11 @@ int ath10k_debug_create(struct ath10k *ar)
ar->debug.cal_data = vzalloc(ATH10K_DEBUG_CAL_DATA_LEN);
if (!ar->debug.cal_data)
- return -ENOMEM;
+ goto err_cal_data;
+
+ ar->rx_stats = vzalloc(sizeof(*ar->rx_stats));
+ if (!ar->rx_stats)
+ goto err_rx_stats;
INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);
INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs);
@@ -2351,6 +2637,13 @@ int ath10k_debug_create(struct ath10k *ar)
INIT_LIST_HEAD(&ar->debug.fw_stats.peers_extd);
return 0;
+
+err_rx_stats:
+ vfree(ar->debug.cal_data);
+
+err_cal_data:
+ vfree(ar->debug.fw_crash_data);
+ return -ENOMEM;
}
void ath10k_debug_destroy(struct ath10k *ar)
@@ -2361,6 +2654,9 @@ void ath10k_debug_destroy(struct ath10k *ar)
vfree(ar->debug.cal_data);
ar->debug.cal_data = NULL;
+ vfree(ar->rx_stats);
+ ar->rx_stats = NULL;
+
ath10k_debug_fw_stats_reset(ar);
kfree(ar->debug.tpc_stats);
@@ -2383,6 +2679,9 @@ int ath10k_debug_register(struct ath10k *ar)
init_completion(&ar->debug.tpc_complete);
init_completion(&ar->debug.fw_stats_complete);
+ debugfs_create_file("datapath_rx_stats", S_IRUSR, ar->debug.debugfs_phy,
+ ar, &fops_datapath_stats);
+
debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar,
&fops_fw_stats);
@@ -2432,6 +2731,9 @@ int ath10k_debug_register(struct ath10k *ar)
debugfs_create_file("ani_enable", S_IRUSR | S_IWUSR,
ar->debug.debugfs_phy, ar, &fops_ani_enable);
+ debugfs_create_file("sifs_burst_enable", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_sifs_burst_enable);
+
if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED)) {
debugfs_create_file("dfs_simulate_radar", S_IWUSR,
ar->debug.debugfs_phy, ar,
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index b1db01a167ac..f963391e3544 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -38,7 +38,7 @@ enum ath10k_debug_mask {
ATH10K_DBG_WMI_PRINT = 0x00002000,
ATH10K_DBG_PCI_PS = 0x00004000,
ATH10K_DBG_AHB = 0x00008000,
- ATH10K_DBG_SNOC = 0x00009000,
+ ATH10K_DBG_SNOC = 0x00010000,
ATH10K_DBG_ANY = 0xffffffff,
};
@@ -59,6 +59,7 @@ enum ath10k_dbg_aggr_mode {
/* FIXME: How to calculate the buffer size sanely? */
#define ATH10K_FW_STATS_BUF_SIZE (1024 * 1024)
+#define ATH10K_DATAPATH_BUF_SIZE (1024 * 1024)
extern unsigned int ath10k_debug_mask;
@@ -95,6 +96,8 @@ int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw,
void ath10k_debug_get_et_stats(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ethtool_stats *stats, u64 *data);
+void fill_datapath_stats(struct ath10k *ar, struct ieee80211_rx_status *status);
+size_t get_datapath_stat(char *buf, struct ath10k *ar);
#else
static inline int ath10k_debug_start(struct ath10k *ar)
{
@@ -145,6 +148,16 @@ ath10k_debug_get_new_fw_crash_data(struct ath10k *ar)
return NULL;
}
+static inline void fill_datapath_stats(struct ath10k *ar,
+ struct ieee80211_rx_status *status)
+{
+}
+
+static inline size_t get_datapath_stat(char *buf, struct ath10k *ar)
+{
+ return 0;
+}
+
#define ATH10K_DFS_STAT_INC(ar, c) do { } while (0)
#define ath10k_debug_get_et_strings NULL
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index ddf097e3a143..437ea2c192b3 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -940,7 +940,7 @@ static void ath10k_process_rx(struct ath10k *ar,
status = IEEE80211_SKB_RXCB(skb);
*status = *rx_status;
-
+ fill_datapath_stats(ar, status);
ath10k_dbg(ar, ATH10K_DBG_DATA,
"rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",
skb,
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 2e51590dacbb..35e5d980ed49 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3506,6 +3506,7 @@ static int ath10k_mac_tx(struct ath10k *ar,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int ret;
+ skb_orphan(skb);
/* We should disable CCK RATE due to P2P */
if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
@@ -4449,7 +4450,8 @@ static int ath10k_start(struct ieee80211_hw *hw)
ar->state = ATH10K_STATE_ON;
break;
case ATH10K_STATE_RESTARTING:
- ath10k_halt(ar);
+ if (!test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ ath10k_halt(ar);
ar->state = ATH10K_STATE_RESTARTED;
break;
case ATH10K_STATE_ON:
@@ -4537,6 +4539,7 @@ static int ath10k_start(struct ieee80211_hw *hw)
ret);
goto err_core_stop;
}
+ ar->sifs_burst_enabled = false;
}
param = ar->wmi.pdev_param->ani_enable;
diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c
new file mode 100644
index 000000000000..7b5fc52d269a
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/qmi.c
@@ -0,0 +1,230 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <soc/qcom/subsystem_notif.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/service-notifier.h>
+#include "core.h"
+#include "qmi.h"
+#include "snoc.h"
+#include <soc/qcom/icnss.h>
+
+static int
+ath10k_snoc_service_notifier_notify(struct notifier_block *nb,
+ unsigned long notification, void *data)
+{
+ struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc,
+ service_notifier_nb);
+ enum pd_subsys_state *state = data;
+ struct ath10k *ar = ar_snoc->ar;
+
+ switch (notification) {
+ case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01:
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Service down, data: 0x%pK\n",
+ data);
+
+ if (!state || *state != ROOT_PD_SHUTDOWN)
+ atomic_set(&ar_snoc->fw_crashed, 1);
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "PD went down %d\n",
+ atomic_read(&ar_snoc->fw_crashed));
+ break;
+ case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Service up\n");
+ queue_work(ar->workqueue, &ar->restart_work);
+ break;
+ default:
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "Service state Unknown, notification: 0x%lx\n",
+ notification);
+ return NOTIFY_DONE;
+ }
+ return NOTIFY_OK;
+}
+
+static int ath10k_snoc_get_service_location_notify(struct notifier_block *nb,
+ unsigned long opcode,
+ void *data)
+{
+ struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc,
+ get_service_nb);
+ struct ath10k *ar = ar_snoc->ar;
+ struct pd_qmi_client_data *pd = data;
+ int curr_state;
+ int ret;
+ int i;
+ struct ath10k_service_notifier_context *notifier;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Get service notify opcode: %lu\n",
+ opcode);
+
+ if (opcode != LOCATOR_UP)
+ return NOTIFY_DONE;
+
+ if (!pd->total_domains) {
+ ath10k_err(ar, "Did not find any domains\n");
+ ret = -ENOENT;
+ goto out;
+ }
+
+ notifier = kcalloc(pd->total_domains,
+ sizeof(struct ath10k_service_notifier_context),
+ GFP_KERNEL);
+ if (!notifier) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ar_snoc->service_notifier_nb.notifier_call =
+ ath10k_snoc_service_notifier_notify;
+
+ for (i = 0; i < pd->total_domains; i++) {
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "%d: domain_name: %s, instance_id: %d\n", i,
+ pd->domain_list[i].name,
+ pd->domain_list[i].instance_id);
+
+ notifier[i].handle =
+ service_notif_register_notifier(
+ pd->domain_list[i].name,
+ pd->domain_list[i].instance_id,
+ &ar_snoc->service_notifier_nb,
+ &curr_state);
+ notifier[i].instance_id = pd->domain_list[i].instance_id;
+ strlcpy(notifier[i].name, pd->domain_list[i].name,
+ QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
+
+ if (IS_ERR(notifier[i].handle)) {
+ ath10k_err(ar, "%d: Unable to register notifier for %s(0x%x)\n",
+ i, pd->domain_list->name,
+ pd->domain_list->instance_id);
+ ret = PTR_ERR(notifier[i].handle);
+ goto free_handle;
+ }
+ }
+
+ ar_snoc->service_notifier = notifier;
+ ar_snoc->total_domains = pd->total_domains;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "PD restart enabled\n");
+
+ return NOTIFY_OK;
+
+free_handle:
+ for (i = 0; i < pd->total_domains; i++) {
+ if (notifier[i].handle) {
+ service_notif_unregister_notifier(
+ notifier[i].handle,
+ &ar_snoc->service_notifier_nb);
+ }
+ }
+ kfree(notifier);
+
+out:
+ ath10k_err(ar, "PD restart not enabled: %d\n", ret);
+
+ return NOTIFY_OK;
+}
+
+int ath10k_snoc_pd_restart_enable(struct ath10k *ar)
+{
+ int ret;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Get service location\n");
+
+ ar_snoc->get_service_nb.notifier_call =
+ ath10k_snoc_get_service_location_notify;
+ ret = get_service_location(ATH10K_SERVICE_LOCATION_CLIENT_NAME,
+ ATH10K_WLAN_SERVICE_NAME,
+ &ar_snoc->get_service_nb);
+ if (ret) {
+ ath10k_err(ar, "Get service location failed: %d\n", ret);
+ goto out;
+ }
+
+ return 0;
+out:
+ ath10k_err(ar, "PD restart not enabled: %d\n", ret);
+ return ret;
+}
+
+int ath10k_snoc_pdr_unregister_notifier(struct ath10k *ar)
+{
+ int i;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ for (i = 0; i < ar_snoc->total_domains; i++) {
+ if (ar_snoc->service_notifier[i].handle)
+ service_notif_unregister_notifier(
+ ar_snoc->service_notifier[i].handle,
+ &ar_snoc->service_notifier_nb);
+ }
+
+ kfree(ar_snoc->service_notifier);
+
+ ar_snoc->service_notifier = NULL;
+
+ return 0;
+}
+
+static int ath10k_snoc_modem_notifier_nb(struct notifier_block *nb,
+ unsigned long code,
+ void *data)
+{
+ struct notif_data *notif = data;
+ struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc,
+ modem_ssr_nb);
+ struct ath10k *ar = ar_snoc->ar;
+
+ if (code != SUBSYS_BEFORE_SHUTDOWN)
+ return NOTIFY_OK;
+
+ if (notif->crashed)
+ atomic_set(&ar_snoc->fw_crashed, 1);
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Modem went down %d\n",
+ atomic_read(&ar_snoc->fw_crashed));
+ if (notif->crashed)
+ queue_work(ar->workqueue, &ar->restart_work);
+
+ return NOTIFY_OK;
+}
+
+int ath10k_snoc_modem_ssr_register_notifier(struct ath10k *ar)
+{
+ int ret = 0;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ ar_snoc->modem_ssr_nb.notifier_call = ath10k_snoc_modem_notifier_nb;
+
+ ar_snoc->modem_notify_handler =
+ subsys_notif_register_notifier("modem", &ar_snoc->modem_ssr_nb);
+
+ if (IS_ERR(ar_snoc->modem_notify_handler)) {
+ ret = PTR_ERR(ar_snoc->modem_notify_handler);
+ ath10k_err(ar, "Modem register notifier failed: %d\n", ret);
+ }
+
+ return ret;
+}
+
+int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar)
+{
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ subsys_notif_unregister_notifier(ar_snoc->modem_notify_handler,
+ &ar_snoc->modem_ssr_nb);
+ ar_snoc->modem_notify_handler = NULL;
+
+ return 0;
+}
+
diff --git a/drivers/net/wireless/ath/ath10k/qmi.h b/drivers/net/wireless/ath/ath10k/qmi.h
new file mode 100644
index 000000000000..f8ba3288753b
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/qmi.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _QMI_H_
+#define _QMI_H_
+int ath10k_snoc_pd_restart_enable(struct ath10k *ar);
+int ath10k_snoc_modem_ssr_register_notifier(struct ath10k *ar);
+int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar);
+int ath10k_snoc_pdr_unregister_notifier(struct ath10k *ar);
+
+#endif
diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
index 081e44b3277a..89042dcf70a0 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.c
+++ b/drivers/net/wireless/ath/ath10k/snoc.c
@@ -24,6 +24,7 @@
#include "htc.h"
#include "ce.h"
#include "snoc.h"
+#include "qmi.h"
#include <soc/qcom/icnss.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -96,8 +97,8 @@ static struct ce_attr host_ce_config_wlan[] = {
/* CE4: host->target HTT */
{
.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
- .src_nentries = 256,
- .src_sz_max = 256,
+ .src_nentries = 2048,
+ .src_sz_max = 2048,
.dest_nentries = 0,
.send_cb = ath10k_snoc_htt_tx_cb,
},
@@ -413,6 +414,20 @@ static struct ath10k_shadow_reg_cfg target_shadow_reg_cfg_map[] = {
{ 11, WCN3990_DST_WR_INDEX_OFFSET},
};
+static bool ath10k_snoc_has_fw_crashed(struct ath10k *ar)
+{
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ return atomic_read(&ar_snoc->fw_crashed);
+}
+
+static void ath10k_snoc_fw_crashed_clear(struct ath10k *ar)
+{
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ atomic_set(&ar_snoc->fw_crashed, 0);
+}
+
void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value)
{
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
@@ -655,6 +670,9 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
"snoc tx item %d paddr %pad len %d n_items %d\n",
i, &items[i].paddr, items[i].len, n_items);
+ if (ath10k_snoc_has_fw_crashed(ar))
+ return -EINVAL;
+
err = ath10k_ce_send_nolock(ce_pipe,
items[i].transfer_context,
items[i].paddr,
@@ -867,11 +885,17 @@ static void ath10k_snoc_hif_stop(struct ath10k *ar)
{
if (!ar)
return;
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n");
- ath10k_snoc_irq_disable(ar);
+ if (ath10k_snoc_has_fw_crashed(ar) ||
+ test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
+ ath10k_snoc_free_irq(ar);
+ } else {
+ ath10k_snoc_irq_disable(ar);
+ }
+
ath10k_snoc_flush(ar);
napi_synchronize(&ar->napi);
napi_disable(&ar->napi);
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n");
}
static int ath10k_snoc_alloc_pipes(struct ath10k *ar)
@@ -1002,24 +1026,28 @@ static void ath10k_snoc_free_irq(struct ath10k *ar)
static int ath10k_snoc_get_soc_info(struct ath10k *ar)
{
- int ret;
- struct icnss_soc_info soc_info;
+ struct resource *res;
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+ struct platform_device *pdev;
- memset(&soc_info, 0, sizeof(soc_info));
-
- ret = icnss_get_soc_info(&soc_info);
- if (ret < 0) {
- ath10k_err(ar, "%s: icnss_get_soc_info error = %d",
- __func__, ret);
- return ret;
+ pdev = ar_snoc->dev;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
+ if (!res) {
+ ath10k_err(ar, "Memory base not found in DT\n");
+ return -EINVAL;
}
- ar_snoc->mem = soc_info.v_addr;
- ar_snoc->mem_pa = soc_info.p_addr;
+ ar_snoc->mem_pa = res->start;
+ ar_snoc->mem = devm_ioremap(&pdev->dev, ar_snoc->mem_pa,
+ resource_size(res));
+ if (!ar_snoc->mem) {
+ ath10k_err(ar, "Memory base ioremap failed: phy addr: %pa\n",
+ &ar_snoc->mem_pa);
+ return -EINVAL;
+ }
- ar_snoc->target_info.soc_version = soc_info.soc_id;
- ar_snoc->target_info.target_version = soc_info.soc_id;
+ ar_snoc->target_info.soc_version = ATH10K_HW_WCN3990;
+ ar_snoc->target_info.target_version = ATH10K_HW_WCN3990;
ar_snoc->target_info.target_revision = 0;
ath10k_dbg(ar, ATH10K_DBG_SNOC,
@@ -1034,10 +1062,8 @@ static int ath10k_snoc_get_soc_info(struct ath10k *ar)
static int ath10k_snoc_wlan_enable(struct ath10k *ar)
{
struct icnss_wlan_enable_cfg cfg;
- int pipe_num, i;
+ int pipe_num;
struct ath10k_ce_tgt_pipe_cfg tgt_cfg[CE_COUNT_MAX];
- struct ce_tgt_pipe_cfg *tmp_tgt_cfg;
- struct ce_svc_pipe_cfg *tmp_svc_cfg;
for (pipe_num = 0; pipe_num < CE_COUNT_MAX; pipe_num++) {
tgt_cfg[pipe_num].pipe_num =
@@ -1066,12 +1092,6 @@ static int ath10k_snoc_wlan_enable(struct ath10k *ar)
cfg.shadow_reg_cfg = (struct icnss_shadow_reg_cfg *)
&target_shadow_reg_cfg_map;
- for (i = 0; i < cfg.num_ce_tgt_cfg; i++)
- tmp_tgt_cfg = cfg.ce_tgt_cfg + i;
-
- for (i = 0; i < cfg.num_ce_svc_pipe_cfg; i++)
- tmp_svc_cfg = cfg.ce_svc_cfg + i;
-
return icnss_wlan_enable(&cfg, ICNSS_MISSION, "5.1.0.26N");
}
@@ -1091,9 +1111,14 @@ static int ath10k_snoc_bus_configure(struct ath10k *ar)
static int ath10k_snoc_hif_start(struct ath10k *ar)
{
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n");
+ if (ath10k_snoc_has_fw_crashed(ar)) {
+ ath10k_snoc_request_irq(ar);
+ ath10k_snoc_fw_crashed_clear(ar);
+ }
ath10k_snoc_irq_enable(ar);
ath10k_snoc_rx_post(ar);
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n");
return 0;
}
@@ -1114,7 +1139,8 @@ static int ath10k_snoc_hif_power_up(struct ath10k *ar)
ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 driver state = %d\n",
__func__, ar->state);
- if (ar->state == ATH10K_STATE_ON) {
+ if (ar->state == ATH10K_STATE_ON ||
+ test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
ret = ath10k_snoc_bus_configure(ar);
if (ret)
ath10k_err(ar, "failed to configure bus: %d\n", ret);
@@ -1137,6 +1163,10 @@ static int ath10k_snoc_napi_poll(struct napi_struct *ctx, int budget)
struct ath10k *ar = container_of(ctx, struct ath10k, napi);
int done = 0;
+ if (ath10k_snoc_has_fw_crashed(ar)) {
+ napi_complete(ctx);
+ return done;
+ }
ath10k_ce_per_engine_service_any(ar);
done = ath10k_htt_txrx_compl_task(ar, budget);
@@ -1258,6 +1288,10 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
ath10k_err(ar, "failed to register driver core: %d\n", ret);
goto err_free_irq;
}
+
+ ath10k_snoc_modem_ssr_register_notifier(ar);
+ ath10k_snoc_pd_restart_enable(ar);
+
ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 probed\n", __func__);
return 0;
@@ -1286,6 +1320,8 @@ static int ath10k_snoc_remove(struct platform_device *pdev)
return -EINVAL;
ath10k_core_unregister(ar);
+ ath10k_snoc_pdr_unregister_notifier(ar);
+ ath10k_snoc_modem_ssr_unregister_notifier(ar);
ath10k_snoc_free_irq(ar);
ath10k_snoc_release_resource(ar);
ath10k_snoc_free_pipes(ar);
diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h
index 0a5f5bff37ec..c62519b2a340 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.h
+++ b/drivers/net/wireless/ath/ath10k/snoc.h
@@ -16,8 +16,11 @@
#include "hw.h"
#include "ce.h"
#include "pci.h"
+#include <soc/qcom/service-locator.h>
#define ATH10K_SNOC_RX_POST_RETRY_MS 50
#define CE_POLL_PIPE 4
+#define ATH10K_SERVICE_LOCATION_CLIENT_NAME "ATH10K-WLAN"
+#define ATH10K_WLAN_SERVICE_NAME "wlan/fw"
/* struct snoc_state: SNOC target state
* @pipe_cfg_addr: pipe configuration address
@@ -88,6 +91,17 @@ struct ath10k_target_info {
u32 soc_version;
};
+/* struct ath10k_service_notifier_context: service notification context
+ * @handle: notifier handle
+ * @instance_id: domain instance id
+ * @name: domain name
+ */
+struct ath10k_service_notifier_context {
+ void *handle;
+ u32 instance_id;
+ char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
+};
+
/* struct ath10k_snoc: SNOC info struct
* @dev: device structure
* @ar:ath10k base structure
@@ -101,6 +115,13 @@ struct ath10k_target_info {
* @rx_post_retry: rx buffer post processing timer
* @vaddr_rri_on_ddr: virtual address for RRI
* @is_driver_probed: flag to indicate driver state
+ * @modem_ssr_nb: notifier callback for modem notification
+ * @modem_notify_handler: modem notification handler
+ * @service_notifier: notifier context for service notification
+ * @service_notifier_nb: notifier callback for service notification
+ * @total_domains: no of service domains
+ * @get_service_nb: notifier callback for service discovery
+ * @fw_crashed: fw state flag
*/
struct ath10k_snoc {
struct bus_opaque opaque_ctx;
@@ -115,6 +136,13 @@ struct ath10k_snoc {
u32 ce_irqs[CE_COUNT_MAX];
u32 *vaddr_rri_on_ddr;
bool is_driver_probed;
+ struct notifier_block modem_ssr_nb;
+ void *modem_notify_handler;
+ struct ath10k_service_notifier_context *service_notifier;
+ struct notifier_block service_notifier_nb;
+ int total_domains;
+ struct notifier_block get_service_nb;
+ atomic_t fw_crashed;
};
/* struct ath10k_ce_tgt_pipe_cfg: target pipe configuration
@@ -171,6 +199,11 @@ struct ath10k_wlan_enable_cfg {
struct ath10k_shadow_reg_cfg *shadow_reg_cfg;
};
+struct ath10k_event_pd_down_data {
+ bool crashed;
+ bool fw_rejuvenate;
+};
+
/* enum ath10k_driver_mode: ath10k driver mode
* @ATH10K_MISSION: mission mode
* @ATH10K_FTM: ftm mode
diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c
index 7d9b0da1b010..2ffc1fe4923b 100644
--- a/drivers/net/wireless/ath/ath10k/spectral.c
+++ b/drivers/net/wireless/ath/ath10k/spectral.c
@@ -338,7 +338,7 @@ static ssize_t write_file_spec_scan_ctl(struct file *file,
} else {
res = -EINVAL;
}
- } else if (strncmp("background", buf, 9) == 0) {
+ } else if (strncmp("background", buf, 10) == 0) {
res = ath10k_spectral_scan_config(ar, SPECTRAL_BACKGROUND);
} else if (strncmp("manual", buf, 6) == 0) {
res = ath10k_spectral_scan_config(ar, SPECTRAL_MANUAL);
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
index 3fef967ca1ad..75f2528b8b84 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
@@ -553,7 +553,7 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb)
ath10k_wmi_tlv_event_tx_pause(ar, skb);
break;
default:
- ath10k_warn(ar, "Unknown eventid: %d\n", id);
+ ath10k_dbg(ar, ATH10K_DBG_WMI, "Unknown eventid: %d\n", id);
break;
}
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 4dd60b74853d..cbbba8d79e6b 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -1799,6 +1799,9 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id)
{
int ret = -EOPNOTSUPP;
+ if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ return -ESHUTDOWN;
+
might_sleep();
if (cmd_id == WMI_CMD_UNSUPPORTED) {
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index dc44cfef7517..16e052d02c94 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -502,8 +502,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
break;
return -EOPNOTSUPP;
default:
- WARN_ON(1);
- return -EINVAL;
+ return -EOPNOTSUPP;
}
mutex_lock(&ah->lock);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
index 694ca2e680e5..74670e08e6da 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
@@ -73,13 +73,13 @@
#define AR9300_OTP_BASE \
((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x30000 : 0x14000)
#define AR9300_OTP_STATUS \
- ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x30018 : 0x15f18)
+ ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x31018 : 0x15f18)
#define AR9300_OTP_STATUS_TYPE 0x7
#define AR9300_OTP_STATUS_VALID 0x4
#define AR9300_OTP_STATUS_ACCESS_BUSY 0x2
#define AR9300_OTP_STATUS_SM_BUSY 0x1
#define AR9300_OTP_READ_DATA \
- ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x3001c : 0x15f1c)
+ ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x3101c : 0x15f1c)
enum targetPowerHTRates {
HT_TARGET_RATE_0_8_16,
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index b42f4a963ef4..a660e40f2df1 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -959,6 +959,7 @@ struct ath_softc {
struct survey_info *cur_survey;
struct survey_info survey[ATH9K_NUM_CHANNELS];
+ spinlock_t intr_lock;
struct tasklet_struct intr_tq;
struct tasklet_struct bcon_tasklet;
struct ath_hw *sc_ah;
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index bc70ce62bc03..0f5672f5c9ba 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -619,6 +619,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
common->bt_ant_diversity = 1;
spin_lock_init(&common->cc_lock);
+ spin_lock_init(&sc->intr_lock);
spin_lock_init(&sc->sc_serial_rw);
spin_lock_init(&sc->sc_pm_lock);
spin_lock_init(&sc->chan_lock);
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index bba85d1a6cd1..d937c39b3a0b 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -805,21 +805,12 @@ void ath9k_hw_disable_interrupts(struct ath_hw *ah)
}
EXPORT_SYMBOL(ath9k_hw_disable_interrupts);
-void ath9k_hw_enable_interrupts(struct ath_hw *ah)
+static void __ath9k_hw_enable_interrupts(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
u32 sync_default = AR_INTR_SYNC_DEFAULT;
u32 async_mask;
- if (!(ah->imask & ATH9K_INT_GLOBAL))
- return;
-
- if (!atomic_inc_and_test(&ah->intr_ref_cnt)) {
- ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n",
- atomic_read(&ah->intr_ref_cnt));
- return;
- }
-
if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) ||
AR_SREV_9561(ah))
sync_default &= ~AR_INTR_SYNC_HOST1_FATAL;
@@ -841,6 +832,39 @@ void ath9k_hw_enable_interrupts(struct ath_hw *ah)
ath_dbg(common, INTERRUPT, "AR_IMR 0x%x IER 0x%x\n",
REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER));
}
+
+void ath9k_hw_resume_interrupts(struct ath_hw *ah)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (!(ah->imask & ATH9K_INT_GLOBAL))
+ return;
+
+ if (atomic_read(&ah->intr_ref_cnt) != 0) {
+ ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n",
+ atomic_read(&ah->intr_ref_cnt));
+ return;
+ }
+
+ __ath9k_hw_enable_interrupts(ah);
+}
+EXPORT_SYMBOL(ath9k_hw_resume_interrupts);
+
+void ath9k_hw_enable_interrupts(struct ath_hw *ah)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (!(ah->imask & ATH9K_INT_GLOBAL))
+ return;
+
+ if (!atomic_inc_and_test(&ah->intr_ref_cnt)) {
+ ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n",
+ atomic_read(&ah->intr_ref_cnt));
+ return;
+ }
+
+ __ath9k_hw_enable_interrupts(ah);
+}
EXPORT_SYMBOL(ath9k_hw_enable_interrupts);
void ath9k_hw_set_interrupts(struct ath_hw *ah)
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index 7fbf7f965f61..1b63d26f30ce 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -748,6 +748,7 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah);
void ath9k_hw_enable_interrupts(struct ath_hw *ah);
void ath9k_hw_disable_interrupts(struct ath_hw *ah);
void ath9k_hw_kill_interrupts(struct ath_hw *ah);
+void ath9k_hw_resume_interrupts(struct ath_hw *ah);
void ar9002_hw_attach_mac_ops(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 7be31f27266c..3abc64574116 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -373,21 +373,20 @@ void ath9k_tasklet(unsigned long data)
struct ath_common *common = ath9k_hw_common(ah);
enum ath_reset_type type;
unsigned long flags;
- u32 status = sc->intrstatus;
+ u32 status;
u32 rxmask;
+ spin_lock_irqsave(&sc->intr_lock, flags);
+ status = sc->intrstatus;
+ sc->intrstatus = 0;
+ spin_unlock_irqrestore(&sc->intr_lock, flags);
+
ath9k_ps_wakeup(sc);
spin_lock(&sc->sc_pcu_lock);
if (status & ATH9K_INT_FATAL) {
type = RESET_TYPE_FATAL_INT;
ath9k_queue_reset(sc, type);
-
- /*
- * Increment the ref. counter here so that
- * interrupts are enabled in the reset routine.
- */
- atomic_inc(&ah->intr_ref_cnt);
ath_dbg(common, RESET, "FATAL: Skipping interrupts\n");
goto out;
}
@@ -403,11 +402,6 @@ void ath9k_tasklet(unsigned long data)
type = RESET_TYPE_BB_WATCHDOG;
ath9k_queue_reset(sc, type);
- /*
- * Increment the ref. counter here so that
- * interrupts are enabled in the reset routine.
- */
- atomic_inc(&ah->intr_ref_cnt);
ath_dbg(common, RESET,
"BB_WATCHDOG: Skipping interrupts\n");
goto out;
@@ -420,7 +414,6 @@ void ath9k_tasklet(unsigned long data)
if ((sc->gtt_cnt >= MAX_GTT_CNT) && !ath9k_hw_check_alive(ah)) {
type = RESET_TYPE_TX_GTT;
ath9k_queue_reset(sc, type);
- atomic_inc(&ah->intr_ref_cnt);
ath_dbg(common, RESET,
"GTT: Skipping interrupts\n");
goto out;
@@ -477,7 +470,7 @@ void ath9k_tasklet(unsigned long data)
ath9k_btcoex_handle_interrupt(sc, status);
/* re-enable hardware interrupt */
- ath9k_hw_enable_interrupts(ah);
+ ath9k_hw_resume_interrupts(ah);
out:
spin_unlock(&sc->sc_pcu_lock);
ath9k_ps_restore(sc);
@@ -541,7 +534,9 @@ irqreturn_t ath_isr(int irq, void *dev)
return IRQ_NONE;
/* Cache the status */
- sc->intrstatus = status;
+ spin_lock(&sc->intr_lock);
+ sc->intrstatus |= status;
+ spin_unlock(&sc->intr_lock);
if (status & SCHED_INTR)
sched = true;
@@ -587,7 +582,7 @@ chip_reset:
if (sched) {
/* turn off every interrupt */
- ath9k_hw_disable_interrupts(ah);
+ ath9k_hw_kill_interrupts(ah);
tasklet_schedule(&sc->intr_tq);
}
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 7cdaf40c3057..ea7b8c25955f 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -27,7 +27,6 @@ static const struct pci_device_id ath_pci_id_table[] = {
{ PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI */
{ PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */
{ PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */
- { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */
#ifdef CONFIG_ATH9K_PCOEM
/* Mini PCI AR9220 MB92 cards: Compex WLM200NX, Wistron DNMA-92 */
@@ -38,7 +37,7 @@ static const struct pci_device_id ath_pci_id_table[] = {
.driver_data = ATH9K_PCI_LED_ACT_HI },
#endif
- { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
+ { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */
#ifdef CONFIG_ATH9K_PCOEM
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
@@ -86,7 +85,11 @@ static const struct pci_device_id ath_pci_id_table[] = {
0x10CF, /* Fujitsu */
0x1536),
.driver_data = ATH9K_PCI_D3_L1_WAR },
+#endif
+ { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
+
+#ifdef CONFIG_ATH9K_PCOEM
/* AR9285 card for Asus */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
0x002B,
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index fe6d5ab7f95b..958c96b75fbb 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -891,6 +891,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
if (wil->hw_version == HW_VER_UNKNOWN)
return -ENODEV;
+ wil_dbg_misc(wil, "Prevent DS in BL & mark FW to set T_POWER_ON=0\n");
+ wil_s(wil, RGF_USER_USAGE_8, BIT_USER_PREVENT_DEEP_SLEEP |
+ BIT_USER_SUPPORT_T_POWER_ON_0);
+
if (wil->platform_ops.notify) {
rc = wil->platform_ops.notify(wil->platform_handle,
WIL_PLATFORM_EVT_PRE_RESET);
@@ -993,6 +997,9 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
return rc;
}
+ if (wil->tt_data_set)
+ wmi_set_tt_cfg(wil, &wil->tt_data);
+
wil_collect_fw_info(wil);
if (wil->platform_ops.notify) {
diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c
index a0acb2d0cb79..7260bef314a4 100644
--- a/drivers/net/wireless/ath/wil6210/pm.c
+++ b/drivers/net/wireless/ath/wil6210/pm.c
@@ -80,12 +80,20 @@ int wil_suspend(struct wil6210_priv *wil, bool is_runtime)
}
}
- if (wil->platform_ops.suspend)
+ /* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */
+ wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n");
+ wil_disable_irq(wil);
+
+ if (wil->platform_ops.suspend) {
rc = wil->platform_ops.suspend(wil->platform_handle);
+ if (rc)
+ wil_enable_irq(wil);
+ }
out:
wil_dbg_pm(wil, "suspend: %s => %d\n",
is_runtime ? "runtime" : "system", rc);
+
return rc;
}
@@ -104,6 +112,9 @@ int wil_resume(struct wil6210_priv *wil, bool is_runtime)
}
}
+ wil_dbg_pm(wil, "Enabling PCIe IRQ\n");
+ wil_enable_irq(wil);
+
/* if netif up, bring hardware up
* During open(), IFF_UP set after actual device method
* invocation. This prevent recursive call to wil_up()
diff --git a/drivers/net/wireless/ath/wil6210/sysfs.c b/drivers/net/wireless/ath/wil6210/sysfs.c
index 0faa26ca41c9..a3689738a070 100644
--- a/drivers/net/wireless/ath/wil6210/sysfs.c
+++ b/drivers/net/wireless/ath/wil6210/sysfs.c
@@ -94,8 +94,119 @@ static DEVICE_ATTR(ftm_txrx_offset, 0644,
wil_ftm_txrx_offset_sysfs_show,
wil_ftm_txrx_offset_sysfs_store);
+static ssize_t
+wil_tt_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct wil6210_priv *wil = dev_get_drvdata(dev);
+ ssize_t len;
+ struct wmi_tt_data tt_data;
+ int i, rc;
+
+ rc = wmi_get_tt_cfg(wil, &tt_data);
+ if (rc)
+ return rc;
+
+ len = snprintf(buf, PAGE_SIZE, " high max critical\n");
+
+ len += snprintf(buf + len, PAGE_SIZE - len, "bb: ");
+ if (tt_data.bb_enabled)
+ for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "%03d-%03d ",
+ tt_data.bb_zones[i].temperature_high,
+ tt_data.bb_zones[i].temperature_low);
+ else
+ len += snprintf(buf + len, PAGE_SIZE - len, "* disabled *");
+ len += snprintf(buf + len, PAGE_SIZE - len, "\nrf: ");
+ if (tt_data.rf_enabled)
+ for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "%03d-%03d ",
+ tt_data.rf_zones[i].temperature_high,
+ tt_data.rf_zones[i].temperature_low);
+ else
+ len += snprintf(buf + len, PAGE_SIZE - len, "* disabled *");
+ len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+
+ return len;
+}
+
+static ssize_t
+wil_tt_sysfs_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct wil6210_priv *wil = dev_get_drvdata(dev);
+ int i, rc = -EINVAL;
+ char *token, *dupbuf, *tmp;
+ struct wmi_tt_data tt_data = {
+ .bb_enabled = 0,
+ .rf_enabled = 0,
+ };
+
+ tmp = kmemdup(buf, count + 1, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+ tmp[count] = '\0';
+ dupbuf = tmp;
+
+ /* Format for writing is 12 unsigned bytes separated by spaces:
+ * <bb_z1_h> <bb_z1_l> <bb_z2_h> <bb_z2_l> <bb_z3_h> <bb_z3_l> \
+ * <rf_z1_h> <rf_z1_l> <rf_z2_h> <rf_z2_l> <rf_z3_h> <rf_z3_l>
+ * To disable thermal throttling for bb or for rf, use 0 for all
+ * its six set points.
+ */
+
+ /* bb */
+ for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) {
+ token = strsep(&dupbuf, " ");
+ if (!token)
+ goto out;
+ if (kstrtou8(token, 0, &tt_data.bb_zones[i].temperature_high))
+ goto out;
+ token = strsep(&dupbuf, " ");
+ if (!token)
+ goto out;
+ if (kstrtou8(token, 0, &tt_data.bb_zones[i].temperature_low))
+ goto out;
+
+ if (tt_data.bb_zones[i].temperature_high > 0 ||
+ tt_data.bb_zones[i].temperature_low > 0)
+ tt_data.bb_enabled = 1;
+ }
+ /* rf */
+ for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) {
+ token = strsep(&dupbuf, " ");
+ if (!token)
+ goto out;
+ if (kstrtou8(token, 0, &tt_data.rf_zones[i].temperature_high))
+ goto out;
+ token = strsep(&dupbuf, " ");
+ if (!token)
+ goto out;
+ if (kstrtou8(token, 0, &tt_data.rf_zones[i].temperature_low))
+ goto out;
+
+ if (tt_data.rf_zones[i].temperature_high > 0 ||
+ tt_data.rf_zones[i].temperature_low > 0)
+ tt_data.rf_enabled = 1;
+ }
+
+ rc = wmi_set_tt_cfg(wil, &tt_data);
+ if (rc)
+ goto out;
+
+ rc = count;
+out:
+ kfree(tmp);
+ return rc;
+}
+
+static DEVICE_ATTR(thermal_throttling, 0644,
+ wil_tt_sysfs_show, wil_tt_sysfs_store);
+
static struct attribute *wil6210_sysfs_entries[] = {
&dev_attr_ftm_txrx_offset.attr,
+ &dev_attr_thermal_throttling.attr,
NULL
};
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index fcfbcf7fbd7d..f64323b03a3b 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -141,6 +141,9 @@ struct RGF_ICR {
#define RGF_USER_USAGE_1 (0x880004)
#define RGF_USER_USAGE_6 (0x880018)
#define BIT_USER_OOB_MODE BIT(31)
+#define RGF_USER_USAGE_8 (0x880020)
+ #define BIT_USER_PREVENT_DEEP_SLEEP BIT(0)
+ #define BIT_USER_SUPPORT_T_POWER_ON_0 BIT(1)
#define RGF_USER_HW_MACHINE_STATE (0x8801dc)
#define HW_MACHINE_BOOT_DONE (0x3fffffd)
#define RGF_USER_USER_CPU_0 (0x8801e0)
@@ -693,6 +696,8 @@ struct wil6210_priv {
struct wil_halp halp;
struct wil_ftm_priv ftm;
+ bool tt_data_set;
+ struct wmi_tt_data tt_data;
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
@@ -851,6 +856,8 @@ int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil,
int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short);
int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short);
int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid);
+int wmi_set_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data);
+int wmi_get_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data);
int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid,
u8 dialog_token, __le16 ba_param_set,
__le16 ba_timeout, __le16 ba_seq_ctrl);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index bccd27dcb42b..6a6ba02beba0 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -1763,6 +1763,67 @@ int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid)
return rc;
}
+int wmi_set_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data)
+{
+ int rc;
+ struct wmi_set_thermal_throttling_cfg_cmd cmd = {
+ .tt_data = *tt_data,
+ };
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_set_thermal_throttling_cfg_event evt;
+ } __packed reply;
+
+ if (!test_bit(WMI_FW_CAPABILITY_THERMAL_THROTTLING,
+ wil->fw_capabilities))
+ return -EOPNOTSUPP;
+
+ memset(&reply, 0, sizeof(reply));
+ rc = wmi_call(wil, WMI_SET_THERMAL_THROTTLING_CFG_CMDID, &cmd,
+ sizeof(cmd), WMI_SET_THERMAL_THROTTLING_CFG_EVENTID,
+ &reply, sizeof(reply), 100);
+ if (rc) {
+ wil_err(wil, "failed to set thermal throttling\n");
+ return rc;
+ }
+ if (reply.evt.status) {
+ wil_err(wil, "set thermal throttling failed, error %d\n",
+ reply.evt.status);
+ return -EIO;
+ }
+
+ wil->tt_data = *tt_data;
+ wil->tt_data_set = true;
+
+ return 0;
+}
+
+int wmi_get_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data)
+{
+ int rc;
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_get_thermal_throttling_cfg_event evt;
+ } __packed reply;
+
+ if (!test_bit(WMI_FW_CAPABILITY_THERMAL_THROTTLING,
+ wil->fw_capabilities))
+ return -EOPNOTSUPP;
+
+ rc = wmi_call(wil, WMI_GET_THERMAL_THROTTLING_CFG_CMDID, NULL, 0,
+ WMI_GET_THERMAL_THROTTLING_CFG_EVENTID, &reply,
+ sizeof(reply), 100);
+ if (rc) {
+ wil_err(wil, "failed to get thermal throttling\n");
+ return rc;
+ }
+
+ if (tt_data)
+ *tt_data = reply.evt.tt_data;
+
+ return 0;
+}
+
void wmi_event_flush(struct wil6210_priv *wil)
{
ulong flags;
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
index 7c9fee57aa91..f7f5f4f801e3 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -58,6 +58,7 @@ enum wmi_fw_capability {
WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT = 3,
WMI_FW_CAPABILITY_DISABLE_AP_SME = 4,
WMI_FW_CAPABILITY_WMI_ONLY = 5,
+ WMI_FW_CAPABILITY_THERMAL_THROTTLING = 7,
WMI_FW_CAPABILITY_MAX,
};
@@ -142,8 +143,6 @@ enum wmi_command_id {
WMI_MAINTAIN_RESUME_CMDID = 0x851,
WMI_RS_MGMT_CMDID = 0x852,
WMI_RF_MGMT_CMDID = 0x853,
- WMI_THERMAL_THROTTLING_CTRL_CMDID = 0x854,
- WMI_THERMAL_THROTTLING_GET_STATUS_CMDID = 0x855,
WMI_OTP_READ_CMDID = 0x856,
WMI_OTP_WRITE_CMDID = 0x857,
WMI_LED_CFG_CMDID = 0x858,
@@ -192,6 +191,8 @@ enum wmi_command_id {
WMI_GET_MGMT_RETRY_LIMIT_CMDID = 0x931,
WMI_NEW_STA_CMDID = 0x935,
WMI_DEL_STA_CMDID = 0x936,
+ WMI_SET_THERMAL_THROTTLING_CFG_CMDID = 0x940,
+ WMI_GET_THERMAL_THROTTLING_CFG_CMDID = 0x941,
WMI_TOF_SESSION_START_CMDID = 0x991,
WMI_TOF_GET_CAPABILITIES_CMDID = 0x992,
WMI_TOF_SET_LCR_CMDID = 0x993,
@@ -438,16 +439,6 @@ struct wmi_rf_mgmt_cmd {
__le32 rf_mgmt_type;
} __packed;
-/* WMI_THERMAL_THROTTLING_CTRL_CMDID */
-#define THERMAL_THROTTLING_USE_DEFAULT_MAX_TXOP_LENGTH (0xFFFFFFFF)
-
-/* WMI_THERMAL_THROTTLING_CTRL_CMDID */
-struct wmi_thermal_throttling_ctrl_cmd {
- __le32 time_on_usec;
- __le32 time_off_usec;
- __le32 max_txop_length_usec;
-} __packed;
-
/* WMI_RF_RX_TEST_CMDID */
struct wmi_rf_rx_test_cmd {
__le32 sector;
@@ -549,7 +540,7 @@ struct wmi_pcp_start_cmd {
u8 hidden_ssid;
u8 is_go;
u8 reserved0[5];
- /* abft_len override if non-0 */
+ /* A-BFT length override if non-0 */
u8 abft_len;
u8 disable_ap_sme;
u8 network_type;
@@ -910,6 +901,39 @@ struct wmi_set_mgmt_retry_limit_cmd {
u8 reserved[3];
} __packed;
+/* Zones: HIGH, MAX, CRITICAL */
+#define WMI_NUM_OF_TT_ZONES (3)
+
+struct wmi_tt_zone_limits {
+ /* Above this temperature this zone is active */
+ u8 temperature_high;
+ /* Below this temperature the adjacent lower zone is active */
+ u8 temperature_low;
+ u8 reserved[2];
+} __packed;
+
+/* Struct used for both configuration and status commands of thermal
+ * throttling
+ */
+struct wmi_tt_data {
+ /* Enable/Disable TT algorithm for baseband */
+ u8 bb_enabled;
+ u8 reserved0[3];
+ /* Define zones for baseband */
+ struct wmi_tt_zone_limits bb_zones[WMI_NUM_OF_TT_ZONES];
+ /* Enable/Disable TT algorithm for radio */
+ u8 rf_enabled;
+ u8 reserved1[3];
+ /* Define zones for all radio chips */
+ struct wmi_tt_zone_limits rf_zones[WMI_NUM_OF_TT_ZONES];
+} __packed;
+
+/* WMI_SET_THERMAL_THROTTLING_CFG_CMDID */
+struct wmi_set_thermal_throttling_cfg_cmd {
+ /* Command data */
+ struct wmi_tt_data tt_data;
+} __packed;
+
/* WMI_NEW_STA_CMDID */
struct wmi_new_sta_cmd {
u8 dst_mac[WMI_MAC_LEN];
@@ -1040,7 +1064,6 @@ enum wmi_event_id {
WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839,
WMI_RS_MGMT_DONE_EVENTID = 0x1852,
WMI_RF_MGMT_STATUS_EVENTID = 0x1853,
- WMI_THERMAL_THROTTLING_STATUS_EVENTID = 0x1855,
WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838,
WMI_RX_MGMT_PACKET_EVENTID = 0x1840,
WMI_TX_MGMT_PACKET_EVENTID = 0x1841,
@@ -1090,6 +1113,8 @@ enum wmi_event_id {
WMI_BRP_SET_ANT_LIMIT_EVENTID = 0x1924,
WMI_SET_MGMT_RETRY_LIMIT_EVENTID = 0x1930,
WMI_GET_MGMT_RETRY_LIMIT_EVENTID = 0x1931,
+ WMI_SET_THERMAL_THROTTLING_CFG_EVENTID = 0x1940,
+ WMI_GET_THERMAL_THROTTLING_CFG_EVENTID = 0x1941,
WMI_TOF_SESSION_END_EVENTID = 0x1991,
WMI_TOF_GET_CAPABILITIES_EVENTID = 0x1992,
WMI_TOF_SET_LCR_EVENTID = 0x1993,
@@ -1133,13 +1158,6 @@ struct wmi_rf_mgmt_status_event {
__le32 rf_status;
} __packed;
-/* WMI_THERMAL_THROTTLING_STATUS_EVENTID */
-struct wmi_thermal_throttling_status_event {
- __le32 time_on_usec;
- __le32 time_off_usec;
- __le32 max_txop_length_usec;
-} __packed;
-
/* WMI_GET_STATUS_DONE_EVENTID */
struct wmi_get_status_done_event {
__le32 is_associated;
@@ -2206,6 +2224,19 @@ struct wmi_tof_get_capabilities_event {
__le32 aoa_supported_types;
} __packed;
+/* WMI_SET_THERMAL_THROTTLING_CFG_EVENTID */
+struct wmi_set_thermal_throttling_cfg_event {
+ /* wmi_fw_status */
+ u8 status;
+ u8 reserved[3];
+} __packed;
+
+/* WMI_GET_THERMAL_THROTTLING_CFG_EVENTID */
+struct wmi_get_thermal_throttling_cfg_event {
+ /* Status data */
+ struct wmi_tt_data tt_data;
+} __packed;
+
enum wmi_tof_session_end_status {
WMI_TOF_SESSION_END_NO_ERROR = 0x00,
WMI_TOF_SESSION_END_FAIL = 0x01,
diff --git a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c
index 16f64e9fb8d3..913f756f9520 100644
--- a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c
+++ b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c
@@ -98,6 +98,7 @@ static struct wcnss_prealloc wcnss_allocs[] = {
{0, 64 * 1024, NULL},
{0, 64 * 1024, NULL},
{0, 64 * 1024, NULL},
+ {0, 64 * 1024, NULL},
{0, 128 * 1024, NULL},
{0, 128 * 1024, NULL},
};
diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c
index 7a40d8dffa36..aab752328c26 100644
--- a/drivers/net/wireless/realtek/rtlwifi/base.c
+++ b/drivers/net/wireless/realtek/rtlwifi/base.c
@@ -1303,12 +1303,13 @@ EXPORT_SYMBOL_GPL(rtl_action_proc);
static void setup_arp_tx(struct rtl_priv *rtlpriv, struct rtl_ps_ctl *ppsc)
{
+ struct ieee80211_hw *hw = rtlpriv->hw;
+
rtlpriv->ra.is_special_data = true;
if (rtlpriv->cfg->ops->get_btc_status())
rtlpriv->btcoexist.btc_ops->btc_special_packet_notify(
rtlpriv, 1);
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
+ rtl_lps_leave(hw);
ppsc->last_delaylps_stamp_jiffies = jiffies;
}
@@ -1381,8 +1382,7 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx,
if (is_tx) {
rtlpriv->ra.is_special_data = true;
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
+ rtl_lps_leave(hw);
ppsc->last_delaylps_stamp_jiffies = jiffies;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c
index a4527880b2b7..8b537a5a4b01 100644
--- a/drivers/net/wireless/realtek/rtlwifi/core.c
+++ b/drivers/net/wireless/realtek/rtlwifi/core.c
@@ -1153,10 +1153,8 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
} else {
mstatus = RT_MEDIA_DISCONNECT;
- if (mac->link_state == MAC80211_LINKED) {
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
- }
+ if (mac->link_state == MAC80211_LINKED)
+ rtl_lps_leave(hw);
if (ppsc->p2p_ps_info.p2p_ps_mode > P2P_PS_NONE)
rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
mac->link_state = MAC80211_NOLINK;
@@ -1434,8 +1432,7 @@ static void rtl_op_sw_scan_start(struct ieee80211_hw *hw,
}
if (mac->link_state == MAC80211_LINKED) {
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
+ rtl_lps_leave(hw);
mac->link_state = MAC80211_LINKED_SCANNING;
} else {
rtl_ips_nic_on(hw);
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index 5b4048041147..a52230377e2c 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -664,11 +664,9 @@ tx_status_ok:
}
if (((rtlpriv->link_info.num_rx_inperiod +
- rtlpriv->link_info.num_tx_inperiod) > 8) ||
- (rtlpriv->link_info.num_rx_inperiod > 2)) {
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
- }
+ rtlpriv->link_info.num_tx_inperiod) > 8) ||
+ (rtlpriv->link_info.num_rx_inperiod > 2))
+ rtl_lps_leave(hw);
}
static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw,
@@ -919,10 +917,8 @@ new_trx_end:
}
if (((rtlpriv->link_info.num_rx_inperiod +
rtlpriv->link_info.num_tx_inperiod) > 8) ||
- (rtlpriv->link_info.num_rx_inperiod > 2)) {
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
- }
+ (rtlpriv->link_info.num_rx_inperiod > 2))
+ rtl_lps_leave(hw);
skb = new_skb;
no_new:
if (rtlpriv->use_new_trx_flow) {
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.h b/drivers/net/wireless/realtek/rtlwifi/pci.h
index 5da6703942d9..672f81ea02d0 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.h
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.h
@@ -275,10 +275,10 @@ struct mp_adapter {
};
struct rtl_pci_priv {
+ struct bt_coexist_info bt_coexist;
+ struct rtl_led_ctl ledctl;
struct rtl_pci dev;
struct mp_adapter ndis_adapter;
- struct rtl_led_ctl ledctl;
- struct bt_coexist_info bt_coexist;
};
#define rtl_pcipriv(hw) (((struct rtl_pci_priv *)(rtl_priv(hw))->priv))
diff --git a/drivers/net/wireless/realtek/rtlwifi/ps.c b/drivers/net/wireless/realtek/rtlwifi/ps.c
index b69321d45f04..626ff300352b 100644
--- a/drivers/net/wireless/realtek/rtlwifi/ps.c
+++ b/drivers/net/wireless/realtek/rtlwifi/ps.c
@@ -414,8 +414,8 @@ void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode)
}
}
-/*Enter the leisure power save mode.*/
-void rtl_lps_enter(struct ieee80211_hw *hw)
+/* Interrupt safe routine to enter the leisure power save mode.*/
+static void rtl_lps_enter_core(struct ieee80211_hw *hw)
{
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
@@ -455,10 +455,9 @@ void rtl_lps_enter(struct ieee80211_hw *hw)
spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
}
-EXPORT_SYMBOL(rtl_lps_enter);
-/*Leave the leisure power save mode.*/
-void rtl_lps_leave(struct ieee80211_hw *hw)
+/* Interrupt safe routine to leave the leisure power save mode.*/
+static void rtl_lps_leave_core(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
@@ -488,7 +487,6 @@ void rtl_lps_leave(struct ieee80211_hw *hw)
}
spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
}
-EXPORT_SYMBOL(rtl_lps_leave);
/* For sw LPS*/
void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len)
@@ -681,12 +679,34 @@ void rtl_lps_change_work_callback(struct work_struct *work)
struct rtl_priv *rtlpriv = rtl_priv(hw);
if (rtlpriv->enter_ps)
- rtl_lps_enter(hw);
+ rtl_lps_enter_core(hw);
else
- rtl_lps_leave(hw);
+ rtl_lps_leave_core(hw);
}
EXPORT_SYMBOL_GPL(rtl_lps_change_work_callback);
+void rtl_lps_enter(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ if (!in_interrupt())
+ return rtl_lps_enter_core(hw);
+ rtlpriv->enter_ps = true;
+ schedule_work(&rtlpriv->works.lps_change_work);
+}
+EXPORT_SYMBOL_GPL(rtl_lps_enter);
+
+void rtl_lps_leave(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ if (!in_interrupt())
+ return rtl_lps_leave_core(hw);
+ rtlpriv->enter_ps = false;
+ schedule_work(&rtlpriv->works.lps_change_work);
+}
+EXPORT_SYMBOL_GPL(rtl_lps_leave);
+
void rtl_swlps_wq_callback(void *data)
{
struct rtl_works *rtlworks = container_of_dwork_rtl(data,
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c
index 5f14308e8eb3..b1601441991d 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c
@@ -1003,7 +1003,7 @@ static void _rtl92ee_hw_configure(struct ieee80211_hw *hw)
rtl_write_word(rtlpriv, REG_SIFS_TRX, 0x100a);
/* Note Data sheet don't define */
- rtl_write_word(rtlpriv, 0x4C7, 0x80);
+ rtl_write_byte(rtlpriv, 0x4C7, 0x80);
rtl_write_byte(rtlpriv, REG_RX_PKT_LIMIT, 0x20);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
index bbb789f8990b..c2103e7a8132 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
@@ -1127,7 +1127,7 @@ static u8 _rtl8821ae_dbi_read(struct rtl_priv *rtlpriv, u16 addr)
}
if (0 == tmp) {
read_addr = REG_DBI_RDATA + addr % 4;
- ret = rtl_read_word(rtlpriv, read_addr);
+ ret = rtl_read_byte(rtlpriv, read_addr);
}
return ret;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c
index aac1ed3f7bb4..ad8390d2997b 100644
--- a/drivers/net/wireless/realtek/rtlwifi/usb.c
+++ b/drivers/net/wireless/realtek/rtlwifi/usb.c
@@ -834,12 +834,30 @@ static void rtl_usb_stop(struct ieee80211_hw *hw)
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw));
+ struct urb *urb;
/* should after adapter start and interrupt enable. */
set_hal_stop(rtlhal);
cancel_work_sync(&rtlpriv->works.fill_h2c_cmd);
/* Enable software */
SET_USB_STOP(rtlusb);
+
+ /* free pre-allocated URBs from rtl_usb_start() */
+ usb_kill_anchored_urbs(&rtlusb->rx_submitted);
+
+ tasklet_kill(&rtlusb->rx_work_tasklet);
+ cancel_work_sync(&rtlpriv->works.lps_change_work);
+
+ flush_workqueue(rtlpriv->works.rtl_wq);
+
+ skb_queue_purge(&rtlusb->rx_queue);
+
+ while ((urb = usb_get_from_anchor(&rtlusb->rx_cleanup_urbs))) {
+ usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+ urb->transfer_buffer, urb->transfer_dma);
+ usb_free_urb(urb);
+ }
+
rtlpriv->cfg->ops->hw_disable(hw);
}
@@ -1073,6 +1091,7 @@ int rtl_usb_probe(struct usb_interface *intf,
return -ENOMEM;
}
rtlpriv = hw->priv;
+ rtlpriv->hw = hw;
rtlpriv->usb_data = kzalloc(RTL_USB_MAX_RX_COUNT * sizeof(u32),
GFP_KERNEL);
if (!rtlpriv->usb_data)
diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.h b/drivers/net/wireless/realtek/rtlwifi/usb.h
index 685273ca9561..441c4412130c 100644
--- a/drivers/net/wireless/realtek/rtlwifi/usb.h
+++ b/drivers/net/wireless/realtek/rtlwifi/usb.h
@@ -150,8 +150,9 @@ struct rtl_usb {
};
struct rtl_usb_priv {
- struct rtl_usb dev;
+ struct bt_coexist_info bt_coexist;
struct rtl_led_ctl ledctl;
+ struct rtl_usb dev;
};
#define rtl_usbpriv(hw) (((struct rtl_usb_priv *)(rtl_priv(hw))->priv))
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index d6abf191122a..1f445f357da1 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -1391,6 +1391,8 @@ static void xennet_disconnect_backend(struct netfront_info *info)
for (i = 0; i < num_queues && info->queues; ++i) {
struct netfront_queue *queue = &info->queues[i];
+ del_timer_sync(&queue->rx_refill_timer);
+
if (queue->tx_irq && (queue->tx_irq == queue->rx_irq))
unbind_from_irqhandler(queue->tx_irq, queue);
if (queue->tx_irq && (queue->tx_irq != queue->rx_irq)) {
@@ -1745,7 +1747,6 @@ static void xennet_destroy_queues(struct netfront_info *info)
if (netif_running(info->netdev))
napi_disable(&queue->napi);
- del_timer_sync(&queue->rx_refill_timer);
netif_napi_del(&queue->napi);
}
diff --git a/drivers/nfc/nq-nci.c b/drivers/nfc/nq-nci.c
index 5b58475ae489..f4ad5bde06a8 100644
--- a/drivers/nfc/nq-nci.c
+++ b/drivers/nfc/nq-nci.c
@@ -267,7 +267,7 @@ static ssize_t nfc_write(struct file *filp, const char __user *buf,
ret = i2c_master_send(nqx_dev->client, tmp, count);
if (ret != count) {
- dev_err(&nqx_dev->client->dev,
+ dev_dbg(&nqx_dev->client->dev,
"%s: failed to write %d\n", __func__, ret);
ret = -EIO;
goto out_free;
@@ -304,7 +304,7 @@ static int nqx_standby_write(struct nqx_dev *nqx_dev,
for (retry_cnt = 1; retry_cnt <= MAX_RETRY_COUNT; retry_cnt++) {
ret = i2c_master_send(nqx_dev->client, buf, len);
if (ret < 0) {
- dev_err(&nqx_dev->client->dev,
+ dev_dbg(&nqx_dev->client->dev,
"%s: write failed, Maybe in Standby Mode - Retry(%d)\n",
__func__, retry_cnt);
usleep_range(1000, 1100);
diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index 60654d524858..ecc6fb9ca92f 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -1623,7 +1623,7 @@ ntb_transport_create_queue(void *data, struct device *client_dev,
node = dev_to_node(&ndev->dev);
- free_queue = ffs(nt->qp_bitmap);
+ free_queue = ffs(nt->qp_bitmap_free);
if (!free_queue)
goto err;
@@ -2082,9 +2082,8 @@ module_init(ntb_transport_init);
static void __exit ntb_transport_exit(void)
{
- debugfs_remove_recursive(nt_debugfs_dir);
-
ntb_unregister_client(&ntb_transport_client);
bus_unregister(&ntb_transport_bus);
+ debugfs_remove_recursive(nt_debugfs_dir);
}
module_exit(ntb_transport_exit);
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index 62120c38d56b..aae7379af4e4 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -1534,6 +1534,7 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id)
static int find_pmem_label_set(struct nd_region *nd_region,
struct nd_namespace_pmem *nspm)
{
+ u64 altcookie = nd_region_interleave_set_altcookie(nd_region);
u64 cookie = nd_region_interleave_set_cookie(nd_region);
struct nd_namespace_label *nd_label;
u8 select_id[NSLABEL_UUID_LEN];
@@ -1542,8 +1543,10 @@ static int find_pmem_label_set(struct nd_region *nd_region,
int rc = -ENODEV, l;
u16 i;
- if (cookie == 0)
+ if (cookie == 0) {
+ dev_dbg(&nd_region->dev, "invalid interleave-set-cookie\n");
return -ENXIO;
+ }
/*
* Find a complete set of labels by uuid. By definition we can start
@@ -1552,13 +1555,24 @@ static int find_pmem_label_set(struct nd_region *nd_region,
for_each_label(l, nd_label, nd_region->mapping[0].labels) {
u64 isetcookie = __le64_to_cpu(nd_label->isetcookie);
- if (isetcookie != cookie)
- continue;
+ if (isetcookie != cookie) {
+ dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n",
+ nd_label->uuid);
+ if (isetcookie != altcookie)
+ continue;
+
+ dev_dbg(&nd_region->dev, "valid altcookie in label: %pUb\n",
+ nd_label->uuid);
+ }
+
+ for (i = 0; nd_region->ndr_mappings; i++) {
+ if (has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i))
+ continue;
+ if (has_uuid_at_pos(nd_region, nd_label->uuid, altcookie, i))
+ continue;
+ break;
+ }
- for (i = 0; nd_region->ndr_mappings; i++)
- if (!has_uuid_at_pos(nd_region, nd_label->uuid,
- cookie, i))
- break;
if (i < nd_region->ndr_mappings) {
/*
* Give up if we don't find an instance of a
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index 417e521d299c..fc870e55bb66 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -245,6 +245,7 @@ struct nd_region *to_nd_region(struct device *dev);
int nd_region_to_nstype(struct nd_region *nd_region);
int nd_region_register_namespaces(struct nd_region *nd_region, int *err);
u64 nd_region_interleave_set_cookie(struct nd_region *nd_region);
+u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region);
void nvdimm_bus_lock(struct device *dev);
void nvdimm_bus_unlock(struct device *dev);
bool is_nvdimm_bus_locked(struct device *dev);
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index 9521696c9385..dc2e919daa39 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -379,6 +379,15 @@ u64 nd_region_interleave_set_cookie(struct nd_region *nd_region)
return 0;
}
+u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region)
+{
+ struct nd_interleave_set *nd_set = nd_region->nd_set;
+
+ if (nd_set)
+ return nd_set->altcookie;
+ return 0;
+}
+
/*
* Upon successful probe/remove, take/release a reference on the
* associated interleave set (if present), and plant new btt + namespace
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index 584ad96c703f..6e9a8649ee2f 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -2511,6 +2511,48 @@ int msm_pcie_debug_info(struct pci_dev *dev, u32 option, u32 base,
}
EXPORT_SYMBOL(msm_pcie_debug_info);
+#ifdef CONFIG_SYSFS
+static ssize_t msm_pcie_enumerate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct msm_pcie_dev_t *pcie_dev = (struct msm_pcie_dev_t *)
+ dev_get_drvdata(dev);
+
+ if (pcie_dev)
+ msm_pcie_enumerate(pcie_dev->rc_idx);
+
+ return count;
+}
+
+static DEVICE_ATTR(enumerate, S_IWUSR, NULL, msm_pcie_enumerate_store);
+
+static void msm_pcie_sysfs_init(struct msm_pcie_dev_t *dev)
+{
+ int ret;
+
+ ret = device_create_file(&dev->pdev->dev, &dev_attr_enumerate);
+ if (ret)
+ PCIE_DBG_FS(dev,
+ "RC%d: failed to create sysfs enumerate node\n",
+ dev->rc_idx);
+}
+
+static void msm_pcie_sysfs_exit(struct msm_pcie_dev_t *dev)
+{
+ if (dev->pdev)
+ device_remove_file(&dev->pdev->dev, &dev_attr_enumerate);
+}
+#else
+static void msm_pcie_sysfs_init(struct msm_pcie_dev_t *dev)
+{
+}
+
+static void msm_pcie_sysfs_exit(struct msm_pcie_dev_t *dev)
+{
+}
+#endif
+
#ifdef CONFIG_DEBUG_FS
static struct dentry *dent_msm_pcie;
static struct dentry *dfile_rc_sel;
@@ -6277,6 +6319,9 @@ static int msm_pcie_probe(struct platform_device *pdev)
msm_pcie_dev[rc_idx].pcidev_table[i].registered = true;
}
+ dev_set_drvdata(&msm_pcie_dev[rc_idx].pdev->dev, &msm_pcie_dev[rc_idx]);
+ msm_pcie_sysfs_init(&msm_pcie_dev[rc_idx]);
+
ret = msm_pcie_get_resources(&msm_pcie_dev[rc_idx],
msm_pcie_dev[rc_idx].pdev);
@@ -6490,11 +6535,16 @@ int __init pcie_init(void)
static void __exit pcie_exit(void)
{
+ int i;
+
PCIE_GEN_DBG("pcie:%s.\n", __func__);
platform_driver_unregister(&msm_pcie_driver);
msm_pcie_debugfs_exit();
+
+ for (i = 0; i < MAX_RC_NUM; i++)
+ msm_pcie_sysfs_exit(&msm_pcie_dev[i]);
}
subsys_initcall_sync(pcie_init);
diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c
index e12bafdc42e0..f2fcbe944d94 100644
--- a/drivers/pci/hotplug/rpadlpar_core.c
+++ b/drivers/pci/hotplug/rpadlpar_core.c
@@ -258,8 +258,13 @@ static int dlpar_add_phb(char *drc_name, struct device_node *dn)
static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn)
{
- if (vio_find_node(dn))
+ struct vio_dev *vio_dev;
+
+ vio_dev = vio_find_node(dn);
+ if (vio_dev) {
+ put_device(&vio_dev->dev);
return -EINVAL;
+ }
if (!vio_register_device_node(dn)) {
printk(KERN_ERR
@@ -335,6 +340,9 @@ static int dlpar_remove_vio_slot(char *drc_name, struct device_node *dn)
return -EINVAL;
vio_unregister_device(vio_dev);
+
+ put_device(&vio_dev->dev);
+
return 0;
}
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 42d8617352ae..e311a9bf2c90 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -2043,6 +2043,10 @@ bool pci_dev_run_wake(struct pci_dev *dev)
if (!dev->pme_support)
return false;
+ /* PME-capable in principle, but not from the intended sleep state */
+ if (!pci_pme_capable(dev, pci_target_state(dev)))
+ return false;
+
while (bus->parent) {
struct pci_dev *bridge = bus->self;
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 317e3558a35e..c6a012b5ba39 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -518,25 +518,32 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev)
link = kzalloc(sizeof(*link), GFP_KERNEL);
if (!link)
return NULL;
+
INIT_LIST_HEAD(&link->sibling);
INIT_LIST_HEAD(&link->children);
INIT_LIST_HEAD(&link->link);
link->pdev = pdev;
- if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) {
+
+ /*
+ * Root Ports and PCI/PCI-X to PCIe Bridges are roots of PCIe
+ * hierarchies.
+ */
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE) {
+ link->root = link;
+ } else {
struct pcie_link_state *parent;
+
parent = pdev->bus->parent->self->link_state;
if (!parent) {
kfree(link);
return NULL;
}
+
link->parent = parent;
+ link->root = link->parent->root;
list_add(&link->link, &parent->children);
}
- /* Setup a pointer to the root port link */
- if (!link->parent)
- link->root = link;
- else
- link->root = link->parent->root;
list_add(&link->sibling, &link_list);
pdev->link_state = link;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index b5843c255263..71d9a6d1bd56 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1019,6 +1019,7 @@ void set_pcie_port_type(struct pci_dev *pdev)
pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
if (!pos)
return;
+
pdev->pcie_cap = pos;
pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &reg16);
pdev->pcie_flags_reg = reg16;
@@ -1026,13 +1027,14 @@ void set_pcie_port_type(struct pci_dev *pdev)
pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
/*
- * A Root Port is always the upstream end of a Link. No PCIe
- * component has two Links. Two Links are connected by a Switch
- * that has a Port on each Link and internal logic to connect the
- * two Ports.
+ * A Root Port or a PCI-to-PCIe bridge is always the upstream end
+ * of a Link. No PCIe component has two Links. Two Links are
+ * connected by a Switch that has a Port on each Link and internal
+ * logic to connect the two Ports.
*/
type = pci_pcie_type(pdev);
- if (type == PCI_EXP_TYPE_ROOT_PORT)
+ if (type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_PCIE_BRIDGE)
pdev->has_secondary_link = 1;
else if (type == PCI_EXP_TYPE_UPSTREAM ||
type == PCI_EXP_TYPE_DOWNSTREAM) {
diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c
index c3cb57ed6083..1e3e175d3e8d 100644
--- a/drivers/phy/phy-qcom-ufs.c
+++ b/drivers/phy/phy-qcom-ufs.c
@@ -113,6 +113,14 @@ struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
goto out;
}
+ /*
+ * UFS PHY power management is managed by its parent (UFS host
+ * controller) hence set the no the no runtime PM callbacks flag
+ * on UFS PHY device to avoid any accidental attempt to call the
+ * PM callbacks for PHY device.
+ */
+ pm_runtime_no_callbacks(&generic_phy->dev);
+
common_cfg->phy_spec_ops = phy_spec_ops;
common_cfg->dev = dev;
diff --git a/drivers/pinctrl/intel/pinctrl-broxton.c b/drivers/pinctrl/intel/pinctrl-broxton.c
index 5979d38c46b2..7329500943a3 100644
--- a/drivers/pinctrl/intel/pinctrl-broxton.c
+++ b/drivers/pinctrl/intel/pinctrl-broxton.c
@@ -19,7 +19,7 @@
#define BXT_PAD_OWN 0x020
#define BXT_HOSTSW_OWN 0x080
-#define BXT_PADCFGLOCK 0x090
+#define BXT_PADCFGLOCK 0x060
#define BXT_GPI_IE 0x110
#define BXT_COMMUNITY(s, e) \
diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c
index 84943e4cff09..13730ca151ad 100644
--- a/drivers/pinctrl/meson/pinctrl-meson.c
+++ b/drivers/pinctrl/meson/pinctrl-meson.c
@@ -246,7 +246,7 @@ static int meson_pmx_request_gpio(struct pinctrl_dev *pcdev,
{
struct meson_pinctrl *pc = pinctrl_dev_get_drvdata(pcdev);
- meson_pmx_disable_other_groups(pc, range->pin_base + offset, -1);
+ meson_pmx_disable_other_groups(pc, offset, -1);
return 0;
}
diff --git a/drivers/pinctrl/qcom/pinctrl-lpi.c b/drivers/pinctrl/qcom/pinctrl-lpi.c
index 67cac25689ef..4ca5d5fa0531 100644
--- a/drivers/pinctrl/qcom/pinctrl-lpi.c
+++ b/drivers/pinctrl/qcom/pinctrl-lpi.c
@@ -117,12 +117,12 @@ static const u32 lpi_offset[] = {
0x00005010,
0x00005020,
0x00005030,
- 0x00005040,
- 0x00005050,
0x00006000,
0x00006010,
0x00007000,
0x00007010,
+ 0x00005040,
+ 0x00005050,
0x00008000,
0x00008010,
0x00008020,
diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c
index 863c3e30ce05..50f2014fed55 100644
--- a/drivers/pinctrl/sh-pfc/pinctrl.c
+++ b/drivers/pinctrl/sh-pfc/pinctrl.c
@@ -483,7 +483,8 @@ static bool sh_pfc_pinconf_validate(struct sh_pfc *pfc, unsigned int _pin,
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
- return true;
+ return pin->configs &
+ (SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN);
case PIN_CONFIG_BIAS_PULL_UP:
return pin->configs & SH_PFC_PIN_CFG_PULL_UP;
diff --git a/drivers/platform/goldfish/pdev_bus.c b/drivers/platform/goldfish/pdev_bus.c
index 1f52462f4cdd..dd9ea463c2a4 100644
--- a/drivers/platform/goldfish/pdev_bus.c
+++ b/drivers/platform/goldfish/pdev_bus.c
@@ -157,23 +157,26 @@ static int goldfish_new_pdev(void)
static irqreturn_t goldfish_pdev_bus_interrupt(int irq, void *dev_id)
{
irqreturn_t ret = IRQ_NONE;
+
while (1) {
u32 op = readl(pdev_bus_base + PDEV_BUS_OP);
- switch (op) {
- case PDEV_BUS_OP_DONE:
- return IRQ_NONE;
+ switch (op) {
case PDEV_BUS_OP_REMOVE_DEV:
goldfish_pdev_remove();
+ ret = IRQ_HANDLED;
break;
case PDEV_BUS_OP_ADD_DEV:
goldfish_new_pdev();
+ ret = IRQ_HANDLED;
break;
+
+ case PDEV_BUS_OP_DONE:
+ default:
+ return ret;
}
- ret = IRQ_HANDLED;
}
- return ret;
}
static int goldfish_pdev_bus_probe(struct platform_device *pdev)
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index f48182cc04df..c2902beaa0b8 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -22,7 +22,7 @@
#include "gsi_reg.h"
#define GSI_CMD_TIMEOUT (5*HZ)
-#define GSI_STOP_CMD_TIMEOUT_MS 10
+#define GSI_STOP_CMD_TIMEOUT_MS 20
#define GSI_MAX_CH_LOW_WEIGHT 15
#define GSI_MHI_ER_START 10
#define GSI_MHI_ER_END 16
@@ -561,7 +561,7 @@ static void gsi_handle_irq(void)
if (!type)
break;
- GSIDBG("type %x\n", type);
+ GSIDBG_LOW("type %x\n", type);
if (type & GSI_EE_n_CNTXT_TYPE_IRQ_CH_CTRL_BMSK)
gsi_handle_ch_ctrl(ee);
@@ -2743,9 +2743,10 @@ void gsi_get_inst_ram_offset_and_size(unsigned long *base_offset,
unsigned long *size)
{
if (base_offset)
- *base_offset = GSI_GSI_INST_RAM_BASE_OFFS;
+ *base_offset = GSI_GSI_INST_RAM_n_OFFS(0);
if (size)
- *size = GSI_GSI_INST_RAM_SIZE;
+ *size = GSI_GSI_INST_RAM_n_WORD_SZ *
+ (GSI_GSI_INST_RAM_n_MAXn + 1);
}
EXPORT_SYMBOL(gsi_get_inst_ram_offset_and_size);
@@ -2821,6 +2822,13 @@ static int msm_gsi_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ gsi_ctx->ipc_logbuf = ipc_log_context_create(GSI_IPC_LOG_PAGES,
+ "gsi", 0);
+ if (gsi_ctx->ipc_logbuf == NULL) {
+ GSIERR("failed to get ipc_logbuf\n");
+ return -ENOMEM;
+ }
+
gsi_ctx->dev = dev;
init_completion(&gsi_ctx->gen_ee_cmd_compl);
gsi_debugfs_init();
diff --git a/drivers/platform/msm/gsi/gsi.h b/drivers/platform/msm/gsi/gsi.h
index d0eb162c49cf..f53a4bd8fa6b 100644
--- a/drivers/platform/msm/gsi/gsi.h
+++ b/drivers/platform/msm/gsi/gsi.h
@@ -18,6 +18,7 @@
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/msm_gsi.h>
+#include <linux/ipc_logging.h>
#define GSI_CHAN_MAX 31
#define GSI_EVT_RING_MAX 23
@@ -26,10 +27,48 @@
#define gsi_readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; })
#define gsi_writel(v, c) ({ __iowmb(); writel_relaxed((v), (c)); })
-#define GSIERR(fmt, args...) \
- dev_err(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, ## args)
+#define GSI_IPC_LOGGING(buf, fmt, args...) \
+ do { \
+ if (buf) \
+ ipc_log_string((buf), fmt, __func__, __LINE__, \
+ ## args); \
+ } while (0)
+
#define GSIDBG(fmt, args...) \
- dev_dbg(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, ## args)
+ do { \
+ dev_dbg(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, \
+ ## args);\
+ if (gsi_ctx) { \
+ GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf, \
+ "%s:%d " fmt, ## args); \
+ GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf_low, \
+ "%s:%d " fmt, ## args); \
+ } \
+ } while (0)
+
+#define GSIDBG_LOW(fmt, args...) \
+ do { \
+ dev_dbg(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, \
+ ## args);\
+ if (gsi_ctx) { \
+ GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf_low, \
+ "%s:%d " fmt, ## args); \
+ } \
+ } while (0)
+
+#define GSIERR(fmt, args...) \
+ do { \
+ dev_err(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, \
+ ## args);\
+ if (gsi_ctx) { \
+ GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf, \
+ "%s:%d " fmt, ## args); \
+ GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf_low, \
+ "%s:%d " fmt, ## args); \
+ } \
+ } while (0)
+
+#define GSI_IPC_LOG_PAGES 50
enum gsi_evt_ring_state {
GSI_EVT_RING_STATE_NOT_ALLOCATED = 0x0,
@@ -163,6 +202,8 @@ struct gsi_ctx {
u32 max_ch;
u32 max_ev;
struct completion gen_ee_cmd_compl;
+ void *ipc_logbuf;
+ void *ipc_logbuf_low;
};
enum gsi_re_type {
diff --git a/drivers/platform/msm/gsi/gsi_dbg.c b/drivers/platform/msm/gsi/gsi_dbg.c
index 5eb9084292a4..717c891788f2 100644
--- a/drivers/platform/msm/gsi/gsi_dbg.c
+++ b/drivers/platform/msm/gsi/gsi_dbg.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -747,6 +747,45 @@ error:
return -EFAULT;
}
+static ssize_t gsi_enable_ipc_low(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ unsigned long missing;
+ s8 option = 0;
+
+ if (sizeof(dbg_buff) < count + 1)
+ return -EFAULT;
+
+ missing = copy_from_user(dbg_buff, ubuf, count);
+ if (missing)
+ return -EFAULT;
+
+ dbg_buff[count] = '\0';
+ if (kstrtos8(dbg_buff, 0, &option))
+ return -EFAULT;
+
+ if (option) {
+ if (!gsi_ctx->ipc_logbuf_low) {
+ gsi_ctx->ipc_logbuf_low =
+ ipc_log_context_create(GSI_IPC_LOG_PAGES,
+ "gsi_low", 0);
+ }
+
+ if (gsi_ctx->ipc_logbuf_low == NULL) {
+ TERR("failed to get ipc_logbuf_low\n");
+ return -EFAULT;
+ }
+ } else {
+ if (gsi_ctx->ipc_logbuf_low)
+ ipc_log_context_destroy(gsi_ctx->ipc_logbuf_low);
+ gsi_ctx->ipc_logbuf_low = NULL;
+ }
+
+ return count;
+}
+
+
+
const struct file_operations gsi_ev_dump_ops = {
.write = gsi_dump_evt,
};
@@ -783,6 +822,10 @@ const struct file_operations gsi_print_dp_stats_ops = {
.write = gsi_print_dp_stats,
};
+const struct file_operations gsi_ipc_low_ops = {
+ .write = gsi_enable_ipc_low,
+};
+
void gsi_debugfs_init(void)
{
static struct dentry *dfile;
@@ -858,6 +901,13 @@ void gsi_debugfs_init(void)
goto fail;
}
+ dfile = debugfs_create_file("ipc_low", write_only_mode,
+ dent, 0, &gsi_ipc_low_ops);
+ if (!dfile || IS_ERR(dfile)) {
+ TERR("could not create ipc_low\n");
+ goto fail;
+ }
+
return;
fail:
debugfs_remove_recursive(dent);
diff --git a/drivers/platform/msm/gsi/gsi_reg.h b/drivers/platform/msm/gsi/gsi_reg.h
index d0462aad72d2..653cdd4823c6 100644
--- a/drivers/platform/msm/gsi/gsi_reg.h
+++ b/drivers/platform/msm/gsi/gsi_reg.h
@@ -688,8 +688,9 @@
#define GSI_GSI_IRAM_PTR_INT_MOD_STOPED_IRAM_PTR_BMSK 0xfff
#define GSI_GSI_IRAM_PTR_INT_MOD_STOPED_IRAM_PTR_SHFT 0x0
+#define GSI_GSI_INST_RAM_n_WORD_SZ 0x4
#define GSI_GSI_INST_RAM_n_OFFS(n) \
- (GSI_GSI_REG_BASE_OFFS + 0x00004000 + 0x4 * (n))
+ (GSI_GSI_REG_BASE_OFFS + 0x00004000 + GSI_GSI_INST_RAM_n_WORD_SZ * (n))
#define GSI_GSI_INST_RAM_n_RMSK 0xffffffff
#define GSI_GSI_INST_RAM_n_MAXn 4095
#define GSI_GSI_INST_RAM_n_INST_BYTE_3_BMSK 0xff000000
@@ -1842,7 +1843,5 @@
#define GSI_INTER_EE_n_SRC_EV_CH_IRQ_CLR_EV_CH_BIT_MAP_BMSK 0xffffffff
#define GSI_INTER_EE_n_SRC_EV_CH_IRQ_CLR_EV_CH_BIT_MAP_SHFT 0x0
-#define GSI_GSI_INST_RAM_BASE_OFFS 0x4000
-#define GSI_GSI_INST_RAM_SIZE 0x4000
#endif /* __GSI_REG_H__ */
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c
index 069f0a2e3fee..51c930a81c8d 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c
@@ -74,6 +74,10 @@ struct ipa_uc_offload_ctx {
static struct ipa_uc_offload_ctx *ipa_uc_offload_ctx[IPA_UC_MAX_PROT_SIZE];
+static int ipa_uc_ntn_cons_release(void);
+static int ipa_uc_ntn_cons_request(void);
+static void ipa_uc_offload_rm_notify(void *, enum ipa_rm_event, unsigned long);
+
static int ipa_commit_partial_hdr(
struct ipa_ioc_add_hdr *hdr,
const char *netdev_name,
@@ -115,16 +119,37 @@ static int ipa_uc_offload_ntn_reg_intf(
struct ipa_uc_offload_out_params *outp,
struct ipa_uc_offload_ctx *ntn_ctx)
{
- struct ipa_ioc_add_hdr *hdr;
+ struct ipa_ioc_add_hdr *hdr = NULL;
struct ipa_tx_intf tx;
struct ipa_rx_intf rx;
struct ipa_ioc_tx_intf_prop tx_prop[2];
struct ipa_ioc_rx_intf_prop rx_prop[2];
+ struct ipa_rm_create_params param;
u32 len;
int ret = 0;
IPA_UC_OFFLOAD_DBG("register interface for netdev %s\n",
inp->netdev_name);
+ memset(&param, 0, sizeof(param));
+ param.name = IPA_RM_RESOURCE_ODU_ADAPT_PROD;
+ param.reg_params.user_data = ntn_ctx;
+ param.reg_params.notify_cb = ipa_uc_offload_rm_notify;
+ param.floor_voltage = IPA_VOLTAGE_SVS;
+ ret = ipa_rm_create_resource(&param);
+ if (ret) {
+ IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_PROD resource\n");
+ return -EFAULT;
+ }
+
+ memset(&param, 0, sizeof(param));
+ param.name = IPA_RM_RESOURCE_ODU_ADAPT_CONS;
+ param.request_resource = ipa_uc_ntn_cons_request;
+ param.release_resource = ipa_uc_ntn_cons_release;
+ ret = ipa_rm_create_resource(&param);
+ if (ret) {
+ IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_CONS resource\n");
+ goto fail_create_rm_cons;
+ }
memcpy(ntn_ctx->netdev_name, inp->netdev_name, IPA_RESOURCE_NAME_MAX);
ntn_ctx->hdr_len = inp->hdr_info[0].hdr_len;
@@ -136,7 +161,8 @@ static int ipa_uc_offload_ntn_reg_intf(
hdr = kzalloc(len, GFP_KERNEL);
if (hdr == NULL) {
IPA_UC_OFFLOAD_ERR("fail to alloc %d bytes\n", len);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto fail_alloc;
}
if (ipa_commit_partial_hdr(hdr, ntn_ctx->netdev_name, inp->hdr_info)) {
@@ -197,8 +223,15 @@ static int ipa_uc_offload_ntn_reg_intf(
init_completion(&ntn_ctx->ntn_completion);
ntn_ctx->state = IPA_UC_OFFLOAD_STATE_INITIALIZED;
+ kfree(hdr);
+ return ret;
+
fail:
kfree(hdr);
+fail_alloc:
+ ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS);
+fail_create_rm_cons:
+ ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
return ret;
}
@@ -301,9 +334,10 @@ int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp,
struct ipa_ntn_conn_out_params *outp,
struct ipa_uc_offload_ctx *ntn_ctx)
{
- struct ipa_rm_create_params param;
int result = 0;
+ enum ipa_uc_offload_state prev_state;
+ prev_state = ntn_ctx->state;
if (inp->dl.ring_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT ||
inp->dl.buff_pool_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT) {
IPA_UC_OFFLOAD_ERR("alignment failure on TX\n");
@@ -315,42 +349,13 @@ int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp,
return -EINVAL;
}
- memset(&param, 0, sizeof(param));
- param.name = IPA_RM_RESOURCE_ODU_ADAPT_PROD;
- param.reg_params.user_data = ntn_ctx;
- param.reg_params.notify_cb = ipa_uc_offload_rm_notify;
- param.floor_voltage = IPA_VOLTAGE_SVS;
- result = ipa_rm_create_resource(&param);
- if (result) {
- IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_PROD resource\n");
- return -EFAULT;
- }
-
- memset(&param, 0, sizeof(param));
- param.name = IPA_RM_RESOURCE_ODU_ADAPT_CONS;
- param.request_resource = ipa_uc_ntn_cons_request;
- param.release_resource = ipa_uc_ntn_cons_release;
- result = ipa_rm_create_resource(&param);
+ result = ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
+ IPA_RM_RESOURCE_APPS_CONS);
if (result) {
- IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_CONS resource\n");
- goto fail_create_rm_cons;
- }
-
- if (ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
- IPA_RM_RESOURCE_APPS_CONS)) {
- IPA_UC_OFFLOAD_ERR("fail to add rm dependency\n");
- result = -EFAULT;
- goto fail;
- }
-
- if (ipa_setup_uc_ntn_pipes(inp, ntn_ctx->notify,
- ntn_ctx->priv, ntn_ctx->hdr_len, outp)) {
- IPA_UC_OFFLOAD_ERR("fail to setup uc offload pipes\n");
- result = -EFAULT;
- goto fail;
+ IPA_UC_OFFLOAD_ERR("fail to add rm dependency: %d\n", result);
+ return result;
}
- ntn_ctx->state = IPA_UC_OFFLOAD_STATE_UP;
result = ipa_rm_request_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
if (result == -EINPROGRESS) {
if (wait_for_completion_timeout(&ntn_ctx->ntn_completion,
@@ -365,13 +370,22 @@ int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp,
goto fail;
}
+ ntn_ctx->state = IPA_UC_OFFLOAD_STATE_UP;
+ result = ipa_setup_uc_ntn_pipes(inp, ntn_ctx->notify,
+ ntn_ctx->priv, ntn_ctx->hdr_len, outp);
+ if (result) {
+ IPA_UC_OFFLOAD_ERR("fail to setup uc offload pipes: %d\n",
+ result);
+ ntn_ctx->state = prev_state;
+ result = -EFAULT;
+ goto fail;
+ }
+
return 0;
fail:
- ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS);
-fail_create_rm_cons:
- ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
-
+ ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
+ IPA_RM_RESOURCE_APPS_CONS);
return result;
}
@@ -399,7 +413,8 @@ int ipa_uc_offload_conn_pipes(struct ipa_uc_offload_conn_in_params *inp,
return -EINVAL;
}
- if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_INITIALIZED) {
+ if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_INITIALIZED &&
+ offload_ctx->state != IPA_UC_OFFLOAD_STATE_DOWN) {
IPA_UC_OFFLOAD_ERR("Invalid state %d\n", offload_ctx->state);
return -EPERM;
}
@@ -454,32 +469,34 @@ EXPORT_SYMBOL(ipa_set_perf_profile);
static int ipa_uc_ntn_disconn_pipes(struct ipa_uc_offload_ctx *ntn_ctx)
{
int ipa_ep_idx_ul, ipa_ep_idx_dl;
+ int ret = 0;
ntn_ctx->state = IPA_UC_OFFLOAD_STATE_DOWN;
- if (ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
- IPA_RM_RESOURCE_APPS_CONS)) {
- IPA_UC_OFFLOAD_ERR("fail to delete rm dependency\n");
- return -EFAULT;
- }
- if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD)) {
- IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_PROD resource\n");
+ ret = ipa_rm_release_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
+ if (ret) {
+ IPA_UC_OFFLOAD_ERR("fail to release ODU_ADAPT_PROD res: %d\n",
+ ret);
return -EFAULT;
}
- if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS)) {
- IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_CONS resource\n");
+ ret = ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
+ IPA_RM_RESOURCE_APPS_CONS);
+ if (ret) {
+ IPA_UC_OFFLOAD_ERR("fail to del dep ODU->APPS, %d\n", ret);
return -EFAULT;
}
ipa_ep_idx_ul = ipa_get_ep_mapping(IPA_CLIENT_ODU_PROD);
ipa_ep_idx_dl = ipa_get_ep_mapping(IPA_CLIENT_ODU_TETH_CONS);
- if (ipa_tear_down_uc_offload_pipes(ipa_ep_idx_ul, ipa_ep_idx_dl)) {
- IPA_UC_OFFLOAD_ERR("fail to tear down uc offload pipes\n");
+ ret = ipa_tear_down_uc_offload_pipes(ipa_ep_idx_ul, ipa_ep_idx_dl);
+ if (ret) {
+ IPA_UC_OFFLOAD_ERR("fail to tear down ntn offload pipes, %d\n",
+ ret);
return -EFAULT;
}
- return 0;
+ return ret;
}
int ipa_uc_offload_disconn_pipes(u32 clnt_hdl)
@@ -524,6 +541,16 @@ static int ipa_uc_ntn_cleanup(struct ipa_uc_offload_ctx *ntn_ctx)
int len, result = 0;
struct ipa_ioc_del_hdr *hdr;
+ if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD)) {
+ IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_PROD resource\n");
+ return -EFAULT;
+ }
+
+ if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS)) {
+ IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_CONS resource\n");
+ return -EFAULT;
+ }
+
len = sizeof(struct ipa_ioc_del_hdr) + 2 * sizeof(struct ipa_hdr_del);
hdr = kzalloc(len, GFP_KERNEL);
if (hdr == NULL) {
diff --git a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c
index 54cad888cb7f..e10c75a473ce 100644
--- a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c
+++ b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c
@@ -227,7 +227,7 @@ int ipa_rm_dep_graph_delete_dependency(struct ipa_rm_dep_graph *graph,
if (ipa_rm_dep_graph_get_resource(graph,
resource_name,
&dependent)) {
- IPA_RM_ERR("%s does not exist\n",
+ IPA_RM_DBG("%s does not exist\n",
ipa_rm_resource_str(resource_name));
result = -EINVAL;
goto bail;
@@ -236,7 +236,7 @@ int ipa_rm_dep_graph_delete_dependency(struct ipa_rm_dep_graph *graph,
if (ipa_rm_dep_graph_get_resource(graph,
depends_on_name,
&dependency)) {
- IPA_RM_ERR("%s does not exist\n",
+ IPA_RM_DBG("%s does not exist\n",
ipa_rm_resource_str(depends_on_name));
result = -EINVAL;
goto bail;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
index a2665c9e9688..25364e8efa38 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
@@ -1974,6 +1974,8 @@ static void ipa_cleanup_wlan_rx_common_cache(void)
struct ipa_rx_pkt_wrapper *rx_pkt;
struct ipa_rx_pkt_wrapper *tmp;
+ spin_lock_bh(&ipa_ctx->wc_memb.wlan_spinlock);
+
list_for_each_entry_safe(rx_pkt, tmp,
&ipa_ctx->wc_memb.wlan_comm_desc_list, link) {
list_del(&rx_pkt->link);
@@ -1994,6 +1996,8 @@ static void ipa_cleanup_wlan_rx_common_cache(void)
IPAERR("wlan comm buff total cnt: %d\n",
ipa_ctx->wc_memb.wlan_comm_total_cnt);
+ spin_unlock_bh(&ipa_ctx->wc_memb.wlan_spinlock);
+
}
static void ipa_alloc_wlan_rx_common_cache(u32 size)
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
index 7ca2314d5839..985c3e560c86 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
@@ -1465,18 +1465,18 @@ void ipa_install_dflt_flt_rules(u32 ipa_ep_idx)
mutex_lock(&ipa_ctx->lock);
tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v4];
- tbl->sticky_rear = true;
rule.action = IPA_PASS_TO_EXCEPTION;
- __ipa_add_flt_rule(tbl, IPA_IP_v4, &rule, false,
+ __ipa_add_flt_rule(tbl, IPA_IP_v4, &rule, true,
&ep->dflt_flt4_rule_hdl);
ipa_ctx->ctrl->ipa_commit_flt(IPA_IP_v4);
+ tbl->sticky_rear = true;
tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v6];
- tbl->sticky_rear = true;
rule.action = IPA_PASS_TO_EXCEPTION;
- __ipa_add_flt_rule(tbl, IPA_IP_v6, &rule, false,
+ __ipa_add_flt_rule(tbl, IPA_IP_v6, &rule, true,
&ep->dflt_flt6_rule_hdl);
ipa_ctx->ctrl->ipa_commit_flt(IPA_IP_v6);
+ tbl->sticky_rear = true;
mutex_unlock(&ipa_ctx->lock);
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
index dfc3e06f452b..f8f8fd12161a 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -272,6 +272,14 @@ int ipa_query_intf_tx_props(struct ipa_ioc_query_intf_tx_props *tx)
mutex_lock(&ipa_ctx->lock);
list_for_each_entry(entry, &ipa_ctx->intf_list, link) {
if (!strncmp(entry->name, tx->name, IPA_RESOURCE_NAME_MAX)) {
+ /* add the entry check */
+ if (entry->num_tx_props != tx->num_tx_props) {
+ IPAERR("invalid entry number(%u %u)\n",
+ entry->num_tx_props,
+ tx->num_tx_props);
+ mutex_unlock(&ipa_ctx->lock);
+ return result;
+ }
memcpy(tx->tx, entry->tx, entry->num_tx_props *
sizeof(struct ipa_ioc_tx_intf_prop));
result = 0;
@@ -305,6 +313,14 @@ int ipa_query_intf_rx_props(struct ipa_ioc_query_intf_rx_props *rx)
mutex_lock(&ipa_ctx->lock);
list_for_each_entry(entry, &ipa_ctx->intf_list, link) {
if (!strncmp(entry->name, rx->name, IPA_RESOURCE_NAME_MAX)) {
+ /* add the entry check */
+ if (entry->num_rx_props != rx->num_rx_props) {
+ IPAERR("invalid entry number(%u %u)\n",
+ entry->num_rx_props,
+ rx->num_rx_props);
+ mutex_unlock(&ipa_ctx->lock);
+ return result;
+ }
memcpy(rx->rx, entry->rx, entry->num_rx_props *
sizeof(struct ipa_ioc_rx_intf_prop));
result = 0;
@@ -338,6 +354,14 @@ int ipa_query_intf_ext_props(struct ipa_ioc_query_intf_ext_props *ext)
mutex_lock(&ipa_ctx->lock);
list_for_each_entry(entry, &ipa_ctx->intf_list, link) {
if (!strcmp(entry->name, ext->name)) {
+ /* add the entry check */
+ if (entry->num_ext_props != ext->num_ext_props) {
+ IPAERR("invalid entry number(%u %u)\n",
+ entry->num_ext_props,
+ ext->num_ext_props);
+ mutex_unlock(&ipa_ctx->lock);
+ return result;
+ }
memcpy(ext->ext, entry->ext, entry->num_ext_props *
sizeof(struct ipa_ioc_ext_intf_prop));
result = 0;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
index e2ac9bfceed7..119d17cae9f5 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
@@ -655,9 +655,8 @@ int qmi_filter_notify_send(struct ipa_fltr_installed_notif_req_msg_v01 *req)
/* check if the filter rules from IPACM is valid */
if (req->filter_index_list_len == 0) {
- IPAWANERR(" delete UL filter rule for pipe %d\n",
+ IPAWANDBG(" delete UL filter rule for pipe %d\n",
req->source_pipe_index);
- return -EINVAL;
} else if (req->filter_index_list_len > QMI_IPA_MAX_FILTERS_V01) {
IPAWANERR(" UL filter rule for pipe %d exceed max (%u)\n",
req->source_pipe_index,
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c
index d14f8da15595..0b46ab2a8439 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c
@@ -299,12 +299,6 @@ int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
/* setup ul ep cfg */
ep_ul->valid = 1;
ep_ul->client = in->ul.client;
- result = ipa_enable_data_path(ipa_ep_idx_ul);
- if (result) {
- IPAERR("disable data path failed res=%d clnt=%d.\n", result,
- ipa_ep_idx_ul);
- return -EFAULT;
- }
ep_ul->client_notify = notify;
ep_ul->priv = priv;
@@ -333,14 +327,6 @@ int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
/* setup dl ep cfg */
ep_dl->valid = 1;
ep_dl->client = in->dl.client;
- result = ipa_enable_data_path(ipa_ep_idx_dl);
- if (result) {
- IPAERR("disable data path failed res=%d clnt=%d.\n", result,
- ipa_ep_idx_dl);
- result = -EFAULT;
- goto fail;
- }
-
memset(&ep_dl->cfg, 0, sizeof(ep_ul->cfg));
ep_dl->cfg.nat.nat_en = IPA_BYPASS_NAT;
ep_dl->cfg.hdr.hdr_len = hdr_len;
@@ -359,6 +345,14 @@ int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
}
outp->dl_uc_db_pa = IPA_UC_NTN_DB_PA_TX;
ep_dl->uc_offload_state |= IPA_UC_OFFLOAD_CONNECTED;
+
+ result = ipa_enable_data_path(ipa_ep_idx_dl);
+ if (result) {
+ IPAERR("Enable data path failed res=%d clnt=%d.\n", result,
+ ipa_ep_idx_dl);
+ result = -EFAULT;
+ goto fail;
+ }
IPAERR("client %d (ep: %d) connected\n", in->dl.client,
ipa_ep_idx_dl);
@@ -402,28 +396,32 @@ int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul,
}
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
- /* teardown the UL pipe */
cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base;
cmd_data->protocol = IPA_HW_FEATURE_NTN;
-
tear = &cmd_data->CommonCh_params.NtnCommonCh_params;
- tear->params.ipa_pipe_number = ipa_ep_idx_ul;
+
+ /* teardown the DL pipe */
+ ipa_disable_data_path(ipa_ep_idx_dl);
+ /*
+ * Reset ep before sending cmd otherwise disconnect
+ * during data transfer will result into
+ * enormous suspend interrupts
+ */
+ memset(&ipa_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa_ep_context));
+ IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl);
+ tear->params.ipa_pipe_number = ipa_ep_idx_dl;
result = ipa_uc_send_cmd((u32)(cmd.phys_base),
IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
false, 10*HZ);
if (result) {
- IPAERR("fail to tear down ul pipe\n");
+ IPAERR("fail to tear down dl pipe\n");
result = -EFAULT;
goto fail;
}
- ipa_disable_data_path(ipa_ep_idx_ul);
- ipa_delete_dflt_flt_rules(ipa_ep_idx_ul);
- memset(&ipa_ctx->ep[ipa_ep_idx_ul], 0, sizeof(struct ipa_ep_context));
- IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul);
- /* teardown the DL pipe */
- tear->params.ipa_pipe_number = ipa_ep_idx_dl;
+ /* teardown the UL pipe */
+ tear->params.ipa_pipe_number = ipa_ep_idx_ul;
result = ipa_uc_send_cmd((u32)(cmd.phys_base),
IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
@@ -433,9 +431,10 @@ int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul,
result = -EFAULT;
goto fail;
}
- ipa_disable_data_path(ipa_ep_idx_dl);
- memset(&ipa_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa_ep_context));
- IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl);
+
+ ipa_delete_dflt_flt_rules(ipa_ep_idx_ul);
+ memset(&ipa_ctx->ep[ipa_ep_idx_ul], 0, sizeof(struct ipa_ep_context));
+ IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul);
fail:
dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
index b9f57552533e..b687b711dc20 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -275,6 +275,14 @@ int ipa3_query_intf_tx_props(struct ipa_ioc_query_intf_tx_props *tx)
mutex_lock(&ipa3_ctx->lock);
list_for_each_entry(entry, &ipa3_ctx->intf_list, link) {
if (!strcmp(entry->name, tx->name)) {
+ /* add the entry check */
+ if (entry->num_tx_props != tx->num_tx_props) {
+ IPAERR("invalid entry number(%u %u)\n",
+ entry->num_tx_props,
+ tx->num_tx_props);
+ mutex_unlock(&ipa3_ctx->lock);
+ return result;
+ }
memcpy(tx->tx, entry->tx, entry->num_tx_props *
sizeof(struct ipa_ioc_tx_intf_prop));
result = 0;
@@ -314,6 +322,14 @@ int ipa3_query_intf_rx_props(struct ipa_ioc_query_intf_rx_props *rx)
mutex_lock(&ipa3_ctx->lock);
list_for_each_entry(entry, &ipa3_ctx->intf_list, link) {
if (!strcmp(entry->name, rx->name)) {
+ /* add the entry check */
+ if (entry->num_rx_props != rx->num_rx_props) {
+ IPAERR("invalid entry number(%u %u)\n",
+ entry->num_rx_props,
+ rx->num_rx_props);
+ mutex_unlock(&ipa3_ctx->lock);
+ return result;
+ }
memcpy(rx->rx, entry->rx, entry->num_rx_props *
sizeof(struct ipa_ioc_rx_intf_prop));
result = 0;
@@ -348,6 +364,14 @@ int ipa3_query_intf_ext_props(struct ipa_ioc_query_intf_ext_props *ext)
mutex_lock(&ipa3_ctx->lock);
list_for_each_entry(entry, &ipa3_ctx->intf_list, link) {
if (!strcmp(entry->name, ext->name)) {
+ /* add the entry check */
+ if (entry->num_ext_props != ext->num_ext_props) {
+ IPAERR("invalid entry number(%u %u)\n",
+ entry->num_ext_props,
+ ext->num_ext_props);
+ mutex_unlock(&ipa3_ctx->lock);
+ return result;
+ }
memcpy(ext->ext, entry->ext, entry->num_ext_props *
sizeof(struct ipa_ioc_ext_intf_prop));
result = 0;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
index 3086311b5c2a..335e5283cc29 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
@@ -786,9 +786,8 @@ int ipa3_qmi_filter_notify_send(
/* check if the filter rules from IPACM is valid */
if (req->rule_id_len == 0) {
- IPAWANERR(" delete UL filter rule for pipe %d\n",
+ IPAWANDBG(" delete UL filter rule for pipe %d\n",
req->source_pipe_index);
- return -EINVAL;
} else if (req->rule_id_len > QMI_IPA_MAX_FILTERS_V01) {
IPAWANERR(" UL filter rule for pipe %d exceed max (%u)\n",
req->source_pipe_index,
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c
index 7b891843028d..fdb6d05f683d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c
@@ -265,12 +265,6 @@ int ipa3_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
/* setup ul ep cfg */
ep_ul->valid = 1;
ep_ul->client = in->ul.client;
- result = ipa3_enable_data_path(ipa_ep_idx_ul);
- if (result) {
- IPAERR("disable data path failed res=%d clnt=%d.\n", result,
- ipa_ep_idx_ul);
- return -EFAULT;
- }
ep_ul->client_notify = notify;
ep_ul->priv = priv;
@@ -299,14 +293,6 @@ int ipa3_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
/* setup dl ep cfg */
ep_dl->valid = 1;
ep_dl->client = in->dl.client;
- result = ipa3_enable_data_path(ipa_ep_idx_dl);
- if (result) {
- IPAERR("disable data path failed res=%d clnt=%d.\n", result,
- ipa_ep_idx_dl);
- result = -EFAULT;
- goto fail;
- }
-
memset(&ep_dl->cfg, 0, sizeof(ep_ul->cfg));
ep_dl->cfg.nat.nat_en = IPA_BYPASS_NAT;
ep_dl->cfg.hdr.hdr_len = hdr_len;
@@ -325,6 +311,14 @@ int ipa3_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
}
outp->dl_uc_db_pa = IPA_UC_NTN_DB_PA_TX;
ep_dl->uc_offload_state |= IPA_UC_OFFLOAD_CONNECTED;
+
+ result = ipa3_enable_data_path(ipa_ep_idx_dl);
+ if (result) {
+ IPAERR("Enable data path failed res=%d clnt=%d.\n", result,
+ ipa_ep_idx_dl);
+ result = -EFAULT;
+ goto fail;
+ }
IPADBG("client %d (ep: %d) connected\n", in->dl.client,
ipa_ep_idx_dl);
@@ -368,28 +362,32 @@ int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul,
}
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
- /* teardown the UL pipe */
cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base;
cmd_data->protocol = IPA_HW_FEATURE_NTN;
-
tear = &cmd_data->CommonCh_params.NtnCommonCh_params;
- tear->params.ipa_pipe_number = ipa_ep_idx_ul;
+
+ /* teardown the DL pipe */
+ ipa3_disable_data_path(ipa_ep_idx_dl);
+ /*
+ * Reset ep before sending cmd otherwise disconnect
+ * during data transfer will result into
+ * enormous suspend interrupts
+ */
+ memset(&ipa3_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa3_ep_context));
+ IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl);
+ tear->params.ipa_pipe_number = ipa_ep_idx_dl;
result = ipa3_uc_send_cmd((u32)(cmd.phys_base),
IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
false, 10*HZ);
if (result) {
- IPAERR("fail to tear down ul pipe\n");
+ IPAERR("fail to tear down dl pipe\n");
result = -EFAULT;
goto fail;
}
- ipa3_disable_data_path(ipa_ep_idx_ul);
- ipa3_delete_dflt_flt_rules(ipa_ep_idx_ul);
- memset(&ipa3_ctx->ep[ipa_ep_idx_ul], 0, sizeof(struct ipa3_ep_context));
- IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul);
- /* teardown the DL pipe */
- tear->params.ipa_pipe_number = ipa_ep_idx_dl;
+ /* teardown the UL pipe */
+ tear->params.ipa_pipe_number = ipa_ep_idx_ul;
result = ipa3_uc_send_cmd((u32)(cmd.phys_base),
IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
@@ -399,9 +397,9 @@ int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul,
result = -EFAULT;
goto fail;
}
- ipa3_disable_data_path(ipa_ep_idx_dl);
+ ipa3_delete_dflt_flt_rules(ipa_ep_idx_ul);
memset(&ipa3_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa3_ep_context));
- IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl);
+ IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul);
fail:
dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index 2f28ba673d5a..c8ff06ddda87 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -3565,11 +3565,6 @@ int ipa3_stop_gsi_channel(u32 clnt_hdl)
memset(&mem, 0, sizeof(mem));
- if (IPA_CLIENT_IS_PROD(ep->client)) {
- res = gsi_stop_channel(ep->gsi_chan_hdl);
- goto end_sequence;
- }
-
for (i = 0; i < IPA_GSI_CHANNEL_STOP_MAX_RETRY; i++) {
IPADBG("Calling gsi_stop_channel\n");
res = gsi_stop_channel(ep->gsi_chan_hdl);
@@ -3577,12 +3572,14 @@ int ipa3_stop_gsi_channel(u32 clnt_hdl)
if (res != -GSI_STATUS_AGAIN && res != -GSI_STATUS_TIMED_OUT)
goto end_sequence;
- IPADBG("Inject a DMA_TASK with 1B packet to IPA and retry\n");
- /* Send a 1B packet DMA_TASK to IPA and try again */
- res = ipa3_inject_dma_task_for_gsi();
- if (res) {
- IPAERR("Failed to inject DMA TASk for GSI\n");
- goto end_sequence;
+ if (IPA_CLIENT_IS_CONS(ep->client)) {
+ IPADBG("Inject a DMA_TASK with 1B packet to IPA\n");
+ /* Send a 1B packet DMA_TASK to IPA and try again */
+ res = ipa3_inject_dma_task_for_gsi();
+ if (res) {
+ IPAERR("Failed to inject DMA TASk for GSI\n");
+ goto end_sequence;
+ }
}
/* sleep for short period to flush IPA */
diff --git a/drivers/platform/msm/mhi/mhi.h b/drivers/platform/msm/mhi/mhi.h
index 3d40d114437a..4bce96102525 100644
--- a/drivers/platform/msm/mhi/mhi.h
+++ b/drivers/platform/msm/mhi/mhi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,7 +29,6 @@
#include <linux/list.h>
#include <linux/dma-mapping.h>
-extern struct mhi_pcie_devices mhi_devices;
struct mhi_device_ctxt;
enum MHI_DEBUG_LEVEL {
@@ -49,23 +48,56 @@ struct pcie_core_info {
u32 mhi_ver;
void __iomem *bar0_base;
void __iomem *bar0_end;
- void __iomem *bar2_base;
- void __iomem *bar2_end;
u32 irq_base;
u32 max_nr_msis;
+ u32 domain;
+ u32 bus;
+ u32 slot;
struct pci_saved_state *pcie_state;
+ bool pci_master;
+};
+
+struct firmware_info {
+ const char *fw_image;
+ size_t max_sbl_len;
+ size_t segment_size;
+};
+
+struct bhie_mem_info {
+ void *pre_aligned;
+ void *aligned;
+ size_t alloc_size;
+ size_t size;
+ phys_addr_t phys_addr;
+ dma_addr_t dma_handle;
+};
+
+struct bhie_vec_table {
+ struct scatterlist *sg_list;
+ struct bhie_mem_info *bhie_mem_info;
+ struct bhi_vec_entry *bhi_vec_entry;
+ unsigned segment_count;
+ u32 sequence; /* sequence to indicate new xfer */
};
struct bhi_ctxt_t {
void __iomem *bhi_base;
+ void *unaligned_image_loc;
+ dma_addr_t dma_handle;
+ size_t alloc_size;
void *image_loc;
dma_addr_t phy_image_loc;
size_t image_size;
- void *unaligned_image_loc;
dev_t bhi_dev;
struct cdev cdev;
- struct class *bhi_class;
struct device *dev;
+ u32 alignment;
+ u32 poll_timeout;
+ /* BHI/E vector table */
+ bool manage_boot; /* fw download done by MHI host */
+ struct work_struct fw_load_work;
+ struct firmware_info firmware_info;
+ struct bhie_vec_table fw_table;
};
enum MHI_CHAN_DIR {
@@ -316,6 +348,11 @@ struct mhi_ring {
u32 msi_disable_cntr;
u32 msi_enable_cntr;
spinlock_t ring_lock;
+ struct dma_pool *dma_pool;
+ struct tasklet_struct ev_task;
+ struct work_struct ev_worker;
+ struct mhi_device_ctxt *mhi_dev_ctxt;
+ int index;
};
enum MHI_CMD_STATUS {
@@ -344,25 +381,27 @@ enum MHI_INIT_ERROR_STAGE {
};
enum STATE_TRANSITION {
- STATE_TRANSITION_RESET = 0x0,
- STATE_TRANSITION_READY = 0x1,
- STATE_TRANSITION_M0 = 0x2,
- STATE_TRANSITION_M1 = 0x3,
- STATE_TRANSITION_M2 = 0x4,
- STATE_TRANSITION_M3 = 0x5,
- STATE_TRANSITION_BHI = 0x6,
- STATE_TRANSITION_SBL = 0x7,
- STATE_TRANSITION_AMSS = 0x8,
- STATE_TRANSITION_LINK_DOWN = 0x9,
- STATE_TRANSITION_WAKE = 0xA,
- STATE_TRANSITION_SYS_ERR = 0xFF,
- STATE_TRANSITION_reserved = 0x80000000
+ STATE_TRANSITION_RESET = MHI_STATE_RESET,
+ STATE_TRANSITION_READY = MHI_STATE_READY,
+ STATE_TRANSITION_M0 = MHI_STATE_M0,
+ STATE_TRANSITION_M1 = MHI_STATE_M1,
+ STATE_TRANSITION_M2 = MHI_STATE_M2,
+ STATE_TRANSITION_M3 = MHI_STATE_M3,
+ STATE_TRANSITION_BHI,
+ STATE_TRANSITION_SBL,
+ STATE_TRANSITION_AMSS,
+ STATE_TRANSITION_LINK_DOWN,
+ STATE_TRANSITION_WAKE,
+ STATE_TRANSITION_BHIE,
+ STATE_TRANSITION_SYS_ERR,
+ STATE_TRANSITION_MAX
};
enum MHI_EXEC_ENV {
MHI_EXEC_ENV_PBL = 0x0,
MHI_EXEC_ENV_SBL = 0x1,
MHI_EXEC_ENV_AMSS = 0x2,
+ MHI_EXEC_ENV_BHIE = 0x3,
MHI_EXEC_ENV_reserved = 0x80000000
};
@@ -382,7 +421,7 @@ struct mhi_chan_cfg {
union mhi_cmd_pkt cmd_pkt;
};
-struct mhi_client_handle {
+struct mhi_client_config {
struct mhi_chan_info chan_info;
struct mhi_device_ctxt *mhi_dev_ctxt;
struct mhi_client_info_t client_info;
@@ -412,9 +451,12 @@ struct mhi_state_work_queue {
struct mhi_buf_info {
dma_addr_t bb_p_addr;
+ dma_addr_t pre_alloc_p_addr;
void *bb_v_addr;
+ void *pre_alloc_v_addr;
void *client_buf;
size_t buf_len;
+ size_t pre_alloc_len;
size_t filled_size;
enum dma_data_direction dir;
int bb_active;
@@ -431,23 +473,19 @@ struct mhi_counters {
u32 bb_used[MHI_MAX_CHANNELS];
atomic_t device_wake;
atomic_t outbound_acks;
- atomic_t events_pending;
u32 *msi_counter;
u32 mhi_reset_cntr;
+ u32 link_down_cntr;
+ u32 link_up_cntr;
};
struct mhi_flags {
u32 mhi_initialized;
u32 link_up;
- int stop_threads;
- u32 kill_threads;
- u32 ev_thread_stopped;
- u32 st_thread_stopped;
+ bool bb_required;
};
struct mhi_wait_queues {
- wait_queue_head_t *mhi_event_wq;
- wait_queue_head_t *state_change_event;
wait_queue_head_t *m0_event;
wait_queue_head_t *m3_event;
wait_queue_head_t *bhi_event;
@@ -486,13 +524,17 @@ struct mhi_dev_space {
};
struct mhi_device_ctxt {
+ struct list_head node;
+ struct pcie_core_info core;
+ struct msm_pcie_register_event mhi_pci_link_event;
+ struct pci_dev *pcie_device;
+ struct bhi_ctxt_t bhi_ctxt;
+ struct platform_device *plat_dev;
enum MHI_PM_STATE mhi_pm_state; /* Host driver state */
enum MHI_STATE mhi_state; /* protocol state */
enum MHI_EXEC_ENV dev_exec_env;
struct mhi_dev_space dev_space;
- struct mhi_pcie_dev_info *dev_info;
- struct pcie_core_info *dev_props;
struct mhi_ring chan_bb_list[MHI_MAX_CHANNELS];
struct mhi_ring mhi_local_chan_ctxt[MHI_MAX_CHANNELS];
@@ -500,12 +542,9 @@ struct mhi_device_ctxt {
struct mhi_ring mhi_local_cmd_ctxt[NR_OF_CMD_RINGS];
struct mhi_chan_cfg mhi_chan_cfg[MHI_MAX_CHANNELS];
-
struct mhi_client_handle *client_handle_list[MHI_MAX_CHANNELS];
struct mhi_event_ring_cfg *ev_ring_props;
- struct task_struct *event_thread_handle;
- struct task_struct *st_thread_handle;
- struct tasklet_struct ev_task; /* Process control Events */
+ struct work_struct st_thread_worker;
struct work_struct process_m1_worker;
struct mhi_wait_queues mhi_ev_wq;
struct dev_mmio_info mmio_info;
@@ -517,7 +556,9 @@ struct mhi_device_ctxt {
struct hrtimer m1_timer;
ktime_t m1_timeout;
+ u32 poll_reset_timeout_ms;
+ struct notifier_block mhi_ssr_nb;
struct esoc_desc *esoc_handle;
void *esoc_ssr_handle;
@@ -534,35 +575,47 @@ struct mhi_device_ctxt {
struct wakeup_source w_lock;
char *chan_info;
- struct dentry *mhi_parent_folder;
-};
+ struct dentry *child;
+ struct dentry *parent;
+ void *mhi_ipc_log;
+
+ /* Shadow functions since not all device supports runtime pm */
+ int (*bus_master_rt_get)(struct pci_dev *pci_dev);
+ void (*bus_master_rt_put)(struct pci_dev *pci_dev);
+ void (*runtime_get)(struct mhi_device_ctxt *mhi_dev_ctxt);
+ void (*runtime_put)(struct mhi_device_ctxt *mhi_dev_ctxt);
+ void (*assert_wake)(struct mhi_device_ctxt *mhi_dev_ctxt,
+ bool force_set);
+ void (*deassert_wake)(struct mhi_device_ctxt *mhi_dev_ctxt);
-struct mhi_pcie_dev_info {
- struct pcie_core_info core;
- struct mhi_device_ctxt mhi_ctxt;
- struct msm_pcie_register_event mhi_pci_link_event;
- struct pci_dev *pcie_device;
- struct pci_driver *mhi_pcie_driver;
- struct bhi_ctxt_t bhi_ctxt;
- struct platform_device *plat_dev;
- u32 link_down_cntr;
- u32 link_up_cntr;
+ struct completion cmd_complete;
};
-struct mhi_pcie_devices {
- struct mhi_pcie_dev_info device_list[MHI_MAX_SUPPORTED_DEVICES];
- s32 nr_of_devices;
+struct mhi_device_driver {
+ struct mutex lock;
+ struct list_head head;
+ struct class *mhi_bhi_class;
+ struct dentry *parent;
};
struct mhi_event_ring_cfg {
u32 nr_desc;
u32 msi_vec;
u32 intmod;
+ enum MHI_CLIENT_CHANNEL chan;
u32 flags;
+ /*
+ * Priority of event handling:
+ * 0 = highest, handle events in isr (reserved for future)
+ * 1 = handles event using tasklet
+ * 2 = handles events using workerthread
+ */
+ u32 priority;
enum MHI_RING_CLASS class;
enum MHI_EVENT_RING_STATE state;
irqreturn_t (*mhi_handler_ptr)(int , void *);
};
+#define MHI_EV_PRIORITY_TASKLET (1)
struct mhi_data_buf {
dma_addr_t bounce_buffer;
@@ -570,18 +623,20 @@ struct mhi_data_buf {
u32 bounce_flag;
};
+extern struct mhi_device_driver *mhi_device_drv;
+
irqreturn_t mhi_msi_ipa_handlr(int irq_number, void *dev_id);
int mhi_reset_all_thread_queues(
struct mhi_device_ctxt *mhi_dev_ctxt);
int mhi_add_elements_to_event_rings(
struct mhi_device_ctxt *mhi_dev_ctxt,
enum STATE_TRANSITION new_state);
-int get_nr_avail_ring_elements(struct mhi_ring *ring);
+int get_nr_avail_ring_elements(struct mhi_device_ctxt *mhi_dev_ctxt,
+ struct mhi_ring *ring);
int get_nr_enclosed_el(struct mhi_ring *ring, void *loc_1,
void *loc_2, u32 *nr_el);
int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt);
-int mhi_init_device_ctxt(struct mhi_pcie_dev_info *dev_info,
- struct mhi_device_ctxt *mhi_dev_ctxt);
+int mhi_init_device_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt);
int mhi_init_local_event_ring(struct mhi_device_ctxt *mhi_dev_ctxt,
u32 nr_ev_el, u32 event_ring_index);
int mhi_send_cmd(struct mhi_device_ctxt *dest_device,
@@ -618,25 +673,24 @@ enum MHI_EVENT_CCS get_cmd_pkt(struct mhi_device_ctxt *mhi_dev_ctxt,
union mhi_cmd_pkt **cmd_pkt, u32 event_index);
int parse_cmd_event(struct mhi_device_ctxt *ctxt,
union mhi_event_pkt *event, u32 event_index);
-int parse_event_thread(void *ctxt);
int mhi_test_for_device_ready(
struct mhi_device_ctxt *mhi_dev_ctxt);
int mhi_test_for_device_reset(
struct mhi_device_ctxt *mhi_dev_ctxt);
int validate_ring_el_addr(struct mhi_ring *ring, uintptr_t addr);
int validate_ev_el_addr(struct mhi_ring *ring, uintptr_t addr);
-int mhi_state_change_thread(void *ctxt);
+void mhi_state_change_worker(struct work_struct *work);
int mhi_init_state_transition(struct mhi_device_ctxt *mhi_dev_ctxt,
enum STATE_TRANSITION new_state);
int mhi_wait_for_mdm(struct mhi_device_ctxt *mhi_dev_ctxt);
enum hrtimer_restart mhi_initiate_m1(struct hrtimer *timer);
int mhi_pci_suspend(struct device *dev);
int mhi_pci_resume(struct device *dev);
-int mhi_init_pcie_device(struct mhi_pcie_dev_info *mhi_pcie_dev);
+int mhi_init_pcie_device(struct mhi_device_ctxt *mhi_dev_ctxt);
int mhi_init_pm_sysfs(struct device *dev);
void mhi_rem_pm_sysfs(struct device *dev);
void mhi_pci_remove(struct pci_dev *mhi_device);
-int mhi_ctxt_init(struct mhi_pcie_dev_info *mhi_pcie_dev);
+int mhi_ctxt_init(struct mhi_device_ctxt *mhi_dev_ctxt);
int mhi_get_chan_max_buffers(u32 chan);
int mhi_esoc_register(struct mhi_device_ctxt *mhi_dev_ctxt);
void mhi_link_state_cb(struct msm_pcie_notify *notify);
@@ -644,6 +698,10 @@ void mhi_notify_clients(struct mhi_device_ctxt *mhi_dev_ctxt,
enum MHI_CB_REASON reason);
void mhi_notify_client(struct mhi_client_handle *client_handle,
enum MHI_CB_REASON reason);
+void mhi_master_mode_runtime_get(struct mhi_device_ctxt *mhi_dev_ctxt);
+void mhi_master_mode_runtime_put(struct mhi_device_ctxt *mhi_dev_ctxt);
+void mhi_slave_mode_runtime_get(struct mhi_device_ctxt *mhi_dev_ctxt);
+void mhi_slave_mode_runtime_put(struct mhi_device_ctxt *mhi_dev_ctxt);
void mhi_deassert_device_wake(struct mhi_device_ctxt *mhi_dev_ctxt);
void mhi_assert_device_wake(struct mhi_device_ctxt *mhi_dev_ctxt,
bool force_set);
@@ -691,10 +749,13 @@ void init_event_ctxt_array(struct mhi_device_ctxt *mhi_dev_ctxt);
int create_local_ev_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt);
enum MHI_STATE mhi_get_m_state(struct mhi_device_ctxt *mhi_dev_ctxt);
void process_m1_transition(struct work_struct *work);
-int set_mhi_base_state(struct mhi_pcie_dev_info *mhi_pcie_dev);
+int set_mhi_base_state(struct mhi_device_ctxt *mhi_dev_ctxt);
void mhi_set_m_state(struct mhi_device_ctxt *mhi_dev_ctxt,
enum MHI_STATE new_state);
const char *state_transition_str(enum STATE_TRANSITION state);
-void mhi_ctrl_ev_task(unsigned long data);
+void mhi_ev_task(unsigned long data);
+void process_event_ring(struct work_struct *work);
+int process_m0_transition(struct mhi_device_ctxt *mhi_dev_ctxt);
+int process_m3_transition(struct mhi_device_ctxt *mhi_dev_ctxt);
#endif
diff --git a/drivers/platform/msm/mhi/mhi_bhi.c b/drivers/platform/msm/mhi/mhi_bhi.c
index 113791a62c38..0cc8967757ec 100644
--- a/drivers/platform/msm/mhi/mhi_bhi.c
+++ b/drivers/platform/msm/mhi/mhi_bhi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*/
+#include <linux/firmware.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
@@ -23,89 +24,207 @@
static int bhi_open(struct inode *mhi_inode, struct file *file_handle)
{
- file_handle->private_data = &mhi_devices.device_list[0];
+ struct mhi_device_ctxt *mhi_dev_ctxt;
+
+ mhi_dev_ctxt = container_of(mhi_inode->i_cdev,
+ struct mhi_device_ctxt,
+ bhi_ctxt.cdev);
+ file_handle->private_data = mhi_dev_ctxt;
return 0;
}
-static ssize_t bhi_write(struct file *file,
- const char __user *buf,
- size_t count, loff_t *offp)
+static int bhi_alloc_bhie_xfer(struct mhi_device_ctxt *mhi_dev_ctxt,
+ size_t size,
+ struct bhie_vec_table *vec_table)
{
- int ret_val = 0;
- u32 pcie_word_val = 0;
- u32 i = 0;
- struct bhi_ctxt_t *bhi_ctxt =
- &(((struct mhi_pcie_dev_info *)file->private_data)->bhi_ctxt);
- struct mhi_device_ctxt *mhi_dev_ctxt =
- &((struct mhi_pcie_dev_info *)file->private_data)->mhi_ctxt;
- size_t amount_copied = 0;
- uintptr_t align_len = 0x1000;
- u32 tx_db_val = 0;
- rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock;
- const long bhi_timeout_ms = 1000;
- long timeout;
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ struct device *dev = &mhi_dev_ctxt->plat_dev->dev;
+ const phys_addr_t align = bhi_ctxt->alignment - 1;
+ size_t seg_size = bhi_ctxt->firmware_info.segment_size;
+ /* We need one additional entry for Vector Table */
+ int segments = DIV_ROUND_UP(size, seg_size) + 1;
+ int i;
+ struct scatterlist *sg_list;
+ struct bhie_mem_info *bhie_mem_info, *info;
- if (buf == NULL || 0 == count)
- return -EIO;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Total size:%lu total_seg:%d seg_size:%lu\n",
+ size, segments, seg_size);
- if (count > BHI_MAX_IMAGE_SIZE)
+ sg_list = kcalloc(segments, sizeof(*sg_list), GFP_KERNEL);
+ if (!sg_list)
return -ENOMEM;
- timeout = wait_event_interruptible_timeout(
- *mhi_dev_ctxt->mhi_ev_wq.bhi_event,
- mhi_dev_ctxt->mhi_state == MHI_STATE_BHI,
- msecs_to_jiffies(bhi_timeout_ms));
- if (timeout <= 0 && mhi_dev_ctxt->mhi_state != MHI_STATE_BHI)
- return -EIO;
+ bhie_mem_info = kcalloc(segments, sizeof(*bhie_mem_info), GFP_KERNEL);
+ if (!bhie_mem_info)
+ goto alloc_bhi_mem_info_error;
- mhi_log(MHI_MSG_INFO, "Entered. User Image size 0x%zx\n", count);
+ /* Allocate buffers for bhi/e vector table */
+ for (i = 0; i < segments; i++) {
+ size_t size = seg_size;
- bhi_ctxt->unaligned_image_loc = kmalloc(count + (align_len - 1),
- GFP_KERNEL);
+ /* Last entry if for vector table */
+ if (i == segments - 1)
+ size = sizeof(struct bhi_vec_entry) * i;
+ info = &bhie_mem_info[i];
+ info->size = size;
+ info->alloc_size = info->size + align;
+ info->pre_aligned =
+ dma_alloc_coherent(dev, info->alloc_size,
+ &info->dma_handle, GFP_KERNEL);
+ if (!info->pre_aligned)
+ goto alloc_dma_error;
+
+ info->phys_addr = (info->dma_handle + align) & ~align;
+ info->aligned = info->pre_aligned +
+ (info->phys_addr - info->dma_handle);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Seg:%d unaligned Img: 0x%llx aligned:0x%llx\n",
+ i, info->dma_handle, info->phys_addr);
+ }
+
+ sg_init_table(sg_list, segments);
+ sg_set_buf(sg_list, info->aligned, info->size);
+ sg_dma_address(sg_list) = info->phys_addr;
+ sg_dma_len(sg_list) = info->size;
+ vec_table->sg_list = sg_list;
+ vec_table->bhie_mem_info = bhie_mem_info;
+ vec_table->bhi_vec_entry = info->aligned;
+ vec_table->segment_count = segments;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "BHI/E table successfully allocated\n");
+ return 0;
+
+alloc_dma_error:
+ for (i = i - 1; i >= 0; i--)
+ dma_free_coherent(dev,
+ bhie_mem_info[i].alloc_size,
+ bhie_mem_info[i].pre_aligned,
+ bhie_mem_info[i].dma_handle);
+ kfree(bhie_mem_info);
+alloc_bhi_mem_info_error:
+ kfree(sg_list);
+ return -ENOMEM;
+}
+
+static int bhi_alloc_pbl_xfer(struct mhi_device_ctxt *mhi_dev_ctxt,
+ size_t size)
+{
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ const phys_addr_t align_len = bhi_ctxt->alignment;
+ size_t alloc_size = size + (align_len - 1);
+ struct device *dev = &mhi_dev_ctxt->plat_dev->dev;
+
+ bhi_ctxt->unaligned_image_loc =
+ dma_alloc_coherent(dev, alloc_size, &bhi_ctxt->dma_handle,
+ GFP_KERNEL);
if (bhi_ctxt->unaligned_image_loc == NULL)
return -ENOMEM;
- mhi_log(MHI_MSG_INFO, "Unaligned Img Loc: %p\n",
- bhi_ctxt->unaligned_image_loc);
- bhi_ctxt->image_loc =
- (void *)((uintptr_t)bhi_ctxt->unaligned_image_loc +
- (align_len - (((uintptr_t)bhi_ctxt->unaligned_image_loc) %
- align_len)));
+ bhi_ctxt->alloc_size = alloc_size;
+ bhi_ctxt->phy_image_loc = (bhi_ctxt->dma_handle + (align_len - 1)) &
+ ~(align_len - 1);
+ bhi_ctxt->image_loc = bhi_ctxt->unaligned_image_loc +
+ (bhi_ctxt->phy_image_loc - bhi_ctxt->dma_handle);
+ bhi_ctxt->image_size = size;
- mhi_log(MHI_MSG_INFO, "Aligned Img Loc: %p\n", bhi_ctxt->image_loc);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "alloc_size:%lu image_size:%lu unal_addr:0x%llx0x al_addr:0x%llx\n",
+ bhi_ctxt->alloc_size, bhi_ctxt->image_size,
+ bhi_ctxt->dma_handle, bhi_ctxt->phy_image_loc);
- bhi_ctxt->image_size = count;
+ return 0;
+}
- if (0 != copy_from_user(bhi_ctxt->image_loc, buf, count)) {
- ret_val = -ENOMEM;
- goto bhi_copy_error;
+/* Load firmware via bhie protocol */
+static int bhi_load_bhie_firmware(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ struct bhie_vec_table *fw_table = &bhi_ctxt->fw_table;
+ const struct bhie_mem_info *bhie_mem_info =
+ &fw_table->bhie_mem_info[fw_table->segment_count - 1];
+ u32 val;
+ const u32 tx_sequence = fw_table->sequence++;
+ unsigned long timeout;
+ rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock;
+
+ /* Program TX/RX Vector table */
+ read_lock_bh(pm_xfer_lock);
+ if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
+ read_unlock_bh(pm_xfer_lock);
+ return -EIO;
}
- amount_copied = count;
- /* Flush the writes, in anticipation for a device read */
- wmb();
- mhi_log(MHI_MSG_INFO,
- "Copied image from user at addr: %p\n", bhi_ctxt->image_loc);
- bhi_ctxt->phy_image_loc = dma_map_single(
- &mhi_dev_ctxt->dev_info->plat_dev->dev,
- bhi_ctxt->image_loc,
- bhi_ctxt->image_size,
- DMA_TO_DEVICE);
-
- if (dma_mapping_error(NULL, bhi_ctxt->phy_image_loc)) {
- ret_val = -EIO;
- goto bhi_copy_error;
+
+ val = HIGH_WORD(bhie_mem_info->phys_addr);
+ mhi_reg_write(mhi_dev_ctxt,
+ bhi_ctxt->bhi_base,
+ BHIE_TXVECADDR_HIGH_OFFS,
+ val);
+ val = LOW_WORD(bhie_mem_info->phys_addr);
+ mhi_reg_write(mhi_dev_ctxt,
+ bhi_ctxt->bhi_base,
+ BHIE_TXVECADDR_LOW_OFFS,
+ val);
+ val = (u32)bhie_mem_info->size;
+ mhi_reg_write(mhi_dev_ctxt,
+ bhi_ctxt->bhi_base,
+ BHIE_TXVECSIZE_OFFS,
+ val);
+
+ /* Ring DB to begin Xfer */
+ mhi_reg_write_field(mhi_dev_ctxt,
+ bhi_ctxt->bhi_base,
+ BHIE_TXVECDB_OFFS,
+ BHIE_TXVECDB_SEQNUM_BMSK,
+ BHIE_TXVECDB_SEQNUM_SHFT,
+ tx_sequence);
+ read_unlock_bh(pm_xfer_lock);
+
+ timeout = jiffies + msecs_to_jiffies(bhi_ctxt->poll_timeout);
+ while (time_before(jiffies, timeout)) {
+ u32 current_seq, status;
+
+ read_lock_bh(pm_xfer_lock);
+ if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
+ read_unlock_bh(pm_xfer_lock);
+ return -EIO;
+ }
+ val = mhi_reg_read(bhi_ctxt->bhi_base, BHIE_TXVECSTATUS_OFFS);
+ read_unlock_bh(pm_xfer_lock);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "TXVEC_STATUS:0x%x\n", val);
+ current_seq = (val & BHIE_TXVECSTATUS_SEQNUM_BMSK) >>
+ BHIE_TXVECSTATUS_SEQNUM_SHFT;
+ status = (val & BHIE_TXVECSTATUS_STATUS_BMSK) >>
+ BHIE_TXVECSTATUS_STATUS_SHFT;
+ if ((status == BHIE_TXVECSTATUS_STATUS_XFER_COMPL) &&
+ (current_seq == tx_sequence)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Image transfer complete\n");
+ return 0;
+ }
+ msleep(BHI_POLL_SLEEP_TIME_MS);
}
- mhi_log(MHI_MSG_INFO,
- "Mapped image to DMA addr 0x%lx:\n",
- (uintptr_t)bhi_ctxt->phy_image_loc);
- bhi_ctxt->image_size = count;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error xfering image via BHIE\n");
+ return -EIO;
+}
+
+static int bhi_load_firmware(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ u32 pcie_word_val = 0;
+ u32 tx_db_val = 0;
+ unsigned long timeout;
+ rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock;
/* Write the image size */
read_lock_bh(pm_xfer_lock);
if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
read_unlock_bh(pm_xfer_lock);
- goto bhi_copy_error;
+ return -EIO;
}
pcie_word_val = HIGH_WORD(bhi_ctxt->phy_image_loc);
mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base,
@@ -129,16 +248,15 @@ static ssize_t bhi_write(struct file *file,
pcie_word_val = mhi_reg_read(bhi_ctxt->bhi_base, BHI_IMGTXDB);
mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base,
BHI_IMGTXDB, 0xFFFFFFFF, 0, ++pcie_word_val);
-
- mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHI_INTVEC, 0);
read_unlock_bh(pm_xfer_lock);
- for (i = 0; i < BHI_POLL_NR_RETRIES; ++i) {
+ timeout = jiffies + msecs_to_jiffies(bhi_ctxt->poll_timeout);
+ while (time_before(jiffies, timeout)) {
u32 err = 0, errdbg1 = 0, errdbg2 = 0, errdbg3 = 0;
read_lock_bh(pm_xfer_lock);
if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
read_unlock_bh(pm_xfer_lock);
- goto bhi_copy_error;
+ return -EIO;
}
err = mhi_reg_read(bhi_ctxt->bhi_base, BHI_ERRCODE);
errdbg1 = mhi_reg_read(bhi_ctxt->bhi_base, BHI_ERRDBG1);
@@ -149,34 +267,83 @@ static ssize_t bhi_write(struct file *file,
BHI_STATUS_MASK,
BHI_STATUS_SHIFT);
read_unlock_bh(pm_xfer_lock);
- mhi_log(MHI_MSG_CRITICAL,
- "BHI STATUS 0x%x, err:0x%x errdbg1:0x%x errdbg2:0x%x errdbg3:0x%x\n",
- tx_db_val, err, errdbg1, errdbg2, errdbg3);
- if (BHI_STATUS_SUCCESS != tx_db_val)
- mhi_log(MHI_MSG_CRITICAL,
- "Incorrect BHI status: %d retry: %d\n",
- tx_db_val, i);
- else
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "%s 0x%x %s:0x%x %s:0x%x %s:0x%x %s:0x%x\n",
+ "BHI STATUS", tx_db_val,
+ "err", err,
+ "errdbg1", errdbg1,
+ "errdbg2", errdbg2,
+ "errdbg3", errdbg3);
+ if (tx_db_val == BHI_STATUS_SUCCESS)
break;
- usleep_range(20000, 25000);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "retrying...\n");
+ msleep(BHI_POLL_SLEEP_TIME_MS);
+ }
+
+ return (tx_db_val == BHI_STATUS_SUCCESS) ? 0 : -EIO;
+}
+
+static ssize_t bhi_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *offp)
+{
+ int ret_val = 0;
+ struct mhi_device_ctxt *mhi_dev_ctxt = file->private_data;
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ long timeout;
+
+ if (buf == NULL || 0 == count)
+ return -EIO;
+
+ if (count > BHI_MAX_IMAGE_SIZE)
+ return -ENOMEM;
+
+ ret_val = bhi_alloc_pbl_xfer(mhi_dev_ctxt, count);
+ if (ret_val)
+ return -ENOMEM;
+
+ if (copy_from_user(bhi_ctxt->image_loc, buf, count)) {
+ ret_val = -ENOMEM;
+ goto bhi_copy_error;
}
- dma_unmap_single(&mhi_dev_ctxt->dev_info->plat_dev->dev,
- bhi_ctxt->phy_image_loc,
- bhi_ctxt->image_size, DMA_TO_DEVICE);
- kfree(bhi_ctxt->unaligned_image_loc);
+ timeout = wait_event_interruptible_timeout(
+ *mhi_dev_ctxt->mhi_ev_wq.bhi_event,
+ mhi_dev_ctxt->mhi_state == MHI_STATE_BHI,
+ msecs_to_jiffies(bhi_ctxt->poll_timeout));
+ if (timeout <= 0 && mhi_dev_ctxt->mhi_state != MHI_STATE_BHI) {
+ ret_val = -EIO;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Timed out waiting for BHI\n");
+ goto bhi_copy_error;
+ }
+ ret_val = bhi_load_firmware(mhi_dev_ctxt);
+ if (ret_val) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to load bhi image\n");
+ }
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
+ bhi_ctxt->alloc_size,
+ bhi_ctxt->unaligned_image_loc,
+ bhi_ctxt->dma_handle);
+
+ /* Regardless of failure set to RESET state */
ret_val = mhi_init_state_transition(mhi_dev_ctxt,
STATE_TRANSITION_RESET);
if (ret_val) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to start state change event\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to start state change event\n");
}
- return amount_copied;
+ return count;
bhi_copy_error:
- kfree(bhi_ctxt->unaligned_image_loc);
- return amount_copied;
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
+ bhi_ctxt->alloc_size,
+ bhi_ctxt->unaligned_image_loc,
+ bhi_ctxt->dma_handle);
+
+ return ret_val;
}
static const struct file_operations bhi_fops = {
@@ -184,48 +351,159 @@ static const struct file_operations bhi_fops = {
.open = bhi_open,
};
-int bhi_probe(struct mhi_pcie_dev_info *mhi_pcie_device)
+int bhi_expose_dev_bhi(struct mhi_device_ctxt *mhi_dev_ctxt)
{
- struct bhi_ctxt_t *bhi_ctxt = &mhi_pcie_device->bhi_ctxt;
- int ret_val = 0;
- int r;
+ int ret_val;
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ const struct pcie_core_info *core = &mhi_dev_ctxt->core;
+ char node_name[32];
- if (NULL == mhi_pcie_device || 0 == mhi_pcie_device->core.bar0_base
- || 0 == mhi_pcie_device->core.bar0_end)
- return -EIO;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Creating dev node\n");
ret_val = alloc_chrdev_region(&bhi_ctxt->bhi_dev, 0, 1, "bhi");
if (IS_ERR_VALUE(ret_val)) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to alloc char device %d\n",
- ret_val);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to alloc char device %d\n", ret_val);
return -EIO;
}
- bhi_ctxt->bhi_class = class_create(THIS_MODULE, "bhi");
- if (IS_ERR(bhi_ctxt->bhi_class)) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to instantiate class %d\n",
- ret_val);
- r = PTR_RET(bhi_ctxt->bhi_class);
- goto err_class_create;
- }
cdev_init(&bhi_ctxt->cdev, &bhi_fops);
bhi_ctxt->cdev.owner = THIS_MODULE;
ret_val = cdev_add(&bhi_ctxt->cdev, bhi_ctxt->bhi_dev, 1);
- bhi_ctxt->dev = device_create(bhi_ctxt->bhi_class, NULL,
- bhi_ctxt->bhi_dev, NULL,
- "bhi");
+ snprintf(node_name, sizeof(node_name),
+ "bhi_%04X_%02u.%02u.%02u",
+ core->dev_id, core->domain, core->bus, core->slot);
+ bhi_ctxt->dev = device_create(mhi_device_drv->mhi_bhi_class,
+ NULL,
+ bhi_ctxt->bhi_dev,
+ NULL,
+ node_name);
if (IS_ERR(bhi_ctxt->dev)) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to add bhi cdev\n");
- r = PTR_RET(bhi_ctxt->dev);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to add bhi cdev\n");
+ ret_val = PTR_RET(bhi_ctxt->dev);
goto err_dev_create;
}
return 0;
+
err_dev_create:
cdev_del(&bhi_ctxt->cdev);
- class_destroy(bhi_ctxt->bhi_class);
-err_class_create:
unregister_chrdev_region(MAJOR(bhi_ctxt->bhi_dev), 1);
- return r;
+ return ret_val;
+}
+
+void bhi_firmware_download(struct work_struct *work)
+{
+ struct mhi_device_ctxt *mhi_dev_ctxt;
+ struct bhi_ctxt_t *bhi_ctxt;
+ int ret;
+ long timeout;
+
+ mhi_dev_ctxt = container_of(work, struct mhi_device_ctxt,
+ bhi_ctxt.fw_load_work);
+ bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enter\n");
+
+ wait_event_interruptible(*mhi_dev_ctxt->mhi_ev_wq.bhi_event,
+ mhi_dev_ctxt->mhi_state == MHI_STATE_BHI);
+
+ ret = bhi_load_firmware(mhi_dev_ctxt);
+ if (ret) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to Load sbl firmware\n");
+ return;
+ }
+ mhi_init_state_transition(mhi_dev_ctxt,
+ STATE_TRANSITION_RESET);
+
+ timeout = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.bhi_event,
+ mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_BHIE,
+ msecs_to_jiffies(bhi_ctxt->poll_timeout));
+ if (!timeout) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to Enter EXEC_ENV_BHIE\n");
+ return;
+ }
+
+ ret = bhi_load_bhie_firmware(mhi_dev_ctxt);
+ if (ret) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to Load amss firmware\n");
+ }
+}
+
+int bhi_probe(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ struct firmware_info *fw_info = &bhi_ctxt->firmware_info;
+ struct bhie_vec_table *fw_table = &bhi_ctxt->fw_table;
+ const struct firmware *firmware;
+ struct scatterlist *itr;
+ int ret, i;
+ size_t remainder;
+ const u8 *image;
+
+ /* expose dev node to userspace */
+ if (bhi_ctxt->manage_boot == false)
+ return bhi_expose_dev_bhi(mhi_dev_ctxt);
+
+ /* Make sure minimum buffer we allocate for BHI/E is >= sbl image */
+ while (fw_info->segment_size < fw_info->max_sbl_len)
+ fw_info->segment_size <<= 1;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "max sbl image size:%lu segment size:%lu\n",
+ fw_info->max_sbl_len, fw_info->segment_size);
+
+ /* Read the fw image */
+ ret = request_firmware(&firmware, fw_info->fw_image,
+ &mhi_dev_ctxt->plat_dev->dev);
+ if (ret) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error request firmware for:%s ret:%d\n",
+ fw_info->fw_image, ret);
+ return ret;
+ }
+
+ ret = bhi_alloc_bhie_xfer(mhi_dev_ctxt,
+ firmware->size,
+ fw_table);
+ if (ret) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error Allocating memory for firmware image\n");
+ release_firmware(firmware);
+ return ret;
+ }
+
+ /* Copy the fw image to vector table */
+ remainder = firmware->size;
+ image = firmware->data;
+ for (i = 0, itr = &fw_table->sg_list[1];
+ i < fw_table->segment_count - 1; i++, itr++) {
+ size_t to_copy = min(remainder, fw_info->segment_size);
+
+ memcpy(fw_table->bhie_mem_info[i].aligned, image, to_copy);
+ fw_table->bhi_vec_entry[i].phys_addr =
+ fw_table->bhie_mem_info[i].phys_addr;
+ fw_table->bhi_vec_entry[i].size = to_copy;
+ sg_set_buf(itr, fw_table->bhie_mem_info[i].aligned, to_copy);
+ sg_dma_address(itr) = fw_table->bhie_mem_info[i].phys_addr;
+ sg_dma_len(itr) = to_copy;
+ remainder -= to_copy;
+ image += to_copy;
+ }
+
+ /*
+ * Re-use BHI/E pointer for BHI since we guranteed BHI/E segment
+ * is >= to SBL image.
+ */
+ bhi_ctxt->phy_image_loc = sg_dma_address(&fw_table->sg_list[1]);
+ bhi_ctxt->image_size = fw_info->max_sbl_len;
+
+ fw_table->sequence++;
+ release_firmware(firmware);
+
+ /* Schedule a worker thread and wait for BHI Event */
+ schedule_work(&bhi_ctxt->fw_load_work);
+ return 0;
}
diff --git a/drivers/platform/msm/mhi/mhi_bhi.h b/drivers/platform/msm/mhi/mhi_bhi.h
index ca44f69cea42..15137ba5dfdf 100644
--- a/drivers/platform/msm/mhi/mhi_bhi.h
+++ b/drivers/platform/msm/mhi/mhi_bhi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014, 2016 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -42,6 +42,38 @@
#define BHI_STATUS_SUCCESS (2)
#define BHI_STATUS_RESET (0)
+/* BHIE Offsets */
+#define BHIE_OFFSET (0x0124) /* BHIE register space offset from BHI base */
+#define BHIE_MSMSOCID_OFFS (BHIE_OFFSET + 0x0000)
+#define BHIE_TXVECADDR_LOW_OFFS (BHIE_OFFSET + 0x002C)
+#define BHIE_TXVECADDR_HIGH_OFFS (BHIE_OFFSET + 0x0030)
+#define BHIE_TXVECSIZE_OFFS (BHIE_OFFSET + 0x0034)
+#define BHIE_TXVECDB_OFFS (BHIE_OFFSET + 0x003C)
+#define BHIE_TXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
+#define BHIE_TXVECDB_SEQNUM_SHFT (0)
+#define BHIE_TXVECSTATUS_OFFS (BHIE_OFFSET + 0x0044)
+#define BHIE_TXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
+#define BHIE_TXVECSTATUS_SEQNUM_SHFT (0)
+#define BHIE_TXVECSTATUS_STATUS_BMSK (0xC0000000)
+#define BHIE_TXVECSTATUS_STATUS_SHFT (30)
+#define BHIE_TXVECSTATUS_STATUS_RESET (0x00)
+#define BHIE_TXVECSTATUS_STATUS_XFER_COMPL (0x02)
+#define BHIE_TXVECSTATUS_STATUS_ERROR (0x03)
+#define BHIE_RXVECADDR_LOW_OFFS (BHIE_OFFSET + 0x0060)
+#define BHIE_RXVECADDR_HIGH_OFFS (BHIE_OFFSET + 0x0064)
+#define BHIE_RXVECSIZE_OFFS (BHIE_OFFSET + 0x0068)
+#define BHIE_RXVECDB_OFFS (BHIE_OFFSET + 0x0070)
+#define BHIE_RXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
+#define BHIE_RXVECDB_SEQNUM_SHFT (0)
+#define BHIE_RXVECSTATUS_OFFS (BHIE_OFFSET + 0x0078)
+#define BHIE_RXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
+#define BHIE_RXVECSTATUS_SEQNUM_SHFT (0)
+#define BHIE_RXVECSTATUS_STATUS_BMSK (0xC0000000)
+#define BHIE_RXVECSTATUS_STATUS_SHFT (30)
+#define BHIE_RXVECSTATUS_STATUS_RESET (0x00)
+#define BHIE_RXVECSTATUS_STATUS_XFER_COMPL (0x02)
+#define BHIE_RXVECSTATUS_STATUS_ERROR (0x03)
+
#define BHI_MAJOR_VERSION 0x0
#define BHI_MINOR_VERSION 0x1
@@ -51,10 +83,12 @@
#define BHI_READBUF_SIZE sizeof(bhi_info_type)
#define BHI_MAX_IMAGE_SIZE (256 * 1024)
+#define BHI_DEFAULT_ALIGNMENT (0x1000)
-#define BHI_POLL_SLEEP_TIME 1000
-#define BHI_POLL_NR_RETRIES 10
+#define BHI_POLL_SLEEP_TIME_MS 100
+#define BHI_POLL_TIMEOUT_MS 2000
-int bhi_probe(struct mhi_pcie_dev_info *mhi_pcie_device);
+int bhi_probe(struct mhi_device_ctxt *mhi_dev_ctxt);
+void bhi_firmware_download(struct work_struct *work);
#endif
diff --git a/drivers/platform/msm/mhi/mhi_event.c b/drivers/platform/msm/mhi/mhi_event.c
index fe163f3895a5..ae677bae63dc 100644
--- a/drivers/platform/msm/mhi/mhi_event.c
+++ b/drivers/platform/msm/mhi/mhi_event.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -26,41 +26,57 @@ int mhi_populate_event_cfg(struct mhi_device_ctxt *mhi_dev_ctxt)
int r, i;
char dt_prop[MAX_BUF_SIZE];
const struct device_node *np =
- mhi_dev_ctxt->dev_info->plat_dev->dev.of_node;
+ mhi_dev_ctxt->plat_dev->dev.of_node;
r = of_property_read_u32(np, "mhi-event-rings",
&mhi_dev_ctxt->mmio_info.nr_event_rings);
if (r) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to pull event ring info from DT, %d\n", r);
- goto dt_error;
+ return -EINVAL;
}
mhi_dev_ctxt->ev_ring_props =
kzalloc(sizeof(struct mhi_event_ring_cfg) *
mhi_dev_ctxt->mmio_info.nr_event_rings,
GFP_KERNEL);
- if (!mhi_dev_ctxt->ev_ring_props) {
- r = -ENOMEM;
- goto dt_error;
- }
+ if (!mhi_dev_ctxt->ev_ring_props)
+ return -ENOMEM;
for (i = 0; i < mhi_dev_ctxt->mmio_info.nr_event_rings; ++i) {
+ u32 dt_configs[6];
+ int no_elements;
+
scnprintf(dt_prop, MAX_BUF_SIZE, "%s%d", "mhi-event-cfg-", i);
- r = of_property_read_u32_array(np, dt_prop,
- (u32 *)&mhi_dev_ctxt->ev_ring_props[i],
- 4);
+ no_elements = of_property_count_elems_of_size(np, dt_prop,
+ sizeof(dt_configs));
+ if (no_elements != 1)
+ goto dt_error;
+ r = of_property_read_u32_array(
+ np,
+ dt_prop,
+ dt_configs,
+ sizeof(dt_configs) / sizeof(u32));
if (r) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to pull ev ring %d info from DT %d\n",
i, r);
goto dt_error;
}
- mhi_log(MHI_MSG_INFO,
- "Pulled ev ring %d,desc:0x%x,msi_vec:0x%x,intmod%d flags0x%x\n",
- i, mhi_dev_ctxt->ev_ring_props[i].nr_desc,
- mhi_dev_ctxt->ev_ring_props[i].msi_vec,
- mhi_dev_ctxt->ev_ring_props[i].intmod,
- mhi_dev_ctxt->ev_ring_props[i].flags);
+ mhi_dev_ctxt->ev_ring_props[i].nr_desc = dt_configs[0];
+ mhi_dev_ctxt->ev_ring_props[i].msi_vec = dt_configs[1];
+ mhi_dev_ctxt->ev_ring_props[i].intmod = dt_configs[2];
+ mhi_dev_ctxt->ev_ring_props[i].chan = dt_configs[3];
+ mhi_dev_ctxt->ev_ring_props[i].priority = dt_configs[4];
+ mhi_dev_ctxt->ev_ring_props[i].flags = dt_configs[5];
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "ev ring %d,desc:0x%x,msi:0x%x,intmod%d chan:%u priority:%u flags0x%x\n",
+ i,
+ mhi_dev_ctxt->ev_ring_props[i].nr_desc,
+ mhi_dev_ctxt->ev_ring_props[i].msi_vec,
+ mhi_dev_ctxt->ev_ring_props[i].intmod,
+ mhi_dev_ctxt->ev_ring_props[i].chan,
+ mhi_dev_ctxt->ev_ring_props[i].priority,
+ mhi_dev_ctxt->ev_ring_props[i].flags);
if (GET_EV_PROPS(EV_MANAGED,
mhi_dev_ctxt->ev_ring_props[i].flags))
mhi_dev_ctxt->ev_ring_props[i].mhi_handler_ptr =
@@ -76,14 +92,18 @@ int mhi_populate_event_cfg(struct mhi_device_ctxt *mhi_dev_ctxt)
mhi_dev_ctxt->ev_ring_props[i].class = MHI_SW_RING;
mhi_dev_ctxt->mmio_info.nr_sw_event_rings++;
}
- mhi_log(MHI_MSG_INFO,
- "Detected %d SW EV rings and %d HW EV rings out of %d EV rings\n",
- mhi_dev_ctxt->mmio_info.nr_sw_event_rings,
- mhi_dev_ctxt->mmio_info.nr_hw_event_rings,
- mhi_dev_ctxt->mmio_info.nr_event_rings);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Detected %d SW EV rings and %d HW EV rings out of %d EV rings\n",
+ mhi_dev_ctxt->mmio_info.nr_sw_event_rings,
+ mhi_dev_ctxt->mmio_info.nr_hw_event_rings,
+ mhi_dev_ctxt->mmio_info.nr_event_rings);
}
+
+ return 0;
dt_error:
- return r;
+ kfree(mhi_dev_ctxt->ev_ring_props);
+ mhi_dev_ctxt->ev_ring_props = NULL;
+ return -EINVAL;
}
int create_local_ev_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt)
@@ -110,6 +130,9 @@ int create_local_ev_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt)
mhi_local_event_ctxt[i];
spin_lock_init(&mhi_ring->ring_lock);
+ tasklet_init(&mhi_ring->ev_task, mhi_ev_task,
+ (unsigned long)mhi_ring);
+ INIT_WORK(&mhi_ring->ev_worker, process_event_ring);
}
return r;
@@ -139,6 +162,8 @@ void ring_ev_db(struct mhi_device_ctxt *mhi_dev_ctxt, u32 event_ring_index)
static int mhi_event_ring_init(struct mhi_event_ctxt *ev_list,
struct mhi_ring *ring,
+ struct mhi_device_ctxt *mhi_dev_ctxt,
+ int index,
u32 el_per_ring,
u32 intmodt_val,
u32 msi_vec,
@@ -148,6 +173,8 @@ static int mhi_event_ring_init(struct mhi_event_ctxt *ev_list,
ev_list->mhi_msi_vector = msi_vec;
ev_list->mhi_event_ring_len = el_per_ring*sizeof(union mhi_event_pkt);
MHI_SET_EV_CTXT(EVENT_CTXT_INTMODT, ev_list, intmodt_val);
+ ring->mhi_dev_ctxt = mhi_dev_ctxt;
+ ring->index = index;
ring->len = ((size_t)(el_per_ring)*sizeof(union mhi_event_pkt));
ring->el_size = sizeof(union mhi_event_pkt);
ring->overwrite_en = 0;
@@ -180,6 +207,7 @@ void init_event_ctxt_array(struct mhi_device_ctxt *mhi_dev_ctxt)
event_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[i];
mhi_local_event_ctxt = &mhi_dev_ctxt->mhi_local_event_ctxt[i];
mhi_event_ring_init(event_ctxt, mhi_local_event_ctxt,
+ mhi_dev_ctxt, i,
mhi_dev_ctxt->ev_ring_props[i].nr_desc,
mhi_dev_ctxt->ev_ring_props[i].intmod,
mhi_dev_ctxt->ev_ring_props[i].msi_vec,
@@ -195,7 +223,7 @@ int init_local_ev_ring_by_type(struct mhi_device_ctxt *mhi_dev_ctxt,
int ret_val = 0;
u32 i;
- mhi_log(MHI_MSG_INFO, "Entered\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n");
for (i = 0; i < mhi_dev_ctxt->mmio_info.nr_event_rings; i++) {
if (GET_EV_PROPS(EV_TYPE,
mhi_dev_ctxt->ev_ring_props[i].flags) == type &&
@@ -207,9 +235,10 @@ int init_local_ev_ring_by_type(struct mhi_device_ctxt *mhi_dev_ctxt,
return ret_val;
}
ring_ev_db(mhi_dev_ctxt, i);
- mhi_log(MHI_MSG_INFO, "Finished ev ring init %d\n", i);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Finished ev ring init %d\n", i);
}
- mhi_log(MHI_MSG_INFO, "Exited\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
return 0;
}
@@ -228,7 +257,7 @@ int mhi_add_elements_to_event_rings(struct mhi_device_ctxt *mhi_dev_ctxt,
MHI_ER_DATA_TYPE);
break;
default:
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Unrecognized event stage, %d\n", new_state);
ret_val = -EINVAL;
break;
@@ -247,23 +276,18 @@ int mhi_init_local_event_ring(struct mhi_device_ctxt *mhi_dev_ctxt,
&mhi_dev_ctxt->mhi_local_event_ctxt[ring_index];
spinlock_t *lock = &event_ctxt->ring_lock;
- if (NULL == mhi_dev_ctxt || 0 == nr_ev_el) {
- mhi_log(MHI_MSG_ERROR, "Bad Input data, quitting\n");
- return -EINVAL;
- }
-
spin_lock_irqsave(lock, flags);
- mhi_log(MHI_MSG_INFO, "mmio_addr = 0x%p, mmio_len = 0x%llx\n",
- mhi_dev_ctxt->mmio_info.mmio_addr,
- mhi_dev_ctxt->mmio_info.mmio_len);
- mhi_log(MHI_MSG_INFO, "Initializing event ring %d with %d desc\n",
- ring_index, nr_ev_el);
+ mhi_log(mhi_dev_ctxt,
+ MHI_MSG_INFO,
+ "Initializing event ring %d with %d desc\n",
+ ring_index,
+ nr_ev_el);
for (i = 0; i < nr_ev_el - 1; ++i) {
ret_val = ctxt_add_element(event_ctxt, (void *)&ev_pkt);
if (0 != ret_val) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to insert el in ev ctxt\n");
break;
}
@@ -279,7 +303,8 @@ void mhi_reset_ev_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
struct mhi_event_ctxt *ev_ctxt;
struct mhi_ring *local_ev_ctxt;
- mhi_log(MHI_MSG_VERBOSE, "Resetting event index %d\n", index);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Resetting event index %d\n", index);
ev_ctxt =
&mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[index];
local_ev_ctxt =
diff --git a/drivers/platform/msm/mhi/mhi_iface.c b/drivers/platform/msm/mhi/mhi_iface.c
index 395e19c91f35..f1c562974816 100644
--- a/drivers/platform/msm/mhi/mhi_iface.c
+++ b/drivers/platform/msm/mhi/mhi_iface.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -32,12 +32,11 @@
#include "mhi_hwio.h"
#include "mhi_bhi.h"
-struct mhi_pcie_devices mhi_devices;
+struct mhi_device_driver *mhi_device_drv;
static int mhi_pci_probe(struct pci_dev *pcie_device,
const struct pci_device_id *mhi_device_id);
static int __exit mhi_plat_remove(struct platform_device *pdev);
-void *mhi_ipc_log;
static DEFINE_PCI_DEVICE_TABLE(mhi_pcie_device_id) = {
{ MHI_PCIE_VENDOR_ID, MHI_PCIE_DEVICE_ID_9x35,
@@ -59,129 +58,71 @@ static const struct of_device_id mhi_plat_match[] = {
static void mhi_msm_fixup(struct pci_dev *pcie_device)
{
if (pcie_device->class == PCI_CLASS_NOT_DEFINED) {
- mhi_log(MHI_MSG_INFO, "Setting msm pcie class\n");
pcie_device->class = PCI_CLASS_STORAGE_SCSI;
}
}
-int mhi_ctxt_init(struct mhi_pcie_dev_info *mhi_pcie_dev)
+int mhi_ctxt_init(struct mhi_device_ctxt *mhi_dev_ctxt)
{
int ret_val = 0;
- u32 i = 0, j = 0;
- u32 requested_msi_number = 32, actual_msi_number = 0;
- struct mhi_device_ctxt *mhi_dev_ctxt = NULL;
- struct pci_dev *pcie_device = NULL;
+ u32 j = 0;
- if (NULL == mhi_pcie_dev)
- return -EINVAL;
- pcie_device = mhi_pcie_dev->pcie_device;
-
- ret_val = mhi_init_pcie_device(mhi_pcie_dev);
- if (ret_val) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to initialize pcie device, ret %d\n",
- ret_val);
- return -ENODEV;
- }
- ret_val = mhi_init_device_ctxt(mhi_pcie_dev, &mhi_pcie_dev->mhi_ctxt);
- if (ret_val) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to initialize main MHI ctxt ret %d\n",
- ret_val);
- goto msi_config_err;
- }
- ret_val = mhi_esoc_register(&mhi_pcie_dev->mhi_ctxt);
+ ret_val = mhi_init_device_ctxt(mhi_dev_ctxt);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to register with esoc ret %d.\n",
- ret_val);
- }
-
- device_disable_async_suspend(&pcie_device->dev);
- ret_val = pci_enable_msi_range(pcie_device, 1, requested_msi_number);
- if (IS_ERR_VALUE(ret_val)) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to enable MSIs for pcie dev ret_val %d.\n",
- ret_val);
- goto msi_config_err;
- } else if (ret_val) {
- mhi_log(MHI_MSG_INFO,
- "Hrmmm, got fewer MSIs than we requested. Requested %d, got %d.\n",
- requested_msi_number, ret_val);
- actual_msi_number = ret_val;
- } else {
- mhi_log(MHI_MSG_VERBOSE,
- "Got all requested MSIs, moving on\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to initialize main MHI ctxt ret %d\n", ret_val);
+ return ret_val;
}
- mhi_dev_ctxt = &mhi_pcie_dev->mhi_ctxt;
for (j = 0; j < mhi_dev_ctxt->mmio_info.nr_event_rings; j++) {
- mhi_log(MHI_MSG_VERBOSE,
- "MSI_number = %d, event ring number = %d\n",
- mhi_dev_ctxt->ev_ring_props[j].msi_vec, j);
-
- ret_val = request_irq(pcie_device->irq +
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "MSI_number = %d, event ring number = %d\n",
+ mhi_dev_ctxt->ev_ring_props[j].msi_vec, j);
+
+ /* outside of requested irq boundary */
+ if (mhi_dev_ctxt->core.max_nr_msis <=
+ mhi_dev_ctxt->ev_ring_props[j].msi_vec) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "max msi supported:%d request:%d ev:%d\n",
+ mhi_dev_ctxt->core.max_nr_msis,
+ mhi_dev_ctxt->ev_ring_props[j].msi_vec,
+ j);
+ goto irq_error;
+ }
+ ret_val = request_irq(mhi_dev_ctxt->core.irq_base +
mhi_dev_ctxt->ev_ring_props[j].msi_vec,
mhi_dev_ctxt->ev_ring_props[j].mhi_handler_ptr,
IRQF_NO_SUSPEND,
"mhi_drv",
- (void *)&pcie_device->dev);
+ (void *)mhi_dev_ctxt);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to register handler for MSI ret_val = %d\n",
- ret_val);
- goto msi_config_err;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to register handler for MSI ret_val = %d\n",
+ ret_val);
+ goto irq_error;
}
}
- mhi_pcie_dev->core.irq_base = pcie_device->irq;
- mhi_log(MHI_MSG_VERBOSE,
- "Setting IRQ Base to 0x%x\n", mhi_pcie_dev->core.irq_base);
- mhi_pcie_dev->core.max_nr_msis = requested_msi_number;
- ret_val = mhi_init_pm_sysfs(&pcie_device->dev);
- if (ret_val) {
- mhi_log(MHI_MSG_ERROR, "Failed to setup sysfs ret %d\n",
- ret_val);
- goto sysfs_config_err;
- }
- if (!mhi_init_debugfs(&mhi_pcie_dev->mhi_ctxt))
- mhi_log(MHI_MSG_ERROR, "Failed to init debugfs.\n");
-
- mhi_pcie_dev->mhi_ctxt.mmio_info.mmio_addr =
- mhi_pcie_dev->core.bar0_base;
- pcie_device->dev.platform_data = &mhi_pcie_dev->mhi_ctxt;
- mhi_pcie_dev->mhi_ctxt.dev_info->plat_dev->dev.platform_data =
- &mhi_pcie_dev->mhi_ctxt;
- ret_val = mhi_reg_notifiers(&mhi_pcie_dev->mhi_ctxt);
- if (ret_val) {
- mhi_log(MHI_MSG_ERROR, "Failed to register for notifiers\n");
- goto mhi_state_transition_error;
- }
- mhi_log(MHI_MSG_INFO,
- "Finished all driver probing returning ret_val %d.\n",
- ret_val);
- return ret_val;
-mhi_state_transition_error:
+ mhi_dev_ctxt->mmio_info.mmio_addr = mhi_dev_ctxt->core.bar0_base;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "exit\n");
+ return 0;
+
+irq_error:
kfree(mhi_dev_ctxt->state_change_work_item_list.q_lock);
- kfree(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
- kfree(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
kfree(mhi_dev_ctxt->mhi_ev_wq.m0_event);
kfree(mhi_dev_ctxt->mhi_ev_wq.m3_event);
kfree(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
- dma_free_coherent(&mhi_dev_ctxt->dev_info->plat_dev->dev,
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
mhi_dev_ctxt->dev_space.dev_mem_len,
mhi_dev_ctxt->dev_space.dev_mem_start,
mhi_dev_ctxt->dev_space.dma_dev_mem_start);
kfree(mhi_dev_ctxt->ev_ring_props);
- mhi_rem_pm_sysfs(&pcie_device->dev);
-sysfs_config_err:
- for (; i >= 0; --i)
- free_irq(pcie_device->irq + i, &pcie_device->dev);
- debugfs_remove_recursive(mhi_pcie_dev->mhi_ctxt.mhi_parent_folder);
-msi_config_err:
- pci_disable_device(pcie_device);
- return ret_val;
+ for (j = j - 1; j >= 0; --j)
+ free_irq(mhi_dev_ctxt->core.irq_base + j, NULL);
+
+ return -EINVAL;
}
static const struct dev_pm_ops pm_ops = {
@@ -204,73 +145,154 @@ static int mhi_pci_probe(struct pci_dev *pcie_device,
const struct pci_device_id *mhi_device_id)
{
int ret_val = 0;
- struct mhi_pcie_dev_info *mhi_pcie_dev = NULL;
struct platform_device *plat_dev;
- struct mhi_device_ctxt *mhi_dev_ctxt;
- u32 nr_dev = mhi_devices.nr_of_devices;
+ struct mhi_device_ctxt *mhi_dev_ctxt = NULL, *itr;
+ u32 domain = pci_domain_nr(pcie_device->bus);
+ u32 bus = pcie_device->bus->number;
+ u32 dev_id = pcie_device->device;
+ u32 slot = PCI_SLOT(pcie_device->devfn);
+ unsigned long msi_requested, msi_required;
+ struct msm_pcie_register_event *mhi_pci_link_event;
+
+ /* Find correct device context based on bdf & dev_id */
+ mutex_lock(&mhi_device_drv->lock);
+ list_for_each_entry(itr, &mhi_device_drv->head, node) {
+ struct pcie_core_info *core = &itr->core;
+
+ if (core->domain == domain &&
+ core->bus == bus &&
+ core->dev_id == dev_id &&
+ core->slot == slot) {
+ mhi_dev_ctxt = itr;
+ break;
+ }
+ }
+ mutex_unlock(&mhi_device_drv->lock);
+ if (!mhi_dev_ctxt)
+ return -EPROBE_DEFER;
- mhi_log(MHI_MSG_INFO, "Entering\n");
- mhi_pcie_dev = &mhi_devices.device_list[mhi_devices.nr_of_devices];
- if (mhi_devices.nr_of_devices + 1 > MHI_MAX_SUPPORTED_DEVICES) {
- mhi_log(MHI_MSG_ERROR, "Error: Too many devices\n");
- return -ENOMEM;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Processing Domain:%02u Bus:%04u dev:0x%04x slot:%04u\n",
+ domain, bus, dev_id, slot);
+
+ ret_val = of_property_read_u32(mhi_dev_ctxt->plat_dev->dev.of_node,
+ "mhi-event-rings",
+ (u32 *)&msi_required);
+ if (ret_val) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to pull ev ring info from DT, %d\n", ret_val);
+ return ret_val;
}
- mhi_devices.nr_of_devices++;
- plat_dev = mhi_devices.device_list[nr_dev].plat_dev;
+ plat_dev = mhi_dev_ctxt->plat_dev;
pcie_device->dev.of_node = plat_dev->dev.of_node;
- mhi_dev_ctxt = &mhi_devices.device_list[nr_dev].mhi_ctxt;
mhi_dev_ctxt->mhi_pm_state = MHI_PM_DISABLE;
INIT_WORK(&mhi_dev_ctxt->process_m1_worker, process_m1_transition);
+ INIT_WORK(&mhi_dev_ctxt->st_thread_worker, mhi_state_change_worker);
mutex_init(&mhi_dev_ctxt->pm_lock);
rwlock_init(&mhi_dev_ctxt->pm_xfer_lock);
spin_lock_init(&mhi_dev_ctxt->dev_wake_lock);
- tasklet_init(&mhi_dev_ctxt->ev_task,
- mhi_ctrl_ev_task,
- (unsigned long)mhi_dev_ctxt);
-
+ init_completion(&mhi_dev_ctxt->cmd_complete);
mhi_dev_ctxt->flags.link_up = 1;
- ret_val = mhi_set_bus_request(mhi_dev_ctxt, 1);
- mhi_pcie_dev->pcie_device = pcie_device;
- mhi_pcie_dev->mhi_pcie_driver = &mhi_pcie_driver;
- mhi_pcie_dev->mhi_pci_link_event.events =
- (MSM_PCIE_EVENT_LINKDOWN | MSM_PCIE_EVENT_WAKEUP);
- mhi_pcie_dev->mhi_pci_link_event.user = pcie_device;
- mhi_pcie_dev->mhi_pci_link_event.callback = mhi_link_state_cb;
- mhi_pcie_dev->mhi_pci_link_event.notify.data = mhi_pcie_dev;
- ret_val = msm_pcie_register_event(&mhi_pcie_dev->mhi_pci_link_event);
+
+ /* Setup bus scale */
+ mhi_dev_ctxt->bus_scale_table = msm_bus_cl_get_pdata(plat_dev);
+ if (!mhi_dev_ctxt->bus_scale_table)
+ return -ENODATA;
+ mhi_dev_ctxt->bus_client = msm_bus_scale_register_client
+ (mhi_dev_ctxt->bus_scale_table);
+ if (!mhi_dev_ctxt->bus_client)
+ return -EINVAL;
+ mhi_set_bus_request(mhi_dev_ctxt, 1);
+
+ mhi_dev_ctxt->pcie_device = pcie_device;
+
+ mhi_pci_link_event = &mhi_dev_ctxt->mhi_pci_link_event;
+ mhi_pci_link_event->events =
+ (MSM_PCIE_EVENT_LINKDOWN | MSM_PCIE_EVENT_WAKEUP);
+ mhi_pci_link_event->user = pcie_device;
+ mhi_pci_link_event->callback = mhi_link_state_cb;
+ mhi_pci_link_event->notify.data = mhi_dev_ctxt;
+ ret_val = msm_pcie_register_event(mhi_pci_link_event);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to register for link notifications %d.\n",
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to reg for link notifications %d\n", ret_val);
+ return ret_val;
+ }
+
+ dev_set_drvdata(&pcie_device->dev, mhi_dev_ctxt);
+
+ mhi_dev_ctxt->core.pci_master = true;
+ ret_val = mhi_init_pcie_device(mhi_dev_ctxt);
+ if (ret_val) {
+ mhi_log(mhi_dev_ctxt,
+ MHI_MSG_CRITICAL,
+ "Failed to initialize pcie device, ret %d\n",
ret_val);
return ret_val;
}
+ pci_set_master(pcie_device);
+ device_disable_async_suspend(&pcie_device->dev);
- /* Initialize MHI CNTXT */
- ret_val = mhi_ctxt_init(mhi_pcie_dev);
+ ret_val = mhi_esoc_register(mhi_dev_ctxt);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
- "MHI Initialization failed, ret %d\n",
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Failed to reg with esoc ret %d\n", ret_val);
+ }
+
+ /* # of MSI requested must be power of 2 */
+ msi_requested = 1 << find_last_bit(&msi_required, 32);
+ if (msi_requested < msi_required)
+ msi_requested <<= 1;
+
+ ret_val = pci_enable_msi_range(pcie_device, 1, msi_requested);
+ if (IS_ERR_VALUE(ret_val) || (ret_val < msi_requested)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to enable MSIs for pcie dev ret_val %d.\n",
ret_val);
+ return -EIO;
+ }
+
+ mhi_dev_ctxt->core.max_nr_msis = msi_requested;
+ mhi_dev_ctxt->core.irq_base = pcie_device->irq;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Setting IRQ Base to 0x%x\n", mhi_dev_ctxt->core.irq_base);
+
+ /* Initialize MHI CNTXT */
+ ret_val = mhi_ctxt_init(mhi_dev_ctxt);
+ if (ret_val) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "MHI Initialization failed, ret %d\n", ret_val);
goto deregister_pcie;
}
- pci_set_master(mhi_pcie_dev->pcie_device);
+
+ mhi_init_pm_sysfs(&pcie_device->dev);
+ mhi_init_debugfs(mhi_dev_ctxt);
+ mhi_reg_notifiers(mhi_dev_ctxt);
+
+ /* setup shadow pm functions */
+ mhi_dev_ctxt->assert_wake = mhi_assert_device_wake;
+ mhi_dev_ctxt->deassert_wake = mhi_deassert_device_wake;
+ mhi_dev_ctxt->runtime_get = mhi_master_mode_runtime_get;
+ mhi_dev_ctxt->runtime_put = mhi_master_mode_runtime_put;
mutex_lock(&mhi_dev_ctxt->pm_lock);
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
mhi_dev_ctxt->mhi_pm_state = MHI_PM_POR;
- ret_val = set_mhi_base_state(mhi_pcie_dev);
+ ret_val = set_mhi_base_state(mhi_dev_ctxt);
+
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt,
+ MHI_MSG_ERROR,
"Error Setting MHI Base State %d\n", ret_val);
goto unlock_pm_lock;
}
if (mhi_dev_ctxt->base_state == STATE_TRANSITION_BHI) {
- ret_val = bhi_probe(mhi_pcie_dev);
+ ret_val = bhi_probe(mhi_dev_ctxt);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Error with bhi_probe ret:%d", ret_val);
goto unlock_pm_lock;
}
@@ -312,33 +334,144 @@ static int mhi_pci_probe(struct pci_dev *pcie_device,
unlock_pm_lock:
mutex_unlock(&mhi_dev_ctxt->pm_lock);
deregister_pcie:
- msm_pcie_deregister_event(&mhi_pcie_dev->mhi_pci_link_event);
+ msm_pcie_deregister_event(&mhi_dev_ctxt->mhi_pci_link_event);
return ret_val;
}
static int mhi_plat_probe(struct platform_device *pdev)
{
- u32 nr_dev = mhi_devices.nr_of_devices;
+ int r = 0, len;
struct mhi_device_ctxt *mhi_dev_ctxt;
- int r = 0;
+ struct pcie_core_info *core;
+ char node[32];
+ struct device_node *of_node = pdev->dev.of_node;
+ u64 address_window[2];
- mhi_log(MHI_MSG_INFO, "Entered\n");
- mhi_dev_ctxt = &mhi_devices.device_list[nr_dev].mhi_ctxt;
+ if (of_node == NULL)
+ return -ENODEV;
- mhi_dev_ctxt->bus_scale_table = msm_bus_cl_get_pdata(pdev);
- if (!mhi_dev_ctxt->bus_scale_table)
- return -ENODATA;
- mhi_dev_ctxt->bus_client = msm_bus_scale_register_client
- (mhi_dev_ctxt->bus_scale_table);
- if (!mhi_dev_ctxt->bus_client)
- return -EINVAL;
+ pdev->id = of_alias_get_id(of_node, "mhi");
+ if (pdev->id < 0)
+ return -ENODEV;
- mhi_devices.device_list[nr_dev].plat_dev = pdev;
- r = dma_set_mask(&pdev->dev, MHI_DMA_MASK);
+ mhi_dev_ctxt = devm_kzalloc(&pdev->dev,
+ sizeof(*mhi_dev_ctxt),
+ GFP_KERNEL);
+ if (!mhi_dev_ctxt)
+ return -ENOMEM;
+
+ if (!of_find_property(of_node, "qcom,mhi-address-window", &len))
+ return -ENODEV;
+
+ if (len != sizeof(address_window))
+ return -ENODEV;
+
+ r = of_property_read_u64_array(of_node,
+ "qcom,mhi-address-window",
+ address_window,
+ sizeof(address_window) / sizeof(u64));
+ if (r)
+ return r;
+
+ core = &mhi_dev_ctxt->core;
+ r = of_property_read_u32(of_node, "qcom,pci-dev_id", &core->dev_id);
+ if (r)
+ return r;
+
+ r = of_property_read_u32(of_node, "qcom,pci-slot", &core->slot);
+ if (r)
+ return r;
+
+ r = of_property_read_u32(of_node, "qcom,pci-domain", &core->domain);
if (r)
- mhi_log(MHI_MSG_CRITICAL,
+ return r;
+
+ r = of_property_read_u32(of_node, "qcom,pci-bus", &core->bus);
+ if (r)
+ return r;
+
+ snprintf(node, sizeof(node),
+ "mhi_%04x_%02u.%02u.%02u",
+ core->dev_id, core->domain, core->bus, core->slot);
+ mhi_dev_ctxt->mhi_ipc_log =
+ ipc_log_context_create(MHI_IPC_LOG_PAGES, node, 0);
+ if (!mhi_dev_ctxt->mhi_ipc_log)
+ pr_err("%s: Error creating ipc_log buffer\n", __func__);
+
+ r = of_property_read_u32(of_node, "qcom,mhi-ready-timeout",
+ &mhi_dev_ctxt->poll_reset_timeout_ms);
+ if (r)
+ mhi_dev_ctxt->poll_reset_timeout_ms =
+ MHI_READY_STATUS_TIMEOUT_MS;
+
+ mhi_dev_ctxt->dev_space.start_win_addr = address_window[0];
+ mhi_dev_ctxt->dev_space.end_win_addr = address_window[1];
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Start Addr:0x%llx End_Addr:0x%llx\n",
+ mhi_dev_ctxt->dev_space.start_win_addr,
+ mhi_dev_ctxt->dev_space.end_win_addr);
+
+ r = of_property_read_u32(of_node, "qcom,bhi-alignment",
+ &mhi_dev_ctxt->bhi_ctxt.alignment);
+ if (r)
+ mhi_dev_ctxt->bhi_ctxt.alignment = BHI_DEFAULT_ALIGNMENT;
+
+ r = of_property_read_u32(of_node, "qcom,bhi-poll-timeout",
+ &mhi_dev_ctxt->bhi_ctxt.poll_timeout);
+ if (r)
+ mhi_dev_ctxt->bhi_ctxt.poll_timeout = BHI_POLL_TIMEOUT_MS;
+
+ mhi_dev_ctxt->bhi_ctxt.manage_boot =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,mhi-manage-boot");
+ if (mhi_dev_ctxt->bhi_ctxt.manage_boot) {
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ struct firmware_info *fw_info = &bhi_ctxt->firmware_info;
+
+ r = of_property_read_string(of_node, "qcom,mhi-fw-image",
+ &fw_info->fw_image);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error reading DT node 'qcom,mhi-fw-image'\n");
+ return r;
+ }
+ r = of_property_read_u32(of_node, "qcom,mhi-max-sbl",
+ (u32 *)&fw_info->max_sbl_len);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error reading DT node 'qcom,mhi-max-sbl'\n");
+ return r;
+ }
+ r = of_property_read_u32(of_node, "qcom,mhi-sg-size",
+ (u32 *)&fw_info->segment_size);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error reading DT node 'qcom,mhi-sg-size'\n");
+ return r;
+ }
+ INIT_WORK(&bhi_ctxt->fw_load_work, bhi_firmware_download);
+ }
+
+ mhi_dev_ctxt->flags.bb_required =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,mhi-bb-required");
+
+ mhi_dev_ctxt->plat_dev = pdev;
+ platform_set_drvdata(pdev, mhi_dev_ctxt);
+
+ r = dma_set_mask(&pdev->dev, MHI_DMA_MASK);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to set mask for DMA ret %d\n", r);
- mhi_log(MHI_MSG_INFO, "Exited\n");
+ return r;
+ }
+
+ mhi_dev_ctxt->parent = mhi_device_drv->parent;
+ mutex_lock(&mhi_device_drv->lock);
+ list_add_tail(&mhi_dev_ctxt->node, &mhi_device_drv->head);
+ mutex_unlock(&mhi_device_drv->lock);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
+
return 0;
}
@@ -354,42 +487,59 @@ static struct platform_driver mhi_plat_driver = {
static void __exit mhi_exit(void)
{
- ipc_log_context_destroy(mhi_ipc_log);
pci_unregister_driver(&mhi_pcie_driver);
platform_driver_unregister(&mhi_plat_driver);
}
static int __exit mhi_plat_remove(struct platform_device *pdev)
{
- platform_driver_unregister(&mhi_plat_driver);
+ struct mhi_device_ctxt *mhi_dev_ctxt = platform_get_drvdata(pdev);
+
+ ipc_log_context_destroy(mhi_dev_ctxt->mhi_ipc_log);
return 0;
}
static int __init mhi_init(void)
{
int r;
+ struct mhi_device_driver *mhi_dev_drv;
+
+ mhi_dev_drv = kmalloc(sizeof(*mhi_dev_drv), GFP_KERNEL);
+ if (mhi_dev_drv == NULL)
+ return -ENOMEM;
+
+ mutex_init(&mhi_dev_drv->lock);
+ mutex_lock(&mhi_dev_drv->lock);
+ INIT_LIST_HEAD(&mhi_dev_drv->head);
+ mutex_unlock(&mhi_dev_drv->lock);
+ mhi_dev_drv->mhi_bhi_class = class_create(THIS_MODULE, "bhi");
+ if (IS_ERR(mhi_dev_drv->mhi_bhi_class)) {
+ pr_err("Error creating mhi_bhi_class\n");
+ goto class_error;
+ }
+ mhi_dev_drv->parent = debugfs_create_dir("mhi", NULL);
+ mhi_device_drv = mhi_dev_drv;
- mhi_log(MHI_MSG_INFO, "Entered\n");
r = platform_driver_register(&mhi_plat_driver);
if (r) {
- mhi_log(MHI_MSG_INFO, "Failed to probe platform ret %d\n", r);
- return r;
+ pr_err("%s: Failed to probe platform ret %d\n", __func__, r);
+ goto platform_error;
}
r = pci_register_driver(&mhi_pcie_driver);
if (r) {
- mhi_log(MHI_MSG_INFO,
- "Failed to register pcie drv ret %d\n", r);
+ pr_err("%s: Failed to register pcie drv ret %d\n", __func__, r);
goto error;
}
- mhi_ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES, "mhi", 0);
- if (!mhi_ipc_log) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to create IPC logging context\n");
- }
- mhi_log(MHI_MSG_INFO, "Exited\n");
+
return 0;
error:
- pci_unregister_driver(&mhi_pcie_driver);
+ platform_driver_unregister(&mhi_plat_driver);
+platform_error:
+ class_destroy(mhi_device_drv->mhi_bhi_class);
+
+class_error:
+ kfree(mhi_dev_drv);
+ mhi_device_drv = NULL;
return r;
}
@@ -407,7 +557,7 @@ DECLARE_PCI_FIXUP_HEADER(MHI_PCIE_VENDOR_ID,
module_exit(mhi_exit);
-module_init(mhi_init);
+subsys_initcall(mhi_init);
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("MHI_CORE");
diff --git a/drivers/platform/msm/mhi/mhi_init.c b/drivers/platform/msm/mhi/mhi_init.c
index a496c81239bf..b6edf707798b 100644
--- a/drivers/platform/msm/mhi/mhi_init.c
+++ b/drivers/platform/msm/mhi/mhi_init.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -53,12 +53,12 @@ size_t calculate_mhi_space(struct mhi_device_ctxt *mhi_dev_ctxt)
(NR_OF_CMD_RINGS * sizeof(struct mhi_chan_ctxt)) +
(mhi_dev_ctxt->mmio_info.nr_event_rings *
sizeof(struct mhi_event_ctxt));
- mhi_log(MHI_MSG_INFO, "Reserved %zd bytes for context info\n",
- mhi_dev_mem);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Reserved %zd bytes for context info\n", mhi_dev_mem);
/*Calculate size needed for cmd TREs */
mhi_dev_mem += (CMD_EL_PER_RING * sizeof(union mhi_cmd_pkt));
- mhi_log(MHI_MSG_INFO, "Final bytes for MHI device space %zd\n",
- mhi_dev_mem);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Final bytes for MHI device space %zd\n", mhi_dev_mem);
return mhi_dev_mem;
}
@@ -105,23 +105,6 @@ void init_local_chan_ctxt(struct mhi_ring *chan_ctxt,
chan_ctxt->overwrite_en = 0;
}
-int populate_bb_list(struct list_head *bb_list, int num_bb)
-{
- struct mhi_buf_info *mhi_buf = NULL;
- int i;
-
- for (i = 0; i < num_bb; ++i) {
- mhi_buf = kzalloc(sizeof(struct mhi_buf_info), GFP_KERNEL);
- if (!mhi_buf)
- return -ENOMEM;
- mhi_buf->bb_p_addr = 0;
- mhi_buf->bb_v_addr = NULL;
- mhi_log(MHI_MSG_INFO,
- "Allocated BB v_addr 0x%p, p_addr 0x%llx\n",
- mhi_buf->bb_v_addr, (u64)mhi_buf->bb_p_addr);
- }
- return 0;
-}
/**
* mhi_cmd_ring_init- Initialization of the command ring
*
@@ -153,91 +136,6 @@ static int mhi_cmd_ring_init(struct mhi_cmd_ctxt *cmd_ctxt,
return 0;
}
-/*
- * The device can have severe addressing limitations, and in this case
- * the MHI driver may be restricted on where memory can be allocated.
- *
- * The allocation of the MHI control data structures takes place as one
- * big, physically contiguous allocation.
- * The device's addressing window, must be placed around that control segment
- * allocation.
- * Here we attempt to do this by building an addressing window around the
- * initial allocated control segment.
- *
- * The window size is specified by the device and must be contiguous,
- * but depending on where the control segment was allocated, it may be
- * necessary to leave more room, before the ctrl segment start or after
- * the ctrl segment end.
- * The following assumptions are made:
- * Assumption: 1. size of allocated ctrl seg << (device allocation window / 2)
- * 2. allocated ctrl seg is physically contiguous
- */
-static int calculate_mhi_addressing_window(struct mhi_device_ctxt *mhi_dev_ctxt)
-{
- u64 dma_dev_mem_start = 0;
- u64 dma_seg_size = 0;
- u64 dma_max_addr = (dma_addr_t)(-1);
- u64 dev_address_limit = 0;
- int r = 0;
- const struct device_node *np =
- mhi_dev_ctxt->dev_info->plat_dev->dev.of_node;
-
- dma_dev_mem_start = mhi_dev_ctxt->dev_space.dma_dev_mem_start;
- r = of_property_read_u64(np, "mhi-dev-address-win-size",
- &dev_address_limit);
- if (r) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to get device addressing limit ret %d",
- r);
- return r;
- }
- /* Mask off the last 3 bits for address calculation */
- dev_address_limit &= ~0x7;
- mhi_log(MHI_MSG_INFO, "Device Addressing limit 0x%llx\n",
- dev_address_limit);
- dma_seg_size = dev_address_limit / 2;
-
- /*
- * The region of the allocated control segment is within the
- * first half of the device's addressing limit
- */
- if (dma_dev_mem_start < dma_seg_size) {
- mhi_dev_ctxt->dev_space.start_win_addr = 0;
- mhi_dev_ctxt->dev_space.end_win_addr =
- dma_dev_mem_start + dma_seg_size +
- (dma_seg_size - dma_dev_mem_start);
- } else if (dma_dev_mem_start >= dma_seg_size &&
- dma_dev_mem_start <= (dma_max_addr - dma_seg_size)) {
- /*
- * The start of the control segment is located past
- * halfway point of the device's addressing limit
- * Place the control segment in the middle of the device's
- * addressing range
- */
- mhi_dev_ctxt->dev_space.start_win_addr =
- dma_dev_mem_start - dma_seg_size;
- mhi_dev_ctxt->dev_space.end_win_addr =
- dma_dev_mem_start + dma_seg_size;
- } else if (dma_dev_mem_start > (dma_max_addr - dma_seg_size)) {
- /*
- * The start of the control segment is located at the tail end
- * of the host addressing space. Leave extra addressing space
- * at window start
- */
- mhi_dev_ctxt->dev_space.start_win_addr = dma_dev_mem_start;
- mhi_dev_ctxt->dev_space.start_win_addr -=
- dma_seg_size + (dma_seg_size -
- (dma_max_addr - dma_dev_mem_start));
- mhi_dev_ctxt->dev_space.end_win_addr = dma_max_addr;
- }
- mhi_log(MHI_MSG_INFO,
- "MHI start address at 0x%llx, Window Start 0x%llx Window End 0x%llx\n",
- (u64)dma_dev_mem_start,
- (u64)mhi_dev_ctxt->dev_space.start_win_addr,
- (u64)mhi_dev_ctxt->dev_space.end_win_addr);
- return 0;
-}
-
int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
{
size_t mhi_mem_index = 0, ring_len;
@@ -249,12 +147,12 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
calculate_mhi_space(mhi_dev_ctxt);
mhi_dev_ctxt->dev_space.dev_mem_start =
- dma_alloc_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev,
+ dma_alloc_coherent(&mhi_dev_ctxt->plat_dev->dev,
mhi_dev_ctxt->dev_space.dev_mem_len,
&mhi_dev_ctxt->dev_space.dma_dev_mem_start,
GFP_KERNEL);
if (!mhi_dev_ctxt->dev_space.dev_mem_start) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to allocate memory of size %zd bytes\n",
mhi_dev_ctxt->dev_space.dev_mem_len);
return -ENOMEM;
@@ -263,26 +161,20 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
dma_dev_mem_start = mhi_dev_ctxt->dev_space.dma_dev_mem_start;
memset(dev_mem_start, 0, mhi_dev_ctxt->dev_space.dev_mem_len);
- r = calculate_mhi_addressing_window(mhi_dev_ctxt);
- if (r) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to calculate addressing window ret %d", r);
- return r;
- }
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Starting Seg address: virt 0x%p, dma 0x%llx\n",
+ dev_mem_start, (u64)dma_dev_mem_start);
- mhi_log(MHI_MSG_INFO, "Starting Seg address: virt 0x%p, dma 0x%llx\n",
- dev_mem_start, (u64)dma_dev_mem_start);
-
- mhi_log(MHI_MSG_INFO, "Initializing CCABAP at virt 0x%p, dma 0x%llx\n",
- dev_mem_start + mhi_mem_index,
- (u64)dma_dev_mem_start + mhi_mem_index);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Initializing CCABAP at dma 0x%llx\n",
+ (u64)dma_dev_mem_start + mhi_mem_index);
mhi_dev_ctxt->dev_space.ring_ctxt.cc_list = dev_mem_start;
mhi_dev_ctxt->dev_space.ring_ctxt.dma_cc_list = dma_dev_mem_start;
mhi_mem_index += MHI_MAX_CHANNELS * sizeof(struct mhi_chan_ctxt);
- mhi_log(MHI_MSG_INFO, "Initializing CRCBAP at virt 0x%p, dma 0x%llx\n",
- dev_mem_start + mhi_mem_index,
- (u64)dma_dev_mem_start + mhi_mem_index);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Initializing CRCBAP at dma 0x%llx\n",
+ (u64)dma_dev_mem_start + mhi_mem_index);
mhi_dev_ctxt->dev_space.ring_ctxt.cmd_ctxt =
dev_mem_start + mhi_mem_index;
@@ -290,9 +182,9 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
dma_dev_mem_start + mhi_mem_index;
mhi_mem_index += NR_OF_CMD_RINGS * sizeof(struct mhi_chan_ctxt);
- mhi_log(MHI_MSG_INFO, "Initializing ECABAP at virt 0x%p, dma 0x%llx\n",
- dev_mem_start + mhi_mem_index,
- (u64)dma_dev_mem_start + mhi_mem_index);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Initializing ECABAP at dma 0x%llx\n",
+ (u64)dma_dev_mem_start + mhi_mem_index);
mhi_dev_ctxt->dev_space.ring_ctxt.ec_list =
dev_mem_start + mhi_mem_index;
mhi_dev_ctxt->dev_space.ring_ctxt.dma_ec_list =
@@ -300,10 +192,9 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
mhi_mem_index += mhi_dev_ctxt->mmio_info.nr_event_rings *
sizeof(struct mhi_event_ctxt);
- mhi_log(MHI_MSG_INFO,
- "Initializing CMD context at virt 0x%p, dma 0x%llx\n",
- dev_mem_start + mhi_mem_index,
- (u64)dma_dev_mem_start + mhi_mem_index);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Initializing CMD context at dma 0x%llx\n",
+ (u64)dma_dev_mem_start + mhi_mem_index);
/* TODO: Initialize both the local and device cmd context */
ring_len = (CMD_EL_PER_RING * sizeof(union mhi_cmd_pkt));
@@ -322,7 +213,7 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
ring_len = sizeof(union mhi_event_pkt) *
mhi_dev_ctxt->ev_ring_props[i].nr_desc;
ring_addr = dma_alloc_coherent(
- &mhi_dev_ctxt->dev_info->pcie_device->dev,
+ &mhi_dev_ctxt->plat_dev->dev,
ring_len, &ring_dma_addr, GFP_KERNEL);
if (!ring_addr)
goto err_ev_alloc;
@@ -330,9 +221,9 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
ring_dma_addr, ring_len);
init_local_ev_ctxt(&mhi_dev_ctxt->mhi_local_event_ctxt[i],
ring_addr, ring_len);
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Initializing EV_%d TRE list at virt 0x%p dma 0x%llx\n",
- i, ring_addr, (u64)ring_dma_addr);
+ i, ring_addr, (u64)ring_dma_addr);
}
return 0;
@@ -344,12 +235,12 @@ err_ev_alloc:
dev_ev_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[i];
ev_ctxt = &mhi_dev_ctxt->mhi_local_event_ctxt[i];
- dma_free_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev,
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
ev_ctxt->len,
ev_ctxt->base,
dev_ev_ctxt->mhi_event_ring_base_addr);
}
- dma_free_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev,
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
mhi_dev_ctxt->dev_space.dev_mem_len,
mhi_dev_ctxt->dev_space.dev_mem_start,
mhi_dev_ctxt->dev_space.dma_dev_mem_start);
@@ -359,44 +250,28 @@ err_ev_alloc:
static int mhi_init_events(struct mhi_device_ctxt *mhi_dev_ctxt)
{
- mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq = kmalloc(
- sizeof(wait_queue_head_t),
- GFP_KERNEL);
- if (NULL == mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq) {
- mhi_log(MHI_MSG_ERROR, "Failed to init event");
- return -ENOMEM;
- }
- mhi_dev_ctxt->mhi_ev_wq.state_change_event =
- kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);
- if (NULL == mhi_dev_ctxt->mhi_ev_wq.state_change_event) {
- mhi_log(MHI_MSG_ERROR, "Failed to init event");
- goto error_event_handle_alloc;
- }
/* Initialize the event which signals M0 */
mhi_dev_ctxt->mhi_ev_wq.m0_event = kmalloc(sizeof(wait_queue_head_t),
GFP_KERNEL);
if (NULL == mhi_dev_ctxt->mhi_ev_wq.m0_event) {
- mhi_log(MHI_MSG_ERROR, "Failed to init event");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to init event");
goto error_state_change_event_handle;
}
/* Initialize the event which signals M0 */
mhi_dev_ctxt->mhi_ev_wq.m3_event = kmalloc(sizeof(wait_queue_head_t),
GFP_KERNEL);
if (NULL == mhi_dev_ctxt->mhi_ev_wq.m3_event) {
- mhi_log(MHI_MSG_ERROR, "Failed to init event");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to init event");
goto error_m0_event;
}
/* Initialize the event which signals M0 */
mhi_dev_ctxt->mhi_ev_wq.bhi_event = kmalloc(sizeof(wait_queue_head_t),
GFP_KERNEL);
if (NULL == mhi_dev_ctxt->mhi_ev_wq.bhi_event) {
- mhi_log(MHI_MSG_ERROR, "Failed to init event");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to init event");
goto error_bhi_event;
}
- /* Initialize the event which starts the event parsing thread */
- init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
- /* Initialize the event which starts the state change thread */
- init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
+
/* Initialize the event which triggers clients waiting to send */
init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.m0_event);
/* Initialize the event which triggers D3hot */
@@ -409,9 +284,6 @@ error_bhi_event:
error_m0_event:
kfree(mhi_dev_ctxt->mhi_ev_wq.m0_event);
error_state_change_event_handle:
- kfree(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
-error_event_handle_alloc:
- kfree(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
return -ENOMEM;
}
@@ -448,102 +320,70 @@ static void mhi_init_wakelock(struct mhi_device_ctxt *mhi_dev_ctxt)
wakeup_source_init(&mhi_dev_ctxt->w_lock, "mhi_wakeup_source");
}
-static int mhi_spawn_threads(struct mhi_device_ctxt *mhi_dev_ctxt)
-{
- mhi_dev_ctxt->event_thread_handle = kthread_run(parse_event_thread,
- mhi_dev_ctxt,
- "mhi_ev_thrd");
- if (IS_ERR(mhi_dev_ctxt->event_thread_handle))
- return PTR_ERR(mhi_dev_ctxt->event_thread_handle);
- mhi_dev_ctxt->st_thread_handle = kthread_run(mhi_state_change_thread,
- mhi_dev_ctxt,
- "mhi_st_thrd");
- if (IS_ERR(mhi_dev_ctxt->event_thread_handle))
- return PTR_ERR(mhi_dev_ctxt->event_thread_handle);
- return 0;
-}
-
/**
* @brief Main initialization function for a mhi struct device context
* All threads, events mutexes, mhi specific data structures
* are initialized here
*
- * @param dev_info [IN ] pcie struct device information structure to
- which this mhi context belongs
* @param mhi_struct device [IN/OUT] reference to a mhi context to be populated
*
* @return errno
*/
-int mhi_init_device_ctxt(struct mhi_pcie_dev_info *dev_info,
- struct mhi_device_ctxt *mhi_dev_ctxt)
+int mhi_init_device_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt)
{
int r = 0;
- if (NULL == dev_info || NULL == mhi_dev_ctxt)
- return -EINVAL;
-
- mhi_log(MHI_MSG_VERBOSE, "Entered\n");
-
- mhi_dev_ctxt->dev_info = dev_info;
- mhi_dev_ctxt->dev_props = &dev_info->core;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Entered\n");
r = mhi_populate_event_cfg(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to get event ring properties ret %d\n", r);
goto error_during_props;
}
r = mhi_init_sync(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR, "Failed to initialize mhi sync\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to initialize mhi sync\n");
goto error_during_sync;
}
r = create_local_ev_ctxt(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to initialize local event ctxt ret %d\n", r);
goto error_during_local_ev_ctxt;
}
r = init_mhi_dev_mem(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to initialize device memory ret %d\n", r);
goto error_during_dev_mem_init;
}
r = mhi_init_events(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to initialize mhi events ret %d\n", r);
goto error_wq_init;
}
r = mhi_reset_all_thread_queues(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to initialize work queues ret %d\n", r);
goto error_during_thread_init;
}
init_event_ctxt_array(mhi_dev_ctxt);
mhi_dev_ctxt->mhi_state = MHI_STATE_RESET;
- r = mhi_spawn_threads(mhi_dev_ctxt);
- if (r) {
- mhi_log(MHI_MSG_ERROR, "Failed to spawn threads ret %d\n", r);
- goto error_during_thread_spawn;
- }
mhi_init_wakelock(mhi_dev_ctxt);
return r;
-error_during_thread_spawn:
- kfree(mhi_dev_ctxt->state_change_work_item_list.q_lock);
error_during_thread_init:
- kfree(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
- kfree(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
kfree(mhi_dev_ctxt->mhi_ev_wq.m0_event);
kfree(mhi_dev_ctxt->mhi_ev_wq.m3_event);
kfree(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
error_wq_init:
- dma_free_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev,
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
mhi_dev_ctxt->dev_space.dev_mem_len,
mhi_dev_ctxt->dev_space.dev_mem_start,
mhi_dev_ctxt->dev_space.dma_dev_mem_start);
@@ -623,7 +463,8 @@ int mhi_reset_all_thread_queues(
ret_val = mhi_init_state_change_thread_work_queue(
&mhi_dev_ctxt->state_change_work_item_list);
if (ret_val)
- mhi_log(MHI_MSG_ERROR, "Failed to reset STT work queue\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to reset STT work queue\n");
return ret_val;
}
diff --git a/drivers/platform/msm/mhi/mhi_isr.c b/drivers/platform/msm/mhi/mhi_isr.c
index d7c604419593..9aa9aeb7e646 100644
--- a/drivers/platform/msm/mhi/mhi_isr.c
+++ b/drivers/platform/msm/mhi/mhi_isr.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,13 +29,14 @@ static int mhi_process_event_ring(
struct mhi_ring *local_ev_ctxt =
&mhi_dev_ctxt->mhi_local_event_ctxt[ev_index];
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "enter ev_index:%u\n", ev_index);
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
if (unlikely(mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE)) {
- mhi_log(MHI_MSG_ERROR, "Invalid MHI PM State\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Invalid MHI PM State\n");
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
return -EIO;
}
- mhi_assert_device_wake(mhi_dev_ctxt, false);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
ev_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[ev_index];
@@ -77,10 +78,9 @@ static int mhi_process_event_ring(
&cmd_pkt, ev_index);
MHI_TRB_GET_INFO(CMD_TRB_CHID, cmd_pkt, chan);
cfg = &mhi_dev_ctxt->mhi_chan_cfg[chan];
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"MHI CCE received ring 0x%x chan:%u\n",
- ev_index,
- chan);
+ ev_index, chan);
spin_lock_irqsave(&cfg->event_lock, flags);
cfg->cmd_pkt = *cmd_pkt;
cfg->cmd_event_pkt =
@@ -102,9 +102,8 @@ static int mhi_process_event_ring(
__pm_stay_awake(&mhi_dev_ctxt->w_lock);
chan = MHI_EV_READ_CHID(EV_CHID, &event_to_process);
if (unlikely(!VALID_CHAN_NR(chan))) {
- mhi_log(MHI_MSG_ERROR,
- "Invalid chan:%d\n",
- chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Invalid chan:%d\n", chan);
break;
}
ring = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
@@ -115,6 +114,7 @@ static int mhi_process_event_ring(
ev_index);
spin_unlock_bh(&ring->ring_lock);
__pm_relax(&mhi_dev_ctxt->w_lock);
+ event_quota--;
break;
}
case MHI_PKT_TYPE_STATE_CHANGE_EVENT:
@@ -122,13 +122,15 @@ static int mhi_process_event_ring(
enum STATE_TRANSITION new_state;
unsigned long flags;
new_state = MHI_READ_STATE(&event_to_process);
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"MHI STE received ring 0x%x State:%s\n",
- ev_index,
- state_transition_str(new_state));
+ ev_index, state_transition_str(new_state));
- /* If transitioning to M1 schedule worker thread */
- if (new_state == STATE_TRANSITION_M1) {
+ switch (new_state) {
+ case STATE_TRANSITION_M0:
+ process_m0_transition(mhi_dev_ctxt);
+ break;
+ case STATE_TRANSITION_M1:
write_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock,
flags);
mhi_dev_ctxt->mhi_state =
@@ -142,9 +144,15 @@ static int mhi_process_event_ring(
write_unlock_irqrestore(&mhi_dev_ctxt->
pm_xfer_lock,
flags);
- } else {
- mhi_init_state_transition(mhi_dev_ctxt,
- new_state);
+ break;
+ case STATE_TRANSITION_M3:
+ process_m3_transition(mhi_dev_ctxt);
+ break;
+ default:
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Unsupported STE received ring 0x%x State:%s\n",
+ ev_index,
+ state_transition_str(new_state));
}
break;
}
@@ -152,9 +160,8 @@ static int mhi_process_event_ring(
{
enum STATE_TRANSITION new_state;
- mhi_log(MHI_MSG_INFO,
- "MHI EEE received ring 0x%x\n",
- ev_index);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "MHI EEE received ring 0x%x\n", ev_index);
__pm_stay_awake(&mhi_dev_ctxt->w_lock);
__pm_relax(&mhi_dev_ctxt->w_lock);
switch (MHI_READ_EXEC_ENV(&event_to_process)) {
@@ -168,21 +175,25 @@ static int mhi_process_event_ring(
mhi_init_state_transition(mhi_dev_ctxt,
new_state);
break;
+ case MHI_EXEC_ENV_BHIE:
+ new_state = STATE_TRANSITION_BHIE;
+ mhi_init_state_transition(mhi_dev_ctxt,
+ new_state);
}
break;
}
case MHI_PKT_TYPE_STALE_EVENT:
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Stale Event received for chan:%u\n",
MHI_EV_READ_CHID(EV_CHID, local_rp));
break;
case MHI_PKT_TYPE_SYS_ERR_EVENT:
- mhi_log(MHI_MSG_INFO,
- "MHI System Error Detected. Triggering Reset\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "MHI System Error Detected. Triggering Reset\n");
BUG();
break;
default:
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Unsupported packet type code 0x%x\n",
MHI_TRB_READ_INFO(EV_TRB_TYPE,
&event_to_process));
@@ -197,181 +208,139 @@ static int mhi_process_event_ring(
ev_ctxt->mhi_event_read_ptr);
spin_unlock_irqrestore(&local_ev_ctxt->ring_lock, flags);
ret_val = 0;
- --event_quota;
}
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "exit ev_index:%u\n", ev_index);
return ret_val;
}
-int parse_event_thread(void *ctxt)
+void mhi_ev_task(unsigned long data)
{
- struct mhi_device_ctxt *mhi_dev_ctxt = ctxt;
- u32 i = 0;
- int ret_val = 0;
- int ret_val_process_event = 0;
- atomic_t *ev_pen_ptr = &mhi_dev_ctxt->counters.events_pending;
+ struct mhi_ring *mhi_ring = (struct mhi_ring *)data;
+ struct mhi_device_ctxt *mhi_dev_ctxt =
+ mhi_ring->mhi_dev_ctxt;
+ int ev_index = mhi_ring->index;
- /* Go through all event rings */
- for (;;) {
- ret_val =
- wait_event_interruptible(
- *mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq,
- ((atomic_read(
- &mhi_dev_ctxt->counters.events_pending) > 0) &&
- !mhi_dev_ctxt->flags.stop_threads) ||
- mhi_dev_ctxt->flags.kill_threads ||
- (mhi_dev_ctxt->flags.stop_threads &&
- !mhi_dev_ctxt->flags.ev_thread_stopped));
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Enter\n");
+ /* Process event ring */
+ mhi_process_event_ring(mhi_dev_ctxt, ev_index, U32_MAX);
- switch (ret_val) {
- case -ERESTARTSYS:
- return 0;
- default:
- if (mhi_dev_ctxt->flags.kill_threads) {
- mhi_log(MHI_MSG_INFO,
- "Caught exit signal, quitting\n");
- return 0;
- }
- if (mhi_dev_ctxt->flags.stop_threads) {
- mhi_dev_ctxt->flags.ev_thread_stopped = 1;
- continue;
- }
- break;
- }
- mhi_dev_ctxt->flags.ev_thread_stopped = 0;
- atomic_dec(&mhi_dev_ctxt->counters.events_pending);
- for (i = 1; i < mhi_dev_ctxt->mmio_info.nr_event_rings; ++i) {
- if (mhi_dev_ctxt->mhi_state == MHI_STATE_SYS_ERR) {
- mhi_log(MHI_MSG_INFO,
- "SYS_ERR detected, not processing events\n");
- atomic_set(&mhi_dev_ctxt->
- counters.events_pending,
- 0);
- break;
- }
- if (GET_EV_PROPS(EV_MANAGED,
- mhi_dev_ctxt->ev_ring_props[i].flags)) {
- ret_val_process_event =
- mhi_process_event_ring(mhi_dev_ctxt,
- i,
- mhi_dev_ctxt->
- ev_ring_props[i].nr_desc);
- if (ret_val_process_event == -EINPROGRESS)
- atomic_inc(ev_pen_ptr);
- }
- }
- }
+ enable_irq(MSI_TO_IRQ(mhi_dev_ctxt, ev_index));
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Exit\n");
}
-void mhi_ctrl_ev_task(unsigned long data)
+void process_event_ring(struct work_struct *work)
{
+ struct mhi_ring *mhi_ring =
+ container_of(work, struct mhi_ring, ev_worker);
struct mhi_device_ctxt *mhi_dev_ctxt =
- (struct mhi_device_ctxt *)data;
- const unsigned CTRL_EV_RING = 0;
- struct mhi_event_ring_cfg *ring_props =
- &mhi_dev_ctxt->ev_ring_props[CTRL_EV_RING];
+ mhi_ring->mhi_dev_ctxt;
+ int ev_index = mhi_ring->index;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Enter\n");
+ /* Process event ring */
+ mhi_process_event_ring(mhi_dev_ctxt, ev_index, U32_MAX);
- mhi_log(MHI_MSG_VERBOSE, "Enter\n");
- /* Process control event ring */
- mhi_process_event_ring(mhi_dev_ctxt,
- CTRL_EV_RING,
- ring_props->nr_desc);
- enable_irq(MSI_TO_IRQ(mhi_dev_ctxt, CTRL_EV_RING));
- mhi_log(MHI_MSG_VERBOSE, "Exit\n");
+ enable_irq(MSI_TO_IRQ(mhi_dev_ctxt, ev_index));
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Exit\n");
}
struct mhi_result *mhi_poll(struct mhi_client_handle *client_handle)
{
int ret_val;
+ struct mhi_client_config *client_config = client_handle->client_config;
- client_handle->result.buf_addr = NULL;
- client_handle->result.bytes_xferd = 0;
- client_handle->result.transaction_status = 0;
- ret_val = mhi_process_event_ring(client_handle->mhi_dev_ctxt,
- client_handle->event_ring_index,
+ client_config->result.buf_addr = NULL;
+ client_config->result.bytes_xferd = 0;
+ client_config->result.transaction_status = 0;
+ ret_val = mhi_process_event_ring(client_config->mhi_dev_ctxt,
+ client_config->event_ring_index,
1);
if (ret_val)
- mhi_log(MHI_MSG_INFO, "NAPI failed to process event ring\n");
- return &(client_handle->result);
+ mhi_log(client_config->mhi_dev_ctxt, MHI_MSG_INFO,
+ "NAPI failed to process event ring\n");
+ return &(client_config->result);
}
void mhi_mask_irq(struct mhi_client_handle *client_handle)
{
+ struct mhi_client_config *client_config = client_handle->client_config;
struct mhi_device_ctxt *mhi_dev_ctxt =
- client_handle->mhi_dev_ctxt;
+ client_config->mhi_dev_ctxt;
struct mhi_ring *ev_ring = &mhi_dev_ctxt->
- mhi_local_event_ctxt[client_handle->event_ring_index];
+ mhi_local_event_ctxt[client_config->event_ring_index];
- disable_irq_nosync(MSI_TO_IRQ(mhi_dev_ctxt, client_handle->msi_vec));
+ disable_irq_nosync(MSI_TO_IRQ(mhi_dev_ctxt, client_config->msi_vec));
ev_ring->msi_disable_cntr++;
}
void mhi_unmask_irq(struct mhi_client_handle *client_handle)
{
+ struct mhi_client_config *client_config = client_handle->client_config;
struct mhi_device_ctxt *mhi_dev_ctxt =
- client_handle->mhi_dev_ctxt;
+ client_config->mhi_dev_ctxt;
struct mhi_ring *ev_ring = &mhi_dev_ctxt->
- mhi_local_event_ctxt[client_handle->event_ring_index];
+ mhi_local_event_ctxt[client_config->event_ring_index];
ev_ring->msi_enable_cntr++;
- enable_irq(MSI_TO_IRQ(mhi_dev_ctxt, client_handle->msi_vec));
+ enable_irq(MSI_TO_IRQ(mhi_dev_ctxt, client_config->msi_vec));
}
irqreturn_t mhi_msi_handlr(int irq_number, void *dev_id)
{
- struct device *mhi_device = dev_id;
- struct mhi_device_ctxt *mhi_dev_ctxt = mhi_device->platform_data;
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_id;
int msi = IRQ_TO_MSI(mhi_dev_ctxt, irq_number);
+ struct mhi_ring *mhi_ring = &mhi_dev_ctxt->mhi_local_event_ctxt[msi];
+ struct mhi_event_ring_cfg *ring_props =
+ &mhi_dev_ctxt->ev_ring_props[msi];
- if (!mhi_dev_ctxt) {
- mhi_log(MHI_MSG_ERROR, "Failed to get a proper context\n");
- return IRQ_HANDLED;
- }
mhi_dev_ctxt->counters.msi_counter[
IRQ_TO_MSI(mhi_dev_ctxt, irq_number)]++;
- mhi_log(MHI_MSG_VERBOSE, "Got MSI 0x%x\n", msi);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Got MSI 0x%x\n", msi);
trace_mhi_msi(IRQ_TO_MSI(mhi_dev_ctxt, irq_number));
-
- if (msi) {
- atomic_inc(&mhi_dev_ctxt->counters.events_pending);
- wake_up_interruptible(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
- } else {
- disable_irq_nosync(irq_number);
- tasklet_schedule(&mhi_dev_ctxt->ev_task);
- }
+ disable_irq_nosync(irq_number);
+ if (ring_props->priority <= MHI_EV_PRIORITY_TASKLET)
+ tasklet_schedule(&mhi_ring->ev_task);
+ else
+ schedule_work(&mhi_ring->ev_worker);
return IRQ_HANDLED;
}
irqreturn_t mhi_msi_ipa_handlr(int irq_number, void *dev_id)
{
- struct device *mhi_device = dev_id;
- u32 client_index;
- struct mhi_device_ctxt *mhi_dev_ctxt = mhi_device->platform_data;
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_id;
+ struct mhi_event_ring_cfg *ev_ring_props;
struct mhi_client_handle *client_handle;
+ struct mhi_client_config *client_config;
struct mhi_client_info_t *client_info;
struct mhi_cb_info cb_info;
int msi_num = (IRQ_TO_MSI(mhi_dev_ctxt, irq_number));
mhi_dev_ctxt->counters.msi_counter[msi_num]++;
- mhi_log(MHI_MSG_VERBOSE, "Got MSI 0x%x\n", msi_num);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Got MSI 0x%x\n", msi_num);
trace_mhi_msi(msi_num);
- client_index = MHI_MAX_CHANNELS -
- (mhi_dev_ctxt->mmio_info.nr_event_rings - msi_num);
- client_handle = mhi_dev_ctxt->client_handle_list[client_index];
- client_info = &client_handle->client_info;
- if (likely(client_handle)) {
- client_handle->result.user_data =
- client_handle->user_data;
- if (likely(client_info->mhi_client_cb)) {
- cb_info.result = &client_handle->result;
- cb_info.cb_reason = MHI_CB_XFER;
- cb_info.chan = client_handle->chan_info.chan_nr;
- cb_info.result->transaction_status = 0;
- client_info->mhi_client_cb(&cb_info);
- }
+
+ /* Obtain client config from MSI */
+ ev_ring_props = &mhi_dev_ctxt->ev_ring_props[msi_num];
+ client_handle = mhi_dev_ctxt->client_handle_list[ev_ring_props->chan];
+ if (unlikely(!client_handle)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Recv MSI for unreg chan:%u\n", ev_ring_props->chan);
+ return IRQ_HANDLED;
}
+
+ client_config = client_handle->client_config;
+ client_info = &client_config->client_info;
+ client_config->result.user_data =
+ client_config->user_data;
+ cb_info.result = &client_config->result;
+ cb_info.cb_reason = MHI_CB_XFER;
+ cb_info.chan = client_config->chan_info.chan_nr;
+ cb_info.result->transaction_status = 0;
+ client_info->mhi_client_cb(&cb_info);
+
return IRQ_HANDLED;
}
diff --git a/drivers/platform/msm/mhi/mhi_macros.h b/drivers/platform/msm/mhi/mhi_macros.h
index 133c0eeb034e..fc0e6f4bc27d 100644
--- a/drivers/platform/msm/mhi/mhi_macros.h
+++ b/drivers/platform/msm/mhi/mhi_macros.h
@@ -39,7 +39,6 @@
#define MHI_WORK_Q_MAX_SIZE 128
#define MAX_XFER_WORK_ITEMS 100
-#define MHI_MAX_SUPPORTED_DEVICES 1
#define MHI_PCIE_VENDOR_ID 0x17CB
#define MHI_PCIE_DEVICE_ID_9x35 0x0300
@@ -70,9 +69,9 @@
((enum MHI_CLIENT_CHANNEL)(_CHAN_NR) < MHI_CLIENT_RESERVED_1_LOWER))
#define IRQ_TO_MSI(_MHI_DEV_CTXT, _IRQ_NR) \
- ((_IRQ_NR) - (_MHI_DEV_CTXT)->dev_info->core.irq_base)
+ ((_IRQ_NR) - (_MHI_DEV_CTXT)->core.irq_base)
#define MSI_TO_IRQ(_MHI_DEV_CTXT, _MSI_NR) \
- ((_MHI_DEV_CTXT)->dev_info->core.irq_base + (_MSI_NR))
+ ((_MHI_DEV_CTXT)->core.irq_base + (_MSI_NR))
#define VALID_CHAN_NR(_CHAN_NR) (IS_HARDWARE_CHANNEL(_CHAN_NR) || \
IS_SOFTWARE_CHANNEL(_CHAN_NR))
@@ -84,8 +83,8 @@
#define MHI_HW_INTMOD_VAL_MS 2
/* Timeout Values */
-#define MHI_READY_STATUS_TIMEOUT_MS 50
-#define MHI_THREAD_SLEEP_TIMEOUT_MS 20
+#define MHI_READY_STATUS_TIMEOUT_MS 500
+#define MHI_THREAD_SLEEP_TIMEOUT_MS 100
#define MHI_RESUME_WAKE_RETRIES 20
#define IS_HW_EV_RING(_mhi_dev_ctxt, _EV_INDEX) (_EV_INDEX >= \
diff --git a/drivers/platform/msm/mhi/mhi_main.c b/drivers/platform/msm/mhi/mhi_main.c
index 430dc918af7e..644004672cd2 100644
--- a/drivers/platform/msm/mhi/mhi_main.c
+++ b/drivers/platform/msm/mhi/mhi_main.c
@@ -27,13 +27,23 @@
#include "mhi.h"
#include "mhi_hwio.h"
#include "mhi_macros.h"
+#include "mhi_bhi.h"
#include "mhi_trace.h"
static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
union mhi_cmd_pkt *cmd_pkt);
-
-static int enable_bb_ctxt(struct mhi_ring *bb_ctxt, int nr_el)
+static void disable_bb_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
+ struct mhi_ring *bb_ctxt);
+
+static int enable_bb_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
+ struct mhi_ring *bb_ctxt,
+ int nr_el,
+ int chan,
+ size_t max_payload)
{
+ int i;
+ struct mhi_buf_info *mhi_buf_info;
+
bb_ctxt->el_size = sizeof(struct mhi_buf_info);
bb_ctxt->len = bb_ctxt->el_size * nr_el;
bb_ctxt->base = kzalloc(bb_ctxt->len, GFP_KERNEL);
@@ -42,7 +52,46 @@ static int enable_bb_ctxt(struct mhi_ring *bb_ctxt, int nr_el)
bb_ctxt->ack_rp = bb_ctxt->base;
if (!bb_ctxt->base)
return -ENOMEM;
+
+ if (mhi_dev_ctxt->flags.bb_required) {
+ char pool_name[32];
+
+ snprintf(pool_name, sizeof(pool_name), "mhi%d_%d",
+ mhi_dev_ctxt->plat_dev->id, chan);
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Creating pool %s for chan:%d payload: 0x%lx\n",
+ pool_name, chan, max_payload);
+
+ bb_ctxt->dma_pool = dma_pool_create(pool_name,
+ &mhi_dev_ctxt->plat_dev->dev, max_payload, 0, 0);
+ if (unlikely(!bb_ctxt->dma_pool))
+ goto dma_pool_error;
+
+ mhi_buf_info = (struct mhi_buf_info *)bb_ctxt->base;
+ for (i = 0; i < nr_el; i++, mhi_buf_info++) {
+ mhi_buf_info->pre_alloc_v_addr =
+ dma_pool_alloc(bb_ctxt->dma_pool, GFP_KERNEL,
+ &mhi_buf_info->pre_alloc_p_addr);
+ if (unlikely(!mhi_buf_info->pre_alloc_v_addr))
+ goto dma_alloc_error;
+ mhi_buf_info->pre_alloc_len = max_payload;
+ }
+ }
+
return 0;
+
+dma_alloc_error:
+ for (--i, --mhi_buf_info; i >= 0; i--, mhi_buf_info--)
+ dma_pool_free(bb_ctxt->dma_pool, mhi_buf_info->pre_alloc_v_addr,
+ mhi_buf_info->pre_alloc_p_addr);
+
+ dma_pool_destroy(bb_ctxt->dma_pool);
+ bb_ctxt->dma_pool = NULL;
+dma_pool_error:
+ kfree(bb_ctxt->base);
+ bb_ctxt->base = NULL;
+ return -ENOMEM;
}
static void mhi_write_db(struct mhi_device_ctxt *mhi_dev_ctxt,
@@ -66,7 +115,7 @@ static void mhi_update_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_trb_write_ptr = val;
} else if (mhi_dev_ctxt->mmio_info.event_db_addr == io_addr) {
if (chan < mhi_dev_ctxt->mmio_info.nr_event_rings) {
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"EV ctxt: %ld val 0x%llx WP 0x%llx RP: 0x%llx",
chan, val,
mhi_dev_ctxt->dev_space.ring_ctxt.
@@ -76,7 +125,7 @@ static void mhi_update_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[chan].
mhi_event_write_ptr = val;
} else {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Bad EV ring index: %lx\n", chan);
}
}
@@ -84,65 +133,48 @@ static void mhi_update_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
wmb();
}
-int mhi_init_pcie_device(struct mhi_pcie_dev_info *mhi_pcie_dev)
+int mhi_init_pcie_device(struct mhi_device_ctxt *mhi_dev_ctxt)
{
int ret_val = 0;
long int sleep_time = 100;
- struct pci_dev *pcie_device =
- (struct pci_dev *)mhi_pcie_dev->pcie_device;
+ struct pci_dev *pcie_device = mhi_dev_ctxt->pcie_device;
+ struct pcie_core_info *core = &mhi_dev_ctxt->core;
do {
- ret_val = pci_enable_device(mhi_pcie_dev->pcie_device);
+ ret_val = pci_enable_device(pcie_device);
if (0 != ret_val) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to enable pcie struct device r: %d\n",
ret_val);
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Sleeping for ~ %li uS, and retrying.\n",
sleep_time);
msleep(sleep_time);
}
} while (ret_val != 0);
- mhi_log(MHI_MSG_INFO, "Successfully enabled pcie device.\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Successfully enabled pcie device.\n");
- mhi_pcie_dev->core.bar0_base =
- ioremap_nocache(pci_resource_start(pcie_device, 0),
- pci_resource_len(pcie_device, 0));
- if (!mhi_pcie_dev->core.bar0_base)
+ core->bar0_base = ioremap_nocache(pci_resource_start(pcie_device, 0),
+ pci_resource_len(pcie_device, 0));
+ if (!core->bar0_base)
goto mhi_device_list_error;
- mhi_pcie_dev->core.bar0_end = mhi_pcie_dev->core.bar0_base +
- pci_resource_len(pcie_device, 0);
- mhi_pcie_dev->core.bar2_base =
- ioremap_nocache(pci_resource_start(pcie_device, 2),
- pci_resource_len(pcie_device, 2));
- if (!mhi_pcie_dev->core.bar2_base)
- goto io_map_err;
-
- mhi_pcie_dev->core.bar2_end = mhi_pcie_dev->core.bar2_base +
- pci_resource_len(pcie_device, 2);
-
- if (!mhi_pcie_dev->core.bar0_base) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to register for pcie resources\n");
- goto mhi_pcie_read_ep_config_err;
- }
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Device BAR0 address is at 0x%p\n", core->bar0_base);
- mhi_log(MHI_MSG_INFO, "Device BAR0 address is at 0x%p\n",
- mhi_pcie_dev->core.bar0_base);
ret_val = pci_request_region(pcie_device, 0, "mhi");
if (ret_val)
- mhi_log(MHI_MSG_ERROR, "Could not request BAR0 region\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Could not request BAR0 region\n");
- mhi_pcie_dev->core.manufact_id = pcie_device->vendor;
- mhi_pcie_dev->core.dev_id = pcie_device->device;
+ core->manufact_id = pcie_device->vendor;
+ core->dev_id = pcie_device->device;
return 0;
-io_map_err:
- iounmap((void *)mhi_pcie_dev->core.bar0_base);
+
mhi_device_list_error:
pci_disable_device(pcie_device);
-mhi_pcie_read_ep_config_err:
return -EIO;
}
@@ -156,7 +188,7 @@ static void mhi_move_interrupts(struct mhi_device_ctxt *mhi_dev_ctxt, u32 cpu)
GET_EV_PROPS(EV_TYPE,
mhi_dev_ctxt->ev_ring_props[i].flags)) {
irq_to_affin = mhi_dev_ctxt->ev_ring_props[i].msi_vec;
- irq_to_affin += mhi_dev_ctxt->dev_props->irq_base;
+ irq_to_affin += mhi_dev_ctxt->core.irq_base;
irq_set_affinity(irq_to_affin, get_cpu_mask(cpu));
}
}
@@ -198,8 +230,9 @@ int get_chan_props(struct mhi_device_ctxt *mhi_dev_ctxt, int chan,
scnprintf(dt_prop, MAX_BUF_SIZE, "%s%d", "mhi-chan-cfg-", chan);
r = of_property_read_u32_array(
- mhi_dev_ctxt->dev_info->plat_dev->dev.of_node,
- dt_prop, (u32 *)chan_info,
+ mhi_dev_ctxt->plat_dev->dev.of_node,
+ dt_prop,
+ (u32 *)chan_info,
sizeof(struct mhi_chan_info) / sizeof(u32));
return r;
}
@@ -211,9 +244,10 @@ int mhi_release_chan_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
if (cc_list == NULL || ring == NULL)
return -EINVAL;
- dma_free_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev,
- ring->len, ring->base,
- cc_list->mhi_trb_ring_base_addr);
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
+ ring->len,
+ ring->base,
+ cc_list->mhi_trb_ring_base_addr);
mhi_init_chan_ctxt(cc_list, 0, 0, 0, 0, 0, ring,
MHI_CHAN_STATE_DISABLED,
false,
@@ -221,38 +255,37 @@ int mhi_release_chan_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
return 0;
}
-void free_tre_ring(struct mhi_client_handle *client_handle)
+void free_tre_ring(struct mhi_device_ctxt *mhi_dev_ctxt, int chan)
{
struct mhi_chan_ctxt *chan_ctxt;
- struct mhi_device_ctxt *mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
- int chan = client_handle->chan_info.chan_nr;
int r;
chan_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[chan];
r = mhi_release_chan_ctxt(mhi_dev_ctxt, chan_ctxt,
&mhi_dev_ctxt->mhi_local_chan_ctxt[chan]);
if (r)
- mhi_log(MHI_MSG_ERROR,
- "Failed to release chan %d ret %d\n", chan, r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to release chan %d ret %d\n", chan, r);
}
-static int populate_tre_ring(struct mhi_client_handle *client_handle)
+static int populate_tre_ring(struct mhi_client_config *client_config)
{
dma_addr_t ring_dma_addr;
void *ring_local_addr;
struct mhi_chan_ctxt *chan_ctxt;
- struct mhi_device_ctxt *mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
- u32 chan = client_handle->chan_info.chan_nr;
- u32 nr_desc = client_handle->chan_info.max_desc;
+ struct mhi_device_ctxt *mhi_dev_ctxt = client_config->mhi_dev_ctxt;
+ u32 chan = client_config->chan_info.chan_nr;
+ u32 nr_desc = client_config->chan_info.max_desc;
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Entered chan %d requested desc %d\n", chan, nr_desc);
chan_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[chan];
- ring_local_addr = dma_alloc_coherent(
- &mhi_dev_ctxt->dev_info->pcie_device->dev,
- nr_desc * sizeof(union mhi_xfer_pkt),
- &ring_dma_addr, GFP_KERNEL);
+ ring_local_addr =
+ dma_alloc_coherent(&mhi_dev_ctxt->plat_dev->dev,
+ nr_desc * sizeof(union mhi_xfer_pkt),
+ &ring_dma_addr,
+ GFP_KERNEL);
if (ring_local_addr == NULL)
return -ENOMEM;
@@ -261,15 +294,15 @@ static int populate_tre_ring(struct mhi_client_handle *client_handle)
(uintptr_t)ring_local_addr,
nr_desc,
GET_CHAN_PROPS(CHAN_DIR,
- client_handle->chan_info.flags),
- client_handle->chan_info.ev_ring,
+ client_config->chan_info.flags),
+ client_config->chan_info.ev_ring,
&mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
MHI_CHAN_STATE_ENABLED,
GET_CHAN_PROPS(PRESERVE_DB_STATE,
- client_handle->chan_info.flags),
+ client_config->chan_info.flags),
GET_CHAN_PROPS(BRSTMODE,
- client_handle->chan_info.flags));
- mhi_log(MHI_MSG_INFO, "Exited\n");
+ client_config->chan_info.flags));
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
return 0;
}
@@ -283,85 +316,64 @@ int mhi_open_channel(struct mhi_client_handle *client_handle)
struct mhi_cmd_complete_event_pkt cmd_event_pkt;
union mhi_cmd_pkt cmd_pkt;
enum MHI_EVENT_CCS ev_code;
+ struct mhi_client_config *client_config = client_handle->client_config;
- if (!client_handle || client_handle->magic != MHI_HANDLE_MAGIC)
+ if (client_config->magic != MHI_HANDLE_MAGIC)
return -EINVAL;
- mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
- ret_val = get_chan_props(mhi_dev_ctxt,
- client_handle->chan_info.chan_nr,
- &client_handle->chan_info);
- if (ret_val)
- return ret_val;
+ mhi_dev_ctxt = client_config->mhi_dev_ctxt;
- chan = client_handle->chan_info.chan_nr;
+ chan = client_config->chan_info.chan_nr;
cfg = &mhi_dev_ctxt->mhi_chan_cfg[chan];
chan_ring = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
mutex_lock(&cfg->chan_lock);
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Entered: Client opening chan 0x%x\n", chan);
if (mhi_dev_ctxt->dev_exec_env <
GET_CHAN_PROPS(CHAN_BRINGUP_STAGE,
- client_handle->chan_info.flags)) {
- mhi_log(MHI_MSG_INFO,
+ client_config->chan_info.flags)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Chan %d, MHI exec_env %d, not ready!\n",
- chan,
- mhi_dev_ctxt->dev_exec_env);
+ chan, mhi_dev_ctxt->dev_exec_env);
mutex_unlock(&cfg->chan_lock);
return -ENOTCONN;
}
- ret_val = populate_tre_ring(client_handle);
+ ret_val = populate_tre_ring(client_config);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to initialize tre ring chan %d ret %d\n",
- chan,
- ret_val);
- mutex_unlock(&cfg->chan_lock);
- return ret_val;
- }
- client_handle->event_ring_index =
- mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[chan].
- mhi_event_ring_index;
- ret_val = enable_bb_ctxt(&mhi_dev_ctxt->chan_bb_list[chan],
- client_handle->chan_info.max_desc);
- if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to initialize bb ctxt chan %d ret %d\n",
- chan,
- ret_val);
- mutex_unlock(&cfg->chan_lock);
- return ret_val;
+ chan, ret_val);
+ goto error_tre_ring;
}
+ client_config->event_ring_index =
+ mhi_dev_ctxt->dev_space.ring_ctxt.
+ cc_list[chan].mhi_event_ring_index;
- client_handle->msi_vec =
+ client_config->msi_vec =
mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[
- client_handle->event_ring_index].mhi_msi_vector;
- client_handle->intmod_t =
+ client_config->event_ring_index].mhi_msi_vector;
+ client_config->intmod_t =
mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[
- client_handle->event_ring_index].mhi_intmodt;
+ client_config->event_ring_index].mhi_intmodt;
init_completion(&cfg->cmd_complete);
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
if (unlikely(mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE)) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"MHI State is disabled\n");
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mutex_unlock(&cfg->chan_lock);
- return -EIO;
+ ret_val = -EIO;
+ goto error_pm_state;
}
- WARN_ON(mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE);
- mhi_assert_device_wake(mhi_dev_ctxt, false);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- pm_runtime_get(&mhi_dev_ctxt->dev_info->pcie_device->dev);
+ mhi_dev_ctxt->runtime_get(mhi_dev_ctxt);
- spin_lock_irq(&chan_ring->ring_lock);
- chan_ring->ch_state = MHI_CHAN_STATE_ENABLED;
- spin_unlock_irq(&chan_ring->ring_lock);
- ret_val = mhi_send_cmd(client_handle->mhi_dev_ctxt,
+ ret_val = mhi_send_cmd(client_config->mhi_dev_ctxt,
MHI_COMMAND_START_CHAN,
chan);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to send start cmd for chan %d ret %d\n",
chan, ret_val);
goto error_completion;
@@ -369,9 +381,9 @@ int mhi_open_channel(struct mhi_client_handle *client_handle)
ret_val = wait_for_completion_timeout(&cfg->cmd_complete,
msecs_to_jiffies(MHI_MAX_CMD_TIMEOUT));
if (!ret_val) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to receive cmd completion for %d\n",
- chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to receive cmd completion for %d\n", chan);
+ ret_val = -EIO;
goto error_completion;
} else {
ret_val = 0;
@@ -385,76 +397,159 @@ int mhi_open_channel(struct mhi_client_handle *client_handle)
ev_code = MHI_EV_READ_CODE(EV_TRB_CODE,
((union mhi_event_pkt *)&cmd_event_pkt));
if (ev_code != MHI_EVENT_CC_SUCCESS) {
- mhi_log(MHI_MSG_ERROR,
- "Error to receive event completion ev_code:0x%x\n",
- ev_code);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error to receive event comp. ev_code:0x%x\n", ev_code);
ret_val = -EIO;
goto error_completion;
}
- client_handle->chan_status = 1;
-
-error_completion:
+ spin_lock_irq(&chan_ring->ring_lock);
+ chan_ring->ch_state = MHI_CHAN_STATE_ENABLED;
+ spin_unlock_irq(&chan_ring->ring_lock);
+ client_config->chan_status = 1;
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- pm_runtime_put_noidle(&mhi_dev_ctxt->dev_info->pcie_device->dev);
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
mutex_unlock(&cfg->chan_lock);
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "chan:%d opened successfully\n", chan);
+ return 0;
+
+error_completion:
+ read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
+ read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
+error_pm_state:
+ free_tre_ring(mhi_dev_ctxt, chan);
+error_tre_ring:
+ mutex_unlock(&cfg->chan_lock);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Exited chan 0x%x ret:%d\n", chan, ret_val);
return ret_val;
}
EXPORT_SYMBOL(mhi_open_channel);
+bool mhi_is_device_ready(const struct device * const dev,
+ const char *node_name)
+{
+ struct mhi_device_ctxt *itr;
+ const struct device_node *of_node;
+ bool match_found = false;
+
+ if (!mhi_device_drv)
+ return false;
+ if (dev->of_node == NULL)
+ return false;
+
+ of_node = of_parse_phandle(dev->of_node, node_name, 0);
+ if (!of_node)
+ return false;
+
+ mutex_lock(&mhi_device_drv->lock);
+ list_for_each_entry(itr, &mhi_device_drv->head, node) {
+ struct platform_device *pdev = itr->plat_dev;
+
+ if (pdev->dev.of_node == of_node) {
+ match_found = true;
+ break;
+ }
+ }
+ mutex_unlock(&mhi_device_drv->lock);
+ return match_found;
+}
+EXPORT_SYMBOL(mhi_is_device_ready);
+
int mhi_register_channel(struct mhi_client_handle **client_handle,
- enum MHI_CLIENT_CHANNEL chan, s32 device_index,
- struct mhi_client_info_t *client_info, void *user_data)
+ struct mhi_client_info_t *client_info)
{
- struct mhi_device_ctxt *mhi_dev_ctxt = NULL;
+ struct mhi_device_ctxt *mhi_dev_ctxt = NULL, *itr;
+ const struct device_node *of_node;
+ struct mhi_client_config *client_config;
+ const char *node_name;
+ enum MHI_CLIENT_CHANNEL chan;
+ int ret;
- if (!VALID_CHAN_NR(chan))
+ if (!client_info || client_info->dev->of_node == NULL)
return -EINVAL;
- if (NULL == client_handle || device_index < 0)
+ node_name = client_info->node_name;
+ chan = client_info->chan;
+ of_node = of_parse_phandle(client_info->dev->of_node, node_name, 0);
+ if (!of_node || !mhi_device_drv || chan >= MHI_MAX_CHANNELS)
return -EINVAL;
- mhi_dev_ctxt = &(mhi_devices.device_list[device_index].mhi_ctxt);
+ /* Traverse thru the list */
+ mutex_lock(&mhi_device_drv->lock);
+ list_for_each_entry(itr, &mhi_device_drv->head, node) {
+ struct platform_device *pdev = itr->plat_dev;
+
+ if (pdev->dev.of_node == of_node) {
+ mhi_dev_ctxt = itr;
+ break;
+ }
+ }
+ mutex_unlock(&mhi_device_drv->lock);
- if (NULL != mhi_dev_ctxt->client_handle_list[chan])
- return -EISCONN;
+ if (!mhi_dev_ctxt)
+ return -EINVAL;
- mhi_log(MHI_MSG_INFO,
- "Opened channel 0x%x for client\n", chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Registering channel 0x%x for client\n", chan);
*client_handle = kzalloc(sizeof(struct mhi_client_handle), GFP_KERNEL);
if (NULL == *client_handle)
return -ENOMEM;
+ (*client_handle)->client_config =
+ kzalloc(sizeof(*(*client_handle)->client_config), GFP_KERNEL);
+ if ((*client_handle)->client_config == NULL) {
+ kfree(*client_handle);
+ *client_handle = NULL;
+ return -ENOMEM;
+ }
mhi_dev_ctxt->client_handle_list[chan] = *client_handle;
- (*client_handle)->mhi_dev_ctxt = mhi_dev_ctxt;
- (*client_handle)->user_data = user_data;
- (*client_handle)->magic = MHI_HANDLE_MAGIC;
- (*client_handle)->chan_info.chan_nr = chan;
+ (*client_handle)->dev_id = mhi_dev_ctxt->core.dev_id;
+ (*client_handle)->domain = mhi_dev_ctxt->core.domain;
+ (*client_handle)->bus = mhi_dev_ctxt->core.bus;
+ (*client_handle)->slot = mhi_dev_ctxt->core.slot;
+ client_config = (*client_handle)->client_config;
+ client_config->mhi_dev_ctxt = mhi_dev_ctxt;
+ client_config->user_data = client_info->user_data;
+ client_config->magic = MHI_HANDLE_MAGIC;
+ client_config->chan_info.chan_nr = chan;
if (NULL != client_info)
- (*client_handle)->client_info = *client_info;
+ client_config->client_info = *client_info;
if (MHI_CLIENT_IP_HW_0_OUT == chan)
- (*client_handle)->intmod_t = 10;
+ client_config->intmod_t = 10;
if (MHI_CLIENT_IP_HW_0_IN == chan)
- (*client_handle)->intmod_t = 10;
+ client_config->intmod_t = 10;
+
+ get_chan_props(mhi_dev_ctxt, chan, &client_config->chan_info);
+ ret = enable_bb_ctxt(mhi_dev_ctxt, &mhi_dev_ctxt->chan_bb_list[chan],
+ client_config->chan_info.max_desc, chan,
+ client_config->client_info.max_payload);
+ if (ret) {
+ kfree(mhi_dev_ctxt->client_handle_list[chan]->client_config);
+ kfree(mhi_dev_ctxt->client_handle_list[chan]);
+ mhi_dev_ctxt->client_handle_list[chan] = NULL;
+ return -ENOMEM;
+ }
- if (mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_AMSS) {
- mhi_log(MHI_MSG_INFO,
- "Exec env is AMSS notifing client now chan: 0x%x\n",
- chan);
+ if (mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_AMSS &&
+ mhi_dev_ctxt->flags.mhi_initialized) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Exec env is AMSS notify client now chan:%u\n", chan);
mhi_notify_client(*client_handle, MHI_CB_MHI_ENABLED);
}
- mhi_log(MHI_MSG_VERBOSE,
- "Successfuly registered chan 0x%x\n", chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Successfuly registered chan:%u\n", chan);
return 0;
}
EXPORT_SYMBOL(mhi_register_channel);
@@ -469,46 +564,54 @@ void mhi_close_channel(struct mhi_client_handle *client_handle)
union mhi_cmd_pkt cmd_pkt;
struct mhi_ring *chan_ring;
enum MHI_EVENT_CCS ev_code;
+ struct mhi_client_config *client_config =
+ client_handle->client_config;
- if (!client_handle ||
- client_handle->magic != MHI_HANDLE_MAGIC ||
- !client_handle->chan_status)
+ if (client_config->magic != MHI_HANDLE_MAGIC ||
+ !client_config->chan_status)
return;
- mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
- chan = client_handle->chan_info.chan_nr;
+ mhi_dev_ctxt = client_config->mhi_dev_ctxt;
+ chan = client_config->chan_info.chan_nr;
cfg = &mhi_dev_ctxt->mhi_chan_cfg[chan];
- mhi_log(MHI_MSG_INFO, "Client attempting to close chan 0x%x\n", chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Client attempting to close chan 0x%x\n", chan);
+
chan_ring = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
mutex_lock(&cfg->chan_lock);
/* No more processing events for this channel */
spin_lock_irq(&chan_ring->ring_lock);
+ if (chan_ring->ch_state != MHI_CHAN_STATE_ENABLED) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Chan %d is not enabled, cur state:0x%x\n",
+ chan, chan_ring->ch_state);
+ spin_unlock_irq(&chan_ring->ring_lock);
+ mutex_unlock(&cfg->chan_lock);
+ return;
+ }
chan_ring->ch_state = MHI_CHAN_STATE_DISABLED;
spin_unlock_irq(&chan_ring->ring_lock);
init_completion(&cfg->cmd_complete);
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
WARN_ON(mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE);
- mhi_assert_device_wake(mhi_dev_ctxt, false);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- pm_runtime_get(&mhi_dev_ctxt->dev_info->pcie_device->dev);
- ret_val = mhi_send_cmd(client_handle->mhi_dev_ctxt,
- MHI_COMMAND_RESET_CHAN,
- chan);
+ mhi_dev_ctxt->runtime_get(mhi_dev_ctxt);
+ ret_val = mhi_send_cmd(mhi_dev_ctxt,
+ MHI_COMMAND_RESET_CHAN, chan);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to send reset cmd for chan %d ret %d\n",
- chan,
- ret_val);
+ chan, ret_val);
goto error_completion;
}
ret_val = wait_for_completion_timeout(&cfg->cmd_complete,
msecs_to_jiffies(MHI_MAX_CMD_TIMEOUT));
if (!ret_val) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to receive cmd completion for %d\n",
- chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to receive cmd completion for %d\n", chan);
goto error_completion;
}
@@ -519,28 +622,36 @@ void mhi_close_channel(struct mhi_client_handle *client_handle)
ev_code = MHI_EV_READ_CODE(EV_TRB_CODE,
((union mhi_event_pkt *)&cmd_event_pkt));
if (ev_code != MHI_EVENT_CC_SUCCESS) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Error to receive event completion ev_cod:0x%x\n",
ev_code);
- goto error_completion;
}
+error_completion:
ret_val = reset_chan_cmd(mhi_dev_ctxt, &cmd_pkt);
if (ret_val)
- mhi_log(MHI_MSG_ERROR,
- "Error resetting cmd ret:%d\n",
- ret_val);
-
-error_completion:
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error resetting cmd ret:%d\n", ret_val);
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- pm_runtime_put_noidle(&mhi_dev_ctxt->dev_info->pcie_device->dev);
- mhi_log(MHI_MSG_INFO, "Freeing ring for chan 0x%x\n", chan);
- free_tre_ring(client_handle);
- mhi_log(MHI_MSG_INFO, "Chan 0x%x confirmed closed.\n", chan);
- client_handle->chan_status = 0;
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "resetting bb_ring for chan 0x%x\n", chan);
+ mhi_dev_ctxt->chan_bb_list[chan].rp =
+ mhi_dev_ctxt->chan_bb_list[chan].base;
+ mhi_dev_ctxt->chan_bb_list[chan].wp =
+ mhi_dev_ctxt->chan_bb_list[chan].base;
+ mhi_dev_ctxt->chan_bb_list[chan].ack_rp =
+ mhi_dev_ctxt->chan_bb_list[chan].base;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Freeing ring for chan 0x%x\n", chan);
+ free_tre_ring(mhi_dev_ctxt, chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Chan 0x%x confirmed closed.\n", chan);
+ client_config->chan_status = 0;
mutex_unlock(&cfg->chan_lock);
}
EXPORT_SYMBOL(mhi_close_channel);
@@ -596,6 +707,7 @@ static inline int mhi_queue_tre(struct mhi_device_ctxt
}
return 0;
}
+
static int create_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
int chan, void *buf, size_t buf_len,
enum dma_data_direction dir, struct mhi_buf_info **bb)
@@ -606,7 +718,8 @@ static int create_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
int r;
uintptr_t bb_index, ctxt_index_wp, ctxt_index_rp;
- mhi_log(MHI_MSG_RAW, "Entered chan %d\n", chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW,
+ "Entered chan %d\n", chan);
get_element_index(bb_ctxt, bb_ctxt->wp, &bb_index);
get_element_index(&mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp,
@@ -615,9 +728,9 @@ static int create_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_dev_ctxt->mhi_local_chan_ctxt[chan].rp,
&ctxt_index_rp);
BUG_ON(bb_index != ctxt_index_wp);
- mhi_log(MHI_MSG_VERBOSE,
- "Chan RP index %ld Chan WP index %ld, chan %d\n",
- ctxt_index_rp, ctxt_index_wp, chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Chan RP index %ld Chan WP index %ld, chan %d\n",
+ ctxt_index_rp, ctxt_index_wp, chan);
r = ctxt_add_element(bb_ctxt, (void **)&bb_info);
if (r)
return r;
@@ -626,88 +739,76 @@ static int create_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
bb_info->client_buf = buf;
bb_info->dir = dir;
bb_info->bb_p_addr = dma_map_single(
- &mhi_dev_ctxt->dev_info->plat_dev->dev,
+ &mhi_dev_ctxt->plat_dev->dev,
bb_info->client_buf,
bb_info->buf_len,
bb_info->dir);
+ bb_info->bb_active = 0;
if (!VALID_BUF(bb_info->bb_p_addr, bb_info->buf_len, mhi_dev_ctxt)) {
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Buffer outside DMA range 0x%lx, size 0x%zx\n",
- (uintptr_t)bb_info->bb_p_addr, buf_len);
- dma_unmap_single(&mhi_dev_ctxt->dev_info->plat_dev->dev,
+ (uintptr_t)bb_info->bb_p_addr, buf_len);
+ dma_unmap_single(&mhi_dev_ctxt->plat_dev->dev,
bb_info->bb_p_addr,
bb_info->buf_len,
bb_info->dir);
- mhi_log(MHI_MSG_RAW, "Allocating BB, chan %d\n", chan);
- bb_info->bb_v_addr = dma_alloc_coherent(
- &mhi_dev_ctxt->dev_info->pcie_device->dev,
- bb_info->buf_len,
- &bb_info->bb_p_addr,
- GFP_ATOMIC);
- if (!bb_info->bb_v_addr)
- return -ENOMEM;
- mhi_dev_ctxt->counters.bb_used[chan]++;
- if (dir == DMA_TO_DEVICE) {
- mhi_log(MHI_MSG_INFO, "Copying client buf into BB.\n");
- memcpy(bb_info->bb_v_addr, buf, bb_info->buf_len);
- /* Flush out data to bounce buffer */
- wmb();
- }
- bb_info->bb_active = 1;
+
+ if (likely((mhi_dev_ctxt->flags.bb_required &&
+ bb_info->pre_alloc_len >= bb_info->buf_len))) {
+ bb_info->bb_p_addr = bb_info->pre_alloc_p_addr;
+ bb_info->bb_v_addr = bb_info->pre_alloc_v_addr;
+ mhi_dev_ctxt->counters.bb_used[chan]++;
+ if (dir == DMA_TO_DEVICE) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Copying client buf into BB.\n");
+ memcpy(bb_info->bb_v_addr, buf,
+ bb_info->buf_len);
+ }
+ bb_info->bb_active = 1;
+ } else
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "No BB allocated\n");
}
*bb = bb_info;
- mhi_log(MHI_MSG_RAW, "Exited chan %d\n", chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Exited chan %d\n", chan);
return 0;
}
+static void disable_bb_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
+ struct mhi_ring *bb_ctxt)
+{
+ if (mhi_dev_ctxt->flags.bb_required) {
+ struct mhi_buf_info *bb =
+ (struct mhi_buf_info *)bb_ctxt->base;
+ int nr_el = bb_ctxt->len / bb_ctxt->el_size;
+ int i = 0;
+
+ for (i = 0; i < nr_el; i++, bb++)
+ dma_pool_free(bb_ctxt->dma_pool, bb->pre_alloc_v_addr,
+ bb->pre_alloc_p_addr);
+ dma_pool_destroy(bb_ctxt->dma_pool);
+ bb_ctxt->dma_pool = NULL;
+ }
+
+ kfree(bb_ctxt->base);
+ bb_ctxt->base = NULL;
+}
+
static void free_bounce_buffer(struct mhi_device_ctxt *mhi_dev_ctxt,
struct mhi_buf_info *bb)
{
- mhi_log(MHI_MSG_RAW, "Entered\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Entered\n");
if (!bb->bb_active)
/* This buffer was maped directly to device */
- dma_unmap_single(&mhi_dev_ctxt->dev_info->plat_dev->dev,
+ dma_unmap_single(&mhi_dev_ctxt->plat_dev->dev,
bb->bb_p_addr, bb->buf_len, bb->dir);
- else
- /* This buffer was bounced */
- dma_free_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev,
- bb->buf_len,
- bb->bb_v_addr,
- bb->bb_p_addr);
- bb->bb_active = 0;
- mhi_log(MHI_MSG_RAW, "Exited\n");
-}
-
-void reset_bb_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
- struct mhi_ring *bb_ctxt)
-{
- int r = 0;
- struct mhi_buf_info *bb = NULL;
- mhi_log(MHI_MSG_VERBOSE, "Entered\n");
- /*
- Assumption: No events are expected during or after
- this operation is occurring for this channel.
- If a bounce buffer was allocated, the coherent memory is
- expected to be already freed.
- If the user's bounce buffer was mapped, it is expected to be
- already unmapped.
- Failure of any of the above conditions will result in
- a memory leak or subtle memory corruption.
- */
- while (!r) {
- r = ctxt_del_element(bb_ctxt, (void **)&bb);
- if (bb)
- free_bounce_buffer(mhi_dev_ctxt, bb);
- }
- bb_ctxt->ack_rp = bb_ctxt->base;
- bb_ctxt->rp = bb_ctxt->base;
- bb_ctxt->wp = bb_ctxt->base;
- mhi_log(MHI_MSG_VERBOSE, "Exited\n");
+ bb->bb_active = 0;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Exited\n");
}
static int mhi_queue_dma_xfer(
- struct mhi_client_handle *client_handle,
+ struct mhi_client_config *client_config,
dma_addr_t buf, size_t buf_len, enum MHI_FLAGS mhi_flags)
{
union mhi_xfer_pkt *pkt_loc;
@@ -715,17 +816,17 @@ static int mhi_queue_dma_xfer(
enum MHI_CLIENT_CHANNEL chan;
struct mhi_device_ctxt *mhi_dev_ctxt;
- mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
+ mhi_dev_ctxt = client_config->mhi_dev_ctxt;
MHI_ASSERT(VALID_BUF(buf, buf_len, mhi_dev_ctxt),
"Client buffer is of invalid length\n");
- chan = client_handle->chan_info.chan_nr;
+ chan = client_config->chan_info.chan_nr;
pkt_loc = mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp;
pkt_loc->data_tx_pkt.buffer_ptr = buf;
pkt_loc->type.info = mhi_flags;
trace_mhi_tre(pkt_loc, chan, 0);
- if (likely(0 != client_handle->intmod_t))
+ if (likely(client_config->intmod_t))
MHI_TRB_SET_INFO(TX_TRB_BEI, pkt_loc, 1);
else
MHI_TRB_SET_INFO(TX_TRB_BEI, pkt_loc, 0);
@@ -736,21 +837,21 @@ static int mhi_queue_dma_xfer(
/* Ensure writes to descriptor are flushed */
wmb();
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Channel %d Has buf size of %zd and buf addr %lx, flags 0x%x\n",
- chan, buf_len, (uintptr_t)buf, mhi_flags);
+ chan, buf_len, (uintptr_t)buf, mhi_flags);
/* Add the TRB to the correct transfer ring */
ret_val = ctxt_add_element(&mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
(void *)&pkt_loc);
if (unlikely(0 != ret_val)) {
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Failed to insert trb in xfer ring\n");
return ret_val;
}
if (MHI_OUT ==
- GET_CHAN_PROPS(CHAN_DIR, client_handle->chan_info.flags))
+ GET_CHAN_PROPS(CHAN_DIR, client_config->chan_info.flags))
atomic_inc(&mhi_dev_ctxt->counters.outbound_acks);
return ret_val;
@@ -765,56 +866,55 @@ int mhi_queue_xfer(struct mhi_client_handle *client_handle,
struct mhi_device_ctxt *mhi_dev_ctxt;
u32 chan;
unsigned long flags;
+ struct mhi_client_config *client_config;
if (!client_handle || !buf || !buf_len)
return -EINVAL;
- mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
- chan = client_handle->chan_info.chan_nr;
+ client_config = client_handle->client_config;
+ mhi_dev_ctxt = client_config->mhi_dev_ctxt;
+ chan = client_config->chan_info.chan_nr;
read_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags);
if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE) {
read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"MHI is not in active state\n");
return -EINVAL;
}
-
- pm_runtime_get(&mhi_dev_ctxt->dev_info->pcie_device->dev);
- mhi_assert_device_wake(mhi_dev_ctxt, false);
+ mhi_dev_ctxt->runtime_get(mhi_dev_ctxt);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
- if (MHI_OUT == GET_CHAN_PROPS(CHAN_DIR, client_handle->chan_info.flags))
+ if (GET_CHAN_PROPS(CHAN_DIR, client_config->chan_info.flags) == MHI_OUT)
dma_dir = DMA_TO_DEVICE;
else
dma_dir = DMA_FROM_DEVICE;
- r = create_bb(client_handle->mhi_dev_ctxt,
- client_handle->chan_info.chan_nr,
- buf, buf_len, dma_dir, &bb);
+ r = create_bb(client_config->mhi_dev_ctxt,
+ client_config->chan_info.chan_nr,
+ buf,
+ buf_len,
+ dma_dir,
+ &bb);
if (r) {
- mhi_log(MHI_MSG_VERBOSE,
- "Failed to create BB, chan %d ret %d\n",
- chan,
- r);
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
- pm_runtime_put_noidle(&mhi_dev_ctxt->dev_info->
- pcie_device->dev);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Failed to create BB, chan %d ret %d\n", chan, r);
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
read_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
return r;
}
- mhi_log(MHI_MSG_VERBOSE,
- "Queueing to HW: Client Buf 0x%p, size 0x%zx, DMA %llx, chan %d\n",
- buf, buf_len, (u64)bb->bb_p_addr,
- client_handle->chan_info.chan_nr);
- r = mhi_queue_dma_xfer(client_handle,
- bb->bb_p_addr,
- bb->buf_len,
- mhi_flags);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Queueing to HW: Client Buf 0x%p, size 0x%zx, DMA %llx, chan %d\n",
+ buf, buf_len, (u64)bb->bb_p_addr,
+ client_config->chan_info.chan_nr);
+ r = mhi_queue_dma_xfer(client_config,
+ bb->bb_p_addr,
+ bb->buf_len,
+ mhi_flags);
/*
* Assumption: If create_bounce_buffer did not fail, we do not
@@ -826,11 +926,8 @@ int mhi_queue_xfer(struct mhi_client_handle *client_handle,
read_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags);
mhi_queue_tre(mhi_dev_ctxt, chan, MHI_RING_TYPE_XFER_RING);
if (dma_dir == DMA_FROM_DEVICE) {
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
- pm_runtime_put_noidle(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
}
read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
return 0;
@@ -848,13 +945,12 @@ int mhi_send_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_local_cmd_ctxt[PRIMARY_CMD_RING];
if (chan >= MHI_MAX_CHANNELS || cmd >= MHI_COMMAND_MAX_NR) {
- mhi_log(MHI_MSG_ERROR,
- "Invalid channel id, received id: 0x%x",
- chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Invalid channel id, received id: 0x%x", chan);
return -EINVAL;
}
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Entered, MHI state %s dev_exec_env %d chan %d cmd %d\n",
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state),
mhi_dev_ctxt->dev_exec_env, chan, cmd);
@@ -868,14 +964,16 @@ int mhi_send_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
ring_el_type = MHI_PKT_TYPE_START_CHAN_CMD;
break;
default:
- mhi_log(MHI_MSG_ERROR, "Bad command received\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Bad command received\n");
return -EINVAL;
}
spin_lock_irqsave(&mhi_ring->ring_lock, flags);
ret_val = ctxt_add_element(mhi_ring, (void *)&cmd_pkt);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR, "Failed to insert element\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to insert element\n");
spin_unlock_irqrestore(&mhi_ring->ring_lock, flags);
return ret_val;
}
@@ -886,13 +984,10 @@ int mhi_send_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_queue_tre(mhi_dev_ctxt, 0, MHI_RING_TYPE_CMD_RING);
read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags2);
spin_unlock_irqrestore(&mhi_ring->ring_lock, flags);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Sent command 0x%x for chan %d\n", cmd, chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited ret %d.\n", ret_val);
- mhi_log(MHI_MSG_VERBOSE,
- "Sent command 0x%x for chan %d\n",
- cmd,
- chan);
-
- mhi_log(MHI_MSG_INFO, "Exited ret %d.\n", ret_val);
return ret_val;
}
@@ -904,7 +999,7 @@ static void parse_inbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
struct mhi_buf_info *bb;
- mhi_log(MHI_MSG_INFO, "Entered\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n");
bb = bb_ctxt->rp;
bb->filled_size = bounced_data_size;
@@ -915,7 +1010,7 @@ static void parse_inbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
if (bb->bb_active) {
/* This is coherent memory, no cache management is needed */
memcpy(bb->client_buf, bb->bb_v_addr, bb->filled_size);
- mhi_log(MHI_MSG_RAW,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW,
"Bounce from BB:0x%p to Client Buf: 0x%p Len 0x%zx\n",
bb->client_buf, bb->bb_v_addr, bb->filled_size);
}
@@ -929,7 +1024,7 @@ static void parse_inbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
* rp, since it can be moved async by mhi_poll_inbound
*/
free_bounce_buffer(mhi_dev_ctxt, bb);
- mhi_log(MHI_MSG_INFO, "Exited\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
}
static void parse_outbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
@@ -940,7 +1035,7 @@ static void parse_outbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
struct mhi_buf_info *bb;
bb = bb_ctxt->rp;
- mhi_log(MHI_MSG_RAW, "Entered\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Entered\n");
BUG_ON(bb->dir != DMA_TO_DEVICE);
bb->filled_size = bounced_data_size;
BUG_ON(bb->filled_size != bb->buf_len);
@@ -948,7 +1043,7 @@ static void parse_outbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
result->bytes_xferd = bb->filled_size;
result->transaction_status = 0;
free_bounce_buffer(mhi_dev_ctxt, bb);
- mhi_log(MHI_MSG_RAW, "Exited\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Exited\n");
}
static int parse_outbound(struct mhi_device_ctxt *mhi_dev_ctxt,
@@ -957,71 +1052,75 @@ static int parse_outbound(struct mhi_device_ctxt *mhi_dev_ctxt,
struct mhi_result *result = NULL;
int ret_val = 0;
struct mhi_client_handle *client_handle = NULL;
+ struct mhi_client_config *client_config;
struct mhi_ring *local_chan_ctxt = NULL;
struct mhi_cb_info cb_info;
struct mhi_ring *bb_ctxt = &mhi_dev_ctxt->chan_bb_list[chan];
local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
client_handle = mhi_dev_ctxt->client_handle_list[chan];
+ client_config = client_handle->client_config;
/* If ring is empty */
MHI_ASSERT(!unlikely(mhi_dev_ctxt->mhi_local_chan_ctxt[chan].rp ==
mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp), "Empty Event Ring\n");
parse_outbound_bb(mhi_dev_ctxt, bb_ctxt,
- &client_handle->result, xfer_len);
+ &client_config->result, xfer_len);
- mhi_log(MHI_MSG_RAW, "Removing BB from head, chan %d\n", chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW,
+ "Removing BB from head, chan %d\n", chan);
atomic_dec(&mhi_dev_ctxt->counters.outbound_acks);
- mhi_deassert_device_wake(mhi_dev_ctxt);
- pm_runtime_put_noidle(&mhi_dev_ctxt->dev_info->pcie_device->dev);
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->dev_info->pcie_device->dev);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
ret_val = ctxt_del_element(&mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
NULL);
BUG_ON(ret_val);
ret_val = ctxt_del_element(bb_ctxt, NULL);
BUG_ON(ret_val);
- if (NULL != client_handle) {
- result = &mhi_dev_ctxt->client_handle_list[chan]->result;
- if (NULL != (&client_handle->client_info.mhi_client_cb)) {
- client_handle->result.user_data =
- client_handle->user_data;
- cb_info.cb_reason = MHI_CB_XFER;
- cb_info.result = &client_handle->result;
- cb_info.chan = chan;
- client_handle->client_info.mhi_client_cb(&cb_info);
- }
+
+ result = &client_config->result;
+ if (NULL != (&client_config->client_info.mhi_client_cb)) {
+ client_config->result.user_data =
+ client_config->user_data;
+ cb_info.cb_reason = MHI_CB_XFER;
+ cb_info.result = result;
+ cb_info.chan = chan;
+ client_config->client_info.mhi_client_cb(&cb_info);
}
- mhi_log(MHI_MSG_RAW,
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW,
"Processed outbound ack chan %d Pending acks %d.\n",
chan, atomic_read(&mhi_dev_ctxt->counters.outbound_acks));
return 0;
}
static int parse_inbound(struct mhi_device_ctxt *mhi_dev_ctxt,
- u32 chan, union mhi_xfer_pkt *local_ev_trb_loc, u16 xfer_len)
+ u32 chan, union mhi_xfer_pkt *local_ev_trb_loc,
+ u16 xfer_len, unsigned ev_ring)
{
struct mhi_client_handle *client_handle;
+ struct mhi_client_config *client_config;
struct mhi_ring *local_chan_ctxt;
struct mhi_result *result;
struct mhi_cb_info cb_info;
struct mhi_ring *bb_ctxt = &mhi_dev_ctxt->chan_bb_list[chan];
+ bool ev_managed = GET_EV_PROPS(EV_MANAGED,
+ mhi_dev_ctxt->ev_ring_props[ev_ring].flags);
int r;
uintptr_t bb_index, ctxt_index_rp, ctxt_index_wp;
client_handle = mhi_dev_ctxt->client_handle_list[chan];
+ client_config = client_handle->client_config;
local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
MHI_ASSERT(!unlikely(mhi_dev_ctxt->mhi_local_chan_ctxt[chan].rp ==
mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp), "Empty Event Ring\n");
- if (NULL != mhi_dev_ctxt->client_handle_list[chan])
- result = &mhi_dev_ctxt->client_handle_list[chan]->result;
-
- parse_inbound_bb(mhi_dev_ctxt, bb_ctxt,
- &client_handle->result, xfer_len);
+ result = &client_config->result;
+ parse_inbound_bb(mhi_dev_ctxt, bb_ctxt, result, xfer_len);
- if (unlikely(IS_SOFTWARE_CHANNEL(chan))) {
+ if (ev_managed) {
MHI_TX_TRB_SET_LEN(TX_TRB_LEN, local_ev_trb_loc, xfer_len);
r = ctxt_del_element(local_chan_ctxt, NULL);
BUG_ON(r);
@@ -1034,19 +1133,19 @@ static int parse_inbound(struct mhi_device_ctxt *mhi_dev_ctxt,
get_element_index(&mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp,
&ctxt_index_wp);
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Chan RP index %ld Chan WP index %ld chan %d\n",
ctxt_index_rp, ctxt_index_wp, chan);
BUG_ON(bb_index != ctxt_index_rp);
- if (NULL != client_handle->client_info.mhi_client_cb) {
- client_handle->result.user_data =
- client_handle->user_data;
+ if (client_config->client_info.mhi_client_cb) {
+ client_config->result.user_data =
+ client_config->user_data;
cb_info.cb_reason = MHI_CB_XFER;
- cb_info.result = &client_handle->result;
+ cb_info.result = &client_config->result;
cb_info.chan = chan;
- client_handle->client_info.mhi_client_cb(&cb_info);
+ client_config->client_info.mhi_client_cb(&cb_info);
} else {
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"No client registered chan %d\n", chan);
}
} else {
@@ -1067,7 +1166,7 @@ static int parse_inbound(struct mhi_device_ctxt *mhi_dev_ctxt,
&mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp,
&ctxt_index_wp);
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Chan RP index %ld Chan WP index %ld chan %d\n",
ctxt_index_rp, ctxt_index_wp, chan);
BUG_ON(bb_index != ctxt_index_rp);
@@ -1088,27 +1187,32 @@ static int validate_xfer_el_addr(struct mhi_chan_ctxt *ring,
-ERANGE : 0;
}
-static void print_tre(int chan, struct mhi_ring *ring, struct mhi_tx_pkt *tre)
+static void print_tre(struct mhi_device_ctxt *mhi_dev_ctxt,
+ int chan,
+ struct mhi_ring *ring,
+ struct mhi_tx_pkt *tre)
{
uintptr_t el_index;
get_element_index(ring, tre, &el_index);
- mhi_log(MHI_MSG_ERROR, "Printing TRE 0x%p index %lx for channel %d:\n",
- tre, el_index, chan);
- mhi_log(MHI_MSG_ERROR, "Buffer Pointer 0x%llx, len 0x%x, info 0x%x\n",
- tre->buffer_ptr, tre->buf_len, tre->info);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Printing TRE 0x%p index %lx for channel %d:\n",
+ tre, el_index, chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Buffer Pointer 0x%llx, len 0x%x, info 0x%x\n",
+ tre->buffer_ptr, tre->buf_len, tre->info);
}
-int parse_xfer_event(struct mhi_device_ctxt *ctxt,
+int parse_xfer_event(struct mhi_device_ctxt *mhi_dev_ctxt,
union mhi_event_pkt *event, u32 event_id)
{
- struct mhi_device_ctxt *mhi_dev_ctxt = (struct mhi_device_ctxt *)ctxt;
struct mhi_result *result;
u32 chan = MHI_MAX_CHANNELS;
u16 xfer_len;
uintptr_t phy_ev_trb_loc;
union mhi_xfer_pkt *local_ev_trb_loc;
struct mhi_client_handle *client_handle;
+ struct mhi_client_config *client_config;
union mhi_xfer_pkt *local_trb_loc;
struct mhi_chan_ctxt *chan_ctxt;
u32 nr_trb_to_parse;
@@ -1121,11 +1225,11 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt,
local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
ev_code = MHI_EV_READ_CODE(EV_TRB_CODE, event);
client_handle = mhi_dev_ctxt->client_handle_list[chan];
- client_handle->pkt_count++;
- result = &client_handle->result;
- mhi_log(MHI_MSG_VERBOSE,
- "Event Received, chan %d, cc_code %d\n",
- chan, ev_code);
+ client_config = client_handle->client_config;
+ client_config->pkt_count++;
+ result = &client_config->result;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Event Received, chan %d, cc_code %d\n", chan, ev_code);
if (ev_code == MHI_EVENT_CC_OVERFLOW)
result->transaction_status = -EOVERFLOW;
else
@@ -1158,10 +1262,9 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt,
local_ev_trb_loc,
&nr_trb_to_parse);
if (unlikely(ret_val)) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to get nr available trbs ret: %d.\n",
ret_val);
- panic("critical error");
return ret_val;
}
do {
@@ -1177,14 +1280,14 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt,
local_trb_loc);
if (!VALID_BUF(trb_data_loc, xfer_len, mhi_dev_ctxt)) {
- mhi_log(MHI_MSG_CRITICAL,
- "Bad buffer ptr: %lx.\n",
- (uintptr_t)trb_data_loc);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Bad buf ptr: %llx.\n", trb_data_loc);
return -EINVAL;
}
if (local_chan_ctxt->dir == MHI_IN) {
parse_inbound(mhi_dev_ctxt, chan,
- local_ev_trb_loc, xfer_len);
+ local_ev_trb_loc, xfer_len,
+ event_id);
} else {
parse_outbound(mhi_dev_ctxt, chan,
local_ev_trb_loc, xfer_len);
@@ -1192,7 +1295,7 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt,
mhi_dev_ctxt->counters.chan_pkts_xferd[chan]++;
if (local_trb_loc ==
(union mhi_xfer_pkt *)local_chan_ctxt->rp) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Done. Processed until: %lx.\n",
(uintptr_t)trb_data_loc);
break;
@@ -1208,7 +1311,8 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt,
{
u64 db_value = 0;
- mhi_log(MHI_MSG_INFO, "DB_MODE/OOB Detected chan %d.\n", chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "DB_MODE/OOB Detected chan %d.\n", chan);
local_chan_ctxt->db_mode.db_mode = 1;
if (local_chan_ctxt->wp != local_chan_ctxt->rp) {
@@ -1219,9 +1323,6 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt,
mhi_dev_ctxt->mmio_info.chan_db_addr, chan,
db_value);
}
- client_handle = mhi_dev_ctxt->client_handle_list[chan];
- if (client_handle)
- result->transaction_status = -ENOTCONN;
break;
}
case MHI_EVENT_CC_BAD_TRE:
@@ -1229,15 +1330,16 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt,
local_ev_trb_loc = (void *)mhi_p2v_addr(mhi_dev_ctxt,
MHI_RING_TYPE_EVENT_RING, event_id,
phy_ev_trb_loc);
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Received BAD TRE event for ring %d, pointer 0x%p\n",
chan, local_ev_trb_loc);
- print_tre(chan, &mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
+ print_tre(mhi_dev_ctxt, chan,
+ &mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
(struct mhi_tx_pkt *)local_ev_trb_loc);
BUG();
break;
default:
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Unknown TX completion.\n");
break;
@@ -1261,12 +1363,14 @@ int recycle_trb_and_ring(struct mhi_device_ctxt *mhi_dev_ctxt,
ret_val = ctxt_del_element(ring, &removed_element);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR, "Could not remove element from ring\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Could not remove element from ring\n");
return ret_val;
}
ret_val = ctxt_add_element(ring, &added_element);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR, "Could not add element to ring\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Could not add element to ring\n");
return ret_val;
}
@@ -1296,9 +1400,7 @@ static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
struct mhi_ring *ev_ring;
struct mhi_chan_ctxt *chan_ctxt;
struct mhi_event_ctxt *ev_ctxt = NULL;
- struct mhi_client_handle *client_handle = NULL;
int pending_el = 0, i;
- struct mhi_ring *bb_ctxt;
unsigned long flags;
union mhi_event_pkt *local_rp = NULL;
union mhi_event_pkt *device_rp = NULL;
@@ -1306,20 +1408,19 @@ static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
MHI_TRB_GET_INFO(CMD_TRB_CHID, cmd_pkt, chan);
if (!VALID_CHAN_NR(chan)) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Bad channel number for CCE\n");
return -EINVAL;
}
- bb_ctxt = &mhi_dev_ctxt->chan_bb_list[chan];
- client_handle = mhi_dev_ctxt->client_handle_list[chan];
local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
chan_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[chan];
ev_ring = &mhi_dev_ctxt->
mhi_local_event_ctxt[chan_ctxt->mhi_event_ring_index];
ev_ctxt = &mhi_dev_ctxt->
dev_space.ring_ctxt.ec_list[chan_ctxt->mhi_event_ring_index];
- mhi_log(MHI_MSG_INFO, "Processed cmd reset event\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Processed cmd reset event\n");
/* Clear all stale events related to Channel */
spin_lock_irqsave(&ev_ring->ring_lock, flags);
@@ -1357,21 +1458,18 @@ static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
local_chan_ctxt->rp,
local_chan_ctxt->wp,
&pending_el);
- mhi_log(MHI_MSG_INFO, "Decrementing chan %d out acks by %d.\n",
- chan, pending_el);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Decrementing chan %d out acks by %d.\n", chan, pending_el);
atomic_sub(pending_el, &mhi_dev_ctxt->counters.outbound_acks);
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
for (i = 0; i < pending_el; i++)
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
for (i = 0; i < pending_el; i++) {
- pm_runtime_put_noidle(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
}
/* Reset the local channel context */
@@ -1384,10 +1482,7 @@ static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
chan_ctxt->mhi_trb_read_ptr = chan_ctxt->mhi_trb_ring_base_addr;
chan_ctxt->mhi_trb_write_ptr = chan_ctxt->mhi_trb_ring_base_addr;
- mhi_log(MHI_MSG_INFO, "Cleaning up BB list\n");
- reset_bb_ctxt(mhi_dev_ctxt, bb_ctxt);
-
- mhi_log(MHI_MSG_INFO, "Reset complete.\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Reset complete.\n");
return ret_val;
}
@@ -1418,15 +1513,17 @@ int mhi_poll_inbound(struct mhi_client_handle *client_handle,
struct mhi_chan_cfg *cfg;
struct mhi_ring *bb_ctxt = NULL;
struct mhi_buf_info *bb = NULL;
+ struct mhi_client_config *client_config;
int chan = 0, r = 0;
- if (!client_handle || !result || !client_handle->mhi_dev_ctxt)
+ if (!client_handle || !result)
return -EINVAL;
+ client_config = client_handle->client_config;
+ mhi_dev_ctxt = client_config->mhi_dev_ctxt;
- mhi_log(MHI_MSG_VERBOSE, "Entered\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Entered\n");
- mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
- chan = client_handle->chan_info.chan_nr;
+ chan = client_config->chan_info.chan_nr;
local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
cfg = &mhi_dev_ctxt->mhi_chan_cfg[chan];
bb_ctxt = &mhi_dev_ctxt->chan_bb_list[chan];
@@ -1437,7 +1534,7 @@ int mhi_poll_inbound(struct mhi_client_handle *client_handle,
result->flags = pending_trb->info;
bb = bb_ctxt->ack_rp;
if (bb->bb_active) {
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Bounce buffer active chan %d, copying data\n",
chan);
}
@@ -1458,7 +1555,7 @@ int mhi_poll_inbound(struct mhi_client_handle *client_handle,
r = -ENODATA;
}
mutex_unlock(&cfg->chan_lock);
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Exited Result: Buf addr: 0x%p Bytes xfed 0x%zx chan %d\n",
result->buf_addr, result->bytes_xferd, chan);
return r;
@@ -1488,11 +1585,11 @@ int mhi_wait_for_mdm(struct mhi_device_ctxt *mhi_dev_ctxt)
while (mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr, MHIREGLEN)
== 0xFFFFFFFF
&& j <= MHI_MAX_LINK_RETRIES) {
- mhi_log(MHI_MSG_CRITICAL,
- "Could not access device retry %d\n", j);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Could not access device retry %d\n", j);
msleep(MHI_LINK_STABILITY_WAIT_MS);
if (MHI_MAX_LINK_RETRIES == j) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Could not access device, FAILING!\n");
return -ETIME;
}
@@ -1503,9 +1600,12 @@ int mhi_wait_for_mdm(struct mhi_device_ctxt *mhi_dev_ctxt)
int mhi_get_max_desc(struct mhi_client_handle *client_handle)
{
+ struct mhi_client_config *client_config;
+
if (!client_handle)
return -EINVAL;
- return client_handle->chan_info.max_desc - 1;
+ client_config = client_handle->client_config;
+ return client_config->chan_info.max_desc - 1;
}
EXPORT_SYMBOL(mhi_get_max_desc);
@@ -1514,6 +1614,27 @@ int mhi_get_epid(struct mhi_client_handle *client_handle)
return MHI_EPID;
}
+void mhi_master_mode_runtime_get(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ pm_runtime_get(&mhi_dev_ctxt->pcie_device->dev);
+}
+
+void mhi_master_mode_runtime_put(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ pm_runtime_mark_last_busy(&mhi_dev_ctxt->pcie_device->dev);
+ pm_runtime_put_noidle(&mhi_dev_ctxt->pcie_device->dev);
+}
+
+void mhi_slave_mode_runtime_get(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ mhi_dev_ctxt->bus_master_rt_get(mhi_dev_ctxt->pcie_device);
+}
+
+void mhi_slave_mode_runtime_put(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ mhi_dev_ctxt->bus_master_rt_put(mhi_dev_ctxt->pcie_device);
+}
+
/*
* mhi_assert_device_wake - Set WAKE_DB register
* force_set - if true, will set bit regardless of counts
@@ -1572,16 +1693,17 @@ void mhi_deassert_device_wake(struct mhi_device_ctxt *mhi_dev_ctxt)
int mhi_set_lpm(struct mhi_client_handle *client_handle, bool enable_lpm)
{
- struct mhi_device_ctxt *mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
+ struct mhi_client_config *client_config = client_handle->client_config;
+ struct mhi_device_ctxt *mhi_dev_ctxt = client_config->mhi_dev_ctxt;
unsigned long flags;
read_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags);
/* Disable low power mode by asserting Wake */
if (enable_lpm == false)
- mhi_assert_device_wake(mhi_dev_ctxt, false);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
else
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
@@ -1592,26 +1714,147 @@ EXPORT_SYMBOL(mhi_set_lpm);
int mhi_set_bus_request(struct mhi_device_ctxt *mhi_dev_ctxt,
int index)
{
- mhi_log(MHI_MSG_INFO, "Setting bus request to index %d\n", index);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Setting bus request to index %d\n", index);
return msm_bus_scale_client_update_request(mhi_dev_ctxt->bus_client,
index);
}
-int mhi_deregister_channel(struct mhi_client_handle
- *client_handle) {
+int mhi_deregister_channel(struct mhi_client_handle *client_handle)
+{
int ret_val = 0;
int chan;
+ struct mhi_client_config *client_config;
+ struct mhi_device_ctxt *mhi_dev_ctxt;
- if (!client_handle || client_handle->magic != MHI_HANDLE_MAGIC)
+ if (!client_handle)
return -EINVAL;
- chan = client_handle->chan_info.chan_nr;
- client_handle->magic = 0;
- client_handle->mhi_dev_ctxt->client_handle_list[chan] = NULL;
+
+ client_config = client_handle->client_config;
+ mhi_dev_ctxt = client_config->mhi_dev_ctxt;
+ chan = client_config->chan_info.chan_nr;
+ client_config->magic = 0;
+ mhi_dev_ctxt->client_handle_list[chan] = NULL;
+ disable_bb_ctxt(mhi_dev_ctxt, &mhi_dev_ctxt->chan_bb_list[chan]);
+ kfree(client_config);
kfree(client_handle);
return ret_val;
}
EXPORT_SYMBOL(mhi_deregister_channel);
+int mhi_register_device(struct mhi_device *mhi_device,
+ const char *node_name,
+ unsigned long user_data)
+{
+ const struct device_node *of_node;
+ struct mhi_device_ctxt *mhi_dev_ctxt = NULL, *itr;
+ struct pcie_core_info *core_info;
+ struct pci_dev *pci_dev = mhi_device->pci_dev;
+ u32 domain = pci_domain_nr(pci_dev->bus);
+ u32 bus = pci_dev->bus->number;
+ u32 dev_id = pci_dev->device;
+ u32 slot = PCI_SLOT(pci_dev->devfn);
+ int ret, i;
+
+ of_node = of_parse_phandle(mhi_device->dev->of_node, node_name, 0);
+ if (!of_node)
+ return -EINVAL;
+
+ if (!mhi_device_drv)
+ return -EPROBE_DEFER;
+
+ /* Traverse thru the list */
+ mutex_lock(&mhi_device_drv->lock);
+ list_for_each_entry(itr, &mhi_device_drv->head, node) {
+ struct platform_device *pdev = itr->plat_dev;
+ struct pcie_core_info *core = &itr->core;
+
+ if (pdev->dev.of_node == of_node &&
+ core->domain == domain &&
+ core->bus == bus &&
+ core->dev_id == dev_id &&
+ core->slot == slot) {
+ mhi_dev_ctxt = itr;
+ break;
+ }
+ }
+ mutex_unlock(&mhi_device_drv->lock);
+
+ /* perhaps we've not probed yet */
+ if (!mhi_dev_ctxt)
+ return -EPROBE_DEFER;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Registering Domain:%02u Bus:%04u dev:0x%04x slot:%04u\n",
+ domain, bus, dev_id, slot);
+
+ /* Set up pcie dev info */
+ mhi_dev_ctxt->pcie_device = pci_dev;
+ mhi_dev_ctxt->mhi_pm_state = MHI_PM_DISABLE;
+ INIT_WORK(&mhi_dev_ctxt->process_m1_worker, process_m1_transition);
+ INIT_WORK(&mhi_dev_ctxt->st_thread_worker, mhi_state_change_worker);
+ mutex_init(&mhi_dev_ctxt->pm_lock);
+ rwlock_init(&mhi_dev_ctxt->pm_xfer_lock);
+ spin_lock_init(&mhi_dev_ctxt->dev_wake_lock);
+ init_completion(&mhi_dev_ctxt->cmd_complete);
+ mhi_dev_ctxt->flags.link_up = 1;
+ core_info = &mhi_dev_ctxt->core;
+ core_info->manufact_id = pci_dev->vendor;
+ core_info->pci_master = false;
+
+ /* Go thru resources and set up */
+ for (i = 0; i < ARRAY_SIZE(mhi_device->resources); i++) {
+ const struct resource *res = &mhi_device->resources[i];
+
+ switch (resource_type(res)) {
+ case IORESOURCE_MEM:
+ /* bus master already mapped it */
+ core_info->bar0_base = (void __iomem *)res->start;
+ core_info->bar0_end = (void __iomem *)res->end;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "bar mapped to:0x%llx - 0x%llx (virtual)\n",
+ res->start, res->end);
+ break;
+ case IORESOURCE_IRQ:
+ core_info->irq_base = (u32)res->start;
+ core_info->max_nr_msis = (u32)resource_size(res);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "irq mapped to: %u size:%u\n",
+ core_info->irq_base,
+ core_info->max_nr_msis);
+ break;
+ };
+ }
+
+ if (!core_info->bar0_base || !core_info->irq_base)
+ return -EINVAL;
+
+ mhi_dev_ctxt->bus_master_rt_get = mhi_device->pm_runtime_get;
+ mhi_dev_ctxt->bus_master_rt_put = mhi_device->pm_runtime_noidle;
+ if (!mhi_dev_ctxt->bus_master_rt_get ||
+ !mhi_dev_ctxt->bus_master_rt_put)
+ return -EINVAL;
+
+ ret = mhi_ctxt_init(mhi_dev_ctxt);
+ if (ret) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "MHI Initialization failed, ret %d\n", ret);
+ return ret;
+ }
+ mhi_init_debugfs(mhi_dev_ctxt);
+
+ /* setup shadow pm functions */
+ mhi_dev_ctxt->assert_wake = mhi_assert_device_wake;
+ mhi_dev_ctxt->deassert_wake = mhi_deassert_device_wake;
+ mhi_dev_ctxt->runtime_get = mhi_slave_mode_runtime_get;
+ mhi_dev_ctxt->runtime_put = mhi_slave_mode_runtime_put;
+ mhi_device->mhi_dev_ctxt = mhi_dev_ctxt;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit success\n");
+
+ return 0;
+}
+EXPORT_SYMBOL(mhi_register_device);
+
void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt,
void __iomem *io_addr,
uintptr_t chan,
@@ -1627,9 +1870,9 @@ void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt,
ring_ctxt = &mhi_dev_ctxt->
mhi_local_event_ctxt[chan];
- mhi_log(MHI_MSG_VERBOSE,
- "db.set addr: %p io_offset 0x%lx val:0x%x\n",
- io_addr, chan, val);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "db.set addr: %p io_offset 0x%lx val:0x%x\n",
+ io_addr, chan, val);
mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val);
@@ -1637,10 +1880,9 @@ void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_write_db(mhi_dev_ctxt, io_addr, chan, val);
ring_ctxt->db_mode.db_mode = 0;
} else {
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Not ringing xfer db, chan %ld, brstmode %d db_mode %d\n",
- chan,
- ring_ctxt->db_mode.brstmode,
+ chan, ring_ctxt->db_mode.brstmode,
ring_ctxt->db_mode.db_mode);
}
}
@@ -1650,10 +1892,9 @@ void mhi_process_db_brstmode_disable(struct mhi_device_ctxt *mhi_dev_ctxt,
uintptr_t chan,
u32 val)
{
- mhi_log(MHI_MSG_VERBOSE,
- "db.set addr: %p io_offset 0x%lx val:0x%x\n",
- io_addr, chan, val);
-
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "db.set addr: %p io_offset 0x%lx val:0x%x\n",
+ io_addr, chan, val);
mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val);
mhi_write_db(mhi_dev_ctxt, io_addr, chan, val);
}
@@ -1663,9 +1904,9 @@ void mhi_process_db(struct mhi_device_ctxt *mhi_dev_ctxt,
uintptr_t chan, u32 val)
{
- mhi_log(MHI_MSG_VERBOSE,
- "db.set addr: %p io_offset 0x%lx val:0x%x\n",
- io_addr, chan, val);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "db.set addr: %p io_offset 0x%lx val:0x%x\n",
+ io_addr, chan, val);
mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val);
@@ -1678,7 +1919,7 @@ void mhi_process_db(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_write_db(mhi_dev_ctxt, io_addr, chan, val);
chan_ctxt->db_mode.db_mode = 0;
} else {
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Not ringing xfer db, chan %ld, brstmode %d db_mode %d\n",
chan, chan_ctxt->db_mode.brstmode,
chan_ctxt->db_mode.db_mode);
@@ -1714,10 +1955,9 @@ void mhi_reg_write(struct mhi_device_ctxt *mhi_dev_ctxt,
void __iomem *io_addr,
uintptr_t io_offset, u32 val)
{
- mhi_log(MHI_MSG_RAW, "d.s 0x%p off: 0x%lx 0x%x\n",
- io_addr, io_offset, val);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW,
+ "d.s 0x%p off: 0x%lx 0x%x\n", io_addr, io_offset, val);
iowrite32(val, io_addr + io_offset);
-
/* Flush write to device */
wmb();
}
diff --git a/drivers/platform/msm/mhi/mhi_mmio_ops.c b/drivers/platform/msm/mhi/mhi_mmio_ops.c
index b4447378683e..a991a2e68b34 100644
--- a/drivers/platform/msm/mhi/mhi_mmio_ops.c
+++ b/drivers/platform/msm/mhi/mhi_mmio_ops.c
@@ -29,93 +29,79 @@
int mhi_test_for_device_reset(struct mhi_device_ctxt *mhi_dev_ctxt)
{
u32 pcie_word_val = 0;
- u32 expiry_counter;
unsigned long flags;
rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock;
+ unsigned long timeout;
- mhi_log(MHI_MSG_INFO, "Waiting for MMIO RESET bit to be cleared.\n");
- read_lock_irqsave(pm_xfer_lock, flags);
- if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
- read_unlock_irqrestore(pm_xfer_lock, flags);
- return -EIO;
- }
- pcie_word_val = mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr,
- MHISTATUS);
- MHI_READ_FIELD(pcie_word_val,
- MHICTRL_RESET_MASK,
- MHICTRL_RESET_SHIFT);
- read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
- if (pcie_word_val == 0xFFFFFFFF)
- return -ENOTCONN;
-
- while (MHI_STATE_RESET != pcie_word_val && expiry_counter < 100) {
- expiry_counter++;
- mhi_log(MHI_MSG_ERROR,
- "Device is not RESET, sleeping and retrying.\n");
- msleep(MHI_READY_STATUS_TIMEOUT_MS);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Waiting for MMIO RESET bit to be cleared.\n");
+
+ timeout = jiffies +
+ msecs_to_jiffies(mhi_dev_ctxt->poll_reset_timeout_ms);
+ while (time_before(jiffies, timeout)) {
read_lock_irqsave(pm_xfer_lock, flags);
if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
read_unlock_irqrestore(pm_xfer_lock, flags);
return -EIO;
}
pcie_word_val = mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr,
- MHICTRL);
+ MHICTRL);
+ read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
+ if (pcie_word_val == 0xFFFFFFFF)
+ return -ENOTCONN;
MHI_READ_FIELD(pcie_word_val,
- MHICTRL_RESET_MASK,
- MHICTRL_RESET_SHIFT);
- read_unlock_irqrestore(pm_xfer_lock, flags);
- }
+ MHICTRL_RESET_MASK,
+ MHICTRL_RESET_SHIFT);
- if (MHI_STATE_READY != pcie_word_val)
- return -ENOTCONN;
- return 0;
+ if (!pcie_word_val)
+ return 0;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "MHI still in Reset sleeping\n");
+ msleep(MHI_THREAD_SLEEP_TIMEOUT_MS);
+ }
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Timeout waiting for reset to be cleared\n");
+ return -ETIMEDOUT;
}
int mhi_test_for_device_ready(struct mhi_device_ctxt *mhi_dev_ctxt)
{
u32 pcie_word_val = 0;
- u32 expiry_counter;
unsigned long flags;
rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock;
+ unsigned long timeout;
- mhi_log(MHI_MSG_INFO, "Waiting for MMIO Ready bit to be set\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Waiting for MMIO Ready bit to be set\n");
- read_lock_irqsave(pm_xfer_lock, flags);
- if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
- read_unlock_irqrestore(pm_xfer_lock, flags);
- return -EIO;
- }
- /* Read MMIO and poll for READY bit to be set */
- pcie_word_val = mhi_reg_read(
- mhi_dev_ctxt->mmio_info.mmio_addr, MHISTATUS);
- MHI_READ_FIELD(pcie_word_val,
- MHISTATUS_READY_MASK,
- MHISTATUS_READY_SHIFT);
- read_unlock_irqrestore(pm_xfer_lock, flags);
-
- if (pcie_word_val == 0xFFFFFFFF)
- return -ENOTCONN;
- expiry_counter = 0;
- while (MHI_STATE_READY != pcie_word_val && expiry_counter < 50) {
- expiry_counter++;
- mhi_log(MHI_MSG_ERROR,
- "Device is not ready, sleeping and retrying.\n");
- msleep(MHI_READY_STATUS_TIMEOUT_MS);
+ timeout = jiffies +
+ msecs_to_jiffies(mhi_dev_ctxt->poll_reset_timeout_ms);
+ while (time_before(jiffies, timeout)) {
+ /* Read MMIO and poll for READY bit to be set */
read_lock_irqsave(pm_xfer_lock, flags);
if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
read_unlock_irqrestore(pm_xfer_lock, flags);
return -EIO;
}
+
pcie_word_val = mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr,
MHISTATUS);
- MHI_READ_FIELD(pcie_word_val,
- MHISTATUS_READY_MASK, MHISTATUS_READY_SHIFT);
read_unlock_irqrestore(pm_xfer_lock, flags);
+ if (pcie_word_val == 0xFFFFFFFF)
+ return -ENOTCONN;
+ MHI_READ_FIELD(pcie_word_val,
+ MHISTATUS_READY_MASK,
+ MHISTATUS_READY_SHIFT);
+ if (pcie_word_val == MHI_STATE_READY)
+ return 0;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Device is not ready, sleeping and retrying.\n");
+ msleep(MHI_THREAD_SLEEP_TIMEOUT_MS);
}
-
- if (pcie_word_val != MHI_STATE_READY)
- return -ETIMEDOUT;
- return 0;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Device timed out waiting for ready\n");
+ return -ETIMEDOUT;
}
int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt)
@@ -125,28 +111,26 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt)
u32 i = 0;
int ret_val;
- mhi_log(MHI_MSG_INFO, "~~~ Initializing MMIO ~~~\n");
- mhi_dev_ctxt->mmio_info.mmio_addr = mhi_dev_ctxt->dev_props->bar0_base;
-
- mhi_log(MHI_MSG_INFO, "Bar 0 address is at: 0x%p\n",
- mhi_dev_ctxt->mmio_info.mmio_addr);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "~~~ Initializing MMIO ~~~\n");
+ mhi_dev_ctxt->mmio_info.mmio_addr = mhi_dev_ctxt->core.bar0_base;
mhi_dev_ctxt->mmio_info.mmio_len = mhi_reg_read(
mhi_dev_ctxt->mmio_info.mmio_addr,
MHIREGLEN);
if (0 == mhi_dev_ctxt->mmio_info.mmio_len) {
- mhi_log(MHI_MSG_ERROR, "Received mmio length as zero\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Received mmio length as zero\n");
return -EIO;
}
- mhi_log(MHI_MSG_INFO, "Testing MHI Ver\n");
- mhi_dev_ctxt->dev_props->mhi_ver = mhi_reg_read(
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Testing MHI Ver\n");
+ mhi_dev_ctxt->core.mhi_ver = mhi_reg_read(
mhi_dev_ctxt->mmio_info.mmio_addr, MHIVER);
- if (MHI_VERSION != mhi_dev_ctxt->dev_props->mhi_ver) {
- mhi_log(MHI_MSG_CRITICAL,
- "Bad MMIO version, 0x%x\n",
- mhi_dev_ctxt->dev_props->mhi_ver);
+ if (mhi_dev_ctxt->core.mhi_ver != MHI_VERSION) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Bad MMIO version, 0x%x\n", mhi_dev_ctxt->core.mhi_ver);
return ret_val;
}
@@ -159,9 +143,10 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt)
else
chan_ctxt->chstate = MHI_CHAN_STATE_DISABLED;
}
- mhi_log(MHI_MSG_INFO,
- "Read back MMIO Ready bit successfully. Moving on..\n");
- mhi_log(MHI_MSG_INFO, "Reading channel doorbell offset\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Read back MMIO Ready bit successfully. Moving on..\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Reading channel doorbell offset\n");
mhi_dev_ctxt->mmio_info.chan_db_addr =
mhi_dev_ctxt->mmio_info.mmio_addr;
@@ -173,13 +158,15 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt)
CHDBOFF, CHDBOFF_CHDBOFF_MASK,
CHDBOFF_CHDBOFF_SHIFT);
- mhi_log(MHI_MSG_INFO, "Reading event doorbell offset\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Reading event doorbell offset\n");
mhi_dev_ctxt->mmio_info.event_db_addr += mhi_reg_read_field(
mhi_dev_ctxt->mmio_info.mmio_addr,
ERDBOFF, ERDBOFF_ERDBOFF_MASK,
ERDBOFF_ERDBOFF_SHIFT);
- mhi_log(MHI_MSG_INFO, "Setting all MMIO values.\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Setting all MMIO values.\n");
mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr,
MHICFG,
@@ -290,7 +277,7 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt)
MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_MASK,
MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_SHIFT,
pcie_word_val);
- mhi_log(MHI_MSG_INFO, "Done..\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Done..\n");
return 0;
}
diff --git a/drivers/platform/msm/mhi/mhi_pm.c b/drivers/platform/msm/mhi/mhi_pm.c
index 2f44601e225e..d7a4f7aa93ef 100644
--- a/drivers/platform/msm/mhi/mhi_pm.c
+++ b/drivers/platform/msm/mhi/mhi_pm.c
@@ -20,14 +20,17 @@
#include "mhi_sys.h"
#include "mhi.h"
#include "mhi_hwio.h"
+#include "mhi_bhi.h"
/* Write only sysfs attributes */
static DEVICE_ATTR(MHI_M0, S_IWUSR, NULL, sysfs_init_m0);
+static DEVICE_ATTR(MHI_M3, S_IWUSR, NULL, sysfs_init_m3);
/* Read only sysfs attributes */
static struct attribute *mhi_attributes[] = {
&dev_attr_MHI_M0.attr,
+ &dev_attr_MHI_M3.attr,
NULL,
};
@@ -38,9 +41,9 @@ static struct attribute_group mhi_attribute_group = {
int mhi_pci_suspend(struct device *dev)
{
int r = 0;
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev);
- mhi_log(MHI_MSG_INFO, "Entered\n");
-
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n");
/* if rpm status still active then force suspend */
if (!pm_runtime_status_suspended(dev)) {
r = mhi_runtime_suspend(dev);
@@ -51,86 +54,134 @@ int mhi_pci_suspend(struct device *dev)
pm_runtime_set_suspended(dev);
pm_runtime_disable(dev);
- mhi_log(MHI_MSG_INFO, "Exit\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit\n");
return r;
}
-int mhi_runtime_suspend(struct device *dev)
+static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt,
+ bool force_m3)
{
int r = 0;
- struct mhi_device_ctxt *mhi_dev_ctxt = dev->platform_data;
- mutex_lock(&mhi_dev_ctxt->pm_lock);
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
-
- mhi_log(MHI_MSG_INFO, "Entered with State:0x%x %s\n",
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Entered with State:0x%x %s\n",
mhi_dev_ctxt->mhi_pm_state,
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
/* Link is already disabled */
if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE ||
mhi_dev_ctxt->mhi_pm_state == MHI_PM_M3) {
- mhi_log(MHI_MSG_INFO, "Already in active state, exiting\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Already in M3 State\n");
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mutex_unlock(&mhi_dev_ctxt->pm_lock);
return 0;
}
- if (unlikely(atomic_read(&mhi_dev_ctxt->counters.device_wake))) {
- mhi_log(MHI_MSG_INFO, "Busy, Aborting Runtime Suspend\n");
+ if (unlikely(atomic_read(&mhi_dev_ctxt->counters.device_wake) &&
+ force_m3 == false)){
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Busy, Aborting M3\n");
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mutex_unlock(&mhi_dev_ctxt->pm_lock);
return -EBUSY;
}
- mhi_assert_device_wake(mhi_dev_ctxt, false);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event,
mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
mhi_dev_ctxt->mhi_state == MHI_STATE_M1,
msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
if (!r) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to get M0||M1 event, timeout, current state:%s\n",
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
- r = -EIO;
- goto rpm_suspend_exit;
+ return -EIO;
}
- mhi_log(MHI_MSG_INFO, "Allowing M3 State\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Allowing M3 State\n");
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
mhi_dev_ctxt->mhi_pm_state = MHI_PM_M3_ENTER;
mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M3);
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_log(MHI_MSG_INFO,
- "Waiting for M3 completion.\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Waiting for M3 completion.\n");
r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m3_event,
mhi_dev_ctxt->mhi_state == MHI_STATE_M3,
msecs_to_jiffies(MHI_MAX_SUSPEND_TIMEOUT));
if (!r) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to get M3 event, timeout, current state:%s\n",
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
- r = -EIO;
- goto rpm_suspend_exit;
+ return -EIO;
}
+ return 0;
+}
+
+static int mhi_pm_initiate_m0(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ int r;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Entered with State:0x%x %s\n",
+ mhi_dev_ctxt->mhi_pm_state,
+ TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
+
+ write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->mhi_pm_state = MHI_PM_M3_EXIT;
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+
+ /* Set and wait for M0 Event */
+ write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M0);
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event,
+ mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
+ mhi_dev_ctxt->mhi_state == MHI_STATE_M1,
+ msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
+ if (!r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to get M0 event, timeout\n");
+ r = -EIO;
+ } else
+ r = 0;
+
+ return r;
+}
+
+int mhi_runtime_suspend(struct device *dev)
+{
+ int r = 0;
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev);
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enter\n");
+
+ mutex_lock(&mhi_dev_ctxt->pm_lock);
+ r = mhi_pm_initiate_m3(mhi_dev_ctxt, false);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "abort due to ret:%d\n", r);
+ mutex_unlock(&mhi_dev_ctxt->pm_lock);
+ return r;
+ }
r = mhi_turn_off_pcie_link(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to Turn off link ret:%d\n", r);
}
-rpm_suspend_exit:
- mhi_log(MHI_MSG_INFO, "Exited\n");
mutex_unlock(&mhi_dev_ctxt->pm_lock);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited with ret:%d\n", r);
+
return r;
}
int mhi_runtime_idle(struct device *dev)
{
- mhi_log(MHI_MSG_INFO, "Entered returning -EBUSY\n");
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev);
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Entered returning -EBUSY\n");
/*
* RPM framework during runtime resume always calls
@@ -150,7 +201,7 @@ int mhi_runtime_idle(struct device *dev)
int mhi_runtime_resume(struct device *dev)
{
int r = 0;
- struct mhi_device_ctxt *mhi_dev_ctxt = dev->platform_data;
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev);
mutex_lock(&mhi_dev_ctxt->pm_lock);
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
@@ -159,45 +210,24 @@ int mhi_runtime_resume(struct device *dev)
/* turn on link */
r = mhi_turn_on_pcie_link(mhi_dev_ctxt);
- if (r) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to resume link\n");
- goto rpm_resume_exit;
- }
-
- write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_dev_ctxt->mhi_pm_state = MHI_PM_M3_EXIT;
- write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
-
- /* Set and wait for M0 Event */
- write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M0);
- write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event,
- mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
- mhi_dev_ctxt->mhi_state == MHI_STATE_M1,
- msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
- if (!r) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to get M0 event, timeout\n");
- r = -EIO;
+ if (r)
goto rpm_resume_exit;
- }
- r = 0; /* no errors */
+ r = mhi_pm_initiate_m0(mhi_dev_ctxt);
rpm_resume_exit:
mutex_unlock(&mhi_dev_ctxt->pm_lock);
- mhi_log(MHI_MSG_INFO, "Exited with :%d\n", r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited with :%d\n", r);
return r;
}
int mhi_pci_resume(struct device *dev)
{
int r = 0;
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev);
r = mhi_runtime_resume(dev);
if (r) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to resume link\n");
} else {
pm_runtime_set_active(dev);
@@ -207,6 +237,97 @@ int mhi_pci_resume(struct device *dev)
return r;
}
+static int mhi_pm_slave_mode_power_on(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ int ret_val;
+ u32 timeout = mhi_dev_ctxt->poll_reset_timeout_ms;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n");
+ mutex_lock(&mhi_dev_ctxt->pm_lock);
+ write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->mhi_pm_state = MHI_PM_POR;
+ ret_val = set_mhi_base_state(mhi_dev_ctxt);
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+
+ if (ret_val) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error Setting MHI Base State %d\n", ret_val);
+ goto unlock_pm_lock;
+ }
+
+ if (mhi_dev_ctxt->base_state != STATE_TRANSITION_BHI) {
+ ret_val = -EIO;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Invalid Base State, cur_state:%s\n",
+ state_transition_str(mhi_dev_ctxt->base_state));
+ goto unlock_pm_lock;
+ }
+
+ reinit_completion(&mhi_dev_ctxt->cmd_complete);
+ init_mhi_base_state(mhi_dev_ctxt);
+
+ /*
+ * Keep wake in Active until AMSS, @ AMSS we will
+ * decrement counts
+ */
+ read_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
+ read_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+
+ ret_val = wait_for_completion_timeout(&mhi_dev_ctxt->cmd_complete,
+ msecs_to_jiffies(timeout));
+ if (!ret_val || mhi_dev_ctxt->dev_exec_env != MHI_EXEC_ENV_AMSS)
+ ret_val = -EIO;
+ else
+ ret_val = 0;
+
+ if (ret_val) {
+ read_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
+ read_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ }
+
+unlock_pm_lock:
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit with ret:%d\n", ret_val);
+ mutex_unlock(&mhi_dev_ctxt->pm_lock);
+ return ret_val;
+}
+
+static int mhi_pm_slave_mode_suspend(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ int r;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n");
+ mutex_lock(&mhi_dev_ctxt->pm_lock);
+
+ r = mhi_pm_initiate_m3(mhi_dev_ctxt, false);
+ if (r)
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "abort due to ret:%d\n", r);
+ mutex_unlock(&mhi_dev_ctxt->pm_lock);
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit with ret:%d\n", r);
+
+ return r;
+}
+
+static int mhi_pm_slave_mode_resume(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ int r;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n");
+ mutex_lock(&mhi_dev_ctxt->pm_lock);
+
+ r = mhi_pm_initiate_m0(mhi_dev_ctxt);
+ if (r)
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "M3 exit failed ret:%d\n", r);
+ mutex_unlock(&mhi_dev_ctxt->pm_lock);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit with ret:%d\n", r);
+
+ return r;
+}
+
int mhi_init_pm_sysfs(struct device *dev)
{
return sysfs_create_group(&dev->kobj, &mhi_attribute_group);
@@ -220,12 +341,29 @@ void mhi_rem_pm_sysfs(struct device *dev)
ssize_t sysfs_init_m0(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct mhi_device_ctxt *mhi_dev_ctxt =
- &mhi_devices.device_list[0].mhi_ctxt;
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev);
+
+ pm_runtime_get(&mhi_dev_ctxt->pcie_device->dev);
+ pm_runtime_mark_last_busy(&mhi_dev_ctxt->pcie_device->dev);
+ pm_runtime_put_noidle(&mhi_dev_ctxt->pcie_device->dev);
+
+ return count;
+}
+
+ssize_t sysfs_init_m3(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev);
+
+ if (atomic_read(&mhi_dev_ctxt->counters.device_wake) == 0) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Schedule RPM suspend");
+ pm_runtime_mark_last_busy(&mhi_dev_ctxt->
+ pcie_device->dev);
+ pm_request_autosuspend(&mhi_dev_ctxt->
+ pcie_device->dev);
+ }
- pm_runtime_get(&mhi_dev_ctxt->dev_info->pcie_device->dev);
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->dev_info->pcie_device->dev);
- pm_runtime_put_noidle(&mhi_dev_ctxt->dev_info->pcie_device->dev);
return count;
}
@@ -234,45 +372,45 @@ int mhi_turn_off_pcie_link(struct mhi_device_ctxt *mhi_dev_ctxt)
struct pci_dev *pcie_dev;
int r = 0;
- mhi_log(MHI_MSG_INFO, "Entered...\n");
- pcie_dev = mhi_dev_ctxt->dev_info->pcie_device;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered...\n");
+ pcie_dev = mhi_dev_ctxt->pcie_device;
if (0 == mhi_dev_ctxt->flags.link_up) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Link already marked as down, nothing to do\n");
goto exit;
}
r = pci_save_state(pcie_dev);
if (r) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to save pcie state ret: %d\n",
- r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to save pcie state ret: %d\n", r);
}
- mhi_dev_ctxt->dev_props->pcie_state = pci_store_saved_state(pcie_dev);
+ mhi_dev_ctxt->core.pcie_state = pci_store_saved_state(pcie_dev);
pci_disable_device(pcie_dev);
r = pci_set_power_state(pcie_dev, PCI_D3hot);
if (r) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to set pcie power state to D3 hot ret: %d\n",
- r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Failed to set pcie power state to D3hot ret:%d\n", r);
}
r = msm_pcie_pm_control(MSM_PCIE_SUSPEND,
pcie_dev->bus->number,
pcie_dev,
- NULL,
- 0);
+ NULL,
+ 0);
if (r)
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Failed to suspend pcie bus ret 0x%x\n", r);
r = mhi_set_bus_request(mhi_dev_ctxt, 0);
if (r)
- mhi_log(MHI_MSG_INFO, "Failed to set bus freq ret %d\n", r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Failed to set bus freq ret %d\n", r);
mhi_dev_ctxt->flags.link_up = 0;
exit:
- mhi_log(MHI_MSG_INFO, "Exited...\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited...\n");
+
return 0;
}
@@ -281,17 +419,16 @@ int mhi_turn_on_pcie_link(struct mhi_device_ctxt *mhi_dev_ctxt)
int r = 0;
struct pci_dev *pcie_dev;
- pcie_dev = mhi_dev_ctxt->dev_info->pcie_device;
+ pcie_dev = mhi_dev_ctxt->pcie_device;
- mhi_log(MHI_MSG_INFO, "Entered...\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered...\n");
if (mhi_dev_ctxt->flags.link_up)
goto exit;
r = mhi_set_bus_request(mhi_dev_ctxt, 1);
if (r)
- mhi_log(MHI_MSG_CRITICAL,
- "Could not set bus frequency ret: %d\n",
- r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Could not set bus frequency ret: %d\n", r);
r = msm_pcie_pm_control(MSM_PCIE_RESUME,
pcie_dev->bus->number,
@@ -299,24 +436,50 @@ int mhi_turn_on_pcie_link(struct mhi_device_ctxt *mhi_dev_ctxt)
NULL,
0);
if (r) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to resume pcie bus ret %d\n", r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to resume pcie bus ret %d\n", r);
goto exit;
}
r = pci_enable_device(pcie_dev);
if (r)
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to enable device ret:%d\n",
- r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to enable device ret:%d\n", r);
pci_load_and_free_saved_state(pcie_dev,
- &mhi_dev_ctxt->dev_props->pcie_state);
+ &mhi_dev_ctxt->core.pcie_state);
pci_restore_state(pcie_dev);
pci_set_master(pcie_dev);
mhi_dev_ctxt->flags.link_up = 1;
exit:
- mhi_log(MHI_MSG_INFO, "Exited...\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited...\n");
return r;
}
+
+int mhi_pm_control_device(struct mhi_device *mhi_device,
+ enum mhi_dev_ctrl ctrl)
+{
+ struct mhi_device_ctxt *mhi_dev_ctxt = mhi_device->mhi_dev_ctxt;
+
+ if (!mhi_dev_ctxt)
+ return -EINVAL;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Entered with cmd:%d\n", ctrl);
+
+ switch (ctrl) {
+ case MHI_DEV_CTRL_INIT:
+ return bhi_probe(mhi_dev_ctxt);
+ case MHI_DEV_CTRL_POWER_ON:
+ return mhi_pm_slave_mode_power_on(mhi_dev_ctxt);
+ case MHI_DEV_CTRL_SUSPEND:
+ return mhi_pm_slave_mode_suspend(mhi_dev_ctxt);
+ case MHI_DEV_CTRL_RESUME:
+ return mhi_pm_slave_mode_resume(mhi_dev_ctxt);
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(mhi_pm_control_device);
diff --git a/drivers/platform/msm/mhi/mhi_ring_ops.c b/drivers/platform/msm/mhi/mhi_ring_ops.c
index 07d0098a1b61..e15055f7db9c 100644
--- a/drivers/platform/msm/mhi/mhi_ring_ops.c
+++ b/drivers/platform/msm/mhi/mhi_ring_ops.c
@@ -21,7 +21,6 @@ static int add_element(struct mhi_ring *ring, void **rp,
if (NULL == ring || 0 == ring->el_size
|| NULL == ring->base || 0 == ring->len) {
- mhi_log(MHI_MSG_ERROR, "Bad input parameters, quitting.\n");
return -EINVAL;
}
@@ -39,8 +38,6 @@ static int add_element(struct mhi_ring *ring, void **rp,
if (ring->overwrite_en) {
ctxt_del_element(ring, NULL);
} else {
- mhi_log(MHI_MSG_INFO, "Ring 0x%lX is full\n",
- (uintptr_t)ring->base);
return -ENOSPC;
}
}
@@ -92,8 +89,6 @@ int delete_element(struct mhi_ring *ring, void **rp,
if (r)
return r;
if (d_wp == d_rp) {
- mhi_log(MHI_MSG_VERBOSE, "Ring 0x%lx is empty\n",
- (uintptr_t)ring->base);
if (NULL != assigned_addr)
*assigned_addr = NULL;
return -ENODATA;
@@ -113,23 +108,26 @@ int delete_element(struct mhi_ring *ring, void **rp,
int mhi_get_free_desc(struct mhi_client_handle *client_handle)
{
u32 chan;
+ struct mhi_client_config *client_config;
struct mhi_device_ctxt *ctxt;
int bb_ring, ch_ring;
- if (!client_handle || MHI_HANDLE_MAGIC != client_handle->magic ||
- !client_handle->mhi_dev_ctxt)
+ if (!client_handle)
return -EINVAL;
- ctxt = client_handle->mhi_dev_ctxt;
- chan = client_handle->chan_info.chan_nr;
+ client_config = client_handle->client_config;
+ ctxt = client_config->mhi_dev_ctxt;
+ chan = client_config->chan_info.chan_nr;
- bb_ring = get_nr_avail_ring_elements(&ctxt->chan_bb_list[chan]);
- ch_ring = get_nr_avail_ring_elements(&ctxt->mhi_local_chan_ctxt[chan]);
+ bb_ring = get_nr_avail_ring_elements(ctxt, &ctxt->chan_bb_list[chan]);
+ ch_ring = get_nr_avail_ring_elements(ctxt,
+ &ctxt->mhi_local_chan_ctxt[chan]);
return min(bb_ring, ch_ring);
}
EXPORT_SYMBOL(mhi_get_free_desc);
-int get_nr_avail_ring_elements(struct mhi_ring *ring)
+int get_nr_avail_ring_elements(struct mhi_device_ctxt *mhi_dev_ctxt,
+ struct mhi_ring *ring)
{
u32 nr_el = 0;
uintptr_t ring_size = 0;
@@ -138,7 +136,7 @@ int get_nr_avail_ring_elements(struct mhi_ring *ring)
ring_size = ring->len / ring->el_size;
ret_val = get_nr_enclosed_el(ring, ring->rp, ring->wp, &nr_el);
if (ret_val != 0) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to get enclosed el ret %d.\n", ret_val);
return 0;
}
@@ -155,19 +153,14 @@ int get_nr_enclosed_el(struct mhi_ring *ring, void *rp,
if (NULL == ring || 0 == ring->el_size ||
NULL == ring->base || 0 == ring->len) {
- mhi_log(MHI_MSG_ERROR, "Bad input parameters, quitting.\n");
return -EINVAL;
}
r = get_element_index(ring, rp, &index_rp);
- if (r) {
- mhi_log(MHI_MSG_CRITICAL, "Bad element index rp 0x%p.\n", rp);
+ if (r)
return r;
- }
r = get_element_index(ring, wp, &index_wp);
- if (r) {
- mhi_log(MHI_MSG_CRITICAL, "Bad element index wp 0x%p.\n", wp);
+ if (r)
return r;
- }
ring_size = ring->len / ring->el_size;
if (index_rp < index_wp)
diff --git a/drivers/platform/msm/mhi/mhi_ssr.c b/drivers/platform/msm/mhi/mhi_ssr.c
index defd6f4fd137..22481dede21a 100644
--- a/drivers/platform/msm/mhi/mhi_ssr.c
+++ b/drivers/platform/msm/mhi/mhi_ssr.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -24,40 +24,35 @@
static int mhi_ssr_notify_cb(struct notifier_block *nb,
unsigned long action, void *data)
{
-
+ struct mhi_device_ctxt *mhi_dev_ctxt =
+ container_of(nb, struct mhi_device_ctxt, mhi_ssr_nb);
switch (action) {
case SUBSYS_BEFORE_POWERUP:
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Received Subsystem event BEFORE_POWERUP\n");
break;
case SUBSYS_AFTER_POWERUP:
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Received Subsystem event AFTER_POWERUP\n");
break;
case SUBSYS_POWERUP_FAILURE:
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Received Subsystem event POWERUP_FAILURE\n");
break;
case SUBSYS_BEFORE_SHUTDOWN:
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Received Subsystem event BEFORE_SHUTDOWN\n");
- mhi_log(MHI_MSG_INFO,
- "Not notifying clients\n");
break;
case SUBSYS_AFTER_SHUTDOWN:
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Received Subsystem event AFTER_SHUTDOWN\n");
- mhi_log(MHI_MSG_INFO,
- "Not notifying clients\n");
break;
case SUBSYS_RAMDUMP_NOTIFICATION:
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Received Subsystem event RAMDUMP\n");
- mhi_log(MHI_MSG_INFO,
- "Not notifying clients\n");
break;
default:
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Received ESOC notifcation %d, NOT handling\n",
(int)action);
break;
@@ -65,36 +60,30 @@ static int mhi_ssr_notify_cb(struct notifier_block *nb,
return NOTIFY_OK;
}
-static struct notifier_block mhi_ssr_nb = {
- .notifier_call = mhi_ssr_notify_cb,
-};
-
int mhi_esoc_register(struct mhi_device_ctxt *mhi_dev_ctxt)
{
int ret_val = 0;
struct device_node *np;
- struct pci_driver *mhi_driver;
- struct device *dev = &mhi_dev_ctxt->dev_info->pcie_device->dev;
+ struct device *dev = &mhi_dev_ctxt->pcie_device->dev;
- mhi_driver = mhi_dev_ctxt->dev_info->mhi_pcie_driver;
np = dev->of_node;
mhi_dev_ctxt->esoc_handle = devm_register_esoc_client(dev, "mdm");
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Of table of pcie struct device property is dev->of_node %p\n",
np);
if (IS_ERR_OR_NULL(mhi_dev_ctxt->esoc_handle)) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to register for SSR, ret %lx\n",
(uintptr_t)mhi_dev_ctxt->esoc_handle);
return -EIO;
}
-
+ mhi_dev_ctxt->mhi_ssr_nb.notifier_call = mhi_ssr_notify_cb;
mhi_dev_ctxt->esoc_ssr_handle = subsys_notif_register_notifier(
mhi_dev_ctxt->esoc_handle->name,
- &mhi_ssr_nb);
+ &mhi_dev_ctxt->mhi_ssr_nb);
if (IS_ERR_OR_NULL(mhi_dev_ctxt->esoc_ssr_handle)) {
ret_val = PTR_RET(mhi_dev_ctxt->esoc_ssr_handle);
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Can't find esoc desc ret 0x%lx\n",
(uintptr_t)mhi_dev_ctxt->esoc_ssr_handle);
}
@@ -107,18 +96,25 @@ void mhi_notify_client(struct mhi_client_handle *client_handle,
{
struct mhi_cb_info cb_info = {0};
struct mhi_result result = {0};
+ struct mhi_client_config *client_config;
cb_info.result = NULL;
cb_info.cb_reason = reason;
- if (NULL != client_handle &&
- NULL != client_handle->client_info.mhi_client_cb) {
- result.user_data = client_handle->user_data;
- cb_info.chan = client_handle->chan_info.chan_nr;
+ if (client_handle == NULL)
+ return;
+
+ client_config = client_handle->client_config;
+
+ if (client_config->client_info.mhi_client_cb) {
+ result.user_data = client_config->user_data;
+ cb_info.chan = client_config->chan_info.chan_nr;
cb_info.result = &result;
- mhi_log(MHI_MSG_INFO, "Calling back for chan %d, reason %d\n",
- cb_info.chan, reason);
- client_handle->client_info.mhi_client_cb(&cb_info);
+ mhi_log(client_config->mhi_dev_ctxt, MHI_MSG_INFO,
+ "Calling back for chan %d, reason %d\n",
+ cb_info.chan,
+ reason);
+ client_config->client_info.mhi_client_cb(&cb_info);
}
}
@@ -136,16 +132,22 @@ void mhi_notify_clients(struct mhi_device_ctxt *mhi_dev_ctxt,
}
}
-int set_mhi_base_state(struct mhi_pcie_dev_info *mhi_pcie_dev)
+int set_mhi_base_state(struct mhi_device_ctxt *mhi_dev_ctxt)
{
u32 pcie_word_val = 0;
int r = 0;
- struct mhi_device_ctxt *mhi_dev_ctxt = &mhi_pcie_dev->mhi_ctxt;
- mhi_pcie_dev->bhi_ctxt.bhi_base = mhi_pcie_dev->core.bar0_base;
- pcie_word_val = mhi_reg_read(mhi_pcie_dev->bhi_ctxt.bhi_base, BHIOFF);
- mhi_pcie_dev->bhi_ctxt.bhi_base += pcie_word_val;
- pcie_word_val = mhi_reg_read(mhi_pcie_dev->bhi_ctxt.bhi_base,
+ mhi_dev_ctxt->bhi_ctxt.bhi_base = mhi_dev_ctxt->core.bar0_base;
+ pcie_word_val = mhi_reg_read(mhi_dev_ctxt->bhi_ctxt.bhi_base, BHIOFF);
+
+ /* confirm it's a valid reading */
+ if (unlikely(pcie_word_val == U32_MAX)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Invalid BHI Offset:0x%x\n", pcie_word_val);
+ return -EIO;
+ }
+ mhi_dev_ctxt->bhi_ctxt.bhi_base += pcie_word_val;
+ pcie_word_val = mhi_reg_read(mhi_dev_ctxt->bhi_ctxt.bhi_base,
BHI_EXECENV);
mhi_dev_ctxt->dev_exec_env = pcie_word_val;
if (pcie_word_val == MHI_EXEC_ENV_AMSS) {
@@ -153,55 +155,50 @@ int set_mhi_base_state(struct mhi_pcie_dev_info *mhi_pcie_dev)
} else if (pcie_word_val == MHI_EXEC_ENV_PBL) {
mhi_dev_ctxt->base_state = STATE_TRANSITION_BHI;
} else {
- mhi_log(MHI_MSG_ERROR, "Invalid EXEC_ENV: 0x%x\n",
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Invalid EXEC_ENV: 0x%x\n",
pcie_word_val);
r = -EIO;
}
- mhi_log(MHI_MSG_INFO, "EXEC_ENV: %d Base state %d\n",
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "EXEC_ENV: %d Base state %d\n",
pcie_word_val, mhi_dev_ctxt->base_state);
return r;
}
void mhi_link_state_cb(struct msm_pcie_notify *notify)
{
-
- struct mhi_pcie_dev_info *mhi_pcie_dev;
struct mhi_device_ctxt *mhi_dev_ctxt = NULL;
- if (NULL == notify || NULL == notify->data) {
- mhi_log(MHI_MSG_CRITICAL,
- "Incomplete handle received\n");
+ if (!notify || !notify->data) {
+ pr_err("%s: incomplete handle received\n", __func__);
return;
}
- mhi_pcie_dev = notify->data;
- mhi_dev_ctxt = &mhi_pcie_dev->mhi_ctxt;
+ mhi_dev_ctxt = notify->data;
switch (notify->event) {
case MSM_PCIE_EVENT_LINKDOWN:
- mhi_log(MHI_MSG_INFO, "Received MSM_PCIE_EVENT_LINKDOWN\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Received MSM_PCIE_EVENT_LINKDOWN\n");
break;
case MSM_PCIE_EVENT_LINKUP:
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Received MSM_PCIE_EVENT_LINKUP\n");
- mhi_pcie_dev->link_up_cntr++;
+ mhi_dev_ctxt->counters.link_up_cntr++;
break;
case MSM_PCIE_EVENT_WAKEUP:
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Received MSM_PCIE_EVENT_WAKE\n");
__pm_stay_awake(&mhi_dev_ctxt->w_lock);
__pm_relax(&mhi_dev_ctxt->w_lock);
if (mhi_dev_ctxt->flags.mhi_initialized) {
- pm_runtime_get(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
- pm_runtime_put_noidle(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
+ mhi_dev_ctxt->runtime_get(mhi_dev_ctxt);
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
}
break;
default:
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Received bad link event\n");
return;
}
@@ -213,9 +210,9 @@ int init_mhi_base_state(struct mhi_device_ctxt *mhi_dev_ctxt)
r = mhi_init_state_transition(mhi_dev_ctxt, mhi_dev_ctxt->base_state);
if (r) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to start state change event, to %d\n",
- mhi_dev_ctxt->base_state);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to start state change event, to %d\n",
+ mhi_dev_ctxt->base_state);
}
return r;
}
diff --git a/drivers/platform/msm/mhi/mhi_states.c b/drivers/platform/msm/mhi/mhi_states.c
index 1021a56d1b3d..a4da6c21b50d 100644
--- a/drivers/platform/msm/mhi/mhi_states.c
+++ b/drivers/platform/msm/mhi/mhi_states.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,24 +19,24 @@
const char *state_transition_str(enum STATE_TRANSITION state)
{
- static const char * const mhi_states_transition_str[] = {
- "RESET",
- "READY",
- "M0",
- "M1",
- "M2",
- "M3",
- "BHI",
- "SBL",
- "AMSS",
- "LINK_DOWN",
- "WAKE"
+ static const char * const
+ mhi_states_transition_str[STATE_TRANSITION_MAX] = {
+ [STATE_TRANSITION_RESET] = "RESET",
+ [STATE_TRANSITION_READY] = "READY",
+ [STATE_TRANSITION_M0] = "M0",
+ [STATE_TRANSITION_M1] = "M1",
+ [STATE_TRANSITION_M2] = "M2",
+ [STATE_TRANSITION_M3] = "M3",
+ [STATE_TRANSITION_BHI] = "BHI",
+ [STATE_TRANSITION_SBL] = "SBL",
+ [STATE_TRANSITION_AMSS] = "AMSS",
+ [STATE_TRANSITION_LINK_DOWN] = "LINK_DOWN",
+ [STATE_TRANSITION_WAKE] = "WAKE",
+ [STATE_TRANSITION_BHIE] = "BHIE",
+ [STATE_TRANSITION_SYS_ERR] = "SYS_ERR",
};
- if (state == STATE_TRANSITION_SYS_ERR)
- return "SYS_ERR";
-
- return (state <= STATE_TRANSITION_WAKE) ?
+ return (state < STATE_TRANSITION_MAX) ?
mhi_states_transition_str[state] : "Invalid";
}
@@ -94,7 +94,7 @@ static void ring_all_chan_dbs(struct mhi_device_ctxt *mhi_dev_ctxt,
u32 i = 0;
struct mhi_ring *local_ctxt = NULL;
- mhi_log(MHI_MSG_VERBOSE, "Ringing chan dbs\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Ringing chan dbs\n");
for (i = 0; i < MHI_MAX_CHANNELS; ++i)
if (VALID_CHAN_NR(i)) {
local_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[i];
@@ -115,7 +115,7 @@ static void ring_all_cmd_dbs(struct mhi_device_ctxt *mhi_dev_ctxt)
u64 rp = 0;
struct mhi_ring *local_ctxt = NULL;
- mhi_log(MHI_MSG_VERBOSE, "Ringing chan dbs\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Ringing chan dbs\n");
local_ctxt = &mhi_dev_ctxt->mhi_local_cmd_ctxt[PRIMARY_CMD_RING];
rp = mhi_v2p_addr(mhi_dev_ctxt, MHI_RING_TYPE_CMD_RING,
@@ -158,12 +158,23 @@ static void ring_all_ev_dbs(struct mhi_device_ctxt *mhi_dev_ctxt)
}
}
-static int process_m0_transition(
- struct mhi_device_ctxt *mhi_dev_ctxt,
- enum STATE_TRANSITION cur_work_item)
+static int process_bhie_transition(struct mhi_device_ctxt *mhi_dev_ctxt,
+ enum STATE_TRANSITION cur_work_item)
{
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n");
+ mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_BHIE;
+ wake_up(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
+
+ return 0;
+}
- mhi_log(MHI_MSG_INFO, "Entered With State %s\n",
+int process_m0_transition(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ unsigned long flags;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Entered With State %s\n",
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
switch (mhi_dev_ctxt->mhi_state) {
@@ -177,12 +188,12 @@ static int process_m0_transition(
break;
}
- write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ write_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags);
mhi_dev_ctxt->mhi_state = MHI_STATE_M0;
mhi_dev_ctxt->mhi_pm_state = MHI_PM_M0;
- write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ write_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_assert_device_wake(mhi_dev_ctxt, true);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, true);
if (mhi_dev_ctxt->flags.mhi_initialized) {
ring_all_ev_dbs(mhi_dev_ctxt);
@@ -190,10 +201,11 @@ static int process_m0_transition(
ring_all_cmd_dbs(mhi_dev_ctxt);
}
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
wake_up(mhi_dev_ctxt->mhi_ev_wq.m0_event);
- mhi_log(MHI_MSG_INFO, "Exited\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
+
return 0;
}
@@ -207,7 +219,7 @@ void process_m1_transition(struct work_struct *work)
mutex_lock(&mhi_dev_ctxt->pm_lock);
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Processing M1 state transition from state %s\n",
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
@@ -218,7 +230,8 @@ void process_m1_transition(struct work_struct *work)
return;
}
- mhi_log(MHI_MSG_INFO, "Transitioning to M2 Transition\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Transitioning to M2 Transition\n");
mhi_dev_ctxt->mhi_pm_state = MHI_PM_M1_M2_TRANSITION;
mhi_dev_ctxt->counters.m1_m2++;
mhi_dev_ctxt->mhi_state = MHI_STATE_M2;
@@ -230,34 +243,32 @@ void process_m1_transition(struct work_struct *work)
/* During DEBOUNCE Time We could be receiving M0 Event */
if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_M1_M2_TRANSITION) {
- mhi_log(MHI_MSG_INFO, "Entered M2 State\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Entered M2 State\n");
mhi_dev_ctxt->mhi_pm_state = MHI_PM_M2;
}
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
if (unlikely(atomic_read(&mhi_dev_ctxt->counters.device_wake))) {
- mhi_log(MHI_MSG_INFO, "Exiting M2 Immediately, count:%d\n",
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Exiting M2 Immediately, count:%d\n",
atomic_read(&mhi_dev_ctxt->counters.device_wake));
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_assert_device_wake(mhi_dev_ctxt, true);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, true);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- } else {
- mhi_log(MHI_MSG_INFO, "Schedule RPM suspend");
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
- pm_request_autosuspend(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
+ } else if (mhi_dev_ctxt->core.pci_master) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Schedule RPM suspend");
+ pm_runtime_mark_last_busy(&mhi_dev_ctxt->pcie_device->dev);
+ pm_request_autosuspend(&mhi_dev_ctxt->pcie_device->dev);
}
mutex_unlock(&mhi_dev_ctxt->pm_lock);
}
-static int process_m3_transition(
- struct mhi_device_ctxt *mhi_dev_ctxt,
- enum STATE_TRANSITION cur_work_item)
+int process_m3_transition(struct mhi_device_ctxt *mhi_dev_ctxt)
{
-
- mhi_log(MHI_MSG_INFO,
+ unsigned long flags;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Entered with State %s\n",
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
@@ -272,45 +283,24 @@ static int process_m3_transition(
break;
}
- write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ write_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags);
mhi_dev_ctxt->mhi_state = MHI_STATE_M3;
mhi_dev_ctxt->mhi_pm_state = MHI_PM_M3;
- write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ write_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
wake_up(mhi_dev_ctxt->mhi_ev_wq.m3_event);
return 0;
}
-static int process_link_down_transition(
- struct mhi_device_ctxt *mhi_dev_ctxt,
- enum STATE_TRANSITION cur_work_item)
-{
- mhi_log(MHI_MSG_INFO,
- "Entered with State %s\n",
- TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
- return -EIO;
-}
-
-static int process_wake_transition(
- struct mhi_device_ctxt *mhi_dev_ctxt,
- enum STATE_TRANSITION cur_work_item)
-{
- mhi_log(MHI_MSG_INFO,
- "Entered with State %s\n",
- TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
- return -EIO;
-
-}
-
static int process_bhi_transition(
struct mhi_device_ctxt *mhi_dev_ctxt,
enum STATE_TRANSITION cur_work_item)
{
- mhi_log(MHI_MSG_INFO, "Entered\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n");
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
mhi_dev_ctxt->mhi_state = MHI_STATE_BHI;
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
wake_up_interruptible(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
- mhi_log(MHI_MSG_INFO, "Exited\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
return 0;
}
@@ -320,11 +310,12 @@ static int process_ready_transition(
{
int r = 0;
- mhi_log(MHI_MSG_INFO, "Processing READY state transition\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Processing READY state transition\n");
r = mhi_reset_all_thread_queues(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to reset thread queues\n");
return r;
}
@@ -335,7 +326,7 @@ static int process_ready_transition(
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
/* Initialize MMIO */
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failure during MMIO initialization\n");
return r;
}
@@ -344,13 +335,12 @@ static int process_ready_transition(
cur_work_item);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failure during event ring init\n");
return r;
}
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_dev_ctxt->flags.stop_threads = 0;
mhi_reg_write_field(mhi_dev_ctxt,
mhi_dev_ctxt->mmio_info.mmio_addr, MHICTRL,
MHICTRL_MHISTATE_MASK,
@@ -379,7 +369,8 @@ static int process_reset_transition(
enum STATE_TRANSITION cur_work_item)
{
int r = 0, i = 0;
- mhi_log(MHI_MSG_INFO, "Processing RESET state transition\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Processing RESET state transition\n");
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
mhi_dev_ctxt->mhi_state = MHI_STATE_RESET;
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
@@ -387,11 +378,12 @@ static int process_reset_transition(
mhi_dev_ctxt->counters.mhi_reset_cntr++;
r = mhi_test_for_device_reset(mhi_dev_ctxt);
if (r)
- mhi_log(MHI_MSG_INFO, "Device not RESET ret %d\n", r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Device not RESET ret %d\n", r);
r = mhi_test_for_device_ready(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR, "timed out waiting for ready ret:%d\n",
- r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "timed out waiting for ready ret:%d\n", r);
return r;
}
@@ -417,22 +409,12 @@ static int process_reset_transition(
r = mhi_init_state_transition(mhi_dev_ctxt,
STATE_TRANSITION_READY);
if (0 != r)
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to initiate %s state trans\n",
state_transition_str(STATE_TRANSITION_READY));
return r;
}
-static int process_syserr_transition(
- struct mhi_device_ctxt *mhi_dev_ctxt,
- enum STATE_TRANSITION cur_work_item)
-{
- mhi_log(MHI_MSG_INFO,
- "Entered with State %s\n",
- TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
- return -EIO;
-}
-
static void enable_clients(struct mhi_device_ctxt *mhi_dev_ctxt,
enum MHI_EXEC_ENV exec_env)
{
@@ -443,7 +425,8 @@ static void enable_clients(struct mhi_device_ctxt *mhi_dev_ctxt,
cb_info.cb_reason = MHI_CB_MHI_ENABLED;
- mhi_log(MHI_MSG_INFO, "Enabling Clients, exec env %d.\n", exec_env);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Enabling Clients, exec env %d.\n", exec_env);
for (i = 0; i < MHI_MAX_CHANNELS; ++i) {
if (!VALID_CHAN_NR(i))
continue;
@@ -455,14 +438,15 @@ static void enable_clients(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_notify_client(client_handle, MHI_CB_MHI_ENABLED);
}
- mhi_log(MHI_MSG_INFO, "Done.\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Done.\n");
}
static int process_sbl_transition(
struct mhi_device_ctxt *mhi_dev_ctxt,
enum STATE_TRANSITION cur_work_item)
{
- mhi_log(MHI_MSG_INFO, "Enabled\n");
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enabled\n");
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_SBL;
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
@@ -476,7 +460,8 @@ static int process_amss_transition(
{
int r = 0;
- mhi_log(MHI_MSG_INFO, "Processing AMSS state transition\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Processing AMSS state transition\n");
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_AMSS;
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
@@ -486,41 +471,40 @@ static int process_amss_transition(
cur_work_item);
mhi_dev_ctxt->flags.mhi_initialized = 1;
if (r) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to set local chan state ret %d\n", r);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
return r;
}
- read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- ring_all_chan_dbs(mhi_dev_ctxt, true);
- read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Notifying clients that MHI is enabled\n");
enable_clients(mhi_dev_ctxt, mhi_dev_ctxt->dev_exec_env);
} else {
- mhi_log(MHI_MSG_INFO, "MHI is initialized\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "MHI is initialized\n");
}
- read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- ring_all_ev_dbs(mhi_dev_ctxt);
- read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
+ complete(&mhi_dev_ctxt->cmd_complete);
/*
* runtime_allow will decrement usage_count, counts were
* incremented by pci fw pci_pm_init() or by
* mhi shutdown/ssr apis.
*/
- mhi_log(MHI_MSG_INFO, "Allow runtime suspend\n");
+ if (mhi_dev_ctxt->core.pci_master) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Allow runtime suspend\n");
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->dev_info->pcie_device->dev);
- pm_runtime_allow(&mhi_dev_ctxt->dev_info->pcie_device->dev);
+ pm_runtime_mark_last_busy(&mhi_dev_ctxt->pcie_device->dev);
+ pm_runtime_allow(&mhi_dev_ctxt->pcie_device->dev);
+ }
/* During probe we incremented, releasing that count */
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_log(MHI_MSG_INFO, "Exited\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
return 0;
}
@@ -530,7 +514,8 @@ static int process_stt_work_item(
{
int r = 0;
- mhi_log(MHI_MSG_INFO, "Transitioning to %s\n",
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Transitioning to %s\n",
state_transition_str(cur_work_item));
trace_mhi_state(cur_work_item);
switch (cur_work_item) {
@@ -549,25 +534,11 @@ static int process_stt_work_item(
case STATE_TRANSITION_AMSS:
r = process_amss_transition(mhi_dev_ctxt, cur_work_item);
break;
- case STATE_TRANSITION_M0:
- r = process_m0_transition(mhi_dev_ctxt, cur_work_item);
- break;
- case STATE_TRANSITION_M3:
- r = process_m3_transition(mhi_dev_ctxt, cur_work_item);
- break;
- case STATE_TRANSITION_SYS_ERR:
- r = process_syserr_transition(mhi_dev_ctxt,
- cur_work_item);
- break;
- case STATE_TRANSITION_LINK_DOWN:
- r = process_link_down_transition(mhi_dev_ctxt,
- cur_work_item);
- break;
- case STATE_TRANSITION_WAKE:
- r = process_wake_transition(mhi_dev_ctxt, cur_work_item);
+ case STATE_TRANSITION_BHIE:
+ r = process_bhie_transition(mhi_dev_ctxt, cur_work_item);
break;
default:
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Unrecongized state: %s\n",
state_transition_str(cur_work_item));
break;
@@ -575,46 +546,26 @@ static int process_stt_work_item(
return r;
}
-int mhi_state_change_thread(void *ctxt)
+void mhi_state_change_worker(struct work_struct *work)
{
int r = 0;
- unsigned long flags = 0;
- struct mhi_device_ctxt *mhi_dev_ctxt = (struct mhi_device_ctxt *)ctxt;
+ struct mhi_device_ctxt *mhi_dev_ctxt = container_of(work,
+ struct mhi_device_ctxt,
+ st_thread_worker);
enum STATE_TRANSITION cur_work_item;
struct mhi_state_work_queue *work_q =
&mhi_dev_ctxt->state_change_work_item_list;
struct mhi_ring *state_change_q = &work_q->q_info;
- if (NULL == mhi_dev_ctxt) {
- mhi_log(MHI_MSG_ERROR, "Got bad context, quitting\n");
- return -EIO;
- }
- for (;;) {
- r = wait_event_interruptible(
- *mhi_dev_ctxt->mhi_ev_wq.state_change_event,
- ((work_q->q_info.rp != work_q->q_info.wp) &&
- !mhi_dev_ctxt->flags.st_thread_stopped));
- if (r) {
- mhi_log(MHI_MSG_INFO,
- "Caught signal %d, quitting\n", r);
- return 0;
- }
-
- if (mhi_dev_ctxt->flags.kill_threads) {
- mhi_log(MHI_MSG_INFO,
- "Caught exit signal, quitting\n");
- return 0;
- }
- mhi_dev_ctxt->flags.st_thread_stopped = 0;
- spin_lock_irqsave(work_q->q_lock, flags);
+ while (work_q->q_info.rp != work_q->q_info.wp) {
+ spin_lock_irq(work_q->q_lock);
cur_work_item = *(enum STATE_TRANSITION *)(state_change_q->rp);
r = ctxt_del_element(&work_q->q_info, NULL);
MHI_ASSERT(r == 0,
"Failed to delete element from STT workqueue\n");
- spin_unlock_irqrestore(work_q->q_lock, flags);
+ spin_unlock_irq(work_q->q_lock);
r = process_stt_work_item(mhi_dev_ctxt, cur_work_item);
}
- return 0;
}
/**
@@ -638,16 +589,17 @@ int mhi_init_state_transition(struct mhi_device_ctxt *mhi_dev_ctxt,
&mhi_dev_ctxt->state_change_work_item_list;
spin_lock_irqsave(work_q->q_lock, flags);
- nr_avail_work_items = get_nr_avail_ring_elements(stt_ring);
+ nr_avail_work_items =
+ get_nr_avail_ring_elements(mhi_dev_ctxt, stt_ring);
BUG_ON(nr_avail_work_items <= 0);
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Processing state transition %s\n",
state_transition_str(new_state));
*(enum STATE_TRANSITION *)stt_ring->wp = new_state;
r = ctxt_add_element(stt_ring, (void **)&cur_work_item);
BUG_ON(r);
spin_unlock_irqrestore(work_q->q_lock, flags);
- wake_up_interruptible(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
+ schedule_work(&mhi_dev_ctxt->st_thread_worker);
return r;
}
diff --git a/drivers/platform/msm/mhi/mhi_sys.c b/drivers/platform/msm/mhi/mhi_sys.c
index c5c025b8585a..3389de2f95b3 100644
--- a/drivers/platform/msm/mhi/mhi_sys.c
+++ b/drivers/platform/msm/mhi/mhi_sys.c
@@ -51,13 +51,13 @@ static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf,
{
int amnt_copied = 0;
struct mhi_chan_ctxt *chan_ctxt;
- struct mhi_device_ctxt *mhi_dev_ctxt =
- &mhi_devices.device_list[0].mhi_ctxt;
+ struct mhi_device_ctxt *mhi_dev_ctxt = fp->private_data;
uintptr_t v_wp_index;
uintptr_t v_rp_index;
int valid_chan = 0;
struct mhi_chan_ctxt *cc_list;
struct mhi_client_handle *client_handle;
+ struct mhi_client_config *client_config;
int pkts_queued;
if (NULL == mhi_dev_ctxt)
@@ -76,6 +76,7 @@ static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf,
continue;
}
client_handle = mhi_dev_ctxt->client_handle_list[*offp];
+ client_config = client_handle->client_config;
valid_chan = 1;
}
@@ -87,8 +88,9 @@ static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf,
mhi_dev_ctxt->mhi_local_chan_ctxt[*offp].wp,
&v_wp_index);
- pkts_queued = client_handle->chan_info.max_desc -
- get_nr_avail_ring_elements(&mhi_dev_ctxt->
+ pkts_queued = client_config->chan_info.max_desc -
+ get_nr_avail_ring_elements(mhi_dev_ctxt,
+ &mhi_dev_ctxt->
mhi_local_chan_ctxt[*offp]) - 1;
amnt_copied =
scnprintf(mhi_dev_ctxt->chan_info,
@@ -115,7 +117,7 @@ static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf,
"pkts_queued",
pkts_queued,
"/",
- client_handle->chan_info.max_desc,
+ client_config->chan_info.max_desc,
"bb_used:",
mhi_dev_ctxt->counters.bb_used[*offp]);
@@ -128,9 +130,16 @@ static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf,
return -ENOMEM;
}
+int mhi_dbgfs_open(struct inode *inode, struct file *fp)
+{
+ fp->private_data = inode->i_private;
+ return 0;
+}
+
static const struct file_operations mhi_dbgfs_chan_fops = {
.read = mhi_dbgfs_chan_read,
.write = NULL,
+ .open = mhi_dbgfs_open,
};
static ssize_t mhi_dbgfs_ev_read(struct file *fp, char __user *buf,
@@ -143,8 +152,7 @@ static ssize_t mhi_dbgfs_ev_read(struct file *fp, char __user *buf,
uintptr_t v_rp_index;
uintptr_t device_p_rp_index;
- struct mhi_device_ctxt *mhi_dev_ctxt =
- &mhi_devices.device_list[0].mhi_ctxt;
+ struct mhi_device_ctxt *mhi_dev_ctxt = fp->private_data;
if (NULL == mhi_dev_ctxt)
return -EIO;
*offp = (u32)(*offp) % mhi_dev_ctxt->mmio_info.nr_event_rings;
@@ -209,31 +217,15 @@ static ssize_t mhi_dbgfs_ev_read(struct file *fp, char __user *buf,
static const struct file_operations mhi_dbgfs_ev_fops = {
.read = mhi_dbgfs_ev_read,
.write = NULL,
-};
-
-static ssize_t mhi_dbgfs_trigger_msi(struct file *fp, const char __user *buf,
- size_t count, loff_t *offp)
-{
- u32 msi_nr = 0;
- void *irq_ctxt = &((mhi_devices.device_list[0]).pcie_device->dev);
-
- if (copy_from_user(&msi_nr, buf, sizeof(msi_nr)))
- return -ENOMEM;
- mhi_msi_handlr(msi_nr, irq_ctxt);
- return 0;
-}
-
-static const struct file_operations mhi_dbgfs_trigger_msi_fops = {
- .read = NULL,
- .write = mhi_dbgfs_trigger_msi,
+ .open = mhi_dbgfs_open,
};
static ssize_t mhi_dbgfs_state_read(struct file *fp, char __user *buf,
size_t count, loff_t *offp)
{
int amnt_copied = 0;
- struct mhi_device_ctxt *mhi_dev_ctxt =
- &mhi_devices.device_list[0].mhi_ctxt;
+ struct mhi_device_ctxt *mhi_dev_ctxt = fp->private_data;
+
if (NULL == mhi_dev_ctxt)
return -EIO;
msleep(100);
@@ -260,7 +252,7 @@ static ssize_t mhi_dbgfs_state_read(struct file *fp, char __user *buf,
"device_wake:",
atomic_read(&mhi_dev_ctxt->counters.device_wake),
"usage_count:",
- atomic_read(&mhi_dev_ctxt->dev_info->pcie_device->dev.
+ atomic_read(&mhi_dev_ctxt->pcie_device->dev.
power.usage_count),
"outbound_acks:",
atomic_read(&mhi_dev_ctxt->counters.outbound_acks));
@@ -275,63 +267,61 @@ static ssize_t mhi_dbgfs_state_read(struct file *fp, char __user *buf,
static const struct file_operations mhi_dbgfs_state_fops = {
.read = mhi_dbgfs_state_read,
.write = NULL,
+ .open = mhi_dbgfs_open,
};
int mhi_init_debugfs(struct mhi_device_ctxt *mhi_dev_ctxt)
{
struct dentry *mhi_chan_stats;
struct dentry *mhi_state_stats;
- struct dentry *mhi_msi_trigger;
struct dentry *mhi_ev_stats;
-
- mhi_dev_ctxt->mhi_parent_folder =
- debugfs_create_dir("mhi", NULL);
- if (mhi_dev_ctxt->mhi_parent_folder == NULL) {
- mhi_log(MHI_MSG_INFO, "Failed to create debugfs parent dir.\n");
+ const struct pcie_core_info *core = &mhi_dev_ctxt->core;
+ char node_name[32];
+
+ snprintf(node_name,
+ sizeof(node_name),
+ "%04x_%02u.%02u.%02u",
+ core->dev_id, core->domain, core->bus, core->slot);
+
+ mhi_dev_ctxt->child =
+ debugfs_create_dir(node_name, mhi_dev_ctxt->parent);
+ if (mhi_dev_ctxt->child == NULL) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to create debugfs parent dir.\n");
return -EIO;
}
mhi_chan_stats = debugfs_create_file("mhi_chan_stats",
0444,
- mhi_dev_ctxt->mhi_parent_folder,
+ mhi_dev_ctxt->child,
mhi_dev_ctxt,
&mhi_dbgfs_chan_fops);
if (mhi_chan_stats == NULL)
return -ENOMEM;
mhi_ev_stats = debugfs_create_file("mhi_ev_stats",
0444,
- mhi_dev_ctxt->mhi_parent_folder,
+ mhi_dev_ctxt->child,
mhi_dev_ctxt,
&mhi_dbgfs_ev_fops);
if (mhi_ev_stats == NULL)
goto clean_chan;
mhi_state_stats = debugfs_create_file("mhi_state_stats",
0444,
- mhi_dev_ctxt->mhi_parent_folder,
+ mhi_dev_ctxt->child,
mhi_dev_ctxt,
&mhi_dbgfs_state_fops);
if (mhi_state_stats == NULL)
goto clean_ev_stats;
- mhi_msi_trigger = debugfs_create_file("mhi_msi_trigger",
- 0444,
- mhi_dev_ctxt->mhi_parent_folder,
- mhi_dev_ctxt,
- &mhi_dbgfs_trigger_msi_fops);
- if (mhi_msi_trigger == NULL)
- goto clean_state;
mhi_dev_ctxt->chan_info = kmalloc(MHI_LOG_SIZE, GFP_KERNEL);
if (mhi_dev_ctxt->chan_info == NULL)
- goto clean_all;
+ goto clean_ev_stats;
return 0;
-clean_all:
- debugfs_remove(mhi_msi_trigger);
-clean_state:
- debugfs_remove(mhi_state_stats);
+
clean_ev_stats:
debugfs_remove(mhi_ev_stats);
clean_chan:
debugfs_remove(mhi_chan_stats);
- debugfs_remove(mhi_dev_ctxt->mhi_parent_folder);
+ debugfs_remove(mhi_dev_ctxt->child);
return -ENOMEM;
}
diff --git a/drivers/platform/msm/mhi/mhi_sys.h b/drivers/platform/msm/mhi/mhi_sys.h
index a948a2354de7..712647dc9f7c 100644
--- a/drivers/platform/msm/mhi/mhi_sys.h
+++ b/drivers/platform/msm/mhi/mhi_sys.h
@@ -38,12 +38,13 @@ extern void *mhi_ipc_log;
} \
} while (0)
-#define mhi_log(_msg_lvl, _msg, ...) do { \
+#define mhi_log(mhi_dev_ctxt, _msg_lvl, _msg, ...) do { \
if ((_msg_lvl) >= mhi_msg_lvl) \
pr_alert("[%s] " _msg, __func__, ##__VA_ARGS__);\
- if (mhi_ipc_log && ((_msg_lvl) >= mhi_ipc_log_lvl)) \
- ipc_log_string(mhi_ipc_log, \
- "[%s] " _msg, __func__, ##__VA_ARGS__); \
+ if (mhi_dev_ctxt->mhi_ipc_log && \
+ ((_msg_lvl) >= mhi_ipc_log_lvl)) \
+ ipc_log_string(mhi_dev_ctxt->mhi_ipc_log, \
+ "[%s] " _msg, __func__, ##__VA_ARGS__); \
} while (0)
extern const char * const mhi_states_str[MHI_STATE_LIMIT];
diff --git a/drivers/platform/msm/mhi_uci/mhi_uci.c b/drivers/platform/msm/mhi_uci/mhi_uci.c
index 96c4671f994f..0e28ebdd8fea 100644
--- a/drivers/platform/msm/mhi_uci/mhi_uci.c
+++ b/drivers/platform/msm/mhi_uci/mhi_uci.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -31,8 +31,6 @@
#define MHI_DEV_NODE_NAME_LEN 13
#define MHI_SOFTWARE_CLIENT_LIMIT 23
-#define TRE_TYPICAL_SIZE 0x1000
-#define TRE_MAX_SIZE 0xFFFF
#define MHI_UCI_IPC_LOG_PAGES (25)
#define MAX_NR_TRBS_PER_CHAN 10
@@ -129,9 +127,8 @@ struct uci_client {
struct mhi_uci_ctxt_t {
struct list_head node;
- struct platform_dev *pdev;
+ struct platform_device *pdev;
struct uci_client client_handles[MHI_SOFTWARE_CLIENT_LIMIT];
- struct mhi_client_info_t client_info;
dev_t dev_t;
struct mutex ctrl_mutex;
struct cdev cdev[MHI_SOFTWARE_CLIENT_LIMIT];
@@ -332,8 +329,8 @@ static int mhi_uci_send_packet(struct mhi_client_handle **client_handle,
return 0;
for (i = 0; i < nr_avail_trbs; ++i) {
- data_to_insert_now = min(data_left_to_insert,
- TRE_MAX_SIZE);
+ data_to_insert_now = min_t(size_t, data_left_to_insert,
+ uci_handle->out_attr.max_packet_size);
if (is_uspace_buf) {
data_loc = kmalloc(data_to_insert_now, GFP_KERNEL);
if (NULL == data_loc) {
@@ -1172,6 +1169,9 @@ static void uci_xfer_cb(struct mhi_cb_info *cb_info)
uci_handle = cb_info->result->user_data;
switch (cb_info->cb_reason) {
case MHI_CB_MHI_ENABLED:
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_INFO,
+ "MHI enabled CB received.\n");
atomic_set(&uci_handle->mhi_disabled, 0);
break;
case MHI_CB_MHI_DISABLED:
@@ -1202,9 +1202,11 @@ static void uci_xfer_cb(struct mhi_cb_info *cb_info)
}
}
-static int mhi_register_client(struct uci_client *mhi_client)
+static int mhi_register_client(struct uci_client *mhi_client,
+ struct device *dev)
{
int ret_val = 0;
+ struct mhi_client_info_t client_info;
uci_log(mhi_client->uci_ipc_log,
UCI_DBG_INFO,
@@ -1222,11 +1224,13 @@ static int mhi_register_client(struct uci_client *mhi_client)
UCI_DBG_INFO,
"Registering chan %d\n",
mhi_client->out_chan);
- ret_val = mhi_register_channel(&mhi_client->out_handle,
- mhi_client->out_chan,
- 0,
- &mhi_client->uci_ctxt->client_info,
- mhi_client);
+ client_info.dev = dev;
+ client_info.node_name = "qcom,mhi";
+ client_info.user_data = mhi_client;
+ client_info.mhi_client_cb = uci_xfer_cb;
+ client_info.chan = mhi_client->out_chan;
+ client_info.max_payload = mhi_client->out_attr.max_packet_size;
+ ret_val = mhi_register_channel(&mhi_client->out_handle, &client_info);
if (0 != ret_val)
uci_log(mhi_client->uci_ipc_log,
UCI_DBG_ERROR,
@@ -1238,11 +1242,9 @@ static int mhi_register_client(struct uci_client *mhi_client)
UCI_DBG_INFO,
"Registering chan %d\n",
mhi_client->in_chan);
- ret_val = mhi_register_channel(&mhi_client->in_handle,
- mhi_client->in_chan,
- 0,
- &mhi_client->uci_ctxt->client_info,
- mhi_client);
+ client_info.max_payload = mhi_client->in_attr.max_packet_size;
+ client_info.chan = mhi_client->in_chan;
+ ret_val = mhi_register_channel(&mhi_client->in_handle, &client_info);
if (0 != ret_val)
uci_log(mhi_client->uci_ipc_log,
UCI_DBG_ERROR,
@@ -1266,13 +1268,16 @@ static int mhi_uci_probe(struct platform_device *pdev)
struct mhi_uci_ctxt_t *uci_ctxt;
int ret_val;
int i;
- char node_name[16];
+ char node_name[32];
uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
UCI_DBG_INFO,
"Entered with pdev:%p\n",
pdev);
+ if (mhi_is_device_ready(&pdev->dev, "qcom,mhi") == false)
+ return -EPROBE_DEFER;
+
if (pdev->dev.of_node == NULL)
return -ENODEV;
@@ -1286,7 +1291,7 @@ static int mhi_uci_probe(struct platform_device *pdev)
if (!uci_ctxt)
return -ENOMEM;
- uci_ctxt->client_info.mhi_client_cb = uci_xfer_cb;
+ uci_ctxt->pdev = pdev;
mutex_init(&uci_ctxt->ctrl_mutex);
uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
@@ -1309,7 +1314,8 @@ static int mhi_uci_probe(struct platform_device *pdev)
uci_client->uci_ctxt = uci_ctxt;
if (uci_client->in_attr.uci_ownership) {
- ret_val = mhi_register_client(uci_client);
+ ret_val = mhi_register_client(uci_client,
+ &pdev->dev);
if (ret_val) {
uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
UCI_DBG_CRITICAL,
@@ -1319,7 +1325,13 @@ static int mhi_uci_probe(struct platform_device *pdev)
return -EIO;
}
- snprintf(node_name, sizeof(node_name), "mhi-uci%d",
+ snprintf(node_name,
+ sizeof(node_name),
+ "mhi_uci_%04x_%02u.%02u.%02u_%d",
+ uci_client->out_handle->dev_id,
+ uci_client->out_handle->domain,
+ uci_client->out_handle->bus,
+ uci_client->out_handle->slot,
uci_client->out_attr.chan_id);
uci_client->uci_ipc_log = ipc_log_context_create
(MHI_UCI_IPC_LOG_PAGES,
@@ -1364,11 +1376,16 @@ static int mhi_uci_probe(struct platform_device *pdev)
}
uci_client->dev =
device_create(mhi_uci_drv_ctxt.mhi_uci_class,
- NULL,
- uci_ctxt->dev_t + i,
- NULL,
- DEVICE_NAME "_pipe_%d",
- uci_client->out_chan);
+ NULL,
+ uci_ctxt->dev_t + i,
+ NULL,
+ DEVICE_NAME "_%04x_%02u.%02u.%02u%s%d",
+ uci_client->out_handle->dev_id,
+ uci_client->out_handle->domain,
+ uci_client->out_handle->bus,
+ uci_client->out_handle->slot,
+ "_pipe_",
+ uci_client->out_chan);
if (IS_ERR(uci_client->dev)) {
uci_log(uci_client->uci_ipc_log,
UCI_DBG_ERROR,
diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c
index 7f97d7c6bab7..9b48282c812c 100644
--- a/drivers/platform/msm/msm_11ad/msm_11ad.c
+++ b/drivers/platform/msm/msm_11ad/msm_11ad.c
@@ -55,7 +55,6 @@
#define VDDIO_MAX_UV 2040000
#define VDDIO_MAX_UA 70300
-#define DISABLE_PCIE_L1_MASK 0xFFFFFFFD
#define PCIE20_CAP_LINKCTRLSTATUS 0x80
#define WIGIG_MIN_CPU_BOOST_KBPS 150000
@@ -90,6 +89,7 @@ struct msm11ad_ctx {
u32 rc_index; /* PCIE root complex index */
struct pci_dev *pcidev;
struct pci_saved_state *pristine_state;
+ bool l1_enabled_in_enum;
/* SMMU */
bool use_smmu; /* have SMMU enabled? */
@@ -479,6 +479,47 @@ static void msm_11ad_disable_clocks(struct msm11ad_ctx *ctx)
msm_11ad_disable_clk(ctx, &ctx->rf_clk3);
}
+int msm_11ad_ctrl_aspm_l1(struct msm11ad_ctx *ctx, bool enable)
+{
+ int rc;
+ u32 val;
+ struct pci_dev *pdev = ctx->pcidev;
+ bool l1_enabled;
+
+ /* Read current state */
+ rc = pci_read_config_dword(pdev,
+ PCIE20_CAP_LINKCTRLSTATUS, &val);
+ if (rc) {
+ dev_err(ctx->dev,
+ "reading PCIE20_CAP_LINKCTRLSTATUS failed:%d\n", rc);
+ return rc;
+ }
+ dev_dbg(ctx->dev, "PCIE20_CAP_LINKCTRLSTATUS read returns 0x%x\n", val);
+
+ l1_enabled = val & PCI_EXP_LNKCTL_ASPM_L1;
+ if (l1_enabled == enable) {
+ dev_dbg(ctx->dev, "ASPM_L1 is already %s\n",
+ l1_enabled ? "enabled" : "disabled");
+ return 0;
+ }
+
+ if (enable)
+ val |= PCI_EXP_LNKCTL_ASPM_L1; /* enable bit 1 */
+ else
+ val &= ~PCI_EXP_LNKCTL_ASPM_L1; /* disable bit 1 */
+
+ dev_dbg(ctx->dev, "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x)\n",
+ val);
+ rc = pci_write_config_dword(pdev,
+ PCIE20_CAP_LINKCTRLSTATUS, val);
+ if (rc)
+ dev_err(ctx->dev,
+ "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x) failed:%d\n",
+ val, rc);
+
+ return rc;
+}
+
static int ops_suspend(void *handle)
{
int rc;
@@ -521,7 +562,6 @@ static int ops_resume(void *handle)
int rc;
struct msm11ad_ctx *ctx = handle;
struct pci_dev *pcidev;
- u32 val;
pr_info("%s(%p)\n", __func__, handle);
if (!ctx) {
@@ -565,25 +605,14 @@ static int ops_resume(void *handle)
goto err_suspend_rc;
}
- /* Disable L1 */
- rc = pci_read_config_dword(ctx->pcidev,
- PCIE20_CAP_LINKCTRLSTATUS, &val);
- if (rc) {
- dev_err(ctx->dev,
- "reading PCIE20_CAP_LINKCTRLSTATUS failed:%d\n",
- rc);
- goto err_suspend_rc;
- }
- val &= DISABLE_PCIE_L1_MASK; /* disable bit 1 */
- dev_dbg(ctx->dev, "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x)\n",
- val);
- rc = pci_write_config_dword(ctx->pcidev,
- PCIE20_CAP_LINKCTRLSTATUS, val);
- if (rc) {
- dev_err(ctx->dev,
- "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x) failed:%d\n",
- val, rc);
- goto err_suspend_rc;
+ /* Disable L1, in case it is enabled */
+ if (ctx->l1_enabled_in_enum) {
+ rc = msm_11ad_ctrl_aspm_l1(ctx, false);
+ if (rc) {
+ dev_err(ctx->dev,
+ "failed to disable L1, rc %d\n", rc);
+ goto err_suspend_rc;
+ }
}
return 0;
@@ -992,8 +1021,8 @@ static int msm_11ad_probe(struct platform_device *pdev)
}
ctx->pcidev = pcidev;
- /* Disable L1 */
- rc = pci_read_config_dword(ctx->pcidev,
+ /* Read current state */
+ rc = pci_read_config_dword(pcidev,
PCIE20_CAP_LINKCTRLSTATUS, &val);
if (rc) {
dev_err(ctx->dev,
@@ -1001,16 +1030,19 @@ static int msm_11ad_probe(struct platform_device *pdev)
rc);
goto out_rc;
}
- val &= DISABLE_PCIE_L1_MASK; /* disable bit 1 */
- dev_dbg(ctx->dev, "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x)\n",
- val);
- rc = pci_write_config_dword(ctx->pcidev,
- PCIE20_CAP_LINKCTRLSTATUS, val);
- if (rc) {
- dev_err(ctx->dev,
- "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x) failed:%d\n",
- val, rc);
- goto out_rc;
+
+ ctx->l1_enabled_in_enum = val & PCI_EXP_LNKCTL_ASPM_L1;
+ dev_dbg(ctx->dev, "L1 is %s in enumeration\n",
+ ctx->l1_enabled_in_enum ? "enabled" : "disabled");
+
+ /* Disable L1, in case it is enabled */
+ if (ctx->l1_enabled_in_enum) {
+ rc = msm_11ad_ctrl_aspm_l1(ctx, false);
+ if (rc) {
+ dev_err(ctx->dev,
+ "failed to disable L1, rc %d\n", rc);
+ goto out_rc;
+ }
}
rc = pci_save_state(pcidev);
@@ -1258,6 +1290,13 @@ static int ops_notify(void *handle, enum wil_platform_event evt)
* TODO: Enable rf_clk3 clock before resetting the device to
* ensure stable ref clock during the device reset
*/
+ /* Re-enable L1 in case it was enabled in enumeration */
+ if (ctx->l1_enabled_in_enum) {
+ rc = msm_11ad_ctrl_aspm_l1(ctx, true);
+ if (rc)
+ dev_err(ctx->dev,
+ "failed to enable L1, rc %d\n", rc);
+ }
break;
case WIL_PLATFORM_EVT_FW_RDY:
/*
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index 131fee2b093e..a3661cc44f86 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -128,6 +128,15 @@ static const struct dmi_system_id asus_quirks[] = {
},
{
.callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X45U",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X45U"),
+ },
+ .driver_data = &quirk_asus_wapf4,
+ },
+ {
+ .callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X456UA",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c
index 1fc0de870ff8..361770568ad0 100644
--- a/drivers/platform/x86/intel_mid_powerbtn.c
+++ b/drivers/platform/x86/intel_mid_powerbtn.c
@@ -77,7 +77,7 @@ static int mfld_pb_probe(struct platform_device *pdev)
input_set_capability(input, EV_KEY, KEY_POWER);
- error = request_threaded_irq(irq, NULL, mfld_pb_isr, 0,
+ error = request_threaded_irq(irq, NULL, mfld_pb_isr, IRQF_ONESHOT,
DRIVER_NAME, input);
if (error) {
dev_err(&pdev->dev, "Unable to request irq %d for mfld power"
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 201a53e66ef0..438da2c51dd6 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -264,6 +264,7 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(dp_dm),
POWER_SUPPLY_ATTR(input_current_limited),
POWER_SUPPLY_ATTR(input_current_now),
+ POWER_SUPPLY_ATTR(charge_qnovo_enable),
POWER_SUPPLY_ATTR(current_qnovo),
POWER_SUPPLY_ATTR(voltage_qnovo),
POWER_SUPPLY_ATTR(rerun_aicl),
diff --git a/drivers/power/qcom/apm.c b/drivers/power/qcom/apm.c
index b5ae7f4ade03..9455468f1734 100644
--- a/drivers/power/qcom/apm.c
+++ b/drivers/power/qcom/apm.c
@@ -629,9 +629,15 @@ done:
#define MSM8953_APCC_APM_MODE 0x000002a8
#define MSM8953_APCC_APM_CTL_STS 0x000002b0
+/* 8953 constants */
+#define MSM8953_APM_SWITCH_TIMEOUT_US 500
+
+/* Register bit mask definitions */
+#define MSM8953_APM_CTL_STS_MASK 0x1f
+
static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
{
- int timeout = MSM_APM_SWITCH_TIMEOUT_US;
+ int timeout = MSM8953_APM_SWITCH_TIMEOUT_US;
u32 regval;
int ret = 0;
unsigned long flags;
@@ -648,7 +654,7 @@ static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
while (timeout > 0) {
regval = readl_relaxed(ctrl_dev->reg_base +
MSM8953_APCC_APM_CTL_STS);
- if ((regval & MSM_APM_CTL_STS_MASK) ==
+ if ((regval & MSM8953_APM_CTL_STS_MASK) ==
MSM8953_APM_MX_DONE_VAL)
break;
@@ -672,7 +678,7 @@ static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
static int msm8953_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
{
- int timeout = MSM_APM_SWITCH_TIMEOUT_US;
+ int timeout = MSM8953_APM_SWITCH_TIMEOUT_US;
u32 regval;
int ret = 0;
unsigned long flags;
@@ -689,7 +695,7 @@ static int msm8953_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
while (timeout > 0) {
regval = readl_relaxed(ctrl_dev->reg_base +
MSM8953_APCC_APM_CTL_STS);
- if ((regval & MSM_APM_CTL_STS_MASK) ==
+ if ((regval & MSM8953_APM_CTL_STS_MASK) ==
MSM8953_APM_APCC_DONE_VAL)
break;
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index 5b320ca8e74e..0c80c8be7909 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -24,7 +24,7 @@
#include <linux/printk.h>
#include <linux/pm_wakeup.h>
#include <linux/slab.h>
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define DRV_MAJOR_VERSION 1
#define DRV_MINOR_VERSION 0
diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index 32a25b4e2c7b..0e15752eb9d2 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -29,7 +29,7 @@
#include <linux/string_helpers.h>
#include <linux/types.h>
#include <linux/uaccess.h>
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define fg_dbg(chip, reason, fmt, ...) \
do { \
@@ -372,6 +372,7 @@ struct fg_chip {
bool esr_flt_cold_temp_en;
bool bsoc_delta_irq_en;
bool slope_limit_en;
+ bool use_ima_single_mode;
struct completion soc_update;
struct completion soc_ready;
struct delayed_work profile_load_work;
diff --git a/drivers/power/supply/qcom/fg-memif.c b/drivers/power/supply/qcom/fg-memif.c
index 2dc76182ed15..8a949bfe61d0 100644
--- a/drivers/power/supply/qcom/fg-memif.c
+++ b/drivers/power/supply/qcom/fg-memif.c
@@ -48,6 +48,10 @@ static int fg_config_access_mode(struct fg_chip *chip, bool access, bool burst)
int rc;
u8 intf_ctl = 0;
+ fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "access: %d burst: %d\n",
+ access, burst);
+
+ WARN_ON(burst && chip->use_ima_single_mode);
intf_ctl = ((access == FG_WRITE) ? IMA_WR_EN_BIT : 0) |
(burst ? MEM_ACS_BURST_BIT : 0);
@@ -175,6 +179,7 @@ int fg_clear_dma_errors_if_any(struct fg_chip *chip)
{
int rc;
u8 dma_sts;
+ bool error_present;
rc = fg_read(chip, MEM_IF_DMA_STS(chip), &dma_sts, 1);
if (rc < 0) {
@@ -184,14 +189,13 @@ int fg_clear_dma_errors_if_any(struct fg_chip *chip)
}
fg_dbg(chip, FG_STATUS, "dma_sts: %x\n", dma_sts);
- if (dma_sts & (DMA_WRITE_ERROR_BIT | DMA_READ_ERROR_BIT)) {
- rc = fg_masked_write(chip, MEM_IF_DMA_CTL(chip),
- DMA_CLEAR_LOG_BIT, DMA_CLEAR_LOG_BIT);
- if (rc < 0) {
- pr_err("failed to write addr=0x%04x, rc=%d\n",
- MEM_IF_DMA_CTL(chip), rc);
- return rc;
- }
+ error_present = dma_sts & (DMA_WRITE_ERROR_BIT | DMA_READ_ERROR_BIT);
+ rc = fg_masked_write(chip, MEM_IF_DMA_CTL(chip), DMA_CLEAR_LOG_BIT,
+ error_present ? DMA_CLEAR_LOG_BIT : 0);
+ if (rc < 0) {
+ pr_err("failed to write addr=0x%04x, rc=%d\n",
+ MEM_IF_DMA_CTL(chip), rc);
+ return rc;
}
return 0;
@@ -293,7 +297,9 @@ static int fg_check_iacs_ready(struct fg_chip *chip)
/* check for error condition */
rc = fg_clear_ima_errors_if_any(chip, false);
if (rc < 0) {
- pr_err("Failed to check for ima errors rc=%d\n", rc);
+ if (rc != -EAGAIN)
+ pr_err("Failed to check for ima errors rc=%d\n",
+ rc);
return rc;
}
@@ -357,7 +363,12 @@ static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address,
/* check for error condition */
rc = fg_clear_ima_errors_if_any(chip, false);
if (rc < 0) {
- pr_err("Failed to check for ima errors rc=%d\n", rc);
+ if (rc == -EAGAIN)
+ pr_err("IMA error cleared, address [%d %d] len %d\n",
+ address, offset, len);
+ else
+ pr_err("Failed to check for ima errors rc=%d\n",
+ rc);
return rc;
}
@@ -365,6 +376,15 @@ static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address,
len -= num_bytes;
offset = byte_enable = 0;
+ if (chip->use_ima_single_mode && len) {
+ address++;
+ rc = fg_set_address(chip, address);
+ if (rc < 0) {
+ pr_err("failed to set address rc = %d\n", rc);
+ return rc;
+ }
+ }
+
rc = fg_check_iacs_ready(chip);
if (rc < 0) {
pr_debug("IACS_RDY failed rc=%d\n", rc);
@@ -403,22 +423,40 @@ static int __fg_interleaved_mem_read(struct fg_chip *chip, u16 address,
/* check for error condition */
rc = fg_clear_ima_errors_if_any(chip, false);
if (rc < 0) {
- pr_err("Failed to check for ima errors rc=%d\n", rc);
+ if (rc == -EAGAIN)
+ pr_err("IMA error cleared, address [%d %d] len %d\n",
+ address, offset, len);
+ else
+ pr_err("Failed to check for ima errors rc=%d\n",
+ rc);
return rc;
}
- if (len && len < BYTES_PER_SRAM_WORD) {
- /*
- * Move to single mode. Changing address is not
- * required here as it must be in burst mode. Address
- * will get incremented internally by FG HW once the MSB
- * of RD_DATA is read.
- */
- rc = fg_config_access_mode(chip, FG_READ, 0);
- if (rc < 0) {
- pr_err("failed to move to single mode rc=%d\n",
- rc);
- return -EIO;
+ if (chip->use_ima_single_mode) {
+ if (len) {
+ address++;
+ rc = fg_set_address(chip, address);
+ if (rc < 0) {
+ pr_err("failed to set address rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+ } else {
+ if (len && len < BYTES_PER_SRAM_WORD) {
+ /*
+ * Move to single mode. Changing address is not
+ * required here as it must be in burst mode.
+ * Address will get incremented internally by FG
+ * HW once the MSB of RD_DATA is read.
+ */
+ rc = fg_config_access_mode(chip, FG_READ,
+ false);
+ if (rc < 0) {
+ pr_err("failed to move to single mode rc=%d\n",
+ rc);
+ return -EIO;
+ }
}
}
@@ -489,6 +527,7 @@ static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val,
u16 address, int offset, int len, bool access)
{
int rc = 0;
+ bool burst_mode = false;
if (!is_mem_access_available(chip, access))
return -EBUSY;
@@ -503,7 +542,8 @@ static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val,
}
/* configure for the read/write, single/burst mode */
- rc = fg_config_access_mode(chip, access, (offset + len) > 4);
+ burst_mode = chip->use_ima_single_mode ? false : ((offset + len) > 4);
+ rc = fg_config_access_mode(chip, access, burst_mode);
if (rc < 0) {
pr_err("failed to set memory access rc = %d\n", rc);
return rc;
@@ -583,7 +623,7 @@ retry:
if (rc < 0) {
count++;
if (rc == -EAGAIN) {
- pr_err("IMA access failed retry_count = %d\n", count);
+ pr_err("IMA read failed retry_count = %d\n", count);
goto retry;
}
pr_err("failed to read SRAM address rc = %d\n", rc);
@@ -667,8 +707,8 @@ retry:
rc = __fg_interleaved_mem_write(chip, address, offset, val, len);
if (rc < 0) {
count++;
- if ((rc == -EAGAIN) && (count < RETRY_COUNT)) {
- pr_err("IMA access failed retry_count = %d\n", count);
+ if (rc == -EAGAIN) {
+ pr_err("IMA write failed retry_count = %d\n", count);
goto retry;
}
pr_err("failed to write SRAM address rc = %d\n", rc);
diff --git a/drivers/power/supply/qcom/pmic-voter.c b/drivers/power/supply/qcom/pmic-voter.c
index e1a92fb23912..3652cc7802eb 100644
--- a/drivers/power/supply/qcom/pmic-voter.c
+++ b/drivers/power/supply/qcom/pmic-voter.c
@@ -18,7 +18,7 @@
#include <linux/slab.h>
#include <linux/string.h>
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define NUM_MAX_CLIENTS 8
#define DEBUG_FORCE_CLIENT "DEBUG_FORCE_CLIENT"
@@ -188,6 +188,38 @@ void unlock_votable(struct votable *votable)
}
/**
+ * is_client_vote_enabled() -
+ * is_client_vote_enabled_locked() -
+ * The unlocked and locked variants of getting whether a client's
+ vote is enabled.
+ * @votable: the votable object
+ * @client_str: client of interest
+ *
+ * Returns:
+ * True if the client's vote is enabled; false otherwise.
+ */
+bool is_client_vote_enabled_locked(struct votable *votable,
+ const char *client_str)
+{
+ int client_id = get_client_id(votable, client_str);
+
+ if (client_id < 0)
+ return false;
+
+ return votable->votes[client_id].enabled;
+}
+
+bool is_client_vote_enabled(struct votable *votable, const char *client_str)
+{
+ bool enabled;
+
+ lock_votable(votable);
+ enabled = is_client_vote_enabled_locked(votable, client_str);
+ unlock_votable(votable);
+ return enabled;
+}
+
+/**
* get_client_vote() -
* get_client_vote_locked() -
* The unlocked and locked variants of getting a client's voted
diff --git a/drivers/power/supply/qcom/pmic-voter.h b/drivers/power/supply/qcom/pmic-voter.h
deleted file mode 100644
index 031b9a010a42..000000000000
--- a/drivers/power/supply/qcom/pmic-voter.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* 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
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef __PMIC_VOTER_H
-#define __PMIC_VOTER_H
-
-#include <linux/mutex.h>
-
-struct votable;
-
-enum votable_type {
- VOTE_MIN,
- VOTE_MAX,
- VOTE_SET_ANY,
- NUM_VOTABLE_TYPES,
-};
-
-int get_client_vote(struct votable *votable, const char *client_str);
-int get_client_vote_locked(struct votable *votable, const char *client_str);
-int get_effective_result(struct votable *votable);
-int get_effective_result_locked(struct votable *votable);
-const char *get_effective_client(struct votable *votable);
-const char *get_effective_client_locked(struct votable *votable);
-int vote(struct votable *votable, const char *client_str, bool state, int val);
-int rerun_election(struct votable *votable);
-struct votable *find_votable(const char *name);
-struct votable *create_votable(const char *name,
- int votable_type,
- int (*callback)(struct votable *votable,
- void *data,
- int effective_result,
- const char *effective_client),
- void *data);
-void destroy_votable(struct votable *votable);
-void lock_votable(struct votable *votable);
-void unlock_votable(struct votable *votable);
-
-#endif /* __PMIC_VOTER_H */
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index 39afc235fbc3..aeffe1f154a1 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -525,7 +525,7 @@ static int fg_get_sram_prop(struct fg_chip *chip, enum fg_sram_param_id id,
}
#define CC_SOC_30BIT GENMASK(29, 0)
-static int fg_get_cc_soc(struct fg_chip *chip, int *val)
+static int fg_get_charge_raw(struct fg_chip *chip, int *val)
{
int rc, cc_soc;
@@ -539,7 +539,7 @@ static int fg_get_cc_soc(struct fg_chip *chip, int *val)
return 0;
}
-static int fg_get_cc_soc_sw(struct fg_chip *chip, int *val)
+static int fg_get_charge_counter(struct fg_chip *chip, int *val)
{
int rc, cc_soc;
@@ -1241,7 +1241,7 @@ static void fg_cap_learning_post_process(struct fg_chip *chip)
chip->cl.final_cc_uah, old_cap, chip->cl.learned_cc_uah);
}
-static int fg_cap_learning_process_full_data(struct fg_chip *chip)
+static int fg_cap_learning_process_full_data(struct fg_chip *chip)
{
int rc, cc_soc_sw, cc_soc_delta_pct;
int64_t delta_cc_uah;
@@ -1263,30 +1263,39 @@ static int fg_cap_learning_process_full_data(struct fg_chip *chip)
return 0;
}
-static int fg_cap_learning_begin(struct fg_chip *chip, int batt_soc)
+#define BATT_SOC_32BIT GENMASK(31, 0)
+static int fg_cap_learning_begin(struct fg_chip *chip, u32 batt_soc)
{
- int rc, cc_soc_sw;
+ int rc, cc_soc_sw, batt_soc_msb;
- if (DIV_ROUND_CLOSEST(batt_soc * 100, FULL_SOC_RAW) >
+ batt_soc_msb = batt_soc >> 24;
+ if (DIV_ROUND_CLOSEST(batt_soc_msb * 100, FULL_SOC_RAW) >
chip->dt.cl_start_soc) {
fg_dbg(chip, FG_CAP_LEARN, "Battery SOC %d is high!, not starting\n",
- batt_soc);
+ batt_soc_msb);
return -EINVAL;
}
- chip->cl.init_cc_uah = div64_s64(chip->cl.learned_cc_uah * batt_soc,
+ chip->cl.init_cc_uah = div64_s64(chip->cl.learned_cc_uah * batt_soc_msb,
FULL_SOC_RAW);
- rc = fg_get_sram_prop(chip, FG_SRAM_CC_SOC_SW, &cc_soc_sw);
+
+ /* Prime cc_soc_sw with battery SOC when capacity learning begins */
+ cc_soc_sw = div64_s64((int64_t)batt_soc * CC_SOC_30BIT,
+ BATT_SOC_32BIT);
+ rc = fg_sram_write(chip, chip->sp[FG_SRAM_CC_SOC_SW].addr_word,
+ chip->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw,
+ chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC);
if (rc < 0) {
- pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc);
- return rc;
+ pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
+ goto out;
}
chip->cl.init_cc_soc_sw = cc_soc_sw;
chip->cl.active = true;
fg_dbg(chip, FG_CAP_LEARN, "Capacity learning started @ battery SOC %d init_cc_soc_sw:%d\n",
- batt_soc, chip->cl.init_cc_soc_sw);
- return 0;
+ batt_soc_msb, chip->cl.init_cc_soc_sw);
+out:
+ return rc;
}
static int fg_cap_learning_done(struct fg_chip *chip)
@@ -1318,7 +1327,7 @@ out:
#define FULL_SOC_RAW 255
static void fg_cap_learning_update(struct fg_chip *chip)
{
- int rc, batt_soc;
+ int rc, batt_soc, batt_soc_msb;
mutex_lock(&chip->cl.lock);
@@ -1337,11 +1346,9 @@ static void fg_cap_learning_update(struct fg_chip *chip)
goto out;
}
- /* We need only the most significant byte here */
- batt_soc = (u32)batt_soc >> 24;
-
+ batt_soc_msb = (u32)batt_soc >> 24;
fg_dbg(chip, FG_CAP_LEARN, "Chg_status: %d cl_active: %d batt_soc: %d\n",
- chip->charge_status, chip->cl.active, batt_soc);
+ chip->charge_status, chip->cl.active, batt_soc_msb);
/* Initialize the starting point of learning capacity */
if (!chip->cl.active) {
@@ -1363,7 +1370,7 @@ static void fg_cap_learning_update(struct fg_chip *chip)
if (chip->charge_status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n",
- batt_soc);
+ batt_soc_msb);
chip->cl.active = false;
chip->cl.init_cc_uah = 0;
}
@@ -1598,6 +1605,9 @@ static int fg_rconn_config(struct fg_chip *chip)
u64 scaling_factor;
u32 val = 0;
+ if (!chip->dt.rconn_mohms)
+ return 0;
+
rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD,
SW_CONFIG_OFFSET, (u8 *)&val, 1, FG_IMA_DEFAULT);
if (rc < 0) {
@@ -1696,6 +1706,9 @@ static int fg_set_recharge_soc(struct fg_chip *chip, int recharge_soc)
if (!chip->dt.auto_recharge_soc)
return 0;
+ if (recharge_soc < 0 || recharge_soc > FULL_CAPACITY)
+ return 0;
+
fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR, recharge_soc, &buf);
rc = fg_sram_write(chip,
chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_word,
@@ -1712,46 +1725,55 @@ static int fg_set_recharge_soc(struct fg_chip *chip, int recharge_soc)
static int fg_adjust_recharge_soc(struct fg_chip *chip)
{
int rc, msoc, recharge_soc, new_recharge_soc = 0;
+ bool recharge_soc_status;
if (!chip->dt.auto_recharge_soc)
return 0;
recharge_soc = chip->dt.recharge_soc_thr;
+ recharge_soc_status = chip->recharge_soc_adjusted;
/*
* If the input is present and charging had been terminated, adjust
* the recharge SOC threshold based on the monotonic SOC at which
* the charge termination had happened.
*/
- if (is_input_present(chip) && !chip->recharge_soc_adjusted
- && chip->charge_done) {
- /* Get raw monotonic SOC for calculation */
- rc = fg_get_msoc(chip, &msoc);
- if (rc < 0) {
- pr_err("Error in getting msoc, rc=%d\n", rc);
- return rc;
- }
+ if (is_input_present(chip)) {
+ if (chip->charge_done) {
+ if (!chip->recharge_soc_adjusted) {
+ /* Get raw monotonic SOC for calculation */
+ rc = fg_get_msoc(chip, &msoc);
+ if (rc < 0) {
+ pr_err("Error in getting msoc, rc=%d\n",
+ rc);
+ return rc;
+ }
- /* Adjust the recharge_soc threshold */
- new_recharge_soc = msoc - (FULL_CAPACITY - recharge_soc);
- } else if (chip->recharge_soc_adjusted && (!is_input_present(chip)
- || chip->health == POWER_SUPPLY_HEALTH_GOOD)) {
+ /* Adjust the recharge_soc threshold */
+ new_recharge_soc = msoc - (FULL_CAPACITY -
+ recharge_soc);
+ chip->recharge_soc_adjusted = true;
+ } else {
+ /* adjusted already, do nothing */
+ return 0;
+ }
+ } else {
+ /* Charging, do nothing */
+ return 0;
+ }
+ } else {
/* Restore the default value */
new_recharge_soc = recharge_soc;
+ chip->recharge_soc_adjusted = false;
}
- if (new_recharge_soc > 0 && new_recharge_soc < FULL_CAPACITY) {
- rc = fg_set_recharge_soc(chip, new_recharge_soc);
- if (rc) {
- pr_err("Couldn't set resume SOC for FG, rc=%d\n", rc);
- return rc;
- }
-
- chip->recharge_soc_adjusted = (new_recharge_soc !=
- recharge_soc);
- fg_dbg(chip, FG_STATUS, "resume soc set to %d\n",
- new_recharge_soc);
+ rc = fg_set_recharge_soc(chip, new_recharge_soc);
+ if (rc < 0) {
+ chip->recharge_soc_adjusted = recharge_soc_status;
+ pr_err("Couldn't set resume SOC for FG, rc=%d\n", rc);
+ return rc;
}
+ fg_dbg(chip, FG_STATUS, "resume soc set to %d\n", new_recharge_soc);
return 0;
}
@@ -2806,7 +2828,7 @@ static int fg_psy_get_property(struct power_supply *psy,
pval->intval = chip->cyc_ctr.id;
break;
case POWER_SUPPLY_PROP_CHARGE_NOW_RAW:
- rc = fg_get_cc_soc(chip, &pval->intval);
+ rc = fg_get_charge_raw(chip, &pval->intval);
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
pval->intval = chip->cl.init_cc_uah;
@@ -2815,7 +2837,7 @@ static int fg_psy_get_property(struct power_supply *psy,
pval->intval = chip->cl.learned_cc_uah;
break;
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
- rc = fg_get_cc_soc_sw(chip, &pval->intval);
+ rc = fg_get_charge_counter(chip, &pval->intval);
break;
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
rc = fg_get_time_to_full(chip, &pval->intval);
@@ -3164,12 +3186,10 @@ static int fg_hw_init(struct fg_chip *chip)
return rc;
}
- if (chip->dt.rconn_mohms > 0) {
- rc = fg_rconn_config(chip);
- if (rc < 0) {
- pr_err("Error in configuring Rconn, rc=%d\n", rc);
- return rc;
- }
+ rc = fg_rconn_config(chip);
+ if (rc < 0) {
+ pr_err("Error in configuring Rconn, rc=%d\n", rc);
+ return rc;
}
fg_encode(chip->sp, FG_SRAM_ESR_TIGHT_FILTER,
@@ -3216,20 +3236,19 @@ static irqreturn_t fg_mem_xcp_irq_handler(int irq, void *data)
}
fg_dbg(chip, FG_IRQ, "irq %d triggered, status:%d\n", irq, status);
- if (status & MEM_XCP_BIT) {
- rc = fg_clear_dma_errors_if_any(chip);
- if (rc < 0) {
- pr_err("Error in clearing DMA error, rc=%d\n", rc);
- return IRQ_HANDLED;
- }
- mutex_lock(&chip->sram_rw_lock);
+ mutex_lock(&chip->sram_rw_lock);
+ rc = fg_clear_dma_errors_if_any(chip);
+ if (rc < 0)
+ pr_err("Error in clearing DMA error, rc=%d\n", rc);
+
+ if (status & MEM_XCP_BIT) {
rc = fg_clear_ima_errors_if_any(chip, true);
if (rc < 0 && rc != -EAGAIN)
pr_err("Error in checking IMA errors rc:%d\n", rc);
- mutex_unlock(&chip->sram_rw_lock);
}
+ mutex_unlock(&chip->sram_rw_lock);
return IRQ_HANDLED;
}
@@ -3725,6 +3744,7 @@ static int fg_parse_dt(struct fg_chip *chip)
case PM660_SUBTYPE:
chip->sp = pmi8998_v2_sram_params;
chip->alg_flags = pmi8998_v2_alg_flags;
+ chip->use_ima_single_mode = true;
break;
default:
return -EINVAL;
@@ -3945,9 +3965,7 @@ static int fg_parse_dt(struct fg_chip *chip)
pr_err("Error in parsing Ki coefficients, rc=%d\n", rc);
rc = of_property_read_u32(node, "qcom,fg-rconn-mohms", &temp);
- if (rc < 0)
- chip->dt.rconn_mohms = -EINVAL;
- else
+ if (!rc)
chip->dt.rconn_mohms = temp;
rc = of_property_read_u32(node, "qcom,fg-esr-filter-switch-temp",
diff --git a/drivers/power/supply/qcom/qpnp-qnovo.c b/drivers/power/supply/qcom/qpnp-qnovo.c
index d36db8d8f3f1..8f9514a25f63 100644
--- a/drivers/power/supply/qcom/qpnp-qnovo.c
+++ b/drivers/power/supply/qcom/qpnp-qnovo.c
@@ -19,7 +19,7 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/qpnp/qpnp-revid.h>
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define QNOVO_REVISION1 0x00
#define QNOVO_REVISION2 0x01
@@ -29,6 +29,7 @@
#define QNOVO_PTRAIN_STS 0x08
#define QNOVO_ERROR_STS 0x09
#define QNOVO_ERROR_BIT BIT(0)
+#define QNOVO_ERROR_STS2 0x0A
#define QNOVO_INT_RT_STS 0x10
#define QNOVO_INT_SET_TYPE 0x11
#define QNOVO_INT_POLARITY_HIGH 0x12
@@ -151,7 +152,7 @@ struct qnovo {
};
static int debug_mask;
-module_param_named(debug_mask, debug_mask, int, S_IRUSR | S_IWUSR);
+module_param_named(debug_mask, debug_mask, int, 0600);
#define qnovo_dbg(chip, reason, fmt, ...) \
do { \
@@ -272,28 +273,22 @@ static int qnovo_disable_cb(struct votable *votable, void *data, int disable,
const char *client)
{
struct qnovo *chip = data;
- int rc = 0;
+ union power_supply_propval pval = {0};
+ int rc;
- if (disable) {
- rc = qnovo_batt_psy_update(chip, true);
- if (rc < 0)
- return rc;
- }
+ if (!is_batt_available(chip))
+ return -EINVAL;
- rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT,
- disable ? 0 : QNOVO_PTRAIN_EN_BIT);
+ pval.intval = !disable;
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE,
+ &pval);
if (rc < 0) {
- dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n",
- disable ? "disable" : "enable", rc);
- return rc;
- }
-
- if (!disable) {
- rc = qnovo_batt_psy_update(chip, false);
- if (rc < 0)
- return rc;
+ pr_err("Couldn't set prop qnovo_enable rc = %d\n", rc);
+ return -EINVAL;
}
+ rc = qnovo_batt_psy_update(chip, disable);
return rc;
}
@@ -348,13 +343,15 @@ static int qnovo_check_chg_version(struct qnovo *chip)
enum {
VER = 0,
OK_TO_QNOVO,
- ENABLE,
+ QNOVO_ENABLE,
+ PT_ENABLE,
FV_REQUEST,
FCC_REQUEST,
PE_CTRL_REG,
PE_CTRL2_REG,
PTRAIN_STS_REG,
INT_RT_STS_REG,
+ ERR_STS2_REG,
PREST1,
PPULS1,
NREST1,
@@ -394,6 +391,12 @@ struct param_info {
};
static struct param_info params[] = {
+ [PT_ENABLE] = {
+ .name = "PT_ENABLE",
+ .start_addr = QNOVO_PTRAIN_EN,
+ .num_regs = 1,
+ .units_str = "",
+ },
[FV_REQUEST] = {
.units_str = "uV",
},
@@ -424,6 +427,12 @@ static struct param_info params[] = {
.num_regs = 1,
.units_str = "",
},
+ [ERR_STS2_REG] = {
+ .name = "RAW_CHGR_ERR",
+ .start_addr = QNOVO_ERROR_STS2,
+ .num_regs = 1,
+ .units_str = "",
+ },
[PREST1] = {
.name = "PREST1",
.start_addr = QNOVO_PREST1_CTRL,
@@ -431,7 +440,7 @@ static struct param_info params[] = {
.reg_to_unit_multiplier = 5,
.reg_to_unit_divider = 1,
.min_val = 5,
- .max_val = 1275,
+ .max_val = 255,
.units_str = "mS",
},
[PPULS1] = {
@@ -440,8 +449,8 @@ static struct param_info params[] = {
.num_regs = 2,
.reg_to_unit_multiplier = 1600, /* converts to uC */
.reg_to_unit_divider = 1,
- .min_val = 0,
- .max_val = 104856000,
+ .min_val = 30000,
+ .max_val = 65535000,
.units_str = "uC",
},
[NREST1] = {
@@ -451,7 +460,7 @@ static struct param_info params[] = {
.reg_to_unit_multiplier = 5,
.reg_to_unit_divider = 1,
.min_val = 5,
- .max_val = 1275,
+ .max_val = 255,
.units_str = "mS",
},
[NPULS1] = {
@@ -460,8 +469,8 @@ static struct param_info params[] = {
.num_regs = 1,
.reg_to_unit_multiplier = 5,
.reg_to_unit_divider = 1,
- .min_val = 5,
- .max_val = 1275,
+ .min_val = 0,
+ .max_val = 255,
.units_str = "mS",
},
[PPCNT] = {
@@ -470,7 +479,7 @@ static struct param_info params[] = {
.num_regs = 1,
.reg_to_unit_multiplier = 1,
.reg_to_unit_divider = 1,
- .min_val = 0,
+ .min_val = 1,
.max_val = 255,
.units_str = "pulses",
},
@@ -480,8 +489,8 @@ static struct param_info params[] = {
.num_regs = 2,
.reg_to_unit_multiplier = 610350, /* converts to nV */
.reg_to_unit_divider = 1,
- .min_val = 0,
- .max_val = 5000000,
+ .min_val = 2200000,
+ .max_val = 4500000,
.units_str = "uV",
},
[PVOLT1] = {
@@ -506,8 +515,6 @@ static struct param_info params[] = {
.num_regs = 1,
.reg_to_unit_multiplier = 2,
.reg_to_unit_divider = 1,
- .min_val = 5,
- .max_val = 1275,
.units_str = "S",
},
[PREST2] = {
@@ -517,7 +524,7 @@ static struct param_info params[] = {
.reg_to_unit_multiplier = 5,
.reg_to_unit_divider = 1,
.min_val = 5,
- .max_val = 327675,
+ .max_val = 65535,
.units_str = "mS",
},
[PPULS2] = {
@@ -526,8 +533,8 @@ static struct param_info params[] = {
.num_regs = 2,
.reg_to_unit_multiplier = 1600, /* converts to uC */
.reg_to_unit_divider = 1,
- .min_val = 0,
- .max_val = 104856000,
+ .min_val = 30000,
+ .max_val = 65535000,
.units_str = "uC",
},
[NREST2] = {
@@ -538,7 +545,7 @@ static struct param_info params[] = {
.reg_to_unit_divider = 1,
.reg_to_unit_offset = -5,
.min_val = 5,
- .max_val = 1280,
+ .max_val = 255,
.units_str = "mS",
},
[NPULS2] = {
@@ -547,18 +554,18 @@ static struct param_info params[] = {
.num_regs = 1,
.reg_to_unit_multiplier = 5,
.reg_to_unit_divider = 1,
- .min_val = 5,
- .max_val = 1275,
+ .min_val = 0,
+ .max_val = 255,
.units_str = "mS",
},
[VLIM2] = {
- .name = "VLIM1",
+ .name = "VLIM2",
.start_addr = QNOVO_VLIM2_LSB_CTRL,
.num_regs = 2,
.reg_to_unit_multiplier = 610350, /* converts to nV */
.reg_to_unit_divider = 1,
- .min_val = 0,
- .max_val = 5000000,
+ .min_val = 2200000,
+ .max_val = 4500000,
.units_str = "uV",
},
[PVOLT2] = {
@@ -591,6 +598,8 @@ static struct param_info params[] = {
.num_regs = 1,
.reg_to_unit_multiplier = 1,
.reg_to_unit_divider = 1,
+ .min_val = 0,
+ .max_val = 255,
.units_str = "pulses",
},
[VMAX] = {
@@ -648,30 +657,70 @@ static ssize_t ok_to_qnovo_show(struct class *c, struct class_attribute *attr,
return snprintf(buf, PAGE_SIZE, "%d\n", chip->cs.ok_to_qnovo);
}
-static ssize_t enable_show(struct class *c, struct class_attribute *attr,
+static ssize_t qnovo_enable_show(struct class *c, struct class_attribute *attr,
char *ubuf)
{
struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
- int val;
+ int val = get_effective_result(chip->disable_votable);
- val = get_client_vote(chip->disable_votable, USER_VOTER);
- val = !val;
- return snprintf(ubuf, PAGE_SIZE, "%d\n", val);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", !val);
+}
+
+static ssize_t qnovo_enable_store(struct class *c, struct class_attribute *attr,
+ const char *ubuf, size_t count)
+{
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ unsigned long val;
+
+ if (kstrtoul(ubuf, 0, &val))
+ return -EINVAL;
+
+ vote(chip->disable_votable, USER_VOTER, !val, 0);
+
+ return count;
+}
+
+static ssize_t pt_enable_show(struct class *c, struct class_attribute *attr,
+ char *ubuf)
+{
+ int i = attr - qnovo_attributes;
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ u8 buf[2] = {0, 0};
+ u16 regval;
+ int rc;
+
+ rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
+ if (rc < 0) {
+ pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
+ return -EINVAL;
+ }
+ regval = buf[1] << 8 | buf[0];
+
+ return snprintf(ubuf, PAGE_SIZE, "%d\n",
+ (int)(regval & QNOVO_PTRAIN_EN_BIT));
}
-static ssize_t enable_store(struct class *c, struct class_attribute *attr,
+static ssize_t pt_enable_store(struct class *c, struct class_attribute *attr,
const char *ubuf, size_t count)
{
struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
unsigned long val;
- bool disable;
+ int rc = 0;
- if (kstrtoul(ubuf, 10, &val))
+ if (get_effective_result(chip->disable_votable))
return -EINVAL;
- disable = !val;
+ if (kstrtoul(ubuf, 0, &val))
+ return -EINVAL;
+
+ rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT,
+ (bool)val ? QNOVO_PTRAIN_EN_BIT : 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n",
+ (bool)val ? "enable" : "disable", rc);
+ return rc;
+ }
- vote(chip->disable_votable, USER_VOTER, disable, 0);
return count;
}
@@ -688,7 +737,7 @@ static ssize_t val_show(struct class *c, struct class_attribute *attr,
if (i == FCC_REQUEST)
val = chip->fcc_uA_request;
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n", val, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", val);
}
static ssize_t val_store(struct class *c, struct class_attribute *attr,
@@ -698,7 +747,10 @@ static ssize_t val_store(struct class *c, struct class_attribute *attr,
int i = attr - qnovo_attributes;
unsigned long val;
- if (kstrtoul(ubuf, 10, &val))
+ if (get_effective_result(chip->disable_votable))
+ return -EINVAL;
+
+ if (kstrtoul(ubuf, 0, &val))
return -EINVAL;
if (i == FV_REQUEST)
@@ -707,6 +759,8 @@ static ssize_t val_store(struct class *c, struct class_attribute *attr,
if (i == FCC_REQUEST)
chip->fcc_uA_request = val;
+ qnovo_batt_psy_update(chip, false);
+
return count;
}
@@ -726,8 +780,7 @@ static ssize_t reg_show(struct class *c, struct class_attribute *attr,
}
regval = buf[1] << 8 | buf[0];
- return snprintf(ubuf, PAGE_SIZE, "0x%04x%s\n",
- regval, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "0x%04x\n", regval);
}
static ssize_t reg_store(struct class *c, struct class_attribute *attr,
@@ -739,7 +792,7 @@ static ssize_t reg_store(struct class *c, struct class_attribute *attr,
unsigned long val;
int rc;
- if (kstrtoul(ubuf, 16, &val))
+ if (kstrtoul(ubuf, 0, &val))
return -EINVAL;
buf[0] = val & 0xFF;
@@ -774,7 +827,7 @@ static ssize_t time_show(struct class *c, struct class_attribute *attr,
/ params[i].reg_to_unit_divider)
- params[i].reg_to_unit_offset;
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n", val, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", val);
}
static ssize_t time_store(struct class *c, struct class_attribute *attr,
@@ -787,7 +840,7 @@ static ssize_t time_store(struct class *c, struct class_attribute *attr,
unsigned long val;
int rc;
- if (kstrtoul(ubuf, 10, &val))
+ if (kstrtoul(ubuf, 0, &val))
return -EINVAL;
if (val < params[i].min_val || val > params[i].max_val) {
@@ -841,11 +894,10 @@ static ssize_t current_show(struct class *c, struct class_attribute *attr,
gain = chip->internal_i_gain_mega;
}
- comp_val_nA = div_s64(regval_nA * gain, 1000000) + offset_nA;
+ comp_val_nA = div_s64(regval_nA * gain, 1000000) - offset_nA;
comp_val_uA = div_s64(comp_val_nA, 1000);
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
- comp_val_uA, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uA);
}
static ssize_t voltage_show(struct class *c, struct class_attribute *attr,
@@ -875,8 +927,7 @@ static ssize_t voltage_show(struct class *c, struct class_attribute *attr,
comp_val_nV = div_s64(regval_nV * gain, 1000000) + offset_nV;
comp_val_uV = div_s64(comp_val_nV, 1000);
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
- comp_val_uV, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uV);
}
static ssize_t voltage_store(struct class *c, struct class_attribute *attr,
@@ -890,7 +941,7 @@ static ssize_t voltage_store(struct class *c, struct class_attribute *attr,
s64 regval_nV;
s64 gain, offset_nV;
- if (kstrtoul(ubuf, 10, &val_uV))
+ if (kstrtoul(ubuf, 0, &val_uV))
return -EINVAL;
if (val_uV < params[i].min_val || val_uV > params[i].max_val) {
@@ -947,8 +998,7 @@ static ssize_t coulomb_show(struct class *c, struct class_attribute *attr,
gain = chip->internal_i_gain_mega;
comp_val_uC = div_s64(regval_uC * gain, 1000000);
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
- comp_val_uC, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uC);
}
static ssize_t coulomb_store(struct class *c, struct class_attribute *attr,
@@ -962,7 +1012,7 @@ static ssize_t coulomb_store(struct class *c, struct class_attribute *attr,
s64 regval;
s64 gain;
- if (kstrtoul(ubuf, 10, &val_uC))
+ if (kstrtoul(ubuf, 0, &val_uC))
return -EINVAL;
if (val_uC < params[i].min_val || val_uC > params[i].max_val) {
@@ -1014,74 +1064,75 @@ static ssize_t batt_prop_show(struct class *c, struct class_attribute *attr,
return -EINVAL;
}
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
- pval.intval, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", pval.intval);
}
static struct class_attribute qnovo_attributes[] = {
[VER] = __ATTR_RO(version),
[OK_TO_QNOVO] = __ATTR_RO(ok_to_qnovo),
- [ENABLE] = __ATTR(enable, S_IRUGO | S_IWUSR,
- enable_show, enable_store),
- [FV_REQUEST] = __ATTR(fv_uV_request, S_IRUGO | S_IWUSR,
+ [QNOVO_ENABLE] = __ATTR_RW(qnovo_enable),
+ [PT_ENABLE] = __ATTR_RW(pt_enable),
+ [FV_REQUEST] = __ATTR(fv_uV_request, 0644,
val_show, val_store),
- [FCC_REQUEST] = __ATTR(fcc_uA_request, S_IRUGO | S_IWUSR,
+ [FCC_REQUEST] = __ATTR(fcc_uA_request, 0644,
val_show, val_store),
- [PE_CTRL_REG] = __ATTR(PE_CTRL_REG, S_IRUGO | S_IWUSR,
- reg_show, reg_store),
- [PE_CTRL2_REG] = __ATTR(PE_CTRL2_REG, S_IRUGO | S_IWUSR,
+ [PE_CTRL_REG] = __ATTR(PE_CTRL_REG, 0644,
reg_show, reg_store),
- [PTRAIN_STS_REG] = __ATTR(PTRAIN_STS_REG, S_IRUGO | S_IWUSR,
+ [PE_CTRL2_REG] = __ATTR(PE_CTRL2_REG, 0644,
reg_show, reg_store),
- [INT_RT_STS_REG] = __ATTR(INT_RT_STS_REG, S_IRUGO | S_IWUSR,
- reg_show, reg_store),
- [PREST1] = __ATTR(PREST1_mS, S_IRUGO | S_IWUSR,
+ [PTRAIN_STS_REG] = __ATTR(PTRAIN_STS_REG, 0444,
+ reg_show, NULL),
+ [INT_RT_STS_REG] = __ATTR(INT_RT_STS_REG, 0444,
+ reg_show, NULL),
+ [ERR_STS2_REG] = __ATTR(ERR_STS2_REG, 0444,
+ reg_show, NULL),
+ [PREST1] = __ATTR(PREST1_mS, 0644,
time_show, time_store),
- [PPULS1] = __ATTR(PPULS1_uC, S_IRUGO | S_IWUSR,
+ [PPULS1] = __ATTR(PPULS1_uC, 0644,
coulomb_show, coulomb_store),
- [NREST1] = __ATTR(NREST1_mS, S_IRUGO | S_IWUSR,
+ [NREST1] = __ATTR(NREST1_mS, 0644,
time_show, time_store),
- [NPULS1] = __ATTR(NPULS1_mS, S_IRUGO | S_IWUSR,
+ [NPULS1] = __ATTR(NPULS1_mS, 0644,
time_show, time_store),
- [PPCNT] = __ATTR(PPCNT, S_IRUGO | S_IWUSR,
+ [PPCNT] = __ATTR(PPCNT, 0644,
time_show, time_store),
- [VLIM1] = __ATTR(VLIM1_uV, S_IRUGO | S_IWUSR,
+ [VLIM1] = __ATTR(VLIM1_uV, 0644,
voltage_show, voltage_store),
- [PVOLT1] = __ATTR(PVOLT1_uV, S_IRUGO,
+ [PVOLT1] = __ATTR(PVOLT1_uV, 0444,
voltage_show, NULL),
- [PCUR1] = __ATTR(PCUR1_uA, S_IRUGO,
+ [PCUR1] = __ATTR(PCUR1_uA, 0444,
current_show, NULL),
- [PTTIME] = __ATTR(PTTIME_S, S_IRUGO,
+ [PTTIME] = __ATTR(PTTIME_S, 0444,
time_show, NULL),
- [PREST2] = __ATTR(PREST2_mS, S_IRUGO | S_IWUSR,
+ [PREST2] = __ATTR(PREST2_mS, 0644,
time_show, time_store),
- [PPULS2] = __ATTR(PPULS2_mS, S_IRUGO | S_IWUSR,
+ [PPULS2] = __ATTR(PPULS2_uC, 0644,
coulomb_show, coulomb_store),
- [NREST2] = __ATTR(NREST2_mS, S_IRUGO | S_IWUSR,
+ [NREST2] = __ATTR(NREST2_mS, 0644,
time_show, time_store),
- [NPULS2] = __ATTR(NPULS2_mS, S_IRUGO | S_IWUSR,
+ [NPULS2] = __ATTR(NPULS2_mS, 0644,
time_show, time_store),
- [VLIM2] = __ATTR(VLIM2_uV, S_IRUGO | S_IWUSR,
+ [VLIM2] = __ATTR(VLIM2_uV, 0644,
voltage_show, voltage_store),
- [PVOLT2] = __ATTR(PVOLT2_uV, S_IRUGO,
+ [PVOLT2] = __ATTR(PVOLT2_uV, 0444,
voltage_show, NULL),
- [RVOLT2] = __ATTR(RVOLT2_uV, S_IRUGO,
+ [RVOLT2] = __ATTR(RVOLT2_uV, 0444,
voltage_show, NULL),
- [PCUR2] = __ATTR(PCUR2_uA, S_IRUGO,
+ [PCUR2] = __ATTR(PCUR2_uA, 0444,
current_show, NULL),
- [SCNT] = __ATTR(SCNT, S_IRUGO | S_IWUSR,
+ [SCNT] = __ATTR(SCNT, 0644,
time_show, time_store),
- [VMAX] = __ATTR(VMAX_uV, S_IRUGO,
+ [VMAX] = __ATTR(VMAX_uV, 0444,
voltage_show, NULL),
- [SNUM] = __ATTR(SNUM, S_IRUGO | S_IWUSR,
- time_show, time_store),
- [VBATT] = __ATTR(VBATT_uV, S_IRUGO,
+ [SNUM] = __ATTR(SNUM, 0444,
+ time_show, NULL),
+ [VBATT] = __ATTR(VBATT_uV, 0444,
batt_prop_show, NULL),
- [IBATT] = __ATTR(IBATT_uA, S_IRUGO,
+ [IBATT] = __ATTR(IBATT_uA, 0444,
batt_prop_show, NULL),
- [BATTTEMP] = __ATTR(BATTTEMP_deciDegC, S_IRUGO,
+ [BATTTEMP] = __ATTR(BATTTEMP_deciDegC, 0444,
batt_prop_show, NULL),
- [BATTSOC] = __ATTR(BATTSOC, S_IRUGO,
+ [BATTSOC] = __ATTR(BATTSOC, 0444,
batt_prop_show, NULL),
__ATTR_NULL,
};
@@ -1140,8 +1191,7 @@ static void get_chg_status(struct qnovo *chip, const struct chg_props *cp,
{
cs->ok_to_qnovo = false;
- if (cp->charging &&
- (cp->usb_online || cp->dc_online))
+ if (cp->charging && (cp->usb_online || cp->dc_online))
cs->ok_to_qnovo = true;
}
@@ -1157,17 +1207,10 @@ static void status_change_work(struct work_struct *work)
get_chg_status(chip, &cp, &cs);
if (cs.ok_to_qnovo ^ chip->cs.ok_to_qnovo) {
- /*
- * when it is not okay to Qnovo charge, disable both voters,
- * so that when it becomes okay to Qnovo charge the user voter
- * has to specifically enable its vote to being Qnovo charging
- */
- if (!cs.ok_to_qnovo) {
- vote(chip->disable_votable, OK_TO_QNOVO_VOTER, 1, 0);
- vote(chip->disable_votable, USER_VOTER, 1, 0);
- } else {
- vote(chip->disable_votable, OK_TO_QNOVO_VOTER, 0, 0);
- }
+ vote(chip->disable_votable, OK_TO_QNOVO_VOTER,
+ !cs.ok_to_qnovo, 0);
+ if (!cs.ok_to_qnovo)
+ vote(chip->disable_votable, USER_VOTER, true, 0);
notify_uevent = true;
}
@@ -1197,8 +1240,6 @@ static irqreturn_t handle_ptrain_done(int irq, void *data)
{
struct qnovo *chip = data;
- /* disable user voter here */
- vote(chip->disable_votable, USER_VOTER, 0, 0);
kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
return IRQ_HANDLED;
}
@@ -1211,7 +1252,14 @@ static int qnovo_hw_init(struct qnovo *chip)
u8 vadc_offset, vadc_gain;
u8 val;
- vote(chip->disable_votable, USER_VOTER, 1, 0);
+ vote(chip->disable_votable, USER_VOTER, true, 0);
+
+ val = 0;
+ rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1);
+ if (rc < 0) {
+ pr_err("Couldn't write iadc bitstream control rc = %d\n", rc);
+ return rc;
+ }
rc = qnovo_read(chip, QNOVO_IADC_OFFSET_0, &iadc_offset_external, 1);
if (rc < 0) {
@@ -1219,12 +1267,28 @@ static int qnovo_hw_init(struct qnovo *chip)
return rc;
}
+ /* stored as an 8 bit 2's complement signed integer */
+ val = -1 * iadc_offset_external;
+ rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1);
+ if (rc < 0) {
+ pr_err("Couldn't write iadc offset rc = %d\n", rc);
+ return rc;
+ }
+
rc = qnovo_read(chip, QNOVO_IADC_OFFSET_1, &iadc_offset_internal, 1);
if (rc < 0) {
pr_err("Couldn't read iadc internal offset rc = %d\n", rc);
return rc;
}
+ /* stored as an 8 bit 2's complement signed integer */
+ val = -1 * iadc_offset_internal;
+ rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1);
+ if (rc < 0) {
+ pr_err("Couldn't write iadc offset rc = %d\n", rc);
+ return rc;
+ }
+
rc = qnovo_read(chip, QNOVO_IADC_GAIN_0, &iadc_gain_external, 1);
if (rc < 0) {
pr_err("Couldn't read iadc external gain rc = %d\n", rc);
@@ -1249,53 +1313,20 @@ static int qnovo_hw_init(struct qnovo *chip)
return rc;
}
- chip->external_offset_nA = (s64)iadc_offset_external * IADC_LSB_NA;
- chip->internal_offset_nA = (s64)iadc_offset_internal * IADC_LSB_NA;
- chip->offset_nV = (s64)vadc_offset * VADC_LSB_NA;
+ chip->external_offset_nA = (s64)(s8)iadc_offset_external * IADC_LSB_NA;
+ chip->internal_offset_nA = (s64)(s8)iadc_offset_internal * IADC_LSB_NA;
+ chip->offset_nV = (s64)(s8)vadc_offset * VADC_LSB_NA;
chip->external_i_gain_mega
- = 1000000000 + (s64)iadc_gain_external * GAIN_LSB_FACTOR;
+ = 1000000000 + (s64)(s8)iadc_gain_external * GAIN_LSB_FACTOR;
chip->external_i_gain_mega
= div_s64(chip->external_i_gain_mega, 1000);
chip->internal_i_gain_mega
- = 1000000000 + (s64)iadc_gain_internal * GAIN_LSB_FACTOR;
+ = 1000000000 + (s64)(s8)iadc_gain_internal * GAIN_LSB_FACTOR;
chip->internal_i_gain_mega
= div_s64(chip->internal_i_gain_mega, 1000);
- chip->v_gain_mega = 1000000000 + (s64)vadc_gain * GAIN_LSB_FACTOR;
+ chip->v_gain_mega = 1000000000 + (s64)(s8)vadc_gain * GAIN_LSB_FACTOR;
chip->v_gain_mega = div_s64(chip->v_gain_mega, 1000);
- val = 0;
- rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1);
- if (rc < 0) {
- pr_err("Couldn't write iadc bitsteam control rc = %d\n", rc);
- return rc;
- }
-
- rc = qnovo_read(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1);
- if (rc < 0) {
- pr_err("Couldn't read iadc offset rc = %d\n", rc);
- return rc;
- }
-
- val *= -1;
- rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1);
- if (rc < 0) {
- pr_err("Couldn't write iadc offset rc = %d\n", rc);
- return rc;
- }
-
- rc = qnovo_read(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1);
- if (rc < 0) {
- pr_err("Couldn't read iadc offset rc = %d\n", rc);
- return rc;
- }
-
- val *= -1;
- rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1);
- if (rc < 0) {
- pr_err("Couldn't write iadc offset rc = %d\n", rc);
- return rc;
- }
-
return 0;
}
@@ -1333,6 +1364,9 @@ static int qnovo_request_interrupts(struct qnovo *chip)
irq_ptrain_done, rc);
return rc;
}
+
+ enable_irq_wake(irq_ptrain_done);
+
return rc;
}
@@ -1414,6 +1448,8 @@ static int qnovo_probe(struct platform_device *pdev)
goto unreg_notifier;
}
+ device_init_wakeup(chip->dev, true);
+
return rc;
unreg_notifier:
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index bf7aa7a36784..e8249163e948 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -11,6 +11,7 @@
*/
#include <linux/debugfs.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -25,7 +26,7 @@
#include "smb-reg.h"
#include "smb-lib.h"
#include "storm-watch.h"
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define SMB2_DEFAULT_WPWR_UW 8000000
@@ -837,7 +838,9 @@ static enum power_supply_property smb2_batt_props[] = {
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_VOLTAGE_QNOVO,
POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_QNOVO,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TECHNOLOGY,
@@ -857,6 +860,7 @@ static int smb2_batt_get_prop(struct power_supply *psy,
{
struct smb_charger *chg = power_supply_get_drvdata(psy);
int rc = 0;
+ union power_supply_propval pval = {0, };
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
@@ -881,7 +885,14 @@ static int smb2_batt_get_prop(struct power_supply *psy,
rc = smblib_get_prop_system_temp_level(chg, val);
break;
case POWER_SUPPLY_PROP_CHARGER_TEMP:
- rc = smblib_get_prop_charger_temp(chg, val);
+ /* do not query RRADC if charger is not present */
+ rc = smblib_get_prop_usb_present(chg, &pval);
+ if (rc < 0)
+ pr_err("Couldn't get usb present rc=%d\n", rc);
+
+ rc = -ENODATA;
+ if (pval.intval)
+ rc = smblib_get_prop_charger_temp(chg, val);
break;
case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX:
rc = smblib_get_prop_charger_temp_max(chg, val);
@@ -901,6 +912,9 @@ static int smb2_batt_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
val->intval = get_client_vote(chg->fv_votable, DEFAULT_VOTER);
break;
+ case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE:
+ rc = smblib_get_prop_charge_qnovo_enable(chg, val);
+ break;
case POWER_SUPPLY_PROP_VOLTAGE_QNOVO:
val->intval = chg->qnovo_fv_uv;
break;
@@ -976,12 +990,17 @@ static int smb2_batt_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
vote(chg->fv_votable, DEFAULT_VOTER, true, val->intval);
break;
+ case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE:
+ rc = smblib_set_prop_charge_qnovo_enable(chg, val);
+ break;
case POWER_SUPPLY_PROP_VOLTAGE_QNOVO:
chg->qnovo_fv_uv = val->intval;
rc = rerun_election(chg->fv_votable);
break;
case POWER_SUPPLY_PROP_CURRENT_QNOVO:
chg->qnovo_fcc_ua = val->intval;
+ vote(chg->pl_disable_votable, PL_QNOVO_VOTER,
+ val->intval != -EINVAL && val->intval < 2000000, 0);
rc = rerun_election(chg->fcc_votable);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
@@ -1324,6 +1343,7 @@ static int smb2_disable_typec(struct smb_charger *chg)
{
int rc;
+ /* Move to typeC mode */
/* configure FSM in idle state */
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
TYPEC_DISABLE_CMD_BIT, TYPEC_DISABLE_CMD_BIT);
@@ -1332,6 +1352,39 @@ static int smb2_disable_typec(struct smb_charger *chg)
return rc;
}
+ /* wait for FSM to enter idle state */
+ msleep(200);
+ /* configure TypeC mode */
+ rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
+ TYPE_C_OR_U_USB_BIT, 0);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't enable micro USB mode rc=%d\n", rc);
+ return rc;
+ }
+
+ /* wait for mode change before enabling FSM */
+ usleep_range(10000, 11000);
+ /* release FSM from idle state */
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_DISABLE_CMD_BIT, 0);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't release FSM rc=%d\n", rc);
+ return rc;
+ }
+
+ /* wait for FSM to start */
+ msleep(100);
+ /* move to uUSB mode */
+ /* configure FSM in idle state */
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_DISABLE_CMD_BIT, TYPEC_DISABLE_CMD_BIT);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't put FSM in idle rc=%d\n", rc);
+ return rc;
+ }
+
+ /* wait for FSM to enter idle state */
+ msleep(200);
/* configure micro USB mode */
rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
TYPE_C_OR_U_USB_BIT, TYPE_C_OR_U_USB_BIT);
@@ -1340,6 +1393,8 @@ static int smb2_disable_typec(struct smb_charger *chg)
return rc;
}
+ /* wait for mode change before enabling FSM */
+ usleep_range(10000, 11000);
/* release FSM from idle state */
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
TYPEC_DISABLE_CMD_BIT, 0);
@@ -1420,10 +1475,10 @@ static int smb2_init_hw(struct smb2 *chip)
DEFAULT_VOTER, true, chip->dt.fv_uv);
vote(chg->dc_icl_votable,
DEFAULT_VOTER, true, chip->dt.dc_icl_ua);
- vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER,
- chip->dt.hvdcp_disable, 0);
vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
true, 0);
+ vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER,
+ chip->dt.hvdcp_disable, 0);
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER,
true, 0);
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
@@ -1489,13 +1544,6 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
- rc = smblib_masked_write(chg, QNOVO_PT_ENABLE_CMD_REG,
- QNOVO_PT_ENABLE_CMD_BIT, QNOVO_PT_ENABLE_CMD_BIT);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't enable qnovo rc=%d\n", rc);
- return rc;
- }
-
/* configure step charging */
rc = smb2_config_step_charging(chip);
if (rc < 0) {
@@ -1608,6 +1656,15 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
+static int smb2_post_init(struct smb2 *chip)
+{
+ struct smb_charger *chg = &chip->chg;
+
+ rerun_election(chg->usb_irq_enable_votable);
+
+ return 0;
+}
+
static int smb2_chg_config_init(struct smb2 *chip)
{
struct smb_charger *chg = &chip->chg;
@@ -2137,6 +2194,8 @@ static int smb2_probe(struct platform_device *pdev)
goto cleanup;
}
+ smb2_post_init(chip);
+
smb2_create_debugfs(chip);
rc = smblib_get_prop_usb_present(chg, &val);
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 4f126854728f..50af1087278a 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -22,7 +22,7 @@
#include "smb-lib.h"
#include "smb-reg.h"
#include "storm-watch.h"
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define smblib_err(chg, fmt, ...) \
pr_err("%s: %s: " fmt, chg->name, \
@@ -161,39 +161,14 @@ static int smblib_get_jeita_cc_delta(struct smb_charger *chg, int *cc_delta_ua)
int smblib_icl_override(struct smb_charger *chg, bool override)
{
int rc;
- bool override_status;
- u8 stat;
- u16 reg;
- switch (chg->smb_version) {
- case PMI8998_SUBTYPE:
- reg = APSD_RESULT_STATUS_REG;
- break;
- case PM660_SUBTYPE:
- reg = AICL_STATUS_REG;
- break;
- default:
- smblib_dbg(chg, PR_MISC, "Unknown chip version=%x\n",
- chg->smb_version);
- return -EINVAL;
- }
-
- rc = smblib_read(chg, reg, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read reg=%x rc=%d\n", reg, rc);
- return rc;
- }
- override_status = (bool)(stat & ICL_OVERRIDE_LATCH_BIT);
+ rc = smblib_masked_write(chg, USBIN_LOAD_CFG_REG,
+ ICL_OVERRIDE_AFTER_APSD_BIT,
+ override ? ICL_OVERRIDE_AFTER_APSD_BIT : 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't override ICL rc=%d\n", rc);
- if (override != override_status) {
- rc = smblib_masked_write(chg, CMD_APSD_REG,
- ICL_OVERRIDE_BIT, ICL_OVERRIDE_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't override ICL rc=%d\n", rc);
- return rc;
- }
- }
- return 0;
+ return rc;
}
/********************
@@ -727,21 +702,6 @@ static void smblib_uusb_removal(struct smb_charger *chg)
rc);
}
-static bool smblib_sysok_reason_usbin(struct smb_charger *chg)
-{
- int rc;
- u8 stat;
-
- rc = smblib_read(chg, SYSOK_REASON_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get SYSOK_REASON_STATUS rc=%d\n", rc);
- /* assuming 'not usbin' in case of read failure */
- return false;
- }
-
- return stat & SYSOK_REASON_USBIN_BIT;
-}
-
void smblib_suspend_on_debug_battery(struct smb_charger *chg)
{
int rc;
@@ -779,11 +739,32 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg)
return 0;
apsd_result = smblib_get_apsd_result(chg);
- if ((apsd_result->pst == POWER_SUPPLY_TYPE_UNKNOWN)
- || (apsd_result->pst == POWER_SUPPLY_TYPE_USB)) {
- smblib_rerun_apsd(chg);
+ if ((apsd_result->pst != POWER_SUPPLY_TYPE_UNKNOWN)
+ && (apsd_result->pst != POWER_SUPPLY_TYPE_USB))
+ /* if type is not usb or unknown no need to rerun apsd */
+ return 0;
+
+ /* fetch the DPDM regulator */
+ if (!chg->dpdm_reg && of_get_property(chg->dev->of_node,
+ "dpdm-supply", NULL)) {
+ chg->dpdm_reg = devm_regulator_get(chg->dev, "dpdm");
+ if (IS_ERR(chg->dpdm_reg)) {
+ smblib_err(chg, "Couldn't get dpdm regulator rc=%ld\n",
+ PTR_ERR(chg->dpdm_reg));
+ chg->dpdm_reg = NULL;
+ }
}
+ if (chg->dpdm_reg && !regulator_is_enabled(chg->dpdm_reg)) {
+ smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n");
+ rc = regulator_enable(chg->dpdm_reg);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable dpdm regulator rc=%d\n",
+ rc);
+ }
+
+ smblib_rerun_apsd(chg);
+
return 0;
}
@@ -1098,16 +1079,6 @@ static int smblib_apsd_disable_vote_callback(struct votable *votable,
int rc;
if (apsd_disable) {
- /* Don't run APSD on CC debounce when APSD is disabled */
- rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
- APSD_START_ON_CC_BIT,
- 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n",
- rc);
- return rc;
- }
-
rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
AUTO_SRC_DETECT_BIT,
0);
@@ -1123,15 +1094,6 @@ static int smblib_apsd_disable_vote_callback(struct votable *votable,
smblib_err(chg, "Couldn't enable APSD rc=%d\n", rc);
return rc;
}
-
- rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
- APSD_START_ON_CC_BIT,
- APSD_START_ON_CC_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n",
- rc);
- return rc;
- }
}
return 0;
@@ -1168,6 +1130,26 @@ static int smblib_hvdcp_hw_inov_dis_vote_callback(struct votable *votable,
return rc;
}
+static int smblib_usb_irq_enable_vote_callback(struct votable *votable,
+ void *data, int enable, const char *client)
+{
+ struct smb_charger *chg = data;
+
+ if (!chg->irq_info[INPUT_CURRENT_LIMIT_IRQ].irq ||
+ !chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq)
+ return 0;
+
+ if (enable) {
+ enable_irq(chg->irq_info[INPUT_CURRENT_LIMIT_IRQ].irq);
+ enable_irq(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq);
+ } else {
+ disable_irq(chg->irq_info[INPUT_CURRENT_LIMIT_IRQ].irq);
+ disable_irq(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq);
+ }
+
+ return 0;
+}
+
/*******************
* VCONN REGULATOR *
* *****************/
@@ -1740,6 +1722,23 @@ int smblib_get_prop_batt_charge_done(struct smb_charger *chg,
return 0;
}
+int smblib_get_prop_charge_qnovo_enable(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc;
+ u8 stat;
+
+ rc = smblib_read(chg, QNOVO_PT_ENABLE_CMD_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read QNOVO_PT_ENABLE_CMD rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val->intval = (bool)(stat & QNOVO_PT_ENABLE_CMD_BIT);
+ return 0;
+}
+
/***********************
* BATTERY PSY SETTERS *
***********************/
@@ -1804,6 +1803,22 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg,
return 0;
}
+int smblib_set_prop_charge_qnovo_enable(struct smb_charger *chg,
+ const union power_supply_propval *val)
+{
+ int rc = 0;
+
+ rc = smblib_masked_write(chg, QNOVO_PT_ENABLE_CMD_REG,
+ QNOVO_PT_ENABLE_CMD_BIT,
+ val->intval ? QNOVO_PT_ENABLE_CMD_BIT : 0);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't enable qnovo rc=%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
int smblib_rerun_aicl(struct smb_charger *chg)
{
int rc, settled_icl_ua;
@@ -2510,10 +2525,6 @@ int smblib_set_prop_usb_voltage_max(struct smb_charger *chg,
}
chg->voltage_max_uv = max_uv;
- rc = smblib_rerun_aicl(chg);
- if (rc < 0)
- smblib_err(chg, "Couldn't re-run AICL rc=%d\n", rc);
-
return rc;
}
@@ -2533,6 +2544,7 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
vote(chg->apsd_disable_votable, PD_VOTER, pd_active, 0);
vote(chg->pd_allowed_votable, PD_VOTER, pd_active, 0);
+ vote(chg->usb_irq_enable_votable, PD_VOTER, pd_active, 0);
/*
* VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2 line
@@ -2568,6 +2580,9 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
return rc;
}
+ /* since PD was found the cable must be non-legacy */
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
+
/* clear USB ICL vote for DCP_VOTER */
rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
if (rc < 0)
@@ -2685,12 +2700,6 @@ int smblib_reg_block_restore(struct smb_charger *chg,
static struct reg_info cc2_detach_settings[] = {
{
- .reg = TYPE_C_CFG_REG,
- .mask = APSD_START_ON_CC_BIT,
- .val = 0,
- .desc = "TYPE_C_CFG_REG",
- },
- {
.reg = TYPE_C_CFG_2_REG,
.mask = TYPE_C_UFP_MODE_BIT | EN_TRY_SOURCE_MODE_BIT,
.val = TYPE_C_UFP_MODE_BIT,
@@ -3370,6 +3379,10 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
/* could be a legacy cable, try doing hvdcp */
try_rerun_apsd_for_hvdcp(chg);
+ /* enable HDC and ICL irq for QC2/3 charger */
+ if (qc_charger)
+ vote(chg->usb_irq_enable_votable, QC_VOTER, true, 0);
+
/*
* HVDCP detection timeout done
* If adapter is not QC2.0/QC3.0 - it is a plain old DCP.
@@ -3406,6 +3419,37 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg,
rising ? "rising" : "falling");
}
+static void smblib_force_legacy_icl(struct smb_charger *chg, int pst)
+{
+ switch (pst) {
+ case POWER_SUPPLY_TYPE_USB:
+ /*
+ * USB_PSY will vote to increase the current to 500/900mA once
+ * enumeration is done. Ensure that USB_PSY has at least voted
+ * for 100mA before releasing the LEGACY_UNKNOWN vote
+ */
+ if (!is_client_vote_enabled(chg->usb_icl_votable,
+ USB_PSY_VOTER))
+ vote(chg->usb_icl_votable, USB_PSY_VOTER, true, 100000);
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
+ break;
+ case POWER_SUPPLY_TYPE_USB_CDP:
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000);
+ break;
+ case POWER_SUPPLY_TYPE_USB_DCP:
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000);
+ break;
+ case POWER_SUPPLY_TYPE_USB_HVDCP:
+ case POWER_SUPPLY_TYPE_USB_HVDCP_3:
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 3000000);
+ break;
+ default:
+ smblib_err(chg, "Unknown APSD %d; forcing 500mA\n", pst);
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 500000);
+ break;
+ }
+}
+
#define HVDCP_DET_MS 2500
static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
{
@@ -3415,6 +3459,10 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
return;
apsd_result = smblib_update_usb_type(chg);
+
+ if (!chg->pd_active)
+ smblib_force_legacy_icl(chg, apsd_result->pst);
+
switch (apsd_result->bit) {
case SDP_CHARGER_BIT:
case CDP_CHARGER_BIT:
@@ -3495,6 +3543,9 @@ static void typec_source_removal(struct smb_charger *chg)
{
int rc;
+ /* reset legacy unknown vote */
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
+
/* reset both usbin current and voltage votes */
vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
@@ -3548,6 +3599,15 @@ static void typec_source_removal(struct smb_charger *chg)
static void typec_source_insertion(struct smb_charger *chg)
{
+ /*
+ * at any time we want LEGACY_UNKNOWN, PD, or USB_PSY to be voting for
+ * ICL, so vote LEGACY_UNKNOWN here if none of the above three have
+ * casted their votes
+ */
+ if (!is_client_vote_enabled(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER)
+ && !is_client_vote_enabled(chg->usb_icl_votable, PD_VOTER)
+ && !is_client_vote_enabled(chg->usb_icl_votable, USB_PSY_VOTER))
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000);
}
static void typec_sink_insertion(struct smb_charger *chg)
@@ -3568,11 +3628,13 @@ static void typec_sink_removal(struct smb_charger *chg)
static void smblib_handle_typec_removal(struct smb_charger *chg)
{
+ int rc;
+
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0);
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, true, 0);
- vote(chg->pd_disallowed_votable_indirect, LEGACY_CABLE_VOTER, true, 0);
- vote(chg->pd_disallowed_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0);
vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, true, 0);
+ vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
+ vote(chg->usb_irq_enable_votable, QC_VOTER, false, 0);
/* reset votes from vbus_cc_short */
vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
@@ -3590,10 +3652,13 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
chg->pulse_cnt = 0;
chg->usb_icl_delta_ua = 0;
- chg->usb_ever_removed = true;
+ /* enable APSD CC trigger for next insertion */
+ rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
+ APSD_START_ON_CC_BIT, APSD_START_ON_CC_BIT);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n", rc);
smblib_update_usb_type(chg);
-
typec_source_removal(chg);
typec_sink_removal(chg);
}
@@ -3601,12 +3666,16 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
static void smblib_handle_typec_insertion(struct smb_charger *chg,
bool sink_attached, bool legacy_cable)
{
- int rp;
- bool vbus_cc_short = false;
- bool valid_legacy_cable;
+ int rp, rc;
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0);
+ /* disable APSD CC trigger since CC is attached */
+ rc = smblib_masked_write(chg, TYPE_C_CFG_REG, APSD_START_ON_CC_BIT, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n",
+ rc);
+
if (sink_attached) {
typec_source_removal(chg);
typec_sink_insertion(chg);
@@ -3615,25 +3684,18 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg,
typec_sink_removal(chg);
}
- valid_legacy_cable = legacy_cable &&
- (chg->usb_ever_removed || !smblib_sysok_reason_usbin(chg));
- vote(chg->pd_disallowed_votable_indirect, LEGACY_CABLE_VOTER,
- valid_legacy_cable, 0);
-
- if (valid_legacy_cable) {
- rp = smblib_get_prop_ufp_mode(chg);
- if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH
- || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) {
- vbus_cc_short = true;
- smblib_err(chg, "Disabling PD and HVDCP, VBUS-CC shorted, rp = %d found\n",
- rp);
- }
+ rp = smblib_get_prop_ufp_mode(chg);
+ if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH
+ || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) {
+ smblib_dbg(chg, PR_MISC, "VBUS & CC could be shorted; keeping HVDCP disabled\n");
+ /* HVDCP is not going to be enabled; enable parallel */
+ vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, false, 0);
+ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
+ true, 0);
+ } else {
+ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
+ false, 0);
}
-
- vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
- vbus_cc_short, 0);
- vote(chg->pd_disallowed_votable_indirect, VBUS_CC_SHORT_VOTER,
- vbus_cc_short, 0);
}
static void smblib_handle_typec_debounce_done(struct smb_charger *chg,
@@ -4255,6 +4317,15 @@ static int smblib_create_votables(struct smb_charger *chg)
return rc;
}
+ chg->usb_irq_enable_votable = create_votable("USB_IRQ_DISABLE",
+ VOTE_SET_ANY,
+ smblib_usb_irq_enable_vote_callback,
+ chg);
+ if (IS_ERR(chg->usb_irq_enable_votable)) {
+ rc = PTR_ERR(chg->usb_irq_enable_votable);
+ return rc;
+ }
+
return rc;
}
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index d6afca010502..32a1c29bb376 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -32,10 +32,12 @@ enum print_reason {
#define USER_VOTER "USER_VOTER"
#define PD_VOTER "PD_VOTER"
#define DCP_VOTER "DCP_VOTER"
+#define QC_VOTER "QC_VOTER"
#define PL_USBIN_USBIN_VOTER "PL_USBIN_USBIN_VOTER"
#define USB_PSY_VOTER "USB_PSY_VOTER"
#define PL_TAPER_WORK_RUNNING_VOTER "PL_TAPER_WORK_RUNNING_VOTER"
#define PL_INDIRECT_VOTER "PL_INDIRECT_VOTER"
+#define PL_QNOVO_VOTER "PL_QNOVO_VOTER"
#define USBIN_I_VOTER "USBIN_I_VOTER"
#define USBIN_V_VOTER "USBIN_V_VOTER"
#define CHG_STATE_VOTER "CHG_STATE_VOTER"
@@ -47,7 +49,6 @@ enum print_reason {
#define PD_DISALLOWED_INDIRECT_VOTER "PD_DISALLOWED_INDIRECT_VOTER"
#define PD_HARD_RESET_VOTER "PD_HARD_RESET_VOTER"
#define VBUS_CC_SHORT_VOTER "VBUS_CC_SHORT_VOTER"
-#define LEGACY_CABLE_VOTER "LEGACY_CABLE_VOTER"
#define PD_INACTIVE_VOTER "PD_INACTIVE_VOTER"
#define BOOST_BACK_VOTER "BOOST_BACK_VOTER"
#define HVDCP_INDIRECT_VOTER "HVDCP_INDIRECT_VOTER"
@@ -58,6 +59,7 @@ enum print_reason {
#define CTM_VOTER "CTM_VOTER"
#define SW_QC3_VOTER "SW_QC3_VOTER"
#define AICL_RERUN_VOTER "AICL_RERUN_VOTER"
+#define LEGACY_UNKNOWN_VOTER "LEGACY_UNKNOWN_VOTER"
#define VCONN_MAX_ATTEMPTS 3
#define OTG_MAX_ATTEMPTS 3
@@ -272,6 +274,7 @@ struct smb_charger {
struct votable *hvdcp_enable_votable;
struct votable *apsd_disable_votable;
struct votable *hvdcp_hw_inov_dis_votable;
+ struct votable *usb_irq_enable_votable;
/* work */
struct work_struct bms_update_work;
@@ -312,10 +315,10 @@ struct smb_charger {
u32 wa_flags;
enum cc2_sink_type cc2_sink_detach_flag;
int boost_current_ua;
+ int temp_speed_reading_count;
/* extcon for VBUS / ID notification to USB for uUSB */
struct extcon_dev *extcon;
- bool usb_ever_removed;
int icl_reduction_ua;
@@ -455,6 +458,8 @@ int smblib_get_prop_charger_temp_max(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_die_health(struct smb_charger *chg,
union power_supply_propval *val);
+int smblib_get_prop_charge_qnovo_enable(struct smb_charger *chg,
+ union power_supply_propval *val);
int smblib_set_prop_pd_current_max(struct smb_charger *chg,
const union power_supply_propval *val);
int smblib_set_prop_usb_current_max(struct smb_charger *chg,
@@ -475,6 +480,8 @@ int smblib_get_prop_slave_current_now(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_set_prop_ship_mode(struct smb_charger *chg,
const union power_supply_propval *val);
+int smblib_set_prop_charge_qnovo_enable(struct smb_charger *chg,
+ const union power_supply_propval *val);
void smblib_suspend_on_debug_battery(struct smb_charger *chg);
int smblib_rerun_apsd_if_required(struct smb_charger *chg);
int smblib_get_prop_fcc_delta(struct smb_charger *chg,
diff --git a/drivers/power/supply/qcom/smb-reg.h b/drivers/power/supply/qcom/smb-reg.h
index 54b6b38d134b..f7c13390d477 100644
--- a/drivers/power/supply/qcom/smb-reg.h
+++ b/drivers/power/supply/qcom/smb-reg.h
@@ -628,6 +628,7 @@ enum {
#define USBIN_LOAD_CFG_REG (USBIN_BASE + 0x65)
#define USBIN_OV_CH_LOAD_OPTION_BIT BIT(7)
+#define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4)
#define USBIN_ICL_OPTIONS_REG (USBIN_BASE + 0x66)
#define CFG_USB3P0_SEL_BIT BIT(2)
diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c
index 1c7c1e78699f..c13f5103b02e 100644
--- a/drivers/power/supply/qcom/smb138x-charger.c
+++ b/drivers/power/supply/qcom/smb138x-charger.c
@@ -28,7 +28,7 @@
#include "smb-reg.h"
#include "smb-lib.h"
#include "storm-watch.h"
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define SMB138X_DEFAULT_FCC_UA 1000000
#define SMB138X_DEFAULT_ICL_UA 1500000
@@ -45,6 +45,7 @@
#define STACKED_DIODE_EN_BIT BIT(2)
#define TDIE_AVG_COUNT 10
+#define MAX_SPEED_READING_TIMES 5
enum {
OOB_COMP_WA_BIT = BIT(0),
@@ -95,6 +96,7 @@ struct smb_dt_props {
int dc_icl_ua;
int chg_temp_max_mdegc;
int connector_temp_max_mdegc;
+ int pl_mode;
};
struct smb138x {
@@ -126,8 +128,16 @@ static int smb138x_get_prop_charger_temp(struct smb138x *chip,
union power_supply_propval pval;
int rc = 0, avg = 0, i;
struct smb_charger *chg = &chip->chg;
+ int die_avg_count;
- for (i = 0; i < TDIE_AVG_COUNT; i++) {
+ if (chg->temp_speed_reading_count < MAX_SPEED_READING_TIMES) {
+ chg->temp_speed_reading_count++;
+ die_avg_count = 1;
+ } else {
+ die_avg_count = TDIE_AVG_COUNT;
+ }
+
+ for (i = 0; i < die_avg_count; i++) {
pval.intval = 0;
rc = smblib_get_prop_charger_temp(chg, &pval);
if (rc < 0) {
@@ -137,7 +147,7 @@ static int smb138x_get_prop_charger_temp(struct smb138x *chip,
}
avg += pval.intval;
}
- val->intval = avg / TDIE_AVG_COUNT;
+ val->intval = avg / die_avg_count;
return rc;
}
@@ -152,6 +162,11 @@ static int smb138x_parse_dt(struct smb138x *chip)
return -EINVAL;
}
+ rc = of_property_read_u32(node,
+ "qcom,parallel-mode", &chip->dt.pl_mode);
+ if (rc < 0)
+ chip->dt.pl_mode = POWER_SUPPLY_PL_USBMID_USBMID;
+
chip->dt.suspend_input = of_property_read_bool(node,
"qcom,suspend-input");
@@ -579,7 +594,7 @@ static int smb138x_parallel_get_prop(struct power_supply *psy,
val->strval = "smb138x";
break;
case POWER_SUPPLY_PROP_PARALLEL_MODE:
- val->intval = POWER_SUPPLY_PL_USBMID_USBMID;
+ val->intval = chip->dt.pl_mode;
break;
case POWER_SUPPLY_PROP_CONNECTOR_HEALTH:
val->intval = smb138x_get_prop_connector_health(chip);
@@ -1449,6 +1464,15 @@ static int smb138x_slave_probe(struct smb138x *chip)
goto cleanup;
}
+ if (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) {
+ rc = smb138x_init_vbus_regulator(chip);
+ if (rc < 0) {
+ pr_err("Couldn't initialize vbus regulator rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
rc = smb138x_init_parallel_psy(chip);
if (rc < 0) {
pr_err("Couldn't initialize parallel psy rc=%d\n", rc);
@@ -1473,6 +1497,8 @@ cleanup:
smblib_deinit(chg);
if (chip->parallel_psy)
power_supply_unregister(chip->parallel_psy);
+ if (chg->vbus_vreg && chg->vbus_vreg->rdev)
+ regulator_unregister(chg->vbus_vreg->rdev);
return rc;
}
diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index 117fccf7934a..01a6a83f625d 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -65,7 +65,6 @@
#define PCA9685_MAXCHAN 0x10
#define LED_FULL (1 << 4)
-#define MODE1_RESTART (1 << 7)
#define MODE1_SLEEP (1 << 4)
#define MODE2_INVRT (1 << 4)
#define MODE2_OUTDRV (1 << 2)
@@ -117,16 +116,6 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
udelay(500);
pca->period_ns = period_ns;
-
- /*
- * If the duty cycle did not change, restart PWM with
- * the same duty cycle to period ratio and return.
- */
- if (duty_ns == pca->duty_ns) {
- regmap_update_bits(pca->regmap, PCA9685_MODE1,
- MODE1_RESTART, 0x1);
- return 0;
- }
} else {
dev_err(chip->dev,
"prescaler not set: period out of bounds!\n");
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index a878dc6a97db..4f9de7fda612 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -2931,7 +2931,8 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
goto out2;
if (rdev->supply && (rdev->desc->min_dropout_uV ||
- !rdev->desc->ops->get_voltage)) {
+ !(rdev->desc->ops->get_voltage ||
+ rdev->desc->ops->get_voltage_sel))) {
int current_supply_uV;
int selector;
@@ -4704,12 +4705,13 @@ static void regulator_summary_show_subtree(struct seq_file *s,
seq_puts(s, "\n");
list_for_each_entry(consumer, &rdev->consumer_list, list) {
- if (consumer->dev->class == &regulator_class)
+ if (consumer->dev && consumer->dev->class == &regulator_class)
continue;
seq_printf(s, "%*s%-*s ",
(level + 1) * 3 + 1, "",
- 30 - (level + 1) * 3, dev_name(consumer->dev));
+ 30 - (level + 1) * 3,
+ consumer->dev ? dev_name(consumer->dev) : "deviceless");
switch (rdev->desc->type) {
case REGULATOR_VOLTAGE:
diff --git a/drivers/regulator/cpr3-hmss-regulator.c b/drivers/regulator/cpr3-hmss-regulator.c
index 77e8bf4b9895..9a20b6925877 100644
--- a/drivers/regulator/cpr3-hmss-regulator.c
+++ b/drivers/regulator/cpr3-hmss-regulator.c
@@ -87,8 +87,9 @@ struct cpr3_msm8996_hmss_fuses {
/*
* Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 with speed bin fuse = 0.
* Fuse combos 8 - 15 map to CPR fusing revision 0 - 7 with speed bin fuse = 1.
+ * Fuse combos 16 - 23 map to CPR fusing revision 0 - 7 with speed bin fuse = 2.
*/
-#define CPR3_MSM8996_HMSS_FUSE_COMBO_COUNT 16
+#define CPR3_MSM8996_HMSS_FUSE_COMBO_COUNT 24
/*
* Constants which define the name of each fuse corner. Note that no actual
@@ -420,6 +421,9 @@ static const int msm8996_vdd_mx_fuse_ret_volt[] = {
#define MSM8996_HMSS_AGING_SENSOR_ID 11
#define MSM8996_HMSS_AGING_BYPASS_MASK0 (GENMASK(7, 0) & ~BIT(3))
+/* Use scaled gate count (GCNT) for aging measurements */
+#define MSM8996_HMSS_AGING_GCNT_SCALING_FACTOR 1500
+
/**
* cpr3_msm8996_hmss_use_voltage_offset_fuse() - return if this part utilizes
* voltage offset fuses or not
@@ -1541,6 +1545,8 @@ static int cpr3_hmss_init_aging(struct cpr3_controller *ctrl)
ctrl->aging_sensor->sensor_id = MSM8996_HMSS_AGING_SENSOR_ID;
ctrl->aging_sensor->bypass_mask[0] = MSM8996_HMSS_AGING_BYPASS_MASK0;
ctrl->aging_sensor->ro_scale = aging_ro_scale;
+ ctrl->aging_gcnt_scaling_factor
+ = MSM8996_HMSS_AGING_GCNT_SCALING_FACTOR;
ctrl->aging_sensor->init_quot_diff
= cpr3_convert_open_loop_voltage_fuse(0,
diff --git a/drivers/regulator/cpr3-mmss-regulator.c b/drivers/regulator/cpr3-mmss-regulator.c
index 41032dd3c15a..5a031f503b51 100644
--- a/drivers/regulator/cpr3-mmss-regulator.c
+++ b/drivers/regulator/cpr3-mmss-regulator.c
@@ -72,8 +72,9 @@ struct cpr3_msm8996_mmss_fuses {
/*
* Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 with speed bin fuse = 0.
* Fuse combos 8 - 15 map to CPR fusing revision 0 - 7 with speed bin fuse = 1.
+ * Fuse combos 16 - 23 map to CPR fusing revision 0 - 7 with speed bin fuse = 2.
*/
-#define CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT 16
+#define CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT 24
/* Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 */
#define CPR3_MSM8998_MMSS_FUSE_COMBO_COUNT 8
@@ -245,6 +246,9 @@ msm8998_v2_rev0_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = {
#define MSM8996_MMSS_AGING_SENSOR_ID 29
#define MSM8996_MMSS_AGING_BYPASS_MASK0 (GENMASK(23, 0))
+/* Use scaled gate count (GCNT) for aging measurements */
+#define MSM8996_MMSS_AGING_GCNT_SCALING_FACTOR 1500
+
#define MSM8998_MMSS_AGING_INIT_QUOT_DIFF_SCALE 1
#define MSM8998_MMSS_AGING_INIT_QUOT_DIFF_SIZE 8
@@ -808,6 +812,8 @@ static int cpr3_mmss_init_aging(struct cpr3_controller *ctrl)
return -ENOMEM;
ctrl->aging_sensor->ro_scale = aging_ro_scale;
+ ctrl->aging_gcnt_scaling_factor
+ = MSM8996_MMSS_AGING_GCNT_SCALING_FACTOR;
if (cpr3_ctrl_is_msm8998(ctrl)) {
ctrl->aging_sensor->sensor_id = MSM8998_MMSS_AGING_SENSOR_ID;
diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c
index 3ffe094fe53c..d131a8ea4144 100644
--- a/drivers/regulator/cpr3-regulator.c
+++ b/drivers/regulator/cpr3-regulator.c
@@ -317,6 +317,12 @@
*/
#define CPRH_DELTA_QUOT_STEP_FACTOR 4
+/*
+ * The multiplier applied to scaling factor value used to derive GCNT
+ * for aging measurements.
+ */
+#define CPR3_AGING_GCNT_SCALING_UNITY 1000
+
static DEFINE_MUTEX(cpr3_controller_list_mutex);
static LIST_HEAD(cpr3_controller_list);
static struct dentry *cpr3_debugfs_base;
@@ -1011,7 +1017,8 @@ static int cpr3_controller_program_sdelta(struct cpr3_controller *ctrl)
max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT
| ((sdelta->allow_core_count_adj || sdelta->allow_boost)
? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0)
- | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop)
+ | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop
+ && sdelta->allow_core_count_adj)
? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0)
| (((ctrl->use_hw_closed_loop && !sdelta->allow_boost)
|| !ctrl->supports_hw_closed_loop)
@@ -3563,7 +3570,12 @@ static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl,
gcnt0_restore = cpr3_read(ctrl, CPR3_REG_GCNT(0));
gcnt1_restore = cpr3_read(ctrl, CPR3_REG_GCNT(1));
gcnt_ref = cpr3_regulator_get_gcnt(ctrl);
- gcnt = gcnt_ref * 3 / 2;
+
+ gcnt = gcnt_ref;
+ if (ctrl->aging_gcnt_scaling_factor)
+ gcnt = gcnt_ref * ctrl->aging_gcnt_scaling_factor
+ / CPR3_AGING_GCNT_SCALING_UNITY;
+
cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt);
cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt);
diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h
index 1ac0f7b816b3..99c86e7b70c9 100644
--- a/drivers/regulator/cpr3-regulator.h
+++ b/drivers/regulator/cpr3-regulator.h
@@ -702,6 +702,13 @@ struct cpr3_panic_regs_info {
* @aging_possible_val: Optional value that the masked aging_possible_reg
* register must have in order for a CPR aging measurement
* to be possible.
+ * @aging_gcnt_scaling_factor: The scaling factor used to derive the gate count
+ * used for aging measurements. This value is divided by
+ * 1000 when used as shown in the below equation:
+ * Aging_GCNT = GCNT_REF * scaling_factor / 1000.
+ * For example, a value of 1500 specifies that the gate
+ * count (GCNT) used for aging measurement should be 1.5
+ * times of reference gate count (GCNT_REF).
* @step_quot_fixed: Fixed step quotient value used for target quotient
* adjustment if use_dynamic_step_quot is not set.
* This parameter is only relevant for CPR4 controllers
@@ -827,6 +834,7 @@ struct cpr3_controller {
int aging_sensor_count;
u32 aging_possible_mask;
u32 aging_possible_val;
+ u32 aging_gcnt_scaling_factor;
u32 step_quot_fixed;
u32 initial_temp_band;
diff --git a/drivers/regulator/spm-regulator.c b/drivers/regulator/spm-regulator.c
index c75beec59a79..484f0412addf 100644
--- a/drivers/regulator/spm-regulator.c
+++ b/drivers/regulator/spm-regulator.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -118,6 +118,12 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000,
#define QPNP_FTS2_STEP_MARGIN_NUM 4
#define QPNP_FTS2_STEP_MARGIN_DEN 5
+/*
+ * Settling delay for FTS2.5
+ * Warm-up=20uS, 0-10% & 90-100% non-linear V-ramp delay = 50uS
+ */
+#define FTS2P5_SETTLING_DELAY_US 70
+
/* VSET value to decide the range of ULT SMPS */
#define ULT_SMPS_RANGE_SPLIT 0x60
@@ -268,6 +274,7 @@ static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV)
unsigned vlevel = spm_regulator_uv_to_vlevel(vreg, uV);
bool spm_failed = false;
int rc = 0;
+ u32 slew_delay;
u8 reg;
if (likely(!vreg->bypass_spm)) {
@@ -295,7 +302,16 @@ static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV)
if (uV > vreg->last_set_uV) {
/* Wait for voltage stepping to complete. */
- udelay(DIV_ROUND_UP(uV - vreg->last_set_uV, vreg->step_rate));
+ slew_delay = DIV_ROUND_UP(uV - vreg->last_set_uV,
+ vreg->step_rate);
+ if (vreg->regulator_type == QPNP_TYPE_FTS2p5)
+ slew_delay += FTS2P5_SETTLING_DELAY_US;
+ udelay(slew_delay);
+ } else if (vreg->regulator_type == QPNP_TYPE_FTS2p5) {
+ /* add the ramp-down delay */
+ slew_delay = DIV_ROUND_UP(vreg->last_set_uV - uV,
+ vreg->step_rate) + FTS2P5_SETTLING_DELAY_US;
+ udelay(slew_delay);
}
vreg->last_set_uV = uV;
diff --git a/drivers/regulator/stw481x-vmmc.c b/drivers/regulator/stw481x-vmmc.c
index 7d2ae3e9e942..342f5da79975 100644
--- a/drivers/regulator/stw481x-vmmc.c
+++ b/drivers/regulator/stw481x-vmmc.c
@@ -47,7 +47,8 @@ static struct regulator_desc vmmc_regulator = {
.volt_table = stw481x_vmmc_voltages,
.enable_time = 200, /* FIXME: look this up */
.enable_reg = STW_CONF1,
- .enable_mask = STW_CONF1_PDN_VMMC,
+ .enable_mask = STW_CONF1_PDN_VMMC | STW_CONF1_MMC_LS_STATUS,
+ .enable_val = STW_CONF1_PDN_VMMC,
.vsel_reg = STW_CONF1,
.vsel_mask = STW_CONF1_VMMC_MASK,
};
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 5836751b8203..9bb934ed2a7a 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -748,9 +748,23 @@ EXPORT_SYMBOL_GPL(rtc_irq_set_freq);
*/
static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)
{
+ struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue);
+ struct rtc_time tm;
+ ktime_t now;
+
timer->enabled = 1;
+ __rtc_read_time(rtc, &tm);
+ now = rtc_tm_to_ktime(tm);
+
+ /* Skip over expired timers */
+ while (next) {
+ if (next->expires.tv64 >= now.tv64)
+ break;
+ next = timerqueue_iterate_next(next);
+ }
+
timerqueue_add(&rtc->timerqueue, &timer->node);
- if (&timer->node == timerqueue_getnext(&rtc->timerqueue)) {
+ if (!next) {
struct rtc_wkalrm alarm;
int err;
alarm.time = rtc_ktime_to_tm(timer->node.expires);
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
index c169a2cd4727..e29cc9fca0bf 100644
--- a/drivers/rtc/rtc-sun6i.c
+++ b/drivers/rtc/rtc-sun6i.c
@@ -37,9 +37,11 @@
/* Control register */
#define SUN6I_LOSC_CTRL 0x0000
+#define SUN6I_LOSC_CTRL_KEY (0x16aa << 16)
#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9)
#define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8)
#define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7)
+#define SUN6I_LOSC_CTRL_EXT_OSC BIT(0)
#define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7)
/* RTC */
@@ -114,13 +116,17 @@ struct sun6i_rtc_dev {
void __iomem *base;
int irq;
unsigned long alarm;
+
+ spinlock_t lock;
};
static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
{
struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id;
+ irqreturn_t ret = IRQ_NONE;
u32 val;
+ spin_lock(&chip->lock);
val = readl(chip->base + SUN6I_ALRM_IRQ_STA);
if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) {
@@ -129,10 +135,11 @@ static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
- return IRQ_HANDLED;
+ ret = IRQ_HANDLED;
}
+ spin_unlock(&chip->lock);
- return IRQ_NONE;
+ return ret;
}
static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
@@ -140,6 +147,7 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
u32 alrm_val = 0;
u32 alrm_irq_val = 0;
u32 alrm_wake_val = 0;
+ unsigned long flags;
if (to) {
alrm_val = SUN6I_ALRM_EN_CNT_EN;
@@ -150,9 +158,11 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
chip->base + SUN6I_ALRM_IRQ_STA);
}
+ spin_lock_irqsave(&chip->lock, flags);
writel(alrm_val, chip->base + SUN6I_ALRM_EN);
writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN);
writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG);
+ spin_unlock_irqrestore(&chip->lock, flags);
}
static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
@@ -191,11 +201,15 @@ static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{
struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+ unsigned long flags;
u32 alrm_st;
u32 alrm_en;
+ spin_lock_irqsave(&chip->lock, flags);
alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN);
alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA);
+ spin_unlock_irqrestore(&chip->lock, flags);
+
wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN);
wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN);
rtc_time_to_tm(chip->alarm, &wkalrm->time);
@@ -356,6 +370,7 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
+ spin_lock_init(&chip->lock);
platform_set_drvdata(pdev, chip);
chip->dev = &pdev->dev;
@@ -404,6 +419,10 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
/* disable alarm wakeup */
writel(0, chip->base + SUN6I_ALARM_CONFIG);
+ /* switch to the external, more precise, oscillator */
+ writel(SUN6I_LOSC_CTRL_KEY | SUN6I_LOSC_CTRL_EXT_OSC,
+ chip->base + SUN6I_LOSC_CTRL);
+
chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev,
&sun6i_rtc_ops, THIS_MODULE);
if (IS_ERR(chip->rtc)) {
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index 94a8f4ab57bc..ae1dc37e4068 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -892,7 +892,7 @@ dcssblk_direct_access (struct block_device *bdev, sector_t secnum,
dev_info = bdev->bd_disk->private_data;
if (!dev_info)
return -ENODEV;
- dev_sz = dev_info->end - dev_info->start;
+ dev_sz = dev_info->end - dev_info->start + 1;
offset = secnum * 512;
addr = (void *) (dev_info->start + offset);
*pfn = virt_to_phys(addr) >> PAGE_SHIFT;
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index 799c1524c779..4b8de3e70cf2 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -872,7 +872,7 @@ static int __init vmlogrdr_init(void)
goto cleanup;
for (i=0; i < MAXMINOR; ++i ) {
- sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL);
+ sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sys_ser[i].buffer) {
rc = -ENOMEM;
break;
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index 5d06253c2a7a..30e9fbbff051 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -147,11 +147,11 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq)
struct qdio_q *q;
int i;
- for_each_input_queue(irq, q, i) {
- if (!references_shared_dsci(irq) &&
- has_multiple_inq_on_dsci(irq))
- xchg(q->irq_ptr->dsci, 0);
+ if (!references_shared_dsci(irq) &&
+ has_multiple_inq_on_dsci(irq))
+ xchg(irq->dsci, 0);
+ for_each_input_queue(irq, q, i) {
if (q->u.in.queue_start_poll) {
/* skip if polling is enabled or already in work */
if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED,
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 581001989937..d5bf36ec8a75 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -289,11 +289,12 @@ void zfcp_dbf_rec_trig(char *tag, struct zfcp_adapter *adapter,
/**
- * zfcp_dbf_rec_run - trace event related to running recovery
+ * zfcp_dbf_rec_run_lvl - trace event related to running recovery
+ * @level: trace level to be used for event
* @tag: identifier for event
* @erp: erp_action running
*/
-void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp)
+void zfcp_dbf_rec_run_lvl(int level, char *tag, struct zfcp_erp_action *erp)
{
struct zfcp_dbf *dbf = erp->adapter->dbf;
struct zfcp_dbf_rec *rec = &dbf->rec_buf;
@@ -319,11 +320,21 @@ void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp)
else
rec->u.run.rec_count = atomic_read(&erp->adapter->erp_counter);
- debug_event(dbf->rec, 1, rec, sizeof(*rec));
+ debug_event(dbf->rec, level, rec, sizeof(*rec));
spin_unlock_irqrestore(&dbf->rec_lock, flags);
}
/**
+ * zfcp_dbf_rec_run - trace event related to running recovery
+ * @tag: identifier for event
+ * @erp: erp_action running
+ */
+void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp)
+{
+ zfcp_dbf_rec_run_lvl(1, tag, erp);
+}
+
+/**
* zfcp_dbf_rec_run_wka - trace wka port event with info like running recovery
* @tag: identifier for event
* @wka_port: well known address port
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index 36d07584271d..db186d44cfaf 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -2,7 +2,7 @@
* zfcp device driver
* debug feature declarations
*
- * Copyright IBM Corp. 2008, 2015
+ * Copyright IBM Corp. 2008, 2016
*/
#ifndef ZFCP_DBF_H
@@ -283,6 +283,30 @@ struct zfcp_dbf {
struct zfcp_dbf_scsi scsi_buf;
};
+/**
+ * zfcp_dbf_hba_fsf_resp_suppress - true if we should not trace by default
+ * @req: request that has been completed
+ *
+ * Returns true if FCP response with only benign residual under count.
+ */
+static inline
+bool zfcp_dbf_hba_fsf_resp_suppress(struct zfcp_fsf_req *req)
+{
+ struct fsf_qtcb *qtcb = req->qtcb;
+ u32 fsf_stat = qtcb->header.fsf_status;
+ struct fcp_resp *fcp_rsp;
+ u8 rsp_flags, fr_status;
+
+ if (qtcb->prefix.qtcb_type != FSF_IO_COMMAND)
+ return false; /* not an FCP response */
+ fcp_rsp = (struct fcp_resp *)&qtcb->bottom.io.fcp_rsp;
+ rsp_flags = fcp_rsp->fr_flags;
+ fr_status = fcp_rsp->fr_status;
+ return (fsf_stat == FSF_FCP_RSP_AVAILABLE) &&
+ (rsp_flags == FCP_RESID_UNDER) &&
+ (fr_status == SAM_STAT_GOOD);
+}
+
static inline
void zfcp_dbf_hba_fsf_resp(char *tag, int level, struct zfcp_fsf_req *req)
{
@@ -304,7 +328,9 @@ void zfcp_dbf_hba_fsf_response(struct zfcp_fsf_req *req)
zfcp_dbf_hba_fsf_resp("fs_perr", 1, req);
} else if (qtcb->header.fsf_status != FSF_GOOD) {
- zfcp_dbf_hba_fsf_resp("fs_ferr", 1, req);
+ zfcp_dbf_hba_fsf_resp("fs_ferr",
+ zfcp_dbf_hba_fsf_resp_suppress(req)
+ ? 5 : 1, req);
} else if ((req->fsf_command == FSF_QTCB_OPEN_PORT_WITH_DID) ||
(req->fsf_command == FSF_QTCB_OPEN_LUN)) {
@@ -388,4 +414,15 @@ void zfcp_dbf_scsi_devreset(char *tag, struct scsi_cmnd *scmnd, u8 flag)
_zfcp_dbf_scsi(tmp_tag, 1, scmnd, NULL);
}
+/**
+ * zfcp_dbf_scsi_nullcmnd() - trace NULLify of SCSI command in dev/tgt-reset.
+ * @scmnd: SCSI command that was NULLified.
+ * @fsf_req: request that owned @scmnd.
+ */
+static inline void zfcp_dbf_scsi_nullcmnd(struct scsi_cmnd *scmnd,
+ struct zfcp_fsf_req *fsf_req)
+{
+ _zfcp_dbf_scsi("scfc__1", 3, scmnd, fsf_req);
+}
+
#endif /* ZFCP_DBF_H */
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index a59d678125bd..7ccfce559034 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -3,7 +3,7 @@
*
* Error Recovery Procedures (ERP).
*
- * Copyright IBM Corp. 2002, 2015
+ * Copyright IBM Corp. 2002, 2016
*/
#define KMSG_COMPONENT "zfcp"
@@ -1204,6 +1204,62 @@ static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action)
}
}
+/**
+ * zfcp_erp_try_rport_unblock - unblock rport if no more/new recovery
+ * @port: zfcp_port whose fc_rport we should try to unblock
+ */
+static void zfcp_erp_try_rport_unblock(struct zfcp_port *port)
+{
+ unsigned long flags;
+ struct zfcp_adapter *adapter = port->adapter;
+ int port_status;
+ struct Scsi_Host *shost = adapter->scsi_host;
+ struct scsi_device *sdev;
+
+ write_lock_irqsave(&adapter->erp_lock, flags);
+ port_status = atomic_read(&port->status);
+ if ((port_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 ||
+ (port_status & (ZFCP_STATUS_COMMON_ERP_INUSE |
+ ZFCP_STATUS_COMMON_ERP_FAILED)) != 0) {
+ /* new ERP of severity >= port triggered elsewhere meanwhile or
+ * local link down (adapter erp_failed but not clear unblock)
+ */
+ zfcp_dbf_rec_run_lvl(4, "ertru_p", &port->erp_action);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+ return;
+ }
+ spin_lock(shost->host_lock);
+ __shost_for_each_device(sdev, shost) {
+ struct zfcp_scsi_dev *zsdev = sdev_to_zfcp(sdev);
+ int lun_status;
+
+ if (zsdev->port != port)
+ continue;
+ /* LUN under port of interest */
+ lun_status = atomic_read(&zsdev->status);
+ if ((lun_status & ZFCP_STATUS_COMMON_ERP_FAILED) != 0)
+ continue; /* unblock rport despite failed LUNs */
+ /* LUN recovery not given up yet [maybe follow-up pending] */
+ if ((lun_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 ||
+ (lun_status & ZFCP_STATUS_COMMON_ERP_INUSE) != 0) {
+ /* LUN blocked:
+ * not yet unblocked [LUN recovery pending]
+ * or meanwhile blocked [new LUN recovery triggered]
+ */
+ zfcp_dbf_rec_run_lvl(4, "ertru_l", &zsdev->erp_action);
+ spin_unlock(shost->host_lock);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+ return;
+ }
+ }
+ /* now port has no child or all children have completed recovery,
+ * and no ERP of severity >= port was meanwhile triggered elsewhere
+ */
+ zfcp_scsi_schedule_rport_register(port);
+ spin_unlock(shost->host_lock);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+}
+
static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
{
struct zfcp_adapter *adapter = act->adapter;
@@ -1214,6 +1270,7 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
case ZFCP_ERP_ACTION_REOPEN_LUN:
if (!(act->status & ZFCP_STATUS_ERP_NO_REF))
scsi_device_put(sdev);
+ zfcp_erp_try_rport_unblock(port);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT:
@@ -1224,7 +1281,7 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
*/
if (act->step != ZFCP_ERP_STEP_UNINITIALIZED)
if (result == ZFCP_ERP_SUCCEEDED)
- zfcp_scsi_schedule_rport_register(port);
+ zfcp_erp_try_rport_unblock(port);
/* fall through */
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
put_device(&port->dev);
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index c8fed9fa1cca..21c8c689b02b 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -3,7 +3,7 @@
*
* External function declarations.
*
- * Copyright IBM Corp. 2002, 2015
+ * Copyright IBM Corp. 2002, 2016
*/
#ifndef ZFCP_EXT_H
@@ -35,6 +35,8 @@ extern void zfcp_dbf_adapter_unregister(struct zfcp_adapter *);
extern void zfcp_dbf_rec_trig(char *, struct zfcp_adapter *,
struct zfcp_port *, struct scsi_device *, u8, u8);
extern void zfcp_dbf_rec_run(char *, struct zfcp_erp_action *);
+extern void zfcp_dbf_rec_run_lvl(int level, char *tag,
+ struct zfcp_erp_action *erp);
extern void zfcp_dbf_rec_run_wka(char *, struct zfcp_fc_wka_port *, u64);
extern void zfcp_dbf_hba_fsf_uss(char *, struct zfcp_fsf_req *);
extern void zfcp_dbf_hba_fsf_res(char *, int, struct zfcp_fsf_req *);
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index 75f820ca17b7..27ff38f839fc 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -1583,7 +1583,7 @@ out:
int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port)
{
struct zfcp_qdio *qdio = wka_port->adapter->qdio;
- struct zfcp_fsf_req *req = NULL;
+ struct zfcp_fsf_req *req;
int retval = -EIO;
spin_lock_irq(&qdio->req_q_lock);
@@ -1612,7 +1612,7 @@ int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port)
zfcp_fsf_req_free(req);
out:
spin_unlock_irq(&qdio->req_q_lock);
- if (req && !IS_ERR(req))
+ if (!retval)
zfcp_dbf_rec_run_wka("fsowp_1", wka_port, req->req_id);
return retval;
}
@@ -1638,7 +1638,7 @@ static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req)
int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port)
{
struct zfcp_qdio *qdio = wka_port->adapter->qdio;
- struct zfcp_fsf_req *req = NULL;
+ struct zfcp_fsf_req *req;
int retval = -EIO;
spin_lock_irq(&qdio->req_q_lock);
@@ -1667,7 +1667,7 @@ int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port)
zfcp_fsf_req_free(req);
out:
spin_unlock_irq(&qdio->req_q_lock);
- if (req && !IS_ERR(req))
+ if (!retval)
zfcp_dbf_rec_run_wka("fscwp_1", wka_port, req->req_id);
return retval;
}
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
index be1c04b334c5..ea3c76ac0de1 100644
--- a/drivers/s390/scsi/zfcp_fsf.h
+++ b/drivers/s390/scsi/zfcp_fsf.h
@@ -3,7 +3,7 @@
*
* Interface to the FSF support functions.
*
- * Copyright IBM Corp. 2002, 2015
+ * Copyright IBM Corp. 2002, 2016
*/
#ifndef FSF_H
@@ -78,6 +78,7 @@
#define FSF_APP_TAG_CHECK_FAILURE 0x00000082
#define FSF_REF_TAG_CHECK_FAILURE 0x00000083
#define FSF_ADAPTER_STATUS_AVAILABLE 0x000000AD
+#define FSF_FCP_RSP_AVAILABLE 0x000000AF
#define FSF_UNKNOWN_COMMAND 0x000000E2
#define FSF_UNKNOWN_OP_SUBTYPE 0x000000E3
#define FSF_INVALID_COMMAND_OPTION 0x000000E5
diff --git a/drivers/s390/scsi/zfcp_reqlist.h b/drivers/s390/scsi/zfcp_reqlist.h
index 7c2c6194dfca..703fce59befe 100644
--- a/drivers/s390/scsi/zfcp_reqlist.h
+++ b/drivers/s390/scsi/zfcp_reqlist.h
@@ -4,7 +4,7 @@
* Data structure and helper functions for tracking pending FSF
* requests.
*
- * Copyright IBM Corp. 2009
+ * Copyright IBM Corp. 2009, 2016
*/
#ifndef ZFCP_REQLIST_H
@@ -180,4 +180,32 @@ static inline void zfcp_reqlist_move(struct zfcp_reqlist *rl,
spin_unlock_irqrestore(&rl->lock, flags);
}
+/**
+ * zfcp_reqlist_apply_for_all() - apply a function to every request.
+ * @rl: the requestlist that contains the target requests.
+ * @f: the function to apply to each request; the first parameter of the
+ * function will be the target-request; the second parameter is the same
+ * pointer as given with the argument @data.
+ * @data: freely chosen argument; passed through to @f as second parameter.
+ *
+ * Uses :c:macro:`list_for_each_entry` to iterate over the lists in the hash-
+ * table (not a 'safe' variant, so don't modify the list).
+ *
+ * Holds @rl->lock over the entire request-iteration.
+ */
+static inline void
+zfcp_reqlist_apply_for_all(struct zfcp_reqlist *rl,
+ void (*f)(struct zfcp_fsf_req *, void *), void *data)
+{
+ struct zfcp_fsf_req *req;
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&rl->lock, flags);
+ for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++)
+ list_for_each_entry(req, &rl->buckets[i], list)
+ f(req, data);
+ spin_unlock_irqrestore(&rl->lock, flags);
+}
+
#endif /* ZFCP_REQLIST_H */
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 9069f98a1817..07ffdbb5107f 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -3,7 +3,7 @@
*
* Interface to Linux SCSI midlayer.
*
- * Copyright IBM Corp. 2002, 2015
+ * Copyright IBM Corp. 2002, 2016
*/
#define KMSG_COMPONENT "zfcp"
@@ -88,9 +88,7 @@ int zfcp_scsi_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scpnt)
}
if (unlikely(!(status & ZFCP_STATUS_COMMON_UNBLOCKED))) {
- /* This could be either
- * open LUN pending: this is temporary, will result in
- * open LUN or ERP_FAILED, so retry command
+ /* This could be
* call to rport_delete pending: mimic retry from
* fc_remote_port_chkready until rport is BLOCKED
*/
@@ -209,6 +207,57 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
return retval;
}
+struct zfcp_scsi_req_filter {
+ u8 tmf_scope;
+ u32 lun_handle;
+ u32 port_handle;
+};
+
+static void zfcp_scsi_forget_cmnd(struct zfcp_fsf_req *old_req, void *data)
+{
+ struct zfcp_scsi_req_filter *filter =
+ (struct zfcp_scsi_req_filter *)data;
+
+ /* already aborted - prevent side-effects - or not a SCSI command */
+ if (old_req->data == NULL || old_req->fsf_command != FSF_QTCB_FCP_CMND)
+ return;
+
+ /* (tmf_scope == FCP_TMF_TGT_RESET || tmf_scope == FCP_TMF_LUN_RESET) */
+ if (old_req->qtcb->header.port_handle != filter->port_handle)
+ return;
+
+ if (filter->tmf_scope == FCP_TMF_LUN_RESET &&
+ old_req->qtcb->header.lun_handle != filter->lun_handle)
+ return;
+
+ zfcp_dbf_scsi_nullcmnd((struct scsi_cmnd *)old_req->data, old_req);
+ old_req->data = NULL;
+}
+
+static void zfcp_scsi_forget_cmnds(struct zfcp_scsi_dev *zsdev, u8 tm_flags)
+{
+ struct zfcp_adapter *adapter = zsdev->port->adapter;
+ struct zfcp_scsi_req_filter filter = {
+ .tmf_scope = FCP_TMF_TGT_RESET,
+ .port_handle = zsdev->port->handle,
+ };
+ unsigned long flags;
+
+ if (tm_flags == FCP_TMF_LUN_RESET) {
+ filter.tmf_scope = FCP_TMF_LUN_RESET;
+ filter.lun_handle = zsdev->lun_handle;
+ }
+
+ /*
+ * abort_lock secures against other processings - in the abort-function
+ * and normal cmnd-handler - of (struct zfcp_fsf_req *)->data
+ */
+ write_lock_irqsave(&adapter->abort_lock, flags);
+ zfcp_reqlist_apply_for_all(adapter->req_list, zfcp_scsi_forget_cmnd,
+ &filter);
+ write_unlock_irqrestore(&adapter->abort_lock, flags);
+}
+
static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
{
struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scpnt->device);
@@ -241,8 +290,10 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) {
zfcp_dbf_scsi_devreset("fail", scpnt, tm_flags);
retval = FAILED;
- } else
+ } else {
zfcp_dbf_scsi_devreset("okay", scpnt, tm_flags);
+ zfcp_scsi_forget_cmnds(zfcp_sdev, tm_flags);
+ }
zfcp_fsf_req_free(fsf_req);
return retval;
diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c
index 0d351cd3191b..26d38b1a45ab 100644
--- a/drivers/scsi/aacraid/comminit.c
+++ b/drivers/scsi/aacraid/comminit.c
@@ -50,9 +50,13 @@ struct aac_common aac_config = {
static inline int aac_is_msix_mode(struct aac_dev *dev)
{
- u32 status;
+ u32 status = 0;
- status = src_readl(dev, MUnit.OMR);
+ if (dev->pdev->device == PMC_DEVICE_S6 ||
+ dev->pdev->device == PMC_DEVICE_S7 ||
+ dev->pdev->device == PMC_DEVICE_S8) {
+ status = src_readl(dev, MUnit.OMR);
+ }
return (status & AAC_INT_MODE_MSIX);
}
diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c
index bc0203f3d243..e415e1c58eb5 100644
--- a/drivers/scsi/aacraid/src.c
+++ b/drivers/scsi/aacraid/src.c
@@ -413,16 +413,23 @@ static int aac_src_check_health(struct aac_dev *dev)
u32 status = src_readl(dev, MUnit.OMR);
/*
+ * Check to see if the board panic'd.
+ */
+ if (unlikely(status & KERNEL_PANIC))
+ goto err_blink;
+
+ /*
* Check to see if the board failed any self tests.
*/
if (unlikely(status & SELF_TEST_FAILED))
- return -1;
+ goto err_out;
/*
- * Check to see if the board panic'd.
+ * Check to see if the board failed any self tests.
*/
- if (unlikely(status & KERNEL_PANIC))
- return (status >> 16) & 0xFF;
+ if (unlikely(status & MONITOR_PANIC))
+ goto err_out;
+
/*
* Wait for the adapter to be up and running.
*/
@@ -432,6 +439,12 @@ static int aac_src_check_health(struct aac_dev *dev)
* Everything is OK
*/
return 0;
+
+err_out:
+ return -1;
+
+err_blink:
+ return (status > 16) & 0xFF;
}
/**
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 33ec4fa39ccb..f224cdb2fce4 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -1182,6 +1182,7 @@ struct lpfc_mbx_wq_create {
#define lpfc_mbx_wq_create_page_size_SHIFT 0
#define lpfc_mbx_wq_create_page_size_MASK 0x000000FF
#define lpfc_mbx_wq_create_page_size_WORD word1
+#define LPFC_WQ_PAGE_SIZE_4096 0x1
#define lpfc_mbx_wq_create_wqe_size_SHIFT 8
#define lpfc_mbx_wq_create_wqe_size_MASK 0x0000000F
#define lpfc_mbx_wq_create_wqe_size_WORD word1
@@ -1253,6 +1254,7 @@ struct rq_context {
#define lpfc_rq_context_page_size_SHIFT 0 /* Version 1 Only */
#define lpfc_rq_context_page_size_MASK 0x000000FF
#define lpfc_rq_context_page_size_WORD word0
+#define LPFC_RQ_PAGE_SIZE_4096 0x1
uint32_t reserved1;
uint32_t word2;
#define lpfc_rq_context_cq_id_SHIFT 16
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 92dfd6a5178c..f5aeda8f014f 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -13475,7 +13475,7 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
LPFC_WQ_WQE_SIZE_128);
bf_set(lpfc_mbx_wq_create_page_size,
&wq_create->u.request_1,
- (PAGE_SIZE/SLI4_PAGE_SIZE));
+ LPFC_WQ_PAGE_SIZE_4096);
page = wq_create->u.request_1.page;
break;
}
@@ -13501,8 +13501,9 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
LPFC_WQ_WQE_SIZE_128);
break;
}
- bf_set(lpfc_mbx_wq_create_page_size, &wq_create->u.request_1,
- (PAGE_SIZE/SLI4_PAGE_SIZE));
+ bf_set(lpfc_mbx_wq_create_page_size,
+ &wq_create->u.request_1,
+ LPFC_WQ_PAGE_SIZE_4096);
page = wq_create->u.request_1.page;
break;
default:
@@ -13688,7 +13689,7 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
LPFC_RQE_SIZE_8);
bf_set(lpfc_rq_context_page_size,
&rq_create->u.request.context,
- (PAGE_SIZE/SLI4_PAGE_SIZE));
+ LPFC_RQ_PAGE_SIZE_4096);
} else {
switch (hrq->entry_count) {
default:
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c
index 021b994fdae8..96007633ad39 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c
@@ -1856,6 +1856,8 @@ megasas_build_syspd_fusion(struct megasas_instance *instance,
io_request->DevHandle = pd_sync->seq[pd_index].devHandle;
pRAID_Context->regLockFlags |=
(MR_RL_FLAGS_SEQ_NUM_ENABLE|MR_RL_FLAGS_GRANT_DESTINATION_CUDA);
+ pRAID_Context->Type = MPI2_TYPE_CUDA;
+ pRAID_Context->nseg = 0x1;
} else if (fusion->fast_path_io) {
pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id);
pRAID_Context->configSeqNum = 0;
@@ -1891,12 +1893,10 @@ megasas_build_syspd_fusion(struct megasas_instance *instance,
pRAID_Context->timeoutValue =
cpu_to_le16((os_timeout_value > timeout_limit) ?
timeout_limit : os_timeout_value);
- if (fusion->adapter_type == INVADER_SERIES) {
- pRAID_Context->Type = MPI2_TYPE_CUDA;
- pRAID_Context->nseg = 0x1;
+ if (fusion->adapter_type == INVADER_SERIES)
io_request->IoFlags |=
cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH);
- }
+
cmd->request_desc->SCSIIO.RequestFlags =
(MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY <<
MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
@@ -2648,6 +2648,7 @@ int megasas_wait_for_outstanding_fusion(struct megasas_instance *instance,
dev_err(&instance->pdev->dev, "pending commands remain after waiting, "
"will reset adapter scsi%d.\n",
instance->host->host_no);
+ *convert = 1;
retval = 1;
}
out:
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index 8cead04f26d6..f6a8e9958e75 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -51,6 +51,7 @@
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/pci.h>
+#include <linux/pci-aspm.h>
#include <linux/interrupt.h>
#include <linux/aer.h>
#include <linux/raid_class.h>
@@ -8483,6 +8484,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
switch (hba_mpi_version) {
case MPI2_VERSION:
+ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S |
+ PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM);
/* Use mpt2sas driver host template for SAS 2.0 HBA's */
shost = scsi_host_alloc(&mpt2sas_driver_template,
sizeof(struct MPT3SAS_ADAPTER));
diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c
index 9270d15ff1a4..7353ac8d0d39 100644
--- a/drivers/scsi/mvsas/mv_94xx.c
+++ b/drivers/scsi/mvsas/mv_94xx.c
@@ -621,7 +621,7 @@ static void mvs_94xx_command_active(struct mvs_info *mvi, u32 slot_idx)
{
u32 tmp;
tmp = mvs_cr32(mvi, MVS_COMMAND_ACTIVE+(slot_idx >> 3));
- if (tmp && 1 << (slot_idx % 32)) {
+ if (tmp & 1 << (slot_idx % 32)) {
mv_printk("command active %08X, slot [%x].\n", tmp, slot_idx);
mvs_cw32(mvi, MVS_COMMAND_ACTIVE + (slot_idx >> 3),
1 << (slot_idx % 32));
diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c
index 9c780740fb82..e712fe745955 100644
--- a/drivers/scsi/mvsas/mv_sas.c
+++ b/drivers/scsi/mvsas/mv_sas.c
@@ -737,8 +737,8 @@ static int mvs_task_prep(struct sas_task *task, struct mvs_info *mvi, int is_tmf
mv_dprintk("device %016llx not ready.\n",
SAS_ADDR(dev->sas_addr));
- rc = SAS_PHY_DOWN;
- return rc;
+ rc = SAS_PHY_DOWN;
+ return rc;
}
tei.port = dev->port->lldd_port;
if (tei.port && !tei.port->port_attached && !tmf) {
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index c44cbf46221c..3588a56aabb4 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -3365,7 +3365,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
sizeof(struct ct6_dsd), 0,
SLAB_HWCACHE_ALIGN, NULL);
if (!ctx_cachep)
- goto fail_free_gid_list;
+ goto fail_free_srb_mempool;
}
ha->ctx_mempool = mempool_create_slab_pool(SRB_MIN_REQ,
ctx_cachep);
@@ -3518,7 +3518,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
ha->loop_id_map = kzalloc(BITS_TO_LONGS(LOOPID_MAP_SIZE) * sizeof(long),
GFP_KERNEL);
if (!ha->loop_id_map)
- goto fail_async_pd;
+ goto fail_loop_id_map;
else {
qla2x00_set_reserved_loop_ids(ha);
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0123,
@@ -3527,6 +3527,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
return 0;
+fail_loop_id_map:
+ dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma);
fail_async_pd:
dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma);
fail_ex_init_cb:
@@ -3554,6 +3556,10 @@ fail_free_ms_iocb:
dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
ha->ms_iocb = NULL;
ha->ms_iocb_dma = 0;
+
+ if (ha->sns_cmd)
+ dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt),
+ ha->sns_cmd, ha->sns_cmd_dma);
fail_dma_pool:
if (IS_QLA82XX(ha) || ql2xenabledif) {
dma_pool_destroy(ha->fcp_cmnd_dma_pool);
@@ -3571,10 +3577,12 @@ fail_free_nvram:
kfree(ha->nvram);
ha->nvram = NULL;
fail_free_ctx_mempool:
- mempool_destroy(ha->ctx_mempool);
+ if (ha->ctx_mempool)
+ mempool_destroy(ha->ctx_mempool);
ha->ctx_mempool = NULL;
fail_free_srb_mempool:
- mempool_destroy(ha->srb_mempool);
+ if (ha->srb_mempool)
+ mempool_destroy(ha->srb_mempool);
ha->srb_mempool = NULL;
fail_free_gid_list:
dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
diff --git a/drivers/scsi/scsi_dh.c b/drivers/scsi/scsi_dh.c
index e7649ed3f667..4d655b568269 100644
--- a/drivers/scsi/scsi_dh.c
+++ b/drivers/scsi/scsi_dh.c
@@ -289,20 +289,6 @@ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
}
EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
-static struct scsi_device *get_sdev_from_queue(struct request_queue *q)
-{
- struct scsi_device *sdev;
- unsigned long flags;
-
- spin_lock_irqsave(q->queue_lock, flags);
- sdev = q->queuedata;
- if (!sdev || !get_device(&sdev->sdev_gendev))
- sdev = NULL;
- spin_unlock_irqrestore(q->queue_lock, flags);
-
- return sdev;
-}
-
/*
* scsi_dh_activate - activate the path associated with the scsi_device
* corresponding to the given request queue.
@@ -321,7 +307,7 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data)
struct scsi_device *sdev;
int err = SCSI_DH_NOSYS;
- sdev = get_sdev_from_queue(q);
+ sdev = scsi_device_from_queue(q);
if (!sdev) {
if (fn)
fn(data, err);
@@ -368,7 +354,7 @@ int scsi_dh_set_params(struct request_queue *q, const char *params)
struct scsi_device *sdev;
int err = -SCSI_DH_NOSYS;
- sdev = get_sdev_from_queue(q);
+ sdev = scsi_device_from_queue(q);
if (!sdev)
return err;
@@ -391,7 +377,7 @@ int scsi_dh_attach(struct request_queue *q, const char *name)
struct scsi_device_handler *scsi_dh;
int err = 0;
- sdev = get_sdev_from_queue(q);
+ sdev = scsi_device_from_queue(q);
if (!sdev)
return -ENODEV;
@@ -429,7 +415,7 @@ const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp)
struct scsi_device *sdev;
const char *handler_name = NULL;
- sdev = get_sdev_from_queue(q);
+ sdev = scsi_device_from_queue(q);
if (!sdev)
return NULL;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index cf5b99e1f12b..887045ae5d10 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -1120,7 +1120,8 @@ int scsi_init_io(struct scsi_cmnd *cmd)
bool is_mq = (rq->mq_ctx != NULL);
int error;
- BUG_ON(!rq->nr_phys_segments);
+ if (WARN_ON_ONCE(!rq->nr_phys_segments))
+ return -EINVAL;
error = scsi_init_sgtable(rq, &cmd->sdb);
if (error)
@@ -2214,6 +2215,29 @@ void scsi_mq_destroy_tags(struct Scsi_Host *shost)
blk_mq_free_tag_set(&shost->tag_set);
}
+/**
+ * scsi_device_from_queue - return sdev associated with a request_queue
+ * @q: The request queue to return the sdev from
+ *
+ * Return the sdev associated with a request queue or NULL if the
+ * request_queue does not reference a SCSI device.
+ */
+struct scsi_device *scsi_device_from_queue(struct request_queue *q)
+{
+ struct scsi_device *sdev = NULL;
+
+ if (q->mq_ops) {
+ if (q->mq_ops == &scsi_mq_ops)
+ sdev = q->queuedata;
+ } else if (q->request_fn == scsi_request_fn)
+ sdev = q->queuedata;
+ if (!sdev || !get_device(&sdev->sdev_gendev))
+ sdev = NULL;
+
+ return sdev;
+}
+EXPORT_SYMBOL_GPL(scsi_device_from_queue);
+
/*
* Function: scsi_block_requests()
*
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 98cabf409bf0..9f2c9a2fdd74 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -1031,10 +1031,6 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
struct request_queue *rq = sdev->request_queue;
struct scsi_target *starget = sdev->sdev_target;
- error = scsi_device_set_state(sdev, SDEV_RUNNING);
- if (error)
- return error;
-
error = scsi_target_add(starget);
if (error)
return error;
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 77b2da269d6e..93a523b42d3d 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -592,6 +592,9 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
sg_io_hdr_t *hp;
unsigned char cmnd[SG_MAX_CDB_SIZE];
+ if (unlikely(segment_eq(get_fs(), KERNEL_DS)))
+ return -EINVAL;
+
if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
return -ENXIO;
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
@@ -1757,6 +1760,10 @@ sg_start_req(Sg_request *srp, unsigned char *cmd)
return res;
iov_iter_truncate(&i, hp->dxfer_len);
+ if (!iov_iter_count(&i)) {
+ kfree(iov);
+ return -EINVAL;
+ }
res = blk_rq_map_user_iov(q, rq, md, &i, GFP_ATOMIC);
kfree(iov);
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index 0f636cc4c809..cd5c1c060481 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -135,6 +135,8 @@ struct hv_fc_wwn_packet {
#define SRB_FLAGS_PORT_DRIVER_RESERVED 0x0F000000
#define SRB_FLAGS_CLASS_DRIVER_RESERVED 0xF0000000
+#define SP_UNTAGGED ((unsigned char) ~0)
+#define SRB_SIMPLE_TAG_REQUEST 0x20
/*
* Platform neutral description of a scsi request -
@@ -354,6 +356,7 @@ enum storvsc_request_type {
#define SRB_STATUS_SUCCESS 0x01
#define SRB_STATUS_ABORTED 0x02
#define SRB_STATUS_ERROR 0x04
+#define SRB_STATUS_DATA_OVERRUN 0x12
#define SRB_STATUS(status) \
(status & ~(SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_QUEUE_FROZEN))
@@ -864,6 +867,13 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb,
switch (SRB_STATUS(vm_srb->srb_status)) {
case SRB_STATUS_ERROR:
/*
+ * Let upper layer deal with error when
+ * sense message is present.
+ */
+
+ if (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID)
+ break;
+ /*
* If there is an error; offline the device since all
* error recovery strategies would have already been
* deployed on the host side. However, if the command
@@ -927,6 +937,7 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request)
struct hv_host_device *host_dev = shost_priv(scmnd->device->host);
struct scsi_sense_hdr sense_hdr;
struct vmscsi_request *vm_srb;
+ u32 data_transfer_length;
struct Scsi_Host *host;
struct storvsc_device *stor_dev;
struct hv_device *dev = host_dev->dev;
@@ -937,6 +948,7 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request)
host = stor_dev->host;
vm_srb = &cmd_request->vstor_packet.vm_srb;
+ data_transfer_length = vm_srb->data_transfer_length;
scmnd->result = vm_srb->scsi_status;
@@ -947,13 +959,20 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request)
&sense_hdr);
}
- if (vm_srb->srb_status != SRB_STATUS_SUCCESS)
+ if (vm_srb->srb_status != SRB_STATUS_SUCCESS) {
storvsc_handle_error(vm_srb, scmnd, host, sense_hdr.asc,
sense_hdr.ascq);
+ /*
+ * The Windows driver set data_transfer_length on
+ * SRB_STATUS_DATA_OVERRUN. On other errors, this value
+ * is untouched. In these cases we set it to 0.
+ */
+ if (vm_srb->srb_status != SRB_STATUS_DATA_OVERRUN)
+ data_transfer_length = 0;
+ }
scsi_set_resid(scmnd,
- cmd_request->payload->range.len -
- vm_srb->data_transfer_length);
+ cmd_request->payload->range.len - data_transfer_length);
scmnd->scsi_done(scmnd);
@@ -1409,6 +1428,13 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd)
vm_srb->win8_extension.srb_flags |=
SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
+ if (scmnd->device->tagged_supported) {
+ vm_srb->win8_extension.srb_flags |=
+ (SRB_FLAGS_QUEUE_ACTION_ENABLE | SRB_FLAGS_NO_QUEUE_FREEZE);
+ vm_srb->win8_extension.queue_tag = SP_UNTAGGED;
+ vm_srb->win8_extension.queue_action = SRB_SIMPLE_TAG_REQUEST;
+ }
+
/* Build the SRB */
switch (scmnd->sc_data_direction) {
case DMA_TO_DEVICE:
diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c
index 14cf10b92122..2dc4208cbc51 100644
--- a/drivers/soc/qcom/glink_smem_native_xprt.c
+++ b/drivers/soc/qcom/glink_smem_native_xprt.c
@@ -213,6 +213,7 @@ struct edge_info {
bool tx_blocked_signal_sent;
struct kthread_work kwork;
struct kthread_worker kworker;
+ struct work_struct wakeup_work;
struct task_struct *task;
struct tasklet_struct tasklet;
struct srcu_struct use_ref;
@@ -826,7 +827,6 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx)
int i;
bool granted;
unsigned long flags;
- bool trigger_wakeup = false;
int rcu_id;
uint16_t rcid;
uint32_t name_len;
@@ -851,22 +851,10 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx)
srcu_read_unlock(&einfo->use_ref, rcu_id);
return;
}
- if (!atomic_ctx) {
- if (einfo->tx_resume_needed && fifo_write_avail(einfo)) {
- einfo->tx_resume_needed = false;
- einfo->xprt_if.glink_core_if_ptr->tx_resume(
- &einfo->xprt_if);
- }
- spin_lock_irqsave(&einfo->write_lock, flags);
- if (waitqueue_active(&einfo->tx_blocked_queue)) {
- einfo->tx_blocked_signal_sent = false;
- trigger_wakeup = true;
- }
- spin_unlock_irqrestore(&einfo->write_lock, flags);
- if (trigger_wakeup)
- wake_up_all(&einfo->tx_blocked_queue);
- }
+ if ((atomic_ctx) && ((einfo->tx_resume_needed) ||
+ (waitqueue_active(&einfo->tx_blocked_queue)))) /* tx waiting ?*/
+ schedule_work(&einfo->wakeup_work);
/*
* Access to the fifo needs to be synchronized, however only the calls
@@ -1174,6 +1162,39 @@ static void rx_worker_atomic(unsigned long param)
}
/**
+ * tx_wakeup_worker() - worker function to wakeup tx blocked thread
+ * @work: kwork associated with the edge to process commands on.
+ */
+static void tx_wakeup_worker(struct work_struct *work)
+{
+ struct edge_info *einfo;
+ bool trigger_wakeup = false;
+ unsigned long flags;
+ int rcu_id;
+
+ einfo = container_of(work, struct edge_info, wakeup_work);
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return;
+ }
+ if (einfo->tx_resume_needed && fifo_write_avail(einfo)) {
+ einfo->tx_resume_needed = false;
+ einfo->xprt_if.glink_core_if_ptr->tx_resume(
+ &einfo->xprt_if);
+ }
+ spin_lock_irqsave(&einfo->write_lock, flags);
+ if (waitqueue_active(&einfo->tx_blocked_queue)) { /* tx waiting ?*/
+ einfo->tx_blocked_signal_sent = false;
+ trigger_wakeup = true;
+ }
+ spin_unlock_irqrestore(&einfo->write_lock, flags);
+ if (trigger_wakeup)
+ wake_up_all(&einfo->tx_blocked_queue);
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
* rx_worker() - worker function to process received commands
* @work: kwork associated with the edge to process commands on.
*/
@@ -2275,6 +2296,7 @@ static int glink_smem_native_probe(struct platform_device *pdev)
init_waitqueue_head(&einfo->tx_blocked_queue);
init_kthread_work(&einfo->kwork, rx_worker);
init_kthread_worker(&einfo->kworker);
+ INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker);
tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo);
einfo->read_from_fifo = read_from_fifo;
einfo->write_to_fifo = write_to_fifo;
@@ -2374,6 +2396,7 @@ request_irq_fail:
reg_xprt_fail:
smem_alloc_fail:
flush_kthread_worker(&einfo->kworker);
+ flush_work(&einfo->wakeup_work);
kthread_stop(einfo->task);
einfo->task = NULL;
tasklet_kill(&einfo->tasklet);
@@ -2462,6 +2485,7 @@ static int glink_rpm_native_probe(struct platform_device *pdev)
init_waitqueue_head(&einfo->tx_blocked_queue);
init_kthread_work(&einfo->kwork, rx_worker);
init_kthread_worker(&einfo->kworker);
+ INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker);
tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo);
einfo->intentless = true;
einfo->read_from_fifo = memcpy32_fromio;
@@ -2622,6 +2646,7 @@ request_irq_fail:
reg_xprt_fail:
toc_init_fail:
flush_kthread_worker(&einfo->kworker);
+ flush_work(&einfo->wakeup_work);
kthread_stop(einfo->task);
einfo->task = NULL;
tasklet_kill(&einfo->tasklet);
@@ -2753,6 +2778,7 @@ static int glink_mailbox_probe(struct platform_device *pdev)
init_waitqueue_head(&einfo->tx_blocked_queue);
init_kthread_work(&einfo->kwork, rx_worker);
init_kthread_worker(&einfo->kworker);
+ INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker);
tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo);
einfo->read_from_fifo = read_from_fifo;
einfo->write_to_fifo = write_to_fifo;
@@ -2873,6 +2899,7 @@ request_irq_fail:
reg_xprt_fail:
smem_alloc_fail:
flush_kthread_worker(&einfo->kworker);
+ flush_work(&einfo->wakeup_work);
kthread_stop(einfo->task);
einfo->task = NULL;
tasklet_kill(&einfo->tasklet);
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 2fcd1a4c636c..16ab2400cd69 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -104,13 +104,16 @@ module_param(qmi_timeout, ulong, 0600);
#ifdef CONFIG_ICNSS_DEBUG
#define ICNSS_ASSERT(_condition) do { \
if (!(_condition)) { \
- icnss_pr_err("ASSERT at line %d\n", \
- __LINE__); \
+ icnss_pr_err("ASSERT at line %d\n", __LINE__); \
BUG_ON(1); \
} \
} while (0)
+
+bool ignore_qmi_timeout;
+#define ICNSS_QMI_ASSERT() ICNSS_ASSERT(ignore_qmi_timeout)
#else
#define ICNSS_ASSERT(_condition) do { } while (0)
+#define ICNSS_QMI_ASSERT() do { } while (0)
#endif
enum icnss_debug_quirks {
@@ -322,8 +325,9 @@ static struct icnss_priv {
u32 pwr_pin_result;
u32 phy_io_pin_result;
u32 rf_pin_result;
+ uint32_t nr_mem_region;
struct icnss_mem_region_info
- icnss_mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01];
+ mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01];
struct dentry *root_dentry;
spinlock_t on_off_lock;
struct icnss_stats stats;
@@ -349,6 +353,15 @@ static struct icnss_priv {
bool bypass_s1_smmu;
} *penv;
+#ifdef CONFIG_ICNSS_DEBUG
+static void icnss_ignore_qmi_timeout(bool ignore)
+{
+ ignore_qmi_timeout = ignore;
+}
+#else
+static void icnss_ignore_qmi_timeout(bool ignore) { }
+#endif
+
static void icnss_pm_stay_awake(struct icnss_priv *priv)
{
if (atomic_inc_return(&priv->pm_count) != 1)
@@ -952,7 +965,7 @@ int icnss_power_off(struct device *dev)
}
EXPORT_SYMBOL(icnss_power_off);
-static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index)
+static int icnss_map_msa_permissions(struct icnss_mem_region_info *mem_region)
{
int ret = 0;
phys_addr_t addr;
@@ -965,10 +978,10 @@ static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index)
int source_nelems = sizeof(source_vmlist)/sizeof(u32);
int dest_nelems = 0;
- addr = priv->icnss_mem_region[index].reg_addr;
- size = priv->icnss_mem_region[index].size;
+ addr = mem_region->reg_addr;
+ size = mem_region->size;
- if (!priv->icnss_mem_region[index].secure_flag) {
+ if (!mem_region->secure_flag) {
dest_vmids[2] = VMID_WLAN_CE;
dest_nelems = 3;
} else {
@@ -978,19 +991,20 @@ static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index)
ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems,
dest_vmids, dest_perms, dest_nelems);
if (ret) {
- icnss_pr_err("Region %u hyp_assign_phys failed IPA=%pa size=%u err=%d\n",
- index, &addr, size, ret);
+ icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
+ &addr, size, ret);
goto out;
}
- icnss_pr_dbg("Hypervisor map for region %u: source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n",
- index, source_vmlist[0], dest_nelems,
- dest_vmids[0], dest_vmids[1], dest_vmids[2]);
+
+ icnss_pr_dbg("Hypervisor map for source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n",
+ source_vmlist[0], dest_nelems, dest_vmids[0],
+ dest_vmids[1], dest_vmids[2]);
out:
return ret;
}
-static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index)
+static int icnss_unmap_msa_permissions(struct icnss_mem_region_info *mem_region)
{
int ret = 0;
phys_addr_t addr;
@@ -1001,9 +1015,10 @@ static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index)
int source_nelems = 0;
int dest_nelems = sizeof(dest_vmids)/sizeof(u32);
- addr = priv->icnss_mem_region[index].reg_addr;
- size = priv->icnss_mem_region[index].size;
- if (!priv->icnss_mem_region[index].secure_flag) {
+ addr = mem_region->reg_addr;
+ size = mem_region->size;
+
+ if (!mem_region->secure_flag) {
source_vmlist[2] = VMID_WLAN_CE;
source_nelems = 3;
} else {
@@ -1014,14 +1029,13 @@ static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index)
ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems,
dest_vmids, dest_perms, dest_nelems);
if (ret) {
- icnss_pr_err("Region %u hyp_assign_phys failed IPA=%pa size=%u err=%d\n",
- index, &addr, size, ret);
+ icnss_pr_err("Hyperviser unmap failed for PA=%pa size=%u err=%d\n",
+ &addr, size, ret);
goto out;
}
- icnss_pr_dbg("hypervisor unmap for region %u, source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x, dest=%x\n",
- index, source_nelems,
- source_vmlist[0], source_vmlist[1], source_vmlist[2],
- dest_vmids[0]);
+ icnss_pr_dbg("Hypervisor unmap for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x, dest=%x\n",
+ source_nelems, source_vmlist[0], source_vmlist[1],
+ source_vmlist[2], dest_vmids[0]);
out:
return ret;
}
@@ -1029,34 +1043,37 @@ out:
static int icnss_setup_msa_permissions(struct icnss_priv *priv)
{
int ret;
+ int i;
if (test_bit(ICNSS_MSA0_ASSIGNED, &priv->state))
return 0;
- ret = icnss_map_msa_permissions(priv, 0);
- if (ret)
- return ret;
+ for (i = 0; i < priv->nr_mem_region; i++) {
- ret = icnss_map_msa_permissions(priv, 1);
- if (ret)
- goto err_map_msa;
+ ret = icnss_map_msa_permissions(&priv->mem_region[i]);
+ if (ret)
+ goto err_unmap;
+ }
set_bit(ICNSS_MSA0_ASSIGNED, &priv->state);
- return ret;
+ return 0;
-err_map_msa:
- icnss_unmap_msa_permissions(priv, 0);
+err_unmap:
+ for (i--; i >= 0; i--)
+ icnss_unmap_msa_permissions(&priv->mem_region[i]);
return ret;
}
static void icnss_remove_msa_permissions(struct icnss_priv *priv)
{
+ int i;
+
if (!test_bit(ICNSS_MSA0_ASSIGNED, &priv->state))
return;
- icnss_unmap_msa_permissions(priv, 0);
- icnss_unmap_msa_permissions(priv, 1);
+ for (i = 0; i < priv->nr_mem_region; i++)
+ icnss_unmap_msa_permissions(&priv->mem_region[i]);
clear_bit(ICNSS_MSA0_ASSIGNED, &priv->state);
}
@@ -1107,7 +1124,7 @@ static int wlfw_msa_mem_info_send_sync_msg(void)
icnss_pr_dbg("Receive mem_region_info_len: %d\n",
resp.mem_region_info_len);
- if (resp.mem_region_info_len > 2) {
+ if (resp.mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
icnss_pr_err("Invalid memory region length received: %d\n",
resp.mem_region_info_len);
ret = -EINVAL;
@@ -1115,24 +1132,25 @@ static int wlfw_msa_mem_info_send_sync_msg(void)
}
penv->stats.msa_info_resp++;
+ penv->nr_mem_region = resp.mem_region_info_len;
for (i = 0; i < resp.mem_region_info_len; i++) {
- penv->icnss_mem_region[i].reg_addr =
+ penv->mem_region[i].reg_addr =
resp.mem_region_info[i].region_addr;
- penv->icnss_mem_region[i].size =
+ penv->mem_region[i].size =
resp.mem_region_info[i].size;
- penv->icnss_mem_region[i].secure_flag =
+ penv->mem_region[i].secure_flag =
resp.mem_region_info[i].secure_flag;
icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n",
- i, penv->icnss_mem_region[i].reg_addr,
- penv->icnss_mem_region[i].size,
- penv->icnss_mem_region[i].secure_flag);
+ i, penv->mem_region[i].reg_addr,
+ penv->mem_region[i].size,
+ penv->mem_region[i].secure_flag);
}
return 0;
out:
penv->stats.msa_info_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -1180,7 +1198,7 @@ static int wlfw_msa_ready_send_sync_msg(void)
out:
penv->stats.msa_ready_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -1243,7 +1261,7 @@ static int wlfw_ind_register_send_sync_msg(void)
out:
penv->stats.ind_register_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -1312,7 +1330,7 @@ static int wlfw_cap_send_sync_msg(void)
out:
penv->stats.cap_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -1373,7 +1391,7 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
out:
penv->stats.mode_req_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -1423,7 +1441,7 @@ static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
out:
penv->stats.cfg_req_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -1476,7 +1494,7 @@ static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode)
out:
penv->stats.ini_req_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -1642,7 +1660,7 @@ static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
out:
priv->stats.rejuvenate_ack_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -1774,6 +1792,8 @@ static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
case QMI_WLFW_REJUVENATE_IND_V01:
icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n",
msg_id, penv->state);
+
+ icnss_ignore_qmi_timeout(true);
event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
if (event_data == NULL)
return;
@@ -1916,6 +1936,9 @@ static int icnss_call_driver_probe(struct icnss_priv *priv)
if (!priv->ops || !priv->ops->probe)
return 0;
+ if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+ return -EINVAL;
+
icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
icnss_hw_power_on(priv);
@@ -2142,7 +2165,7 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
struct icnss_event_pd_service_down_data *event_data = data;
if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
- return 0;
+ goto out;
if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
@@ -2159,6 +2182,8 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
out:
kfree(data);
+ icnss_ignore_qmi_timeout(false);
+
return ret;
}
@@ -2300,9 +2325,11 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
if (test_bit(ICNSS_PDR_ENABLED, &priv->state))
return NOTIFY_OK;
- icnss_pr_info("Modem went down, state: %lx, crashed: %d\n",
+ icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
priv->state, notif->crashed);
+ icnss_ignore_qmi_timeout(true);
+
event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
if (event_data == NULL)
@@ -2409,6 +2436,8 @@ static int icnss_service_notifier_notify(struct notifier_block *nb,
}
event_post:
+ icnss_ignore_qmi_timeout(true);
+
icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
ICNSS_EVENT_SYNC, event_data);
done:
@@ -2517,7 +2546,7 @@ static int icnss_pd_restart_enable(struct icnss_priv *priv)
return 0;
out:
- icnss_pr_err("PD restart not enabled: %d\n", ret);
+ icnss_pr_err("Failed to enable PD restart: %d\n", ret);
return ret;
}
@@ -3130,22 +3159,23 @@ int icnss_trigger_recovery(struct device *dev)
goto out;
}
- if (test_bit(ICNSS_PDR_ENABLED, &priv->state)) {
- icnss_pr_err("PD restart not enabled: state: 0x%lx\n",
+ if (!test_bit(ICNSS_PDR_ENABLED, &priv->state)) {
+ icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n",
priv->state);
ret = -EOPNOTSUPP;
goto out;
}
- if (!priv->service_notifier[0].handle) {
+ if (!priv->service_notifier || !priv->service_notifier[0].handle) {
icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n",
priv->state);
ret = -EINVAL;
goto out;
}
- icnss_pr_dbg("Initiate PD restart at WLAN FW, state: 0x%lx\n",
- priv->state);
+ WARN_ON(1);
+ icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n",
+ priv->state);
priv->stats.trigger_recovery++;
/*
@@ -3343,6 +3373,7 @@ static int icnss_fw_debug_show(struct seq_file *s, void *data)
seq_puts(s, " VAL: 0 (Test mode disable)\n");
seq_puts(s, " VAL: 1 (WLAN FW test)\n");
seq_puts(s, " VAL: 2 (CCPM test)\n");
+ seq_puts(s, " VAL: 3 (Trigger Recovery)\n");
seq_puts(s, "\nCMD: dynamic_feature_mask\n");
seq_puts(s, " VAL: (64 bit feature mask)\n");
diff --git a/drivers/soc/qcom/ipc_router_mhi_xprt.c b/drivers/soc/qcom/ipc_router_mhi_xprt.c
index 9a0624804c21..f9d967fd0af6 100644
--- a/drivers/soc/qcom/ipc_router_mhi_xprt.c
+++ b/drivers/soc/qcom/ipc_router_mhi_xprt.c
@@ -792,20 +792,14 @@ static int ipc_router_mhi_driver_register(
{
int rc_status;
- rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.out_handle,
- mhi_xprtp->ch_hndl.out_chan_id, 0,
- &mhi_xprtp->ch_hndl.out_clnt_info,
- (void *)mhi_xprtp);
+ rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.out_handle, NULL);
if (rc_status) {
IPC_RTR_ERR("%s: Error %d registering out_chan for %s\n",
__func__, rc_status, mhi_xprtp->xprt_name);
return -EFAULT;
}
- rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.in_handle,
- mhi_xprtp->ch_hndl.in_chan_id, 0,
- &mhi_xprtp->ch_hndl.in_clnt_info,
- (void *)mhi_xprtp);
+ rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.in_handle, NULL);
if (rc_status) {
mhi_deregister_channel(mhi_xprtp->ch_hndl.out_handle);
IPC_RTR_ERR("%s: Error %d registering in_chan for %s\n",
diff --git a/drivers/soc/qcom/mpm-of.c b/drivers/soc/qcom/mpm-of.c
index 93f2de8a59dd..77c50528be4b 100644
--- a/drivers/soc/qcom/mpm-of.c
+++ b/drivers/soc/qcom/mpm-of.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -129,18 +129,6 @@ static uint32_t *msm_mpm_falling_edge;
static uint32_t *msm_mpm_rising_edge;
static uint32_t *msm_mpm_polarity;
-enum {
- MSM_MPM_DEBUG_NON_DETECTABLE_IRQ = BIT(0),
- MSM_MPM_DEBUG_PENDING_IRQ = BIT(1),
- MSM_MPM_DEBUG_WRITE = BIT(2),
- MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE = BIT(3),
-};
-
-static int msm_mpm_debug_mask = 1;
-module_param_named(
- debug_mask, msm_mpm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
-);
-
enum mpm_state {
MSM_MPM_GIC_IRQ_MAPPING_DONE = BIT(0),
MSM_MPM_GPIO_IRQ_MAPPING_DONE = BIT(1),
@@ -174,9 +162,6 @@ static inline void msm_mpm_write(
unsigned int offset = reg * MSM_MPM_REG_WIDTH + subreg_index + 2;
__raw_writel(value, msm_mpm_dev_data.mpm_request_reg_base + offset * 4);
- if (MSM_MPM_DEBUG_WRITE & msm_mpm_debug_mask)
- pr_info("%s: reg %u.%u: 0x%08x\n",
- __func__, reg, subreg_index, value);
}
static inline void msm_mpm_send_interrupt(void)
@@ -513,37 +498,19 @@ int msm_mpm_set_pin_type(unsigned int pin, unsigned int flow_type)
static bool msm_mpm_interrupts_detectable(int d, bool from_idle)
{
unsigned long *irq_bitmap;
- bool debug_mask, ret = false;
+ bool ret = false;
struct mpm_irqs *unlisted = &unlisted_irqs[d];
if (!msm_mpm_is_initialized())
return false;
- if (from_idle) {
+ if (from_idle)
irq_bitmap = unlisted->enabled_irqs;
- debug_mask = msm_mpm_debug_mask &
- MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE;
- } else {
+ else
irq_bitmap = unlisted->wakeup_irqs;
- debug_mask = msm_mpm_debug_mask &
- MSM_MPM_DEBUG_NON_DETECTABLE_IRQ;
- }
ret = (bool) bitmap_empty(irq_bitmap, unlisted->size);
- if (debug_mask && !ret) {
- int i = 0;
- i = find_first_bit(irq_bitmap, unlisted->size);
- pr_info("%s(): %s preventing system sleep modes during %s\n",
- __func__, unlisted->domain_name,
- from_idle ? "idle" : "suspend");
-
- while (i < unlisted->size) {
- pr_info("\thwirq: %d\n", i);
- i = find_next_bit(irq_bitmap, unlisted->size, i + 1);
- }
- }
-
return ret;
}
@@ -601,10 +568,6 @@ void msm_mpm_exit_sleep(bool from_idle)
pending = msm_mpm_read(MSM_MPM_REG_STATUS, i);
pending &= enabled_intr[i];
- if (MSM_MPM_DEBUG_PENDING_IRQ & msm_mpm_debug_mask)
- pr_info("%s: enabled_intr.%d pending.%d: 0x%08x 0x%08lx\n",
- __func__, i, i, enabled_intr[i], pending);
-
k = find_first_bit(&pending, 32);
while (k < 32) {
unsigned int mpm_irq = 32 * i + k;
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c b/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c
index 10fb4cc8ebff..aafe82f59b9a 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is Mree software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1349,6 +1349,7 @@ static void unregister_adhoc(struct msm_bus_client_handle *cl)
cl->first_hop, cl->active_only);
commit_data();
msm_bus_dbg_remove_client(cl);
+ kfree(cl->name);
kfree(cl);
exit_unregister_client:
rt_mutex_unlock(&msm_bus_adhoc_lock);
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c b/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c
index 06657a666f2e..305f31f31bf8 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c
@@ -531,7 +531,7 @@ static int msm_bus_enable_node_qos_clk(struct msm_bus_node_device_type *node)
{
struct msm_bus_node_device_type *bus_node = NULL;
int i;
- int ret;
+ int ret = 0;
long rounded_rate;
if (!node || (!to_msm_bus_node(node->node_info->bus_device))) {
diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c
index 21e9f17e6a7e..6e5ddc4a3a7d 100644
--- a/drivers/soc/qcom/peripheral-loader.c
+++ b/drivers/soc/qcom/peripheral-loader.c
@@ -465,6 +465,8 @@ static int pil_alloc_region(struct pil_priv *priv, phys_addr_t min_addr,
if (region == NULL) {
pil_err(priv->desc, "Failed to allocate relocatable region of size %zx\n",
size);
+ priv->region_start = 0;
+ priv->region_end = 0;
return -ENOMEM;
}
@@ -920,7 +922,8 @@ out:
&desc->attrs);
priv->region = NULL;
}
- pil_clear_segment(desc);
+ if (desc->clear_fw_region && priv->region_start)
+ pil_clear_segment(desc);
pil_release_mmap(desc);
}
return ret;
diff --git a/drivers/soc/qcom/peripheral-loader.h b/drivers/soc/qcom/peripheral-loader.h
index 802abe26a960..9521cf726069 100644
--- a/drivers/soc/qcom/peripheral-loader.h
+++ b/drivers/soc/qcom/peripheral-loader.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -37,6 +37,7 @@ struct pil_priv;
* This defaults to iounmap if not specified.
* @shutdown_fail: Set if PIL op for shutting down subsystem fails.
* @modem_ssr: true if modem is restarting, false if booting for first time.
+ * @clear_fw_region: Clear fw region on failure in loading.
* @subsys_vmid: memprot id for the subsystem.
*/
struct pil_desc {
@@ -56,6 +57,7 @@ struct pil_desc {
void *map_data;
bool shutdown_fail;
bool modem_ssr;
+ bool clear_fw_region;
u32 subsys_vmid;
};
diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c
index 0e023a019280..793edc5b67ed 100644
--- a/drivers/soc/qcom/pil-q6v5-mss.c
+++ b/drivers/soc/qcom/pil-q6v5-mss.c
@@ -276,8 +276,12 @@ static int pil_mss_loadable_init(struct modem_data *drv,
if (q6->cx_ipeak_vote) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"cxip_lm_vote_clear");
- q6->cxip_lm_vote_clear = devm_ioremap_resource(&pdev->dev,
- res);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get resource for ipeak reg\n");
+ return -EINVAL;
+ }
+ q6->cxip_lm_vote_clear = devm_ioremap(&pdev->dev,
+ res->start, resource_size(res));
if (!q6->cxip_lm_vote_clear)
return -ENOMEM;
}
diff --git a/drivers/soc/qcom/pil-q6v5.c b/drivers/soc/qcom/pil-q6v5.c
index a39c2b6aa672..34228f072b28 100644
--- a/drivers/soc/qcom/pil-q6v5.c
+++ b/drivers/soc/qcom/pil-q6v5.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -588,6 +588,7 @@ struct q6v5_data *pil_q6v5_init(struct platform_device *pdev)
if (ret)
return ERR_PTR(ret);
+ desc->clear_fw_region = false;
desc->dev = &pdev->dev;
drv->qdsp6v5_2_0 = of_device_is_compatible(pdev->dev.of_node,
diff --git a/drivers/soc/qcom/qmi_interface.c b/drivers/soc/qcom/qmi_interface.c
index d6853e4bea72..0e7bf13c192b 100644
--- a/drivers/soc/qcom/qmi_interface.c
+++ b/drivers/soc/qcom/qmi_interface.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -101,6 +101,7 @@ struct elem_info qmi_response_type_v01_ei[] = {
.ei_array = NULL,
},
};
+EXPORT_SYMBOL(qmi_response_type_v01_ei);
struct elem_info qmi_error_resp_type_v01_ei[] = {
{
diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c
index c7059d60d786..f0f9306ebe47 100644
--- a/drivers/soc/qcom/qpnp-haptic.c
+++ b/drivers/soc/qcom/qpnp-haptic.c
@@ -10,6 +10,8 @@
* GNU General Public License for more details.
*/
+#define pr_fmt(fmt) "haptic: %s: " fmt, __func__
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -25,23 +27,21 @@
#include <linux/qpnp/pwm.h>
#include <linux/err.h>
#include <linux/delay.h>
-#include <linux/qpnp/qpnp-revid.h>
+#include <linux/log2.h>
+#include <linux/qpnp-misc.h>
#include <linux/qpnp/qpnp-haptic.h>
+#include <linux/qpnp/qpnp-revid.h>
#include "../../staging/android/timed_output.h"
-#define QPNP_IRQ_FLAGS (IRQF_TRIGGER_RISING | \
- IRQF_TRIGGER_FALLING | \
- IRQF_ONESHOT)
-
#define QPNP_HAP_STATUS(b) (b + 0x0A)
#define QPNP_HAP_LRA_AUTO_RES_LO(b) (b + 0x0B)
#define QPNP_HAP_LRA_AUTO_RES_HI(b) (b + 0x0C)
#define QPNP_HAP_EN_CTL_REG(b) (b + 0x46)
#define QPNP_HAP_EN_CTL2_REG(b) (b + 0x48)
#define QPNP_HAP_AUTO_RES_CTRL(b) (b + 0x4B)
-#define QPNP_HAP_ACT_TYPE_REG(b) (b + 0x4C)
-#define QPNP_HAP_WAV_SHAPE_REG(b) (b + 0x4D)
-#define QPNP_HAP_PLAY_MODE_REG(b) (b + 0x4E)
+#define QPNP_HAP_CFG1_REG(b) (b + 0x4C)
+#define QPNP_HAP_CFG2_REG(b) (b + 0x4D)
+#define QPNP_HAP_SEL_REG(b) (b + 0x4E)
#define QPNP_HAP_LRA_AUTO_RES_REG(b) (b + 0x4F)
#define QPNP_HAP_VMAX_REG(b) (b + 0x51)
#define QPNP_HAP_ILIM_REG(b) (b + 0x52)
@@ -61,7 +61,7 @@
#define QPNP_HAP_TEST2_REG(b) (b + 0xE3)
#define QPNP_HAP_STATUS_BUSY 0x02
-#define QPNP_HAP_ACT_TYPE_MASK 0xFE
+#define QPNP_HAP_ACT_TYPE_MASK BIT(0)
#define QPNP_HAP_LRA 0x0
#define QPNP_HAP_ERM 0x1
#define QPNP_HAP_AUTO_RES_MODE_MASK GENMASK(6, 4)
@@ -75,35 +75,33 @@
#define QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT BIT(3)
#define QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT 3
#define QPNP_HAP_PM660_LRA_ZXD_CAL_PERIOD_BIT GENMASK(2, 0)
-#define QPNP_HAP_LRA_HIGH_Z_MASK GENMASK(3, 2)
-#define QPNP_HAP_LRA_HIGH_Z_SHIFT 2
-#define QPNP_HAP_LRA_RES_CAL_PER_MASK GENMASK(1, 0)
+#define QPNP_HAP_LRA_HIGH_Z_MASK GENMASK(3, 2)
+#define QPNP_HAP_LRA_HIGH_Z_SHIFT 2
+#define QPNP_HAP_LRA_RES_CAL_PER_MASK GENMASK(1, 0)
#define QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK GENMASK(2, 0)
-#define QPNP_HAP_RES_CAL_PERIOD_MIN 4
-#define QPNP_HAP_RES_CAL_PERIOD_MAX 32
-#define QPNP_HAP_PM660_RES_CAL_PERIOD_MIN 4
+#define QPNP_HAP_RES_CAL_PERIOD_MIN 4
+#define QPNP_HAP_RES_CAL_PERIOD_MAX 32
#define QPNP_HAP_PM660_RES_CAL_PERIOD_MAX 256
-#define QPNP_HAP_PLAY_MODE_MASK 0xCF
-#define QPNP_HAP_PLAY_MODE_SHFT 4
-#define QPNP_HAP_VMAX_MASK 0xC1
+#define QPNP_HAP_WF_SOURCE_MASK GENMASK(5, 4)
+#define QPNP_HAP_WF_SOURCE_SHIFT 4
+#define QPNP_HAP_VMAX_MASK GENMASK(5, 1)
#define QPNP_HAP_VMAX_SHIFT 1
#define QPNP_HAP_VMAX_MIN_MV 116
#define QPNP_HAP_VMAX_MAX_MV 3596
-#define QPNP_HAP_ILIM_MASK 0xFE
+#define QPNP_HAP_ILIM_MASK BIT(0)
#define QPNP_HAP_ILIM_MIN_MV 400
#define QPNP_HAP_ILIM_MAX_MV 800
-#define QPNP_HAP_SC_DEB_MASK 0xF8
-#define QPNP_HAP_SC_DEB_SUB 2
+#define QPNP_HAP_SC_DEB_MASK GENMASK(2, 0)
#define QPNP_HAP_SC_DEB_CYCLES_MIN 0
#define QPNP_HAP_DEF_SC_DEB_CYCLES 8
#define QPNP_HAP_SC_DEB_CYCLES_MAX 32
#define QPNP_HAP_SC_CLR 1
-#define QPNP_HAP_INT_PWM_MASK 0xFC
+#define QPNP_HAP_INT_PWM_MASK GENMASK(1, 0)
#define QPNP_HAP_INT_PWM_FREQ_253_KHZ 253
#define QPNP_HAP_INT_PWM_FREQ_505_KHZ 505
#define QPNP_HAP_INT_PWM_FREQ_739_KHZ 739
#define QPNP_HAP_INT_PWM_FREQ_1076_KHZ 1076
-#define QPNP_HAP_WAV_SHAPE_MASK 0xFE
+#define QPNP_HAP_WAV_SHAPE_MASK BIT(0)
#define QPNP_HAP_RATE_CFG1_MASK 0xFF
#define QPNP_HAP_RATE_CFG2_MASK 0xF0
#define QPNP_HAP_RATE_CFG2_SHFT 8
@@ -111,9 +109,9 @@
#define QPNP_HAP_WAV_PLAY_RATE_US_MIN 0
#define QPNP_HAP_DEF_WAVE_PLAY_RATE_US 5715
#define QPNP_HAP_WAV_PLAY_RATE_US_MAX 20475
-#define QPNP_HAP_WAV_REP_MASK 0x8F
-#define QPNP_HAP_WAV_S_REP_MASK 0xFC
-#define QPNP_HAP_WAV_REP_SHFT 4
+#define QPNP_HAP_WAV_REP_MASK GENMASK(6, 4)
+#define QPNP_HAP_WAV_S_REP_MASK GENMASK(1, 0)
+#define QPNP_HAP_WAV_REP_SHIFT 4
#define QPNP_HAP_WAV_REP_MIN 1
#define QPNP_HAP_WAV_REP_MAX 128
#define QPNP_HAP_WAV_S_REP_MIN 1
@@ -121,13 +119,13 @@
#define QPNP_HAP_BRAKE_PAT_MASK 0x3
#define QPNP_HAP_ILIM_MIN_MA 400
#define QPNP_HAP_ILIM_MAX_MA 800
-#define QPNP_HAP_EXT_PWM_MASK 0xFC
+#define QPNP_HAP_EXT_PWM_MASK GENMASK(1, 0)
#define QPNP_HAP_EXT_PWM_FREQ_25_KHZ 25
#define QPNP_HAP_EXT_PWM_FREQ_50_KHZ 50
#define QPNP_HAP_EXT_PWM_FREQ_75_KHZ 75
#define QPNP_HAP_EXT_PWM_FREQ_100_KHZ 100
#define PWM_MAX_DTEST_LINES 4
-#define QPNP_HAP_EXT_PWM_DTEST_MASK 0x0F
+#define QPNP_HAP_EXT_PWM_DTEST_MASK GENMASK(6, 4)
#define QPNP_HAP_EXT_PWM_DTEST_SHFT 4
#define QPNP_HAP_EXT_PWM_PEAK_DATA 0x7F
#define QPNP_HAP_EXT_PWM_HALF_DUTY 50
@@ -140,10 +138,9 @@
#define QPNP_HAP_BRAKE_PAT_LEN 4
#define QPNP_HAP_PLAY_EN 0x80
#define QPNP_HAP_EN 0x80
-#define QPNP_HAP_BRAKE_MASK 0xFE
-#define QPNP_HAP_TEST2_AUTO_RES_MASK 0x7F
-#define QPNP_HAP_SEC_UNLOCK 0xA5
-#define AUTO_RES_ENABLE 0x80
+#define QPNP_HAP_BRAKE_MASK BIT(0)
+#define QPNP_HAP_AUTO_RES_MASK BIT(7)
+#define AUTO_RES_ENABLE BIT(7)
#define AUTO_RES_ERR_BIT 0x10
#define SC_FOUND_BIT 0x08
#define SC_MAX_DURATION 5
@@ -155,12 +152,6 @@
#define QPNP_TEST_TIMER_MS 5
#define QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN 20000
-
-#define MISC_TRIM_ERROR_RC19P2_CLK 0x09F5
-#define MISC_SEC_ACCESS 0x09D0
-#define MISC_SEC_UNLOCK 0xA5
-#define PMI8950_MISC_SID 2
-
#define POLL_TIME_AUTO_RES_ERR_NS (20 * NSEC_PER_MSEC)
#define MAX_POSITIVE_VARIATION_LRA_FREQ 30
@@ -172,11 +163,11 @@
((MAX_POSITIVE_VARIATION_LRA_FREQ - MAX_NEGATIVE_VARIATION_LRA_FREQ) \
/ FREQ_VARIATION_STEP)
#define LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent) \
- (hap->init_drive_period_code = (hap->init_drive_period_code * \
- (1000 + rc_clk_err_percent_x10)) / 1000)
+ (hap->init_drive_period_code = (hap->init_drive_period_code * \
+ (1000 + rc_clk_err_percent_x10)) / 1000)
#define LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent) \
- (hap->init_drive_period_code = (hap->init_drive_period_code * \
- (1000 - rc_clk_err_percent_x10)) / 1000)
+ (hap->init_drive_period_code = (hap->init_drive_period_code * \
+ (1000 - rc_clk_err_percent_x10)) / 1000)
u32 adjusted_lra_play_rate_code[ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE];
@@ -318,15 +309,14 @@ struct qpnp_pwm_info {
* @ lra_res_cal_period - period for resonance calibration
* @ sc_duration - counter to determine the duration of short circuit condition
* @ state - current state of haptics
- * @ use_play_irq - play irq usage state
- * @ use_sc_irq - short circuit irq usage state
* @ wf_update - waveform update flag
* @ pwm_cfg_state - pwm mode configuration state
* @ buffer_cfg_state - buffer mode configuration state
* @ en_brake - brake state
* @ sup_brake_pat - support custom brake pattern
* @ correct_lra_drive_freq - correct LRA Drive Frequency
- * @ misc_trim_error_rc19p2_clk_reg_present - if MISC Trim Error reg is present
+ * @ misc_clk_trim_error_reg - MISC clock trim error register if present
+ * @ clk_trim_error_code - MISC clock trim error code
* @ perform_lra_auto_resonance_search - whether lra auto resonance search
* algorithm should be performed or not.
*/
@@ -344,11 +334,13 @@ struct qpnp_hap {
struct qpnp_pwm_info pwm_info;
struct mutex lock;
struct mutex wf_lock;
+ spinlock_t bus_lock;
struct completion completion;
enum qpnp_hap_mode play_mode;
enum qpnp_hap_high_z lra_high_z;
int lra_qwd_drive_duration;
int calibrate_at_eop;
+ u32 misc_clk_trim_error_reg;
u32 init_drive_period_code;
u32 timeout_ms;
u32 time_required_to_generate_back_emf_us;
@@ -380,10 +372,9 @@ struct qpnp_hap {
u8 ext_pwm_dtest_line;
u8 pmic_subtype;
u8 auto_res_mode;
+ u8 clk_trim_error_code;
bool vcc_pon_enabled;
bool state;
- bool use_play_irq;
- bool use_sc_irq;
bool manage_pon_supply;
bool wf_update;
bool pwm_cfg_state;
@@ -391,82 +382,98 @@ struct qpnp_hap {
bool en_brake;
bool sup_brake_pat;
bool correct_lra_drive_freq;
- bool misc_trim_error_rc19p2_clk_reg_present;
bool perform_lra_auto_resonance_search;
};
static struct qpnp_hap *ghap;
/* helper to read a pmic register */
-static int qpnp_hap_read_reg(struct qpnp_hap *hap, u8 *data, u16 addr)
+static int qpnp_hap_read_reg(struct qpnp_hap *hap, u16 addr, u8 *val)
{
int rc;
- uint val;
+ uint tmp;
- rc = regmap_read(hap->regmap, addr, &val);
+ rc = regmap_read(hap->regmap, addr, &tmp);
if (rc < 0)
- dev_err(&hap->pdev->dev,
- "Error reading address: %X - ret %X\n", addr, rc);
- *data = (u8)val;
+ pr_err("Error reading address: %X - ret %X\n", addr, rc);
+ *val = (u8)tmp;
return rc;
}
/* helper to write a pmic register */
-static int qpnp_hap_write_reg(struct qpnp_hap *hap, u8 *data, u16 addr)
+static int qpnp_hap_write_reg(struct qpnp_hap *hap, u16 addr, u8 val)
{
+ unsigned long flags;
int rc;
- rc = regmap_write(hap->regmap, addr, *data);
+ spin_lock_irqsave(&hap->bus_lock, flags);
+ rc = regmap_write(hap->regmap, addr, val);
if (rc < 0)
- dev_err(&hap->pdev->dev,
- "Error writing address: %X - ret %X\n", addr, rc);
+ pr_err("Error writing address: %X - ret %X\n", addr, rc);
- dev_dbg(&hap->pdev->dev, "write: HAP_0x%x = 0x%x\n", addr, *data);
+ spin_unlock_irqrestore(&hap->bus_lock, flags);
+ if (!rc)
+ pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val);
return rc;
}
-static int
-qpnp_hap_masked_write_reg(struct qpnp_hap *hap, u8 val, u16 addr, u8 mask)
+/* helper to access secure registers */
+#define QPNP_HAP_SEC_UNLOCK 0xA5
+static int qpnp_hap_sec_masked_write_reg(struct qpnp_hap *hap, u16 addr,
+ u8 mask, u8 val)
{
+ unsigned long flags;
int rc;
+ u8 tmp = QPNP_HAP_SEC_UNLOCK;
+
+ spin_lock_irqsave(&hap->bus_lock, flags);
+ rc = regmap_write(hap->regmap, QPNP_HAP_SEC_ACCESS_REG(hap->base), tmp);
+ if (rc < 0) {
+ pr_err("Error writing sec_code - ret %X\n", rc);
+ goto out;
+ }
rc = regmap_update_bits(hap->regmap, addr, mask, val);
if (rc < 0)
- pr_err("Unable to update bits from 0x%04X, rc = %d\n", addr,
- rc);
- else
- pr_debug("Wrote 0x%02X to addr 0x%04X\n", val, addr);
+ pr_err("Error writing address: %X - ret %X\n", addr, rc);
+out:
+ spin_unlock_irqrestore(&hap->bus_lock, flags);
+ if (!rc)
+ pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val);
return rc;
}
-/* helper to access secure registers */
-static int qpnp_hap_sec_access(struct qpnp_hap *hap)
+static int qpnp_hap_masked_write_reg(struct qpnp_hap *hap, u16 addr, u8 mask,
+ u8 val)
{
+ unsigned long flags;
int rc;
- u8 reg = QPNP_HAP_SEC_UNLOCK;
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_SEC_ACCESS_REG(hap->base));
- if (rc)
- return rc;
+ spin_lock_irqsave(&hap->bus_lock, flags);
+ rc = regmap_update_bits(hap->regmap, addr, mask, val);
+ if (rc < 0)
+ pr_err("Error writing address: %X - ret %X\n", addr, rc);
- return 0;
+ spin_unlock_irqrestore(&hap->bus_lock, flags);
+ if (!rc)
+ pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val);
+ return rc;
}
static void qpnp_handle_sc_irq(struct work_struct *work)
{
struct qpnp_hap *hap = container_of(work,
struct qpnp_hap, sc_work.work);
- u8 val, reg;
+ u8 val;
- qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base));
+ qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val);
/* clear short circuit register */
if (val & SC_FOUND_BIT) {
hap->sc_duration++;
- reg = QPNP_HAP_SC_CLR;
- qpnp_hap_write_reg(hap, &reg, QPNP_HAP_SC_CLR_REG(hap->base));
+ val = QPNP_HAP_SC_CLR;
+ qpnp_hap_write_reg(hap, QPNP_HAP_SC_CLR_REG(hap->base), val);
}
}
@@ -484,10 +491,10 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on)
unsigned long sleep_time =
QPNP_HAP_CYCLS * hap->wave_play_rate_us;
- rc = qpnp_hap_read_reg(hap, &val,
- QPNP_HAP_STATUS(hap->base));
+ rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base),
+ &val);
- dev_dbg(&hap->pdev->dev, "HAP_STATUS=0x%x\n", val);
+ pr_debug("HAP_STATUS=0x%x\n", val);
/* wait for QPNP_HAP_CYCLS cycles of play rate */
if (val & QPNP_HAP_STATUS_BUSY) {
@@ -500,14 +507,12 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on)
}
if (i >= QPNP_HAP_MAX_RETRIES)
- dev_dbg(&hap->pdev->dev,
- "Haptics Busy. Force disable\n");
+ pr_debug("Haptics Busy. Force disable\n");
val &= ~QPNP_HAP_EN;
}
- rc = qpnp_hap_write_reg(hap, &val,
- QPNP_HAP_EN_CTL_REG(hap->base));
+ rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), val);
if (rc < 0)
return rc;
@@ -527,8 +532,7 @@ static int qpnp_hap_play(struct qpnp_hap *hap, int on)
else
val &= ~QPNP_HAP_PLAY_EN;
- rc = qpnp_hap_write_reg(hap, &val,
- QPNP_HAP_PLAY_REG(hap->base));
+ rc = qpnp_hap_write_reg(hap, QPNP_HAP_PLAY_REG(hap->base), val);
if (rc < 0)
return rc;
@@ -548,7 +552,7 @@ static ssize_t qpnp_hap_dump_regs_show(struct device *dev,
u8 val;
for (i = 0; i < ARRAY_SIZE(qpnp_hap_dbg_regs); i++) {
- qpnp_hap_read_reg(hap, &val, hap->base + qpnp_hap_dbg_regs[i]);
+ qpnp_hap_read_reg(hap, hap->base + qpnp_hap_dbg_regs[i], &val);
count += snprintf(buf + count, PAGE_SIZE - count,
"qpnp_haptics: REG_0x%x = 0x%x\n",
hap->base + qpnp_hap_dbg_regs[i],
@@ -566,15 +570,15 @@ static irqreturn_t qpnp_hap_play_irq(int irq, void *_hap)
{
struct qpnp_hap *hap = _hap;
int i, rc;
- u8 reg;
+ u8 val;
mutex_lock(&hap->wf_lock);
/* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */
for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN && hap->wf_update; i++) {
- reg = hap->wave_samp[i] = hap->shadow_wave_samp[i];
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_WAV_S_REG_BASE(hap->base) + i);
+ val = hap->wave_samp[i] = hap->shadow_wave_samp[i];
+ rc = qpnp_hap_write_reg(hap,
+ QPNP_HAP_WAV_S_REG_BASE(hap->base) + i, val);
if (rc)
goto unlock;
}
@@ -591,13 +595,12 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap)
{
struct qpnp_hap *hap = _hap;
int rc;
- u8 disable_haptics = 0x00;
u8 val;
- dev_dbg(&hap->pdev->dev, "Short circuit detected\n");
+ pr_debug("Short circuit detected\n");
if (hap->sc_duration < SC_MAX_DURATION) {
- qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base));
+ qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val);
if (val & SC_FOUND_BIT)
schedule_delayed_work(&hap->sc_work,
QPNP_HAP_SC_IRQ_STATUS_DELAY);
@@ -607,10 +610,10 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap)
/* Disable haptics module if the duration of short circuit
* exceeds the maximum limit (5 secs).
*/
- rc = qpnp_hap_write_reg(hap, &disable_haptics,
- QPNP_HAP_EN_CTL_REG(hap->base));
- dev_err(&hap->pdev->dev,
- "Haptics disabled permanently due to short circuit\n");
+ val = 0;
+ rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base),
+ val);
+ pr_err("Haptics disabled permanently due to short circuit\n");
}
return IRQ_HANDLED;
@@ -619,8 +622,8 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap)
/* configuration api for buffer mode */
static int qpnp_hap_buffer_config(struct qpnp_hap *hap)
{
- u8 reg = 0;
- int rc, i, temp;
+ u8 val = 0;
+ int rc, i;
/* Configure the WAVE_REPEAT register */
if (hap->wave_rep_cnt < QPNP_HAP_WAV_REP_MIN)
@@ -633,44 +636,22 @@ static int qpnp_hap_buffer_config(struct qpnp_hap *hap)
else if (hap->wave_s_rep_cnt > QPNP_HAP_WAV_S_REP_MAX)
hap->wave_s_rep_cnt = QPNP_HAP_WAV_S_REP_MAX;
- rc = qpnp_hap_read_reg(hap, &reg,
- QPNP_HAP_WAV_REP_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_WAV_REP_MASK;
- temp = fls(hap->wave_rep_cnt) - 1;
- reg |= (temp << QPNP_HAP_WAV_REP_SHFT);
- reg &= QPNP_HAP_WAV_S_REP_MASK;
- temp = fls(hap->wave_s_rep_cnt) - 1;
- reg |= temp;
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_WAV_REP_REG(hap->base));
+ val = ilog2(hap->wave_rep_cnt) << QPNP_HAP_WAV_REP_SHIFT |
+ ilog2(hap->wave_s_rep_cnt);
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base),
+ QPNP_HAP_WAV_REP_MASK | QPNP_HAP_WAV_S_REP_MASK, val);
if (rc)
return rc;
/* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */
- for (i = 0, reg = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) {
- reg = hap->wave_samp[i];
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_WAV_S_REG_BASE(hap->base) + i);
+ for (i = 0, val = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) {
+ val = hap->wave_samp[i];
+ rc = qpnp_hap_write_reg(hap,
+ QPNP_HAP_WAV_S_REG_BASE(hap->base) + i, val);
if (rc)
return rc;
}
- /* setup play irq */
- if (hap->use_play_irq) {
- rc = devm_request_threaded_irq(&hap->pdev->dev, hap->play_irq,
- NULL, qpnp_hap_play_irq,
- QPNP_IRQ_FLAGS,
- "qpnp_play_irq", hap);
- if (rc < 0) {
- dev_err(&hap->pdev->dev,
- "Unable to request play(%d) IRQ(err:%d)\n",
- hap->play_irq, rc);
- return rc;
- }
- }
-
hap->buffer_cfg_state = true;
return 0;
}
@@ -678,59 +659,41 @@ static int qpnp_hap_buffer_config(struct qpnp_hap *hap)
/* configuration api for pwm */
static int qpnp_hap_pwm_config(struct qpnp_hap *hap)
{
- u8 reg = 0;
- int rc, temp;
+ u8 val = 0;
+ int rc;
/* Configure the EXTERNAL_PWM register */
if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_25_KHZ) {
hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_25_KHZ;
- temp = 0;
+ val = 0;
} else if (hap->ext_pwm_freq_khz <=
QPNP_HAP_EXT_PWM_FREQ_50_KHZ) {
hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_50_KHZ;
- temp = 1;
+ val = 1;
} else if (hap->ext_pwm_freq_khz <=
QPNP_HAP_EXT_PWM_FREQ_75_KHZ) {
hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_75_KHZ;
- temp = 2;
+ val = 2;
} else {
hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_100_KHZ;
- temp = 3;
+ val = 3;
}
- rc = qpnp_hap_read_reg(hap, &reg,
- QPNP_HAP_EXT_PWM_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_EXT_PWM_MASK;
- reg |= temp;
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_EXT_PWM_REG(hap->base));
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EXT_PWM_REG(hap->base),
+ QPNP_HAP_EXT_PWM_MASK, val);
if (rc)
return rc;
- rc = qpnp_hap_read_reg(hap, &reg,
- QPNP_HAP_TEST2_REG(hap->base));
- if (rc)
- return rc;
if (!hap->ext_pwm_dtest_line ||
hap->ext_pwm_dtest_line > PWM_MAX_DTEST_LINES) {
- dev_err(&hap->pdev->dev, "invalid dtest line\n");
+ pr_err("invalid dtest line\n");
return -EINVAL;
}
/* disable auto res for PWM mode */
- reg &= QPNP_HAP_EXT_PWM_DTEST_MASK;
- temp = hap->ext_pwm_dtest_line << QPNP_HAP_EXT_PWM_DTEST_SHFT;
- reg |= temp;
-
- /* TEST2 is a secure access register */
- rc = qpnp_hap_sec_access(hap);
- if (rc)
- return rc;
-
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_TEST2_REG(hap->base));
+ val = hap->ext_pwm_dtest_line << QPNP_HAP_EXT_PWM_DTEST_SHFT;
+ rc = qpnp_hap_sec_masked_write_reg(hap, QPNP_HAP_TEST2_REG(hap->base),
+ QPNP_HAP_EXT_PWM_DTEST_MASK | QPNP_HAP_AUTO_RES_MASK, val);
if (rc)
return rc;
@@ -738,7 +701,7 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap)
hap->pwm_info.duty_us * NSEC_PER_USEC,
hap->pwm_info.period_us * NSEC_PER_USEC);
if (rc < 0) {
- dev_err(&hap->pdev->dev, "hap pwm config failed\n");
+ pr_err("hap pwm config failed\n");
pwm_free(hap->pwm_info.pwm_dev);
return -ENODEV;
}
@@ -748,72 +711,171 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap)
return 0;
}
+static int qpnp_hap_lra_auto_res_config(struct qpnp_hap *hap)
+{
+ int rc;
+ u8 val, mask;
+
+ /* disable auto resonance for ERM */
+ if (hap->act_type == QPNP_HAP_ERM) {
+ val = 0x00;
+ rc = qpnp_hap_write_reg(hap,
+ QPNP_HAP_LRA_AUTO_RES_REG(hap->base), val);
+ return rc;
+ }
+
+ if (hap->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN)
+ hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN;
+
+ if (hap->pmic_subtype == PM660_SUBTYPE) {
+ if (hap->lra_res_cal_period >
+ QPNP_HAP_PM660_RES_CAL_PERIOD_MAX)
+ hap->lra_res_cal_period =
+ QPNP_HAP_PM660_RES_CAL_PERIOD_MAX;
+
+ if (hap->auto_res_mode == QPNP_HAP_PM660_AUTO_RES_QWD)
+ hap->lra_res_cal_period = 0;
+ } else {
+ if (hap->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX)
+ hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX;
+ }
+
+ val = mask = 0;
+ if (hap->lra_res_cal_period)
+ val = ilog2(hap->lra_res_cal_period /
+ QPNP_HAP_RES_CAL_PERIOD_MIN);
+
+ if (hap->pmic_subtype == PM660_SUBTYPE) {
+ val |= hap->auto_res_mode <<
+ QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT;
+ mask = QPNP_HAP_PM660_AUTO_RES_MODE_BIT;
+ val |= hap->lra_high_z <<
+ QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT;
+ mask |= QPNP_HAP_PM660_CALIBRATE_DURATION_MASK;
+ if (hap->lra_qwd_drive_duration != -EINVAL) {
+ val |= hap->lra_qwd_drive_duration <<
+ QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT;
+ mask |= QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT;
+ }
+ if (hap->calibrate_at_eop != -EINVAL) {
+ val |= hap->calibrate_at_eop <<
+ QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT;
+ mask |= QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT;
+ }
+ mask |= QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK;
+ } else {
+ val |= (hap->auto_res_mode << QPNP_HAP_AUTO_RES_MODE_SHIFT);
+ val |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT);
+ mask = QPNP_HAP_AUTO_RES_MODE_MASK | QPNP_HAP_LRA_HIGH_Z_MASK |
+ QPNP_HAP_LRA_RES_CAL_PER_MASK;
+ }
+
+ rc = qpnp_hap_masked_write_reg(hap,
+ QPNP_HAP_LRA_AUTO_RES_REG(hap->base), mask, val);
+ return rc;
+}
+
/* configuration api for play mode */
static int qpnp_hap_play_mode_config(struct qpnp_hap *hap)
{
- u8 reg = 0;
- int rc, temp;
+ u8 val = 0;
+ int rc;
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_PLAY_MODE_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_PLAY_MODE_MASK;
- temp = hap->play_mode << QPNP_HAP_PLAY_MODE_SHFT;
- reg |= temp;
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_PLAY_MODE_REG(hap->base));
- if (rc)
- return rc;
- return 0;
+ val = hap->play_mode << QPNP_HAP_WF_SOURCE_SHIFT;
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_SEL_REG(hap->base),
+ QPNP_HAP_WF_SOURCE_MASK, val);
+ return rc;
}
-/* configuration api for max volatge */
+/* configuration api for max voltage */
static int qpnp_hap_vmax_config(struct qpnp_hap *hap)
{
- u8 reg = 0;
- int rc, temp;
+ u8 val = 0;
+ int rc;
if (hap->vmax_mv < QPNP_HAP_VMAX_MIN_MV)
hap->vmax_mv = QPNP_HAP_VMAX_MIN_MV;
else if (hap->vmax_mv > QPNP_HAP_VMAX_MAX_MV)
hap->vmax_mv = QPNP_HAP_VMAX_MAX_MV;
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_VMAX_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_VMAX_MASK;
- temp = hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV;
- reg |= (temp << QPNP_HAP_VMAX_SHIFT);
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_VMAX_REG(hap->base));
- if (rc)
- return rc;
+ val = (hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV) << QPNP_HAP_VMAX_SHIFT;
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_VMAX_REG(hap->base),
+ QPNP_HAP_VMAX_MASK, val);
+ return rc;
+}
- return 0;
+/* configuration api for ilim */
+static int qpnp_hap_ilim_config(struct qpnp_hap *hap)
+{
+ u8 val = 0;
+ int rc;
+
+ if (hap->ilim_ma < QPNP_HAP_ILIM_MIN_MA)
+ hap->ilim_ma = QPNP_HAP_ILIM_MIN_MA;
+ else if (hap->ilim_ma > QPNP_HAP_ILIM_MAX_MA)
+ hap->ilim_ma = QPNP_HAP_ILIM_MAX_MA;
+
+ val = (hap->ilim_ma / QPNP_HAP_ILIM_MIN_MA) - 1;
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_ILIM_REG(hap->base),
+ QPNP_HAP_ILIM_MASK, val);
+ return rc;
}
/* configuration api for short circuit debounce */
static int qpnp_hap_sc_deb_config(struct qpnp_hap *hap)
{
- u8 reg = 0;
- int rc, temp;
+ u8 val = 0;
+ int rc;
if (hap->sc_deb_cycles < QPNP_HAP_SC_DEB_CYCLES_MIN)
hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MIN;
else if (hap->sc_deb_cycles > QPNP_HAP_SC_DEB_CYCLES_MAX)
hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MAX;
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_SC_DEB_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_SC_DEB_MASK;
- if (hap->sc_deb_cycles) {
- temp = fls(hap->sc_deb_cycles) - 1;
- reg |= temp - QPNP_HAP_SC_DEB_SUB;
+ if (hap->sc_deb_cycles != QPNP_HAP_SC_DEB_CYCLES_MIN)
+ val = ilog2(hap->sc_deb_cycles /
+ QPNP_HAP_DEF_SC_DEB_CYCLES) + 1;
+ else
+ val = QPNP_HAP_SC_DEB_CYCLES_MIN;
+
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_SC_DEB_REG(hap->base),
+ QPNP_HAP_SC_DEB_MASK, val);
+
+ return rc;
+}
+
+static int qpnp_hap_int_pwm_config(struct qpnp_hap *hap)
+{
+ int rc;
+ u8 val;
+
+ if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) {
+ if (hap->pmic_subtype == PM660_SUBTYPE) {
+ hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ;
+ val = 1;
+ } else {
+ hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_253_KHZ;
+ val = 0;
+ }
+ } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_505_KHZ) {
+ hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ;
+ val = 1;
+ } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_739_KHZ) {
+ hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_739_KHZ;
+ val = 2;
+ } else {
+ hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_1076_KHZ;
+ val = 3;
}
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_SC_DEB_REG(hap->base));
+
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_INT_PWM_REG(hap->base),
+ QPNP_HAP_INT_PWM_MASK, val);
if (rc)
return rc;
- return 0;
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_PWM_CAP_REG(hap->base),
+ QPNP_HAP_INT_PWM_MASK, val);
+ return rc;
}
/* DT parsing api for buffer mode */
@@ -830,7 +892,7 @@ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap)
if (!rc) {
hap->wave_rep_cnt = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read rep cnt\n");
+ pr_err("Unable to read rep cnt\n");
return rc;
}
@@ -840,30 +902,20 @@ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap)
if (!rc) {
hap->wave_s_rep_cnt = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read samp rep cnt\n");
+ pr_err("Unable to read samp rep cnt\n");
return rc;
}
prop = of_find_property(pdev->dev.of_node,
"qcom,wave-samples", &temp);
if (!prop || temp != QPNP_HAP_WAV_SAMP_LEN) {
- dev_err(&pdev->dev, "Invalid wave samples, use default");
+ pr_err("Invalid wave samples, use default");
for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++)
hap->wave_samp[i] = QPNP_HAP_WAV_SAMP_MAX;
} else {
memcpy(hap->wave_samp, prop->value, QPNP_HAP_WAV_SAMP_LEN);
}
- hap->use_play_irq = of_property_read_bool(pdev->dev.of_node,
- "qcom,use-play-irq");
- if (hap->use_play_irq) {
- hap->play_irq = platform_get_irq_byname(hap->pdev, "play-irq");
- if (hap->play_irq < 0) {
- dev_err(&pdev->dev, "Unable to get play irq\n");
- return hap->play_irq;
- }
- }
-
return 0;
}
@@ -880,7 +932,7 @@ static int qpnp_hap_parse_pwm_dt(struct qpnp_hap *hap)
if (!rc) {
hap->ext_pwm_freq_khz = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read ext pwm freq\n");
+ pr_err("Unable to read ext pwm freq\n");
return rc;
}
@@ -895,7 +947,7 @@ static int qpnp_hap_parse_pwm_dt(struct qpnp_hap *hap)
if (IS_ERR(hap->pwm_info.pwm_dev)) {
rc = PTR_ERR(hap->pwm_info.pwm_dev);
- dev_err(&pdev->dev, "Cannot get PWM device rc:(%d)\n", rc);
+ pr_err("Cannot get PWM device rc:(%d)\n", rc);
hap->pwm_info.pwm_dev = NULL;
return rc;
}
@@ -930,7 +982,7 @@ static ssize_t qpnp_hap_wf_samp_show(struct device *dev, char *buf, int index)
timed_dev);
if (index < 0 || index >= QPNP_HAP_WAV_SAMP_LEN) {
- dev_err(dev, "Invalid sample index(%d)\n", index);
+ pr_err("Invalid sample index(%d)\n", index);
return -EINVAL;
}
@@ -996,7 +1048,7 @@ static ssize_t qpnp_hap_wf_samp_store(struct device *dev,
int data, rc;
if (index < 0 || index >= QPNP_HAP_WAV_SAMP_LEN) {
- dev_err(dev, "Invalid sample index(%d)\n", index);
+ pr_err("Invalid sample index(%d)\n", index);
return -EINVAL;
}
@@ -1005,7 +1057,7 @@ static ssize_t qpnp_hap_wf_samp_store(struct device *dev,
return rc;
if (data < 0 || data > 0xff) {
- dev_err(dev, "Invalid sample wf_%d (%d)\n", index, data);
+ pr_err("Invalid sample wf_%d (%d)\n", index, data);
return -EINVAL;
}
@@ -1105,8 +1157,8 @@ static ssize_t qpnp_hap_wf_rep_store(struct device *dev,
struct timed_output_dev *timed_dev = dev_get_drvdata(dev);
struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap,
timed_dev);
- int data, rc, temp;
- u8 reg;
+ int data, rc;
+ u8 val;
rc = kstrtoint(buf, 10, &data);
if (rc)
@@ -1117,19 +1169,11 @@ static ssize_t qpnp_hap_wf_rep_store(struct device *dev,
else if (data > QPNP_HAP_WAV_REP_MAX)
data = QPNP_HAP_WAV_REP_MAX;
- rc = qpnp_hap_read_reg(hap, &reg,
- QPNP_HAP_WAV_REP_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_WAV_REP_MASK;
- temp = fls(data) - 1;
- reg |= (temp << QPNP_HAP_WAV_REP_SHFT);
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_WAV_REP_REG(hap->base));
- if (rc)
- return rc;
-
- hap->wave_rep_cnt = data;
+ val = ilog2(data) << QPNP_HAP_WAV_REP_SHIFT;
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base),
+ QPNP_HAP_WAV_REP_MASK, val);
+ if (!rc)
+ hap->wave_rep_cnt = data;
return count;
}
@@ -1152,8 +1196,8 @@ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev,
struct timed_output_dev *timed_dev = dev_get_drvdata(dev);
struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap,
timed_dev);
- int data, rc, temp;
- u8 reg;
+ int data, rc;
+ u8 val;
rc = kstrtoint(buf, 10, &data);
if (rc)
@@ -1164,19 +1208,11 @@ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev,
else if (data > QPNP_HAP_WAV_S_REP_MAX)
data = QPNP_HAP_WAV_S_REP_MAX;
- rc = qpnp_hap_read_reg(hap, &reg,
- QPNP_HAP_WAV_REP_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_WAV_S_REP_MASK;
- temp = fls(data) - 1;
- reg |= temp;
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_WAV_REP_REG(hap->base));
- if (rc)
- return rc;
-
- hap->wave_s_rep_cnt = data;
+ val = ilog2(hap->wave_s_rep_cnt);
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base),
+ QPNP_HAP_WAV_S_REP_MASK, val);
+ if (!rc)
+ hap->wave_s_rep_cnt = data;
return count;
}
@@ -1397,25 +1433,22 @@ static int calculate_lra_code(struct qpnp_hap *hap)
u8 neg_idx = 0, pos_idx = ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE - 1;
int rc = 0;
- rc = qpnp_hap_read_reg(hap, &lra_drive_period_code_lo,
- QPNP_HAP_RATE_CFG1_REG(hap->base));
+ rc = qpnp_hap_read_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base),
+ &lra_drive_period_code_lo);
if (rc) {
- dev_err(&hap->pdev->dev,
- "Error while reading RATE_CFG1 register\n");
+ pr_err("Error while reading RATE_CFG1 register\n");
return rc;
}
- rc = qpnp_hap_read_reg(hap, &lra_drive_period_code_hi,
- QPNP_HAP_RATE_CFG2_REG(hap->base));
+ rc = qpnp_hap_read_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base),
+ &lra_drive_period_code_hi);
if (rc) {
- dev_err(&hap->pdev->dev,
- "Error while reading RATE_CFG2 register\n");
+ pr_err("Error while reading RATE_CFG2 register\n");
return rc;
}
if (!lra_drive_period_code_lo && !lra_drive_period_code_hi) {
- dev_err(&hap->pdev->dev,
- "Unexpected Error: both RATE_CFG1 and RATE_CFG2 read 0\n");
+ pr_err("Unexpected Error: both RATE_CFG1 and RATE_CFG2 read 0\n");
return -EINVAL;
}
@@ -1433,16 +1466,14 @@ static int calculate_lra_code(struct qpnp_hap *hap)
start_variation -= AUTO_RES_ERROR_CAPTURE_RES;
}
- dev_dbg(&hap->pdev->dev,
- "lra_drive_period_code_lo = 0x%x lra_drive_period_code_hi = 0x%x\n"
+ pr_debug("lra_drive_period_code_lo = 0x%x lra_drive_period_code_hi = 0x%x\n"
"lra_drive_period_code = 0x%x, lra_drive_frequency_hz = 0x%x\n"
"Calculated play rate code values are :\n",
lra_drive_period_code_lo, lra_drive_period_code_hi,
lra_drive_period_code, lra_drive_frequency_hz);
for (i = 0; i < ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE; ++i)
- dev_dbg(&hap->pdev->dev,
- " 0x%x", adjusted_lra_play_rate_code[i]);
+ pr_debug(" 0x%x", adjusted_lra_play_rate_code[i]);
return 0;
}
@@ -1450,25 +1481,18 @@ static int calculate_lra_code(struct qpnp_hap *hap)
static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
{
int rc = 0;
- u8 val = 0;
- u16 addr;
-
-
- if (hap->pmic_subtype == PM660_SUBTYPE) {
- addr = QPNP_HAP_AUTO_RES_CTRL(hap->base);
- } else {
- addr = QPNP_HAP_TEST2_REG(hap->base);
- /* TEST2 is a secure access register */
- rc = qpnp_hap_sec_access(hap);
- if (rc)
- return rc;
- }
-
- if (enable)
- val |= AUTO_RES_ENABLE;
+ u8 val;
- rc = qpnp_hap_masked_write_reg(hap, val, addr, AUTO_RES_ENABLE);
- if (rc)
+ val = enable ? AUTO_RES_ENABLE : 0;
+ if (hap->pmic_subtype == PM660_SUBTYPE)
+ rc = qpnp_hap_masked_write_reg(hap,
+ QPNP_HAP_AUTO_RES_CTRL(hap->base),
+ QPNP_HAP_AUTO_RES_MASK, val);
+ else
+ rc = qpnp_hap_sec_masked_write_reg(hap,
+ QPNP_HAP_TEST2_REG(hap->base),
+ QPNP_HAP_AUTO_RES_MASK, val);
+ if (rc < 0)
return rc;
if (enable)
@@ -1476,7 +1500,7 @@ static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
else
hap->status_flags &= ~AUTO_RESONANCE_ENABLED;
- return 0;
+ return rc;
}
static void update_lra_frequency(struct qpnp_hap *hap)
@@ -1485,19 +1509,18 @@ static void update_lra_frequency(struct qpnp_hap *hap)
u32 play_rate_code;
int rc;
- qpnp_hap_read_reg(hap, &lra_auto_res_lo,
- QPNP_HAP_LRA_AUTO_RES_LO(hap->base));
- qpnp_hap_read_reg(hap, &lra_auto_res_hi,
- QPNP_HAP_LRA_AUTO_RES_HI(hap->base));
+ qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_LO(hap->base),
+ &lra_auto_res_lo);
+ qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_HI(hap->base),
+ &lra_auto_res_hi);
play_rate_code =
(lra_auto_res_hi & 0xF0) << 4 | (lra_auto_res_lo & 0xFF);
- dev_dbg(&hap->pdev->dev,
- "lra_auto_res_lo = 0x%x lra_auto_res_hi = 0x%x play_rate_code = 0x%x\n",
+ pr_debug("lra_auto_res_lo = 0x%x lra_auto_res_hi = 0x%x play_rate_code = 0x%x\n",
lra_auto_res_lo, lra_auto_res_hi, play_rate_code);
- rc = qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base));
+ rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val);
if (rc < 0)
return;
@@ -1511,22 +1534,25 @@ static void update_lra_frequency(struct qpnp_hap *hap)
if ((val & AUTO_RES_ERR_BIT) ||
((play_rate_code <= hap->drive_period_code_min_limit) ||
(play_rate_code >= hap->drive_period_code_max_limit))) {
- dev_dbg(&hap->pdev->dev,
- "Auto-resonance error, out of 25%%, [min: 0x%x, max: 0x%x]\n",
- hap->drive_period_code_min_limit,
- hap->drive_period_code_max_limit);
+ if (val & AUTO_RES_ERR_BIT)
+ pr_debug("Auto-resonance error %x\n", val);
+ else
+ pr_debug("play rate %x out of bounds [min: 0x%x, max: 0x%x]\n",
+ play_rate_code,
+ hap->drive_period_code_min_limit,
+ hap->drive_period_code_max_limit);
rc = qpnp_hap_auto_res_enable(hap, 0);
if (rc < 0)
- dev_dbg(&hap->pdev->dev, "Auto-resonance write failed\n");
+ pr_debug("Auto-resonance write failed\n");
return;
}
- qpnp_hap_write_reg(hap, &lra_auto_res_lo,
- QPNP_HAP_RATE_CFG1_REG(hap->base));
+ qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base),
+ lra_auto_res_lo);
lra_auto_res_hi = lra_auto_res_hi >> 4;
- qpnp_hap_write_reg(hap, &lra_auto_res_hi,
- QPNP_HAP_RATE_CFG2_REG(hap->base));
+ qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base),
+ lra_auto_res_hi);
}
static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer)
@@ -1535,7 +1561,6 @@ static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer)
auto_res_err_poll_timer);
ktime_t currtime;
-
if (!(hap->status_flags & AUTO_RESONANCE_ENABLED))
return HRTIMER_NORESTART;
@@ -1680,7 +1705,7 @@ int qpnp_hap_play_byte(u8 data, bool on)
}
if (hap->play_mode != QPNP_HAP_PWM) {
- dev_err(&hap->pdev->dev, "only PWM mode is supported\n");
+ pr_err("only PWM mode is supported\n");
return -EINVAL;
}
@@ -1721,8 +1746,7 @@ int qpnp_hap_play_byte(u8 data, bool on)
if (rc)
return rc;
- dev_dbg(&hap->pdev->dev, "data=0x%x duty_per=%d\n", data,
- duty_percent);
+ pr_debug("data=0x%x duty_per=%d\n", data, duty_percent);
rc = qpnp_hap_set(hap, true);
@@ -1741,8 +1765,8 @@ static void qpnp_hap_worker(struct work_struct *work)
if (hap->vcc_pon && hap->state && !hap->vcc_pon_enabled) {
rc = regulator_enable(hap->vcc_pon);
if (rc < 0)
- pr_err("%s: could not enable vcc_pon regulator rc=%d\n",
- __func__, rc);
+ pr_err("could not enable vcc_pon regulator rc=%d\n",
+ rc);
else
hap->vcc_pon_enabled = true;
}
@@ -1751,8 +1775,8 @@ static void qpnp_hap_worker(struct work_struct *work)
* exceeds the maximum limit (5 secs).
*/
if (hap->sc_duration == SC_MAX_DURATION) {
- rc = qpnp_hap_write_reg(hap, &val,
- QPNP_HAP_EN_CTL_REG(hap->base));
+ rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base),
+ val);
} else {
if (hap->play_mode == QPNP_HAP_PWM)
qpnp_hap_mod_enable(hap, hap->state);
@@ -1762,8 +1786,8 @@ static void qpnp_hap_worker(struct work_struct *work)
if (hap->vcc_pon && !hap->state && hap->vcc_pon_enabled) {
rc = regulator_disable(hap->vcc_pon);
if (rc)
- pr_err("%s: could not disable vcc_pon regulator rc=%d\n",
- __func__, rc);
+ pr_err("could not disable vcc_pon regulator rc=%d\n",
+ rc);
else
hap->vcc_pon_enabled = false;
}
@@ -1827,99 +1851,25 @@ static SIMPLE_DEV_PM_OPS(qpnp_haptic_pm_ops, qpnp_haptic_suspend, NULL);
/* Configuration api for haptics registers */
static int qpnp_hap_config(struct qpnp_hap *hap)
{
- u8 reg = 0, unlock_val, mask;
+ u8 val = 0;
u32 temp;
int rc, i;
- uint error_code = 0;
/*
* This denotes the percentage error in rc clock multiplied by 10
*/
u8 rc_clk_err_percent_x10;
- /* Configure the ACTUATOR TYPE register */
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_ACT_TYPE_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_ACT_TYPE_MASK;
- reg |= hap->act_type;
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_ACT_TYPE_REG(hap->base));
+ /* Configure the CFG1 register for actuator type */
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_CFG1_REG(hap->base),
+ QPNP_HAP_ACT_TYPE_MASK, hap->act_type);
if (rc)
return rc;
/* Configure auto resonance parameters */
- if (hap->act_type == QPNP_HAP_LRA) {
- if (hap->pmic_subtype == PM660_SUBTYPE) {
- if (hap->lra_res_cal_period <
- QPNP_HAP_PM660_RES_CAL_PERIOD_MIN)
- hap->lra_res_cal_period =
- QPNP_HAP_PM660_RES_CAL_PERIOD_MIN;
- else if (hap->lra_res_cal_period >
- QPNP_HAP_PM660_RES_CAL_PERIOD_MAX)
- hap->lra_res_cal_period =
- QPNP_HAP_PM660_RES_CAL_PERIOD_MAX;
- } else if (hap->pmic_subtype != PM660_SUBTYPE) {
- if (hap->lra_res_cal_period <
- QPNP_HAP_RES_CAL_PERIOD_MIN)
- hap->lra_res_cal_period =
- QPNP_HAP_RES_CAL_PERIOD_MIN;
- else if (hap->lra_res_cal_period >
- QPNP_HAP_RES_CAL_PERIOD_MAX)
- hap->lra_res_cal_period =
- QPNP_HAP_RES_CAL_PERIOD_MAX;
- }
- if (hap->pmic_subtype == PM660_SUBTYPE &&
- hap->auto_res_mode == QPNP_HAP_PM660_AUTO_RES_QWD) {
- hap->lra_res_cal_period = 0;
- }
-
- reg = mask = 0;
- if (hap->pmic_subtype == PM660_SUBTYPE) {
- reg |= hap->auto_res_mode <<
- QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT;
- mask = QPNP_HAP_PM660_AUTO_RES_MODE_BIT;
- reg |= hap->lra_high_z <<
- QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT;
- mask |= QPNP_HAP_PM660_CALIBRATE_DURATION_MASK;
- if (hap->lra_qwd_drive_duration != -EINVAL) {
- reg |= hap->lra_qwd_drive_duration <<
- QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT;
- mask |= QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT;
- }
- if (hap->calibrate_at_eop != -EINVAL) {
- reg |= hap->calibrate_at_eop <<
- QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT;
- mask |= QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT;
- }
- if (hap->lra_res_cal_period) {
- temp = fls(hap->lra_res_cal_period) - 1;
- reg |= (temp - 1);
- }
- mask |= QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK;
- } else {
- reg |= (hap->auto_res_mode <<
- QPNP_HAP_AUTO_RES_MODE_SHIFT);
- mask = QPNP_HAP_AUTO_RES_MODE_MASK;
- reg |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT);
- mask |= QPNP_HAP_LRA_HIGH_Z_MASK;
- temp = fls(hap->lra_res_cal_period) - 1;
- reg |= (temp - 2);
- mask |= QPNP_HAP_LRA_RES_CAL_PER_MASK;
- }
- rc = qpnp_hap_masked_write_reg(hap, reg,
- QPNP_HAP_LRA_AUTO_RES_REG(hap->base),
- mask);
- if (rc)
- return rc;
- } else {
- /* disable auto resonance for ERM */
- reg = 0x00;
-
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_LRA_AUTO_RES_REG(hap->base));
- if (rc)
- return rc;
- }
+ rc = qpnp_hap_lra_auto_res_config(hap);
+ if (rc)
+ return rc;
/* Configure the PLAY MODE register */
rc = qpnp_hap_play_mode_config(hap);
@@ -1932,18 +1882,7 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
return rc;
/* Configure the ILIM register */
- if (hap->ilim_ma < QPNP_HAP_ILIM_MIN_MA)
- hap->ilim_ma = QPNP_HAP_ILIM_MIN_MA;
- else if (hap->ilim_ma > QPNP_HAP_ILIM_MAX_MA)
- hap->ilim_ma = QPNP_HAP_ILIM_MAX_MA;
-
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_ILIM_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_ILIM_MASK;
- temp = (hap->ilim_ma / QPNP_HAP_ILIM_MIN_MA) >> 1;
- reg |= temp;
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_ILIM_REG(hap->base));
+ rc = qpnp_hap_ilim_config(hap);
if (rc)
return rc;
@@ -1953,52 +1892,13 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
return rc;
/* Configure the INTERNAL_PWM register */
- if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) {
- if (hap->pmic_subtype == PM660_SUBTYPE) {
- hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ;
- temp = 1;
- } else {
- hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_253_KHZ;
- temp = 0;
- }
- } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_505_KHZ) {
- hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ;
- temp = 1;
- } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_739_KHZ) {
- hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_739_KHZ;
- temp = 2;
- } else {
- hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_1076_KHZ;
- temp = 3;
- }
-
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_INT_PWM_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_INT_PWM_MASK;
- reg |= temp;
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_INT_PWM_REG(hap->base));
- if (rc)
- return rc;
-
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_PWM_CAP_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_INT_PWM_MASK;
- reg |= temp;
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_PWM_CAP_REG(hap->base));
+ rc = qpnp_hap_int_pwm_config(hap);
if (rc)
return rc;
/* Configure the WAVE SHAPE register */
- rc = qpnp_hap_read_reg(hap, &reg,
- QPNP_HAP_WAV_SHAPE_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_WAV_SHAPE_MASK;
- reg |= hap->wave_shape;
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_WAV_SHAPE_REG(hap->base));
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_CFG2_REG(hap->base),
+ QPNP_HAP_WAV_SHAPE_MASK, hap->wave_shape);
if (rc)
return rc;
@@ -2019,35 +1919,24 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
* The frequency of 19.2Mzhz RC clock is subject to variation. Currently
* a few PMI modules have MISC_TRIM_ERROR_RC19P2_CLK register
* present in their MISC block. This register holds the frequency error
- * in 19.2Mhz RC clock.
+ * in 19.2 MHz RC clock.
*/
if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq
- && hap->misc_trim_error_rc19p2_clk_reg_present) {
- unlock_val = MISC_SEC_UNLOCK;
- /*
- * This SID value may change depending on the PMI chip where
- * the MISC block is present.
- */
- rc = regmap_write(hap->regmap, MISC_SEC_ACCESS, unlock_val);
- if (rc)
- dev_err(&hap->pdev->dev,
- "Unable to do SEC_ACCESS rc:%d\n", rc);
-
- regmap_read(hap->regmap, MISC_TRIM_ERROR_RC19P2_CLK,
- &error_code);
- dev_dbg(&hap->pdev->dev, "TRIM register = 0x%x\n", error_code);
+ && hap->misc_clk_trim_error_reg) {
+ pr_debug("TRIM register = 0x%x\n", hap->clk_trim_error_code);
/*
* Extract the 4 LSBs and multiply by 7 to get
* the %error in RC clock multiplied by 10
*/
- rc_clk_err_percent_x10 = (error_code & 0x0F) * 7;
+ rc_clk_err_percent_x10 = (hap->clk_trim_error_code & 0x0F) * 7;
/*
* If the TRIM register holds value less than 0x80,
* then there is a positive error in the RC clock.
* If the TRIM register holds value greater than or equal to
- * 0x80, then there is a negative error in the RC clock.
+ * 0x80, then there is a negative error in the RC clock. Bit 7
+ * is the sign bit for error code.
*
* The adjusted play rate code is calculated as follows:
* LRA drive period code (RATE_CFG) =
@@ -2060,24 +1949,21 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
* Since 200KHz * 1/LRA drive frequency is already calculated
* above we only do rest of the scaling here.
*/
- if (error_code >= 128)
+ if (hap->clk_trim_error_code & BIT(7))
LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent_x10);
else
LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent_x10);
}
- dev_dbg(&hap->pdev->dev,
- "Play rate code 0x%x\n", hap->init_drive_period_code);
+ pr_debug("Play rate code 0x%x\n", hap->init_drive_period_code);
- reg = hap->init_drive_period_code & QPNP_HAP_RATE_CFG1_MASK;
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_RATE_CFG1_REG(hap->base));
+ val = hap->init_drive_period_code & QPNP_HAP_RATE_CFG1_MASK;
+ rc = qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), val);
if (rc)
return rc;
- reg = (hap->init_drive_period_code & 0xF00) >> QPNP_HAP_RATE_CFG2_SHFT;
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_RATE_CFG2_REG(hap->base));
+ val = (hap->init_drive_period_code & 0xF00) >> QPNP_HAP_RATE_CFG2_SHFT;
+ rc = qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base), val);
if (rc)
return rc;
@@ -2094,45 +1980,40 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
(hap->init_drive_period_code * (100 -
hap->drive_period_code_min_limit_percent_variation))
/ 100;
- dev_dbg(&hap->pdev->dev, "Drive period code max limit %x\n"
- "Drive period code min limit %x\n",
- hap->drive_period_code_max_limit,
- hap->drive_period_code_min_limit);
+ pr_debug("Drive period code max limit %x min limit %x\n",
+ hap->drive_period_code_max_limit,
+ hap->drive_period_code_min_limit);
}
/* Configure BRAKE register */
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_EN_CTL2_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_BRAKE_MASK;
- reg |= hap->en_brake;
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_EN_CTL2_REG(hap->base));
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EN_CTL2_REG(hap->base),
+ QPNP_HAP_BRAKE_MASK, (u8)hap->en_brake);
if (rc)
return rc;
if (hap->en_brake && hap->sup_brake_pat) {
- for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, reg = 0; i >= 0; i--) {
+ for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, val = 0; i >= 0; i--) {
hap->brake_pat[i] &= QPNP_HAP_BRAKE_PAT_MASK;
temp = i << 1;
- reg |= hap->brake_pat[i] << temp;
+ val |= hap->brake_pat[i] << temp;
}
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_BRAKE_REG(hap->base));
+ rc = qpnp_hap_write_reg(hap, QPNP_HAP_BRAKE_REG(hap->base),
+ val);
if (rc)
return rc;
}
/* Cache enable control register */
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_EN_CTL_REG(hap->base));
+ rc = qpnp_hap_read_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), &val);
if (rc < 0)
return rc;
- hap->reg_en_ctl = reg;
+ hap->reg_en_ctl = val;
/* Cache play register */
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_PLAY_REG(hap->base));
+ rc = qpnp_hap_read_reg(hap, QPNP_HAP_PLAY_REG(hap->base), &val);
if (rc < 0)
return rc;
- hap->reg_play = reg;
+ hap->reg_play = val;
if (hap->play_mode == QPNP_HAP_BUFFER)
rc = qpnp_hap_buffer_config(hap);
@@ -2144,15 +2025,29 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
if (rc)
return rc;
+ /* setup play irq */
+ if (hap->play_irq >= 0) {
+ rc = devm_request_threaded_irq(&hap->pdev->dev, hap->play_irq,
+ NULL, qpnp_hap_play_irq, IRQF_ONESHOT, "qpnp_hap_play",
+ hap);
+ if (rc < 0) {
+ pr_err("Unable to request play(%d) IRQ(err:%d)\n",
+ hap->play_irq, rc);
+ return rc;
+ }
+
+ /* use play_irq only for buffer mode */
+ if (hap->play_mode != QPNP_HAP_BUFFER)
+ disable_irq(hap->play_irq);
+ }
+
/* setup short circuit irq */
- if (hap->use_sc_irq) {
+ if (hap->sc_irq >= 0) {
rc = devm_request_threaded_irq(&hap->pdev->dev, hap->sc_irq,
- NULL, qpnp_hap_sc_irq,
- QPNP_IRQ_FLAGS,
- "qpnp_sc_irq", hap);
+ NULL, qpnp_hap_sc_irq, IRQF_ONESHOT, "qpnp_hap_sc",
+ hap);
if (rc < 0) {
- dev_err(&hap->pdev->dev,
- "Unable to request sc(%d) IRQ(err:%d)\n",
+ pr_err("Unable to request sc(%d) IRQ(err:%d)\n",
hap->sc_irq, rc);
return rc;
}
@@ -2167,18 +2062,46 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
{
struct platform_device *pdev = hap->pdev;
+ struct device_node *misc_node;
struct property *prop;
const char *temp_str;
u32 temp;
int rc;
+ if (of_find_property(pdev->dev.of_node, "qcom,pmic-misc", NULL)) {
+ misc_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,pmic-misc", 0);
+ if (!misc_node)
+ return -EINVAL;
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,misc-clk-trim-error-reg", &temp);
+ if (rc < 0) {
+ pr_err("Missing misc-clk-trim-error-reg\n");
+ return rc;
+ }
+
+ if (!temp || temp > 0xFF) {
+ pr_err("Invalid misc-clk-trim-error-reg\n");
+ return -EINVAL;
+ }
+
+ hap->misc_clk_trim_error_reg = temp;
+ rc = qpnp_misc_read_reg(misc_node, hap->misc_clk_trim_error_reg,
+ &hap->clk_trim_error_code);
+ if (rc < 0) {
+ pr_err("Couldn't get clk_trim_error_code, rc=%d\n", rc);
+ return -EPROBE_DEFER;
+ }
+ }
+
hap->timeout_ms = QPNP_HAP_TIMEOUT_MS_MAX;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,timeout-ms", &temp);
if (!rc) {
hap->timeout_ms = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read timeout\n");
+ pr_err("Unable to read timeout\n");
return rc;
}
@@ -2191,11 +2114,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
else if (strcmp(temp_str, "lra") == 0)
hap->act_type = QPNP_HAP_LRA;
else {
- dev_err(&pdev->dev, "Invalid actuator type\n");
+ pr_err("Invalid actuator type\n");
return -EINVAL;
}
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read actuator type\n");
+ pr_err("Unable to read actuator type\n");
return rc;
}
@@ -2231,7 +2154,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
QPNP_HAP_AUTO_RES_ZXD_EOP;
}
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read auto res mode\n");
+ pr_err("Unable to read auto res mode\n");
return rc;
}
@@ -2253,7 +2176,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
else
hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read LRA high-z\n");
+ pr_err("Unable to read LRA high-z\n");
return rc;
}
@@ -2272,7 +2195,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
if (!rc) {
hap->lra_res_cal_period = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read cal period\n");
+ pr_err("Unable to read cal period\n");
return rc;
}
@@ -2298,10 +2221,6 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
hap->drive_period_code_min_limit_percent_variation =
(u8) temp;
- hap->misc_trim_error_rc19p2_clk_reg_present =
- of_property_read_bool(pdev->dev.of_node,
- "qcom,misc-trim-error-rc19p2-clk-reg-present");
-
if (hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) {
hap->time_required_to_generate_back_emf_us =
QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN;
@@ -2328,11 +2247,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
else if (strcmp(temp_str, "audio") == 0)
hap->play_mode = QPNP_HAP_AUDIO;
else {
- dev_err(&pdev->dev, "Invalid play mode\n");
+ pr_err("Invalid play mode\n");
return -EINVAL;
}
} else {
- dev_err(&pdev->dev, "Unable to read play mode\n");
+ pr_err("Unable to read play mode\n");
return rc;
}
@@ -2341,7 +2260,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
if (!rc) {
hap->vmax_mv = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read vmax\n");
+ pr_err("Unable to read vmax\n");
return rc;
}
@@ -2350,7 +2269,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
if (!rc) {
hap->ilim_ma = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read ILim\n");
+ pr_err("Unable to read ILim\n");
return rc;
}
@@ -2360,7 +2279,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
if (!rc) {
hap->sc_deb_cycles = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read sc debounce\n");
+ pr_err("Unable to read sc debounce\n");
return rc;
}
@@ -2370,7 +2289,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
if (!rc) {
hap->int_pwm_freq_khz = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read int pwm freq\n");
+ pr_err("Unable to read int pwm freq\n");
return rc;
}
@@ -2383,11 +2302,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
else if (strcmp(temp_str, "square") == 0)
hap->wave_shape = QPNP_HAP_WAV_SQUARE;
else {
- dev_err(&pdev->dev, "Unsupported wav shape\n");
+ pr_err("Unsupported wav shape\n");
return -EINVAL;
}
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read wav shape\n");
+ pr_err("Unable to read wav shape\n");
return rc;
}
@@ -2397,7 +2316,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
if (!rc) {
hap->wave_play_rate_us = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read play rate\n");
+ pr_err("Unable to read play rate\n");
return rc;
}
@@ -2416,9 +2335,9 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
prop = of_find_property(pdev->dev.of_node,
"qcom,brake-pattern", &temp);
if (!prop) {
- dev_info(&pdev->dev, "brake pattern not found");
+ pr_info("brake pattern not found");
} else if (temp != QPNP_HAP_BRAKE_PAT_LEN) {
- dev_err(&pdev->dev, "Invalid len of brake pattern\n");
+ pr_err("Invalid len of brake pattern\n");
return -EINVAL;
} else {
hap->sup_brake_pat = true;
@@ -2427,14 +2346,14 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
}
}
- hap->use_sc_irq = of_property_read_bool(pdev->dev.of_node,
- "qcom,use-sc-irq");
- if (hap->use_sc_irq) {
- hap->sc_irq = platform_get_irq_byname(hap->pdev, "sc-irq");
- if (hap->sc_irq < 0) {
- dev_err(&pdev->dev, "Unable to get sc irq\n");
- return hap->sc_irq;
- }
+ hap->play_irq = platform_get_irq_byname(hap->pdev, "play-irq");
+ if (hap->play_irq < 0)
+ pr_warn("Unable to get play irq\n");
+
+ hap->sc_irq = platform_get_irq_byname(hap->pdev, "sc-irq");
+ if (hap->sc_irq < 0) {
+ pr_err("Unable to get sc irq\n");
+ return hap->sc_irq;
}
if (of_find_property(pdev->dev.of_node, "vcc_pon-supply", NULL))
@@ -2483,7 +2402,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev)
return -ENOMEM;
hap->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!hap->regmap) {
- dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+ pr_err("Couldn't get parent's regmap\n");
return -EINVAL;
}
@@ -2491,8 +2410,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev)
rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
if (rc < 0) {
- dev_err(&pdev->dev,
- "Couldn't find reg in node = %s rc = %d\n",
+ pr_err("Couldn't find reg in node = %s rc = %d\n",
pdev->dev.of_node->full_name, rc);
return rc;
}
@@ -2508,13 +2426,14 @@ static int qpnp_haptic_probe(struct platform_device *pdev)
rc = qpnp_hap_parse_dt(hap);
if (rc) {
- dev_err(&pdev->dev, "DT parsing failed\n");
+ pr_err("DT parsing failed\n");
return rc;
}
+ spin_lock_init(&hap->bus_lock);
rc = qpnp_hap_config(hap);
if (rc) {
- dev_err(&pdev->dev, "hap config failed\n");
+ pr_err("hap config failed\n");
return rc;
}
@@ -2542,7 +2461,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev)
rc = timed_output_dev_register(&hap->timed_dev);
if (rc < 0) {
- dev_err(&pdev->dev, "timed_output registration failed\n");
+ pr_err("timed_output registration failed\n");
goto timed_output_fail;
}
@@ -2550,7 +2469,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev)
rc = sysfs_create_file(&hap->timed_dev.dev->kobj,
&qpnp_hap_attrs[i].attr);
if (rc < 0) {
- dev_err(&pdev->dev, "sysfs creation failed\n");
+ pr_err("sysfs creation failed\n");
goto sysfs_fail;
}
}
@@ -2559,8 +2478,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev)
vcc_pon = regulator_get(&pdev->dev, "vcc_pon");
if (IS_ERR(vcc_pon)) {
rc = PTR_ERR(vcc_pon);
- dev_err(&pdev->dev,
- "regulator get failed vcc_pon rc=%d\n", rc);
+ pr_err("regulator get failed vcc_pon rc=%d\n", rc);
goto sysfs_fail;
}
hap->vcc_pon = vcc_pon;
diff --git a/drivers/soc/qcom/rpm-smd.c b/drivers/soc/qcom/rpm-smd.c
index 20f406b9a2f7..f2784dedbc7a 100644
--- a/drivers/soc/qcom/rpm-smd.c
+++ b/drivers/soc/qcom/rpm-smd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -716,51 +716,6 @@ int msm_rpm_smd_buffer_request(struct msm_rpm_request *cdata,
return 0;
}
-static void msm_rpm_print_sleep_buffer(struct slp_buf *s)
-{
- char buf[DEBUG_PRINT_BUFFER_SIZE] = {0};
- int pos;
- int buflen = DEBUG_PRINT_BUFFER_SIZE;
- char ch[5] = {0};
- struct kvp *e;
- uint32_t type;
- unsigned int id;
-
- if (!s)
- return;
-
- if (!s->valid)
- return;
-
- type = get_rsc_type(s->buf);
- id = get_rsc_id(s->buf);
-
- memcpy(ch, &type, sizeof(u32));
-
- pos = scnprintf(buf, buflen,
- "Sleep request type = 0x%08x(%s)",
- type, ch);
- pos += scnprintf(buf + pos, buflen - pos, " id = 0%x",
- id);
- for_each_kvp(s->buf, e) {
- uint32_t i;
- char *data = get_data(e);
-
- memcpy(ch, &e->k, sizeof(u32));
-
- pos += scnprintf(buf + pos, buflen - pos,
- "\n\t\tkey = 0x%08x(%s)",
- e->k, ch);
- pos += scnprintf(buf + pos, buflen - pos,
- " sz= %d data =", e->s);
-
- for (i = 0; i < e->s; i++)
- pos += scnprintf(buf + pos, buflen - pos,
- " 0x%02X", data[i]);
- }
- pos += scnprintf(buf + pos, buflen - pos, "\n");
- printk(buf);
-}
static struct msm_rpm_driver_data msm_rpm_data = {
.smd_open = COMPLETION_INITIALIZER(msm_rpm_data.smd_open),
@@ -821,9 +776,6 @@ static int msm_rpm_flush_requests(bool print)
if (!s->valid)
continue;
- if (print)
- msm_rpm_print_sleep_buffer(s);
-
set_msg_id(s->buf, msm_rpm_get_next_msg_id());
if (!glink_enabled)
diff --git a/drivers/soc/qcom/secure_buffer.c b/drivers/soc/qcom/secure_buffer.c
index 90c7585d480c..4307937d9f6d 100644
--- a/drivers/soc/qcom/secure_buffer.c
+++ b/drivers/soc/qcom/secure_buffer.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2011 Google, Inc
- * Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -379,6 +379,7 @@ int hyp_assign_phys(phys_addr_t addr, u64 size, u32 *source_vm_list,
sg_free_table(&table);
return ret;
}
+EXPORT_SYMBOL(hyp_assign_phys);
const char *msm_secure_vmid_to_string(int secure_vmid)
{
diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c
index 8581ed587ead..5ac2a58899f4 100644
--- a/drivers/soc/qcom/service-locator.c
+++ b/drivers/soc/qcom/service-locator.c
@@ -266,10 +266,9 @@ static int service_locator_send_msg(struct pd_qmi_client_data *pd)
if (!domains_read) {
db_rev_count = pd->db_rev_count = resp->db_rev_count;
pd->total_domains = resp->total_domains;
- if (!pd->total_domains && resp->domain_list_len) {
- pr_err("total domains not set\n");
- pd->total_domains = resp->domain_list_len;
- }
+ if (!resp->total_domains)
+ pr_info("No matching domains found\n");
+
pd->domain_list = kmalloc(
sizeof(struct servreg_loc_entry_v01) *
resp->total_domains, GFP_KERNEL);
@@ -286,6 +285,10 @@ static int service_locator_send_msg(struct pd_qmi_client_data *pd)
rc = -EAGAIN;
goto out;
}
+ if (resp->domain_list_len > resp->total_domains) {
+ /* Always read total_domains from the response msg */
+ resp->domain_list_len = resp->total_domains;
+ }
/* Copy the response*/
store_get_domain_list_response(pd, resp, domains_read);
domains_read += resp->domain_list_len;
@@ -372,6 +375,7 @@ int get_service_location(char *client_name, char *service_name,
if (!pqw) {
rc = -ENOMEM;
pr_err("Allocation failed\n");
+ kfree(pqcd);
goto err;
}
pqw->notifier = locator_nb;
diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c
index ebea5b7726e4..fa916ac5ade4 100644
--- a/drivers/soc/qcom/service-notifier.c
+++ b/drivers/soc/qcom/service-notifier.c
@@ -99,11 +99,12 @@ struct ind_req_resp {
*/
struct qmi_client_info {
int instance_id;
- int subsys_state;
+ enum pd_subsys_state subsys_state;
struct work_struct svc_arrive;
struct work_struct svc_exit;
struct work_struct svc_rcv_msg;
struct work_struct ind_ack;
+ struct work_struct qmi_handle_free;
struct workqueue_struct *svc_event_wq;
struct qmi_handle *clnt_handle;
struct notifier_block notifier;
@@ -123,6 +124,18 @@ static void root_service_clnt_recv_msg(struct work_struct *work);
static void root_service_service_arrive(struct work_struct *work);
static void root_service_exit_work(struct work_struct *work);
+static void free_qmi_handle(struct work_struct *work)
+{
+ struct qmi_client_info *data = container_of(work,
+ struct qmi_client_info, qmi_handle_free);
+
+ mutex_lock(&qmi_client_release_lock);
+ data->service_connected = false;
+ qmi_handle_destroy(data->clnt_handle);
+ data->clnt_handle = NULL;
+ mutex_unlock(&qmi_client_release_lock);
+}
+
static struct service_notif_info *_find_service_info(const char *service_path)
{
struct service_notif_info *service_notif;
@@ -426,11 +439,7 @@ static void root_service_service_exit(struct qmi_client_info *data,
* Destroy client handle and try connecting when
* service comes up again.
*/
- mutex_lock(&qmi_client_release_lock);
- data->service_connected = false;
- qmi_handle_destroy(data->clnt_handle);
- data->clnt_handle = NULL;
- mutex_unlock(&qmi_client_release_lock);
+ queue_work(data->svc_event_wq, &data->qmi_handle_free);
}
static void root_service_exit_work(struct work_struct *work)
@@ -486,7 +495,7 @@ static int ssr_event_notify(struct notifier_block *this,
info->subsys_state = ROOT_PD_SHUTDOWN;
break;
}
- queue_work(info->svc_event_wq, &info->svc_exit);
+ root_service_service_exit(info, info->subsys_state);
break;
default:
break;
@@ -561,6 +570,7 @@ static void *add_service_notif(const char *service_path, int instance_id,
INIT_WORK(&qmi_data->svc_exit, root_service_exit_work);
INIT_WORK(&qmi_data->svc_rcv_msg, root_service_clnt_recv_msg);
INIT_WORK(&qmi_data->ind_ack, send_ind_ack);
+ INIT_WORK(&qmi_data->qmi_handle_free, free_qmi_handle);
*curr_state = service_notif->curr_state =
SERVREG_NOTIF_SERVICE_STATE_UNINIT_V01;
diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c
index 07610877f140..457361ba5ff8 100644
--- a/drivers/soc/qcom/spcom.c
+++ b/drivers/soc/qcom/spcom.c
@@ -2241,7 +2241,7 @@ static ssize_t spcom_device_write(struct file *filp,
}
/**
- * spcom_device_read() - handle channel file write() from user space.
+ * spcom_device_read() - handle channel file read() from user space.
*
* @filp: file pointer
*
@@ -2267,6 +2267,16 @@ static ssize_t spcom_device_read(struct file *filp, char __user *user_buff,
ch = filp->private_data;
+ if (ch == NULL) {
+ pr_err("invalid ch pointer, file [%s].\n", name);
+ return -EINVAL;
+ }
+
+ if (!spcom_is_channel_open(ch)) {
+ pr_err("ch is not open, file [%s].\n", name);
+ return -EINVAL;
+ }
+
buf = kzalloc(size, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
@@ -2354,6 +2364,10 @@ static unsigned int spcom_device_poll(struct file *filp,
done = (spcom_dev->link_state == GLINK_LINK_STATE_UP);
break;
case SPCOM_POLL_CH_CONNECT:
+ if (ch == NULL) {
+ pr_err("invalid ch pointer, file [%s].\n", name);
+ return -EINVAL;
+ }
pr_debug("ch [%s] SPCOM_POLL_CH_CONNECT.\n", name);
if (wait) {
reinit_completion(&ch->connect);
diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c
index c6531de48f65..991bce363740 100644
--- a/drivers/soc/qcom/subsys-pil-tz.c
+++ b/drivers/soc/qcom/subsys-pil-tz.c
@@ -1034,6 +1034,7 @@ static int pil_tz_driver_probe(struct platform_device *pdev)
d->desc.ops = &pil_ops_trusted;
d->desc.proxy_timeout = PROXY_TIMEOUT_MS;
+ d->desc.clear_fw_region = true;
rc = of_property_read_u32(pdev->dev.of_node, "qcom,proxy-timeout-ms",
&proxy_timeout);
diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c
index 63bc3961de3b..e72663bd2138 100644
--- a/drivers/soundwire/swr-wcd-ctrl.c
+++ b/drivers/soundwire/swr-wcd-ctrl.c
@@ -224,6 +224,12 @@ static struct dentry *debugfs_poke;
static struct dentry *debugfs_reg_dump;
static unsigned int read_data;
+
+static bool swrm_is_msm_variant(int val)
+{
+ return (val == SWRM_VERSION_1_3);
+}
+
static int swrm_debug_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
@@ -514,8 +520,17 @@ static int swrm_cmd_fifo_wr_cmd(struct swr_mstr_ctrl *swrm, u8 cmd_data,
__func__, val, ret);
goto err;
}
- if (cmd_id == 0xF)
- wait_for_completion_timeout(&swrm->broadcast, (2 * HZ/10));
+ if (cmd_id == 0xF) {
+ /*
+ * sleep for 10ms for MSM soundwire variant to allow broadcast
+ * command to complete.
+ */
+ if (swrm_is_msm_variant(swrm->version))
+ usleep_range(10000, 10100);
+ else
+ wait_for_completion_timeout(&swrm->broadcast,
+ (2 * HZ/10));
+ }
err:
return ret;
}
@@ -1472,6 +1487,7 @@ static int swrm_probe(struct platform_device *pdev)
mutex_unlock(&swrm->mlock);
goto err_mstr_fail;
}
+ swrm->version = swrm->read(swrm->handle, SWRM_COMP_HW_VERSION);
/* Enumerate slave devices */
list_for_each_entry_safe(swr_dev, safe, &swrm->master.devices,
diff --git a/drivers/soundwire/swr-wcd-ctrl.h b/drivers/soundwire/swr-wcd-ctrl.h
index 8992318cdbd3..b7a3edac3e00 100755
--- a/drivers/soundwire/swr-wcd-ctrl.h
+++ b/drivers/soundwire/swr-wcd-ctrl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -23,6 +23,10 @@
#define SWR_MSTR_PORT_LEN 8 /* Number of master ports */
+#define SWRM_VERSION_1_0 0x01010000
+#define SWRM_VERSION_1_2 0x01030000
+#define SWRM_VERSION_1_3 0x01040000
+
enum {
SWR_MSTR_PAUSE,
SWR_MSTR_RESUME,
@@ -88,6 +92,7 @@ struct swr_mstr_ctrl {
int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq,
void *data), void *swr_handle, int type);
int irq;
+ int version;
int num_enum_slaves;
int slave_status;
struct swr_mstr_port *mstr_port;
diff --git a/drivers/soundwire/swrm_registers.h b/drivers/soundwire/swrm_registers.h
index c6923f301f9f..50c3ecfdd47d 100755
--- a/drivers/soundwire/swrm_registers.h
+++ b/drivers/soundwire/swrm_registers.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -15,6 +15,7 @@
#define SWRM_BASE_ADDRESS 0x00
+#define SWRM_COMP_HW_VERSION SWRM_BASE_ADDRESS
#define SWRM_COMP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000004)
#define SWRM_COMP_CFG_RMSK 0x3
#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_BMSK 0x2
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index a87cfd4ba17b..61a86d391599 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -127,37 +127,62 @@ static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
tclk_hz = clk_get_rate(orion_spi->clk);
if (devdata->typ == ARMADA_SPI) {
- unsigned int clk, spr, sppr, sppr2, err;
- unsigned int best_spr, best_sppr, best_err;
-
- best_err = speed;
- best_spr = 0;
- best_sppr = 0;
-
- /* Iterate over the valid range looking for best fit */
- for (sppr = 0; sppr < 8; sppr++) {
- sppr2 = 0x1 << sppr;
-
- spr = tclk_hz / sppr2;
- spr = DIV_ROUND_UP(spr, speed);
- if ((spr == 0) || (spr > 15))
- continue;
-
- clk = tclk_hz / (spr * sppr2);
- err = speed - clk;
-
- if (err < best_err) {
- best_spr = spr;
- best_sppr = sppr;
- best_err = err;
- }
- }
+ /*
+ * Given the core_clk (tclk_hz) and the target rate (speed) we
+ * determine the best values for SPR (in [0 .. 15]) and SPPR (in
+ * [0..7]) such that
+ *
+ * core_clk / (SPR * 2 ** SPPR)
+ *
+ * is as big as possible but not bigger than speed.
+ */
- if ((best_sppr == 0) && (best_spr == 0))
- return -EINVAL;
+ /* best integer divider: */
+ unsigned divider = DIV_ROUND_UP(tclk_hz, speed);
+ unsigned spr, sppr;
+
+ if (divider < 16) {
+ /* This is the easy case, divider is less than 16 */
+ spr = divider;
+ sppr = 0;
+
+ } else {
+ unsigned two_pow_sppr;
+ /*
+ * Find the highest bit set in divider. This and the
+ * three next bits define SPR (apart from rounding).
+ * SPPR is then the number of zero bits that must be
+ * appended:
+ */
+ sppr = fls(divider) - 4;
+
+ /*
+ * As SPR only has 4 bits, we have to round divider up
+ * to the next multiple of 2 ** sppr.
+ */
+ two_pow_sppr = 1 << sppr;
+ divider = (divider + two_pow_sppr - 1) & -two_pow_sppr;
+
+ /*
+ * recalculate sppr as rounding up divider might have
+ * increased it enough to change the position of the
+ * highest set bit. In this case the bit that now
+ * doesn't make it into SPR is 0, so there is no need to
+ * round again.
+ */
+ sppr = fls(divider) - 4;
+ spr = divider >> sppr;
+
+ /*
+ * Now do range checking. SPR is constructed to have a
+ * width of 4 bits, so this is fine for sure. So we
+ * still need to check for sppr to fit into 3 bits:
+ */
+ if (sppr > 7)
+ return -EINVAL;
+ }
- prescale = ((best_sppr & 0x6) << 5) |
- ((best_sppr & 0x1) << 4) | best_spr;
+ prescale = ((sppr & 0x6) << 5) | ((sppr & 0x1) << 4) | spr;
} else {
/*
* the supported rates are: 4,6,8...30
diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
index 0f28c08fcb3c..77b551da5728 100644
--- a/drivers/ssb/pci.c
+++ b/drivers/ssb/pci.c
@@ -909,6 +909,7 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus,
if (err) {
ssb_warn("WARNING: Using fallback SPROM failed (err %d)\n",
err);
+ goto out_free;
} else {
ssb_dbg("Using SPROM revision %d provided by platform\n",
sprom->revision);
diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c
index b132cff14f01..15ff4e149d75 100644
--- a/drivers/staging/android/fiq_debugger/fiq_debugger.c
+++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c
@@ -30,6 +30,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp.h>
+#include <linux/sysrq.h>
#include <linux/timer.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
@@ -401,7 +402,7 @@ static void fiq_debugger_work(struct work_struct *work)
cmd += 6;
while (*cmd == ' ')
cmd++;
- if (cmd != '\0')
+ if ((cmd != '\0') && sysrq_on())
kernel_restart(cmd);
else
kernel_restart(NULL);
@@ -431,29 +432,39 @@ static void fiq_debugger_irq_exec(struct fiq_debugger_state *state, char *cmd)
static void fiq_debugger_help(struct fiq_debugger_state *state)
{
fiq_debugger_printf(&state->output,
- "FIQ Debugger commands:\n"
- " pc PC status\n"
- " regs Register dump\n"
- " allregs Extended Register dump\n"
- " bt Stack trace\n"
- " reboot [<c>] Reboot with command <c>\n"
- " reset [<c>] Hard reset with command <c>\n"
- " irqs Interupt status\n"
- " kmsg Kernel log\n"
- " version Kernel version\n");
- fiq_debugger_printf(&state->output,
- " sleep Allow sleep while in FIQ\n"
- " nosleep Disable sleep while in FIQ\n"
- " console Switch terminal to console\n"
- " cpu Current CPU\n"
- " cpu <number> Switch to CPU<number>\n");
+ "FIQ Debugger commands:\n");
+ if (sysrq_on()) {
+ fiq_debugger_printf(&state->output,
+ " pc PC status\n"
+ " regs Register dump\n"
+ " allregs Extended Register dump\n"
+ " bt Stack trace\n");
+ fiq_debugger_printf(&state->output,
+ " reboot [<c>] Reboot with command <c>\n"
+ " reset [<c>] Hard reset with command <c>\n"
+ " irqs Interrupt status\n"
+ " kmsg Kernel log\n"
+ " version Kernel version\n");
+ fiq_debugger_printf(&state->output,
+ " cpu Current CPU\n"
+ " cpu <number> Switch to CPU<number>\n"
+ " sysrq sysrq options\n"
+ " sysrq <param> Execute sysrq with <param>\n");
+ } else {
+ fiq_debugger_printf(&state->output,
+ " reboot Reboot\n"
+ " reset Hard reset\n"
+ " irqs Interrupt status\n");
+ }
fiq_debugger_printf(&state->output,
- " ps Process list\n"
- " sysrq sysrq options\n"
- " sysrq <param> Execute sysrq with <param>\n");
+ " sleep Allow sleep while in FIQ\n"
+ " nosleep Disable sleep while in FIQ\n"
+ " console Switch terminal to console\n"
+ " ps Process list\n");
#ifdef CONFIG_KGDB
- fiq_debugger_printf(&state->output,
- " kgdb Enter kernel debugger\n");
+ if (fiq_kgdb_enable) {
+ fiq_debugger_printf(&state->output,
+ " kgdb Enter kernel debugger\n");
#endif
}
@@ -485,18 +496,23 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state,
if (!strcmp(cmd, "help") || !strcmp(cmd, "?")) {
fiq_debugger_help(state);
} else if (!strcmp(cmd, "pc")) {
- fiq_debugger_dump_pc(&state->output, regs);
+ if (sysrq_on())
+ fiq_debugger_dump_pc(&state->output, regs);
} else if (!strcmp(cmd, "regs")) {
- fiq_debugger_dump_regs(&state->output, regs);
+ if (sysrq_on())
+ fiq_debugger_dump_regs(&state->output, regs);
} else if (!strcmp(cmd, "allregs")) {
- fiq_debugger_dump_allregs(&state->output, regs);
+ if (sysrq_on())
+ fiq_debugger_dump_allregs(&state->output, regs);
} else if (!strcmp(cmd, "bt")) {
- fiq_debugger_dump_stacktrace(&state->output, regs, 100, svc_sp);
+ if (sysrq_on())
+ fiq_debugger_dump_stacktrace(&state->output, regs,
+ 100, svc_sp);
} else if (!strncmp(cmd, "reset", 5)) {
cmd += 5;
while (*cmd == ' ')
cmd++;
- if (*cmd) {
+ if (*cmd && sysrq_on()) {
char tmp_cmd[32];
strlcpy(tmp_cmd, cmd, sizeof(tmp_cmd));
machine_restart(tmp_cmd);
@@ -506,9 +522,12 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state,
} else if (!strcmp(cmd, "irqs")) {
fiq_debugger_dump_irqs(state);
} else if (!strcmp(cmd, "kmsg")) {
- fiq_debugger_dump_kernel_log(state);
+ if (sysrq_on())
+ fiq_debugger_dump_kernel_log(state);
} else if (!strcmp(cmd, "version")) {
- fiq_debugger_printf(&state->output, "%s\n", linux_banner);
+ if (sysrq_on())
+ fiq_debugger_printf(&state->output, "%s\n",
+ linux_banner);
} else if (!strcmp(cmd, "sleep")) {
state->no_sleep = false;
fiq_debugger_printf(&state->output, "enabling sleep\n");
@@ -520,14 +539,17 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state,
fiq_debugger_uart_flush(state);
state->console_enable = true;
} else if (!strcmp(cmd, "cpu")) {
- fiq_debugger_printf(&state->output, "cpu %d\n", state->current_cpu);
- } else if (!strncmp(cmd, "cpu ", 4)) {
+ if (sysrq_on())
+ fiq_debugger_printf(&state->output, "cpu %d\n",
+ state->current_cpu);
+ } else if (!strncmp(cmd, "cpu ", 4) && sysrq_on()) {
unsigned long cpu = 0;
if (kstrtoul(cmd + 4, 10, &cpu) == 0)
fiq_debugger_switch_cpu(state, cpu);
else
fiq_debugger_printf(&state->output, "invalid cpu\n");
- fiq_debugger_printf(&state->output, "cpu %d\n", state->current_cpu);
+ fiq_debugger_printf(&state->output, "cpu %d\n",
+ state->current_cpu);
} else {
if (state->debug_busy) {
fiq_debugger_printf(&state->output,
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
index 81bda878a7ec..b7fe42582e89 100644
--- a/drivers/staging/android/ion/ion.c
+++ b/drivers/staging/android/ion/ion.c
@@ -807,7 +807,7 @@ static int ion_debug_client_show(struct seq_file *s, void *unused)
struct ion_handle *handle = rb_entry(n, struct ion_handle,
node);
- seq_printf(s, "%16.16s: %16zx : %16d : %12p",
+ seq_printf(s, "%16.16s: %16zx : %16d : %12pK",
handle->buffer->heap->name,
handle->buffer->size,
atomic_read(&handle->ref.refcount),
diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c
index 03b2b8a38991..2ad4cc7a4785 100644
--- a/drivers/staging/android/ion/ion_system_heap.c
+++ b/drivers/staging/android/ion/ion_system_heap.c
@@ -2,7 +2,7 @@
* drivers/staging/android/ion/ion_system_heap.c
*
* Copyright (C) 2011 Google, Inc.
- * Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -123,9 +123,11 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap,
gfp_t gfp_mask = low_order_gfp_flags;
if (order)
gfp_mask = high_order_gfp_flags;
+
page = alloc_pages(gfp_mask, order);
- ion_pages_sync_for_device(dev, page, PAGE_SIZE << order,
- DMA_BIDIRECTIONAL);
+ if (page)
+ ion_pages_sync_for_device(dev, page, PAGE_SIZE << order,
+ DMA_BIDIRECTIONAL);
}
if (!page)
return 0;
diff --git a/drivers/staging/comedi/drivers/dt282x.c b/drivers/staging/comedi/drivers/dt282x.c
index 5a536a00066f..b63472de761a 100644
--- a/drivers/staging/comedi/drivers/dt282x.c
+++ b/drivers/staging/comedi/drivers/dt282x.c
@@ -69,48 +69,49 @@
* Register map
*/
#define DT2821_ADCSR_REG 0x00
-#define DT2821_ADCSR_ADERR (1 << 15)
-#define DT2821_ADCSR_ADCLK (1 << 9)
-#define DT2821_ADCSR_MUXBUSY (1 << 8)
-#define DT2821_ADCSR_ADDONE (1 << 7)
-#define DT2821_ADCSR_IADDONE (1 << 6)
+#define DT2821_ADCSR_ADERR BIT(15)
+#define DT2821_ADCSR_ADCLK BIT(9)
+#define DT2821_ADCSR_MUXBUSY BIT(8)
+#define DT2821_ADCSR_ADDONE BIT(7)
+#define DT2821_ADCSR_IADDONE BIT(6)
#define DT2821_ADCSR_GS(x) (((x) & 0x3) << 4)
#define DT2821_ADCSR_CHAN(x) (((x) & 0xf) << 0)
#define DT2821_CHANCSR_REG 0x02
-#define DT2821_CHANCSR_LLE (1 << 15)
-#define DT2821_CHANCSR_PRESLA(x) (((x) & 0xf) >> 8)
+#define DT2821_CHANCSR_LLE BIT(15)
+#define DT2821_CHANCSR_TO_PRESLA(x) (((x) >> 8) & 0xf)
#define DT2821_CHANCSR_NUMB(x) ((((x) - 1) & 0xf) << 0)
#define DT2821_ADDAT_REG 0x04
#define DT2821_DACSR_REG 0x06
-#define DT2821_DACSR_DAERR (1 << 15)
+#define DT2821_DACSR_DAERR BIT(15)
#define DT2821_DACSR_YSEL(x) ((x) << 9)
-#define DT2821_DACSR_SSEL (1 << 8)
-#define DT2821_DACSR_DACRDY (1 << 7)
-#define DT2821_DACSR_IDARDY (1 << 6)
-#define DT2821_DACSR_DACLK (1 << 5)
-#define DT2821_DACSR_HBOE (1 << 1)
-#define DT2821_DACSR_LBOE (1 << 0)
+#define DT2821_DACSR_SSEL BIT(8)
+#define DT2821_DACSR_DACRDY BIT(7)
+#define DT2821_DACSR_IDARDY BIT(6)
+#define DT2821_DACSR_DACLK BIT(5)
+#define DT2821_DACSR_HBOE BIT(1)
+#define DT2821_DACSR_LBOE BIT(0)
#define DT2821_DADAT_REG 0x08
#define DT2821_DIODAT_REG 0x0a
#define DT2821_SUPCSR_REG 0x0c
-#define DT2821_SUPCSR_DMAD (1 << 15)
-#define DT2821_SUPCSR_ERRINTEN (1 << 14)
-#define DT2821_SUPCSR_CLRDMADNE (1 << 13)
-#define DT2821_SUPCSR_DDMA (1 << 12)
-#define DT2821_SUPCSR_DS_PIO (0 << 10)
-#define DT2821_SUPCSR_DS_AD_CLK (1 << 10)
-#define DT2821_SUPCSR_DS_DA_CLK (2 << 10)
-#define DT2821_SUPCSR_DS_AD_TRIG (3 << 10)
-#define DT2821_SUPCSR_BUFFB (1 << 9)
-#define DT2821_SUPCSR_SCDN (1 << 8)
-#define DT2821_SUPCSR_DACON (1 << 7)
-#define DT2821_SUPCSR_ADCINIT (1 << 6)
-#define DT2821_SUPCSR_DACINIT (1 << 5)
-#define DT2821_SUPCSR_PRLD (1 << 4)
-#define DT2821_SUPCSR_STRIG (1 << 3)
-#define DT2821_SUPCSR_XTRIG (1 << 2)
-#define DT2821_SUPCSR_XCLK (1 << 1)
-#define DT2821_SUPCSR_BDINIT (1 << 0)
+#define DT2821_SUPCSR_DMAD BIT(15)
+#define DT2821_SUPCSR_ERRINTEN BIT(14)
+#define DT2821_SUPCSR_CLRDMADNE BIT(13)
+#define DT2821_SUPCSR_DDMA BIT(12)
+#define DT2821_SUPCSR_DS(x) (((x) & 0x3) << 10)
+#define DT2821_SUPCSR_DS_PIO DT2821_SUPCSR_DS(0)
+#define DT2821_SUPCSR_DS_AD_CLK DT2821_SUPCSR_DS(1)
+#define DT2821_SUPCSR_DS_DA_CLK DT2821_SUPCSR_DS(2)
+#define DT2821_SUPCSR_DS_AD_TRIG DT2821_SUPCSR_DS(3)
+#define DT2821_SUPCSR_BUFFB BIT(9)
+#define DT2821_SUPCSR_SCDN BIT(8)
+#define DT2821_SUPCSR_DACON BIT(7)
+#define DT2821_SUPCSR_ADCINIT BIT(6)
+#define DT2821_SUPCSR_DACINIT BIT(5)
+#define DT2821_SUPCSR_PRLD BIT(4)
+#define DT2821_SUPCSR_STRIG BIT(3)
+#define DT2821_SUPCSR_XTRIG BIT(2)
+#define DT2821_SUPCSR_XCLK BIT(1)
+#define DT2821_SUPCSR_BDINIT BIT(0)
#define DT2821_TMRCTR_REG 0x0e
static const struct comedi_lrange range_dt282x_ai_lo_bipolar = {
diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c
index 35ab4a9ef95d..c975f6e8be49 100644
--- a/drivers/staging/comedi/drivers/ni_mio_common.c
+++ b/drivers/staging/comedi/drivers/ni_mio_common.c
@@ -1929,7 +1929,7 @@ static int ni_ai_insn_read(struct comedi_device *dev,
unsigned int *data)
{
struct ni_private *devpriv = dev->private;
- unsigned int mask = (s->maxdata + 1) >> 1;
+ unsigned int mask = s->maxdata;
int i, n;
unsigned signbits;
unsigned int d;
@@ -1972,7 +1972,7 @@ static int ni_ai_insn_read(struct comedi_device *dev,
return -ETIME;
}
d += signbits;
- data[n] = d;
+ data[n] = d & 0xffff;
}
} else if (devpriv->is_6143) {
for (n = 0; n < insn->n; n++) {
@@ -2017,8 +2017,8 @@ static int ni_ai_insn_read(struct comedi_device *dev,
data[n] = dl;
} else {
d = ni_readw(dev, NI_E_AI_FIFO_DATA_REG);
- d += signbits; /* subtle: needs to be short addition */
- data[n] = d;
+ d += signbits;
+ data[n] = d & 0xffff;
}
}
}
diff --git a/drivers/staging/iio/adc/ad7606_core.c b/drivers/staging/iio/adc/ad7606_core.c
index 5796ed2409d0..39bbbaaff07c 100644
--- a/drivers/staging/iio/adc/ad7606_core.c
+++ b/drivers/staging/iio/adc/ad7606_core.c
@@ -189,7 +189,7 @@ static ssize_t ad7606_store_oversampling_ratio(struct device *dev,
mutex_lock(&indio_dev->mlock);
gpio_set_value(st->pdata->gpio_os0, (ret >> 0) & 1);
gpio_set_value(st->pdata->gpio_os1, (ret >> 1) & 1);
- gpio_set_value(st->pdata->gpio_os1, (ret >> 2) & 1);
+ gpio_set_value(st->pdata->gpio_os2, (ret >> 2) & 1);
st->oversampling = lval;
mutex_unlock(&indio_dev->mlock);
diff --git a/drivers/staging/rtl8188eu/core/rtw_recv.c b/drivers/staging/rtl8188eu/core/rtw_recv.c
index 110b8c0b6cd7..0f2fe34e14c2 100644
--- a/drivers/staging/rtl8188eu/core/rtw_recv.c
+++ b/drivers/staging/rtl8188eu/core/rtw_recv.c
@@ -1405,6 +1405,9 @@ static int wlanhdr_to_ethhdr(struct recv_frame *precvframe)
ptr = recvframe_pull(precvframe, (rmv_len-sizeof(struct ethhdr) + (bsnaphdr ? 2 : 0)));
}
+ if (!ptr)
+ return _FAIL;
+
memcpy(ptr, pattrib->dst, ETH_ALEN);
memcpy(ptr+ETH_ALEN, pattrib->src, ETH_ALEN);
diff --git a/drivers/staging/rtl8712/rtl871x_recv.c b/drivers/staging/rtl8712/rtl871x_recv.c
index 4ff530155187..04ac23cc47a8 100644
--- a/drivers/staging/rtl8712/rtl871x_recv.c
+++ b/drivers/staging/rtl8712/rtl871x_recv.c
@@ -641,11 +641,16 @@ sint r8712_wlanhdr_to_ethhdr(union recv_frame *precvframe)
/* append rx status for mp test packets */
ptr = recvframe_pull(precvframe, (rmv_len -
sizeof(struct ethhdr) + 2) - 24);
+ if (!ptr)
+ return _FAIL;
memcpy(ptr, get_rxmem(precvframe), 24);
ptr += 24;
- } else
+ } else {
ptr = recvframe_pull(precvframe, (rmv_len -
sizeof(struct ethhdr) + (bsnaphdr ? 2 : 0)));
+ if (!ptr)
+ return _FAIL;
+ }
memcpy(ptr, pattrib->dst, ETH_ALEN);
memcpy(ptr + ETH_ALEN, pattrib->src, ETH_ALEN);
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index bd810c109277..6ed80b05d674 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -3436,7 +3436,7 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd,
if ((tpg->tpg_attrib.generate_node_acls == 0) &&
(tpg->tpg_attrib.demo_mode_discovery == 0) &&
- (!core_tpg_get_initiator_node_acl(&tpg->tpg_se_tpg,
+ (!target_tpg_has_node_acl(&tpg->tpg_se_tpg,
cmd->conn->sess->sess_ops->InitiatorName))) {
continue;
}
diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c
index 23c95cd14167..68261b7dcefe 100644
--- a/drivers/target/iscsi/iscsi_target_tpg.c
+++ b/drivers/target/iscsi/iscsi_target_tpg.c
@@ -260,7 +260,6 @@ err_out:
iscsi_release_param_list(tpg->param_list);
tpg->param_list = NULL;
}
- kfree(tpg);
return -ENOMEM;
}
diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c
index dcd5ed26eb18..bb6a6c35324a 100644
--- a/drivers/target/target_core_device.c
+++ b/drivers/target/target_core_device.c
@@ -77,12 +77,16 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun)
&deve->read_bytes);
se_lun = rcu_dereference(deve->se_lun);
+
+ if (!percpu_ref_tryget_live(&se_lun->lun_ref)) {
+ se_lun = NULL;
+ goto out_unlock;
+ }
+
se_cmd->se_lun = rcu_dereference(deve->se_lun);
se_cmd->pr_res_key = deve->pr_res_key;
se_cmd->orig_fe_lun = unpacked_lun;
se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD;
-
- percpu_ref_get(&se_lun->lun_ref);
se_cmd->lun_ref_active = true;
if ((se_cmd->data_direction == DMA_TO_DEVICE) &&
@@ -96,6 +100,7 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun)
goto ref_dev;
}
}
+out_unlock:
rcu_read_unlock();
if (!se_lun) {
@@ -362,7 +367,15 @@ int core_enable_device_list_for_node(
kfree(new);
return -EINVAL;
}
- BUG_ON(orig->se_lun_acl != NULL);
+ if (orig->se_lun_acl != NULL) {
+ pr_warn_ratelimited("Detected existing explicit"
+ " se_lun_acl->se_lun_group reference for %s"
+ " mapped_lun: %llu, failing\n",
+ nacl->initiatorname, mapped_lun);
+ mutex_unlock(&nacl->lun_entry_mutex);
+ kfree(new);
+ return -EINVAL;
+ }
rcu_assign_pointer(new->se_lun, lun);
rcu_assign_pointer(new->se_lun_acl, lun_acl);
@@ -818,6 +831,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name)
xcopy_lun = &dev->xcopy_lun;
rcu_assign_pointer(xcopy_lun->lun_se_dev, dev);
init_completion(&xcopy_lun->lun_ref_comp);
+ init_completion(&xcopy_lun->lun_shutdown_comp);
INIT_LIST_HEAD(&xcopy_lun->lun_deve_list);
INIT_LIST_HEAD(&xcopy_lun->lun_dev_link);
mutex_init(&xcopy_lun->lun_tg_pt_md_mutex);
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index c220bb8dfa9d..2e27b1034ede 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -442,6 +442,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
int *post_ret)
{
struct se_device *dev = cmd->se_dev;
+ sense_reason_t ret = TCM_NO_SENSE;
/*
* Only set SCF_COMPARE_AND_WRITE_POST to force a response fall-through
@@ -449,9 +450,12 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
* sent to the backend driver.
*/
spin_lock_irq(&cmd->t_state_lock);
- if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status) {
+ if (cmd->transport_state & CMD_T_SENT) {
cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST;
*post_ret = 1;
+
+ if (cmd->scsi_status == SAM_STAT_CHECK_CONDITION)
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
spin_unlock_irq(&cmd->t_state_lock);
@@ -461,7 +465,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
*/
up(&dev->caw_sem);
- return TCM_NO_SENSE;
+ return ret;
}
static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success,
diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c
index 5fb9dd7f08bb..2794c6ec5c3c 100644
--- a/drivers/target/target_core_tpg.c
+++ b/drivers/target/target_core_tpg.c
@@ -75,9 +75,21 @@ struct se_node_acl *core_tpg_get_initiator_node_acl(
unsigned char *initiatorname)
{
struct se_node_acl *acl;
-
+ /*
+ * Obtain se_node_acl->acl_kref using fabric driver provided
+ * initiatorname[] during node acl endpoint lookup driven by
+ * new se_session login.
+ *
+ * The reference is held until se_session shutdown -> release
+ * occurs via fabric driver invoked transport_deregister_session()
+ * or transport_free_session() code.
+ */
mutex_lock(&tpg->acl_node_mutex);
acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname);
+ if (acl) {
+ if (!kref_get_unless_zero(&acl->acl_kref))
+ acl = NULL;
+ }
mutex_unlock(&tpg->acl_node_mutex);
return acl;
@@ -232,6 +244,25 @@ static void target_add_node_acl(struct se_node_acl *acl)
acl->initiatorname);
}
+bool target_tpg_has_node_acl(struct se_portal_group *tpg,
+ const char *initiatorname)
+{
+ struct se_node_acl *acl;
+ bool found = false;
+
+ mutex_lock(&tpg->acl_node_mutex);
+ list_for_each_entry(acl, &tpg->acl_node_list, acl_list) {
+ if (!strcmp(acl->initiatorname, initiatorname)) {
+ found = true;
+ break;
+ }
+ }
+ mutex_unlock(&tpg->acl_node_mutex);
+
+ return found;
+}
+EXPORT_SYMBOL(target_tpg_has_node_acl);
+
struct se_node_acl *core_tpg_check_initiator_node_acl(
struct se_portal_group *tpg,
unsigned char *initiatorname)
@@ -248,6 +279,15 @@ struct se_node_acl *core_tpg_check_initiator_node_acl(
acl = target_alloc_node_acl(tpg, initiatorname);
if (!acl)
return NULL;
+ /*
+ * When allocating a dynamically generated node_acl, go ahead
+ * and take the extra kref now before returning to the fabric
+ * driver caller.
+ *
+ * Note this reference will be released at session shutdown
+ * time within transport_free_session() code.
+ */
+ kref_get(&acl->acl_kref);
acl->dynamic_node_acl = 1;
/*
@@ -499,7 +539,7 @@ static void core_tpg_lun_ref_release(struct percpu_ref *ref)
{
struct se_lun *lun = container_of(ref, struct se_lun, lun_ref);
- complete(&lun->lun_ref_comp);
+ complete(&lun->lun_shutdown_comp);
}
int core_tpg_register(
@@ -626,6 +666,7 @@ struct se_lun *core_tpg_alloc_lun(
lun->lun_link_magic = SE_LUN_LINK_MAGIC;
atomic_set(&lun->lun_acl_count, 0);
init_completion(&lun->lun_ref_comp);
+ init_completion(&lun->lun_shutdown_comp);
INIT_LIST_HEAD(&lun->lun_deve_list);
INIT_LIST_HEAD(&lun->lun_dev_link);
atomic_set(&lun->lun_tg_pt_secondary_offline, 0);
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 2a67af4e2e13..df2059984e14 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -341,7 +341,6 @@ void __transport_register_session(
&buf[0], PR_REG_ISID_LEN);
se_sess->sess_bin_isid = get_unaligned_be64(&buf[0]);
}
- kref_get(&se_nacl->acl_kref);
spin_lock_irq(&se_nacl->nacl_sess_lock);
/*
@@ -424,14 +423,27 @@ static void target_complete_nacl(struct kref *kref)
{
struct se_node_acl *nacl = container_of(kref,
struct se_node_acl, acl_kref);
+ struct se_portal_group *se_tpg = nacl->se_tpg;
- complete(&nacl->acl_free_comp);
+ if (!nacl->dynamic_stop) {
+ complete(&nacl->acl_free_comp);
+ return;
+ }
+
+ mutex_lock(&se_tpg->acl_node_mutex);
+ list_del(&nacl->acl_list);
+ mutex_unlock(&se_tpg->acl_node_mutex);
+
+ core_tpg_wait_for_nacl_pr_ref(nacl);
+ core_free_device_list_for_node(nacl, se_tpg);
+ kfree(nacl);
}
void target_put_nacl(struct se_node_acl *nacl)
{
kref_put(&nacl->acl_kref, target_complete_nacl);
}
+EXPORT_SYMBOL(target_put_nacl);
void transport_deregister_session_configfs(struct se_session *se_sess)
{
@@ -464,6 +476,42 @@ EXPORT_SYMBOL(transport_deregister_session_configfs);
void transport_free_session(struct se_session *se_sess)
{
+ struct se_node_acl *se_nacl = se_sess->se_node_acl;
+
+ /*
+ * Drop the se_node_acl->nacl_kref obtained from within
+ * core_tpg_get_initiator_node_acl().
+ */
+ if (se_nacl) {
+ struct se_portal_group *se_tpg = se_nacl->se_tpg;
+ const struct target_core_fabric_ops *se_tfo = se_tpg->se_tpg_tfo;
+ unsigned long flags;
+
+ se_sess->se_node_acl = NULL;
+
+ /*
+ * Also determine if we need to drop the extra ->cmd_kref if
+ * it had been previously dynamically generated, and
+ * the endpoint is not caching dynamic ACLs.
+ */
+ mutex_lock(&se_tpg->acl_node_mutex);
+ if (se_nacl->dynamic_node_acl &&
+ !se_tfo->tpg_check_demo_mode_cache(se_tpg)) {
+ spin_lock_irqsave(&se_nacl->nacl_sess_lock, flags);
+ if (list_empty(&se_nacl->acl_sess_list))
+ se_nacl->dynamic_stop = true;
+ spin_unlock_irqrestore(&se_nacl->nacl_sess_lock, flags);
+
+ if (se_nacl->dynamic_stop)
+ list_del(&se_nacl->acl_list);
+ }
+ mutex_unlock(&se_tpg->acl_node_mutex);
+
+ if (se_nacl->dynamic_stop)
+ target_put_nacl(se_nacl);
+
+ target_put_nacl(se_nacl);
+ }
if (se_sess->sess_cmd_map) {
percpu_ida_destroy(&se_sess->sess_tag_pool);
kvfree(se_sess->sess_cmd_map);
@@ -475,16 +523,12 @@ EXPORT_SYMBOL(transport_free_session);
void transport_deregister_session(struct se_session *se_sess)
{
struct se_portal_group *se_tpg = se_sess->se_tpg;
- const struct target_core_fabric_ops *se_tfo;
- struct se_node_acl *se_nacl;
unsigned long flags;
- bool comp_nacl = true, drop_nacl = false;
if (!se_tpg) {
transport_free_session(se_sess);
return;
}
- se_tfo = se_tpg->se_tpg_tfo;
spin_lock_irqsave(&se_tpg->session_lock, flags);
list_del(&se_sess->sess_list);
@@ -492,37 +536,16 @@ void transport_deregister_session(struct se_session *se_sess)
se_sess->fabric_sess_ptr = NULL;
spin_unlock_irqrestore(&se_tpg->session_lock, flags);
- /*
- * Determine if we need to do extra work for this initiator node's
- * struct se_node_acl if it had been previously dynamically generated.
- */
- se_nacl = se_sess->se_node_acl;
-
- mutex_lock(&se_tpg->acl_node_mutex);
- if (se_nacl && se_nacl->dynamic_node_acl) {
- if (!se_tfo->tpg_check_demo_mode_cache(se_tpg)) {
- list_del(&se_nacl->acl_list);
- se_tpg->num_node_acls--;
- drop_nacl = true;
- }
- }
- mutex_unlock(&se_tpg->acl_node_mutex);
-
- if (drop_nacl) {
- core_tpg_wait_for_nacl_pr_ref(se_nacl);
- core_free_device_list_for_node(se_nacl, se_tpg);
- kfree(se_nacl);
- comp_nacl = false;
- }
pr_debug("TARGET_CORE[%s]: Deregistered fabric_sess\n",
se_tpg->se_tpg_tfo->get_fabric_name());
/*
* If last kref is dropping now for an explicit NodeACL, awake sleeping
* ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group
- * removal context.
+ * removal context from within transport_free_session() code.
+ *
+ * For dynamic ACL, target_put_nacl() uses target_complete_nacl()
+ * to release all remaining generate_node_acl=1 created ACL resources.
*/
- if (se_nacl && comp_nacl)
- target_put_nacl(se_nacl);
transport_free_session(se_sess);
}
@@ -2657,10 +2680,39 @@ void target_wait_for_sess_cmds(struct se_session *se_sess)
}
EXPORT_SYMBOL(target_wait_for_sess_cmds);
+static void target_lun_confirm(struct percpu_ref *ref)
+{
+ struct se_lun *lun = container_of(ref, struct se_lun, lun_ref);
+
+ complete(&lun->lun_ref_comp);
+}
+
void transport_clear_lun_ref(struct se_lun *lun)
{
- percpu_ref_kill(&lun->lun_ref);
+ /*
+ * Mark the percpu-ref as DEAD, switch to atomic_t mode, drop
+ * the initial reference and schedule confirm kill to be
+ * executed after one full RCU grace period has completed.
+ */
+ percpu_ref_kill_and_confirm(&lun->lun_ref, target_lun_confirm);
+ /*
+ * The first completion waits for percpu_ref_switch_to_atomic_rcu()
+ * to call target_lun_confirm after lun->lun_ref has been marked
+ * as __PERCPU_REF_DEAD on all CPUs, and switches to atomic_t
+ * mode so that percpu_ref_tryget_live() lookup of lun->lun_ref
+ * fails for all new incoming I/O.
+ */
wait_for_completion(&lun->lun_ref_comp);
+ /*
+ * The second completion waits for percpu_ref_put_many() to
+ * invoke ->release() after lun->lun_ref has switched to
+ * atomic_t mode, and lun->lun_ref.count has reached zero.
+ *
+ * At this point all target-core lun->lun_ref references have
+ * been dropped via transport_lun_remove_cmd(), and it's safe
+ * to proceed with the remaining LUN shutdown.
+ */
+ wait_for_completion(&lun->lun_shutdown_comp);
}
static bool
@@ -3058,7 +3110,6 @@ static void target_tmr_work(struct work_struct *work)
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
goto check_stop;
}
- cmd->t_state = TRANSPORT_ISTATE_PROCESSING;
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
cmd->se_tfo->queue_tm_rsp(cmd);
@@ -3071,11 +3122,25 @@ int transport_generic_handle_tmr(
struct se_cmd *cmd)
{
unsigned long flags;
+ bool aborted = false;
spin_lock_irqsave(&cmd->t_state_lock, flags);
- cmd->transport_state |= CMD_T_ACTIVE;
+ if (cmd->transport_state & CMD_T_ABORTED) {
+ aborted = true;
+ } else {
+ cmd->t_state = TRANSPORT_ISTATE_PROCESSING;
+ cmd->transport_state |= CMD_T_ACTIVE;
+ }
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ if (aborted) {
+ pr_warn_ratelimited("handle_tmr caught CMD_T_ABORTED TMR %d"
+ "ref_tag: %llu tag: %llu\n", cmd->se_tmr_req->function,
+ cmd->se_tmr_req->ref_task_tag, cmd->tag);
+ transport_cmd_check_stop_to_fabric(cmd);
+ return 0;
+ }
+
INIT_WORK(&cmd->work, target_tmr_work);
queue_work(cmd->se_dev->tmr_wq, &cmd->work);
return 0;
diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c
index 5e6d6cb348fc..a7d30e894cab 100644
--- a/drivers/target/target_core_user.c
+++ b/drivers/target/target_core_user.c
@@ -645,8 +645,6 @@ static int tcmu_check_expired_cmd(int id, void *p, void *data)
target_complete_cmd(cmd->se_cmd, SAM_STAT_CHECK_CONDITION);
cmd->se_cmd = NULL;
- kmem_cache_free(tcmu_cmd_cache, cmd);
-
return 0;
}
diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c
index 153a6f255b6d..6415e9b09a52 100644
--- a/drivers/target/target_core_xcopy.c
+++ b/drivers/target/target_core_xcopy.c
@@ -836,7 +836,7 @@ out:
" CHECK_CONDITION -> sending response\n", rc);
ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION;
}
- target_complete_cmd(ec_cmd, SAM_STAT_CHECK_CONDITION);
+ target_complete_cmd(ec_cmd, ec_cmd->scsi_status);
}
sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
diff --git a/drivers/thermal/msm_lmh_dcvs.c b/drivers/thermal/msm_lmh_dcvs.c
index cff5b6e3fc63..efe80c24d270 100644
--- a/drivers/thermal/msm_lmh_dcvs.c
+++ b/drivers/thermal/msm_lmh_dcvs.c
@@ -114,9 +114,9 @@ static void msm_lmh_dcvs_get_max_freq(uint32_t cpu, uint32_t *max_freq)
static uint32_t msm_lmh_mitigation_notify(struct msm_lmh_dcvs_hw *hw)
{
- uint32_t max_limit = 0, val = 0;
+ uint32_t val = 0;
struct device *cpu_dev = NULL;
- unsigned long freq_val;
+ unsigned long freq_val, max_limit = 0;
struct dev_pm_opp *opp_entry;
val = readl_relaxed(hw->osm_hw_reg);
diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c
index 06fd2ed9ef9d..705b0cafedbb 100644
--- a/drivers/thermal/thermal_hwmon.c
+++ b/drivers/thermal/thermal_hwmon.c
@@ -98,7 +98,7 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
int temperature;
int ret;
- ret = tz->ops->get_trip_temp(tz, 0, &temperature);
+ ret = tz->ops->get_crit_temp(tz, &temperature);
if (ret)
return ret;
diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c
index 644ddb841d9f..6d1e2f746ab4 100644
--- a/drivers/tty/n_hdlc.c
+++ b/drivers/tty/n_hdlc.c
@@ -114,7 +114,7 @@
#define DEFAULT_TX_BUF_COUNT 3
struct n_hdlc_buf {
- struct n_hdlc_buf *link;
+ struct list_head list_item;
int count;
char buf[1];
};
@@ -122,8 +122,7 @@ struct n_hdlc_buf {
#define N_HDLC_BUF_SIZE (sizeof(struct n_hdlc_buf) + maxframe)
struct n_hdlc_buf_list {
- struct n_hdlc_buf *head;
- struct n_hdlc_buf *tail;
+ struct list_head list;
int count;
spinlock_t spinlock;
};
@@ -136,7 +135,6 @@ struct n_hdlc_buf_list {
* @backup_tty - TTY to use if tty gets closed
* @tbusy - reentrancy flag for tx wakeup code
* @woke_up - FIXME: describe this field
- * @tbuf - currently transmitting tx buffer
* @tx_buf_list - list of pending transmit frame buffers
* @rx_buf_list - list of received frame buffers
* @tx_free_buf_list - list unused transmit frame buffers
@@ -149,7 +147,6 @@ struct n_hdlc {
struct tty_struct *backup_tty;
int tbusy;
int woke_up;
- struct n_hdlc_buf *tbuf;
struct n_hdlc_buf_list tx_buf_list;
struct n_hdlc_buf_list rx_buf_list;
struct n_hdlc_buf_list tx_free_buf_list;
@@ -159,7 +156,8 @@ struct n_hdlc {
/*
* HDLC buffer list manipulation functions
*/
-static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list);
+static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list,
+ struct n_hdlc_buf *buf);
static void n_hdlc_buf_put(struct n_hdlc_buf_list *list,
struct n_hdlc_buf *buf);
static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list);
@@ -209,16 +207,9 @@ static void flush_tx_queue(struct tty_struct *tty)
{
struct n_hdlc *n_hdlc = tty2n_hdlc(tty);
struct n_hdlc_buf *buf;
- unsigned long flags;
while ((buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list)))
n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, buf);
- spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
- if (n_hdlc->tbuf) {
- n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, n_hdlc->tbuf);
- n_hdlc->tbuf = NULL;
- }
- spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
}
static struct tty_ldisc_ops n_hdlc_ldisc = {
@@ -284,7 +275,6 @@ static void n_hdlc_release(struct n_hdlc *n_hdlc)
} else
break;
}
- kfree(n_hdlc->tbuf);
kfree(n_hdlc);
} /* end of n_hdlc_release() */
@@ -403,13 +393,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
n_hdlc->woke_up = 0;
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
- /* get current transmit buffer or get new transmit */
- /* buffer from list of pending transmit buffers */
-
- tbuf = n_hdlc->tbuf;
- if (!tbuf)
- tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
-
+ tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
while (tbuf) {
if (debuglevel >= DEBUG_LEVEL_INFO)
printk("%s(%d)sending frame %p, count=%d\n",
@@ -421,7 +405,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
/* rollback was possible and has been done */
if (actual == -ERESTARTSYS) {
- n_hdlc->tbuf = tbuf;
+ n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf);
break;
}
/* if transmit error, throw frame away by */
@@ -436,10 +420,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
/* free current transmit buffer */
n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, tbuf);
-
- /* this tx buffer is done */
- n_hdlc->tbuf = NULL;
-
+
/* wait up sleeping writers */
wake_up_interruptible(&tty->write_wait);
@@ -449,10 +430,12 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
if (debuglevel >= DEBUG_LEVEL_INFO)
printk("%s(%d)frame %p pending\n",
__FILE__,__LINE__,tbuf);
-
- /* buffer not accepted by driver */
- /* set this buffer as pending buffer */
- n_hdlc->tbuf = tbuf;
+
+ /*
+ * the buffer was not accepted by driver,
+ * return it back into tx queue
+ */
+ n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf);
break;
}
}
@@ -750,7 +733,8 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
int error = 0;
int count;
unsigned long flags;
-
+ struct n_hdlc_buf *buf = NULL;
+
if (debuglevel >= DEBUG_LEVEL_INFO)
printk("%s(%d)n_hdlc_tty_ioctl() called %d\n",
__FILE__,__LINE__,cmd);
@@ -764,8 +748,10 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
/* report count of read data available */
/* in next available frame (if any) */
spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags);
- if (n_hdlc->rx_buf_list.head)
- count = n_hdlc->rx_buf_list.head->count;
+ buf = list_first_entry_or_null(&n_hdlc->rx_buf_list.list,
+ struct n_hdlc_buf, list_item);
+ if (buf)
+ count = buf->count;
else
count = 0;
spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags);
@@ -777,8 +763,10 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
count = tty_chars_in_buffer(tty);
/* add size of next output frame in queue */
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags);
- if (n_hdlc->tx_buf_list.head)
- count += n_hdlc->tx_buf_list.head->count;
+ buf = list_first_entry_or_null(&n_hdlc->tx_buf_list.list,
+ struct n_hdlc_buf, list_item);
+ if (buf)
+ count += buf->count;
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock,flags);
error = put_user(count, (int __user *)arg);
break;
@@ -826,14 +814,14 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp,
poll_wait(filp, &tty->write_wait, wait);
/* set bits for operations that won't block */
- if (n_hdlc->rx_buf_list.head)
+ if (!list_empty(&n_hdlc->rx_buf_list.list))
mask |= POLLIN | POLLRDNORM; /* readable */
if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
mask |= POLLHUP;
if (tty_hung_up_p(filp))
mask |= POLLHUP;
if (!tty_is_writelocked(tty) &&
- n_hdlc->tx_free_buf_list.head)
+ !list_empty(&n_hdlc->tx_free_buf_list.list))
mask |= POLLOUT | POLLWRNORM; /* writable */
}
return mask;
@@ -853,11 +841,16 @@ static struct n_hdlc *n_hdlc_alloc(void)
if (!n_hdlc)
return NULL;
- n_hdlc_buf_list_init(&n_hdlc->rx_free_buf_list);
- n_hdlc_buf_list_init(&n_hdlc->tx_free_buf_list);
- n_hdlc_buf_list_init(&n_hdlc->rx_buf_list);
- n_hdlc_buf_list_init(&n_hdlc->tx_buf_list);
-
+ spin_lock_init(&n_hdlc->rx_free_buf_list.spinlock);
+ spin_lock_init(&n_hdlc->tx_free_buf_list.spinlock);
+ spin_lock_init(&n_hdlc->rx_buf_list.spinlock);
+ spin_lock_init(&n_hdlc->tx_buf_list.spinlock);
+
+ INIT_LIST_HEAD(&n_hdlc->rx_free_buf_list.list);
+ INIT_LIST_HEAD(&n_hdlc->tx_free_buf_list.list);
+ INIT_LIST_HEAD(&n_hdlc->rx_buf_list.list);
+ INIT_LIST_HEAD(&n_hdlc->tx_buf_list.list);
+
/* allocate free rx buffer list */
for(i=0;i<DEFAULT_RX_BUF_COUNT;i++) {
buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL);
@@ -885,63 +878,65 @@ static struct n_hdlc *n_hdlc_alloc(void)
} /* end of n_hdlc_alloc() */
/**
- * n_hdlc_buf_list_init - initialize specified HDLC buffer list
- * @list - pointer to buffer list
+ * n_hdlc_buf_return - put the HDLC buffer after the head of the specified list
+ * @buf_list - pointer to the buffer list
+ * @buf - pointer to the buffer
*/
-static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list)
+static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list,
+ struct n_hdlc_buf *buf)
{
- memset(list, 0, sizeof(*list));
- spin_lock_init(&list->spinlock);
-} /* end of n_hdlc_buf_list_init() */
+ unsigned long flags;
+
+ spin_lock_irqsave(&buf_list->spinlock, flags);
+
+ list_add(&buf->list_item, &buf_list->list);
+ buf_list->count++;
+
+ spin_unlock_irqrestore(&buf_list->spinlock, flags);
+}
/**
* n_hdlc_buf_put - add specified HDLC buffer to tail of specified list
- * @list - pointer to buffer list
+ * @buf_list - pointer to buffer list
* @buf - pointer to buffer
*/
-static void n_hdlc_buf_put(struct n_hdlc_buf_list *list,
+static void n_hdlc_buf_put(struct n_hdlc_buf_list *buf_list,
struct n_hdlc_buf *buf)
{
unsigned long flags;
- spin_lock_irqsave(&list->spinlock,flags);
-
- buf->link=NULL;
- if (list->tail)
- list->tail->link = buf;
- else
- list->head = buf;
- list->tail = buf;
- (list->count)++;
-
- spin_unlock_irqrestore(&list->spinlock,flags);
-
+
+ spin_lock_irqsave(&buf_list->spinlock, flags);
+
+ list_add_tail(&buf->list_item, &buf_list->list);
+ buf_list->count++;
+
+ spin_unlock_irqrestore(&buf_list->spinlock, flags);
} /* end of n_hdlc_buf_put() */
/**
* n_hdlc_buf_get - remove and return an HDLC buffer from list
- * @list - pointer to HDLC buffer list
+ * @buf_list - pointer to HDLC buffer list
*
* Remove and return an HDLC buffer from the head of the specified HDLC buffer
* list.
* Returns a pointer to HDLC buffer if available, otherwise %NULL.
*/
-static struct n_hdlc_buf* n_hdlc_buf_get(struct n_hdlc_buf_list *list)
+static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list)
{
unsigned long flags;
struct n_hdlc_buf *buf;
- spin_lock_irqsave(&list->spinlock,flags);
-
- buf = list->head;
+
+ spin_lock_irqsave(&buf_list->spinlock, flags);
+
+ buf = list_first_entry_or_null(&buf_list->list,
+ struct n_hdlc_buf, list_item);
if (buf) {
- list->head = buf->link;
- (list->count)--;
+ list_del(&buf->list_item);
+ buf_list->count--;
}
- if (!list->head)
- list->tail = NULL;
-
- spin_unlock_irqrestore(&list->spinlock,flags);
+
+ spin_unlock_irqrestore(&buf_list->spinlock, flags);
return buf;
-
} /* end of n_hdlc_buf_get() */
static char hdlc_banner[] __initdata =
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 029de3f99752..5b24ffd93649 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -2880,6 +2880,8 @@ enum pci_board_num_t {
pbn_b0_4_1152000_200,
pbn_b0_8_1152000_200,
+ pbn_b0_4_1250000,
+
pbn_b0_2_1843200,
pbn_b0_4_1843200,
@@ -3113,6 +3115,13 @@ static struct pciserial_board pci_boards[] = {
.uart_offset = 0x200,
},
+ [pbn_b0_4_1250000] = {
+ .flags = FL_BASE0,
+ .num_ports = 4,
+ .base_baud = 1250000,
+ .uart_offset = 8,
+ },
+
[pbn_b0_2_1843200] = {
.flags = FL_BASE0,
.num_ports = 2,
@@ -5778,6 +5787,10 @@ static struct pci_device_id serial_pci_tbl[] = {
{ PCI_DEVICE(0x1c29, 0x1108), .driver_data = pbn_fintek_8 },
{ PCI_DEVICE(0x1c29, 0x1112), .driver_data = pbn_fintek_12 },
+ /* MKS Tenta SCOM-080x serial cards */
+ { PCI_DEVICE(0x1601, 0x0800), .driver_data = pbn_b0_4_1250000 },
+ { PCI_DEVICE(0x1601, 0xa801), .driver_data = pbn_b0_4_1250000 },
+
/*
* These entries match devices with class COMMUNICATION_SERIAL,
* COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index e0b89b961e1b..a0f911641b04 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -470,6 +470,14 @@ static void atmel_stop_tx(struct uart_port *port)
/* disable PDC transmit */
atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS);
}
+
+ /*
+ * Disable the transmitter.
+ * This is mandatory when DMA is used, otherwise the DMA buffer
+ * is fully transmitted.
+ */
+ atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS);
+
/* Disable interrupts */
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
@@ -502,6 +510,9 @@ static void atmel_start_tx(struct uart_port *port)
/* Enable interrupts */
atmel_uart_writel(port, ATMEL_US_IER, atmel_port->tx_done_mask);
+
+ /* re-enable the transmitter */
+ atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
}
/*
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 8f68acd1d95d..db329230c7ca 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -1834,6 +1834,7 @@ static const struct of_device_id msm_match_table[] = {
{ .compatible = "qcom,msm-uartdm" },
{}
};
+MODULE_DEVICE_TABLE(of, msm_match_table);
#ifdef CONFIG_PM_SLEEP
static int msm_serial_suspend(struct device *dev)
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index f0d5c96ac2e0..cc616d678d42 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -3,7 +3,7 @@
* MSM 7k High speed uart driver
*
* Copyright (c) 2008 Google Inc.
- * Copyright (c) 2007-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2017, The Linux Foundation. All rights reserved.
* Modified: Nick Pelly <npelly@google.com>
*
* All source code in this file is licensed under the following license
@@ -1436,13 +1436,13 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport)
hex_dump_ipc(msm_uport, tx->ipc_tx_ctxt, "Tx",
&tx_buf->buf[tx_buf->tail], (u64)src_addr, tx_count);
sps_pipe_handle = tx->cons.pipe_handle;
- /* Queue transfer request to SPS */
- ret = sps_transfer_one(sps_pipe_handle, src_addr, tx_count,
- msm_uport, flags);
/* Set 1 second timeout */
mod_timer(&tx->tx_timeout_timer,
jiffies + msecs_to_jiffies(MSEC_PER_SEC));
+ /* Queue transfer request to SPS */
+ ret = sps_transfer_one(sps_pipe_handle, src_addr, tx_count,
+ msm_uport, flags);
MSM_HS_DBG("%s:Enqueue Tx Cmd, ret %d\n", __func__, ret);
}
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index ab8308ff7e69..cad76b1cf672 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -1030,8 +1030,10 @@ static int s3c64xx_serial_startup(struct uart_port *port)
if (ourport->dma) {
ret = s3c24xx_serial_request_dma(ourport);
if (ret < 0) {
- dev_warn(port->dev, "DMA request failed\n");
- return ret;
+ dev_warn(port->dev,
+ "DMA request failed, DMA will not be used\n");
+ devm_kfree(port->dev, ourport->dma);
+ ourport->dma = NULL;
}
}
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index edb5305b9d4d..7d5ee8a13ac6 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -1230,7 +1230,7 @@ static int sc16is7xx_probe(struct device *dev,
/* Setup interrupt */
ret = devm_request_irq(dev, irq, sc16is7xx_irq,
- IRQF_ONESHOT | flags, dev_name(dev), s);
+ flags, dev_name(dev), s);
if (!ret)
return 0;
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index e5139402e7f8..1ca9cea2eaf8 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -55,10 +55,11 @@
static int __read_mostly sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE;
static bool __read_mostly sysrq_always_enabled;
-static bool sysrq_on(void)
+bool sysrq_on(void)
{
return sysrq_enabled || sysrq_always_enabled;
}
+EXPORT_SYMBOL(sysrq_on);
/*
* A value of 1 means 'all', other nonzero values are an op mask:
@@ -945,8 +946,8 @@ static const struct input_device_id sysrq_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT,
- .evbit = { BIT_MASK(EV_KEY) },
- .keybit = { BIT_MASK(KEY_LEFTALT) },
+ .evbit = { [BIT_WORD(EV_KEY)] = BIT_MASK(EV_KEY) },
+ .keybit = { [BIT_WORD(KEY_LEFTALT)] = BIT_MASK(KEY_LEFTALT) },
},
{ },
};
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 41987a55a538..988c564b61a8 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -982,7 +982,7 @@ static void kbd_led_trigger_activate(struct led_classdev *cdev)
KBD_LED_TRIGGER((_led_bit) + 8, _name)
static struct kbd_led_trigger kbd_led_triggers[] = {
- KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrollock"),
+ KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrolllock"),
KBD_LED_TRIGGER(VC_NUMLOCK, "kbd-numlock"),
KBD_LED_TRIGGER(VC_CAPSLOCK, "kbd-capslock"),
KBD_LED_TRIGGER(VC_KANALOCK, "kbd-kanalock"),
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index 5a048b7b92e8..2949289bb3c5 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -244,7 +244,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
struct ci_hdrc_platform_data pdata = {
.name = dev_name(&pdev->dev),
.capoffset = DEF_CAPOFFSET,
- .flags = CI_HDRC_SET_NON_ZERO_TTHA,
};
int ret;
const struct of_device_id *of_id;
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 4d77745f439f..96849e2e7435 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1708,6 +1708,7 @@ static const struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */
.driver_info = QUIRK_CONTROL_LINE_STATE, },
{ USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
+ { USB_DEVICE(0x2184, 0x0036) }, /* GW Instek AFG-125 */
{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
},
/* Motorola H24 HSPA module: */
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index ff44cfa26af8..ac30a051ad71 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -205,6 +205,16 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
if (ifp->desc.bNumEndpoints >= num_ep)
goto skip_to_next_endpoint_or_interface_descriptor;
+ /* Check for duplicate endpoint addresses */
+ for (i = 0; i < ifp->desc.bNumEndpoints; ++i) {
+ if (ifp->endpoint[i].desc.bEndpointAddress ==
+ d->bEndpointAddress) {
+ dev_warn(ddev, "config %d interface %d altsetting %d has a duplicate endpoint with address 0x%X, skipping\n",
+ cfgno, inum, asnum, d->bEndpointAddress);
+ goto skip_to_next_endpoint_or_interface_descriptor;
+ }
+ }
+
endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints];
++ifp->desc.bNumEndpoints;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 29242ffe8dca..f84ef04284f5 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -106,6 +106,7 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
static void hub_release(struct kref *kref);
static int usb_reset_and_verify_device(struct usb_device *udev);
+static int hub_port_disable(struct usb_hub *hub, int port1, int set_state);
static inline char *portspeed(struct usb_hub *hub, int portstatus)
{
@@ -894,88 +895,6 @@ static int hub_set_port_link_state(struct usb_hub *hub, int port1,
}
/*
- * If USB 3.0 ports are placed into the Disabled state, they will no longer
- * detect any device connects or disconnects. This is generally not what the
- * USB core wants, since it expects a disabled port to produce a port status
- * change event when a new device connects.
- *
- * Instead, set the link state to Disabled, wait for the link to settle into
- * that state, clear any change bits, and then put the port into the RxDetect
- * state.
- */
-static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
-{
- int ret;
- int total_time;
- u16 portchange, portstatus;
-
- if (!hub_is_superspeed(hub->hdev))
- return -EINVAL;
-
- ret = hub_port_status(hub, port1, &portstatus, &portchange);
- if (ret < 0)
- return ret;
-
- /*
- * USB controller Advanced Micro Devices, Inc. [AMD] FCH USB XHCI
- * Controller [1022:7814] will have spurious result making the following
- * usb 3.0 device hotplugging route to the 2.0 root hub and recognized
- * as high-speed device if we set the usb 3.0 port link state to
- * Disabled. Since it's already in USB_SS_PORT_LS_RX_DETECT state, we
- * check the state here to avoid the bug.
- */
- if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
- USB_SS_PORT_LS_RX_DETECT) {
- dev_dbg(&hub->ports[port1 - 1]->dev,
- "Not disabling port; link state is RxDetect\n");
- return ret;
- }
-
- ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED);
- if (ret)
- return ret;
-
- /* Wait for the link to enter the disabled state. */
- for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
- ret = hub_port_status(hub, port1, &portstatus, &portchange);
- if (ret < 0)
- return ret;
-
- if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
- USB_SS_PORT_LS_SS_DISABLED)
- break;
- if (total_time >= HUB_DEBOUNCE_TIMEOUT)
- break;
- msleep(HUB_DEBOUNCE_STEP);
- }
- if (total_time >= HUB_DEBOUNCE_TIMEOUT)
- dev_warn(&hub->ports[port1 - 1]->dev,
- "Could not disable after %d ms\n", total_time);
-
- return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT);
-}
-
-static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
-{
- struct usb_port *port_dev = hub->ports[port1 - 1];
- struct usb_device *hdev = hub->hdev;
- int ret = 0;
-
- if (port_dev->child && set_state)
- usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);
- if (!hub->error) {
- if (hub_is_superspeed(hub->hdev))
- ret = hub_usb3_port_disable(hub, port1);
- else
- ret = usb_clear_port_feature(hdev, port1,
- USB_PORT_FEAT_ENABLE);
- }
- if (ret && ret != -ENODEV)
- dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret);
- return ret;
-}
-
-/*
* Disable a port and mark a logical connect-change event, so that some
* time later hub_wq will disconnect() any existing usb_device on the port
* and will re-enumerate if there actually is a device attached.
@@ -4086,6 +4005,26 @@ void usb_unlocked_enable_lpm(struct usb_device *udev)
}
EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
+/* usb3 devices use U3 for disabled, make sure remote wakeup is disabled */
+static void hub_usb3_port_prepare_disable(struct usb_hub *hub,
+ struct usb_port *port_dev)
+{
+ struct usb_device *udev = port_dev->child;
+ int ret;
+
+ if (udev && udev->port_is_suspended && udev->do_remote_wakeup) {
+ ret = hub_set_port_link_state(hub, port_dev->portnum,
+ USB_SS_PORT_LS_U0);
+ if (!ret) {
+ msleep(USB_RESUME_TIMEOUT);
+ ret = usb_disable_remote_wakeup(udev);
+ }
+ if (ret)
+ dev_warn(&udev->dev,
+ "Port disable: can't disable remote wake\n");
+ udev->do_remote_wakeup = 0;
+ }
+}
#else /* CONFIG_PM */
@@ -4093,6 +4032,9 @@ EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
#define hub_resume NULL
#define hub_reset_resume NULL
+static inline void hub_usb3_port_prepare_disable(struct usb_hub *hub,
+ struct usb_port *port_dev) { }
+
int usb_disable_lpm(struct usb_device *udev)
{
return 0;
@@ -4128,6 +4070,34 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
#endif /* CONFIG_PM */
+/*
+ * USB-3 does not have a similar link state as USB-2 that will avoid negotiating
+ * a connection with a plugged-in cable but will signal the host when the cable
+ * is unplugged. Disable remote wake and set link state to U3 for USB-3 devices
+ */
+static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
+{
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ struct usb_device *hdev = hub->hdev;
+ int ret = 0;
+
+ if (!hub->error) {
+ if (hub_is_superspeed(hub->hdev)) {
+ hub_usb3_port_prepare_disable(hub, port_dev);
+ ret = hub_set_port_link_state(hub, port_dev->portnum,
+ USB_SS_PORT_LS_U3);
+ } else {
+ ret = usb_clear_port_feature(hdev, port1,
+ USB_PORT_FEAT_ENABLE);
+ }
+ }
+ if (port_dev->child && set_state)
+ usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);
+ if (ret && ret != -ENODEV)
+ dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret);
+ return ret;
+}
+
/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
*
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index d2e50a27140c..24f9f98968a5 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -37,6 +37,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* CBM - Flash disk */
{ USB_DEVICE(0x0204, 0x6025), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* WORLDE easy key (easykey.25) MIDI controller */
+ { USB_DEVICE(0x0218, 0x0401), .driver_info =
+ USB_QUIRK_CONFIG_INTF_STRINGS },
+
/* HP 5300/5370C scanner */
{ USB_DEVICE(0x03f0, 0x0701), .driver_info =
USB_QUIRK_STRING_FETCH_255 },
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 9de5e06430e1..f6e2bea7b9aa 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -44,7 +44,7 @@
#define DWC3_XHCI_RESOURCES_NUM 2
#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
-#define DWC3_EVENT_BUFFERS_SIZE (2 * PAGE_SIZE)
+#define DWC3_EVENT_BUFFERS_SIZE 4096
#define DWC3_EVENT_TYPE_MASK 0xfe
#define DWC3_EVENT_TYPE_DEV 0
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 3d731d1b5c60..d2c0c1a8d979 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -37,6 +37,7 @@
#define PCI_DEVICE_ID_INTEL_BXT 0x0aaa
#define PCI_DEVICE_ID_INTEL_APL 0x5aaa
#define PCI_DEVICE_ID_INTEL_KBP 0xa2b0
+#define PCI_DEVICE_ID_INTEL_GLK 0x31aa
static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
static const struct acpi_gpio_params cs_gpios = { 1, 0, false };
@@ -216,6 +217,7 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BXT), },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_APL), },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KBP), },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_GLK), },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), },
{ } /* Terminating Entry */
};
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 9cd87513619c..c2a6fdbfcfee 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -62,20 +62,13 @@ static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
}
}
-static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
- u32 len, u32 type, bool chain)
+static void dwc3_ep0_prepare_one_trb(struct dwc3 *dwc, u8 epnum,
+ dma_addr_t buf_dma, u32 len, u32 type, bool chain)
{
- struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb *trb;
struct dwc3_ep *dep;
- int ret;
-
dep = dwc->eps[epnum];
- if (dep->flags & DWC3_EP_BUSY) {
- dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name);
- return 0;
- }
trb = &dwc->ep0_trb[dep->free_slot];
@@ -96,15 +89,25 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
trb->ctrl |= (DWC3_TRB_CTRL_IOC
| DWC3_TRB_CTRL_LST);
- if (chain)
+ trace_dwc3_prepare_trb(dep, trb);
+}
+
+static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_ep *dep;
+ int ret;
+
+ dep = dwc->eps[epnum];
+ if (dep->flags & DWC3_EP_BUSY) {
+ dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name);
return 0;
+ }
memset(&params, 0, sizeof(params));
params.param0 = upper_32_bits(dwc->ep0_trb_addr);
params.param1 = lower_32_bits(dwc->ep0_trb_addr);
- trace_dwc3_prepare_trb(dep, trb);
-
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
DWC3_DEPCMD_STARTTRANSFER, &params);
if (ret < 0) {
@@ -333,9 +336,9 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
{
int ret;
- ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8,
+ dwc3_ep0_prepare_one_trb(dwc, 0, dwc->ctrl_req_addr, 8,
DWC3_TRBCTL_CONTROL_SETUP, false);
-
+ ret = dwc3_ep0_start_trans(dwc, 0);
if (WARN_ON_ONCE(ret < 0))
dbg_event(dwc->eps[0]->number, "EOUTSTART", ret);
}
@@ -923,9 +926,9 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
- ret = dwc3_ep0_start_trans(dwc, epnum,
- dwc->ctrl_req_addr, 0,
- DWC3_TRBCTL_CONTROL_DATA, false);
+ dwc3_ep0_prepare_one_trb(dwc, epnum, dwc->ctrl_req_addr,
+ 0, DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dwc, epnum);
if (WARN_ON_ONCE(ret < 0))
dbg_event(epnum, "ECTRL_DATA", ret);
}
@@ -1010,9 +1013,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
req->direction = !!dep->number;
if (req->request.length == 0) {
- ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ctrl_req_addr, 0,
DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dwc, dep->number);
} else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
&& (dep->number == 0)) {
u32 transfer_size = 0;
@@ -1030,7 +1034,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
if (req->request.length > DWC3_EP0_BOUNCE_SIZE) {
transfer_size = ALIGN(req->request.length - maxpacket,
maxpacket);
- ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
req->request.dma,
transfer_size,
DWC3_TRBCTL_CONTROL_DATA,
@@ -1042,9 +1046,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
dwc->ep0_bounced = true;
- ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ep0_bounce_addr, transfer_size,
DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dwc, dep->number);
} else {
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
dep->number);
@@ -1053,9 +1058,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
return;
}
- ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number, req->request.dma,
req->request.length, DWC3_TRBCTL_CONTROL_DATA,
false);
+ ret = dwc3_ep0_start_trans(dwc, dep->number);
}
dbg_queue(dep->number, &req->request, ret);
@@ -1069,8 +1075,9 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3
: DWC3_TRBCTL_CONTROL_STATUS2;
- return dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ctrl_req_addr, 0, type, false);
+ return dwc3_ep0_start_trans(dwc, dep->number);
}
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 7a279db521ca..e1284b6cc2ef 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -285,11 +285,11 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
if (req->request.status == -EINPROGRESS)
req->request.status = status;
- if (dwc->ep0_bounced && dep->number == 0)
+ if (dwc->ep0_bounced && dep->number <= 1)
dwc->ep0_bounced = false;
- else
- usb_gadget_unmap_request(&dwc->gadget, &req->request,
- req->direction);
+
+ usb_gadget_unmap_request(&dwc->gadget, &req->request,
+ req->direction);
dev_dbg(dwc->dev, "request %pK from %s completed %d/%d ===> %d\n",
req, dep->name, req->request.actual,
@@ -388,8 +388,10 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
}
return -ETIMEDOUT;
}
-
- udelay(1);
+ if ((cmd & DWC3_DEPCMD_SETTRANSFRESOURCE))
+ udelay(20);
+ else
+ udelay(1);
} while (1);
}
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index a21962c8f513..99ff6df063a7 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -28,23 +28,23 @@ struct dwc3;
#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
/* DEPCFG parameter 1 */
-#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0)
+#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0)
#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8)
#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9)
#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10)
#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11)
#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13)
-#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16)
+#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16)
#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24)
-#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25)
+#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25)
#define DWC3_DEPCFG_BULK_BASED (1 << 30)
#define DWC3_DEPCFG_FIFO_BASED (1 << 31)
/* DEPCFG parameter 0 */
-#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1)
-#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3)
-#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17)
-#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22)
+#define DWC3_DEPCFG_EP_TYPE(n) (((n) & 0x3) << 1)
+#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) (((n) & 0x7ff) << 3)
+#define DWC3_DEPCFG_FIFO_NUMBER(n) (((n) & 0x1f) << 17)
+#define DWC3_DEPCFG_BURST_SIZE(n) (((n) & 0xf) << 22)
#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
/* This applies for core versions earlier than 1.94a */
#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 9d795489c285..a844ea4d06db 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -160,11 +160,16 @@ int config_ep_by_speed(struct usb_gadget *g,
ep_found:
/* commit results */
- _ep->maxpacket = usb_endpoint_maxp(chosen_desc);
+ _ep->maxpacket = usb_endpoint_maxp(chosen_desc) & 0x7ff;
_ep->desc = chosen_desc;
_ep->comp_desc = NULL;
_ep->maxburst = 0;
- _ep->mult = 0;
+ _ep->mult = 1;
+
+ if (g->speed == USB_SPEED_HIGH && (usb_endpoint_xfer_isoc(_ep->desc) ||
+ usb_endpoint_xfer_int(_ep->desc)))
+ _ep->mult = ((usb_endpoint_maxp(_ep->desc) & 0x1800) >> 11) + 1;
+
if (!want_comp_desc)
return 0;
@@ -181,7 +186,7 @@ ep_found:
switch (usb_endpoint_type(_ep->desc)) {
case USB_ENDPOINT_XFER_ISOC:
/* mult: bits 1:0 of bmAttributes */
- _ep->mult = comp_desc->bmAttributes & 0x3;
+ _ep->mult = (comp_desc->bmAttributes & 0x3) + 1;
case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_INT:
_ep->maxburst = comp_desc->bMaxBurst + 1;
@@ -1750,9 +1755,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
value = min(w_length, (u16) 1);
break;
- /* function drivers must handle get/set altsetting; if there's
- * no get() method, we know only altsetting zero works.
- */
+ /* function drivers must handle get/set altsetting */
case USB_REQ_SET_INTERFACE:
if (ctrl->bRequestType != USB_RECIP_INTERFACE)
goto unknown;
@@ -1761,7 +1764,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
f = cdev->config->interface[intf];
if (!f)
break;
- if (w_value && !f->set_alt)
+
+ /*
+ * If there's no get_alt() method, we know only altsetting zero
+ * works. There is no need to check if set_alt() is not NULL
+ * as we check this in usb_add_function().
+ */
+ if (w_value && !f->get_alt)
break;
value = f->set_alt(f, w_index, w_value);
if (value == USB_GADGET_DELAYED_STATUS) {
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 7a64b24b8bf6..a42550950953 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -60,7 +60,7 @@ usb_f_cdev-y := f_cdev.o
obj-$(CONFIG_USB_F_CDEV) += usb_f_cdev.o
usb_f_qdss-y := f_qdss.o u_qdss.o
obj-$(CONFIG_USB_F_QDSS) += usb_f_qdss.o
-usb_f_qcrndis-y := f_qc_rndis.o u_data_ipa.o
+usb_f_qcrndis-y := f_qc_rndis.o rndis.o u_data_ipa.o
obj-$(CONFIG_USB_F_QCRNDIS) += usb_f_qcrndis.o
usb_f_rmnet_bam-y := f_rmnet.o u_ctrl_qti.o
obj-$(CONFIG_USB_F_RMNET_BAM) += usb_f_rmnet_bam.o
diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c
index db7903d19c43..a2a9185a0143 100644
--- a/drivers/usb/gadget/function/f_audio_source.c
+++ b/drivers/usb/gadget/function/f_audio_source.c
@@ -989,6 +989,7 @@ static ssize_t audio_source_pcm_show(struct device *dev,
struct device *create_function_device(char *name);
+#define AUDIO_SOURCE_DEV_NAME_LENGTH 20
static struct usb_function_instance *audio_source_alloc_inst(void)
{
struct audio_source_instance *fi_audio;
@@ -997,6 +998,8 @@ static struct usb_function_instance *audio_source_alloc_inst(void)
struct device *dev;
void *err_ptr;
int err = 0;
+ char device_name[AUDIO_SOURCE_DEV_NAME_LENGTH];
+ static u8 count;
fi_audio = kzalloc(sizeof(*fi_audio), GFP_KERNEL);
if (!fi_audio)
@@ -1014,7 +1017,11 @@ static struct usb_function_instance *audio_source_alloc_inst(void)
config_group_init_type_name(&fi_audio->func_inst.group, "",
&audio_source_func_type);
- dev = create_function_device("f_audio_source");
+
+ snprintf(device_name, AUDIO_SOURCE_DEV_NAME_LENGTH,
+ "f_audio_source%d", count++);
+
+ dev = create_function_device(device_name);
if (IS_ERR(dev)) {
err_ptr = dev;
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 31d3022b6ce7..a1c00525a598 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -2437,6 +2437,8 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
if (len < sizeof(*d) || h->interface >= ffs->interfaces_count)
return -EINVAL;
length = le32_to_cpu(d->dwSize);
+ if (len < length)
+ return -EINVAL;
type = le32_to_cpu(d->dwPropertyDataType);
if (type < USB_EXT_PROP_UNICODE ||
type > USB_EXT_PROP_UNICODE_MULTI) {
@@ -2445,6 +2447,11 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
return -EINVAL;
}
pnl = le16_to_cpu(d->wPropertyNameLength);
+ if (length < 14 + pnl) {
+ pr_vdebug("invalid os descriptor length: %d pnl:%d (descriptor %d)\n",
+ length, pnl, type);
+ return -EINVAL;
+ }
pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl));
if (length != 14 + pnl + pdl) {
pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
@@ -2534,6 +2541,9 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
}
}
if (flags & (1 << i)) {
+ if (len < 4) {
+ goto error;
+ }
os_descs_count = get_unaligned_le32(data);
data += 4;
len -= 4;
@@ -2611,7 +2621,8 @@ static int __ffs_data_got_strings(struct ffs_data *ffs,
ffs_log("enter: len %zu", len);
- if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
+ if (unlikely(len < 16 ||
+ get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
get_unaligned_le32(data + 4) != len))
goto error;
str_count = get_unaligned_le32(data + 8);
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 4e35ed9654b7..2a7d57cd14cb 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -1426,17 +1426,39 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
*/
if (!ncm_opts->bound) {
mutex_lock(&ncm_opts->lock);
+ ncm_opts->net = gether_setup_default();
+ if (IS_ERR(ncm_opts->net)) {
+ status = PTR_ERR(ncm_opts->net);
+ mutex_unlock(&ncm_opts->lock);
+ goto error;
+ }
gether_set_gadget(ncm_opts->net, cdev->gadget);
status = gether_register_netdev(ncm_opts->net);
mutex_unlock(&ncm_opts->lock);
- if (status)
- return status;
+ if (status) {
+ free_netdev(ncm_opts->net);
+ goto error;
+ }
ncm_opts->bound = true;
}
+
+ /* export host's Ethernet address in CDC format */
+ status = gether_get_host_addr_cdc(ncm_opts->net, ncm->ethaddr,
+ sizeof(ncm->ethaddr));
+ if (status < 12) { /* strlen("01234567890a") */
+ ERROR(cdev, "%s: failed to get host eth addr, err %d\n",
+ __func__, status);
+ status = -EINVAL;
+ goto netdev_cleanup;
+ }
+ ncm->port.ioport = netdev_priv(ncm_opts->net);
+
us = usb_gstrings_attach(cdev, ncm_strings,
ARRAY_SIZE(ncm_string_defs));
- if (IS_ERR(us))
- return PTR_ERR(us);
+ if (IS_ERR(us)) {
+ status = PTR_ERR(us);
+ goto netdev_cleanup;
+ }
ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
@@ -1540,7 +1562,10 @@ fail:
kfree(ncm->notify_req->buf);
usb_ep_free_request(ncm->notify, ncm->notify_req);
}
+netdev_cleanup:
+ gether_cleanup(netdev_priv(ncm_opts->net));
+error:
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
return status;
@@ -1588,8 +1613,6 @@ static void ncm_free_inst(struct usb_function_instance *f)
opts = container_of(f, struct f_ncm_opts, func_inst);
if (opts->bound)
gether_cleanup(netdev_priv(opts->net));
- else
- free_netdev(opts->net);
kfree(opts);
}
@@ -1602,12 +1625,6 @@ static struct usb_function_instance *ncm_alloc_inst(void)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = ncm_free_inst;
- opts->net = gether_setup_default();
- if (IS_ERR(opts->net)) {
- struct net_device *net = opts->net;
- kfree(opts);
- return ERR_CAST(net);
- }
config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);
@@ -1630,6 +1647,8 @@ static void ncm_free(struct usb_function *f)
static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_ncm *ncm = func_to_ncm(f);
+ struct f_ncm_opts *opts = container_of(f->fi, struct f_ncm_opts,
+ func_inst);
DBG(c->cdev, "ncm unbind\n");
@@ -1641,13 +1660,15 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(ncm->notify_req->buf);
usb_ep_free_request(ncm->notify, ncm->notify_req);
+
+ gether_cleanup(netdev_priv(opts->net));
+ opts->bound = false;
}
static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
{
struct f_ncm *ncm;
struct f_ncm_opts *opts;
- int status;
/* allocate and initialize one new instance */
ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
@@ -1657,20 +1678,9 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
opts = container_of(fi, struct f_ncm_opts, func_inst);
mutex_lock(&opts->lock);
opts->refcnt++;
-
- /* export host's Ethernet address in CDC format */
- status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
- sizeof(ncm->ethaddr));
- if (status < 12) { /* strlen("01234567890a") */
- kfree(ncm);
- mutex_unlock(&opts->lock);
- return ERR_PTR(-EINVAL);
- }
ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
-
spin_lock_init(&ncm->lock);
ncm_reset_values(ncm);
- ncm->port.ioport = netdev_priv(opts->net);
mutex_unlock(&opts->lock);
ncm->port.is_fixed = true;
ncm->port.supports_multi_frame = true;
diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c
index ac17d0aa46ae..45c39d3c4225 100644
--- a/drivers/usb/gadget/function/f_qc_rndis.c
+++ b/drivers/usb/gadget/function/f_qc_rndis.c
@@ -153,6 +153,7 @@ static unsigned int rndis_qc_bitrate(struct usb_gadget *g)
/* interface descriptor: */
+/* interface descriptor: Supports "Wireless" RNDIS; auto-detected by Windows*/
static struct usb_interface_descriptor rndis_qc_control_intf = {
.bLength = sizeof(rndis_qc_control_intf),
.bDescriptorType = USB_DT_INTERFACE,
@@ -160,9 +161,9 @@ static struct usb_interface_descriptor rndis_qc_control_intf = {
/* .bInterfaceNumber = DYNAMIC */
/* status endpoint is optional; this could be patched later */
.bNumEndpoints = 1,
- .bInterfaceClass = USB_CLASS_COMM,
- .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
- .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
+ .bInterfaceClass = USB_CLASS_WIRELESS_CONTROLLER,
+ .bInterfaceSubClass = 0x01,
+ .bInterfaceProtocol = 0x03,
/* .iInterface = DYNAMIC */
};
@@ -214,15 +215,16 @@ static struct usb_interface_descriptor rndis_qc_data_intf = {
};
+/* Supports "Wireless" RNDIS; auto-detected by Windows */
static struct usb_interface_assoc_descriptor
rndis_qc_iad_descriptor = {
.bLength = sizeof(rndis_qc_iad_descriptor),
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
.bFirstInterface = 0, /* XXX, hardcoded */
.bInterfaceCount = 2, /* control + data */
- .bFunctionClass = USB_CLASS_COMM,
- .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
- .bFunctionProtocol = USB_CDC_PROTO_NONE,
+ .bFunctionClass = USB_CLASS_WIRELESS_CONTROLLER,
+ .bFunctionSubClass = 0x01,
+ .bFunctionProtocol = 0x03,
/* .iFunction = DYNAMIC */
};
diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c
index 29263a84bbea..88db253aeef4 100644
--- a/drivers/usb/gadget/function/f_qdss.c
+++ b/drivers/usb/gadget/function/f_qdss.c
@@ -493,11 +493,7 @@ static void usb_qdss_disconnect_work(struct work_struct *work)
NULL,
NULL);
- status = set_qdss_data_connection(
- qdss->gadget,
- qdss->port.data,
- qdss->port.data->address,
- 0);
+ status = set_qdss_data_connection(qdss, 0);
if (status)
pr_err("qdss_disconnect error");
}
@@ -543,11 +539,7 @@ static void usb_qdss_connect_work(struct work_struct *work)
}
pr_debug("usb_qdss_connect_work\n");
- status = set_qdss_data_connection(
- qdss->gadget,
- qdss->port.data,
- qdss->port.data->address,
- 1);
+ status = set_qdss_data_connection(qdss, 1);
if (status) {
pr_err("set_qdss_data_connection error(%d)", status);
return;
@@ -868,14 +860,9 @@ void usb_qdss_close(struct usb_qdss_ch *ch)
if (status)
pr_err("%s: uninit_data error\n", __func__);
- status = set_qdss_data_connection(
- gadget,
- qdss->port.data,
- qdss->port.data->address,
- 0);
+ status = set_qdss_data_connection(qdss, 0);
if (status)
pr_err("%s:qdss_disconnect error\n", __func__);
- usb_gadget_restart(gadget);
}
EXPORT_SYMBOL(usb_qdss_close);
diff --git a/drivers/usb/gadget/function/f_qdss.h b/drivers/usb/gadget/function/f_qdss.h
index e3fe8ae03775..cad0f4cc06f9 100644
--- a/drivers/usb/gadget/function/f_qdss.h
+++ b/drivers/usb/gadget/function/f_qdss.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -69,6 +69,5 @@ struct usb_qdss_opts {
};
int uninit_data(struct usb_ep *ep);
-int set_qdss_data_connection(struct usb_gadget *gadget,
- struct usb_ep *data_ep, u8 data_addr, int enable);
+int set_qdss_data_connection(struct f_qdss *qdss, int enable);
#endif
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 12628dd36e55..12064d3bddf6 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -1079,13 +1079,13 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
if (!agdev->out_ep) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- goto err;
+ return ret;
}
agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
if (!agdev->in_ep) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- goto err;
+ return ret;
}
uac2->p_prm.uac2 = uac2;
@@ -1102,7 +1102,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL);
if (ret)
- goto err;
+ return ret;
prm = &agdev->uac2.c_prm;
prm->max_psize = hs_epout_desc.wMaxPacketSize;
@@ -1117,19 +1117,19 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
if (!prm->rbuf) {
prm->max_psize = 0;
- goto err_free_descs;
+ goto err;
}
ret = alsa_uac2_init(agdev);
if (ret)
- goto err_free_descs;
+ goto err;
return 0;
-err_free_descs:
- usb_free_all_descriptors(fn);
err:
kfree(agdev->uac2.p_prm.rbuf);
kfree(agdev->uac2.c_prm.rbuf);
+err_free_descs:
+ usb_free_all_descriptors(fn);
return -EINVAL;
}
diff --git a/drivers/usb/gadget/function/u_data_ipa.c b/drivers/usb/gadget/function/u_data_ipa.c
index 5718f71bcdea..d9a0b0e0b271 100644
--- a/drivers/usb/gadget/function/u_data_ipa.c
+++ b/drivers/usb/gadget/function/u_data_ipa.c
@@ -735,7 +735,7 @@ int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func,
{
struct ipa_data_ch_info *port;
unsigned long flags;
- int ret;
+ int ret = 0;
pr_debug("dev:%pK port#%d src_connection_idx:%d dst_connection_idx:%d\n",
gp, func, src_connection_idx, dst_connection_idx);
diff --git a/drivers/usb/gadget/function/u_qdss.c b/drivers/usb/gadget/function/u_qdss.c
index 42a9cda68659..0ef1e2ab34be 100644
--- a/drivers/usb/gadget/function/u_qdss.c
+++ b/drivers/usb/gadget/function/u_qdss.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -40,19 +40,25 @@ static int alloc_sps_req(struct usb_ep *data_ep)
}
static int init_data(struct usb_ep *ep);
-int set_qdss_data_connection(struct usb_gadget *gadget,
- struct usb_ep *data_ep, u8 data_addr, int enable)
+int set_qdss_data_connection(struct f_qdss *qdss, int enable)
{
enum usb_ctrl usb_bam_type;
int res = 0;
int idx;
- struct f_qdss *qdss = data_ep->driver_data;
- struct usb_qdss_bam_connect_info bam_info = qdss->bam_info;
+ struct usb_qdss_bam_connect_info bam_info;
+ struct usb_gadget *gadget;
pr_debug("set_qdss_data_connection\n");
+ if (!qdss) {
+ pr_err("%s: qdss ptr is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ gadget = qdss->gadget;
usb_bam_type = usb_bam_get_bam_type(gadget->name);
+ bam_info = qdss->bam_info;
/* There is only one qdss pipe, so the pipe number can be set to 0 */
idx = usb_bam_get_connection_idx(usb_bam_type, QDSS_P_BAM,
PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, 0);
@@ -67,14 +73,16 @@ int set_qdss_data_connection(struct usb_gadget *gadget,
kzalloc(sizeof(struct sps_mem_buffer), GFP_KERNEL);
if (!bam_info.data_fifo) {
pr_err("qdss_data_connection: memory alloc failed\n");
+ usb_bam_free_fifos(usb_bam_type, idx);
return -ENOMEM;
}
get_bam2bam_connection_info(usb_bam_type, idx,
&bam_info.usb_bam_pipe_idx,
NULL, bam_info.data_fifo, NULL);
- alloc_sps_req(data_ep);
- msm_data_fifo_config(data_ep, bam_info.data_fifo->phys_base,
+ alloc_sps_req(qdss->port.data);
+ msm_data_fifo_config(qdss->port.data,
+ bam_info.data_fifo->phys_base,
bam_info.data_fifo->size,
bam_info.usb_bam_pipe_idx);
init_data(qdss->port.data);
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 3d0d5d94a62f..0f01c04d7cbd 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -243,7 +243,7 @@ uvc_video_alloc_requests(struct uvc_video *video)
req_size = video->ep->maxpacket
* max_t(unsigned int, video->ep->maxburst, 1)
- * (video->ep->mult + 1);
+ * (video->ep->mult);
for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL);
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index e57f48f9528f..de014436fb22 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1125,7 +1125,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
/* data and/or status stage for control request */
} else if (dev->state == STATE_DEV_SETUP) {
- /* IN DATA+STATUS caller makes len <= wLength */
+ len = min_t(size_t, len, dev->setup_wLength);
if (dev->setup_in) {
retval = setup_req (dev->gadget->ep0, dev->req, len);
if (retval == 0) {
@@ -1755,10 +1755,12 @@ static struct usb_gadget_driver probe_driver = {
* such as configuration notifications.
*/
-static int is_valid_config (struct usb_config_descriptor *config)
+static int is_valid_config(struct usb_config_descriptor *config,
+ unsigned int total)
{
return config->bDescriptorType == USB_DT_CONFIG
&& config->bLength == USB_DT_CONFIG_SIZE
+ && total >= USB_DT_CONFIG_SIZE
&& config->bConfigurationValue != 0
&& (config->bmAttributes & USB_CONFIG_ATT_ONE) != 0
&& (config->bmAttributes & USB_CONFIG_ATT_WAKEUP) == 0;
@@ -1783,7 +1785,8 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
}
spin_unlock_irq(&dev->lock);
- if (len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4))
+ if ((len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) ||
+ (len > PAGE_SIZE * 4))
return -EINVAL;
/* we might need to change message format someday */
@@ -1807,7 +1810,8 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
/* full or low speed config */
dev->config = (void *) kbuf;
total = le16_to_cpu(dev->config->wTotalLength);
- if (!is_valid_config (dev->config) || total >= length)
+ if (!is_valid_config(dev->config, total) ||
+ total > length - USB_DT_DEVICE_SIZE)
goto fail;
kbuf += total;
length -= total;
@@ -1816,10 +1820,13 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
if (kbuf [1] == USB_DT_CONFIG) {
dev->hs_config = (void *) kbuf;
total = le16_to_cpu(dev->hs_config->wTotalLength);
- if (!is_valid_config (dev->hs_config) || total >= length)
+ if (!is_valid_config(dev->hs_config, total) ||
+ total > length - USB_DT_DEVICE_SIZE)
goto fail;
kbuf += total;
length -= total;
+ } else {
+ dev->hs_config = NULL;
}
/* could support multiple configs, using another encoding! */
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index dde44450dfa9..6610f7a023d3 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -330,7 +330,7 @@ static void nuke(struct dummy *dum, struct dummy_ep *ep)
/* caller must hold lock */
static void stop_activity(struct dummy *dum)
{
- struct dummy_ep *ep;
+ int i;
/* prevent any more requests */
dum->address = 0;
@@ -338,8 +338,8 @@ static void stop_activity(struct dummy *dum)
/* The timer is left running so that outstanding URBs can fail */
/* nuke any pending requests first, so driver i/o is quiesced */
- list_for_each_entry(ep, &dum->gadget.ep_list, ep.ep_list)
- nuke(dum, ep);
+ for (i = 0; i < DUMMY_ENDPOINTS; ++i)
+ nuke(dum, &dum->ep[i]);
/* driver now does any non-usb quiescing necessary */
}
@@ -1033,6 +1033,8 @@ static int dummy_udc_probe(struct platform_device *pdev)
int rc;
dum = *((void **)dev_get_platdata(&pdev->dev));
+ /* Clear usb_gadget region for new registration to udc-core */
+ memzero_explicit(&dum->gadget, sizeof(struct usb_gadget));
dum->gadget.name = gadget_name;
dum->gadget.ops = &dummy_ops;
dum->gadget.max_speed = USB_SPEED_SUPER;
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index aab5221d6c2e..aac0ce8aeb0b 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -1249,6 +1249,12 @@ static const struct usb_gadget_ops fsl_gadget_ops = {
.udc_stop = fsl_udc_stop,
};
+/*
+ * Empty complete function used by this driver to fill in the req->complete
+ * field when creating a request since the complete field is mandatory.
+ */
+static void fsl_noop_complete(struct usb_ep *ep, struct usb_request *req) { }
+
/* Set protocol stall on ep0, protocol stall will automatically be cleared
on new transaction */
static void ep0stall(struct fsl_udc *udc)
@@ -1283,7 +1289,7 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction)
req->req.length = 0;
req->req.status = -EINPROGRESS;
req->req.actual = 0;
- req->req.complete = NULL;
+ req->req.complete = fsl_noop_complete;
req->dtd_count = 0;
ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
@@ -1366,7 +1372,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
req->req.length = 2;
req->req.status = -EINPROGRESS;
req->req.actual = 0;
- req->req.complete = NULL;
+ req->req.complete = fsl_noop_complete;
req->dtd_count = 0;
ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c
index 940304c33224..02260cfdedb1 100644
--- a/drivers/usb/host/uhci-pci.c
+++ b/drivers/usb/host/uhci-pci.c
@@ -129,6 +129,10 @@ static int uhci_pci_init(struct usb_hcd *hcd)
if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP)
uhci->wait_for_hp = 1;
+ /* Intel controllers use non-PME wakeup signalling */
+ if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_INTEL)
+ device_set_run_wake(uhci_dev(uhci), 1);
+
/* Set up pointers to PCI-specific functions */
uhci->reset_hc = uhci_pci_reset_hc;
uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc;
diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c
index 34388950f96b..a190c97d11e4 100644
--- a/drivers/usb/host/xhci-dbg.c
+++ b/drivers/usb/host/xhci-dbg.c
@@ -111,7 +111,7 @@ static void xhci_print_cap_regs(struct xhci_hcd *xhci)
xhci_dbg(xhci, "RTSOFF 0x%x:\n", temp & RTSOFF_MASK);
/* xhci 1.1 controllers have the HCCPARAMS2 register */
- if (hci_version > 100) {
+ if (hci_version > 0x100) {
temp = readl(&xhci->cap_regs->hcc_params2);
xhci_dbg(xhci, "HCC PARAMS2 0x%x:\n", (unsigned int) temp);
xhci_dbg(xhci, " HC %s Force save context capability",
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 163de4bde2d8..d885033d3322 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -1529,6 +1529,35 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
return 0;
}
+/*
+ * Workaround for missing Cold Attach Status (CAS) if device re-plugged in S3.
+ * warm reset a USB3 device stuck in polling or compliance mode after resume.
+ * See Intel 100/c230 series PCH specification update Doc #332692-006 Errata #8
+ */
+static bool xhci_port_missing_cas_quirk(int port_index,
+ __le32 __iomem **port_array)
+{
+ u32 portsc;
+
+ portsc = readl(port_array[port_index]);
+
+ /* if any of these are set we are not stuck */
+ if (portsc & (PORT_CONNECT | PORT_CAS))
+ return false;
+
+ if (((portsc & PORT_PLS_MASK) != XDEV_POLLING) &&
+ ((portsc & PORT_PLS_MASK) != XDEV_COMP_MODE))
+ return false;
+
+ /* clear wakeup/change bits, and do a warm port reset */
+ portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
+ portsc |= PORT_WR;
+ writel(portsc, port_array[port_index]);
+ /* flush write */
+ readl(port_array[port_index]);
+ return true;
+}
+
int xhci_bus_resume(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
@@ -1566,6 +1595,14 @@ int xhci_bus_resume(struct usb_hcd *hcd)
u32 temp;
temp = readl(port_array[port_index]);
+
+ /* warm reset CAS limited ports stuck in polling/compliance */
+ if ((xhci->quirks & XHCI_MISSING_CAS) &&
+ (hcd->speed >= HCD_USB3) &&
+ xhci_port_missing_cas_quirk(port_index, port_array)) {
+ xhci_dbg(xhci, "reset stuck port %d\n", port_index);
+ continue;
+ }
if (DEV_SUPERSPEED_ANY(temp))
temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
else
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 8e5ab373dce9..82483599a882 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -964,6 +964,40 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
xhci->devs[slot_id] = NULL;
}
+/*
+ * Free a virt_device structure.
+ * If the virt_device added a tt_info (a hub) and has children pointing to
+ * that tt_info, then free the child first. Recursive.
+ * We can't rely on udev at this point to find child-parent relationships.
+ */
+void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id)
+{
+ struct xhci_virt_device *vdev;
+ struct list_head *tt_list_head;
+ struct xhci_tt_bw_info *tt_info, *next;
+ int i;
+
+ vdev = xhci->devs[slot_id];
+ if (!vdev)
+ return;
+
+ tt_list_head = &(xhci->rh_bw[vdev->real_port - 1].tts);
+ list_for_each_entry_safe(tt_info, next, tt_list_head, tt_list) {
+ /* is this a hub device that added a tt_info to the tts list */
+ if (tt_info->slot_id == slot_id) {
+ /* are any devices using this tt_info? */
+ for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) {
+ vdev = xhci->devs[i];
+ if (vdev && (vdev->tt_info == tt_info))
+ xhci_free_virt_devices_depth_first(
+ xhci, i);
+ }
+ }
+ }
+ /* we are now at a leaf device */
+ xhci_free_virt_device(xhci, slot_id);
+}
+
int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
struct usb_device *udev, gfp_t flags)
{
@@ -1933,7 +1967,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
struct device *dev = xhci_to_hcd(xhci)->self.controller;
int i, j, num_ports;
- del_timer_sync(&xhci->cmd_timer);
+ cancel_delayed_work_sync(&xhci->cmd_timer);
xhci_event_ring_cleanup(xhci);
@@ -1956,8 +1990,8 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
}
}
- for (i = 1; i < MAX_HC_SLOTS; ++i)
- xhci_free_virt_device(xhci, i);
+ for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--)
+ xhci_free_virt_devices_depth_first(xhci, i);
dma_pool_destroy(xhci->segment_pool);
xhci->segment_pool = NULL;
@@ -2636,9 +2670,9 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
INIT_LIST_HEAD(&xhci->cmd_list);
- /* init command timeout timer */
- setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout,
- (unsigned long)xhci);
+ /* init command timeout work */
+ INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout);
+ init_completion(&xhci->cmd_ring_stop_completion);
page_size = readl(&xhci->op_regs->page_size);
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
@@ -2677,7 +2711,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
* "physically contiguous and 64-byte (cache line) aligned".
*/
xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma,
- GFP_KERNEL);
+ flags);
if (!xhci->dcbaa)
goto fail;
memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa));
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index cf147ccac7d3..dd262f418140 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -51,6 +51,7 @@
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f
#define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI 0x0aa8
#define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8
+#define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8
static const char hcd_name[] = "xhci_hcd";
@@ -165,9 +166,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI)) {
+ pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI)) {
xhci->quirks |= XHCI_PME_STUCK_QUIRK;
}
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
+ (pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI))
+ xhci->quirks |= XHCI_MISSING_CAS;
+
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
pdev->device == PCI_DEVICE_ID_EJ168) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 05d96fd8c07c..7423645c204c 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -38,12 +38,19 @@ static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
{
+ struct device_node *node = dev->of_node;
+ struct usb_xhci_pdata *pdata = dev_get_platdata(dev);
+
/*
* As of now platform drivers don't provide MSI support so we ensure
* here that the generic code does not try to make a pci_dev from our
* dev struct in order to setup MSI
*/
xhci->quirks |= XHCI_PLAT;
+
+ if ((node && of_property_read_bool(node, "usb3-lpm-capable")) ||
+ (pdata && pdata->usb3_lpm_capable))
+ xhci->quirks |= XHCI_LPM_SUPPORT;
}
/* called during probe() after chip reset completes */
@@ -129,7 +136,6 @@ static DEVICE_ATTR(config_imod, S_IRUGO | S_IWUSR,
static int xhci_plat_probe(struct platform_device *pdev)
{
- struct device_node *node = pdev->dev.of_node;
struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev);
const struct hc_driver *driver;
struct xhci_hcd *xhci;
@@ -227,13 +233,6 @@ static int xhci_plat_probe(struct platform_device *pdev)
hcd_to_bus(xhci->shared_hcd)->skip_resume = true;
- if ((node && of_property_read_bool(node, "usb3-lpm-capable")) ||
- (pdata && pdata->usb3_lpm_capable))
- xhci->quirks |= XHCI_LPM_SUPPORT;
-
- if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
- xhci->shared_hcd->can_do_streams = 1;
-
hcd->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
if (IS_ERR(hcd->usb_phy)) {
ret = PTR_ERR(hcd->usb_phy);
@@ -252,6 +251,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
device_wakeup_enable(&hcd->self.root_hub->dev);
+ if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
+ xhci->shared_hcd->can_do_streams = 1;
+
ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED | IRQF_ONESHOT);
if (ret)
goto dealloc_usb2_hcd;
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 98d01acc8b11..12e38cf022ff 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -280,23 +280,76 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci)
readl(&xhci->dba->doorbell[0]);
}
-static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
+static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci, unsigned long delay)
+{
+ return mod_delayed_work(system_wq, &xhci->cmd_timer, delay);
+}
+
+static struct xhci_command *xhci_next_queued_cmd(struct xhci_hcd *xhci)
+{
+ return list_first_entry_or_null(&xhci->cmd_list, struct xhci_command,
+ cmd_list);
+}
+
+/*
+ * Turn all commands on command ring with status set to "aborted" to no-op trbs.
+ * If there are other commands waiting then restart the ring and kick the timer.
+ * This must be called with command ring stopped and xhci->lock held.
+ */
+static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
+ struct xhci_command *cur_cmd)
+{
+ struct xhci_command *i_cmd;
+ u32 cycle_state;
+
+ /* Turn all aborted commands in list to no-ops, then restart */
+ list_for_each_entry(i_cmd, &xhci->cmd_list, cmd_list) {
+
+ if (i_cmd->status != COMP_CMD_ABORT)
+ continue;
+
+ i_cmd->status = COMP_CMD_STOP;
+
+ xhci_dbg(xhci, "Turn aborted command %pK to no-op\n",
+ i_cmd->command_trb);
+ /* get cycle state from the original cmd trb */
+ cycle_state = le32_to_cpu(
+ i_cmd->command_trb->generic.field[3]) & TRB_CYCLE;
+ /* modify the command trb to no-op command */
+ i_cmd->command_trb->generic.field[0] = 0;
+ i_cmd->command_trb->generic.field[1] = 0;
+ i_cmd->command_trb->generic.field[2] = 0;
+ i_cmd->command_trb->generic.field[3] = cpu_to_le32(
+ TRB_TYPE(TRB_CMD_NOOP) | cycle_state);
+
+ /*
+ * caller waiting for completion is called when command
+ * completion event is received for these no-op commands
+ */
+ }
+
+ xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
+
+ /* ring command ring doorbell to restart the command ring */
+ if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) &&
+ !(xhci->xhc_state & XHCI_STATE_DYING)) {
+ xhci->current_cmd = cur_cmd;
+ xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
+ xhci_ring_cmd_db(xhci);
+ }
+}
+
+/* Must be called with xhci->lock held, releases and aquires lock back */
+static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags)
{
u64 temp_64;
int ret;
xhci_dbg(xhci, "Abort command ring\n");
- temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
- xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
+ reinit_completion(&xhci->cmd_ring_stop_completion);
- /*
- * Writing the CMD_RING_ABORT bit should cause a cmd completion event,
- * however on some host hw the CMD_RING_RUNNING bit is correctly cleared
- * but the completion event in never sent. Use the cmd timeout timer to
- * handle those cases. Use twice the time to cover the bit polling retry
- */
- mod_timer(&xhci->cmd_timer, jiffies + (2 * XHCI_CMD_DEFAULT_TIMEOUT));
+ temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
&xhci->op_regs->cmd_ring);
@@ -316,16 +369,30 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
udelay(1000);
ret = xhci_handshake(&xhci->op_regs->cmd_ring,
CMD_RING_RUNNING, 0, 3 * 1000 * 1000);
- if (ret == 0)
- return 0;
-
- xhci_err(xhci, "Stopped the command ring failed, "
- "maybe the host is dead\n");
- del_timer(&xhci->cmd_timer);
- xhci->xhc_state |= XHCI_STATE_DYING;
- xhci_quiesce(xhci);
- xhci_halt(xhci);
- return -ESHUTDOWN;
+ if (ret < 0) {
+ xhci_err(xhci, "Stopped the command ring failed, "
+ "maybe the host is dead\n");
+ xhci->xhc_state |= XHCI_STATE_DYING;
+ xhci_quiesce(xhci);
+ xhci_halt(xhci);
+ return -ESHUTDOWN;
+ }
+ }
+ /*
+ * Writing the CMD_RING_ABORT bit should cause a cmd completion event,
+ * however on some host hw the CMD_RING_RUNNING bit is correctly cleared
+ * but the completion event in never sent. Wait 2 secs (arbitrary
+ * number) to handle those cases after negation of CMD_RING_RUNNING.
+ */
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ ret = wait_for_completion_timeout(&xhci->cmd_ring_stop_completion,
+ msecs_to_jiffies(2000));
+ spin_lock_irqsave(&xhci->lock, flags);
+ if (!ret) {
+ xhci_dbg(xhci, "No stop event for abort, ring start fail?\n");
+ xhci_cleanup_command_queue(xhci);
+ } else {
+ xhci_handle_stopped_cmd_ring(xhci, xhci_next_queued_cmd(xhci));
}
return 0;
@@ -846,17 +913,6 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
spin_lock_irqsave(&xhci->lock, flags);
ep->stop_cmds_pending--;
- if (xhci->xhc_state & XHCI_STATE_REMOVING) {
- spin_unlock_irqrestore(&xhci->lock, flags);
- return;
- }
- if (xhci->xhc_state & XHCI_STATE_DYING) {
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Stop EP timer ran, but another timer marked "
- "xHCI as DYING, exiting.");
- spin_unlock_irqrestore(&xhci->lock, flags);
- return;
- }
if (!(ep->stop_cmds_pending == 0 && (ep->ep_state & EP_HALT_PENDING))) {
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Stop EP timer ran, but no command pending, "
@@ -1208,101 +1264,62 @@ void xhci_cleanup_command_queue(struct xhci_hcd *xhci)
xhci_complete_del_and_free_cmd(cur_cmd, COMP_CMD_ABORT);
}
-/*
- * Turn all commands on command ring with status set to "aborted" to no-op trbs.
- * If there are other commands waiting then restart the ring and kick the timer.
- * This must be called with command ring stopped and xhci->lock held.
- */
-static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
- struct xhci_command *cur_cmd)
-{
- struct xhci_command *i_cmd, *tmp_cmd;
- u32 cycle_state;
-
- /* Turn all aborted commands in list to no-ops, then restart */
- list_for_each_entry_safe(i_cmd, tmp_cmd, &xhci->cmd_list,
- cmd_list) {
-
- if (i_cmd->status != COMP_CMD_ABORT)
- continue;
-
- i_cmd->status = COMP_CMD_STOP;
-
- xhci_dbg(xhci, "Turn aborted command %pK to no-op\n",
- i_cmd->command_trb);
- /* get cycle state from the original cmd trb */
- cycle_state = le32_to_cpu(
- i_cmd->command_trb->generic.field[3]) & TRB_CYCLE;
- /* modify the command trb to no-op command */
- i_cmd->command_trb->generic.field[0] = 0;
- i_cmd->command_trb->generic.field[1] = 0;
- i_cmd->command_trb->generic.field[2] = 0;
- i_cmd->command_trb->generic.field[3] = cpu_to_le32(
- TRB_TYPE(TRB_CMD_NOOP) | cycle_state);
-
- /*
- * caller waiting for completion is called when command
- * completion event is received for these no-op commands
- */
- }
-
- xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
-
- /* ring command ring doorbell to restart the command ring */
- if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) &&
- !(xhci->xhc_state & XHCI_STATE_DYING)) {
- xhci->current_cmd = cur_cmd;
- mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
- xhci_ring_cmd_db(xhci);
- }
- return;
-}
-
-
-void xhci_handle_command_timeout(unsigned long data)
+void xhci_handle_command_timeout(struct work_struct *work)
{
struct xhci_hcd *xhci;
int ret;
unsigned long flags;
u64 hw_ring_state;
- bool second_timeout = false;
- xhci = (struct xhci_hcd *) data;
- /* mark this command to be cancelled */
+ xhci = container_of(to_delayed_work(work), struct xhci_hcd, cmd_timer);
+
spin_lock_irqsave(&xhci->lock, flags);
- if (xhci->current_cmd) {
- if (xhci->current_cmd->status == COMP_CMD_ABORT)
- second_timeout = true;
- xhci->current_cmd->status = COMP_CMD_ABORT;
+
+ /*
+ * If timeout work is pending, or current_cmd is NULL, it means we
+ * raced with command completion. Command is handled so just return.
+ */
+ if (!xhci->current_cmd || delayed_work_pending(&xhci->cmd_timer)) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return;
}
+ /* mark this command to be cancelled */
+ xhci->current_cmd->status = COMP_CMD_ABORT;
/* Make sure command ring is running before aborting it */
hw_ring_state = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
if ((xhci->cmd_ring_state & CMD_RING_STATE_RUNNING) &&
(hw_ring_state & CMD_RING_RUNNING)) {
- spin_unlock_irqrestore(&xhci->lock, flags);
+ /* Prevent new doorbell, and start command abort */
+ xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
xhci_dbg(xhci, "Command timeout\n");
- ret = xhci_abort_cmd_ring(xhci);
+ ret = xhci_abort_cmd_ring(xhci, flags);
if (unlikely(ret == -ESHUTDOWN)) {
xhci_err(xhci, "Abort command ring failed\n");
xhci_cleanup_command_queue(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
xhci_dbg(xhci, "xHCI host controller is dead.\n");
+
+ return;
}
- return;
+
+ goto time_out_completed;
}
- /* command ring failed to restart, or host removed. Bail out */
- if (second_timeout || xhci->xhc_state & XHCI_STATE_REMOVING) {
- spin_unlock_irqrestore(&xhci->lock, flags);
- xhci_dbg(xhci, "command timed out twice, ring start fail?\n");
+ /* host removed. Bail out */
+ if (xhci->xhc_state & XHCI_STATE_REMOVING) {
+ xhci_dbg(xhci, "host removed, ring start fail?\n");
xhci_cleanup_command_queue(xhci);
- return;
+
+ goto time_out_completed;
}
/* command timeout on stopped ring, ring can't be aborted */
xhci_dbg(xhci, "Command timeout on stopped ring\n");
xhci_handle_stopped_cmd_ring(xhci, xhci->current_cmd);
+
+time_out_completed:
spin_unlock_irqrestore(&xhci->lock, flags);
return;
}
@@ -1335,7 +1352,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
cmd = list_entry(xhci->cmd_list.next, struct xhci_command, cmd_list);
- del_timer(&xhci->cmd_timer);
+ cancel_delayed_work(&xhci->cmd_timer);
trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event);
@@ -1343,7 +1360,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
/* If CMD ring stopped we own the trbs between enqueue and dequeue */
if (cmd_comp_code == COMP_CMD_STOP) {
- xhci_handle_stopped_cmd_ring(xhci, cmd);
+ complete_all(&xhci->cmd_ring_stop_completion);
return;
}
@@ -1361,8 +1378,11 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
*/
if (cmd_comp_code == COMP_CMD_ABORT) {
xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
- if (cmd->status == COMP_CMD_ABORT)
+ if (cmd->status == COMP_CMD_ABORT) {
+ if (xhci->current_cmd == cmd)
+ xhci->current_cmd = NULL;
goto event_handled;
+ }
}
cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3]));
@@ -1423,7 +1443,9 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
if (cmd->cmd_list.next != &xhci->cmd_list) {
xhci->current_cmd = list_entry(cmd->cmd_list.next,
struct xhci_command, cmd_list);
- mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
+ xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
+ } else if (xhci->current_cmd == cmd) {
+ xhci->current_cmd = NULL;
}
event_handled:
@@ -4206,9 +4228,9 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
/* if there are no other commands queued we start the timeout timer */
if (xhci->cmd_list.next == &cmd->cmd_list &&
- !timer_pending(&xhci->cmd_timer)) {
+ !delayed_work_pending(&xhci->cmd_timer)) {
xhci->current_cmd = cmd;
- mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
+ xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
}
queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3,
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 8c6bb15ef51f..aab1c7903288 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -121,10 +121,10 @@ int xhci_halt(struct xhci_hcd *xhci)
xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
- if (timer_pending(&xhci->cmd_timer)) {
+ if (delayed_work_pending(&xhci->cmd_timer)) {
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Cleanup command queue");
- del_timer(&xhci->cmd_timer);
+ cancel_delayed_work(&xhci->cmd_timer);
xhci_cleanup_command_queue(xhci);
}
@@ -138,7 +138,13 @@ static int xhci_start(struct xhci_hcd *xhci)
{
u32 temp;
int ret;
+ struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ /*
+ * disable irq to avoid xhci_irq flooding due to unhandeled port
+ * change event in halt state, as soon as xhci_start clears halt bit
+ */
+ disable_irq(hcd->irq);
temp = readl(&xhci->op_regs->command);
temp |= (CMD_RUN);
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Turn on HC, cmd = 0x%x.",
@@ -159,6 +165,8 @@ static int xhci_start(struct xhci_hcd *xhci)
/* clear state flags. Including dying, halted or removing */
xhci->xhc_state = 0;
+ enable_irq(hcd->irq);
+
return ret;
}
@@ -1579,19 +1587,6 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
xhci_urb_free_priv(urb_priv);
return ret;
}
- if ((xhci->xhc_state & XHCI_STATE_DYING) ||
- (xhci->xhc_state & XHCI_STATE_HALTED)) {
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Ep 0x%x: URB %pK to be canceled on "
- "non-responsive xHCI host.",
- urb->ep->desc.bEndpointAddress, urb);
- /* Let the stop endpoint command watchdog timer (which set this
- * state) finish cleaning up the endpoint TD lists. We must
- * have caught it in the middle of dropping a lock and giving
- * back an URB.
- */
- goto done;
- }
ep_index = xhci_get_endpoint_index(&urb->ep->desc);
ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index];
@@ -3818,8 +3813,10 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
mutex_lock(&xhci->mutex);
- if (xhci->xhc_state) /* dying, removing or halted */
+ if (xhci->xhc_state) { /* dying, removing or halted */
+ ret = -ESHUTDOWN;
goto out;
+ }
if (!udev->slot_id) {
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 097cfa9e4692..8fcec1be6b1a 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -312,6 +312,8 @@ struct xhci_op_regs {
#define XDEV_U2 (0x2 << 5)
#define XDEV_U3 (0x3 << 5)
#define XDEV_INACTIVE (0x6 << 5)
+#define XDEV_POLLING (0x7 << 5)
+#define XDEV_COMP_MODE (0xa << 5)
#define XDEV_RESUME (0xf << 5)
/* true: port has power (see HCC_PPC) */
#define PORT_POWER (1 << 9)
@@ -1553,7 +1555,8 @@ struct xhci_hcd {
#define CMD_RING_STATE_STOPPED (1 << 2)
struct list_head cmd_list;
unsigned int cmd_ring_reserved_trbs;
- struct timer_list cmd_timer;
+ struct delayed_work cmd_timer;
+ struct completion cmd_ring_stop_completion;
struct xhci_command *current_cmd;
struct xhci_ring *event_ring;
struct xhci_erst erst;
@@ -1639,6 +1642,7 @@ struct xhci_hcd {
/* For controllers with a broken beyond repair streams implementation */
#define XHCI_BROKEN_STREAMS (1 << 19)
#define XHCI_PME_STUCK_QUIRK (1 << 20)
+#define XHCI_MISSING_CAS (1 << 24)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
@@ -1922,7 +1926,7 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
struct xhci_dequeue_state *deq_state);
void xhci_stop_endpoint_command_watchdog(unsigned long arg);
-void xhci_handle_command_timeout(unsigned long data);
+void xhci_handle_command_timeout(struct work_struct *work);
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
unsigned int ep_index, unsigned int stream_id);
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index 1950e87b4219..775690bed4c0 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -787,12 +787,6 @@ static int iowarrior_probe(struct usb_interface *interface,
iface_desc = interface->cur_altsetting;
dev->product_id = le16_to_cpu(udev->descriptor.idProduct);
- if (iface_desc->desc.bNumEndpoints < 1) {
- dev_err(&interface->dev, "Invalid number of endpoints\n");
- retval = -EINVAL;
- goto error;
- }
-
/* set up the endpoint information */
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
@@ -803,6 +797,21 @@ static int iowarrior_probe(struct usb_interface *interface,
/* this one will match for the IOWarrior56 only */
dev->int_out_endpoint = endpoint;
}
+
+ if (!dev->int_in_endpoint) {
+ dev_err(&interface->dev, "no interrupt-in endpoint found\n");
+ retval = -ENODEV;
+ goto error;
+ }
+
+ if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56) {
+ if (!dev->int_out_endpoint) {
+ dev_err(&interface->dev, "no interrupt-out endpoint found\n");
+ retval = -ENODEV;
+ goto error;
+ }
+ }
+
/* we have to check the report_size often, so remember it in the endianness suitable for our machine */
dev->report_size = usb_endpoint_maxp(dev->int_in_endpoint);
if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) &&
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c
index 310238c6b5cd..896798071817 100644
--- a/drivers/usb/musb/blackfin.c
+++ b/drivers/usb/musb/blackfin.c
@@ -469,6 +469,7 @@ static const struct musb_platform_ops bfin_ops = {
.init = bfin_musb_init,
.exit = bfin_musb_exit,
+ .fifo_offset = bfin_fifo_offset,
.readb = bfin_readb,
.writeb = bfin_writeb,
.readw = bfin_readw,
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c
index b03d3b867fca..9a9c82a4d35d 100644
--- a/drivers/usb/musb/da8xx.c
+++ b/drivers/usb/musb/da8xx.c
@@ -458,15 +458,11 @@ static int da8xx_musb_exit(struct musb *musb)
}
static const struct musb_platform_ops da8xx_ops = {
- .quirks = MUSB_DMA_CPPI | MUSB_INDEXED_EP,
+ .quirks = MUSB_INDEXED_EP,
.init = da8xx_musb_init,
.exit = da8xx_musb_exit,
.fifo_mode = 2,
-#ifdef CONFIG_USB_TI_CPPI_DMA
- .dma_init = cppi_dma_controller_create,
- .dma_exit = cppi_dma_controller_destroy,
-#endif
.enable = da8xx_musb_enable,
.disable = da8xx_musb_disable,
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 2337d7a7d62d..90de7900e4b8 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -214,6 +214,7 @@ struct musb_platform_ops {
dma_addr_t *dma_addr, u32 *len);
void (*pre_root_reset_end)(struct musb *musb);
void (*post_root_reset_end)(struct musb *musb);
+ void (*clear_ep_rxintr)(struct musb *musb, int epnum);
};
/*
@@ -612,4 +613,10 @@ static inline void musb_platform_post_root_reset_end(struct musb *musb)
musb->ops->post_root_reset_end(musb);
}
+static inline void musb_platform_clear_ep_rxintr(struct musb *musb, int epnum)
+{
+ if (musb->ops->clear_ep_rxintr)
+ musb->ops->clear_ep_rxintr(musb, epnum);
+}
+
#endif /* __MUSB_CORE_H__ */
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index eeb7d9ecf7df..5a021b26d7d2 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -301,6 +301,17 @@ static void otg_timer(unsigned long _musb)
spin_unlock_irqrestore(&musb->lock, flags);
}
+void dsps_musb_clear_ep_rxintr(struct musb *musb, int epnum)
+{
+ u32 epintr;
+ struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent);
+ const struct dsps_musb_wrapper *wrp = glue->wrp;
+
+ /* musb->lock might already been held */
+ epintr = (1 << epnum) << wrp->rxep_shift;
+ musb_writel(musb->ctrl_base, wrp->epintr_status, epintr);
+}
+
static irqreturn_t dsps_interrupt(int irq, void *hci)
{
struct musb *musb = hci;
@@ -647,6 +658,7 @@ static struct musb_platform_ops dsps_ops = {
.try_idle = dsps_musb_try_idle,
.set_mode = dsps_musb_set_mode,
.recover = dsps_musb_recover,
+ .clear_ep_rxintr = dsps_musb_clear_ep_rxintr,
};
static u64 musb_dmamask = DMA_BIT_MASK(32);
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index e0a083f6ab68..13d5614f37f1 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -2390,12 +2390,11 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh)
int is_in = usb_pipein(urb->pipe);
int status = 0;
u16 csr;
+ struct dma_channel *dma = NULL;
musb_ep_select(regs, hw_end);
if (is_dma_capable()) {
- struct dma_channel *dma;
-
dma = is_in ? ep->rx_channel : ep->tx_channel;
if (dma) {
status = ep->musb->dma_controller->channel_abort(dma);
@@ -2412,10 +2411,9 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh)
/* giveback saves bulk toggle */
csr = musb_h_flush_rxfifo(ep, 0);
- /* REVISIT we still get an irq; should likely clear the
- * endpoint's irq status here to avoid bogus irqs.
- * clearing that status is platform-specific...
- */
+ /* clear the endpoint's irq status here to avoid bogus irqs */
+ if (is_dma_capable() && dma)
+ musb_platform_clear_ep_rxintr(musb, ep->epnum);
} else if (ep->epnum) {
musb_h_tx_flush_fifo(ep);
csr = musb_readw(epio, MUSB_TXCSR);
diff --git a/drivers/usb/musb/musbhsdma.h b/drivers/usb/musb/musbhsdma.h
index f7b13fd25257..a3dcbd55e436 100644
--- a/drivers/usb/musb/musbhsdma.h
+++ b/drivers/usb/musb/musbhsdma.h
@@ -157,5 +157,5 @@ struct musb_dma_controller {
void __iomem *base;
u8 channel_count;
u8 used_channels;
- u8 irq;
+ int irq;
};
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 935bd0778bfb..c76ca5a94557 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -1417,6 +1417,7 @@ static void dr_swap(struct usbpd *pd)
}
pd_phy_update_roles(pd->current_dr, pd->current_pr);
+ dual_role_instance_changed(pd->dual_role);
}
@@ -2656,11 +2657,17 @@ static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role,
static int usbpd_dr_prop_writeable(struct dual_role_phy_instance *dual_role,
enum dual_role_property prop)
{
+ struct usbpd *pd = dual_role_get_drvdata(dual_role);
+
switch (prop) {
case DUAL_ROLE_PROP_MODE:
+ return 1;
case DUAL_ROLE_PROP_DR:
case DUAL_ROLE_PROP_PR:
- return 1;
+ if (pd)
+ return pd->current_state == PE_SNK_READY ||
+ pd->current_state == PE_SRC_READY;
+ break;
default:
break;
}
diff --git a/drivers/usb/phy/class-dual-role.c b/drivers/usb/phy/class-dual-role.c
index 51fcb545a9d5..9ef889593ef5 100644
--- a/drivers/usb/phy/class-dual-role.c
+++ b/drivers/usb/phy/class-dual-role.c
@@ -70,15 +70,7 @@ static char *kstrdupcase(const char *str, gfp_t gfp, bool to_upper)
return ret;
}
-static void dual_role_changed_work(struct work_struct *work)
-{
- struct dual_role_phy_instance *dual_role =
- container_of(work, struct dual_role_phy_instance,
- changed_work);
-
- dev_dbg(&dual_role->dev, "%s\n", __func__);
- kobject_uevent(&dual_role->dev.kobj, KOBJ_CHANGE);
-}
+static void dual_role_changed_work(struct work_struct *work);
void dual_role_instance_changed(struct dual_role_phy_instance *dual_role)
{
@@ -505,6 +497,17 @@ out:
return ret;
}
+static void dual_role_changed_work(struct work_struct *work)
+{
+ struct dual_role_phy_instance *dual_role =
+ container_of(work, struct dual_role_phy_instance,
+ changed_work);
+
+ dev_dbg(&dual_role->dev, "%s\n", __func__);
+ sysfs_update_group(&dual_role->dev.kobj, &dual_role_attr_group);
+ kobject_uevent(&dual_role->dev.kobj, KOBJ_CHANGE);
+}
+
/******************* Module Init ***********************************/
static int __init dual_role_class_init(void)
diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c
index 7b3035ff9434..1b4d742a2397 100644
--- a/drivers/usb/phy/phy-am335x-control.c
+++ b/drivers/usb/phy/phy-am335x-control.c
@@ -126,10 +126,12 @@ struct phy_control *am335x_get_phy_control(struct device *dev)
return NULL;
dev = bus_find_device(&platform_bus_type, NULL, node, match);
+ of_node_put(node);
if (!dev)
return NULL;
ctrl_usb = dev_get_drvdata(dev);
+ put_device(dev);
if (!ctrl_usb)
return NULL;
return &ctrl_usb->phy_ctrl;
diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c
index 64916f5566b5..aa11cf2f7417 100644
--- a/drivers/usb/phy/phy-msm-ssusb-qmp.c
+++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -27,10 +27,10 @@
#include <linux/clk/msm-clk.h>
#include <linux/reset.h>
-enum core_ldo_levels {
- CORE_LEVEL_NONE = 0,
- CORE_LEVEL_MIN,
- CORE_LEVEL_MAX,
+enum ldo_levels {
+ VOLTAGE_LEVEL_NONE = 0,
+ VOLTAGE_LEVEL_MIN,
+ VOLTAGE_LEVEL_MAX,
};
#define INIT_MAX_TIME_USEC 1000
@@ -40,6 +40,8 @@ enum core_ldo_levels {
#define USB_SSPHY_1P2_VOL_MAX 1200000 /* uV */
#define USB_SSPHY_HPM_LOAD 23000 /* uA */
+#define USB_SSPHY_LOAD_DEFAULT -1
+
/* USB3PHY_PCIE_USB3_PCS_PCS_STATUS bit */
#define PHYSTATUS BIT(6)
@@ -83,6 +85,9 @@ struct msm_ssphy_qmp {
int vdd_levels[3]; /* none, low, high */
struct regulator *core_ldo;
int core_voltage_levels[3];
+ struct regulator *fpc_redrive_ldo;
+ int redrive_voltage_levels[3];
+ int redrive_load;
struct clk *ref_clk_src;
struct clk *ref_clk;
struct clk *aux_clk;
@@ -162,6 +167,33 @@ static void msm_ssusb_qmp_enable_autonomous(struct msm_ssphy_qmp *phy,
}
}
+static int msm_ldo_enable(struct msm_ssphy_qmp *phy,
+ struct regulator *ldo, int *voltage_levels, int load)
+{
+ int ret = 0;
+
+ dev_dbg(phy->phy.dev,
+ "ldo: min_vol:%duV max_vol:%duV\n",
+ voltage_levels[VOLTAGE_LEVEL_MIN],
+ voltage_levels[VOLTAGE_LEVEL_MAX]);
+
+ if (load > 0) {
+ ret = regulator_set_load(ldo, load);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = regulator_set_voltage(ldo,
+ voltage_levels[VOLTAGE_LEVEL_MIN],
+ voltage_levels[VOLTAGE_LEVEL_MAX]);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(ldo);
+
+ return ret;
+}
+
static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on)
{
int min, rc = 0;
@@ -181,74 +213,65 @@ static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on)
if (!on)
goto disable_regulators;
- rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min],
- phy->vdd_levels[2]);
- if (rc) {
- dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n");
- return rc;
- }
-
- dev_dbg(phy->phy.dev, "min_vol:%d max_vol:%d\n",
- phy->vdd_levels[min], phy->vdd_levels[2]);
+ if (phy->fpc_redrive_ldo) {
+ rc = msm_ldo_enable(phy, phy->fpc_redrive_ldo,
+ phy->redrive_voltage_levels,
+ phy->redrive_load);
+ if (rc < 0) {
+ dev_err(phy->phy.dev,
+ "enable phy->fpc_redrive_ldo failed\n");
+ return rc;
+ }
- rc = regulator_enable(phy->vdd);
- if (rc) {
- dev_err(phy->phy.dev,
- "regulator_enable(phy->vdd) failed, ret=%d",
- rc);
- goto unconfig_vdd;
+ dev_dbg(phy->phy.dev,
+ "fpc redrive ldo: min_vol:%duV max_vol:%duV\n",
+ phy->redrive_voltage_levels[VOLTAGE_LEVEL_MIN],
+ phy->redrive_voltage_levels[VOLTAGE_LEVEL_MAX]);
}
- rc = regulator_set_load(phy->core_ldo, USB_SSPHY_HPM_LOAD);
+ rc = msm_ldo_enable(phy, phy->vdd, phy->vdd_levels,
+ USB_SSPHY_LOAD_DEFAULT);
if (rc < 0) {
- dev_err(phy->phy.dev, "Unable to set HPM of core_ldo\n");
- goto disable_vdd;
+ dev_err(phy->phy.dev, "enable phy->vdd failed\n");
+ goto disable_fpc_redrive;
}
- rc = regulator_set_voltage(phy->core_ldo,
- phy->core_voltage_levels[CORE_LEVEL_MIN],
- phy->core_voltage_levels[CORE_LEVEL_MAX]);
- if (rc) {
- dev_err(phy->phy.dev, "unable to set voltage for core_ldo\n");
- goto put_core_ldo_lpm;
- }
+ dev_dbg(phy->phy.dev,
+ "vdd ldo: min_vol:%duV max_vol:%duV\n",
+ phy->vdd_levels[VOLTAGE_LEVEL_MIN],
+ phy->vdd_levels[VOLTAGE_LEVEL_MAX]);
- rc = regulator_enable(phy->core_ldo);
- if (rc) {
- dev_err(phy->phy.dev, "Unable to enable core_ldo\n");
- goto unset_core_ldo;
+ rc = msm_ldo_enable(phy, phy->core_ldo, phy->core_voltage_levels,
+ USB_SSPHY_HPM_LOAD);
+ if (rc < 0) {
+ dev_err(phy->phy.dev, "enable phy->core_ldo failed\n");
+ goto disable_vdd;
}
+ dev_dbg(phy->phy.dev,
+ "core ldo: min_vol:%duV max_vol:%duV\n",
+ phy->core_voltage_levels[VOLTAGE_LEVEL_MIN],
+ phy->core_voltage_levels[VOLTAGE_LEVEL_MAX]);
+
return 0;
disable_regulators:
rc = regulator_disable(phy->core_ldo);
if (rc)
- dev_err(phy->phy.dev, "Unable to disable core_ldo\n");
-
-unset_core_ldo:
- rc = regulator_set_voltage(phy->core_ldo,
- phy->core_voltage_levels[CORE_LEVEL_NONE],
- phy->core_voltage_levels[CORE_LEVEL_MAX]);
- if (rc)
- dev_err(phy->phy.dev, "unable to set voltage for core_ldo\n");
-
-put_core_ldo_lpm:
- rc = regulator_set_load(phy->core_ldo, 0);
- if (rc < 0)
- dev_err(phy->phy.dev, "Unable to set LPM of core_ldo\n");
+ dev_err(phy->phy.dev, "disable phy->core_ldo failed\n");
disable_vdd:
rc = regulator_disable(phy->vdd);
if (rc)
- dev_err(phy->phy.dev, "regulator_disable(phy->vdd) failed, ret=%d",
- rc);
+ dev_err(phy->phy.dev, "disable phy->vdd failed\n");
-unconfig_vdd:
- rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min],
- phy->vdd_levels[2]);
- if (rc)
- dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n");
+disable_fpc_redrive:
+ if (phy->fpc_redrive_ldo) {
+ rc = regulator_disable(phy->fpc_redrive_ldo);
+ if (rc)
+ dev_err(phy->phy.dev,
+ "disable phy->fpc_redrive_ldo failed\n");
+ }
return rc < 0 ? rc : 0;
}
@@ -307,10 +330,6 @@ static int msm_ssphy_qmp_init(struct usb_phy *uphy)
phy->clk_enabled = true;
}
- /* select usb3 phy mode */
- if (phy->tcsr_usb3_dp_phymode)
- writel_relaxed(0x0, phy->tcsr_usb3_dp_phymode);
-
writel_relaxed(0x01,
phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]);
@@ -386,6 +405,10 @@ static int msm_ssphy_qmp_reset(struct usb_phy *uphy)
goto deassert_phy_phy_reset;
}
+ /* select usb3 phy mode */
+ if (phy->tcsr_usb3_dp_phymode)
+ writel_relaxed(0x0, phy->tcsr_usb3_dp_phymode);
+
/* Deassert USB3 PHY CSR reset */
ret = reset_control_deassert(phy->phy_reset);
if (ret) {
@@ -683,9 +706,9 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev)
}
/* Set default core voltage values */
- phy->core_voltage_levels[CORE_LEVEL_NONE] = 0;
- phy->core_voltage_levels[CORE_LEVEL_MIN] = USB_SSPHY_1P2_VOL_MIN;
- phy->core_voltage_levels[CORE_LEVEL_MAX] = USB_SSPHY_1P2_VOL_MAX;
+ phy->core_voltage_levels[VOLTAGE_LEVEL_NONE] = 0;
+ phy->core_voltage_levels[VOLTAGE_LEVEL_MIN] = USB_SSPHY_1P2_VOL_MIN;
+ phy->core_voltage_levels[VOLTAGE_LEVEL_MAX] = USB_SSPHY_1P2_VOL_MAX;
if (of_get_property(dev->of_node, "qcom,core-voltage-level", &len) &&
len == sizeof(phy->core_voltage_levels)) {
@@ -729,6 +752,39 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev)
goto err;
}
+ phy->fpc_redrive_ldo = devm_regulator_get_optional(dev, "fpc-redrive");
+ if (IS_ERR(phy->fpc_redrive_ldo)) {
+ phy->fpc_redrive_ldo = NULL;
+ dev_dbg(dev, "no FPC re-drive ldo regulator\n");
+ } else {
+ if (of_get_property(dev->of_node,
+ "qcom,redrive-voltage-level", &len) &&
+ len == sizeof(phy->redrive_voltage_levels)) {
+ ret = of_property_read_u32_array(dev->of_node,
+ "qcom,redrive-voltage-level",
+ (u32 *) phy->redrive_voltage_levels,
+ len / sizeof(u32));
+ if (ret) {
+ dev_err(dev,
+ "err qcom,redrive-voltage-level\n");
+ goto err;
+ }
+ } else {
+ ret = -EINVAL;
+ dev_err(dev, "err inputs for redrive-voltage-level\n");
+ goto err;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "qcom,redrive-load",
+ &phy->redrive_load);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to read redrive load\n");
+ goto err;
+ }
+
+ dev_dbg(dev, "Get FPC re-drive ldo regulator\n");
+ }
+
phy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
if (IS_ERR(phy->ref_clk_src))
phy->ref_clk_src = NULL;
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index 1532cde8a437..7812052dc700 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -99,10 +99,17 @@ static int ark3116_read_reg(struct usb_serial *serial,
usb_rcvctrlpipe(serial->dev, 0),
0xfe, 0xc0, 0, reg,
buf, 1, ARK_TIMEOUT);
- if (result < 0)
+ if (result < 1) {
+ dev_err(&serial->interface->dev,
+ "failed to read register %u: %d\n",
+ reg, result);
+ if (result >= 0)
+ result = -EIO;
+
return result;
- else
- return buf[0];
+ }
+
+ return buf[0];
}
static inline int calc_divisor(int bps)
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index c73808f095bb..71133d96f97d 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -99,6 +99,8 @@ static int ch341_control_out(struct usb_device *dev, u8 request,
r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
value, index, NULL, 0, DEFAULT_TIMEOUT);
+ if (r < 0)
+ dev_err(&dev->dev, "failed to send control message: %d\n", r);
return r;
}
@@ -116,7 +118,20 @@ static int ch341_control_in(struct usb_device *dev,
r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
value, index, buf, bufsize, DEFAULT_TIMEOUT);
- return r;
+ if (r < bufsize) {
+ if (r >= 0) {
+ dev_err(&dev->dev,
+ "short control message received (%d < %u)\n",
+ r, bufsize);
+ r = -EIO;
+ }
+
+ dev_err(&dev->dev, "failed to receive control message: %d\n",
+ r);
+ return r;
+ }
+
+ return 0;
}
static int ch341_set_baudrate(struct usb_device *dev,
@@ -158,9 +173,9 @@ static int ch341_set_handshake(struct usb_device *dev, u8 control)
static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
{
+ const unsigned int size = 2;
char *buffer;
int r;
- const unsigned size = 8;
unsigned long flags;
buffer = kmalloc(size, GFP_KERNEL);
@@ -171,14 +186,9 @@ static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
if (r < 0)
goto out;
- /* setup the private status if available */
- if (r == 2) {
- r = 0;
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
- spin_unlock_irqrestore(&priv->lock, flags);
- } else
- r = -EPROTO;
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
+ spin_unlock_irqrestore(&priv->lock, flags);
out: kfree(buffer);
return r;
@@ -188,9 +198,9 @@ out: kfree(buffer);
static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
{
+ const unsigned int size = 2;
char *buffer;
int r;
- const unsigned size = 8;
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer)
@@ -253,7 +263,6 @@ static int ch341_port_probe(struct usb_serial_port *port)
spin_lock_init(&priv->lock);
priv->baud_rate = DEFAULT_BAUD_RATE;
- priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR;
r = ch341_configure(port->serial->dev, priv);
if (r < 0)
@@ -315,7 +324,7 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
r = ch341_configure(serial->dev, priv);
if (r)
- goto out;
+ return r;
if (tty)
ch341_set_termios(tty, port, NULL);
@@ -325,12 +334,19 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
if (r) {
dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n",
__func__, r);
- goto out;
+ return r;
}
r = usb_serial_generic_open(tty, port);
+ if (r)
+ goto err_kill_interrupt_urb;
-out: return r;
+ return 0;
+
+err_kill_interrupt_urb:
+ usb_kill_urb(port->interrupt_in_urb);
+
+ return r;
}
/* Old_termios contains the original termios settings and
@@ -345,26 +361,25 @@ static void ch341_set_termios(struct tty_struct *tty,
baud_rate = tty_get_baud_rate(tty);
- priv->baud_rate = baud_rate;
-
if (baud_rate) {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
- spin_unlock_irqrestore(&priv->lock, flags);
+ priv->baud_rate = baud_rate;
ch341_set_baudrate(port->serial->dev, priv);
- } else {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
- spin_unlock_irqrestore(&priv->lock, flags);
}
- ch341_set_handshake(port->serial->dev, priv->line_control);
-
/* Unimplemented:
* (cflag & CSIZE) : data bits [5, 8]
* (cflag & PARENB) : parity {NONE, EVEN, ODD}
* (cflag & CSTOPB) : stop bits [1, 2]
*/
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (C_BAUD(tty) == B0)
+ priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
+ else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
+ priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ ch341_set_handshake(port->serial->dev, priv->line_control);
}
static void ch341_break_ctl(struct tty_struct *tty, int break_state)
@@ -539,14 +554,23 @@ static int ch341_tiocmget(struct tty_struct *tty)
static int ch341_reset_resume(struct usb_serial *serial)
{
- struct ch341_private *priv;
-
- priv = usb_get_serial_port_data(serial->port[0]);
+ struct usb_serial_port *port = serial->port[0];
+ struct ch341_private *priv = usb_get_serial_port_data(port);
+ int ret;
/* reconfigure ch341 serial port after bus-reset */
ch341_configure(serial->dev, priv);
- return 0;
+ if (test_bit(ASYNCB_INITIALIZED, &port->port.flags)) {
+ ret = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
+ if (ret) {
+ dev_err(&port->dev, "failed to submit interrupt urb: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return usb_serial_generic_resume(serial);
}
static struct usb_serial_driver ch341_device = {
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index fe7452f0f38a..33cec50978b8 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -171,6 +171,8 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */
{ USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */
{ USB_DEVICE(0x1901, 0x0194) }, /* GE Healthcare Remote Alarm Box */
+ { USB_DEVICE(0x1901, 0x0195) }, /* GE B850/B650/B450 CP2104 DP UART interface */
+ { USB_DEVICE(0x1901, 0x0196) }, /* GE B850 CP2105 DP UART interface */
{ USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */
{ USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */
{ USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index 2916dea3ede8..8948f375e75d 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -50,6 +50,7 @@
#define CYBERJACK_PRODUCT_ID 0x0100
/* Function prototypes */
+static int cyberjack_attach(struct usb_serial *serial);
static int cyberjack_port_probe(struct usb_serial_port *port);
static int cyberjack_port_remove(struct usb_serial_port *port);
static int cyberjack_open(struct tty_struct *tty,
@@ -77,6 +78,7 @@ static struct usb_serial_driver cyberjack_device = {
.description = "Reiner SCT Cyberjack USB card reader",
.id_table = id_table,
.num_ports = 1,
+ .attach = cyberjack_attach,
.port_probe = cyberjack_port_probe,
.port_remove = cyberjack_port_remove,
.open = cyberjack_open,
@@ -100,6 +102,14 @@ struct cyberjack_private {
short wrsent; /* Data already sent */
};
+static int cyberjack_attach(struct usb_serial *serial)
+{
+ if (serial->num_bulk_out < serial->num_ports)
+ return -ENODEV;
+
+ return 0;
+}
+
static int cyberjack_port_probe(struct usb_serial_port *port)
{
struct cyberjack_private *priv;
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 3df7b7ec178e..e0b1fe2f60e1 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -1483,16 +1483,20 @@ static int digi_read_oob_callback(struct urb *urb)
struct usb_serial *serial = port->serial;
struct tty_struct *tty;
struct digi_port *priv = usb_get_serial_port_data(port);
+ unsigned char *buf = urb->transfer_buffer;
int opcode, line, status, val;
int i;
unsigned int rts;
+ if (urb->actual_length < 4)
+ return -1;
+
/* handle each oob command */
- for (i = 0; i < urb->actual_length - 3;) {
- opcode = ((unsigned char *)urb->transfer_buffer)[i++];
- line = ((unsigned char *)urb->transfer_buffer)[i++];
- status = ((unsigned char *)urb->transfer_buffer)[i++];
- val = ((unsigned char *)urb->transfer_buffer)[i++];
+ for (i = 0; i < urb->actual_length - 3; i += 4) {
+ opcode = buf[i];
+ line = buf[i + 1];
+ status = buf[i + 2];
+ val = buf[i + 3];
dev_dbg(&port->dev, "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n",
opcode, line, status, val);
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index d3d6ec455151..19a98116c2ab 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1807,8 +1807,6 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
mutex_init(&priv->cfg_lock);
- priv->flags = ASYNC_LOW_LATENCY;
-
if (quirk && quirk->port_probe)
quirk->port_probe(priv);
@@ -2072,6 +2070,20 @@ static int ftdi_process_packet(struct usb_serial_port *port,
priv->prev_status = status;
}
+ /* save if the transmitter is empty or not */
+ if (packet[1] & FTDI_RS_TEMT)
+ priv->transmit_empty = 1;
+ else
+ priv->transmit_empty = 0;
+
+ len -= 2;
+ if (!len)
+ return 0; /* status only */
+
+ /*
+ * Break and error status must only be processed for packets with
+ * data payload to avoid over-reporting.
+ */
flag = TTY_NORMAL;
if (packet[1] & FTDI_RS_ERR_MASK) {
/* Break takes precedence over parity, which takes precedence
@@ -2094,15 +2106,6 @@ static int ftdi_process_packet(struct usb_serial_port *port,
}
}
- /* save if the transmitter is empty or not */
- if (packet[1] & FTDI_RS_TEMT)
- priv->transmit_empty = 1;
- else
- priv->transmit_empty = 0;
-
- len -= 2;
- if (!len)
- return 0; /* status only */
port->icount.rx += len;
ch = packet + 2;
@@ -2433,8 +2436,12 @@ static int ftdi_get_modem_status(struct usb_serial_port *port,
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
0, priv->interface,
buf, len, WDR_TIMEOUT);
- if (ret < 0) {
+
+ /* NOTE: We allow short responses and handle that below. */
+ if (ret < 1) {
dev_err(&port->dev, "failed to get modem status: %d\n", ret);
+ if (ret >= 0)
+ ret = -EIO;
ret = usb_translate_errors(ret);
goto out;
}
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index db591d19d416..37d0e8cc7af6 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -1044,6 +1044,7 @@ static int garmin_write_bulk(struct usb_serial_port *port,
"%s - usb_submit_urb(write bulk) failed with status = %d\n",
__func__, status);
count = status;
+ kfree(buffer);
}
/* we are done with this urb, so let the host driver
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 1947ea0e0988..b63a6c3899c5 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -2761,6 +2761,11 @@ static int edge_startup(struct usb_serial *serial)
EDGE_COMPATIBILITY_MASK1,
EDGE_COMPATIBILITY_MASK2 };
+ if (serial->num_bulk_in < 1 || serial->num_interrupt_in < 1) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
dev = serial->dev;
/* create our private serial structure */
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index fce82fd79f77..f1a8fdcd8674 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -1499,8 +1499,7 @@ static int do_boot_mode(struct edgeport_serial *serial,
dev_dbg(dev, "%s - Download successful -- Device rebooting...\n", __func__);
- /* return an error on purpose */
- return -ENODEV;
+ return 1;
}
stayinbootmode:
@@ -1508,7 +1507,7 @@ stayinbootmode:
dev_dbg(dev, "%s - STAYING IN BOOT MODE\n", __func__);
serial->product_info.TiMode = TI_MODE_BOOT;
- return 0;
+ return 1;
}
static int ti_do_config(struct edgeport_port *port, int feature, int on)
@@ -1675,6 +1674,12 @@ static void edge_interrupt_callback(struct urb *urb)
function = TIUMP_GET_FUNC_FROM_CODE(data[0]);
dev_dbg(dev, "%s - port_number %d, function %d, info 0x%x\n", __func__,
port_number, function, data[1]);
+
+ if (port_number >= edge_serial->serial->num_ports) {
+ dev_err(dev, "bad port number %d\n", port_number);
+ goto exit;
+ }
+
port = edge_serial->serial->port[port_number];
edge_port = usb_get_serial_port_data(port);
if (!edge_port) {
@@ -1756,7 +1761,7 @@ static void edge_bulk_in_callback(struct urb *urb)
port_number = edge_port->port->port_number;
- if (edge_port->lsr_event) {
+ if (urb->actual_length > 0 && edge_port->lsr_event) {
edge_port->lsr_event = 0;
dev_dbg(dev, "%s ===== Port %u LSR Status = %02x, Data = %02x ======\n",
__func__, port_number, edge_port->lsr_mask, *data);
@@ -2549,6 +2554,13 @@ static int edge_startup(struct usb_serial *serial)
int status;
u16 product_id;
+ /* Make sure we have the required endpoints when in download mode. */
+ if (serial->interface->cur_altsetting->desc.bNumEndpoints > 1) {
+ if (serial->num_bulk_in < serial->num_ports ||
+ serial->num_bulk_out < serial->num_ports)
+ return -ENODEV;
+ }
+
/* create our private serial structure */
edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
if (!edge_serial)
@@ -2556,14 +2568,18 @@ static int edge_startup(struct usb_serial *serial)
mutex_init(&edge_serial->es_lock);
edge_serial->serial = serial;
+ INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work);
usb_set_serial_data(serial, edge_serial);
status = download_fw(edge_serial);
- if (status) {
+ if (status < 0) {
kfree(edge_serial);
return status;
}
+ if (status > 0)
+ return 1; /* bind but do not register any ports */
+
product_id = le16_to_cpu(
edge_serial->serial->dev->descriptor.idProduct);
@@ -2575,7 +2591,6 @@ static int edge_startup(struct usb_serial *serial)
}
}
- INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work);
edge_heartbeat_schedule(edge_serial);
return 0;
@@ -2583,6 +2598,9 @@ static int edge_startup(struct usb_serial *serial)
static void edge_disconnect(struct usb_serial *serial)
{
+ struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+
+ cancel_delayed_work_sync(&edge_serial->heartbeat_work);
}
static void edge_release(struct usb_serial *serial)
diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c
index 5ad4a0fb4b26..7ed7d33d6c10 100644
--- a/drivers/usb/serial/iuu_phoenix.c
+++ b/drivers/usb/serial/iuu_phoenix.c
@@ -68,6 +68,16 @@ struct iuu_private {
u32 clk;
};
+static int iuu_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_in < num_ports || serial->num_bulk_out < num_ports)
+ return -ENODEV;
+
+ return 0;
+}
+
static int iuu_port_probe(struct usb_serial_port *port)
{
struct iuu_private *priv;
@@ -1196,6 +1206,7 @@ static struct usb_serial_driver iuu_device = {
.tiocmset = iuu_tiocmset,
.set_termios = iuu_set_termios,
.init_termios = iuu_init_termios,
+ .attach = iuu_attach,
.port_probe = iuu_port_probe,
.port_remove = iuu_port_remove,
};
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index 4f7e072e4e00..930be98d59b3 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -699,6 +699,19 @@ MODULE_FIRMWARE("keyspan_pda/keyspan_pda.fw");
MODULE_FIRMWARE("keyspan_pda/xircom_pgs.fw");
#endif
+static int keyspan_pda_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_out < num_ports ||
+ serial->num_interrupt_in < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int keyspan_pda_port_probe(struct usb_serial_port *port)
{
@@ -776,6 +789,7 @@ static struct usb_serial_driver keyspan_pda_device = {
.break_ctl = keyspan_pda_break_ctl,
.tiocmget = keyspan_pda_tiocmget,
.tiocmset = keyspan_pda_tiocmset,
+ .attach = keyspan_pda_attach,
.port_probe = keyspan_pda_port_probe,
.port_remove = keyspan_pda_port_remove,
};
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index e020ad28a00c..83c823d32ff9 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -192,10 +192,11 @@ static int klsi_105_get_line_state(struct usb_serial_port *port,
status_buf, KLSI_STATUSBUF_LEN,
10000
);
- if (rc < 0)
- dev_err(&port->dev, "Reading line status failed (error = %d)\n",
- rc);
- else {
+ if (rc != KLSI_STATUSBUF_LEN) {
+ dev_err(&port->dev, "reading line status failed: %d\n", rc);
+ if (rc >= 0)
+ rc = -EIO;
+ } else {
status = get_unaligned_le16(status_buf);
dev_info(&port->serial->dev->dev, "read status %x %x\n",
@@ -296,7 +297,7 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
rc = usb_serial_generic_open(tty, port);
if (rc) {
retval = rc;
- goto exit;
+ goto err_free_cfg;
}
rc = usb_control_msg(port->serial->dev,
@@ -311,21 +312,38 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
if (rc < 0) {
dev_err(&port->dev, "Enabling read failed (error = %d)\n", rc);
retval = rc;
+ goto err_generic_close;
} else
dev_dbg(&port->dev, "%s - enabled reading\n", __func__);
rc = klsi_105_get_line_state(port, &line_state);
- if (rc >= 0) {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_state = line_state;
- spin_unlock_irqrestore(&priv->lock, flags);
- dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state);
- retval = 0;
- } else
+ if (rc < 0) {
retval = rc;
+ goto err_disable_read;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->line_state = line_state;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__,
+ line_state);
+
+ return 0;
-exit:
+err_disable_read:
+ usb_control_msg(port->serial->dev,
+ usb_sndctrlpipe(port->serial->dev, 0),
+ KL5KUSB105A_SIO_CONFIGURE,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
+ 0, /* index */
+ NULL, 0,
+ KLSI_TIMEOUT);
+err_generic_close:
+ usb_serial_generic_close(port);
+err_free_cfg:
kfree(cfg);
+
return retval;
}
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index 2363654cafc9..813035f51fe7 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -51,6 +51,7 @@
/* Function prototypes */
+static int kobil_attach(struct usb_serial *serial);
static int kobil_port_probe(struct usb_serial_port *probe);
static int kobil_port_remove(struct usb_serial_port *probe);
static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port);
@@ -86,6 +87,7 @@ static struct usb_serial_driver kobil_device = {
.description = "KOBIL USB smart card terminal",
.id_table = id_table,
.num_ports = 1,
+ .attach = kobil_attach,
.port_probe = kobil_port_probe,
.port_remove = kobil_port_remove,
.ioctl = kobil_ioctl,
@@ -113,6 +115,16 @@ struct kobil_private {
};
+static int kobil_attach(struct usb_serial *serial)
+{
+ if (serial->num_interrupt_out < serial->num_ports) {
+ dev_err(&serial->interface->dev, "missing interrupt-out endpoint\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int kobil_port_probe(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 63db004af21f..e56cdb436de3 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -65,8 +65,6 @@ struct moschip_port {
struct urb *write_urb_pool[NUM_URBS];
};
-static struct usb_serial_driver moschip7720_2port_driver;
-
#define USB_VENDOR_ID_MOSCHIP 0x9710
#define MOSCHIP_DEVICE_ID_7720 0x7720
#define MOSCHIP_DEVICE_ID_7715 0x7715
@@ -970,25 +968,6 @@ static void mos7720_bulk_out_data_callback(struct urb *urb)
tty_port_tty_wakeup(&mos7720_port->port->port);
}
-/*
- * mos77xx_probe
- * this function installs the appropriate read interrupt endpoint callback
- * depending on whether the device is a 7720 or 7715, thus avoiding costly
- * run-time checks in the high-frequency callback routine itself.
- */
-static int mos77xx_probe(struct usb_serial *serial,
- const struct usb_device_id *id)
-{
- if (id->idProduct == MOSCHIP_DEVICE_ID_7715)
- moschip7720_2port_driver.read_int_callback =
- mos7715_interrupt_callback;
- else
- moschip7720_2port_driver.read_int_callback =
- mos7720_interrupt_callback;
-
- return 0;
-}
-
static int mos77xx_calc_num_ports(struct usb_serial *serial)
{
u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
@@ -1920,6 +1899,11 @@ static int mos7720_startup(struct usb_serial *serial)
u16 product;
int ret_val;
+ if (serial->num_bulk_in < 2 || serial->num_bulk_out < 2) {
+ dev_err(&serial->interface->dev, "missing bulk endpoints\n");
+ return -ENODEV;
+ }
+
product = le16_to_cpu(serial->dev->descriptor.idProduct);
dev = serial->dev;
@@ -1944,19 +1928,18 @@ static int mos7720_startup(struct usb_serial *serial)
tmp->interrupt_in_endpointAddress;
serial->port[1]->interrupt_in_urb = NULL;
serial->port[1]->interrupt_in_buffer = NULL;
+
+ if (serial->port[0]->interrupt_in_urb) {
+ struct urb *urb = serial->port[0]->interrupt_in_urb;
+
+ urb->complete = mos7715_interrupt_callback;
+ }
}
/* setting configuration feature to one */
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
(__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5000);
- /* start the interrupt urb */
- ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL);
- if (ret_val)
- dev_err(&dev->dev,
- "%s - Error %d submitting control urb\n",
- __func__, ret_val);
-
#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
if (product == MOSCHIP_DEVICE_ID_7715) {
ret_val = mos7715_parport_init(serial);
@@ -1964,6 +1947,13 @@ static int mos7720_startup(struct usb_serial *serial)
return ret_val;
}
#endif
+ /* start the interrupt urb */
+ ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL);
+ if (ret_val) {
+ dev_err(&dev->dev, "failed to submit interrupt urb: %d\n",
+ ret_val);
+ }
+
/* LSR For Port 1 */
read_mos_reg(serial, 0, MOS7720_LSR, &data);
dev_dbg(&dev->dev, "LSR:%x\n", data);
@@ -1973,6 +1963,8 @@ static int mos7720_startup(struct usb_serial *serial)
static void mos7720_release(struct usb_serial *serial)
{
+ usb_kill_urb(serial->port[0]->interrupt_in_urb);
+
#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
/* close the parallel port */
@@ -2056,7 +2048,6 @@ static struct usb_serial_driver moschip7720_2port_driver = {
.close = mos7720_close,
.throttle = mos7720_throttle,
.unthrottle = mos7720_unthrottle,
- .probe = mos77xx_probe,
.attach = mos7720_startup,
.release = mos7720_release,
.port_probe = mos7720_port_probe,
@@ -2070,7 +2061,7 @@ static struct usb_serial_driver moschip7720_2port_driver = {
.chars_in_buffer = mos7720_chars_in_buffer,
.break_ctl = mos7720_break,
.read_bulk_callback = mos7720_bulk_in_callback,
- .read_int_callback = NULL /* dynamically assigned in probe() */
+ .read_int_callback = mos7720_interrupt_callback,
};
static struct usb_serial_driver * const serial_drivers[] = {
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 7f3ddd7ba2ce..d17685cc00c9 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -1024,6 +1024,7 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
* (can't set it up in mos7840_startup as the structures *
* were not set up at that time.) */
if (port0->open_ports == 1) {
+ /* FIXME: Buffer never NULL, so URB is not submitted. */
if (serial->port[0]->interrupt_in_buffer == NULL) {
/* set up interrupt urb */
usb_fill_int_urb(serial->port[0]->interrupt_in_urb,
@@ -2116,6 +2117,18 @@ static int mos7840_calc_num_ports(struct usb_serial *serial)
return mos7840_num_ports;
}
+static int mos7840_attach(struct usb_serial *serial)
+{
+ if (serial->num_bulk_in < serial->num_ports ||
+ serial->num_bulk_out < serial->num_ports ||
+ serial->num_interrupt_in < 1) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int mos7840_port_probe(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
@@ -2391,6 +2404,7 @@ static struct usb_serial_driver moschip7840_4port_device = {
.tiocmset = mos7840_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
+ .attach = mos7840_attach,
.port_probe = mos7840_port_probe,
.port_remove = mos7840_port_remove,
.read_bulk_callback = mos7840_bulk_in_callback,
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index f6c6900bccf0..76564b3bebb9 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -38,6 +38,7 @@ static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
static int omninet_write_room(struct tty_struct *tty);
static void omninet_disconnect(struct usb_serial *serial);
+static int omninet_attach(struct usb_serial *serial);
static int omninet_port_probe(struct usb_serial_port *port);
static int omninet_port_remove(struct usb_serial_port *port);
@@ -56,6 +57,7 @@ static struct usb_serial_driver zyxel_omninet_device = {
.description = "ZyXEL - omni.net lcd plus usb",
.id_table = id_table,
.num_ports = 1,
+ .attach = omninet_attach,
.port_probe = omninet_port_probe,
.port_remove = omninet_port_remove,
.open = omninet_open,
@@ -104,6 +106,17 @@ struct omninet_data {
__u8 od_outseq; /* Sequence number for bulk_out URBs */
};
+static int omninet_attach(struct usb_serial *serial)
+{
+ /* The second bulk-out endpoint is used for writing. */
+ if (serial->num_bulk_out < 2) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int omninet_port_probe(struct usb_serial_port *port)
{
struct omninet_data *od;
@@ -129,12 +142,6 @@ static int omninet_port_remove(struct usb_serial_port *port)
static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port)
{
- struct usb_serial *serial = port->serial;
- struct usb_serial_port *wport;
-
- wport = serial->port[1];
- tty_port_tty_set(&wport->port, tty);
-
return usb_serial_generic_open(tty, port);
}
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index 4b7bfb394a32..64bf258e7e00 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -142,7 +142,7 @@ static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port)
usb_clear_halt(port->serial->dev, port->read_urb->pipe);
res = usb_serial_generic_open(tty, port);
- if (!res)
+ if (res)
return res;
/* Request CTS line state, sometimes during opening the current
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 9894e341c6ac..42cc72e54c05 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -268,6 +268,8 @@ static void option_instat_callback(struct urb *urb);
#define TELIT_PRODUCT_CC864_SINGLE 0x1006
#define TELIT_PRODUCT_DE910_DUAL 0x1010
#define TELIT_PRODUCT_UE910_V2 0x1012
+#define TELIT_PRODUCT_LE922_USBCFG1 0x1040
+#define TELIT_PRODUCT_LE922_USBCFG2 0x1041
#define TELIT_PRODUCT_LE922_USBCFG0 0x1042
#define TELIT_PRODUCT_LE922_USBCFG3 0x1043
#define TELIT_PRODUCT_LE922_USBCFG5 0x1045
@@ -1210,6 +1212,10 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG0),
.driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG1),
+ .driver_info = (kernel_ulong_t)&telit_le910_blacklist },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG2),
+ .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG3),
.driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff),
@@ -1989,6 +1995,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d02, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x02, 0x01) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) },
+ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) }, /* D-Link DWM-158 */
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
@@ -2000,6 +2007,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD200, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_6802, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD300, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d, 0xff, 0xff, 0xff) }, /* HP lt2523 (Novatel E371) */
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);
diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c
index a4b88bc038b6..b8bf52bf7a94 100644
--- a/drivers/usb/serial/oti6858.c
+++ b/drivers/usb/serial/oti6858.c
@@ -134,6 +134,7 @@ static int oti6858_chars_in_buffer(struct tty_struct *tty);
static int oti6858_tiocmget(struct tty_struct *tty);
static int oti6858_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
+static int oti6858_attach(struct usb_serial *serial);
static int oti6858_port_probe(struct usb_serial_port *port);
static int oti6858_port_remove(struct usb_serial_port *port);
@@ -158,6 +159,7 @@ static struct usb_serial_driver oti6858_device = {
.write_bulk_callback = oti6858_write_bulk_callback,
.write_room = oti6858_write_room,
.chars_in_buffer = oti6858_chars_in_buffer,
+ .attach = oti6858_attach,
.port_probe = oti6858_port_probe,
.port_remove = oti6858_port_remove,
};
@@ -324,6 +326,20 @@ static void send_data(struct work_struct *work)
usb_serial_port_softint(port);
}
+static int oti6858_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_in < num_ports ||
+ serial->num_bulk_out < num_ports ||
+ serial->num_interrupt_in < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int oti6858_port_probe(struct usb_serial_port *port)
{
struct oti6858_private *priv;
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index ae682e4eeaef..1db4b61bdf7b 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -49,6 +49,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) },
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) },
+ { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID2) },
{ USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) },
{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) },
{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) },
@@ -220,9 +221,17 @@ static int pl2303_probe(struct usb_serial *serial,
static int pl2303_startup(struct usb_serial *serial)
{
struct pl2303_serial_private *spriv;
+ unsigned char num_ports = serial->num_ports;
enum pl2303_type type = TYPE_01;
unsigned char *buf;
+ if (serial->num_bulk_in < num_ports ||
+ serial->num_bulk_out < num_ports ||
+ serial->num_interrupt_in < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
if (!spriv)
return -ENOMEM;
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index e3b7af8adfb7..09d9be88209e 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -27,6 +27,7 @@
#define ATEN_VENDOR_ID 0x0557
#define ATEN_VENDOR_ID2 0x0547
#define ATEN_PRODUCT_ID 0x2008
+#define ATEN_PRODUCT_ID2 0x2118
#define IODATA_VENDOR_ID 0x04bb
#define IODATA_PRODUCT_ID 0x0a03
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 1bc6089b9008..696458db7e3c 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -124,6 +124,7 @@ static const struct usb_device_id id_table[] = {
{USB_DEVICE(0x1410, 0xa021)}, /* Novatel Gobi 3000 Composite */
{USB_DEVICE(0x413c, 0x8193)}, /* Dell Gobi 3000 QDL */
{USB_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */
+ {USB_DEVICE(0x413c, 0x81a6)}, /* Dell DW5570 QDL (MC8805) */
{USB_DEVICE(0x1199, 0x68a4)}, /* Sierra Wireless QDL */
{USB_DEVICE(0x1199, 0x68a5)}, /* Sierra Wireless Modem */
{USB_DEVICE(0x1199, 0x68a8)}, /* Sierra Wireless QDL */
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index b18974cbd995..a3ed07c58754 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -408,16 +408,12 @@ static void qt2_close(struct usb_serial_port *port)
{
struct usb_serial *serial;
struct qt2_port_private *port_priv;
- unsigned long flags;
int i;
serial = port->serial;
port_priv = usb_get_serial_port_data(port);
- spin_lock_irqsave(&port_priv->urb_lock, flags);
usb_kill_urb(port_priv->write_urb);
- port_priv->urb_in_use = false;
- spin_unlock_irqrestore(&port_priv->urb_lock, flags);
/* flush the port transmit buffer */
i = usb_control_msg(serial->dev,
diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c
index b2dff0f14743..236ea43f7815 100644
--- a/drivers/usb/serial/safe_serial.c
+++ b/drivers/usb/serial/safe_serial.c
@@ -205,6 +205,11 @@ static void safe_process_read_urb(struct urb *urb)
if (!safe)
goto out;
+ if (length < 2) {
+ dev_err(&port->dev, "malformed packet\n");
+ return;
+ }
+
fcs = fcs_compute10(data, length, CRC10_INITFCS);
if (fcs) {
dev_err(&port->dev, "%s - bad CRC %x\n", __func__, fcs);
diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c
index ef0dbf0703c5..ddfd787c461c 100644
--- a/drivers/usb/serial/spcp8x5.c
+++ b/drivers/usb/serial/spcp8x5.c
@@ -154,6 +154,19 @@ static int spcp8x5_probe(struct usb_serial *serial,
return 0;
}
+static int spcp8x5_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_in < num_ports ||
+ serial->num_bulk_out < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int spcp8x5_port_probe(struct usb_serial_port *port)
{
const struct usb_device_id *id = usb_get_serial_data(port->serial);
@@ -219,11 +232,17 @@ static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status)
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
GET_UART_STATUS, GET_UART_STATUS_TYPE,
0, GET_UART_STATUS_MSR, buf, 1, 100);
- if (ret < 0)
+ if (ret < 1) {
dev_err(&port->dev, "failed to get modem status: %d\n", ret);
+ if (ret >= 0)
+ ret = -EIO;
+ goto out;
+ }
dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x\n", ret, *buf);
*status = *buf;
+ ret = 0;
+out:
kfree(buf);
return ret;
@@ -477,6 +496,7 @@ static struct usb_serial_driver spcp8x5_device = {
.tiocmget = spcp8x5_tiocmget,
.tiocmset = spcp8x5_tiocmset,
.probe = spcp8x5_probe,
+ .attach = spcp8x5_attach,
.port_probe = spcp8x5_port_probe,
.port_remove = spcp8x5_port_remove,
};
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index 2694df2f4559..535fcfafc097 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -339,6 +339,13 @@ static int ti_startup(struct usb_serial *serial)
goto free_tdev;
}
+ if (serial->num_bulk_in < serial->num_ports ||
+ serial->num_bulk_out < serial->num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ status = -ENODEV;
+ goto free_tdev;
+ }
+
return 0;
free_tdev:
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 7ffe4209067b..640a2e2ec04d 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -2135,6 +2135,13 @@ UNUSUAL_DEV( 0x22b8, 0x3010, 0x0001, 0x0001,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ),
+/* Reported-by George Cherian <george.cherian@cavium.com> */
+UNUSUAL_DEV(0x152d, 0x9561, 0x0000, 0x9999,
+ "JMicron",
+ "JMS56x",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_REPORT_OPCODES),
+
/*
* Patch by Constantin Baranov <const@tltsu.ru>
* Report by Andreas Koenecke.
diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c
index 021755f7f32d..a04fa896361a 100644
--- a/drivers/video/fbdev/core/fbcmap.c
+++ b/drivers/video/fbdev/core/fbcmap.c
@@ -163,17 +163,18 @@ void fb_dealloc_cmap(struct fb_cmap *cmap)
int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to)
{
- int tooff = 0, fromoff = 0;
- int size;
+ unsigned int tooff = 0, fromoff = 0;
+ size_t size;
if (to->start > from->start)
fromoff = to->start - from->start;
else
tooff = from->start - to->start;
- size = to->len - tooff;
- if (size > (int) (from->len - fromoff))
- size = from->len - fromoff;
- if (size <= 0)
+ if (fromoff >= from->len || tooff >= to->len)
+ return -EINVAL;
+
+ size = min_t(size_t, to->len - tooff, from->len - fromoff);
+ if (size == 0)
return -EINVAL;
size *= sizeof(u16);
@@ -187,22 +188,22 @@ int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to)
int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to)
{
- u32 tooff = 0, fromoff = 0;
- u32 size;
+ unsigned int tooff = 0, fromoff = 0;
+ size_t size;
if (to->start > from->start)
fromoff = to->start - from->start;
else
tooff = from->start - to->start;
- if ((to->len <= tooff) || (from->len <= fromoff))
+ if (fromoff >= from->len || tooff >= to->len)
return -EINVAL;
- size = to->len - tooff;
- if (size > (from->len - fromoff))
- size = from->len - fromoff;
- size *= sizeof(u16);
+ size = min_t(size_t, to->len - tooff, from->len - fromoff);
if (size == 0)
return -EINVAL;
+ size *= sizeof(u16);
+
+
if (copy_to_user(to->red+tooff, from->red+fromoff, size))
return -EFAULT;
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index d9a4bd91f3eb..796246a856b4 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -210,6 +210,15 @@ enum mdss_mdp_pipe_type {
MDSS_MDP_PIPE_TYPE_MAX,
};
+enum mdss_mdp_intf_index {
+ MDSS_MDP_NO_INTF,
+ MDSS_MDP_INTF0,
+ MDSS_MDP_INTF1,
+ MDSS_MDP_INTF2,
+ MDSS_MDP_INTF3,
+ MDSS_MDP_MAX_INTF
+};
+
struct reg_bus_client {
char name[MAX_CLIENT_NAME_LEN];
short usecase_ndx;
diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c
index 14d998d14eeb..17644e3556b6 100644
--- a/drivers/video/fbdev/msm/mdss_compat_utils.c
+++ b/drivers/video/fbdev/msm/mdss_compat_utils.c
@@ -125,6 +125,7 @@ static void __copy_atomic_commit_struct(struct mdp_layer_commit *commit,
commit32->commit_v1.input_layer_cnt;
commit->commit_v1.left_roi = commit32->commit_v1.left_roi;
commit->commit_v1.right_roi = commit32->commit_v1.right_roi;
+ commit->commit_v1.bl_level = commit32->commit_v1.bl_level;
memcpy(&commit->commit_v1.reserved, &commit32->commit_v1.reserved,
sizeof(commit32->commit_v1.reserved));
}
diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.h b/drivers/video/fbdev/msm/mdss_compat_utils.h
index 626792925cb6..4f44cd1c9471 100644
--- a/drivers/video/fbdev/msm/mdss_compat_utils.h
+++ b/drivers/video/fbdev/msm/mdss_compat_utils.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,9 +19,9 @@
* To allow proper structure padding for 64bit/32bit target
*/
#ifdef __LP64
-#define MDP_LAYER_COMMIT_V1_PAD 3
+#define MDP_LAYER_COMMIT_V1_PAD 2
#else
-#define MDP_LAYER_COMMIT_V1_PAD 4
+#define MDP_LAYER_COMMIT_V1_PAD 3
#endif
struct mdp_buf_sync32 {
@@ -537,6 +537,7 @@ struct mdp_layer_commit_v1_32 {
compat_caddr_t dest_scaler;
uint32_t dest_scaler_cnt;
compat_caddr_t frc_info;
+ uint32_t bl_level; /* BL level to be updated in commit */
uint32_t reserved[MDP_LAYER_COMMIT_V1_PAD];
};
diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c
index 3e1dbba7c9ae..e60869144339 100644
--- a/drivers/video/fbdev/msm/mdss_debug.c
+++ b/drivers/video/fbdev/msm/mdss_debug.c
@@ -44,6 +44,8 @@
#define INVALID_XIN_ID 0xFF
+static DEFINE_MUTEX(mdss_debug_lock);
+
static char panel_reg[2] = {DEFAULT_READ_PANEL_POWER_MODE_REG, 0x00};
static int panel_debug_base_open(struct inode *inode, struct file *file)
@@ -93,8 +95,10 @@ static ssize_t panel_debug_base_offset_write(struct file *file,
if (cnt > (dbg->max_offset - off))
cnt = dbg->max_offset - off;
+ mutex_lock(&mdss_debug_lock);
dbg->off = off;
dbg->cnt = cnt;
+ mutex_unlock(&mdss_debug_lock);
pr_debug("offset=%x cnt=%d\n", off, cnt);
@@ -114,15 +118,21 @@ static ssize_t panel_debug_base_offset_read(struct file *file,
if (*ppos)
return 0; /* the end */
+ mutex_lock(&mdss_debug_lock);
len = snprintf(buf, sizeof(buf), "0x%02zx %zx\n", dbg->off, dbg->cnt);
- if (len < 0 || len >= sizeof(buf))
+ if (len < 0 || len >= sizeof(buf)) {
+ mutex_unlock(&mdss_debug_lock);
return 0;
+ }
- if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
+ if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) {
+ mutex_unlock(&mdss_debug_lock);
return -EFAULT;
+ }
*ppos += len; /* increase offset */
+ mutex_unlock(&mdss_debug_lock);
return len;
}
@@ -220,11 +230,16 @@ static ssize_t panel_debug_base_reg_read(struct file *file,
if (!dbg)
return -ENODEV;
- if (!dbg->cnt)
+ mutex_lock(&mdss_debug_lock);
+ if (!dbg->cnt) {
+ mutex_unlock(&mdss_debug_lock);
return 0;
+ }
- if (*ppos)
+ if (*ppos) {
+ mutex_unlock(&mdss_debug_lock);
return 0; /* the end */
+ }
/* '0x' + 2 digit + blank = 5 bytes for each number */
reg_buf_len = (dbg->cnt * PANEL_REG_FORMAT_LEN)
@@ -265,11 +280,13 @@ static ssize_t panel_debug_base_reg_read(struct file *file,
kfree(panel_reg_buf);
*ppos += len; /* increase offset */
+ mutex_unlock(&mdss_debug_lock);
return len;
read_reg_fail:
kfree(rx_buf);
kfree(panel_reg_buf);
+ mutex_unlock(&mdss_debug_lock);
return rc;
}
@@ -403,8 +420,10 @@ static ssize_t mdss_debug_base_offset_write(struct file *file,
if (cnt > (dbg->max_offset - off))
cnt = dbg->max_offset - off;
+ mutex_lock(&mdss_debug_lock);
dbg->off = off;
dbg->cnt = cnt;
+ mutex_unlock(&mdss_debug_lock);
pr_debug("offset=%x cnt=%x\n", off, cnt);
@@ -424,15 +443,21 @@ static ssize_t mdss_debug_base_offset_read(struct file *file,
if (*ppos)
return 0; /* the end */
+ mutex_lock(&mdss_debug_lock);
len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt);
- if (len < 0 || len >= sizeof(buf))
+ if (len < 0 || len >= sizeof(buf)) {
+ mutex_unlock(&mdss_debug_lock);
return 0;
+ }
- if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
+ if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) {
+ mutex_unlock(&mdss_debug_lock);
return -EFAULT;
+ }
*ppos += len; /* increase offset */
+ mutex_unlock(&mdss_debug_lock);
return len;
}
@@ -489,6 +514,8 @@ static ssize_t mdss_debug_base_reg_read(struct file *file,
return -ENODEV;
}
+ mutex_lock(&mdss_debug_lock);
+
if (!dbg->buf) {
char dump_buf[64];
char *ptr;
@@ -500,6 +527,7 @@ static ssize_t mdss_debug_base_reg_read(struct file *file,
if (!dbg->buf) {
pr_err("not enough memory to hold reg dump\n");
+ mutex_unlock(&mdss_debug_lock);
return -ENOMEM;
}
@@ -530,17 +558,21 @@ static ssize_t mdss_debug_base_reg_read(struct file *file,
dbg->buf_len = tot;
}
- if (*ppos >= dbg->buf_len)
+ if (*ppos >= dbg->buf_len) {
+ mutex_unlock(&mdss_debug_lock);
return 0; /* done reading */
+ }
len = min(count, dbg->buf_len - (size_t) *ppos);
if (copy_to_user(user_buf, dbg->buf + *ppos, len)) {
pr_err("failed to copy to user\n");
+ mutex_unlock(&mdss_debug_lock);
return -EFAULT;
}
*ppos += len; /* increase offset */
+ mutex_unlock(&mdss_debug_lock);
return len;
}
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
index 153c733fdf43..67adc46d1e39 100644
--- a/drivers/video/fbdev/msm/mdss_dp.c
+++ b/drivers/video/fbdev/msm/mdss_dp.c
@@ -1013,9 +1013,10 @@ static int dp_get_cable_status(struct platform_device *pdev, u32 vote)
return hpd;
}
-static bool mdss_dp_is_dvi_mode(struct mdss_dp_drv_pdata *dp)
+static bool mdss_dp_sink_audio_supp(struct mdss_dp_drv_pdata *dp)
{
- return hdmi_edid_is_dvi_mode(dp->panel_data.panel_info.edid_data);
+ return hdmi_edid_is_audio_supported(
+ dp->panel_data.panel_info.edid_data);
}
static int dp_audio_info_setup(struct platform_device *pdev,
@@ -1290,6 +1291,23 @@ exit:
return ret;
}
+static u32 mdss_dp_calc_max_pclk_rate(struct mdss_dp_drv_pdata *dp)
+{
+ u32 bpp = mdss_dp_get_bpp(dp);
+ u32 max_link_rate_khz = dp->dpcd.max_link_rate *
+ (DP_LINK_RATE_MULTIPLIER / 100);
+ u32 max_data_rate_khz = dp->dpcd.max_lane_count *
+ max_link_rate_khz * 8 / 10;
+ u32 max_pclk_rate_khz = max_data_rate_khz / bpp;
+
+ pr_debug("bpp=%d, max_lane_cnt=%d, max_link_rate=%dKHz\n", bpp,
+ dp->dpcd.max_lane_count, max_link_rate_khz);
+ pr_debug("max_data_rate=%dKHz, max_pclk_rate=%dKHz\n",
+ max_data_rate_khz, max_pclk_rate_khz);
+
+ return max_pclk_rate_khz;
+}
+
static void mdss_dp_set_clock_rate(struct mdss_dp_drv_pdata *dp,
char *name, u32 rate)
{
@@ -1689,14 +1707,17 @@ static int mdss_dp_send_audio_notification(
goto end;
}
- if (!mdss_dp_is_dvi_mode(dp) || dp->audio_test_req) {
+ if (mdss_dp_sink_audio_supp(dp) || dp->audio_test_req) {
dp->audio_test_req = false;
+ pr_debug("sending audio notification\n");
flags |= MSM_EXT_DISP_HPD_AUDIO;
if (dp->ext_audio_data.intf_ops.hpd)
ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev,
dp->ext_audio_data.type, val, flags);
+ } else {
+ pr_debug("sink does not support audio\n");
}
end:
@@ -1981,6 +2002,7 @@ end:
static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp)
{
int ret;
+ u32 max_pclk_khz;
if (dp->sink_info_read)
return 0;
@@ -1998,6 +2020,10 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp)
goto notify;
}
+ max_pclk_khz = mdss_dp_calc_max_pclk_rate(dp);
+ hdmi_edid_set_max_pclk_rate(dp->panel_data.panel_info.edid_data,
+ min(dp->max_pclk_khz, max_pclk_khz));
+
ret = hdmi_edid_parser(dp->panel_data.panel_info.edid_data);
if (ret) {
pr_err("edid parse failed, setting default resolution\n");
@@ -4022,8 +4048,6 @@ static int mdss_dp_probe(struct platform_device *pdev)
pr_debug("done\n");
- dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES);
-
return 0;
probe_err:
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index 5f1dde279732..17722eac3006 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -34,7 +34,6 @@
#include "mdss_dsi_phy.h"
#include "mdss_dba_utils.h"
-#define XO_CLK_RATE 19200000
#define CMDLINE_DSI_CTL_NUM_STRING_LEN 2
/* Master structure to hold all the information about the DSI/panel */
@@ -1464,15 +1463,6 @@ int mdss_dsi_on(struct mdss_panel_data *pdata)
if (mipi->init_delay)
usleep_range(mipi->init_delay, mipi->init_delay);
- if (mipi->force_clk_lane_hs) {
- u32 tmp;
-
- tmp = MIPI_INP((ctrl_pdata->ctrl_base) + 0xac);
- tmp |= (1<<28);
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0xac, tmp);
- wmb();
- }
-
if (pdata->panel_info.type == MIPI_CMD_PANEL)
mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle,
MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_OFF);
@@ -1576,10 +1566,12 @@ static int mdss_dsi_unblank(struct mdss_panel_data *pdata)
mdss_dsi_clk_ctrl(sctrl, sctrl->dsi_clk_handle,
MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_ON);
- if (mdss_dsi_is_panel_on_lp(pdata)) {
+ if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_LP) {
pr_debug("%s: dsi_unblank with panel always on\n", __func__);
if (ctrl_pdata->low_power_config)
ret = ctrl_pdata->low_power_config(pdata, false);
+ if (!ret)
+ ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_LP;
goto error;
}
@@ -1644,6 +1636,8 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata, int power_state)
pr_debug("%s: low power state requested\n", __func__);
if (ctrl_pdata->low_power_config)
ret = ctrl_pdata->low_power_config(pdata, true);
+ if (!ret)
+ ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_LP;
goto error;
}
@@ -1686,7 +1680,8 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata, int power_state)
}
ATRACE_END("dsi_panel_off");
}
- ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT;
+ ctrl_pdata->ctrl_state &= ~(CTRL_STATE_PANEL_INIT |
+ CTRL_STATE_PANEL_LP);
}
error:
@@ -1866,7 +1861,7 @@ static void __mdss_dsi_dyn_refresh_config(
static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata)
{
- u32 esc_clk_rate = XO_CLK_RATE;
+ u32 esc_clk_rate_hz;
u32 pipe_delay, pipe_delay2 = 0, pll_delay;
u32 hsync_period = 0;
u32 pclk_to_esc_ratio, byte_to_esc_ratio, hr_bit_to_esc_ratio;
@@ -1893,9 +1888,11 @@ static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata)
pinfo = &pdata->panel_info;
pd = &(pinfo->mipi.dsi_phy_db);
- pclk_to_esc_ratio = (ctrl_pdata->pclk_rate / esc_clk_rate);
- byte_to_esc_ratio = (ctrl_pdata->byte_clk_rate / esc_clk_rate);
- hr_bit_to_esc_ratio = ((ctrl_pdata->byte_clk_rate * 4) / esc_clk_rate);
+ esc_clk_rate_hz = ctrl_pdata->esc_clk_rate_hz;
+ pclk_to_esc_ratio = (ctrl_pdata->pclk_rate / esc_clk_rate_hz);
+ byte_to_esc_ratio = (ctrl_pdata->byte_clk_rate / esc_clk_rate_hz);
+ hr_bit_to_esc_ratio = ((ctrl_pdata->byte_clk_rate * 4) /
+ esc_clk_rate_hz);
hsync_period = mdss_panel_get_htotal(pinfo, true);
pipe_delay = (hsync_period + 1) / pclk_to_esc_ratio;
@@ -1917,7 +1914,7 @@ static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata)
((pd->timing[4] >> 1) + 1)) / hr_bit_to_esc_ratio);
/* 130 us pll delay recommended by h/w doc */
- pll_delay = ((130 * esc_clk_rate) / 1000000) * 2;
+ pll_delay = ((130 * esc_clk_rate_hz) / 1000000) * 2;
MIPI_OUTP((ctrl_pdata->ctrl_base) + DSI_DYNAMIC_REFRESH_PIPE_DELAY,
pipe_delay);
@@ -2510,6 +2507,15 @@ static int mdss_dsi_register_mdp_callback(struct mdss_dsi_ctrl_pdata *ctrl,
return 0;
}
+static int mdss_dsi_register_clamp_handler(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct mdss_intf_ulp_clamp *clamp_handler)
+{
+ mutex_lock(&ctrl->mutex);
+ ctrl->clamp_handler = clamp_handler;
+ mutex_unlock(&ctrl->mutex);
+ return 0;
+}
+
static struct device_node *mdss_dsi_get_fb_node_cb(struct platform_device *pdev)
{
struct device_node *fb_node;
@@ -2694,6 +2700,10 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
rc = mdss_dsi_register_mdp_callback(ctrl_pdata,
(struct mdss_intf_recovery *)arg);
break;
+ case MDSS_EVENT_REGISTER_CLAMP_HANDLER:
+ rc = mdss_dsi_register_clamp_handler(ctrl_pdata,
+ (struct mdss_intf_ulp_clamp *)arg);
+ break;
case MDSS_EVENT_DSI_DYNAMIC_SWITCH:
mode = (u32)(unsigned long) arg;
mdss_dsi_switch_mode(pdata, mode);
@@ -3052,7 +3062,7 @@ static int mdss_dsi_set_clk_rates(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
rc = mdss_dsi_clk_set_link_rate(ctrl_pdata->dsi_clk_handle,
MDSS_DSI_LINK_ESC_CLK,
- 19200000,
+ ctrl_pdata->esc_clk_rate_hz,
MDSS_DSI_CLK_UPDATE_CLK_RATE_AT_ON);
if (rc) {
pr_err("%s: dsi_esc_clk - clk_set_rate failed\n",
@@ -4231,6 +4241,9 @@ int dsi_panel_device_register(struct platform_device *ctrl_pdev,
pr_debug("%s: pclk=%d, bclk=%d\n", __func__,
ctrl_pdata->pclk_rate, ctrl_pdata->byte_clk_rate);
+ ctrl_pdata->esc_clk_rate_hz = pinfo->esc_clk_rate_hz;
+ pr_debug("%s: esc clk=%d\n", __func__,
+ ctrl_pdata->esc_clk_rate_hz);
rc = mdss_dsi_get_dt_vreg_data(&ctrl_pdev->dev, pan_node,
&ctrl_pdata->panel_power_data, DSI_PANEL_PM);
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index df24352ff87d..2a76466abf3e 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -166,6 +166,7 @@ enum dsi_pm_type {
#define CTRL_STATE_PANEL_INIT BIT(0)
#define CTRL_STATE_MDP_ACTIVE BIT(1)
#define CTRL_STATE_DSI_ACTIVE BIT(2)
+#define CTRL_STATE_PANEL_LP BIT(3)
#define DSI_NON_BURST_SYNCH_PULSE 0
#define DSI_NON_BURST_SYNCH_EVENT 1
@@ -476,6 +477,7 @@ struct mdss_dsi_ctrl_pdata {
u32 byte_clk_rate;
u32 pclk_rate_bkp;
u32 byte_clk_rate_bkp;
+ u32 esc_clk_rate_hz;
bool refresh_clk_rate; /* flag to recalculate clk_rate */
struct dss_module_power panel_power_data;
struct dss_module_power power_data[DSI_MAX_PM]; /* for 8x10 */
@@ -483,6 +485,7 @@ struct mdss_dsi_ctrl_pdata {
struct mdss_hw *dsi_hw;
struct mdss_intf_recovery *recovery;
struct mdss_intf_recovery *mdp_callback;
+ struct mdss_intf_ulp_clamp *clamp_handler;
struct dsi_panel_cmds on_cmds;
struct dsi_panel_cmds post_dms_on_cmds;
@@ -696,6 +699,8 @@ void mdss_dsi_dsc_config(struct mdss_dsi_ctrl_pdata *ctrl,
struct dsc_desc *dsc);
void mdss_dsi_dfps_config_8996(struct mdss_dsi_ctrl_pdata *ctrl);
void mdss_dsi_set_burst_mode(struct mdss_dsi_ctrl_pdata *ctrl);
+void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
+ u32 bits, int set);
void mdss_dsi_set_reg(struct mdss_dsi_ctrl_pdata *ctrl, int off,
u32 mask, u32 val);
int mdss_dsi_phy_pll_reset_status(struct mdss_dsi_ctrl_pdata *ctrl);
diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c
index 734d3bee8fd0..9f4b7eb52492 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_host.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_host.c
@@ -41,6 +41,8 @@
#define LANE_SWAP_CTRL 0x0B0
#define LOGICAL_LANE_SWAP_CTRL 0x310
+#define MAX_BTA_WAIT_RETRY 5
+
#define CEIL(x, y) (((x) + ((y)-1)) / (y))
struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX];
@@ -600,7 +602,7 @@ error:
return rc;
}
-static void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
+void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
u32 bits, int set)
{
u32 data;
@@ -611,6 +613,7 @@ static void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
else
data &= ~bits;
MIPI_OUTP(ctrl->ctrl_base + 0x0ac, data);
+ wmb(); /* make sure write happens */
}
@@ -766,6 +769,8 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event)
u32 data0, data1, mask = 0, data_lane_en = 0;
struct mdss_dsi_ctrl_pdata *ctrl0, *ctrl1;
u32 ln0, ln1, ln_ctrl0, ln_ctrl1, i;
+ int rc = 0;
+
/*
* Add 2 ms delay suggested by HW team.
* Check clk lane stop state after every 200 us
@@ -787,9 +792,15 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event)
ctrl0 = mdss_dsi_get_ctrl_by_index(DSI_CTRL_0);
ctrl1 = mdss_dsi_get_ctrl_by_index(DSI_CTRL_1);
- if (ctrl0->recovery)
- ctrl0->recovery->fxn(ctrl0->recovery->data,
+ if (ctrl0->recovery) {
+ rc = ctrl0->recovery->fxn(ctrl0->recovery->data,
MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW);
+ if (rc < 0) {
+ pr_debug("%s: Target is in suspend/shutdown\n",
+ __func__);
+ return;
+ }
+ }
/*
* Disable PHY contention detection and receive.
* Configure the strength ctrl 1 register.
@@ -879,9 +890,15 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event)
*/
udelay(200);
} else {
- if (ctrl->recovery)
- ctrl->recovery->fxn(ctrl->recovery->data,
+ if (ctrl->recovery) {
+ rc = ctrl->recovery->fxn(ctrl->recovery->data,
MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW);
+ if (rc < 0) {
+ pr_debug("%s: Target is in suspend/shutdown\n",
+ __func__);
+ return;
+ }
+ }
/* Disable PHY contention detection and receive */
MIPI_OUTP((ctrl->phy_io.base) + 0x0188, 0);
@@ -1437,8 +1454,7 @@ static int mdss_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl)
if (ctrl->ctrl_state & CTRL_STATE_MDP_ACTIVE) {
mdss_dsi_wait4video_done(ctrl);
v_total = mdss_panel_get_vtotal(pinfo);
- v_blank = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_front_porch +
- pinfo->lcdc.v_pulse_width;
+ v_blank = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width;
if (pinfo->dynamic_fps && pinfo->current_fps)
fps = pinfo->current_fps;
else
@@ -1469,6 +1485,8 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
int ret = 0;
unsigned long flag;
int ignore_underflow = 0;
+ int retry_count = 0;
+ int in_blanking = 0;
if (ctrl_pdata == NULL) {
pr_err("%s: Invalid input data\n", __func__);
@@ -1494,7 +1512,25 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
reinit_completion(&ctrl_pdata->bta_comp);
mdss_dsi_enable_irq(ctrl_pdata, DSI_BTA_TERM);
spin_unlock_irqrestore(&ctrl_pdata->mdp_lock, flag);
+wait:
mdss_dsi_wait4video_eng_busy(ctrl_pdata);
+ if (ctrl_pdata->panel_mode == DSI_VIDEO_MODE) {
+ in_blanking = ctrl_pdata->mdp_callback->fxn(
+ ctrl_pdata->mdp_callback->data,
+ MDP_INTF_CALLBACK_CHECK_LINE_COUNT);
+ /* Try for maximum of 5 attempts */
+ if (in_blanking && (retry_count < MAX_BTA_WAIT_RETRY)) {
+ pr_debug("%s: not in active region\n", __func__);
+ retry_count++;
+ goto wait;
+ }
+ }
+ if (retry_count == MAX_BTA_WAIT_RETRY)
+ MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl",
+ "dsi0_phy", "dsi1_ctrl", "dsi1_phy",
+ "vbif", "vbif_nrt", "dbg_bus",
+ "vbif_dbg_bus", "panic");
+
/* mask out overflow errors */
if (ignore_underflow)
mdss_dsi_set_reg(ctrl_pdata, 0x10c, 0x0f0000, 0x0f0000);
@@ -2134,8 +2170,8 @@ static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl,
status = reg_val & DSI_INTR_CMD_DMA_DONE;
if (status) {
reg_val &= DSI_INTR_MASK_ALL;
- /* clear CMD DMA isr only */
- reg_val |= DSI_INTR_CMD_DMA_DONE;
+ /* clear CMD DMA and BTA_DONE isr only */
+ reg_val |= (DSI_INTR_CMD_DMA_DONE | DSI_INTR_BTA_DONE);
MIPI_OUTP(ctrl->ctrl_base + 0x0110, reg_val);
mdss_dsi_disable_irq_nosync(ctrl, DSI_CMD_TERM);
complete(&ctrl->dma_comp);
@@ -3030,6 +3066,13 @@ static bool mdss_dsi_fifo_status(struct mdss_dsi_ctrl_pdata *ctrl)
pr_err("%s: status=%x\n", __func__, status);
+ /*
+ * if DSI FIFO overflow is masked,
+ * do not report overflow error
+ */
+ if (MIPI_INP(base + 0x10c) & 0xf0000)
+ status = status & 0xaaaaffff;
+
if (status & 0x44440000) {/* DLNx_HS_FIFO_OVERFLOW */
dsi_send_events(ctrl, DSI_EV_DLNx_FIFO_OVERFLOW, 0);
/* Ignore FIFO EMPTY when overflow happens */
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index 7cc9ce6e034d..bf701e2a4ac5 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -2819,6 +2819,13 @@ static int mdss_panel_parse_dt(struct device_node *np,
MSM_DBA_CHIP_NAME_MAX_LEN);
}
+ rc = of_property_read_u32(np,
+ "qcom,mdss-dsi-host-esc-clk-freq-hz",
+ &pinfo->esc_clk_rate_hz);
+ if (rc)
+ pinfo->esc_clk_rate_hz = MDSS_DSI_MAX_ESC_CLK_RATE_HZ;
+ pr_debug("%s: esc clk %d\n", __func__, pinfo->esc_clk_rate_hz);
+
return 0;
error:
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index db27842eaccc..fe79b6fd52b4 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -303,10 +303,23 @@ static void mdss_fb_set_bl_brightness(struct led_classdev *led_cdev,
}
}
+static enum led_brightness mdss_fb_get_bl_brightness(
+ struct led_classdev *led_cdev)
+{
+ struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent);
+ enum led_brightness value;
+
+ MDSS_BL_TO_BRIGHT(value, mfd->bl_level, mfd->panel_info->bl_max,
+ mfd->panel_info->brightness_max);
+
+ return value;
+}
+
static struct led_classdev backlight_led = {
.name = "lcd-backlight",
.brightness = MDSS_MAX_BL_BRIGHTNESS / 2,
.brightness_set = mdss_fb_set_bl_brightness,
+ .brightness_get = mdss_fb_get_bl_brightness,
.max_brightness = MDSS_MAX_BL_BRIGHTNESS,
};
@@ -1262,6 +1275,7 @@ static int mdss_fb_probe(struct platform_device *pdev)
mfd->fb_imgType = MDP_RGBA_8888;
mfd->calib_mode_bl = 0;
mfd->unset_bl_level = U32_MAX;
+ mfd->bl_extn_level = -1;
mfd->pdev = pdev;
@@ -3422,6 +3436,14 @@ int mdss_fb_atomic_commit(struct fb_info *info,
mfd->msm_fb_backup.disp_commit.l_roi = commit_v1->left_roi;
mfd->msm_fb_backup.disp_commit.r_roi = commit_v1->right_roi;
mfd->msm_fb_backup.disp_commit.flags = commit_v1->flags;
+ if (commit_v1->flags & MDP_COMMIT_UPDATE_BRIGHTNESS) {
+ MDSS_BRIGHT_TO_BL(mfd->bl_extn_level, commit_v1->bl_level,
+ mfd->panel_info->bl_max,
+ mfd->panel_info->brightness_max);
+ if (!mfd->bl_extn_level && commit_v1->bl_level)
+ mfd->bl_extn_level = 1;
+ } else
+ mfd->bl_extn_level = -1;
mutex_lock(&mfd->mdp_sync_pt_data.sync_mutex);
atomic_inc(&mfd->mdp_sync_pt_data.commit_cnt);
diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h
index d64580a35775..321531c72a08 100644
--- a/drivers/video/fbdev/msm/mdss_fb.h
+++ b/drivers/video/fbdev/msm/mdss_fb.h
@@ -241,6 +241,10 @@ struct msm_mdp_interface {
out = (2 * (v) * (bl_max) + max_bright);\
do_div(out, 2 * max_bright);\
} while (0)
+#define MDSS_BL_TO_BRIGHT(out, v, bl_max, max_bright) do {\
+ out = ((v) * (max_bright));\
+ do_div(out, bl_max);\
+ } while (0)
struct mdss_fb_file_info {
struct file *file;
@@ -305,6 +309,7 @@ struct msm_fb_data_type {
u32 calib_mode_bl;
u32 ad_bl_level;
u32 bl_level;
+ int bl_extn_level;
u32 bl_scale;
u32 unset_bl_level;
bool allow_bl_update;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
index 3d8fe2ceebac..502bc1570609 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
@@ -137,6 +137,7 @@ struct hdmi_edid_ctrl {
u16 video_latency;
u32 present_3d;
u32 page_id;
+ bool basic_audio_supp;
u8 audio_data_block[MAX_NUMBER_ADB * MAX_AUDIO_DATA_BLOCK_SIZE];
int adb_size;
u8 spkr_alloc_data_block[MAX_SPKR_ALLOC_DATA_BLOCK_SIZE];
@@ -1289,6 +1290,14 @@ static void hdmi_edid_extract_sink_caps(struct hdmi_edid_ctrl *edid_ctrl,
return;
}
+ /* Check if sink supports basic audio */
+ if (in_buf[3] & BIT(6))
+ edid_ctrl->basic_audio_supp = true;
+ else
+ edid_ctrl->basic_audio_supp = false;
+ pr_debug("%s: basic audio supported: %s\n", __func__,
+ edid_ctrl->basic_audio_supp ? "true" : "false");
+
vsd = hdmi_edid_find_hfvsdb(in_buf);
if (vsd) {
@@ -2620,6 +2629,24 @@ void hdmi_edid_config_override(void *input, bool enable,
}
}
+void hdmi_edid_set_max_pclk_rate(void *input, u32 max_pclk_khz)
+{
+ struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
+
+ edid_ctrl->init_data.max_pclk_khz = max_pclk_khz;
+}
+
+bool hdmi_edid_is_audio_supported(void *input)
+{
+ struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
+
+ /*
+ * return true if basic audio is supported or if an audio
+ * data block was successfully parsed.
+ */
+ return (edid_ctrl->basic_audio_supp || edid_ctrl->adb_size);
+}
+
void hdmi_edid_deinit(void *input)
{
struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h
index 16efb6ee4014..557e9326a81d 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h
@@ -79,5 +79,7 @@ void hdmi_edid_get_hdr_data(void *edid_ctrl,
struct hdmi_edid_hdr_data **hdr_data);
void hdmi_edid_config_override(void *input, bool enable,
struct hdmi_edid_override_data *data);
+void hdmi_edid_set_max_pclk_rate(void *input, u32 max_pclk_khz);
+bool hdmi_edid_is_audio_supported(void *input);
#endif /* __HDMI_EDID_H__ */
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index 3cadfa4ecef7..171f44815430 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -2179,6 +2179,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
mdss_set_quirk(mdata, MDSS_QUIRK_HDR_SUPPORT_ENABLED);
break;
case MDSS_MDP_HW_REV_320:
+ mdss_set_quirk(mdata, MDSS_QUIRK_DSC_RIGHT_ONLY_PU);
+ mdss_set_quirk(mdata, MDSS_QUIRK_DSC_2SLICE_PU_THRPUT);
case MDSS_MDP_HW_REV_330:
mdata->max_target_zorder = 7; /* excluding base layer */
mdata->max_cursor_size = 512;
@@ -2206,6 +2208,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
set_bit(MDSS_QOS_TS_PREFILL, mdata->mdss_qos_map);
set_bit(MDSS_QOS_IB_NOCR, mdata->mdss_qos_map);
set_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, mdata->mdss_qos_map);
+ set_bit(MDSS_QOS_WB_QOS, mdata->mdss_qos_map);
+ set_bit(MDSS_CAPS_CWB_SUPPORTED, mdata->mdss_caps_map);
set_bit(MDSS_CAPS_YUV_CONFIG, mdata->mdss_caps_map);
set_bit(MDSS_CAPS_SCM_RESTORE_NOT_REQUIRED,
mdata->mdss_caps_map);
@@ -2216,8 +2220,6 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
set_bit(MDSS_CAPS_MDP_VOTE_CLK_NOT_SUPPORTED,
mdata->mdss_caps_map);
mdss_mdp_init_default_prefill_factors(mdata);
- mdss_set_quirk(mdata, MDSS_QUIRK_DSC_RIGHT_ONLY_PU);
- mdss_set_quirk(mdata, MDSS_QUIRK_DSC_2SLICE_PU_THRPUT);
mdss_set_quirk(mdata, MDSS_QUIRK_MMSS_GDSC_COLLAPSE);
mdss_set_quirk(mdata, MDSS_QUIRK_MDP_CLK_SET_RATE);
mdss_set_quirk(mdata, MDSS_QUIRK_DMA_BI_DIR);
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index 36a866685f21..2fd047edd3e8 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -164,6 +164,14 @@ enum mdss_mdp_mixer_mux {
MDSS_MDP_MIXER_MUX_RIGHT,
};
+enum mdss_secure_transition {
+ SECURE_TRANSITION_NONE,
+ SD_NON_SECURE_TO_SECURE,
+ SD_SECURE_TO_NON_SECURE,
+ SC_NON_SECURE_TO_SECURE,
+ SC_SECURE_TO_NON_SECURE,
+};
+
static inline enum mdss_mdp_sspp_index get_pipe_num_from_ndx(u32 ndx)
{
u32 id;
@@ -421,6 +429,9 @@ struct mdss_mdp_ctl_intfs_ops {
/* to update lineptr, [1..yres] - enable, 0 - disable */
int (*update_lineptr)(struct mdss_mdp_ctl *ctl, bool enable);
int (*avr_ctrl_fnc)(struct mdss_mdp_ctl *, bool enable);
+
+ /* to wait for vsync */
+ int (*wait_for_vsync_fnc)(struct mdss_mdp_ctl *ctl);
};
struct mdss_mdp_cwb {
@@ -650,6 +661,7 @@ struct mdss_mdp_img_data {
struct dma_buf *srcp_dma_buf;
struct dma_buf_attachment *srcp_attachment;
struct sg_table *srcp_table;
+ struct ion_handle *ihandle;
};
enum mdss_mdp_data_state {
@@ -691,6 +703,8 @@ struct pp_hist_col_info {
char __iomem *base;
u32 intr_shift;
u32 disp_num;
+ u32 expect_sum;
+ u32 next_sum;
struct mdss_mdp_ctl *ctl;
};
@@ -953,6 +967,8 @@ struct mdss_overlay_private {
struct kthread_worker worker;
struct kthread_work vsync_work;
struct task_struct *thread;
+
+ u8 secure_transition_state;
};
struct mdss_mdp_set_ot_params {
@@ -1976,6 +1992,7 @@ void mdss_mdp_disable_hw_irq(struct mdss_data_type *mdata);
void mdss_mdp_set_supported_formats(struct mdss_data_type *mdata);
int mdss_mdp_dest_scaler_setup_locked(struct mdss_mdp_mixer *mixer);
+void *mdss_mdp_intf_get_ctx_base(struct mdss_mdp_ctl *ctl, int intf_num);
#ifdef CONFIG_FB_MSM_MDP_NONE
struct mdss_data_type *mdss_mdp_get_mdata(void)
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index c31df04643d6..bd70535e79f9 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -73,8 +73,18 @@ static void __mdss_mdp_mixer_write_cfg(struct mdss_mdp_mixer *mixer,
static inline u64 fudge_factor(u64 val, u32 numer, u32 denom)
{
- u64 result = (val * (u64)numer);
- do_div(result, denom);
+ u64 result = val;
+
+ if (val) {
+ u64 temp = -1UL;
+
+ do_div(temp, val);
+ if (temp > numer) {
+ /* no overflow, so we can do the operation*/
+ result = (val * (u64)numer);
+ do_div(result, denom);
+ }
+ }
return result;
}
@@ -4486,11 +4496,15 @@ end:
*/
static void mdss_mdp_pipe_reset(struct mdss_mdp_mixer *mixer, bool is_recovery)
{
- unsigned long pipe_map = mixer->pipe_mapped;
+ unsigned long pipe_map;
u32 bit = 0;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
bool sw_rst_avail = mdss_mdp_pipe_is_sw_reset_available(mdata);
+ if (!mixer)
+ return;
+
+ pipe_map = mixer->pipe_mapped;
pr_debug("pipe_map=0x%lx\n", pipe_map);
for_each_set_bit_from(bit, &pipe_map, MAX_PIPES_PER_LM) {
struct mdss_mdp_pipe *pipe;
@@ -5706,6 +5720,15 @@ static void mdss_mdp_force_border_color(struct mdss_mdp_ctl *ctl)
ctl->mixer_right->params_changed++;
}
+static bool mdss_mdp_handle_backlight_extn(struct mdss_mdp_ctl *ctl)
+{
+ if (ctl->intf_type == MDSS_INTF_DSI && !ctl->is_video_mode &&
+ ctl->mfd->bl_extn_level >= 0)
+ return true;
+ else
+ return false;
+}
+
int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
struct mdss_mdp_commit_cb *commit_cb)
{
@@ -5874,6 +5897,15 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
if (ctl->ops.wait_pingpong && !mdata->serialize_wait4pp)
mdss_mdp_display_wait4pingpong(ctl, false);
+ /*
+ * If backlight needs to change, wait for 1 vsync before setting
+ * PCC and kickoff
+ */
+ if (mdss_mdp_handle_backlight_extn(ctl) &&
+ ctl->ops.wait_for_vsync_fnc) {
+ ret = ctl->ops.wait_for_vsync_fnc(ctl);
+ }
+
/* Moved pp programming to post ping pong */
if (!ctl->is_video_mode && ctl->mfd &&
ctl->mfd->dcm_state != DTM_ENTER) {
@@ -6022,6 +6054,17 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
if (ret)
pr_warn("ctl %d error displaying frame\n", ctl->num);
+ /* update backlight in commit */
+ if (mdss_mdp_handle_backlight_extn(ctl)) {
+ if (!IS_CALIB_MODE_BL(ctl->mfd) && (!ctl->mfd->ext_bl_ctrl ||
+ !ctl->mfd->bl_level)) {
+ mutex_lock(&ctl->mfd->bl_lock);
+ mdss_fb_set_backlight(ctl->mfd,
+ ctl->mfd->bl_extn_level);
+ mutex_unlock(&ctl->mfd->bl_lock);
+ }
+ }
+
ctl->play_cnt++;
ATRACE_END("flush_kickoff");
diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
index 294e05c2fbb0..7d495232c198 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -634,15 +634,6 @@ enum mdss_mdp_dspp_index {
#define MDSS_MDP_DSPP_OP_PA_SIX_ZONE_SAT_MASK BIT(30)
#define MDSS_MDP_DSPP_OP_PA_SIX_ZONE_VAL_MASK BIT(31)
-enum mdss_mpd_intf_index {
- MDSS_MDP_NO_INTF,
- MDSS_MDP_INTF0,
- MDSS_MDP_INTF1,
- MDSS_MDP_INTF2,
- MDSS_MDP_INTF3,
- MDSS_MDP_MAX_INTF
-};
-
#define MDSS_MDP_REG_INTF_TIMING_ENGINE_EN 0x000
#define MDSS_MDP_REG_INTF_CONFIG 0x004
#define MDSS_MDP_REG_INTF_HSYNC_CTL 0x008
@@ -703,6 +694,8 @@ enum mdss_mpd_intf_index {
#define MDSS_MDP_PANEL_FORMAT_RGB888 0x213F
#define MDSS_MDP_PANEL_FORMAT_RGB666 0x212A
+#define MDSS_MDP_REG_DSI_ULP_CLAMP_VALUE 0x064
+
#define MDSS_MDP_PANEL_FORMAT_PACK_ALIGN_MSB BIT(7)
enum mdss_mdp_pingpong_index {
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
index dfd6226ce602..c9ce56fb96a4 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
@@ -103,6 +103,7 @@ struct mdss_mdp_cmd_ctx {
struct mdss_intf_recovery intf_recovery;
struct mdss_intf_recovery intf_mdp_callback;
+ struct mdss_intf_ulp_clamp intf_clamp_handler;
struct mdss_mdp_cmd_ctx *sync_ctx; /* for partial update */
u32 pp_timeout_report_cnt;
bool pingpong_split_slave;
@@ -1197,7 +1198,7 @@ static int mdss_mdp_cmd_wait4readptr(struct mdss_mdp_cmd_ctx *ctx)
return rc;
}
-static void mdss_mdp_cmd_intf_callback(void *data, int event)
+static int mdss_mdp_cmd_intf_callback(void *data, int event)
{
struct mdss_mdp_cmd_ctx *ctx = data;
struct mdss_mdp_pp_tear_check *te = NULL;
@@ -1206,11 +1207,11 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event)
if (!data) {
pr_err("%s: invalid ctx\n", __func__);
- return;
+ return -EINVAL;
}
if (!ctx->ctl)
- return;
+ return -EINVAL;
switch (event) {
case MDP_INTF_CALLBACK_DSI_WAIT:
@@ -1222,7 +1223,7 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event)
* just return
*/
if (ctx->intf_stopped || !is_pingpong_split(ctx->ctl->mfd))
- return;
+ return -EINVAL;
atomic_inc(&ctx->rdptr_cnt);
/* enable clks and rd_ptr interrupt */
@@ -1231,7 +1232,7 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event)
mixer = mdss_mdp_mixer_get(ctx->ctl, MDSS_MDP_MIXER_MUX_LEFT);
if (!mixer) {
pr_err("%s: null mixer\n", __func__);
- return;
+ return -EINVAL;
}
/* wait for read pointer */
@@ -1255,6 +1256,7 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event)
pr_debug("%s: unhandled event=%d\n", __func__, event);
break;
}
+ return 0;
}
static void mdss_mdp_cmd_lineptr_done(void *arg)
@@ -1280,7 +1282,33 @@ static void mdss_mdp_cmd_lineptr_done(void *arg)
spin_unlock(&ctx->clk_lock);
}
-static void mdss_mdp_cmd_intf_recovery(void *data, int event)
+static int mdss_mdp_cmd_intf_clamp_ctrl(void *data, int intf_num, bool enable)
+{
+ struct mdss_mdp_ctl *ctl = data;
+ char __iomem *ctx_base = NULL;
+
+ if (!data) {
+ pr_err("%s: invalid ctl\n", __func__);
+ return -EINVAL;
+ }
+
+ ctx_base =
+ (char __iomem *)mdss_mdp_intf_get_ctx_base(ctl, intf_num);
+ if (!ctx_base) {
+ pr_err("%s: invalid ctx\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: intf num = %d, enable = %d\n",
+ __func__, intf_num, enable);
+
+ writel_relaxed(enable, (ctx_base + MDSS_MDP_REG_DSI_ULP_CLAMP_VALUE));
+ wmb(); /* ensure clamp is enabled */
+
+ return 0;
+}
+
+static int mdss_mdp_cmd_intf_recovery(void *data, int event)
{
struct mdss_mdp_cmd_ctx *ctx = data;
unsigned long flags;
@@ -1288,11 +1316,11 @@ static void mdss_mdp_cmd_intf_recovery(void *data, int event)
if (!data) {
pr_err("%s: invalid ctx\n", __func__);
- return;
+ return -EINVAL;
}
if (!ctx->ctl)
- return;
+ return -EINVAL;
/*
* Currently, only intf_fifo_underflow is
@@ -1302,7 +1330,7 @@ static void mdss_mdp_cmd_intf_recovery(void *data, int event)
if (event != MDP_INTF_DSI_CMD_FIFO_UNDERFLOW) {
pr_warn("%s: unsupported recovery event:%d\n",
__func__, event);
- return;
+ return -EPERM;
}
if (atomic_read(&ctx->koff_cnt)) {
@@ -1326,6 +1354,7 @@ static void mdss_mdp_cmd_intf_recovery(void *data, int event)
if (notify_frame_timeout)
mdss_mdp_ctl_notify(ctx->ctl, MDP_NOTIFY_FRAME_TIMEOUT);
+ return 0;
}
static void mdss_mdp_cmd_pingpong_done(void *arg)
@@ -2917,6 +2946,41 @@ static void __mdss_mdp_kickoff(struct mdss_mdp_ctl *ctl,
}
}
+int mdss_mdp_cmd_wait4_vsync(struct mdss_mdp_ctl *ctl)
+{
+ int rc = 0;
+ struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX];
+
+ if (!ctx) {
+ pr_err("invalid context to wait for vsync\n");
+ return rc;
+ }
+
+ atomic_inc(&ctx->rdptr_cnt);
+
+ /* enable clks and rd_ptr interrupt */
+ mdss_mdp_setup_vsync(ctx, true);
+
+ /* wait for read pointer */
+ MDSS_XLOG(atomic_read(&ctx->rdptr_cnt));
+ pr_debug("%s: wait for vsync cnt:%d\n",
+ __func__, atomic_read(&ctx->rdptr_cnt));
+
+ rc = mdss_mdp_cmd_wait4readptr(ctx);
+
+ /* wait for 1ms to make sure we are out from trigger window */
+ usleep_range(1000, 1010);
+
+ /* disable rd_ptr interrupt */
+ mdss_mdp_setup_vsync(ctx, false);
+
+ MDSS_XLOG(ctl->num);
+ pr_debug("%s: out from wait for rd_ptr ctl:%d\n", __func__, ctl->num);
+
+ return rc;
+}
+
+
/*
* There are 3 partial update possibilities
* left only ==> enable left pingpong_done
@@ -3170,6 +3234,14 @@ int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl,
ctx->default_pp_num, NULL, NULL);
memset(ctx, 0, sizeof(*ctx));
+ /* intf stopped, no more kickoff */
+ ctx->intf_stopped = 1;
+ /*
+ * Restore clamp handler as it might get called later
+ * when DSI panel events are handled.
+ */
+ ctx->intf_clamp_handler.fxn = mdss_mdp_cmd_intf_clamp_ctrl;
+ ctx->intf_clamp_handler.data = ctl;
return 0;
}
@@ -3310,12 +3382,21 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
(void *)&ctx->intf_mdp_callback,
CTL_INTF_EVENT_FLAG_DEFAULT);
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_CLAMP_HANDLER,
+ (void *)&ctx->intf_clamp_handler,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
+
mdss_mdp_tearcheck_enable(ctl, true);
ctx->intf_stopped = 0;
if (sctx)
sctx->intf_stopped = 0;
-
+ /*
+ * Tearcheck was disabled while entering LP2 state.
+ * Enable it back to allow updates in LP1 state.
+ */
+ mdss_mdp_tearcheck_enable(ctl, true);
goto end;
}
}
@@ -3364,6 +3445,10 @@ panel_events:
goto end;
}
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_CLAMP_HANDLER,
+ NULL, CTL_INTF_EVENT_FLAG_DEFAULT);
+
pr_debug("%s: turn off panel\n", __func__);
ctl->intf_ctx[MASTER_CTX] = NULL;
ctl->intf_ctx[SLAVE_CTX] = NULL;
@@ -3373,6 +3458,7 @@ panel_events:
ctl->ops.add_vsync_handler = NULL;
ctl->ops.remove_vsync_handler = NULL;
ctl->ops.reconfigure = NULL;
+ ctl->ops.wait_for_vsync_fnc = NULL;
end:
if (!IS_ERR_VALUE(ret)) {
@@ -3496,6 +3582,14 @@ static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl,
ctx->intf_mdp_callback.fxn = mdss_mdp_cmd_intf_callback;
ctx->intf_mdp_callback.data = ctx;
+ ctx->intf_clamp_handler.fxn = mdss_mdp_cmd_intf_clamp_ctrl;
+ ctx->intf_clamp_handler.data = ctl;
+
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_CLAMP_HANDLER,
+ (void *)&ctx->intf_clamp_handler,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
+
ctx->intf_stopped = 0;
pr_debug("%s: ctx=%pK num=%d aux=%d\n", __func__, ctx,
@@ -3773,6 +3867,7 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl)
ctl->ops.pre_programming = mdss_mdp_cmd_pre_programming;
ctl->ops.update_lineptr = mdss_mdp_cmd_update_lineptr;
ctl->ops.panel_disable_cfg = mdss_mdp_cmd_panel_disable_cfg;
+ ctl->ops.wait_for_vsync_fnc = mdss_mdp_cmd_wait4_vsync;
pr_debug("%s:-\n", __func__);
return 0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
index 533239c99345..a3511a1a07ef 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
@@ -83,6 +83,8 @@ struct mdss_mdp_video_ctx {
struct mutex vsync_mtx;
struct list_head vsync_handlers;
struct mdss_intf_recovery intf_recovery;
+ struct mdss_intf_recovery intf_mdp_callback;
+ struct mdss_intf_ulp_clamp intf_clamp_handler;
struct work_struct early_wakeup_dfps_work;
atomic_t lineptr_ref;
@@ -149,6 +151,23 @@ static int mdss_mdp_intf_intr2index(u32 intr_type)
return index;
}
+void *mdss_mdp_intf_get_ctx_base(struct mdss_mdp_ctl *ctl, int intf_num)
+{
+ struct mdss_mdp_video_ctx *head;
+ int i = 0;
+
+ if (!ctl)
+ return NULL;
+
+ head = ctl->mdata->video_intf;
+ for (i = 0; i < ctl->mdata->nintf; i++) {
+ if (head[i].intf_num == intf_num)
+ return (void *)head[i].base;
+ }
+
+ return NULL;
+}
+
int mdss_mdp_set_intf_intr_callback(struct mdss_mdp_video_ctx *ctx,
u32 intr_type, void (*fnc_ptr)(void *), void *arg)
{
@@ -315,7 +334,30 @@ int mdss_mdp_video_addr_setup(struct mdss_data_type *mdata,
return 0;
}
-static void mdss_mdp_video_intf_recovery(void *data, int event)
+static int mdss_mdp_video_intf_clamp_ctrl(void *data, int intf_num, bool enable)
+{
+ struct mdss_mdp_video_ctx *ctx = data;
+
+ if (!data) {
+ pr_err("%s: invalid ctl\n", __func__);
+ return -EINVAL;
+ }
+
+ if (intf_num != ctx->intf_num) {
+ pr_err("%s: invalid intf num\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: ctx intf num = %d, enable = %d\n",
+ __func__, ctx->intf_num, enable);
+
+ mdp_video_write(ctx, MDSS_MDP_REG_DSI_ULP_CLAMP_VALUE, enable);
+ wmb(); /* ensure clamp is enabled */
+
+ return 0;
+}
+
+static int mdss_mdp_video_intf_recovery(void *data, int event)
{
struct mdss_mdp_video_ctx *ctx;
struct mdss_mdp_ctl *ctl = data;
@@ -327,7 +369,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
if (!data) {
pr_err("%s: invalid ctl\n", __func__);
- return;
+ return -EINVAL;
}
/*
@@ -338,7 +380,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
if (event != MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW) {
pr_warn("%s: unsupported recovery event:%d\n",
__func__, event);
- return;
+ return -EPERM;
}
ctx = ctl->intf_ctx[MASTER_CTX];
@@ -353,7 +395,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
clk_rate = DIV_ROUND_UP_ULL(clk_rate, 1000); /* in kHz */
if (!clk_rate) {
pr_err("Unable to get proper clk_rate\n");
- return;
+ return -EINVAL;
}
/*
* calculate clk_period as pico second to maintain good
@@ -363,7 +405,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
clk_period = DIV_ROUND_UP_ULL(1000000000, clk_rate);
if (!clk_period) {
pr_err("Unable to calculate clock period\n");
- return;
+ return -EINVAL;
}
min_ln_cnt = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width;
active_lns_cnt = pinfo->yres;
@@ -389,7 +431,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
!ctx->timegen_en) {
pr_warn("Target is in suspend or shutdown pending\n");
mutex_unlock(&ctl->offlock);
- return;
+ return -EPERM;
}
line_cnt = mdss_mdp_video_line_count(ctl);
@@ -399,7 +441,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
pr_debug("%s, Needed lines left line_cnt=%d\n",
__func__, line_cnt);
mutex_unlock(&ctl->offlock);
- return;
+ return 0;
} else {
pr_warn("line count is less. line_cnt = %d\n",
line_cnt);
@@ -1668,12 +1710,42 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
return 0;
}
+static int mdss_mdp_video_splash_handoff(struct mdss_mdp_ctl *ctl)
+{
+ int i, ret = 0;
+ u32 data, flush;
+
+ ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_BEGIN,
+ NULL, CTL_INTF_EVENT_FLAG_DEFAULT);
+
+ if (ret) {
+ pr_err("%s:ctl%d failed to handle 'CONT_SPLASH_BEGIN' event\n"
+ , __func__, ctl->num);
+ return ret;
+ }
+
+ /* clear up mixer0 and mixer1 */
+ flush = 0;
+ for (i = 0; i < 2; i++) {
+ data = mdss_mdp_ctl_read(ctl,
+ MDSS_MDP_REG_CTL_LAYER(i));
+ if (data) {
+ mdss_mdp_ctl_write(ctl,
+ MDSS_MDP_REG_CTL_LAYER(i),
+ MDSS_MDP_LM_BORDER_COLOR);
+ flush |= (0x40 << i);
+ }
+ }
+ mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush);
+
+ return ret;
+}
+
int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl,
bool handoff)
{
struct mdss_panel_data *pdata;
- int i, ret = 0, off;
- u32 data, flush;
+ int ret = 0, off;
struct mdss_mdp_video_ctx *ctx, *sctx = NULL;
struct mdss_mdp_ctl *sctl;
@@ -1707,29 +1779,20 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl,
}
if (!handoff) {
- ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_BEGIN,
- NULL, CTL_INTF_EVENT_FLAG_DEFAULT);
- if (ret) {
- pr_err("%s: Failed to handle 'CONT_SPLASH_BEGIN' event\n"
- , __func__);
- return ret;
- }
+ ret = mdss_mdp_video_splash_handoff(ctl);
- /* clear up mixer0 and mixer1 */
- flush = 0;
- for (i = 0; i < 2; i++) {
- data = mdss_mdp_ctl_read(ctl,
- MDSS_MDP_REG_CTL_LAYER(i));
- if (data) {
- mdss_mdp_ctl_write(ctl,
- MDSS_MDP_REG_CTL_LAYER(i),
- MDSS_MDP_LM_BORDER_COLOR);
- flush |= (0x40 << i);
- }
- }
- mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush);
+ if (!ret && sctl)
+ ret = mdss_mdp_video_splash_handoff(sctl);
+
+ if (ret)
+ return ret;
mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0);
+
+ if (sctx)
+ mdp_video_write(sctx,
+ MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0);
+
mdss_mdp_video_timegen_flush(ctl, sctx);
/* wait for 1 VSYNC for the pipe to be unstaged */
@@ -1738,6 +1801,12 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl,
ret = mdss_mdp_ctl_intf_event(ctl,
MDSS_EVENT_CONT_SPLASH_FINISH, NULL,
CTL_INTF_EVENT_FLAG_DEFAULT);
+
+ if (!ret && sctl)
+ ret = mdss_mdp_ctl_intf_event(sctl,
+ MDSS_EVENT_CONT_SPLASH_FINISH, NULL,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
+
}
return ret;
@@ -1790,7 +1859,7 @@ static void mdss_mdp_fetch_end_config(struct mdss_mdp_video_ctx *ctx,
static void mdss_mdp_fetch_start_config(struct mdss_mdp_video_ctx *ctx,
struct mdss_mdp_ctl *ctl)
{
- int fetch_start, fetch_enable, v_total, h_total;
+ int fetch_start = 0, fetch_enable, v_total, h_total;
struct mdss_data_type *mdata;
struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info;
@@ -1799,6 +1868,15 @@ static void mdss_mdp_fetch_start_config(struct mdss_mdp_video_ctx *ctx,
pinfo->prg_fet = mdss_mdp_get_prefetch_lines(pinfo);
if (!pinfo->prg_fet) {
pr_debug("programmable fetch is not needed/supported\n");
+
+ fetch_enable = mdp_video_read(ctx, MDSS_MDP_REG_INTF_CONFIG);
+ fetch_enable &= ~BIT(31);
+
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_CONFIG, fetch_enable);
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_PROG_FETCH_START,
+ fetch_start);
+
+ MDSS_XLOG(ctx->intf_num, fetch_enable, fetch_start);
return;
}
@@ -1896,6 +1974,58 @@ static void mdss_mdp_handoff_programmable_fetch(struct mdss_mdp_ctl *ctl,
}
}
+static int mdss_mdp_video_intf_callback(void *data, int event)
+{
+ struct mdss_mdp_video_ctx *ctx;
+ struct mdss_mdp_ctl *ctl = data;
+ struct mdss_panel_info *pinfo;
+ u32 line_cnt, min_ln_cnt, active_lns_cnt, line_buff = 50;
+
+ if (!data) {
+ pr_err("%s: invalid ctl\n", __func__);
+ return -EINVAL;
+ }
+
+ ctx = ctl->intf_ctx[MASTER_CTX];
+ pr_debug("%s: ctl num = %d, event = %d\n",
+ __func__, ctl->num, event);
+
+ if (!ctl->is_video_mode)
+ return 0;
+
+ pinfo = &ctl->panel_data->panel_info;
+ min_ln_cnt = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width;
+ active_lns_cnt = pinfo->yres;
+
+ switch (event) {
+ case MDP_INTF_CALLBACK_CHECK_LINE_COUNT:
+ if (!ctl || !ctx || !ctx->timegen_en) {
+ pr_debug("%s: no need to check for active line\n",
+ __func__);
+ goto end;
+ }
+
+ line_cnt = mdss_mdp_video_line_count(ctl);
+
+ if ((line_cnt >= min_ln_cnt) && (line_cnt <
+ (min_ln_cnt + active_lns_cnt - line_buff))) {
+ pr_debug("%s: line count is within active range=%d\n",
+ __func__, line_cnt);
+ goto end;
+ } else {
+ pr_debug("line count is less. line_cnt = %d\n",
+ line_cnt);
+ return -EPERM;
+ }
+ break;
+ default:
+ pr_debug("%s: unhandled event!\n", __func__);
+ break;
+ }
+end:
+ return 0;
+}
+
static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_video_ctx *ctx, struct mdss_panel_info *pinfo)
{
@@ -1929,6 +2059,20 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl,
pr_err("Failed to register intf recovery handler\n");
return -EINVAL;
}
+
+ ctx->intf_mdp_callback.fxn = mdss_mdp_video_intf_callback;
+ ctx->intf_mdp_callback.data = ctl;
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_MDP_CALLBACK,
+ (void *)&ctx->intf_mdp_callback,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
+
+ ctx->intf_clamp_handler.fxn = mdss_mdp_video_intf_clamp_ctrl;
+ ctx->intf_clamp_handler.data = ctx;
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_CLAMP_HANDLER,
+ (void *)&ctx->intf_clamp_handler,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
} else {
ctx->intf_recovery.fxn = NULL;
ctx->intf_recovery.data = NULL;
@@ -2348,6 +2492,7 @@ int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl)
ctl->ops.early_wake_up_fnc = mdss_mdp_video_early_wake_up;
ctl->ops.update_lineptr = mdss_mdp_video_lineptr_ctrl;
ctl->ops.avr_ctrl_fnc = mdss_mdp_video_avr_ctrl;
+ ctl->ops.wait_for_vsync_fnc = NULL;
return 0;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index c9e32d69d444..7c6938d40e0b 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_layer.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c
@@ -1123,6 +1123,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
int ret = 0;
u32 left_lm_w = left_lm_w_from_mfd(mfd);
u64 flags;
+ bool is_right_blend = false;
struct mdss_mdp_mixer *mixer = NULL;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
@@ -1221,6 +1222,15 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
goto end;
}
+ /* scaling is not allowed for solid_fill layers */
+ if ((pipe->flags & MDP_SOLID_FILL) &&
+ ((pipe->src.w != pipe->dst.w) ||
+ (pipe->src.h != pipe->dst.h))) {
+ pr_err("solid fill pipe:%d cannot have scaling\n", pipe->num);
+ ret = -EINVAL;
+ goto end;
+ }
+
/*
* unstage the pipe if it's current z_order does not match with new
* z_order because client may only call the validate.
@@ -1234,6 +1244,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
* staging, same pipe will be stagged on both layer mixers.
*/
if (mdata->has_src_split) {
+ is_right_blend = pipe->is_right_blend;
if (left_blend_pipe) {
if (__validate_pipe_priorities(left_blend_pipe, pipe)) {
pr_err("priority limitation. left:%d rect:%d, right:%d rect:%d\n",
@@ -1245,7 +1256,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
goto end;
} else {
pr_debug("pipe%d is a right_pipe\n", pipe->num);
- pipe->is_right_blend = true;
+ is_right_blend = true;
}
} else if (pipe->is_right_blend) {
/*
@@ -1254,7 +1265,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
*/
mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left);
mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_right);
- pipe->is_right_blend = false;
+ is_right_blend = false;
}
if (is_split_lm(mfd) && __layer_needs_src_split(layer)) {
@@ -1280,6 +1291,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
}
pipe->src_split_req = false;
}
+ pipe->is_right_blend = is_right_blend;
}
pipe->multirect.mode = vinfo->multirect.mode;
@@ -2261,6 +2273,78 @@ static int __validate_multirect(struct msm_fb_data_type *mfd,
return 0;
}
+static int __check_source_split(struct mdp_input_layer *layer_list,
+ struct mdss_mdp_pipe **pipe_list, u32 index,
+ u32 left_lm_w, struct mdss_mdp_pipe **left_blend_pipe)
+{
+ int i = index - 1;
+ struct mdp_input_layer *curr, *prev;
+ struct mdp_rect *left, *right;
+ bool match = false;
+ struct mdss_mdp_pipe *left_pipe = NULL;
+
+ /*
+ * check if current layer is at same z_order as any of the
+ * previous layers, and fail if any or both are async layers,
+ * as async layers should have unique z_order.
+ *
+ * If it has same z_order and qualifies as a right blend,
+ * pass a pointer to the pipe representing previous overlay or
+ * in other terms left blend layer.
+ *
+ * Following logic of selecting left_blend has an inherent
+ * assumption that layer list is sorted on dst_x within a
+ * same z_order. Otherwise it will fail based on z_order checks.
+ */
+ curr = &layer_list[index];
+
+ while (i >= 0) {
+ if (layer_list[i].z_order == curr->z_order) {
+ pr_debug("z=%d found match @ %d of %d\n",
+ curr->z_order, i, index);
+ match = true;
+ break;
+ }
+ i--;
+ }
+
+ if (match) {
+ left_pipe = pipe_list[i];
+ prev = &layer_list[i];
+ left = &prev->dst_rect;
+ right = &curr->dst_rect;
+
+ if ((curr->flags & MDP_LAYER_ASYNC)
+ || (prev->flags & MDP_LAYER_ASYNC)) {
+ curr->error_code = -EINVAL;
+ pr_err("async curr should have unique z_order\n");
+ return curr->error_code;
+ }
+
+ /*
+ * check if curr is right blend by checking it's
+ * directly to the right.
+ */
+ if (((left->x + left->w) == right->x) &&
+ (left->y == right->y) && (left->h == right->h)) {
+ *left_blend_pipe = left_pipe;
+ MDSS_XLOG(curr->z_order, i, index);
+ }
+
+ /*
+ * if the curr is right at the left lm boundary and
+ * src split is not required then right blend is not
+ * required as it will lie only on the left mixer
+ */
+ if (!__layer_needs_src_split(prev) &&
+ ((left->x + left->w) == left_lm_w))
+ *left_blend_pipe = NULL;
+ }
+
+ return 0;
+}
+
+
/*
* __validate_layers() - validate input layers
* @mfd: Framebuffer data structure for display
@@ -2291,13 +2375,14 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
struct mdss_mdp_mixer *mixer = NULL;
- struct mdp_input_layer *layer, *prev_layer, *layer_list;
+ struct mdp_input_layer *layer, *layer_list;
struct mdss_mdp_validate_info_t *validate_info_list = NULL;
bool is_single_layer = false, force_validate;
enum layer_pipe_q pipe_q_type;
enum layer_zorder_used zorder_used[MDSS_MDP_MAX_STAGE] = {0};
enum mdss_mdp_pipe_rect rect_num;
struct mdp_destination_scaler_data *ds_data;
+ struct mdss_mdp_pipe *pipe_list[MAX_LAYER_COUNT] = {0};
ret = mutex_lock_interruptible(&mdp5_data->ov_lock);
if (ret)
@@ -2369,49 +2454,10 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
dst_x = layer->dst_rect.x;
left_blend_pipe = NULL;
- prev_layer = (i > 0) ? &layer_list[i - 1] : NULL;
- /*
- * check if current layer is at same z_order as
- * previous one, and fail if any or both are async layers,
- * as async layers should have unique z_order.
- *
- * If it has same z_order and qualifies as a right blend,
- * pass a pointer to the pipe representing previous overlay or
- * in other terms left blend layer.
- *
- * Following logic of selecting left_blend has an inherent
- * assumption that layer list is sorted on dst_x within a
- * same z_order. Otherwise it will fail based on z_order checks.
- */
- if (prev_layer && (prev_layer->z_order == layer->z_order)) {
- struct mdp_rect *left = &prev_layer->dst_rect;
- struct mdp_rect *right = &layer->dst_rect;
-
- if ((layer->flags & MDP_LAYER_ASYNC)
- || (prev_layer->flags & MDP_LAYER_ASYNC)) {
- ret = -EINVAL;
- layer->error_code = ret;
- pr_err("async layer should have unique z_order\n");
- goto validate_exit;
- }
-
- /*
- * check if layer is right blend by checking it's
- * directly to the right.
- */
- if (((left->x + left->w) == right->x) &&
- (left->y == right->y) && (left->h == right->h))
- left_blend_pipe = pipe;
-
- /*
- * if the layer is right at the left lm boundary and
- * src split is not required then right blend is not
- * required as it will lie only on the left mixer
- */
- if (!__layer_needs_src_split(prev_layer) &&
- ((left->x + left->w) == left_lm_w))
- left_blend_pipe = NULL;
- }
+ if ((i > 0) &&
+ __check_source_split(layer_list, pipe_list, i, left_lm_w,
+ &left_blend_pipe))
+ goto validate_exit;
if (!is_split_lm(mfd) || __layer_needs_src_split(layer))
z = LAYER_ZORDER_BOTH;
@@ -2456,6 +2502,8 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
else
left_plist[left_cnt++] = pipe;
+ pipe_list[i] = pipe;
+
if (layer->flags & MDP_LAYER_PP) {
memcpy(&pipe->pp_cfg, layer->pp_info,
sizeof(struct mdp_overlay_pp_params));
@@ -2548,6 +2596,8 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
else
left_plist[left_cnt++] = pipe;
+ pipe_list[i] = pipe;
+
pr_debug("id:0x%x flags:0x%x dst_x:%d\n",
layer->pipe_ndx, layer->flags, layer->dst_rect.x);
layer->z_order -= MDSS_MDP_STAGE_0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index 9e295815da77..5daa8a7a2752 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -2322,14 +2322,12 @@ set_roi:
}
/*
- * Enables/disable secure (display or camera) sessions
+ * Check if there is any change in secure state and store it.
*/
-static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
+static void __overlay_set_secure_transition_state(struct msm_fb_data_type *mfd)
{
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
- struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
struct mdss_mdp_pipe *pipe;
- int ret = 0;
int sd_in_pipe = 0;
int sc_in_pipe = 0;
u64 pipes_flags = 0;
@@ -2350,25 +2348,53 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
MDSS_XLOG(sd_in_pipe, sc_in_pipe, pipes_flags,
mdp5_data->sc_enabled, mdp5_data->sd_enabled);
pr_debug("sd:%d sd_in_pipe:%d sc:%d sc_in_pipe:%d flags:0x%llx\n",
- mdp5_data->sd_enabled, sd_in_pipe,
- mdp5_data->sc_enabled, sc_in_pipe, pipes_flags);
+ mdp5_data->sd_enabled, sd_in_pipe,
+ mdp5_data->sc_enabled, sc_in_pipe, pipes_flags);
+
+ /* Reset the secure transition state */
+ mdp5_data->secure_transition_state = SECURE_TRANSITION_NONE;
/*
- * Return early in only two conditions:
- * 1. All the features are already disabled and state remains
- * disabled for the pipes.
- * 2. One of the features is already enabled and state remains
- * enabled for the pipes.
- */
+ * Secure transition would be NONE in two conditions:
+ * 1. All the features are already disabled and state remains
+ * disabled for the pipes.
+ * 2. One of the features is already enabled and state remains
+ * enabled for the pipes.
+ */
if (!sd_in_pipe && !mdp5_data->sd_enabled &&
!sc_in_pipe && !mdp5_data->sc_enabled)
- return ret;
+ return;
else if ((sd_in_pipe && mdp5_data->sd_enabled) ||
(sc_in_pipe && mdp5_data->sc_enabled))
+ return;
+
+ /* Secure Display */
+ if (!mdp5_data->sd_enabled && sd_in_pipe)
+ mdp5_data->secure_transition_state |= SD_NON_SECURE_TO_SECURE;
+ else if (mdp5_data->sd_enabled && !sd_in_pipe)
+ mdp5_data->secure_transition_state |= SD_SECURE_TO_NON_SECURE;
+
+ /* Secure Camera */
+ if (!mdp5_data->sc_enabled && sc_in_pipe)
+ mdp5_data->secure_transition_state |= SC_NON_SECURE_TO_SECURE;
+ else if (mdp5_data->sc_enabled && !sc_in_pipe)
+ mdp5_data->secure_transition_state |= SC_SECURE_TO_NON_SECURE;
+}
+
+/*
+ * Enable/disable secure (display or camera) sessions
+ */
+static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
+{
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
+ int ret = 0;
+
+ if (mdp5_data->secure_transition_state == SECURE_TRANSITION_NONE)
return ret;
/* Secure Display */
- if (!mdp5_data->sd_enabled && sd_in_pipe) {
+ if (mdp5_data->secure_transition_state == SD_NON_SECURE_TO_SECURE) {
if (!mdss_get_sd_client_cnt()) {
MDSS_XLOG(0x11);
/*wait for ping pong done */
@@ -2390,7 +2416,8 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
}
mdp5_data->sd_enabled = 1;
mdss_update_sd_client(mdp5_data->mdata, true);
- } else if (mdp5_data->sd_enabled && !sd_in_pipe) {
+ } else if (mdp5_data->secure_transition_state ==
+ SD_SECURE_TO_NON_SECURE) {
/* disable the secure display on last client */
if (mdss_get_sd_client_cnt() == 1) {
MDSS_XLOG(0x22);
@@ -2408,11 +2435,9 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
}
/* Secure Camera */
- if (!mdp5_data->sc_enabled && sc_in_pipe) {
+ if (mdp5_data->secure_transition_state == SC_NON_SECURE_TO_SECURE) {
if (!mdss_get_sc_client_cnt()) {
MDSS_XLOG(0x33);
- if (ctl->ops.wait_pingpong)
- mdss_mdp_display_wait4pingpong(ctl, true);
ret = mdss_mdp_secure_session_ctrl(1,
MDP_SECURE_CAMERA_OVERLAY_SESSION);
if (ret) {
@@ -2422,7 +2447,8 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
}
mdp5_data->sc_enabled = 1;
mdss_update_sc_client(mdp5_data->mdata, true);
- } else if (mdp5_data->sc_enabled && !sc_in_pipe) {
+ } else if (mdp5_data->secure_transition_state ==
+ SC_SECURE_TO_NON_SECURE) {
/* disable the secure camera on last client */
if (mdss_get_sc_client_cnt() == 1) {
MDSS_XLOG(0x44);
@@ -2501,15 +2527,23 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
list_move(&pipe->list, &mdp5_data->pipes_destroy);
}
+ __overlay_set_secure_transition_state(mfd);
/*
* go to secure state if required, this should be done
* after moving the buffers from the previous commit to
- * destroy list
+ * destroy list.
+ * For video mode panels, secure display/camera should be disabled
+ * after flushing the new buffer. Skip secure disable here for those
+ * cases.
*/
- ret = __overlay_secure_ctrl(mfd);
- if (IS_ERR_VALUE(ret)) {
- pr_err("secure operation failed %d\n", ret);
- goto commit_fail;
+ if (!((mfd->panel_info->type == MIPI_VIDEO_PANEL) &&
+ ((mdp5_data->secure_transition_state == SD_SECURE_TO_NON_SECURE) ||
+ (mdp5_data->secure_transition_state == SC_SECURE_TO_NON_SECURE)))) {
+ ret = __overlay_secure_ctrl(mfd);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("secure operation failed %d\n", ret);
+ goto commit_fail;
+ }
}
/* call this function before any registers programming */
@@ -2581,6 +2615,17 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
mutex_lock(&mdp5_data->ov_lock);
+ /* Disable secure display/camera for video mode panels */
+ if ((mfd->panel_info->type == MIPI_VIDEO_PANEL) &&
+ ((mdp5_data->secure_transition_state == SD_SECURE_TO_NON_SECURE) ||
+ (mdp5_data->secure_transition_state == SC_SECURE_TO_NON_SECURE))) {
+ ret = __overlay_secure_ctrl(mfd);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("secure operation failed %d\n", ret);
+ goto commit_fail;
+ }
+ }
+
mdss_fb_update_notify_update(mfd);
commit_fail:
ATRACE_BEGIN("overlay_cleanup");
@@ -4367,12 +4412,21 @@ static int mdss_mdp_hw_cursor_pipe_update(struct msm_fb_data_type *mfd,
start_y = 0;
}
+ if ((img->width > mdata->max_cursor_size) ||
+ (img->height > mdata->max_cursor_size) ||
+ (img->depth != 32) || (start_x >= xres) ||
+ (start_y >= yres)) {
+ pr_err("Invalid cursor image coordinates\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
roi.w = min(xres - start_x, img->width - roi.x);
roi.h = min(yres - start_y, img->height - roi.y);
if ((roi.w > mdata->max_cursor_size) ||
- (roi.h > mdata->max_cursor_size) ||
- (img->depth != 32) || (start_x >= xres) || (start_y >= yres)) {
+ (roi.h > mdata->max_cursor_size)) {
+ pr_err("Invalid cursor ROI size\n");
ret = -EINVAL;
goto done;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c
index 1fe8fe6f7be8..f10d4fb60f52 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c
@@ -2146,6 +2146,7 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix,
unsigned long flag;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
u32 intr_mask;
+ u32 expected_sum = 0;
if (!mdata)
return -EPERM;
@@ -2156,6 +2157,7 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix,
block_type = DSPP;
op_flags = BIT(16);
hist_info = &mdss_pp_res->dspp_hist[mix->num];
+ expected_sum = mix->width * mix->height;
base = mdss_mdp_get_dspp_addr_off(PP_BLOCK(block));
if (IS_ERR(base)) {
ret = -EPERM;
@@ -2207,6 +2209,15 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix,
else if (hist_info->col_en)
*op |= op_flags;
+ if (hist_info->col_en) {
+ if (!hist_info->expect_sum) {
+ hist_info->expect_sum = expected_sum;
+ } else if (hist_info->expect_sum != expected_sum) {
+ hist_info->expect_sum = 0;
+ hist_info->next_sum = expected_sum;
+ }
+ }
+
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
mutex_unlock(&hist_info->hist_mutex);
error:
@@ -5276,8 +5287,7 @@ exit:
static int pp_hist_collect(struct mdp_histogram_data *hist,
struct pp_hist_col_info *hist_info,
- char __iomem *ctl_base, u32 expect_sum,
- u32 block)
+ char __iomem *ctl_base, u32 block)
{
int ret = 0;
int sum = 0;
@@ -5318,11 +5328,16 @@ static int pp_hist_collect(struct mdp_histogram_data *hist,
if (sum < 0) {
pr_err("failed to get the hist data, sum = %d\n", sum);
ret = sum;
- } else if (expect_sum && sum != expect_sum) {
+ } else if (hist_info->expect_sum && sum != hist_info->expect_sum) {
pr_err_ratelimited("hist error: bin sum incorrect! (%d/%d)\n",
- sum, expect_sum);
+ sum, hist_info->expect_sum);
ret = -EINVAL;
}
+
+ if (hist_info->next_sum) {
+ hist_info->expect_sum = hist_info->next_sum;
+ hist_info->next_sum = 0;
+ }
hist_collect_exit:
mutex_unlock(&hist_info->hist_mutex);
return ret;
@@ -5387,8 +5402,7 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
mdata->mixer_intf[dspp_num].height);
if (ret)
temp_ret = ret;
- ret = pp_hist_collect(hist, hists[i], ctl_base,
- exp_sum, DSPP);
+ ret = pp_hist_collect(hist, hists[i], ctl_base, DSPP);
if (ret)
pr_err_ratelimited("hist error: dspp[%d] collect %d\n",
dspp_num, ret);
@@ -5487,7 +5501,7 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
if (ret)
temp_ret = ret;
ret = pp_hist_collect(hist, hist_info, ctl_base,
- exp_sum, SSPP_VIG);
+ SSPP_VIG);
if (ret)
pr_debug("hist error: pipe[%d] collect: %d\n",
pipe->num, ret);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_util.c b/drivers/video/fbdev/msm/mdss_mdp_util.c
index c14840ffd08d..d0bf61679f61 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_util.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -974,7 +974,9 @@ static int mdss_mdp_put_img(struct mdss_mdp_img_data *data, bool rotator,
* be filled due to map call which will be unmapped above.
*
*/
- pr_debug("skip memory unmapping for secure display/camera content\n");
+ if (data->ihandle)
+ ion_free(iclient, data->ihandle);
+ pr_debug("free memory handle for secure display/camera content\n");
} else {
return -ENOMEM;
}
@@ -1053,19 +1055,18 @@ static int mdss_mdp_get_img(struct msmfb_data *img,
ret = 0;
goto done;
} else {
- struct ion_handle *ihandle = NULL;
struct sg_table *sg_ptr = NULL;
+ data->ihandle = ion_import_dma_buf(iclient,
+ img->memory_id);
+ if (IS_ERR_OR_NULL(data->ihandle)) {
+ ret = -EINVAL;
+ pr_err("ion import buffer failed\n");
+ data->ihandle = NULL;
+ goto done;
+ }
do {
- ihandle = ion_import_dma_buf(iclient,
- img->memory_id);
- if (IS_ERR_OR_NULL(ihandle)) {
- ret = -EINVAL;
- pr_err("ion import buffer failed\n");
- break;
- }
-
- sg_ptr = ion_sg_table(iclient, ihandle);
+ sg_ptr = ion_sg_table(iclient, data->ihandle);
if (sg_ptr == NULL) {
pr_err("ion sg table get failed\n");
ret = -EINVAL;
@@ -1091,8 +1092,6 @@ static int mdss_mdp_get_img(struct msmfb_data *img,
ret = 0;
} while (0);
- if (!IS_ERR_OR_NULL(ihandle))
- ion_free(iclient, ihandle);
return ret;
}
}
diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h
index e466c0097540..92413e078244 100644
--- a/drivers/video/fbdev/msm/mdss_panel.h
+++ b/drivers/video/fbdev/msm/mdss_panel.h
@@ -29,6 +29,9 @@ struct panel_id {
#define DEFAULT_FRAME_RATE 60
#define DEFAULT_ROTATOR_FRAME_RATE 120
#define ROTATOR_LOW_FRAME_RATE 30
+
+#define MDSS_DSI_MAX_ESC_CLK_RATE_HZ 19200000
+
#define MDSS_DSI_RST_SEQ_LEN 10
/* worst case prefill lines for all chipsets including all vertical blank */
#define MDSS_MDP_MAX_PREFILL_FETCH 25
@@ -112,12 +115,6 @@ enum {
};
enum {
- MDSS_PANEL_BLANK_BLANK = 0,
- MDSS_PANEL_BLANK_UNBLANK,
- MDSS_PANEL_BLANK_LOW_POWER,
-};
-
-enum {
MDSS_PANEL_LOW_PERSIST_MODE_OFF = 0,
MDSS_PANEL_LOW_PERSIST_MODE_ON,
};
@@ -192,10 +189,16 @@ struct mdss_panel_cfg {
enum {
MDP_INTF_CALLBACK_DSI_WAIT,
+ MDP_INTF_CALLBACK_CHECK_LINE_COUNT,
};
struct mdss_intf_recovery {
- void (*fxn)(void *ctx, int event);
+ int (*fxn)(void *ctx, int event);
+ void *data;
+};
+
+struct mdss_intf_ulp_clamp {
+ int (*fxn)(void *ctx, int intf_num, bool enable);
void *data;
};
@@ -304,6 +307,7 @@ enum mdss_intf_events {
MDSS_EVENT_UPDATE_PANEL_PPM,
MDSS_EVENT_DSI_TIMING_DB_CTRL,
MDSS_EVENT_AVR_MODE,
+ MDSS_EVENT_REGISTER_CLAMP_HANDLER,
MDSS_EVENT_MAX,
};
@@ -360,6 +364,8 @@ static inline char *mdss_panel_intf_event_to_string(int event)
return INTF_EVENT_STR(MDSS_EVENT_REGISTER_RECOVERY_HANDLER);
case MDSS_EVENT_REGISTER_MDP_CALLBACK:
return INTF_EVENT_STR(MDSS_EVENT_REGISTER_MDP_CALLBACK);
+ case MDSS_EVENT_REGISTER_CLAMP_HANDLER:
+ return INTF_EVENT_STR(MDSS_EVENT_REGISTER_CLAMP_HANDLER);
case MDSS_EVENT_DSI_PANEL_STATUS:
return INTF_EVENT_STR(MDSS_EVENT_DSI_PANEL_STATUS);
case MDSS_EVENT_DSI_DYNAMIC_SWITCH:
@@ -915,6 +921,9 @@ struct mdss_panel_info {
/* HDR properties of display panel*/
struct mdss_panel_hdr_properties hdr_properties;
+
+ /* esc clk recommended for the panel */
+ u32 esc_clk_rate_hz;
};
struct mdss_panel_timing {
diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c
index 0307d570ff64..fdd1c0153ce0 100644
--- a/drivers/video/fbdev/msm/mdss_rotator.c
+++ b/drivers/video/fbdev/msm/mdss_rotator.c
@@ -2065,10 +2065,12 @@ static int mdss_rotator_config_session(struct mdss_rot_mgr *mgr,
return ret;
}
+ mutex_lock(&mgr->lock);
perf = mdss_rotator_find_session(private, config.session_id);
if (!perf) {
pr_err("No session with id=%u could be found\n",
config.session_id);
+ mutex_unlock(&mgr->lock);
return -EINVAL;
}
@@ -2091,6 +2093,7 @@ static int mdss_rotator_config_session(struct mdss_rot_mgr *mgr,
config.output.format);
done:
ATRACE_END(__func__);
+ mutex_unlock(&mgr->lock);
return ret;
}
diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
index e6151b4c75a1..03e78733d168 100644
--- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c
+++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1863,8 +1863,10 @@ static int mdss_dsi_ulps_config_default(struct mdss_dsi_ctrl_pdata *ctrl,
* to be in stop state.
*/
MIPI_OUTP(ctrl->ctrl_base + 0x0AC, active_lanes << 16);
+ wmb(); /* ensure lanes are put to stop state */
MIPI_OUTP(ctrl->ctrl_base + 0x0AC, 0x0);
+ wmb(); /* ensure lanes are in proper state */
lane_status = MIPI_INP(ctrl->ctrl_base + 0xA8);
}
@@ -1983,6 +1985,7 @@ static int mdss_dsi_clamp_ctrl_default(struct mdss_dsi_ctrl_pdata *ctrl,
struct mipi_panel_info *mipi = NULL;
u32 clamp_reg, regval = 0;
u32 clamp_reg_off;
+ u32 intf_num = 0;
if (!ctrl) {
pr_err("%s: invalid input\n", __func__);
@@ -1994,6 +1997,21 @@ static int mdss_dsi_clamp_ctrl_default(struct mdss_dsi_ctrl_pdata *ctrl,
return -EINVAL;
}
+ /*
+ * For DSI HW version 2.1.0 ULPS_CLAMP register
+ * is moved to interface level.
+ */
+ if (ctrl->shared_data->hw_rev == MDSS_DSI_HW_REV_201) {
+ intf_num = ctrl->ndx ? MDSS_MDP_INTF2 : MDSS_MDP_INTF1;
+ if (ctrl->clamp_handler) {
+ ctrl->clamp_handler->fxn(ctrl->clamp_handler->data,
+ intf_num, enable);
+ pr_debug("%s: ndx: %d enable: %d\n",
+ __func__, ctrl->ndx, enable);
+ }
+ return 0;
+ }
+
clamp_reg_off = ctrl->shared_data->ulps_clamp_ctrl_off;
mipi = &ctrl->panel_data.panel_info.mipi;
@@ -2256,6 +2274,8 @@ int mdss_dsi_pre_clkoff_cb(void *priv,
pdata = &ctrl->panel_data;
if ((clk & MDSS_DSI_LINK_CLK) && (new_state == MDSS_DSI_CLK_OFF)) {
+ if (pdata->panel_info.mipi.force_clk_lane_hs)
+ mdss_dsi_cfg_lane_ctrl(ctrl, BIT(28), 0);
/*
* If ULPS feature is enabled, enter ULPS first.
* However, when blanking the panel, we should enter ULPS
@@ -2371,6 +2391,8 @@ int mdss_dsi_post_clkon_cb(void *priv,
goto error;
}
}
+ if (pdata->panel_info.mipi.force_clk_lane_hs)
+ mdss_dsi_cfg_lane_ctrl(ctrl, BIT(28), 1);
}
error:
return rc;
diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c
index b79a74a98a23..ad94d8a45728 100644
--- a/drivers/vme/bridges/vme_ca91cx42.c
+++ b/drivers/vme/bridges/vme_ca91cx42.c
@@ -467,7 +467,7 @@ static int ca91cx42_slave_get(struct vme_slave_resource *image, int *enabled,
vme_bound = ioread32(bridge->base + CA91CX42_VSI_BD[i]);
pci_offset = ioread32(bridge->base + CA91CX42_VSI_TO[i]);
- *pci_base = (dma_addr_t)vme_base + pci_offset;
+ *pci_base = (dma_addr_t)*vme_base + pci_offset;
*size = (unsigned long long)((vme_bound - *vme_base) + granularity);
*enabled = 0;
diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c
index 049a884a756f..59d74d1b47a8 100644
--- a/drivers/w1/masters/ds2490.c
+++ b/drivers/w1/masters/ds2490.c
@@ -153,6 +153,9 @@ struct ds_device
*/
u16 spu_bit;
+ u8 st_buf[ST_SIZE];
+ u8 byte_buf;
+
struct w1_bus_master master;
};
@@ -174,7 +177,6 @@ struct ds_status
u8 data_in_buffer_status;
u8 reserved1;
u8 reserved2;
-
};
static struct usb_device_id ds_id_table [] = {
@@ -244,28 +246,6 @@ static int ds_send_control(struct ds_device *dev, u16 value, u16 index)
return err;
}
-static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st,
- unsigned char *buf, int size)
-{
- int count, err;
-
- memset(st, 0, sizeof(*st));
-
- count = 0;
- err = usb_interrupt_msg(dev->udev, usb_rcvintpipe(dev->udev,
- dev->ep[EP_STATUS]), buf, size, &count, 1000);
- if (err < 0) {
- pr_err("Failed to read 1-wire data from 0x%x: err=%d.\n",
- dev->ep[EP_STATUS], err);
- return err;
- }
-
- if (count >= sizeof(*st))
- memcpy(st, buf, sizeof(*st));
-
- return count;
-}
-
static inline void ds_print_msg(unsigned char *buf, unsigned char *str, int off)
{
pr_info("%45s: %8x\n", str, buf[off]);
@@ -324,6 +304,35 @@ static void ds_dump_status(struct ds_device *dev, unsigned char *buf, int count)
}
}
+static int ds_recv_status(struct ds_device *dev, struct ds_status *st,
+ bool dump)
+{
+ int count, err;
+
+ if (st)
+ memset(st, 0, sizeof(*st));
+
+ count = 0;
+ err = usb_interrupt_msg(dev->udev,
+ usb_rcvintpipe(dev->udev,
+ dev->ep[EP_STATUS]),
+ dev->st_buf, sizeof(dev->st_buf),
+ &count, 1000);
+ if (err < 0) {
+ pr_err("Failed to read 1-wire data from 0x%x: err=%d.\n",
+ dev->ep[EP_STATUS], err);
+ return err;
+ }
+
+ if (dump)
+ ds_dump_status(dev, dev->st_buf, count);
+
+ if (st && count >= sizeof(*st))
+ memcpy(st, dev->st_buf, sizeof(*st));
+
+ return count;
+}
+
static void ds_reset_device(struct ds_device *dev)
{
ds_send_control_cmd(dev, CTL_RESET_DEVICE, 0);
@@ -344,7 +353,6 @@ static void ds_reset_device(struct ds_device *dev)
static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size)
{
int count, err;
- struct ds_status st;
/* Careful on size. If size is less than what is available in
* the input buffer, the device fails the bulk transfer and
@@ -359,14 +367,9 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size)
err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]),
buf, size, &count, 1000);
if (err < 0) {
- u8 buf[ST_SIZE];
- int count;
-
pr_info("Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]);
usb_clear_halt(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]));
-
- count = ds_recv_status_nodump(dev, &st, buf, sizeof(buf));
- ds_dump_status(dev, buf, count);
+ ds_recv_status(dev, NULL, true);
return err;
}
@@ -404,7 +407,6 @@ int ds_stop_pulse(struct ds_device *dev, int limit)
{
struct ds_status st;
int count = 0, err = 0;
- u8 buf[ST_SIZE];
do {
err = ds_send_control(dev, CTL_HALT_EXE_IDLE, 0);
@@ -413,7 +415,7 @@ int ds_stop_pulse(struct ds_device *dev, int limit)
err = ds_send_control(dev, CTL_RESUME_EXE, 0);
if (err)
break;
- err = ds_recv_status_nodump(dev, &st, buf, sizeof(buf));
+ err = ds_recv_status(dev, &st, false);
if (err)
break;
@@ -456,18 +458,17 @@ int ds_detect(struct ds_device *dev, struct ds_status *st)
static int ds_wait_status(struct ds_device *dev, struct ds_status *st)
{
- u8 buf[ST_SIZE];
int err, count = 0;
do {
st->status = 0;
- err = ds_recv_status_nodump(dev, st, buf, sizeof(buf));
+ err = ds_recv_status(dev, st, false);
#if 0
if (err >= 0) {
int i;
printk("0x%x: count=%d, status: ", dev->ep[EP_STATUS], err);
for (i=0; i<err; ++i)
- printk("%02x ", buf[i]);
+ printk("%02x ", dev->st_buf[i]);
printk("\n");
}
#endif
@@ -485,7 +486,7 @@ static int ds_wait_status(struct ds_device *dev, struct ds_status *st)
* can do something with it).
*/
if (err > 16 || count >= 100 || err < 0)
- ds_dump_status(dev, buf, err);
+ ds_dump_status(dev, dev->st_buf, err);
/* Extended data isn't an error. Well, a short is, but the dump
* would have already told the user that and we can't do anything
@@ -608,7 +609,6 @@ static int ds_write_byte(struct ds_device *dev, u8 byte)
{
int err;
struct ds_status st;
- u8 rbyte;
err = ds_send_control(dev, COMM_BYTE_IO | COMM_IM | dev->spu_bit, byte);
if (err)
@@ -621,11 +621,11 @@ static int ds_write_byte(struct ds_device *dev, u8 byte)
if (err)
return err;
- err = ds_recv_data(dev, &rbyte, sizeof(rbyte));
+ err = ds_recv_data(dev, &dev->byte_buf, 1);
if (err < 0)
return err;
- return !(byte == rbyte);
+ return !(byte == dev->byte_buf);
}
static int ds_read_byte(struct ds_device *dev, u8 *byte)
@@ -712,7 +712,6 @@ static void ds9490r_search(void *data, struct w1_master *master,
int err;
u16 value, index;
struct ds_status st;
- u8 st_buf[ST_SIZE];
int search_limit;
int found = 0;
int i;
@@ -724,7 +723,12 @@ static void ds9490r_search(void *data, struct w1_master *master,
/* FIFO 128 bytes, bulk packet size 64, read a multiple of the
* packet size.
*/
- u64 buf[2*64/8];
+ const size_t bufsize = 2 * 64;
+ u64 *buf;
+
+ buf = kmalloc(bufsize, GFP_KERNEL);
+ if (!buf)
+ return;
mutex_lock(&master->bus_mutex);
@@ -745,10 +749,9 @@ static void ds9490r_search(void *data, struct w1_master *master,
do {
schedule_timeout(jtime);
- if (ds_recv_status_nodump(dev, &st, st_buf, sizeof(st_buf)) <
- sizeof(st)) {
+ err = ds_recv_status(dev, &st, false);
+ if (err < 0 || err < sizeof(st))
break;
- }
if (st.data_in_buffer_status) {
/* Bulk in can receive partial ids, but when it does
@@ -758,7 +761,7 @@ static void ds9490r_search(void *data, struct w1_master *master,
* bulk without first checking if status says there
* is data to read.
*/
- err = ds_recv_data(dev, (u8 *)buf, sizeof(buf));
+ err = ds_recv_data(dev, (u8 *)buf, bufsize);
if (err < 0)
break;
for (i = 0; i < err/8; ++i) {
@@ -794,9 +797,14 @@ static void ds9490r_search(void *data, struct w1_master *master,
}
search_out:
mutex_unlock(&master->bus_mutex);
+ kfree(buf);
}
#if 0
+/*
+ * FIXME: if this disabled code is ever used in the future all ds_send_data()
+ * calls must be changed to use a DMAable buffer.
+ */
static int ds_match_access(struct ds_device *dev, u64 init)
{
int err;
@@ -845,13 +853,12 @@ static int ds_set_path(struct ds_device *dev, u64 init)
static u8 ds9490r_touch_bit(void *data, u8 bit)
{
- u8 ret;
struct ds_device *dev = data;
- if (ds_touch_bit(dev, bit, &ret))
+ if (ds_touch_bit(dev, bit, &dev->byte_buf))
return 0;
- return ret;
+ return dev->byte_buf;
}
#if 0
@@ -866,13 +873,12 @@ static u8 ds9490r_read_bit(void *data)
{
struct ds_device *dev = data;
int err;
- u8 bit = 0;
- err = ds_touch_bit(dev, 1, &bit);
+ err = ds_touch_bit(dev, 1, &dev->byte_buf);
if (err)
return 0;
- return bit & 1;
+ return dev->byte_buf & 1;
}
#endif
@@ -887,32 +893,52 @@ static u8 ds9490r_read_byte(void *data)
{
struct ds_device *dev = data;
int err;
- u8 byte = 0;
- err = ds_read_byte(dev, &byte);
+ err = ds_read_byte(dev, &dev->byte_buf);
if (err)
return 0;
- return byte;
+ return dev->byte_buf;
}
static void ds9490r_write_block(void *data, const u8 *buf, int len)
{
struct ds_device *dev = data;
+ u8 *tbuf;
+
+ if (len <= 0)
+ return;
+
+ tbuf = kmalloc(len, GFP_KERNEL);
+ if (!tbuf)
+ return;
- ds_write_block(dev, (u8 *)buf, len);
+ memcpy(tbuf, buf, len);
+ ds_write_block(dev, tbuf, len);
+
+ kfree(tbuf);
}
static u8 ds9490r_read_block(void *data, u8 *buf, int len)
{
struct ds_device *dev = data;
int err;
+ u8 *tbuf;
- err = ds_read_block(dev, buf, len);
- if (err < 0)
+ if (len <= 0)
+ return 0;
+
+ tbuf = kmalloc(len, GFP_KERNEL);
+ if (!tbuf)
return 0;
- return len;
+ err = ds_read_block(dev, tbuf, len);
+ if (err >= 0)
+ memcpy(buf, tbuf, len);
+
+ kfree(tbuf);
+
+ return err >= 0 ? len : 0;
}
static u8 ds9490r_reset(void *data)
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index c9a7ff67d395..39886edfa222 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -763,6 +763,7 @@ int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)
dev_err(&dev->dev, "%s: Attaching %s failed.\n", __func__,
sl->name);
w1_family_put(sl->family);
+ atomic_dec(&sl->master->refcnt);
kfree(sl);
return err;
}
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 1be5dd048622..308600adf6e0 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -804,7 +804,7 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma)
vma->vm_ops = &gntdev_vmops;
- vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
+ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_MIXEDMAP;
if (use_ptemod)
vma->vm_flags |= VM_DONTCOPY;