summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/acpica/hwxface.c24
-rw-r--r--drivers/acpi/apei/ghes.c2
-rw-r--r--drivers/acpi/nfit.c3
-rw-r--r--drivers/acpi/nfit.h4
-rw-r--r--drivers/android/binder.c4
-rw-r--r--drivers/base/dma-mapping.c2
-rw-r--r--drivers/base/firmware_class.c3
-rw-r--r--drivers/base/platform.c4
-rw-r--r--drivers/base/power/main.c8
-rw-r--r--drivers/block/drbd/drbd_main.c2
-rw-r--r--drivers/block/zram/zram_drv.c3
-rw-r--r--drivers/bus/arm-ccn.c27
-rw-r--r--drivers/char/adsprpc.c2
-rw-r--r--drivers/char/hw_random/core.c6
-rw-r--r--drivers/char/hw_random/omap-rng.c16
-rw-r--r--drivers/char/tpm/tpm-dev.c2
-rw-r--r--drivers/char/tpm/tpm-interface.c51
-rw-r--r--drivers/char/tpm/tpm-sysfs.c2
-rw-r--r--drivers/char/tpm/tpm.h12
-rw-r--r--drivers/char/tpm/tpm2-cmd.c103
-rw-r--r--drivers/char/tpm/tpm_crb.c7
-rw-r--r--drivers/char/virtio_console.c22
-rw-r--r--drivers/clk/clk-divider.c2
-rw-r--r--drivers/clk/clk-qoriq.c19
-rw-r--r--drivers/clk/clk-xgene.c4
-rw-r--r--drivers/clk/imx/clk-imx35.c2
-rw-r--r--drivers/clk/imx/clk-imx6q.c18
-rw-r--r--drivers/clk/mmp/clk-of-mmp2.c2
-rw-r--r--drivers/clk/mmp/clk-of-pxa168.c2
-rw-r--r--drivers/clk/mmp/clk-of-pxa910.c4
-rw-r--r--drivers/clk/msm/clock-mmss-8998.c16
-rw-r--r--drivers/clk/qcom/clk-rcg2.c5
-rw-r--r--drivers/clocksource/sun4i_timer.c9
-rw-r--r--drivers/cpufreq/intel_pstate.c10
-rw-r--r--drivers/cpuidle/cpuidle-arm.c1
-rw-r--r--drivers/cpuidle/lpm-levels.c10
-rw-r--r--drivers/crypto/caam/caamalg.c9
-rw-r--r--drivers/crypto/vmx/ghash.c31
-rw-r--r--drivers/dma/at_xdmac.c26
-rw-r--r--drivers/dma/ipu/ipu_irq.c9
-rw-r--r--drivers/esoc/Kconfig8
-rw-r--r--drivers/esoc/esoc-mdm-dbg-eng.c169
-rw-r--r--drivers/esoc/esoc-mdm-drv.c2
-rw-r--r--drivers/esoc/esoc_client.c4
-rw-r--r--drivers/esoc/mdm-dbg.h6
-rw-r--r--drivers/firewire/net.c59
-rw-r--r--drivers/firmware/Kconfig3
-rw-r--r--drivers/firmware/efi/Makefile3
-rw-r--r--drivers/firmware/efi/arm-init.c209
-rw-r--r--drivers/firmware/efi/arm-runtime.c135
-rw-r--r--drivers/firmware/efi/efi.c2
-rw-r--r--drivers/firmware/psci.c119
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c2
-rw-r--r--drivers/gpio/gpio-sa1100.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c20
-rw-r--r--drivers/gpu/drm/amd/amdgpu/atombios_dp.c20
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v10_0.c23
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v11_0.c24
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v8_0.c48
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c10
-rw-r--r--drivers/gpu/drm/drm_dp_mst_topology.c6
-rw-r--r--drivers/gpu/drm/drm_ioc32.c4
-rw-r--r--drivers/gpu/drm/drm_prime.c17
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_core.c2
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_dp.c2
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h24
-rw-r--r--drivers/gpu/drm/i915/i915_gem_stolen.c23
-rw-r--r--drivers/gpu/drm/i915/intel_display.c16
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c2
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h8
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c120
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c42
-rw-r--r--drivers/gpu/drm/i915/intel_sprite.c2
-rw-r--r--drivers/gpu/drm/msm/Kconfig1
-rw-r--r--drivers/gpu/drm/msm/Makefile3
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c210
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h71
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c37
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c227
-rw-r--r--drivers/gpu/drm/msm/sde/sde_formats.c79
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c6
-rw-r--r--drivers/gpu/drm/qxl/qxl_draw.c2
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c2
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c20
-rw-r--r--drivers/gpu/drm/radeon/ni.c4
-rw-r--r--drivers/gpu/drm/radeon/r600_dpm.c15
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c5
-rw-r--r--drivers/gpu/drm/radeon/radeon_dp_auxch.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_crtc.c2
-rw-r--r--drivers/gpu/drm/radeon/si_dpm.c49
-rw-r--r--drivers/gpu/drm/radeon/sislands_smc.h1
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c6
-rw-r--r--drivers/gpu/msm/adreno_a5xx.c3
-rw-r--r--drivers/gpu/msm/kgsl.c2
-rw-r--r--drivers/gpu/msm/kgsl_debugfs.c7
-rw-r--r--drivers/gpu/msm/kgsl_debugfs.h4
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.c69
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.h7
-rw-r--r--drivers/gpu/msm/kgsl_snapshot.c8
-rw-r--r--drivers/hid/hid-ids.h1
-rw-r--r--drivers/hid/usbhid/hid-quirks.c1
-rw-r--r--drivers/hv/hv_util.c10
-rw-r--r--drivers/hwmon/adt7411.c5
-rw-r--r--drivers/hwtracing/coresight/coresight-tpdm.c8
-rw-r--r--drivers/i2c/Kconfig1
-rw-r--r--drivers/i2c/busses/i2c-eg20t.c18
-rw-r--r--drivers/i2c/busses/i2c-msm-v2.c70
-rw-r--r--drivers/i2c/busses/i2c-qup.c3
-rw-r--r--drivers/i2c/busses/i2c-xgene-slimpro.c2
-rw-r--r--drivers/i2c/i2c-core.c2
-rw-r--r--drivers/i2c/muxes/Kconfig1
-rw-r--r--drivers/iio/accel/bmc150-accel-core.c11
-rw-r--r--drivers/iio/accel/kxsd9.c2
-rw-r--r--drivers/iio/adc/Kconfig1
-rw-r--r--drivers/iio/adc/ad799x.c1
-rw-r--r--drivers/iio/adc/at91_adc.c4
-rw-r--r--drivers/iio/adc/rockchip_saradc.c30
-rw-r--r--drivers/iio/adc/ti_am335x_adc.c16
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-attributes.c58
-rw-r--r--drivers/iio/industrialio-buffer.c4
-rw-r--r--drivers/iio/industrialio-core.c14
-rw-r--r--drivers/iio/orientation/hid-sensor-rotation.c1
-rw-r--r--drivers/iio/proximity/as3935.c2
-rw-r--r--drivers/infiniband/core/cm.c126
-rw-r--r--drivers/infiniband/core/multicast.c13
-rw-r--r--drivers/infiniband/core/umem.c2
-rw-r--r--drivers/infiniband/core/uverbs.h1
-rw-r--r--drivers/infiniband/core/uverbs_main.c44
-rw-r--r--drivers/infiniband/hw/mlx4/ah.c5
-rw-r--r--drivers/infiniband/hw/mlx4/cq.c5
-rw-r--r--drivers/infiniband/hw/mlx4/mad.c23
-rw-r--r--drivers/infiniband/hw/mlx4/mcg.c14
-rw-r--r--drivers/infiniband/hw/mlx4/mlx4_ib.h2
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c37
-rw-r--r--drivers/infiniband/hw/mlx5/cq.c3
-rw-r--r--drivers/infiniband/hw/mlx5/main.c7
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h1
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_cm.c16
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_ib.c9
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c2
-rw-r--r--drivers/input/keyboard/goldfish_events.c45
-rw-r--r--drivers/input/keyreset.c3
-rw-r--r--drivers/input/mouse/elantech.c25
-rw-r--r--drivers/input/serio/i8042-io.h2
-rw-r--r--drivers/input/serio/i8042-ip22io.h2
-rw-r--r--drivers/input/serio/i8042-ppcio.h2
-rw-r--r--drivers/input/serio/i8042-sparcio.h2
-rw-r--r--drivers/input/serio/i8042-unicore32io.h2
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h103
-rw-r--r--drivers/input/serio/i8042.c55
-rw-r--r--drivers/iommu/amd_iommu.c3
-rw-r--r--drivers/iommu/arm-smmu.c2
-rw-r--r--drivers/iommu/dma-mapping-fast.c25
-rw-r--r--drivers/iommu/dmar.c7
-rw-r--r--drivers/iommu/intel-iommu.c30
-rw-r--r--drivers/iommu/intel-svm.c28
-rw-r--r--drivers/iommu/io-pgtable-fast.c116
-rw-r--r--drivers/iommu/io-pgtable.h2
-rw-r--r--drivers/irqchip/irq-atmel-aic.c5
-rw-r--r--drivers/irqchip/irq-atmel-aic5.c5
-rw-r--r--drivers/irqchip/irq-gic-v3.c9
-rw-r--r--drivers/leds/Kconfig11
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-qpnp-flash.c2683
-rw-r--r--drivers/leds/leds-qpnp-wled.c85
-rw-r--r--drivers/lightnvm/rrpc.c4
-rw-r--r--drivers/md/dm-crypt.c31
-rw-r--r--drivers/md/dm-flakey.c27
-rw-r--r--drivers/md/dm-log-writes.c10
-rw-r--r--drivers/md/dm-mpath.c6
-rw-r--r--drivers/md/dm-raid1.c1
-rw-r--r--drivers/md/dm.c14
-rw-r--r--drivers/md/md.c12
-rw-r--r--drivers/media/dvb-frontends/mb86a20s.c104
-rw-r--r--drivers/media/platform/am437x/am437x-vpfe.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c13
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp.c8
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp.h3
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.c27
-rw-r--r--drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c22
-rw-r--r--drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c53
-rw-r--r--drivers/media/platform/msm/camera_v2/msm.c18
-rw-r--r--drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c11
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h3
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h3
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c24
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h3
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c39
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h3
-rw-r--r--drivers/media/platform/msm/vidc/msm_v4l2_vidc.c11
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc.c28
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_common.c50
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_dcvs.c2
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_res_parse.c31
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_resources.h3
-rw-r--r--drivers/media/platform/msm/vidc/venus_hfi.c64
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-avcore.c5
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-core.c3
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_core.c5
-rw-r--r--drivers/media/usb/em28xx/em28xx-i2c.c5
-rw-r--r--drivers/media/usb/gspca/cpia1.c2
-rw-r--r--drivers/media/usb/gspca/konica.c2
-rw-r--r--drivers/media/usb/gspca/t613.c2
-rw-r--r--drivers/memstick/host/rtsx_usb_ms.c6
-rw-r--r--drivers/mfd/Kconfig1
-rw-r--r--drivers/mfd/atmel-hlcdc.c5
-rw-r--r--drivers/mfd/intel-lpss.c3
-rw-r--r--drivers/mfd/mfd-core.c2
-rw-r--r--drivers/mfd/rtsx_usb.c10
-rw-r--r--drivers/misc/genwqe/card_utils.c12
-rw-r--r--drivers/misc/hdcp.c3
-rw-r--r--drivers/misc/mei/bus-fixup.c2
-rw-r--r--drivers/misc/mei/bus.c2
-rw-r--r--drivers/misc/mei/hw-me-regs.h4
-rw-r--r--drivers/misc/mei/hw-me.c10
-rw-r--r--drivers/misc/mei/hw-txe.c6
-rw-r--r--drivers/misc/mei/main.c2
-rw-r--r--drivers/misc/mei/pci-me.c7
-rw-r--r--drivers/misc/qseecom.c4
-rw-r--r--drivers/mmc/card/block.c18
-rw-r--r--drivers/mmc/card/queue.h2
-rw-r--r--drivers/mmc/core/core.c38
-rw-r--r--drivers/mmc/core/host.c4
-rw-r--r--drivers/mmc/core/sd.c12
-rw-r--r--drivers/mmc/host/cmdq_hci.c88
-rw-r--r--drivers/mmc/host/cmdq_hci.h4
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.c5
-rw-r--r--drivers/mmc/host/dw_mmc.c2
-rw-r--r--drivers/mmc/host/mxs-mmc.c4
-rw-r--r--drivers/mmc/host/pxamci.c16
-rw-r--r--drivers/mmc/host/rtsx_usb_sdmmc.c7
-rw-r--r--drivers/mmc/host/sdhci.c2
-rw-r--r--drivers/mtd/maps/pmcmsp-flash.c6
-rw-r--r--drivers/mtd/maps/sa1100-flash.c4
-rw-r--r--drivers/mtd/nand/davinci_nand.c3
-rw-r--r--drivers/mtd/ubi/fastmap.c17
-rw-r--r--drivers/mtd/ubi/wl.c21
-rw-r--r--drivers/net/bonding/bond_main.c7
-rw-r--r--drivers/net/can/dev.c27
-rw-r--r--drivers/net/can/flexcan.c13
-rw-r--r--drivers/net/dsa/bcm_sf2.c4
-rw-r--r--drivers/net/dsa/bcm_sf2.h2
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.c5
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c4
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c8
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c10
-rw-r--r--drivers/net/ethernet/cavium/thunder/nic_reg.h1
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c5
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c10
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_common.c4
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c8
-rw-r--r--drivers/net/ethernet/marvell/sky2.c13
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_netdev.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cmd.c85
-rw-r--r--drivers/net/ethernet/msm/msm_rmnet_mhi.c938
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.c2
-rw-r--r--drivers/net/ethernet/smsc/smc91x.c9
-rw-r--r--drivers/net/ethernet/smsc/smc91x.h65
-rw-r--r--drivers/net/geneve.c16
-rw-r--r--drivers/net/phy/phy.c6
-rw-r--r--drivers/net/tun.c5
-rw-r--r--drivers/net/virtio_net.c5
-rw-r--r--drivers/net/vxlan.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/ce.c342
-rw-r--r--drivers/net/wireless/ath/ath10k/ce.h347
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c6
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h5
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.c18
-rw-r--r--drivers/net/wireless/ath/ath10k/hif.h4
-rw-r--r--drivers/net/wireless/ath/ath10k/htt.h4
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_rx.c5
-rw-r--r--drivers/net/wireless/ath/ath10k/hw.c295
-rw-r--r--drivers/net/wireless/ath/ath10k/hw.h92
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.c48
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.h18
-rw-r--r--drivers/net/wireless/ath/ath10k/snoc.c36
-rw-r--r--drivers/net/wireless/ath/ath10k/snoc.h8
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_eeprom.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c8
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c3
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c4
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c7
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/dma.c4
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/stf.c2
-rw-r--r--drivers/net/wireless/iwlegacy/3945.c3
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/calib.c2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw.c3
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c8
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sf.c2
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/drv.c79
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/tx.c4
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c13
-rw-r--r--drivers/net/wireless/mwifiex/join.c3
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/regd.c4
-rw-r--r--drivers/nfc/fdp/fdp.c4
-rw-r--r--drivers/nfc/mei_phy.c2
-rw-r--r--drivers/of/of_reserved_mem.c8
-rw-r--r--drivers/pci/host/pci-msm.c49
-rw-r--r--drivers/pci/pcie/aer/aer_inject.c14
-rw-r--r--drivers/pci/probe.c28
-rw-r--r--drivers/pci/quirks.c1
-rw-r--r--drivers/perf/arm_pmu.c1
-rw-r--r--drivers/pinctrl/intel/pinctrl-cherryview.c17
-rw-r--r--drivers/pinctrl/pinctrl-at91-pio4.c2
-rw-r--r--drivers/pinctrl/pinctrl-pistachio.c12
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c4
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c4
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-core.c6
-rw-r--r--drivers/platform/goldfish/Makefile3
-rw-r--r--drivers/platform/goldfish/goldfish_pipe.c287
-rw-r--r--drivers/platform/goldfish/goldfish_pipe.h91
-rw-r--r--drivers/platform/goldfish/goldfish_pipe_v2.c889
-rw-r--r--drivers/platform/msm/Kconfig7
-rw-r--r--drivers/platform/msm/Makefile1
-rw-r--r--drivers/platform/msm/gsi/gsi.c2
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa.c193
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c158
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_rt.c45
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_client.c3
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_dma.c6
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_dp.c2
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_flt.c3
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c26
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_uc.c6
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_utils.c7
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h2
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c2
-rw-r--r--drivers/platform/msm/mhi_uci/mhi_uci.c916
-rw-r--r--drivers/platform/msm/msm_ext_display.c (renamed from drivers/video/fbdev/msm/msm_ext_display.c)246
-rw-r--r--drivers/platform/x86/toshiba-wmi.c26
-rw-r--r--drivers/power/bq24257_charger.c12
-rw-r--r--drivers/power/goldfish_battery.c17
-rw-r--r--drivers/power/max17042_battery.c15
-rw-r--r--drivers/power/qcom/apm.c2
-rw-r--r--drivers/power/reset/hisi-reboot.c5
-rw-r--r--drivers/power/supply/qcom/Kconfig18
-rw-r--r--drivers/power/supply/qcom/Makefile2
-rw-r--r--drivers/power/supply/qcom/battery.c75
-rw-r--r--drivers/power/supply/qcom/battery_current_limit.c35
-rw-r--r--drivers/power/supply/qcom/bcl_peripheral.c6
-rw-r--r--drivers/power/supply/qcom/fg-core.h2
-rw-r--r--drivers/power/supply/qcom/fg-util.c4
-rw-r--r--drivers/power/supply/qcom/qpnp-fg-gen3.c81
-rw-r--r--drivers/power/supply/qcom/qpnp-fg.c7051
-rw-r--r--drivers/power/supply/qcom/qpnp-smbcharger.c8472
-rw-r--r--drivers/power/supply/qcom/smb-lib.c56
-rw-r--r--drivers/power/supply/qcom/smb138x-charger.c40
-rw-r--r--drivers/power/tps65217_charger.c1
-rw-r--r--drivers/pwm/core.c4
-rw-r--r--drivers/pwm/sysfs.c20
-rw-r--r--drivers/regulator/qcom_smd-regulator.c17
-rw-r--r--drivers/regulator/qcom_spmi-regulator.c7
-rw-r--r--drivers/regulator/qpnp-oledb-regulator.c162
-rw-r--r--drivers/regulator/spm-regulator.c2
-rw-r--r--drivers/regulator/tps65910-regulator.c6
-rw-r--r--drivers/rtc/rtc-omap.c6
-rw-r--r--drivers/s390/char/con3270.c11
-rw-r--r--drivers/s390/cio/chsc.c20
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c162
-rw-r--r--drivers/s390/scsi/zfcp_dbf.h14
-rw-r--r--drivers/s390/scsi/zfcp_erp.c12
-rw-r--r--drivers/s390/scsi/zfcp_ext.h8
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c22
-rw-r--r--drivers/s390/scsi/zfcp_fsf.h4
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c8
-rw-r--r--drivers/scsi/arcmsr/arcmsr_hba.c21
-rw-r--r--drivers/scsi/fnic/fnic_fcs.c8
-rw-r--r--drivers/scsi/hpsa.c78
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c1
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.h2
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_base.c23
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_scsih.c26
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c2
-rw-r--r--drivers/scsi/scsi_debug.c1
-rw-r--r--drivers/scsi/scsi_scan.c2
-rw-r--r--drivers/scsi/sd.c8
-rw-r--r--drivers/scsi/sd.h5
-rw-r--r--drivers/sensors/sensors_ssc.c17
-rw-r--r--drivers/soc/qcom/Kconfig9
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/icnss.c372
-rw-r--r--drivers/soc/qcom/qdsp6v2/adsp-loader.c16
-rw-r--r--drivers/soc/qcom/qdsp6v2/apr_tal_glink.c1
-rw-r--r--drivers/soc/qcom/qdsp6v2/cdsp-loader.c22
-rw-r--r--drivers/soc/qcom/qpnp-haptic.c354
-rw-r--r--drivers/soc/qcom/qpnp-pbs.c361
-rw-r--r--drivers/soc/qcom/service-locator.c5
-rw-r--r--drivers/soc/qcom/service-notifier.c8
-rw-r--r--drivers/soc/qcom/spcom.c20
-rw-r--r--drivers/soc/qcom/spm.c12
-rw-r--r--drivers/soc/qcom/sysmon-qmi.c8
-rw-r--r--drivers/spi/spi-fsl-dspi.c1
-rw-r--r--drivers/spi/spi-sh-msiof.c3
-rw-r--r--drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c2
-rw-r--r--drivers/staging/fbtft/fbtft-core.c4
-rw-r--r--drivers/staging/goldfish/Kconfig6
-rw-r--r--drivers/staging/goldfish/Makefile5
-rw-r--r--drivers/staging/goldfish/goldfish_audio.c23
-rw-r--r--drivers/staging/goldfish/goldfish_sync.c987
-rw-r--r--drivers/staging/iio/adc/ad7192.c2
-rw-r--r--drivers/staging/iio/impedance-analyzer/ad5933.c17
-rw-r--r--drivers/staging/nvec/nvec_ps2.c8
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_cmd.c4
-rw-r--r--drivers/target/target_core_transport.c11
-rw-r--r--drivers/target/target_core_xcopy.c34
-rw-r--r--drivers/tty/goldfish.c40
-rw-r--r--drivers/tty/serial/8250/8250_dw.c2
-rw-r--r--drivers/tty/serial/8250/8250_mid.c3
-rw-r--r--drivers/tty/serial/8250/8250_pci.c139
-rw-r--r--drivers/tty/serial/atmel_serial.c27
-rw-r--r--drivers/tty/serial/sunhv.c6
-rw-r--r--drivers/tty/vt/vt.c7
-rw-r--r--drivers/uio/uio_dmem_genirq.c2
-rw-r--r--drivers/usb/chipidea/core.c1
-rw-r--r--drivers/usb/chipidea/udc.c11
-rw-r--r--drivers/usb/class/cdc-acm.c2
-rw-r--r--drivers/usb/class/usbtmc.c3
-rw-r--r--drivers/usb/core/config.c28
-rw-r--r--drivers/usb/dwc3/debugfs.c36
-rw-r--r--drivers/usb/dwc3/gadget.c4
-rw-r--r--drivers/usb/gadget/composite.c12
-rw-r--r--drivers/usb/gadget/configfs.c5
-rw-r--r--drivers/usb/gadget/function/f_accessory.c2
-rw-r--r--drivers/usb/gadget/function/f_audio_source.c8
-rw-r--r--drivers/usb/gadget/function/f_fs.c7
-rw-r--r--drivers/usb/gadget/function/f_gsi.c218
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c8
-rw-r--r--drivers/usb/gadget/function/f_midi.c2
-rw-r--r--drivers/usb/gadget/function/f_mtp.c41
-rw-r--r--drivers/usb/gadget/function/u_data_ipa.c4
-rw-r--r--drivers/usb/gadget/function/u_ether.c3
-rw-r--r--drivers/usb/gadget/functions.c2
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c7
-rw-r--r--drivers/usb/host/ohci-hcd.c2
-rw-r--r--drivers/usb/host/xhci-pci.c4
-rw-r--r--drivers/usb/host/xhci-ring.c6
-rw-r--r--drivers/usb/misc/legousbtower.c35
-rw-r--r--drivers/usb/musb/musb_host.c8
-rw-r--r--drivers/usb/pd/policy_engine.c2
-rw-r--r--drivers/usb/renesas_usbhs/mod.c11
-rw-r--r--drivers/usb/serial/cp210x.c8
-rw-r--r--drivers/usb/serial/ftdi_sio.c5
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h11
-rw-r--r--drivers/usb/serial/usb-serial-simple.c3
-rw-r--r--drivers/usb/serial/usb-serial.c3
-rw-r--r--drivers/usb/storage/transport.c7
-rw-r--r--drivers/uwb/lc-rc.c16
-rw-r--r--drivers/uwb/pal.c2
-rw-r--r--drivers/video/adf/adf_fops.c73
-rw-r--r--drivers/video/fbdev/efifb.c6
-rw-r--r--drivers/video/fbdev/goldfishfb.c18
-rw-r--r--drivers/video/fbdev/msm/Kconfig2
-rw-r--r--drivers/video/fbdev/msm/Makefile1
-rw-r--r--drivers/video/fbdev/msm/mdss.h2
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c2
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c19
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_host.c58
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c27
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c172
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h2
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c5
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_video.c29
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c127
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pipe.c5
-rw-r--r--drivers/virtio/virtio_ring.c14
-rw-r--r--drivers/xen/xenbus/xenbus_dev_frontend.c2
474 files changed, 11249 insertions, 21955 deletions
diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c
index 5f97468df8ff..b2e50d8007fe 100644
--- a/drivers/acpi/acpica/hwxface.c
+++ b/drivers/acpi/acpica/hwxface.c
@@ -504,11 +504,20 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b)
* Evaluate the \_Sx namespace object containing the register values
* for this state
*/
- info->relative_pathname =
- ACPI_CAST_PTR(char, acpi_gbl_sleep_state_names[sleep_state]);
+ info->relative_pathname = ACPI_CAST_PTR(char,
+ acpi_gbl_sleep_state_names
+ [sleep_state]);
+
status = acpi_ns_evaluate(info);
if (ACPI_FAILURE(status)) {
- goto cleanup;
+ if (status == AE_NOT_FOUND) {
+
+ /* The _Sx states are optional, ignore NOT_FOUND */
+
+ goto final_cleanup;
+ }
+
+ goto warning_cleanup;
}
/* Must have a return object */
@@ -517,7 +526,7 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b)
ACPI_ERROR((AE_INFO, "No Sleep State object returned from [%s]",
info->relative_pathname));
status = AE_AML_NO_RETURN_VALUE;
- goto cleanup;
+ goto warning_cleanup;
}
/* Return object must be of type Package */
@@ -526,7 +535,7 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b)
ACPI_ERROR((AE_INFO,
"Sleep State return object is not a Package"));
status = AE_AML_OPERAND_TYPE;
- goto cleanup1;
+ goto return_value_cleanup;
}
/*
@@ -570,16 +579,17 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b)
break;
}
-cleanup1:
+return_value_cleanup:
acpi_ut_remove_reference(info->return_object);
-cleanup:
+warning_cleanup:
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"While evaluating Sleep State [%s]",
info->relative_pathname));
}
+final_cleanup:
ACPI_FREE(info);
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 3dd9c462d22a..8f8da9f92090 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -657,7 +657,7 @@ static int ghes_proc(struct ghes *ghes)
ghes_do_proc(ghes, ghes->estatus);
out:
ghes_clear_estatus(ghes);
- return 0;
+ return rc;
}
static void ghes_add_timer(struct ghes *ghes)
diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c
index 5230e8449d30..c097f477c74c 100644
--- a/drivers/acpi/nfit.c
+++ b/drivers/acpi/nfit.c
@@ -1806,6 +1806,9 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event)
dev_dbg(dev, "%s: event: %d\n", __func__, event);
+ if (event != NFIT_NOTIFY_UPDATE)
+ return;
+
device_lock(dev);
if (!dev->driver) {
/* dev->driver may be null if we're being removed */
diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h
index 3d549a383659..13d6ec1ff055 100644
--- a/drivers/acpi/nfit.h
+++ b/drivers/acpi/nfit.h
@@ -45,6 +45,10 @@ enum {
ND_BLK_DCR_LATCH = 2,
};
+enum nfit_root_notifiers {
+ NFIT_NOTIFY_UPDATE = 0x80,
+};
+
struct nfit_spa {
struct acpi_nfit_system_address *spa;
struct list_head list;
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index c8c11de7d44c..0f6591a72cf8 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -1113,7 +1113,7 @@ static int binder_dec_node(struct binder_node *node, int strong, int internal)
static struct binder_ref *binder_get_ref(struct binder_proc *proc,
- uint32_t desc, bool need_strong_ref)
+ u32 desc, bool need_strong_ref)
{
struct rb_node *n = proc->refs_by_desc.rb_node;
struct binder_ref *ref;
@@ -2117,7 +2117,7 @@ static void binder_transaction(struct binder_proc *proc,
goto err_bad_offset;
}
if (!IS_ALIGNED(extra_buffers_size, sizeof(u64))) {
- binder_user_error("%d:%d got transaction with unaligned buffers size, %lld\n",
+ binder_user_error("%d:%d got transaction with unaligned buffers size, %llu\n",
proc->pid, thread->pid,
(u64)extra_buffers_size);
return_error = BR_FAILED_REPLY;
diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c
index 016f6f5f1e5e..7bc0d4a24c22 100644
--- a/drivers/base/dma-mapping.c
+++ b/drivers/base/dma-mapping.c
@@ -344,7 +344,7 @@ void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags,
return;
}
- unmap_kernel_range((unsigned long)cpu_addr, size);
+ unmap_kernel_range((unsigned long)cpu_addr, PAGE_ALIGN(size));
vunmap(cpu_addr);
}
#endif
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 87a48268b663..1c6e4da01e69 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -307,8 +307,7 @@ static const char * const fw_path[] = {
"/lib/firmware/updates/" UTS_RELEASE,
"/lib/firmware/updates",
"/lib/firmware/" UTS_RELEASE,
- "/lib/firmware",
- "/firmware/image"
+ "/lib/firmware"
};
/*
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index bd70f8db469b..9920916a6220 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -96,7 +96,7 @@ int platform_get_irq(struct platform_device *dev, unsigned int num)
int ret;
ret = of_irq_get(dev->dev.of_node, num);
- if (ret >= 0 || ret == -EPROBE_DEFER)
+ if (ret > 0 || ret == -EPROBE_DEFER)
return ret;
}
@@ -174,7 +174,7 @@ int platform_get_irq_byname(struct platform_device *dev, const char *name)
int ret;
ret = of_irq_get_byname(dev->dev.of_node, name);
- if (ret >= 0 || ret == -EPROBE_DEFER)
+ if (ret > 0 || ret == -EPROBE_DEFER)
return ret;
}
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 7eea95d490e6..6c5bc3fadfcf 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -1025,6 +1025,8 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
TRACE_DEVICE(dev);
TRACE_SUSPEND(0);
+ dpm_wait_for_children(dev, async);
+
if (async_error)
goto Complete;
@@ -1036,8 +1038,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
if (dev->power.syscore || dev->power.direct_complete)
goto Complete;
- dpm_wait_for_children(dev, async);
-
if (dev->pm_domain) {
info = "noirq power domain ";
callback = pm_noirq_op(&dev->pm_domain->ops, state);
@@ -1172,6 +1172,8 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
__pm_runtime_disable(dev, false);
+ dpm_wait_for_children(dev, async);
+
if (async_error)
goto Complete;
@@ -1183,8 +1185,6 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
if (dev->power.syscore || dev->power.direct_complete)
goto Complete;
- dpm_wait_for_children(dev, async);
-
if (dev->pm_domain) {
info = "late power domain ";
callback = pm_late_early_op(&dev->pm_domain->ops, state);
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index 74d97f4bac34..1d58854c4a9f 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -1802,7 +1802,7 @@ int drbd_send(struct drbd_connection *connection, struct socket *sock,
* do we need to block DRBD_SIG if sock == &meta.socket ??
* otherwise wake_asender() might interrupt some send_*Ack !
*/
- rv = kernel_sendmsg(sock, &msg, &iov, 1, size);
+ rv = kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len);
if (rv == -EAGAIN) {
if (we_should_drop_the_connection(connection, sock))
break;
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 3c59417b485a..eada042b6eab 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -1381,7 +1381,8 @@ static ssize_t hot_remove_store(struct class *class,
zram = idr_find(&zram_index_idr, dev_id);
if (zram) {
ret = zram_remove(zram);
- idr_remove(&zram_index_idr, dev_id);
+ if (!ret)
+ idr_remove(&zram_index_idr, dev_id);
} else {
ret = -ENODEV;
}
diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c
index 7082c7268845..0f54cb7ddcbb 100644
--- a/drivers/bus/arm-ccn.c
+++ b/drivers/bus/arm-ccn.c
@@ -187,6 +187,7 @@ struct arm_ccn {
struct arm_ccn_component *xp;
struct arm_ccn_dt dt;
+ int mn_id;
};
@@ -326,6 +327,7 @@ struct arm_ccn_pmu_event {
static ssize_t arm_ccn_pmu_event_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));
struct arm_ccn_pmu_event *event = container_of(attr,
struct arm_ccn_pmu_event, attr);
ssize_t res;
@@ -352,6 +354,9 @@ static ssize_t arm_ccn_pmu_event_show(struct device *dev,
res += snprintf(buf + res, PAGE_SIZE - res,
",cmp_l=?,cmp_h=?,mask=?");
break;
+ case CCN_TYPE_MN:
+ res += snprintf(buf + res, PAGE_SIZE - res, ",node=%d", ccn->mn_id);
+ break;
default:
res += snprintf(buf + res, PAGE_SIZE - res, ",node=?");
break;
@@ -381,9 +386,9 @@ static umode_t arm_ccn_pmu_events_is_visible(struct kobject *kobj,
}
static struct arm_ccn_pmu_event arm_ccn_pmu_events[] = {
- CCN_EVENT_MN(eobarrier, "dir=0,vc=0,cmp_h=0x1c00", CCN_IDX_MASK_OPCODE),
- CCN_EVENT_MN(ecbarrier, "dir=0,vc=0,cmp_h=0x1e00", CCN_IDX_MASK_OPCODE),
- CCN_EVENT_MN(dvmop, "dir=0,vc=0,cmp_h=0x2800", CCN_IDX_MASK_OPCODE),
+ CCN_EVENT_MN(eobarrier, "dir=1,vc=0,cmp_h=0x1c00", CCN_IDX_MASK_OPCODE),
+ CCN_EVENT_MN(ecbarrier, "dir=1,vc=0,cmp_h=0x1e00", CCN_IDX_MASK_OPCODE),
+ CCN_EVENT_MN(dvmop, "dir=1,vc=0,cmp_h=0x2800", CCN_IDX_MASK_OPCODE),
CCN_EVENT_HNI(txdatflits, "dir=1,vc=3", CCN_IDX_MASK_ANY),
CCN_EVENT_HNI(rxdatflits, "dir=0,vc=3", CCN_IDX_MASK_ANY),
CCN_EVENT_HNI(txreqflits, "dir=1,vc=0", CCN_IDX_MASK_ANY),
@@ -757,6 +762,12 @@ static int arm_ccn_pmu_event_init(struct perf_event *event)
/* Validate node/xp vs topology */
switch (type) {
+ case CCN_TYPE_MN:
+ if (node_xp != ccn->mn_id) {
+ dev_warn(ccn->dev, "Invalid MN ID %d!\n", node_xp);
+ return -EINVAL;
+ }
+ break;
case CCN_TYPE_XP:
if (node_xp >= ccn->num_xps) {
dev_warn(ccn->dev, "Invalid XP ID %d!\n", node_xp);
@@ -884,6 +895,10 @@ static void arm_ccn_pmu_xp_dt_config(struct perf_event *event, int enable)
struct arm_ccn_component *xp;
u32 val, dt_cfg;
+ /* Nothing to do for cycle counter */
+ if (hw->idx == CCN_IDX_PMU_CYCLE_COUNTER)
+ return;
+
if (CCN_CONFIG_TYPE(event->attr.config) == CCN_TYPE_XP)
xp = &ccn->xp[CCN_CONFIG_XP(event->attr.config)];
else
@@ -986,7 +1001,7 @@ static void arm_ccn_pmu_xp_watchpoint_config(struct perf_event *event)
/* Comparison values */
writel(cmp_l & 0xffffffff, source->base + CCN_XP_DT_CMP_VAL_L(wp));
- writel((cmp_l >> 32) & 0xefffffff,
+ writel((cmp_l >> 32) & 0x7fffffff,
source->base + CCN_XP_DT_CMP_VAL_L(wp) + 4);
writel(cmp_h & 0xffffffff, source->base + CCN_XP_DT_CMP_VAL_H(wp));
writel((cmp_h >> 32) & 0x0fffffff,
@@ -994,7 +1009,7 @@ static void arm_ccn_pmu_xp_watchpoint_config(struct perf_event *event)
/* Mask */
writel(mask_l & 0xffffffff, source->base + CCN_XP_DT_CMP_MASK_L(wp));
- writel((mask_l >> 32) & 0xefffffff,
+ writel((mask_l >> 32) & 0x7fffffff,
source->base + CCN_XP_DT_CMP_MASK_L(wp) + 4);
writel(mask_h & 0xffffffff, source->base + CCN_XP_DT_CMP_MASK_H(wp));
writel((mask_h >> 32) & 0x0fffffff,
@@ -1368,6 +1383,8 @@ static int arm_ccn_init_nodes(struct arm_ccn *ccn, int region,
switch (type) {
case CCN_TYPE_MN:
+ ccn->mn_id = id;
+ return 0;
case CCN_TYPE_DT:
return 0;
case CCN_TYPE_XP:
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 88bd6afdeea5..9ca46ae54ce3 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -1951,6 +1951,8 @@ static void fastrpc_channel_close(struct kref *kref)
cid = ctx - &gcinfo[0];
fastrpc_glink_close(ctx->chan, cid);
ctx->chan = 0;
+ glink_unregister_link_state_cb(ctx->link.link_notify_handle);
+ ctx->link.link_notify_handle = 0;
mutex_unlock(&me->smd_mutex);
pr_info("'closed /dev/%s c %d %d'\n", gcinfo[cid].name,
MAJOR(me->dev_no), cid);
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 6f497aa1b276..cf25020576fa 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -84,14 +84,14 @@ static size_t rng_buffer_size(void)
static void add_early_randomness(struct hwrng *rng)
{
- unsigned char bytes[16];
int bytes_read;
+ size_t size = min_t(size_t, 16, rng_buffer_size());
mutex_lock(&reading_mutex);
- bytes_read = rng_get_data(rng, bytes, sizeof(bytes), 1);
+ bytes_read = rng_get_data(rng, rng_buffer, size, 1);
mutex_unlock(&reading_mutex);
if (bytes_read > 0)
- add_device_randomness(bytes, bytes_read);
+ add_device_randomness(rng_buffer, bytes_read);
}
static inline void cleanup_rng(struct kref *kref)
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index 8a1432e8bb80..f5c26a5f6875 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -384,7 +384,12 @@ static int omap_rng_probe(struct platform_device *pdev)
}
pm_runtime_enable(&pdev->dev);
- pm_runtime_get_sync(&pdev->dev);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to runtime_get device: %d\n", ret);
+ pm_runtime_put_noidle(&pdev->dev);
+ goto err_ioremap;
+ }
ret = (dev->of_node) ? of_get_omap_rng_device_details(priv, pdev) :
get_omap_rng_device_details(priv);
@@ -435,8 +440,15 @@ static int __maybe_unused omap_rng_suspend(struct device *dev)
static int __maybe_unused omap_rng_resume(struct device *dev)
{
struct omap_rng_dev *priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to runtime_get device: %d\n", ret);
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
- pm_runtime_get_sync(dev);
priv->pdata->init(priv);
return 0;
diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
index de0337ebd658..4f3137d9a35e 100644
--- a/drivers/char/tpm/tpm-dev.c
+++ b/drivers/char/tpm/tpm-dev.c
@@ -139,7 +139,7 @@ static ssize_t tpm_write(struct file *file, const char __user *buf,
/* atomic tpm command send and result receive */
out_size = tpm_transmit(priv->chip, priv->data_buffer,
- sizeof(priv->data_buffer));
+ sizeof(priv->data_buffer), 0);
if (out_size < 0) {
mutex_unlock(&priv->buffer_mutex);
return out_size;
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index c50637db3a8a..17abe52e6365 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -328,8 +328,8 @@ EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
/*
* Internal kernel interface to transmit TPM commands
*/
-ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
- size_t bufsiz)
+ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
+ unsigned int flags)
{
ssize_t rc;
u32 count, ordinal;
@@ -348,7 +348,8 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
return -E2BIG;
}
- mutex_lock(&chip->tpm_mutex);
+ if (!(flags & TPM_TRANSMIT_UNLOCKED))
+ mutex_lock(&chip->tpm_mutex);
rc = chip->ops->send(chip, (u8 *) buf, count);
if (rc < 0) {
@@ -391,20 +392,21 @@ out_recv:
dev_err(chip->pdev,
"tpm_transmit: tpm_recv: error %zd\n", rc);
out:
- mutex_unlock(&chip->tpm_mutex);
+ if (!(flags & TPM_TRANSMIT_UNLOCKED))
+ mutex_unlock(&chip->tpm_mutex);
return rc;
}
#define TPM_DIGEST_SIZE 20
#define TPM_RET_CODE_IDX 6
-ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd,
- int len, const char *desc)
+ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *cmd,
+ int len, unsigned int flags, const char *desc)
{
- struct tpm_output_header *header;
+ const struct tpm_output_header *header;
int err;
- len = tpm_transmit(chip, (u8 *) cmd, len);
+ len = tpm_transmit(chip, (const u8 *)cmd, len, flags);
if (len < 0)
return len;
else if (len < TPM_HEADER_SIZE)
@@ -452,7 +454,8 @@ ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap,
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = subcap_id;
}
- rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
+ rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
+ desc);
if (!rc)
*cap = tpm_cmd.params.getcap_out.cap;
return rc;
@@ -468,7 +471,7 @@ void tpm_gen_interrupt(struct tpm_chip *chip)
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
- rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
+ rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
"attempting to determine the timeouts");
}
EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
@@ -489,7 +492,7 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
start_cmd.header.in = tpm_startup_header;
start_cmd.params.startup_in.startup_type = startup_type;
- return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE,
+ return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
"attempting to start the TPM");
}
@@ -505,7 +508,8 @@ int tpm_get_timeouts(struct tpm_chip *chip)
tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
- rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
+ rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
+ NULL);
if (rc == TPM_ERR_INVALID_POSTINIT) {
/* The TPM is not started, we are the first to talk to it.
@@ -519,7 +523,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
- NULL);
+ 0, NULL);
}
if (rc) {
dev_err(chip->pdev,
@@ -580,7 +584,7 @@ duration:
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
- rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
+ rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
"attempting to determine the durations");
if (rc)
return rc;
@@ -636,7 +640,7 @@ static int tpm_continue_selftest(struct tpm_chip *chip)
struct tpm_cmd_t cmd;
cmd.header.in = continue_selftest_header;
- rc = tpm_transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE,
+ rc = tpm_transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE, 0,
"continue selftest");
return rc;
}
@@ -656,7 +660,7 @@ int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
cmd.header.in = pcrread_header;
cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx);
- rc = tpm_transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE,
+ rc = tpm_transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE, 0,
"attempting to read a pcr value");
if (rc == 0)
@@ -754,7 +758,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
cmd.header.in = pcrextend_header;
cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx);
memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE);
- rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
+ rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, 0,
"attempting extend a PCR value");
tpm_chip_put(chip);
@@ -793,7 +797,7 @@ int tpm_do_selftest(struct tpm_chip *chip)
/* Attempt to read a PCR value */
cmd.header.in = pcrread_header;
cmd.params.pcrread_in.pcr_idx = cpu_to_be32(0);
- rc = tpm_transmit(chip, (u8 *) &cmd, READ_PCR_RESULT_SIZE);
+ rc = tpm_transmit(chip, (u8 *) &cmd, READ_PCR_RESULT_SIZE, 0);
/* Some buggy TPMs will not respond to tpm_tis_ready() for
* around 300ms while the self test is ongoing, keep trying
* until the self test duration expires. */
@@ -834,7 +838,7 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen)
if (chip == NULL)
return -ENODEV;
- rc = tpm_transmit_cmd(chip, cmd, buflen, "attempting tpm_cmd");
+ rc = tpm_transmit_cmd(chip, cmd, buflen, 0, "attempting tpm_cmd");
tpm_chip_put(chip);
return rc;
@@ -936,14 +940,15 @@ int tpm_pm_suspend(struct device *dev)
cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(tpm_suspend_pcr);
memcpy(cmd.params.pcrextend_in.hash, dummy_hash,
TPM_DIGEST_SIZE);
- rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
+ rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, 0,
"extending dummy pcr before suspend");
}
/* now do the actual savestate */
for (try = 0; try < TPM_RETRY; try++) {
cmd.header.in = savestate_header;
- rc = tpm_transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, NULL);
+ rc = tpm_transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, 0,
+ NULL);
/*
* If the TPM indicates that it is too busy to respond to
@@ -1027,8 +1032,8 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes);
err = tpm_transmit_cmd(chip, &tpm_cmd,
- TPM_GETRANDOM_RESULT_SIZE + num_bytes,
- "attempting get random");
+ TPM_GETRANDOM_RESULT_SIZE + num_bytes,
+ 0, "attempting get random");
if (err)
break;
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index ee66fd4673f3..f880856aa75e 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -39,7 +39,7 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
struct tpm_chip *chip = dev_get_drvdata(dev);
tpm_cmd.header.in = tpm_readpubek_header;
- err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
+ err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE, 0,
"attempting to read the PUBEK");
if (err)
goto out;
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index a4257a32964f..2216861f89f1 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -498,11 +498,15 @@ extern struct class *tpm_class;
extern dev_t tpm_devt;
extern const struct file_operations tpm_fops;
+enum tpm_transmit_flags {
+ TPM_TRANSMIT_UNLOCKED = BIT(0),
+};
+
+ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
+ unsigned int flags);
+ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *cmd, int len,
+ unsigned int flags, const char *desc);
ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *);
-ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
- size_t bufsiz);
-ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd, int len,
- const char *desc);
extern int tpm_get_timeouts(struct tpm_chip *);
extern void tpm_gen_interrupt(struct tpm_chip *);
extern int tpm_do_selftest(struct tpm_chip *);
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index c12130485fc1..cb7e4f6b70ba 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -264,7 +264,7 @@ int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
sizeof(cmd.params.pcrread_in.pcr_select));
cmd.params.pcrread_in.pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
- rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+ rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0,
"attempting to read a pcr value");
if (rc == 0) {
buf = cmd.params.pcrread_out.digest;
@@ -312,7 +312,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash)
cmd.params.pcrextend_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1);
memcpy(cmd.params.pcrextend_in.digest, hash, TPM_DIGEST_SIZE);
- rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+ rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0,
"attempting extend a PCR value");
return rc;
@@ -358,7 +358,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max)
cmd.header.in = tpm2_getrandom_header;
cmd.params.getrandom_in.size = cpu_to_be16(num_bytes);
- err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+ err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0,
"attempting get random");
if (err)
break;
@@ -416,12 +416,12 @@ static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle,
}
/**
- * tpm2_seal_trusted() - seal a trusted key
- * @chip_num: A specific chip number for the request or TPM_ANY_NUM
- * @options: authentication values and other options
+ * tpm2_seal_trusted() - seal the payload of a trusted key
+ * @chip_num: TPM chip to use
* @payload: the key data in clear and encrypted form
+ * @options: authentication values and other options
*
- * Returns < 0 on error and 0 on success.
+ * Return: < 0 on error and 0 on success.
*/
int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
@@ -472,7 +472,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out;
}
- rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "sealing data");
+ rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 0, "sealing data");
if (rc)
goto out;
@@ -494,10 +494,18 @@ out:
return rc;
}
-static int tpm2_load(struct tpm_chip *chip,
- struct trusted_key_payload *payload,
- struct trusted_key_options *options,
- u32 *blob_handle)
+/**
+ * tpm2_load_cmd() - execute a TPM2_Load command
+ * @chip_num: TPM chip to use
+ * @payload: the key data in clear and encrypted form
+ * @options: authentication values and other options
+ *
+ * Return: same as with tpm_transmit_cmd
+ */
+static int tpm2_load_cmd(struct tpm_chip *chip,
+ struct trusted_key_payload *payload,
+ struct trusted_key_options *options,
+ u32 *blob_handle, unsigned int flags)
{
struct tpm_buf buf;
unsigned int private_len;
@@ -532,7 +540,7 @@ static int tpm2_load(struct tpm_chip *chip,
goto out;
}
- rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "loading blob");
+ rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags, "loading blob");
if (!rc)
*blob_handle = be32_to_cpup(
(__be32 *) &buf.data[TPM_HEADER_SIZE]);
@@ -546,7 +554,16 @@ out:
return rc;
}
-static void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
+/**
+ * tpm2_flush_context_cmd() - execute a TPM2_FlushContext command
+ * @chip_num: TPM chip to use
+ * @payload: the key data in clear and encrypted form
+ * @options: authentication values and other options
+ *
+ * Return: same as with tpm_transmit_cmd
+ */
+static void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
+ unsigned int flags)
{
struct tpm_buf buf;
int rc;
@@ -560,7 +577,8 @@ static void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
tpm_buf_append_u32(&buf, handle);
- rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "flushing context");
+ rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags,
+ "flushing context");
if (rc)
dev_warn(chip->pdev, "0x%08x was not flushed, rc=%d\n", handle,
rc);
@@ -568,10 +586,18 @@ static void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
tpm_buf_destroy(&buf);
}
-static int tpm2_unseal(struct tpm_chip *chip,
- struct trusted_key_payload *payload,
- struct trusted_key_options *options,
- u32 blob_handle)
+/**
+ * tpm2_unseal_cmd() - execute a TPM2_Unload command
+ * @chip_num: TPM chip to use
+ * @payload: the key data in clear and encrypted form
+ * @options: authentication values and other options
+ *
+ * Return: same as with tpm_transmit_cmd
+ */
+static int tpm2_unseal_cmd(struct tpm_chip *chip,
+ struct trusted_key_payload *payload,
+ struct trusted_key_options *options,
+ u32 blob_handle, unsigned int flags)
{
struct tpm_buf buf;
u16 data_len;
@@ -589,7 +615,7 @@ static int tpm2_unseal(struct tpm_chip *chip,
options->blobauth /* hmac */,
TPM_DIGEST_SIZE);
- rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "unsealing");
+ rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags, "unsealing");
if (rc > 0)
rc = -EPERM;
@@ -608,12 +634,12 @@ static int tpm2_unseal(struct tpm_chip *chip,
}
/**
- * tpm_unseal_trusted() - unseal a trusted key
- * @chip_num: A specific chip number for the request or TPM_ANY_NUM
- * @options: authentication values and other options
+ * tpm_unseal_trusted() - unseal the payload of a trusted key
+ * @chip_num: TPM chip to use
* @payload: the key data in clear and encrypted form
+ * @options: authentication values and other options
*
- * Returns < 0 on error and 0 on success.
+ * Return: < 0 on error and 0 on success.
*/
int tpm2_unseal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
@@ -622,14 +648,17 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
u32 blob_handle;
int rc;
- rc = tpm2_load(chip, payload, options, &blob_handle);
+ mutex_lock(&chip->tpm_mutex);
+ rc = tpm2_load_cmd(chip, payload, options, &blob_handle,
+ TPM_TRANSMIT_UNLOCKED);
if (rc)
- return rc;
-
- rc = tpm2_unseal(chip, payload, options, blob_handle);
-
- tpm2_flush_context(chip, blob_handle);
+ goto out;
+ rc = tpm2_unseal_cmd(chip, payload, options, blob_handle,
+ TPM_TRANSMIT_UNLOCKED);
+ tpm2_flush_context_cmd(chip, blob_handle, TPM_TRANSMIT_UNLOCKED);
+out:
+ mutex_unlock(&chip->tpm_mutex);
return rc;
}
@@ -655,9 +684,9 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(property_id);
cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1);
- rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), desc);
+ rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, desc);
if (!rc)
- *value = cmd.params.get_tpm_pt_out.value;
+ *value = be32_to_cpu(cmd.params.get_tpm_pt_out.value);
return rc;
}
@@ -689,7 +718,7 @@ int tpm2_startup(struct tpm_chip *chip, u16 startup_type)
cmd.header.in = tpm2_startup_header;
cmd.params.startup_in.startup_type = cpu_to_be16(startup_type);
- return tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+ return tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0,
"attempting to start the TPM");
}
EXPORT_SYMBOL_GPL(tpm2_startup);
@@ -718,7 +747,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
cmd.header.in = tpm2_shutdown_header;
cmd.params.startup_in.startup_type = cpu_to_be16(shutdown_type);
- rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), "stopping the TPM");
+ rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, "stopping the TPM");
/* In places where shutdown command is sent there's no much we can do
* except print the error code on a system failure.
@@ -784,7 +813,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full)
cmd.header.in = tpm2_selftest_header;
cmd.params.selftest_in.full_test = full;
- rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE,
+ rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE, 0,
"continue selftest");
/* At least some prototype chips seem to give RC_TESTING error
@@ -836,7 +865,7 @@ int tpm2_do_selftest(struct tpm_chip *chip)
cmd.params.pcrread_in.pcr_select[1] = 0x00;
cmd.params.pcrread_in.pcr_select[2] = 0x00;
- rc = tpm_transmit_cmd(chip, (u8 *) &cmd, sizeof(cmd), NULL);
+ rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, NULL);
if (rc < 0)
break;
@@ -885,7 +914,7 @@ int tpm2_probe(struct tpm_chip *chip)
cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(0x100);
cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1);
- rc = tpm_transmit(chip, (const char *) &cmd, sizeof(cmd));
+ rc = tpm_transmit(chip, (const u8 *)&cmd, sizeof(cmd), 0);
if (rc < 0)
return rc;
else if (rc < TPM_HEADER_SIZE)
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index 61e64293b765..2b21398c3adc 100644
--- a/drivers/char/tpm/tpm_crb.c
+++ b/drivers/char/tpm/tpm_crb.c
@@ -149,6 +149,11 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
struct crb_priv *priv = chip->vendor.priv;
int rc = 0;
+ /* Zero the cancel register so that the next command will not get
+ * canceled.
+ */
+ iowrite32(0, &priv->cca->cancel);
+
if (len > le32_to_cpu(ioread32(&priv->cca->cmd_size))) {
dev_err(&chip->dev,
"invalid command count value %x %zx\n",
@@ -182,8 +187,6 @@ static void crb_cancel(struct tpm_chip *chip)
if ((priv->flags & CRB_FL_ACPI_START) && crb_do_acpi_start(chip))
dev_err(&chip->dev, "ACPI Start failed\n");
-
- iowrite32(0, &priv->cca->cancel);
}
static bool crb_req_canceled(struct tpm_chip *chip, u8 status)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index d2406fe25533..090183f812be 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1533,19 +1533,29 @@ static void remove_port_data(struct port *port)
spin_lock_irq(&port->inbuf_lock);
/* Remove unused data this port might have received. */
discard_port_data(port);
+ spin_unlock_irq(&port->inbuf_lock);
/* Remove buffers we queued up for the Host to send us data in. */
- while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
- free_buf(buf, true);
- spin_unlock_irq(&port->inbuf_lock);
+ do {
+ spin_lock_irq(&port->inbuf_lock);
+ buf = virtqueue_detach_unused_buf(port->in_vq);
+ spin_unlock_irq(&port->inbuf_lock);
+ if (buf)
+ free_buf(buf, true);
+ } while (buf);
spin_lock_irq(&port->outvq_lock);
reclaim_consumed_buffers(port);
+ spin_unlock_irq(&port->outvq_lock);
/* Free pending buffers from the out-queue. */
- while ((buf = virtqueue_detach_unused_buf(port->out_vq)))
- free_buf(buf, true);
- spin_unlock_irq(&port->outvq_lock);
+ do {
+ spin_lock_irq(&port->outvq_lock);
+ buf = virtqueue_detach_unused_buf(port->out_vq);
+ spin_unlock_irq(&port->outvq_lock);
+ if (buf)
+ free_buf(buf, true);
+ } while (buf);
}
/*
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 0c83ffc22dd2..f45d923ed3cd 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -354,7 +354,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
/* if read only, just return current value */
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
- bestdiv = readl(divider->reg) >> divider->shift;
+ bestdiv = clk_readl(divider->reg) >> divider->shift;
bestdiv &= div_mask(divider->width);
bestdiv = _get_div(divider->table, bestdiv, divider->flags,
divider->width);
diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c
index 7bc1c4527ae4..a5070f9cb0d4 100644
--- a/drivers/clk/clk-qoriq.c
+++ b/drivers/clk/clk-qoriq.c
@@ -700,6 +700,7 @@ static struct clk * __init create_mux_common(struct clockgen *cg,
struct mux_hwclock *hwc,
const struct clk_ops *ops,
unsigned long min_rate,
+ unsigned long max_rate,
unsigned long pct80_rate,
const char *fmt, int idx)
{
@@ -728,6 +729,8 @@ static struct clk * __init create_mux_common(struct clockgen *cg,
continue;
if (rate < min_rate)
continue;
+ if (rate > max_rate)
+ continue;
parent_names[j] = div->name;
hwc->parent_to_clksel[j] = i;
@@ -759,14 +762,18 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx)
struct mux_hwclock *hwc;
const struct clockgen_pll_div *div;
unsigned long plat_rate, min_rate;
- u64 pct80_rate;
+ u64 max_rate, pct80_rate;
u32 clksel;
hwc = kzalloc(sizeof(*hwc), GFP_KERNEL);
if (!hwc)
return NULL;
- hwc->reg = cg->regs + 0x20 * idx;
+ if (cg->info.flags & CG_VER3)
+ hwc->reg = cg->regs + 0x70000 + 0x20 * idx;
+ else
+ hwc->reg = cg->regs + 0x20 * idx;
+
hwc->info = cg->info.cmux_groups[cg->info.cmux_to_group[idx]];
/*
@@ -783,8 +790,8 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx)
return NULL;
}
- pct80_rate = clk_get_rate(div->clk);
- pct80_rate *= 8;
+ max_rate = clk_get_rate(div->clk);
+ pct80_rate = max_rate * 8;
do_div(pct80_rate, 10);
plat_rate = clk_get_rate(cg->pll[PLATFORM_PLL].div[PLL_DIV1].clk);
@@ -794,7 +801,7 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx)
else
min_rate = plat_rate / 2;
- return create_mux_common(cg, hwc, &cmux_ops, min_rate,
+ return create_mux_common(cg, hwc, &cmux_ops, min_rate, max_rate,
pct80_rate, "cg-cmux%d", idx);
}
@@ -809,7 +816,7 @@ static struct clk * __init create_one_hwaccel(struct clockgen *cg, int idx)
hwc->reg = cg->regs + 0x20 * idx + 0x10;
hwc->info = cg->info.hwaccel[idx];
- return create_mux_common(cg, hwc, &hwaccel_ops, 0, 0,
+ return create_mux_common(cg, hwc, &hwaccel_ops, 0, ULONG_MAX, 0,
"cg-hwaccel%d", idx);
}
diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c
index 10224b01b97c..b134a8b15e2c 100644
--- a/drivers/clk/clk-xgene.c
+++ b/drivers/clk/clk-xgene.c
@@ -351,8 +351,8 @@ static int xgene_clk_set_rate(struct clk_hw *hw, unsigned long rate,
/* Set new divider */
data = xgene_clk_read(pclk->param.divider_reg +
pclk->param.reg_divider_offset);
- data &= ~((1 << pclk->param.reg_divider_width) - 1)
- << pclk->param.reg_divider_shift;
+ data &= ~(((1 << pclk->param.reg_divider_width) - 1)
+ << pclk->param.reg_divider_shift);
data |= divider;
xgene_clk_write(data, pclk->param.divider_reg +
pclk->param.reg_divider_offset);
diff --git a/drivers/clk/imx/clk-imx35.c b/drivers/clk/imx/clk-imx35.c
index b0978d3b83e2..d302ed3b8225 100644
--- a/drivers/clk/imx/clk-imx35.c
+++ b/drivers/clk/imx/clk-imx35.c
@@ -115,7 +115,7 @@ static void __init _mx35_clocks_init(void)
}
clk[ckih] = imx_clk_fixed("ckih", 24000000);
- clk[ckil] = imx_clk_fixed("ckih", 32768);
+ clk[ckil] = imx_clk_fixed("ckil", 32768);
clk[mpll] = imx_clk_pllv1(IMX_PLLV1_IMX35, "mpll", "ckih", base + MX35_CCM_MPCTL);
clk[ppll] = imx_clk_pllv1(IMX_PLLV1_IMX35, "ppll", "ckih", base + MX35_CCM_PPCTL);
diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
index c1935081d34a..aab64205d866 100644
--- a/drivers/clk/imx/clk-imx6q.c
+++ b/drivers/clk/imx/clk-imx6q.c
@@ -550,6 +550,24 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
if (IS_ENABLED(CONFIG_PCI_IMX6))
clk_set_parent(clk[IMX6QDL_CLK_LVDS1_SEL], clk[IMX6QDL_CLK_SATA_REF_100M]);
+ /*
+ * Initialize the GPU clock muxes, so that the maximum specified clock
+ * rates for the respective SoC are not exceeded.
+ */
+ if (clk_on_imx6dl()) {
+ clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL],
+ clk[IMX6QDL_CLK_PLL2_PFD1_594M]);
+ clk_set_parent(clk[IMX6QDL_CLK_GPU2D_CORE_SEL],
+ clk[IMX6QDL_CLK_PLL2_PFD1_594M]);
+ } else if (clk_on_imx6q()) {
+ clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL],
+ clk[IMX6QDL_CLK_MMDC_CH0_AXI]);
+ clk_set_parent(clk[IMX6QDL_CLK_GPU3D_SHADER_SEL],
+ clk[IMX6QDL_CLK_PLL2_PFD1_594M]);
+ clk_set_parent(clk[IMX6QDL_CLK_GPU2D_CORE_SEL],
+ clk[IMX6QDL_CLK_PLL3_USB_OTG]);
+ }
+
imx_register_uart_clocks(uart_clks);
}
CLK_OF_DECLARE(imx6q, "fsl,imx6q-ccm", imx6q_clocks_init);
diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c
index 251533d87c65..f261b1d292c7 100644
--- a/drivers/clk/mmp/clk-of-mmp2.c
+++ b/drivers/clk/mmp/clk-of-mmp2.c
@@ -313,7 +313,7 @@ static void __init mmp2_clk_init(struct device_node *np)
}
pxa_unit->apmu_base = of_iomap(np, 1);
- if (!pxa_unit->mpmu_base) {
+ if (!pxa_unit->apmu_base) {
pr_err("failed to map apmu registers\n");
return;
}
diff --git a/drivers/clk/mmp/clk-of-pxa168.c b/drivers/clk/mmp/clk-of-pxa168.c
index 64eaf4141c69..427f4bb08665 100644
--- a/drivers/clk/mmp/clk-of-pxa168.c
+++ b/drivers/clk/mmp/clk-of-pxa168.c
@@ -262,7 +262,7 @@ static void __init pxa168_clk_init(struct device_node *np)
}
pxa_unit->apmu_base = of_iomap(np, 1);
- if (!pxa_unit->mpmu_base) {
+ if (!pxa_unit->apmu_base) {
pr_err("failed to map apmu registers\n");
return;
}
diff --git a/drivers/clk/mmp/clk-of-pxa910.c b/drivers/clk/mmp/clk-of-pxa910.c
index 13d6173326a4..cdf5ba566d3b 100644
--- a/drivers/clk/mmp/clk-of-pxa910.c
+++ b/drivers/clk/mmp/clk-of-pxa910.c
@@ -282,7 +282,7 @@ static void __init pxa910_clk_init(struct device_node *np)
}
pxa_unit->apmu_base = of_iomap(np, 1);
- if (!pxa_unit->mpmu_base) {
+ if (!pxa_unit->apmu_base) {
pr_err("failed to map apmu registers\n");
return;
}
@@ -294,7 +294,7 @@ static void __init pxa910_clk_init(struct device_node *np)
}
pxa_unit->apbcp_base = of_iomap(np, 3);
- if (!pxa_unit->mpmu_base) {
+ if (!pxa_unit->apbcp_base) {
pr_err("failed to map apbcp registers\n");
return;
}
diff --git a/drivers/clk/msm/clock-mmss-8998.c b/drivers/clk/msm/clock-mmss-8998.c
index 1661878fc650..6ebb3ed6ed91 100644
--- a/drivers/clk/msm/clock-mmss-8998.c
+++ b/drivers/clk/msm/clock-mmss-8998.c
@@ -664,8 +664,8 @@ static struct rcg_clk byte0_clk_src = {
.parent = &ext_byte0_clk_src.c,
.ops = &clk_ops_byte_multiparent,
.flags = CLKFLAG_NO_RATE_CACHE,
- VDD_DIG_FMAX_MAP3(LOWER, 150000000, LOW, 240000000,
- NOMINAL, 357140000),
+ VDD_DIG_FMAX_MAP3(LOWER, 131250000, LOW, 210000000,
+ NOMINAL, 312500000),
CLK_INIT(byte0_clk_src.c),
},
};
@@ -681,8 +681,8 @@ static struct rcg_clk byte1_clk_src = {
.parent = &ext_byte1_clk_src.c,
.ops = &clk_ops_byte_multiparent,
.flags = CLKFLAG_NO_RATE_CACHE,
- VDD_DIG_FMAX_MAP3(LOWER, 150000000, LOW, 240000000,
- NOMINAL, 357140000),
+ VDD_DIG_FMAX_MAP3(LOWER, 131250000, LOW, 210000000,
+ NOMINAL, 312500000),
CLK_INIT(byte1_clk_src.c),
},
};
@@ -722,8 +722,8 @@ static struct rcg_clk pclk0_clk_src = {
.parent = &ext_pclk0_clk_src.c,
.ops = &clk_ops_pixel_multiparent,
.flags = CLKFLAG_NO_RATE_CACHE,
- VDD_DIG_FMAX_MAP3(LOWER, 184000000, LOW, 295000000,
- NOMINAL, 610000000),
+ VDD_DIG_FMAX_MAP3(LOWER, 175000000, LOW, 280000000,
+ NOMINAL, 416670000),
CLK_INIT(pclk0_clk_src.c),
},
};
@@ -739,8 +739,8 @@ static struct rcg_clk pclk1_clk_src = {
.parent = &ext_pclk1_clk_src.c,
.ops = &clk_ops_pixel_multiparent,
.flags = CLKFLAG_NO_RATE_CACHE,
- VDD_DIG_FMAX_MAP3(LOWER, 184000000, LOW, 295000000,
- NOMINAL, 610000000),
+ VDD_DIG_FMAX_MAP3(LOWER, 175000000, LOW, 280000000,
+ NOMINAL, 416670000),
CLK_INIT(pclk1_clk_src.c),
},
};
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 9e5c0b6f7a0e..c9ba7f97eebe 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -513,7 +513,7 @@ static int clk_rcg2_enable(struct clk_hw *hw)
* is always on while APPS is online. Therefore, the RCG can safely be
* switched.
*/
- rate = clk_get_rate(hw->clk);
+ rate = rcg->current_freq;
f = qcom_find_freq(rcg->freq_tbl, rate);
if (!f)
return -EINVAL;
@@ -627,6 +627,9 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
rcg->new_index, false);
}
+ /* Update current frequency with the frequency requested. */
+ rcg->current_freq = rate;
+
return ret;
}
diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c
index 6f3719d73390..e84877a2cacc 100644
--- a/drivers/clocksource/sun4i_timer.c
+++ b/drivers/clocksource/sun4i_timer.c
@@ -123,12 +123,16 @@ static struct clock_event_device sun4i_clockevent = {
.set_next_event = sun4i_clkevt_next_event,
};
+static void sun4i_timer_clear_interrupt(void)
+{
+ writel(TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_ST_REG);
+}
static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = (struct clock_event_device *)dev_id;
- writel(0x1, timer_base + TIMER_IRQ_ST_REG);
+ sun4i_timer_clear_interrupt();
evt->event_handler(evt);
return IRQ_HANDLED;
@@ -193,6 +197,9 @@ static void __init sun4i_timer_init(struct device_node *node)
/* Make sure timer is stopped before playing with interrupts */
sun4i_clkevt_time_stop(0);
+ /* clear timer0 interrupt */
+ sun4i_timer_clear_interrupt();
+
sun4i_clockevent.cpumask = cpu_possible_mask;
sun4i_clockevent.irq = irq;
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 6e80e4298274..7ff8b15a3422 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -285,14 +285,14 @@ static void intel_pstate_hwp_set(void)
int min, hw_min, max, hw_max, cpu, range, adj_range;
u64 value, cap;
- rdmsrl(MSR_HWP_CAPABILITIES, cap);
- hw_min = HWP_LOWEST_PERF(cap);
- hw_max = HWP_HIGHEST_PERF(cap);
- range = hw_max - hw_min;
-
get_online_cpus();
for_each_online_cpu(cpu) {
+ rdmsrl_on_cpu(cpu, MSR_HWP_CAPABILITIES, &cap);
+ hw_min = HWP_LOWEST_PERF(cap);
+ hw_max = HWP_HIGHEST_PERF(cap);
+ range = hw_max - hw_min;
+
rdmsrl_on_cpu(cpu, MSR_HWP_REQUEST, &value);
adj_range = limits->min_perf_pct * range / 100;
min = hw_min + adj_range;
diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c
index e342565e8715..1855b9ee807f 100644
--- a/drivers/cpuidle/cpuidle-arm.c
+++ b/drivers/cpuidle/cpuidle-arm.c
@@ -135,6 +135,7 @@ static int __init arm_idle_init(void)
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
pr_err("Failed to allocate cpuidle device\n");
+ ret = -ENOMEM;
goto out_fail;
}
dev->cpu = cpu;
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 823b7d988284..d8e571981c35 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
* Copyright (C) 2006-2007 Adam Belay <abelay@novell.com>
* Copyright (C) 2009 Intel Corporation
*
@@ -36,6 +36,7 @@
#include <linux/moduleparam.h>
#include <linux/sched.h>
#include <linux/cpu_pm.h>
+#include <linux/arm-smccc.h>
#include <soc/qcom/spm.h>
#include <soc/qcom/pm.h>
#include <soc/qcom/rpm-notifier.h>
@@ -161,13 +162,13 @@ s32 msm_cpuidle_get_deep_idle_latency(void)
void lpm_suspend_wake_time(uint64_t wakeup_time)
{
if (wakeup_time <= 0) {
- suspend_wake_time = msm_pm_sleep_time_override;
+ suspend_wake_time = msm_pm_sleep_time_override * MSEC_PER_SEC;
return;
}
if (msm_pm_sleep_time_override &&
(msm_pm_sleep_time_override < wakeup_time))
- suspend_wake_time = msm_pm_sleep_time_override;
+ suspend_wake_time = msm_pm_sleep_time_override * MSEC_PER_SEC;
else
suspend_wake_time = wakeup_time;
}
@@ -793,7 +794,7 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
if (!suspend_wake_time)
return ~0ULL;
else
- return USEC_PER_SEC * suspend_wake_time;
+ return USEC_PER_MSEC * suspend_wake_time;
}
cpumask_and(&online_cpus_in_cluster,
@@ -1412,7 +1413,6 @@ unlock_and_return:
}
#if !defined(CONFIG_CPU_V7)
-asmlinkage int __invoke_psci_fn_smc(u64, u64, u64, u64);
bool psci_enter_sleep(struct lpm_cluster *cluster, int idx, bool from_idle)
{
/*
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index b3044219772c..2cde3796cb82 100644
--- a/drivers/crypto/caam/caamalg.c
+++ b/drivers/crypto/caam/caamalg.c
@@ -4542,6 +4542,15 @@ static int __init caam_algapi_init(void)
if (!aes_inst && (alg_sel == OP_ALG_ALGSEL_AES))
continue;
+ /*
+ * Check support for AES modes not available
+ * on LP devices.
+ */
+ if ((cha_vid & CHA_ID_LS_AES_MASK) == CHA_ID_LS_AES_LP)
+ if ((alg->class1_alg_type & OP_ALG_AAI_MASK) ==
+ OP_ALG_AAI_XTS)
+ continue;
+
t_alg = caam_alg_alloc(alg);
if (IS_ERR(t_alg)) {
err = PTR_ERR(t_alg);
diff --git a/drivers/crypto/vmx/ghash.c b/drivers/crypto/vmx/ghash.c
index 2183a2e77641..9cb3a0b715e2 100644
--- a/drivers/crypto/vmx/ghash.c
+++ b/drivers/crypto/vmx/ghash.c
@@ -26,16 +26,13 @@
#include <linux/hardirq.h>
#include <asm/switch_to.h>
#include <crypto/aes.h>
+#include <crypto/ghash.h>
#include <crypto/scatterwalk.h>
#include <crypto/internal/hash.h>
#include <crypto/b128ops.h>
#define IN_INTERRUPT in_interrupt()
-#define GHASH_BLOCK_SIZE (16)
-#define GHASH_DIGEST_SIZE (16)
-#define GHASH_KEY_LEN (16)
-
void gcm_init_p8(u128 htable[16], const u64 Xi[2]);
void gcm_gmult_p8(u64 Xi[2], const u128 htable[16]);
void gcm_ghash_p8(u64 Xi[2], const u128 htable[16],
@@ -55,16 +52,11 @@ struct p8_ghash_desc_ctx {
static int p8_ghash_init_tfm(struct crypto_tfm *tfm)
{
- const char *alg;
+ const char *alg = "ghash-generic";
struct crypto_shash *fallback;
struct crypto_shash *shash_tfm = __crypto_shash_cast(tfm);
struct p8_ghash_ctx *ctx = crypto_tfm_ctx(tfm);
- if (!(alg = crypto_tfm_alg_name(tfm))) {
- printk(KERN_ERR "Failed to get algorithm name.\n");
- return -ENOENT;
- }
-
fallback = crypto_alloc_shash(alg, 0, CRYPTO_ALG_NEED_FALLBACK);
if (IS_ERR(fallback)) {
printk(KERN_ERR
@@ -78,10 +70,18 @@ static int p8_ghash_init_tfm(struct crypto_tfm *tfm)
crypto_shash_set_flags(fallback,
crypto_shash_get_flags((struct crypto_shash
*) tfm));
- ctx->fallback = fallback;
- shash_tfm->descsize = sizeof(struct p8_ghash_desc_ctx)
- + crypto_shash_descsize(fallback);
+ /* Check if the descsize defined in the algorithm is still enough. */
+ if (shash_tfm->descsize < sizeof(struct p8_ghash_desc_ctx)
+ + crypto_shash_descsize(fallback)) {
+ printk(KERN_ERR
+ "Desc size of the fallback implementation (%s) does not match the expected value: %lu vs %u\n",
+ alg,
+ shash_tfm->descsize - sizeof(struct p8_ghash_desc_ctx),
+ crypto_shash_descsize(fallback));
+ return -EINVAL;
+ }
+ ctx->fallback = fallback;
return 0;
}
@@ -113,7 +113,7 @@ static int p8_ghash_setkey(struct crypto_shash *tfm, const u8 *key,
{
struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(tfm));
- if (keylen != GHASH_KEY_LEN)
+ if (keylen != GHASH_BLOCK_SIZE)
return -EINVAL;
preempt_disable();
@@ -215,7 +215,8 @@ struct shash_alg p8_ghash_alg = {
.update = p8_ghash_update,
.final = p8_ghash_final,
.setkey = p8_ghash_setkey,
- .descsize = sizeof(struct p8_ghash_desc_ctx),
+ .descsize = sizeof(struct p8_ghash_desc_ctx)
+ + sizeof(struct ghash_desc_ctx),
.base = {
.cra_name = "ghash",
.cra_driver_name = "p8_ghash",
diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
index e44a1bfb0250..66c073fc8afc 100644
--- a/drivers/dma/at_xdmac.c
+++ b/drivers/dma/at_xdmac.c
@@ -864,8 +864,12 @@ at_xdmac_interleaved_queue_desc(struct dma_chan *chan,
* access. Hopefully we can access DDR through both ports (at least on
* SAMA5D4x), so we can use the same interface for source and dest,
* that solves the fact we don't know the direction.
+ * ERRATA: Even if useless for memory transfers, the PERID has to not
+ * match the one of another channel. If not, it could lead to spurious
+ * flag status.
*/
- u32 chan_cc = AT_XDMAC_CC_DIF(0)
+ u32 chan_cc = AT_XDMAC_CC_PERID(0x3f)
+ | AT_XDMAC_CC_DIF(0)
| AT_XDMAC_CC_SIF(0)
| AT_XDMAC_CC_MBSIZE_SIXTEEN
| AT_XDMAC_CC_TYPE_MEM_TRAN;
@@ -1042,8 +1046,12 @@ at_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
* access DDR through both ports (at least on SAMA5D4x), so we can use
* the same interface for source and dest, that solves the fact we
* don't know the direction.
+ * ERRATA: Even if useless for memory transfers, the PERID has to not
+ * match the one of another channel. If not, it could lead to spurious
+ * flag status.
*/
- u32 chan_cc = AT_XDMAC_CC_DAM_INCREMENTED_AM
+ u32 chan_cc = AT_XDMAC_CC_PERID(0x3f)
+ | AT_XDMAC_CC_DAM_INCREMENTED_AM
| AT_XDMAC_CC_SAM_INCREMENTED_AM
| AT_XDMAC_CC_DIF(0)
| AT_XDMAC_CC_SIF(0)
@@ -1144,8 +1152,12 @@ static struct at_xdmac_desc *at_xdmac_memset_create_desc(struct dma_chan *chan,
* access. Hopefully we can access DDR through both ports (at least on
* SAMA5D4x), so we can use the same interface for source and dest,
* that solves the fact we don't know the direction.
+ * ERRATA: Even if useless for memory transfers, the PERID has to not
+ * match the one of another channel. If not, it could lead to spurious
+ * flag status.
*/
- u32 chan_cc = AT_XDMAC_CC_DAM_UBS_AM
+ u32 chan_cc = AT_XDMAC_CC_PERID(0x3f)
+ | AT_XDMAC_CC_DAM_UBS_AM
| AT_XDMAC_CC_SAM_INCREMENTED_AM
| AT_XDMAC_CC_DIF(0)
| AT_XDMAC_CC_SIF(0)
@@ -1183,8 +1195,8 @@ static struct at_xdmac_desc *at_xdmac_memset_create_desc(struct dma_chan *chan,
desc->lld.mbr_cfg = chan_cc;
dev_dbg(chan2dev(chan),
- "%s: lld: mbr_da=%pad, mbr_ds=%pad, mbr_ubc=0x%08x, mbr_cfg=0x%08x\n",
- __func__, &desc->lld.mbr_da, &desc->lld.mbr_ds, desc->lld.mbr_ubc,
+ "%s: lld: mbr_da=%pad, mbr_ds=0x%08x, mbr_ubc=0x%08x, mbr_cfg=0x%08x\n",
+ __func__, &desc->lld.mbr_da, desc->lld.mbr_ds, desc->lld.mbr_ubc,
desc->lld.mbr_cfg);
return desc;
@@ -2055,7 +2067,7 @@ err_dma_unregister:
err_clk_disable:
clk_disable_unprepare(atxdmac->clk);
err_free_irq:
- free_irq(atxdmac->irq, atxdmac->dma.dev);
+ free_irq(atxdmac->irq, atxdmac);
return ret;
}
@@ -2071,7 +2083,7 @@ static int at_xdmac_remove(struct platform_device *pdev)
synchronize_irq(atxdmac->irq);
- free_irq(atxdmac->irq, atxdmac->dma.dev);
+ free_irq(atxdmac->irq, atxdmac);
for (i = 0; i < atxdmac->dma.chancnt; i++) {
struct at_xdmac_chan *atchan = &atxdmac->chan[i];
diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c
index 2bf37e68ad0f..dd184b50e5b4 100644
--- a/drivers/dma/ipu/ipu_irq.c
+++ b/drivers/dma/ipu/ipu_irq.c
@@ -286,22 +286,21 @@ static void ipu_irq_handler(struct irq_desc *desc)
raw_spin_unlock(&bank_lock);
while ((line = ffs(status))) {
struct ipu_irq_map *map;
- unsigned int irq = NO_IRQ;
+ unsigned int irq;
line--;
status &= ~(1UL << line);
raw_spin_lock(&bank_lock);
map = src2map(32 * i + line);
- if (map)
- irq = map->irq;
- raw_spin_unlock(&bank_lock);
-
if (!map) {
+ raw_spin_unlock(&bank_lock);
pr_err("IPU: Interrupt on unmapped source %u bank %d\n",
line, i);
continue;
}
+ irq = map->irq;
+ raw_spin_unlock(&bank_lock);
generic_handle_irq(irq);
}
}
diff --git a/drivers/esoc/Kconfig b/drivers/esoc/Kconfig
index 91e5c0b8b57b..ce6efd8e529c 100644
--- a/drivers/esoc/Kconfig
+++ b/drivers/esoc/Kconfig
@@ -58,4 +58,12 @@ config ESOC_MDM_DBG_ENG
by command engine to the external modem. Also allows masking
of certain notifications being sent to the external modem.
+config MDM_DBG_REQ_ENG
+ tristate "manual request engine for 4x series external modems"
+ depends on ESOC_MDM_DBG_ENG
+ help
+ Provides a user interface to handle incoming requests from
+ the external modem. Allows for debugging of IPC mechanism
+ between the external modem and the primary soc.
+
endif
diff --git a/drivers/esoc/esoc-mdm-dbg-eng.c b/drivers/esoc/esoc-mdm-dbg-eng.c
index 4d465c18a646..d4ad25123565 100644
--- a/drivers/esoc/esoc-mdm-dbg-eng.c
+++ b/drivers/esoc/esoc-mdm-dbg-eng.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -179,7 +179,165 @@ static ssize_t notifier_mask_store(struct device_driver *drv, const char *buf,
}
static DRIVER_ATTR(notifier_mask, S_IWUSR, NULL, notifier_mask_store);
-int mdm_dbg_eng_init(struct esoc_drv *esoc_drv)
+#ifdef CONFIG_MDM_DBG_REQ_ENG
+static struct esoc_clink *dbg_clink;
+/* Last recorded request from esoc */
+static enum esoc_req last_req;
+static DEFINE_SPINLOCK(req_lock);
+/*
+ * esoc_to_user: Conversion of esoc ids to user visible strings
+ * id: esoc request, command, notifier, event id
+ * str: string equivalent of the above
+ */
+struct esoc_to_user {
+ unsigned int id;
+ char str[20];
+};
+
+static struct esoc_to_user in_to_resp[] = {
+ {
+ .id = ESOC_IMG_XFER_DONE,
+ .str = "XFER_DONE",
+ },
+ {
+ .id = ESOC_BOOT_DONE,
+ .str = "BOOT_DONE",
+ },
+ {
+ .id = ESOC_BOOT_FAIL,
+ .str = "BOOT_FAIL",
+ },
+ {
+ .id = ESOC_IMG_XFER_RETRY,
+ .str = "XFER_RETRY",
+ },
+ { .id = ESOC_IMG_XFER_FAIL,
+ .str = "XFER_FAIL",
+ },
+ {
+ .id = ESOC_UPGRADE_AVAILABLE,
+ .str = "UPGRADE",
+ },
+ { .id = ESOC_DEBUG_DONE,
+ .str = "DEBUG_DONE",
+ },
+ {
+ .id = ESOC_DEBUG_FAIL,
+ .str = "DEBUG_FAIL",
+ },
+};
+
+static struct esoc_to_user req_to_str[] = {
+ {
+ .id = ESOC_REQ_IMG,
+ .str = "REQ_IMG",
+ },
+ {
+ .id = ESOC_REQ_DEBUG,
+ .str = "REQ_DEBUG",
+ },
+ {
+ .id = ESOC_REQ_SHUTDOWN,
+ .str = "REQ_SHUTDOWN",
+ },
+};
+
+static ssize_t req_eng_resp_store(struct device_driver *drv, const char *buf,
+ size_t count)
+{
+ unsigned int i;
+ const struct esoc_clink_ops *const clink_ops = dbg_clink->clink_ops;
+
+ dev_dbg(&dbg_clink->dev, "user input req eng response %s\n", buf);
+ for (i = 0; i < ARRAY_SIZE(in_to_resp); i++) {
+ size_t len1 = strlen(buf);
+ size_t len2 = strlen(in_to_resp[i].str);
+
+ if (len1 == len2 && !strcmp(buf, in_to_resp[i].str)) {
+ clink_ops->notify(in_to_resp[i].id, dbg_clink);
+ break;
+ }
+ }
+ if (i > ARRAY_SIZE(in_to_resp))
+ dev_err(&dbg_clink->dev, "Invalid resp %s, specified\n", buf);
+ return count;
+}
+
+static DRIVER_ATTR(req_eng_resp, S_IWUSR, NULL, req_eng_resp_store);
+
+static ssize_t last_esoc_req_show(struct device_driver *drv, char *buf)
+{
+ unsigned int i;
+ unsigned long flags;
+ size_t count;
+
+ spin_lock_irqsave(&req_lock, flags);
+ for (i = 0; i < ARRAY_SIZE(req_to_str); i++) {
+ if (last_req == req_to_str[i].id) {
+ count = snprintf(buf, PAGE_SIZE, "%s\n",
+ req_to_str[i].str);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&req_lock, flags);
+ return count;
+}
+static DRIVER_ATTR(last_esoc_req, S_IRUSR, last_esoc_req_show, NULL);
+
+static void esoc_handle_req(enum esoc_req req, struct esoc_eng *eng)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&req_lock, flags);
+ last_req = req;
+ spin_unlock_irqrestore(&req_lock, flags);
+}
+
+static void esoc_handle_evt(enum esoc_evt evt, struct esoc_eng *eng)
+{
+}
+
+static struct esoc_eng dbg_req_eng = {
+ .handle_clink_req = esoc_handle_req,
+ .handle_clink_evt = esoc_handle_evt,
+};
+
+int register_dbg_req_eng(struct esoc_clink *clink,
+ struct device_driver *drv)
+{
+ int ret;
+
+ dbg_clink = clink;
+ ret = driver_create_file(drv, &driver_attr_req_eng_resp);
+ if (ret)
+ return ret;
+ ret = driver_create_file(drv, &driver_attr_last_esoc_req);
+ if (ret) {
+ dev_err(&clink->dev, "Unable to create last esoc req\n");
+ goto last_req_err;
+ }
+ ret = esoc_clink_register_req_eng(clink, &dbg_req_eng);
+ if (ret) {
+ pr_err("Unable to register req eng\n");
+ goto req_eng_fail;
+ }
+ spin_lock_init(&req_lock);
+ return 0;
+last_req_err:
+ driver_remove_file(drv, &driver_attr_last_esoc_req);
+req_eng_fail:
+ driver_remove_file(drv, &driver_attr_req_eng_resp);
+ return ret;
+}
+#else
+int register_dbg_req_eng(struct esoc_clink *clink, struct device_driver *d)
+{
+ return 0;
+}
+#endif
+
+int mdm_dbg_eng_init(struct esoc_drv *esoc_drv,
+ struct esoc_clink *clink)
{
int ret;
struct device_driver *drv = &esoc_drv->driver;
@@ -194,7 +352,14 @@ int mdm_dbg_eng_init(struct esoc_drv *esoc_drv)
pr_err("Unable to create notify mask file\n");
goto notify_mask_err;
}
+ ret = register_dbg_req_eng(clink, drv);
+ if (ret) {
+ pr_err("Failed to register esoc dbg req eng\n");
+ goto dbg_req_fail;
+ }
return 0;
+dbg_req_fail:
+ driver_remove_file(drv, &driver_attr_notifier_mask);
notify_mask_err:
driver_remove_file(drv, &driver_attr_command_mask);
cmd_mask_err:
diff --git a/drivers/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c
index 0c14929a0339..9abe125f28b5 100644
--- a/drivers/esoc/esoc-mdm-drv.c
+++ b/drivers/esoc/esoc-mdm-drv.c
@@ -258,7 +258,7 @@ int esoc_ssr_probe(struct esoc_clink *esoc_clink, struct esoc_drv *drv)
ret = register_reboot_notifier(&mdm_drv->esoc_restart);
if (ret)
dev_err(&esoc_clink->dev, "register for reboot failed\n");
- ret = mdm_dbg_eng_init(drv);
+ ret = mdm_dbg_eng_init(drv, esoc_clink);
if (ret) {
debug_init_done = false;
dev_err(&esoc_clink->dev, "dbg engine failure\n");
diff --git a/drivers/esoc/esoc_client.c b/drivers/esoc/esoc_client.c
index 6dee2f37b46d..e9932ea3e964 100644
--- a/drivers/esoc/esoc_client.c
+++ b/drivers/esoc/esoc_client.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-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
@@ -47,6 +47,8 @@ struct esoc_desc *devm_register_esoc_client(struct device *dev,
for (index = 0;; index++) {
esoc_prop = kasprintf(GFP_KERNEL, "esoc-%d", index);
+ if (IS_ERR_OR_NULL(esoc_prop))
+ return ERR_PTR(-ENOMEM);
parp = of_get_property(np, esoc_prop, NULL);
if (parp == NULL) {
dev_err(dev, "esoc device not present\n");
diff --git a/drivers/esoc/mdm-dbg.h b/drivers/esoc/mdm-dbg.h
index ae31339ab152..ffba87c6f7bb 100644
--- a/drivers/esoc/mdm-dbg.h
+++ b/drivers/esoc/mdm-dbg.h
@@ -24,7 +24,8 @@ static inline bool dbg_check_notify_mask(unsigned int notify)
return false;
}
-static inline int mdm_dbg_eng_init(struct esoc_drv *drv)
+static inline int mdm_dbg_eng_init(struct esoc_drv *drv,
+ struct esoc_clink *clink)
{
return 0;
}
@@ -32,7 +33,8 @@ static inline int mdm_dbg_eng_init(struct esoc_drv *drv)
#else
extern bool dbg_check_cmd_mask(unsigned int cmd);
extern bool dbg_check_notify_mask(unsigned int notify);
-extern int mdm_dbg_eng_init(struct esoc_drv *drv);
+extern int mdm_dbg_eng_init(struct esoc_drv *drv,
+ struct esoc_clink *clink);
#endif
static inline bool mdm_dbg_stall_cmd(unsigned int cmd)
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c
index f4ea80d602f7..b9d2f76a0cf7 100644
--- a/drivers/firewire/net.c
+++ b/drivers/firewire/net.c
@@ -73,13 +73,13 @@ struct rfc2734_header {
#define fwnet_get_hdr_lf(h) (((h)->w0 & 0xc0000000) >> 30)
#define fwnet_get_hdr_ether_type(h) (((h)->w0 & 0x0000ffff))
-#define fwnet_get_hdr_dg_size(h) (((h)->w0 & 0x0fff0000) >> 16)
+#define fwnet_get_hdr_dg_size(h) ((((h)->w0 & 0x0fff0000) >> 16) + 1)
#define fwnet_get_hdr_fg_off(h) (((h)->w0 & 0x00000fff))
#define fwnet_get_hdr_dgl(h) (((h)->w1 & 0xffff0000) >> 16)
-#define fwnet_set_hdr_lf(lf) ((lf) << 30)
+#define fwnet_set_hdr_lf(lf) ((lf) << 30)
#define fwnet_set_hdr_ether_type(et) (et)
-#define fwnet_set_hdr_dg_size(dgs) ((dgs) << 16)
+#define fwnet_set_hdr_dg_size(dgs) (((dgs) - 1) << 16)
#define fwnet_set_hdr_fg_off(fgo) (fgo)
#define fwnet_set_hdr_dgl(dgl) ((dgl) << 16)
@@ -578,6 +578,9 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
int retval;
u16 ether_type;
+ if (len <= RFC2374_UNFRAG_HDR_SIZE)
+ return 0;
+
hdr.w0 = be32_to_cpu(buf[0]);
lf = fwnet_get_hdr_lf(&hdr);
if (lf == RFC2374_HDR_UNFRAG) {
@@ -602,7 +605,12 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
return fwnet_finish_incoming_packet(net, skb, source_node_id,
is_broadcast, ether_type);
}
+
/* A datagram fragment has been received, now the fun begins. */
+
+ if (len <= RFC2374_FRAG_HDR_SIZE)
+ return 0;
+
hdr.w1 = ntohl(buf[1]);
buf += 2;
len -= RFC2374_FRAG_HDR_SIZE;
@@ -614,7 +622,10 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
fg_off = fwnet_get_hdr_fg_off(&hdr);
}
datagram_label = fwnet_get_hdr_dgl(&hdr);
- dg_size = fwnet_get_hdr_dg_size(&hdr); /* ??? + 1 */
+ dg_size = fwnet_get_hdr_dg_size(&hdr);
+
+ if (fg_off + len > dg_size)
+ return 0;
spin_lock_irqsave(&dev->lock, flags);
@@ -722,6 +733,22 @@ static void fwnet_receive_packet(struct fw_card *card, struct fw_request *r,
fw_send_response(card, r, rcode);
}
+static int gasp_source_id(__be32 *p)
+{
+ return be32_to_cpu(p[0]) >> 16;
+}
+
+static u32 gasp_specifier_id(__be32 *p)
+{
+ return (be32_to_cpu(p[0]) & 0xffff) << 8 |
+ (be32_to_cpu(p[1]) & 0xff000000) >> 24;
+}
+
+static u32 gasp_version(__be32 *p)
+{
+ return be32_to_cpu(p[1]) & 0xffffff;
+}
+
static void fwnet_receive_broadcast(struct fw_iso_context *context,
u32 cycle, size_t header_length, void *header, void *data)
{
@@ -731,9 +758,6 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
__be32 *buf_ptr;
int retval;
u32 length;
- u16 source_node_id;
- u32 specifier_id;
- u32 ver;
unsigned long offset;
unsigned long flags;
@@ -750,22 +774,17 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
spin_unlock_irqrestore(&dev->lock, flags);
- specifier_id = (be32_to_cpu(buf_ptr[0]) & 0xffff) << 8
- | (be32_to_cpu(buf_ptr[1]) & 0xff000000) >> 24;
- ver = be32_to_cpu(buf_ptr[1]) & 0xffffff;
- source_node_id = be32_to_cpu(buf_ptr[0]) >> 16;
-
- if (specifier_id == IANA_SPECIFIER_ID &&
- (ver == RFC2734_SW_VERSION
+ if (length > IEEE1394_GASP_HDR_SIZE &&
+ gasp_specifier_id(buf_ptr) == IANA_SPECIFIER_ID &&
+ (gasp_version(buf_ptr) == RFC2734_SW_VERSION
#if IS_ENABLED(CONFIG_IPV6)
- || ver == RFC3146_SW_VERSION
+ || gasp_version(buf_ptr) == RFC3146_SW_VERSION
#endif
- )) {
- buf_ptr += 2;
- length -= IEEE1394_GASP_HDR_SIZE;
- fwnet_incoming_packet(dev, buf_ptr, length, source_node_id,
+ ))
+ fwnet_incoming_packet(dev, buf_ptr + 2,
+ length - IEEE1394_GASP_HDR_SIZE,
+ gasp_source_id(buf_ptr),
context->card->generation, true);
- }
packet.payload_length = dev->rcv_buffer_size;
packet.interrupt = 1;
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 9411b1ab2c76..68489ef7422f 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -173,6 +173,9 @@ config QCOM_SCM_64
def_bool y
depends on QCOM_SCM && ARM64
+config HAVE_ARM_SMCCC
+ bool
+
source "drivers/firmware/broadcom/Kconfig"
source "drivers/firmware/google/Kconfig"
source "drivers/firmware/efi/Kconfig"
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index ec379a4164cc..f292917b00e7 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -18,3 +18,6 @@ obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o
obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o
obj-$(CONFIG_EFI_STUB) += libstub/
obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o
+
+arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o
+obj-$(CONFIG_ARM64) += $(arm-obj-y)
diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
new file mode 100644
index 000000000000..9e15d571b53c
--- /dev/null
+++ b/drivers/firmware/efi/arm-init.c
@@ -0,0 +1,209 @@
+/*
+ * Extensible Firmware Interface
+ *
+ * Based on Extensible Firmware Interface Specification version 2.4
+ *
+ * Copyright (C) 2013 - 2015 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/efi.h>
+#include <linux/init.h>
+#include <linux/memblock.h>
+#include <linux/mm_types.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+
+#include <asm/efi.h>
+
+struct efi_memory_map memmap;
+
+u64 efi_system_table;
+
+static int __init is_normal_ram(efi_memory_desc_t *md)
+{
+ if (md->attribute & EFI_MEMORY_WB)
+ return 1;
+ return 0;
+}
+
+/*
+ * Translate a EFI virtual address into a physical address: this is necessary,
+ * as some data members of the EFI system table are virtually remapped after
+ * SetVirtualAddressMap() has been called.
+ */
+static phys_addr_t efi_to_phys(unsigned long addr)
+{
+ efi_memory_desc_t *md;
+
+ for_each_efi_memory_desc(&memmap, md) {
+ if (!(md->attribute & EFI_MEMORY_RUNTIME))
+ continue;
+ if (md->virt_addr == 0)
+ /* no virtual mapping has been installed by the stub */
+ break;
+ if (md->virt_addr <= addr &&
+ (addr - md->virt_addr) < (md->num_pages << EFI_PAGE_SHIFT))
+ return md->phys_addr + addr - md->virt_addr;
+ }
+ return addr;
+}
+
+static int __init uefi_init(void)
+{
+ efi_char16_t *c16;
+ void *config_tables;
+ size_t table_size;
+ char vendor[100] = "unknown";
+ int i, retval;
+
+ efi.systab = early_memremap(efi_system_table,
+ sizeof(efi_system_table_t));
+ if (efi.systab == NULL) {
+ pr_warn("Unable to map EFI system table.\n");
+ return -ENOMEM;
+ }
+
+ set_bit(EFI_BOOT, &efi.flags);
+ if (IS_ENABLED(CONFIG_64BIT))
+ set_bit(EFI_64BIT, &efi.flags);
+
+ /*
+ * Verify the EFI Table
+ */
+ if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
+ pr_err("System table signature incorrect\n");
+ retval = -EINVAL;
+ goto out;
+ }
+ if ((efi.systab->hdr.revision >> 16) < 2)
+ pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n",
+ efi.systab->hdr.revision >> 16,
+ efi.systab->hdr.revision & 0xffff);
+
+ /* Show what we know for posterity */
+ c16 = early_memremap(efi_to_phys(efi.systab->fw_vendor),
+ sizeof(vendor) * sizeof(efi_char16_t));
+ if (c16) {
+ for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i)
+ vendor[i] = c16[i];
+ vendor[i] = '\0';
+ early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t));
+ }
+
+ pr_info("EFI v%u.%.02u by %s\n",
+ efi.systab->hdr.revision >> 16,
+ efi.systab->hdr.revision & 0xffff, vendor);
+
+ table_size = sizeof(efi_config_table_64_t) * efi.systab->nr_tables;
+ config_tables = early_memremap(efi_to_phys(efi.systab->tables),
+ table_size);
+ if (config_tables == NULL) {
+ pr_warn("Unable to map EFI config table array.\n");
+ retval = -ENOMEM;
+ goto out;
+ }
+ retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables,
+ sizeof(efi_config_table_t), NULL);
+
+ early_memunmap(config_tables, table_size);
+out:
+ early_memunmap(efi.systab, sizeof(efi_system_table_t));
+ return retval;
+}
+
+/*
+ * Return true for RAM regions we want to permanently reserve.
+ */
+static __init int is_reserve_region(efi_memory_desc_t *md)
+{
+ switch (md->type) {
+ case EFI_LOADER_CODE:
+ case EFI_LOADER_DATA:
+ case EFI_BOOT_SERVICES_CODE:
+ case EFI_BOOT_SERVICES_DATA:
+ case EFI_CONVENTIONAL_MEMORY:
+ case EFI_PERSISTENT_MEMORY:
+ return 0;
+ default:
+ break;
+ }
+ return is_normal_ram(md);
+}
+
+static __init void reserve_regions(void)
+{
+ efi_memory_desc_t *md;
+ u64 paddr, npages, size;
+
+ if (efi_enabled(EFI_DBG))
+ pr_info("Processing EFI memory map:\n");
+
+ for_each_efi_memory_desc(&memmap, md) {
+ paddr = md->phys_addr;
+ npages = md->num_pages;
+
+ if (efi_enabled(EFI_DBG)) {
+ char buf[64];
+
+ pr_info(" 0x%012llx-0x%012llx %s",
+ paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1,
+ efi_md_typeattr_format(buf, sizeof(buf), md));
+ }
+
+ memrange_efi_to_native(&paddr, &npages);
+ size = npages << PAGE_SHIFT;
+
+ if (is_normal_ram(md))
+ early_init_dt_add_memory_arch(paddr, size);
+
+ if (is_reserve_region(md)) {
+ memblock_mark_nomap(paddr, size);
+ if (efi_enabled(EFI_DBG))
+ pr_cont("*");
+ }
+
+ if (efi_enabled(EFI_DBG))
+ pr_cont("\n");
+ }
+
+ set_bit(EFI_MEMMAP, &efi.flags);
+}
+
+void __init efi_init(void)
+{
+ struct efi_fdt_params params;
+
+ /* Grab UEFI information placed in FDT by stub */
+ if (!efi_get_fdt_params(&params))
+ return;
+
+ efi_system_table = params.system_table;
+
+ memmap.phys_map = params.mmap;
+ memmap.map = early_memremap(params.mmap, params.mmap_size);
+ if (memmap.map == NULL) {
+ /*
+ * If we are booting via UEFI, the UEFI memory map is the only
+ * description of memory we have, so there is little point in
+ * proceeding if we cannot access it.
+ */
+ panic("Unable to map EFI memory map.\n");
+ }
+ memmap.map_end = memmap.map + params.mmap_size;
+ memmap.desc_size = params.desc_size;
+ memmap.desc_version = params.desc_ver;
+
+ if (uefi_init() < 0)
+ return;
+
+ reserve_regions();
+ early_memunmap(memmap.map, params.mmap_size);
+ memblock_mark_nomap(params.mmap & PAGE_MASK,
+ PAGE_ALIGN(params.mmap_size +
+ (params.mmap & ~PAGE_MASK)));
+}
diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c
new file mode 100644
index 000000000000..6ae21e41a429
--- /dev/null
+++ b/drivers/firmware/efi/arm-runtime.c
@@ -0,0 +1,135 @@
+/*
+ * Extensible Firmware Interface
+ *
+ * Based on Extensible Firmware Interface Specification version 2.4
+ *
+ * Copyright (C) 2013, 2014 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/efi.h>
+#include <linux/io.h>
+#include <linux/memblock.h>
+#include <linux/mm_types.h>
+#include <linux/preempt.h>
+#include <linux/rbtree.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm/cacheflush.h>
+#include <asm/efi.h>
+#include <asm/mmu.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+
+extern u64 efi_system_table;
+
+static struct mm_struct efi_mm = {
+ .mm_rb = RB_ROOT,
+ .mm_users = ATOMIC_INIT(2),
+ .mm_count = ATOMIC_INIT(1),
+ .mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem),
+ .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
+ .mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
+};
+
+static bool __init efi_virtmap_init(void)
+{
+ efi_memory_desc_t *md;
+
+ efi_mm.pgd = pgd_alloc(&efi_mm);
+ init_new_context(NULL, &efi_mm);
+
+ for_each_efi_memory_desc(&memmap, md) {
+ phys_addr_t phys = md->phys_addr;
+ int ret;
+
+ if (!(md->attribute & EFI_MEMORY_RUNTIME))
+ continue;
+ if (md->virt_addr == 0)
+ return false;
+
+ ret = efi_create_mapping(&efi_mm, md);
+ if (!ret) {
+ pr_info(" EFI remap %pa => %p\n",
+ &phys, (void *)(unsigned long)md->virt_addr);
+ } else {
+ pr_warn(" EFI remap %pa: failed to create mapping (%d)\n",
+ &phys, ret);
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ * Enable the UEFI Runtime Services if all prerequisites are in place, i.e.,
+ * non-early mapping of the UEFI system table and virtual mappings for all
+ * EFI_MEMORY_RUNTIME regions.
+ */
+static int __init arm_enable_runtime_services(void)
+{
+ u64 mapsize;
+
+ if (!efi_enabled(EFI_BOOT)) {
+ pr_info("EFI services will not be available.\n");
+ return 0;
+ }
+
+ if (efi_runtime_disabled()) {
+ pr_info("EFI runtime services will be disabled.\n");
+ return 0;
+ }
+
+ pr_info("Remapping and enabling EFI services.\n");
+
+ mapsize = memmap.map_end - memmap.map;
+ memmap.map = (__force void *)ioremap_cache(memmap.phys_map,
+ mapsize);
+ if (!memmap.map) {
+ pr_err("Failed to remap EFI memory map\n");
+ return -ENOMEM;
+ }
+ memmap.map_end = memmap.map + mapsize;
+ efi.memmap = &memmap;
+
+ efi.systab = (__force void *)ioremap_cache(efi_system_table,
+ sizeof(efi_system_table_t));
+ if (!efi.systab) {
+ pr_err("Failed to remap EFI System Table\n");
+ return -ENOMEM;
+ }
+ set_bit(EFI_SYSTEM_TABLES, &efi.flags);
+
+ if (!efi_virtmap_init()) {
+ pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n");
+ return -ENOMEM;
+ }
+
+ /* Set up runtime services function pointers */
+ efi_native_runtime_setup();
+ set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
+
+ efi.runtime_version = efi.systab->hdr.revision;
+
+ return 0;
+}
+early_initcall(arm_enable_runtime_services);
+
+void efi_virtmap_load(void)
+{
+ preempt_disable();
+ efi_set_pgd(&efi_mm);
+}
+
+void efi_virtmap_unload(void)
+{
+ efi_set_pgd(current->active_mm);
+ preempt_enable();
+}
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 3b52677f459a..c51f3b2fe3c0 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -25,6 +25,8 @@
#include <linux/io.h>
#include <linux/platform_device.h>
+#include <asm/early_ioremap.h>
+
struct efi __read_mostly efi = {
.mps = EFI_INVALID_TABLE_ADDR,
.acpi = EFI_INVALID_TABLE_ADDR,
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index d24f35d74b27..af0060d6a22a 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -13,6 +13,8 @@
#define pr_fmt(fmt) "psci: " fmt
+#include <linux/arm-smccc.h>
+#include <linux/cpuidle.h>
#include <linux/errno.h>
#include <linux/linkage.h>
#include <linux/of.h>
@@ -20,10 +22,12 @@
#include <linux/printk.h>
#include <linux/psci.h>
#include <linux/reboot.h>
+#include <linux/slab.h>
#include <linux/suspend.h>
#include <uapi/linux/psci.h>
+#include <asm/cpuidle.h>
#include <asm/cputype.h>
#include <asm/system_misc.h>
#include <asm/smp_plat.h>
@@ -58,8 +62,6 @@ struct psci_operations psci_ops;
typedef unsigned long (psci_fn)(unsigned long, unsigned long,
unsigned long, unsigned long);
-asmlinkage psci_fn __invoke_psci_fn_hvc;
-asmlinkage psci_fn __invoke_psci_fn_smc;
static psci_fn *invoke_psci_fn;
enum psci_function {
@@ -225,6 +227,119 @@ static int __init psci_features(u32 psci_func_id)
psci_func_id, 0, 0);
}
+#ifdef CONFIG_CPU_IDLE
+static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
+
+static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
+{
+ int i, ret, count = 0;
+ u32 *psci_states;
+ struct device_node *state_node;
+
+ /*
+ * If the PSCI cpu_suspend function hook has not been initialized
+ * idle states must not be enabled, so bail out
+ */
+ if (!psci_ops.cpu_suspend)
+ return -EOPNOTSUPP;
+
+ /* Count idle states */
+ while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
+ count))) {
+ count++;
+ of_node_put(state_node);
+ }
+
+ if (!count)
+ return -ENODEV;
+
+ psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL);
+ if (!psci_states)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ u32 state;
+
+ state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
+
+ ret = of_property_read_u32(state_node,
+ "arm,psci-suspend-param",
+ &state);
+ if (ret) {
+ pr_warn(" * %s missing arm,psci-suspend-param property\n",
+ state_node->full_name);
+ of_node_put(state_node);
+ goto free_mem;
+ }
+
+ of_node_put(state_node);
+ pr_debug("psci-power-state %#x index %d\n", state, i);
+ if (!psci_power_state_is_valid(state)) {
+ pr_warn("Invalid PSCI power state %#x\n", state);
+ ret = -EINVAL;
+ goto free_mem;
+ }
+ psci_states[i] = state;
+ }
+ /* Idle states parsed correctly, initialize per-cpu pointer */
+ per_cpu(psci_power_state, cpu) = psci_states;
+ return 0;
+
+free_mem:
+ kfree(psci_states);
+ return ret;
+}
+
+int psci_cpu_init_idle(unsigned int cpu)
+{
+ struct device_node *cpu_node;
+ int ret;
+
+ cpu_node = of_get_cpu_node(cpu, NULL);
+ if (!cpu_node)
+ return -ENODEV;
+
+ ret = psci_dt_cpu_init_idle(cpu_node, cpu);
+
+ of_node_put(cpu_node);
+
+ return ret;
+}
+
+static int psci_suspend_finisher(unsigned long state_id)
+{
+ return psci_ops.cpu_suspend(state_id, virt_to_phys(cpu_resume));
+}
+
+int psci_cpu_suspend_enter(unsigned long state_id)
+{
+ int ret;
+ /*
+ * idle state_id 0 corresponds to wfi, should never be called
+ * from the cpu_suspend operations
+ */
+ if (WARN_ON_ONCE(!state_id))
+ return -EINVAL;
+
+ if (!psci_power_state_loses_context(state_id))
+ ret = psci_ops.cpu_suspend(state_id, 0);
+ else
+ ret = cpu_suspend(state_id, psci_suspend_finisher);
+
+ return ret;
+}
+
+/* ARM specific CPU idle operations */
+#ifdef CONFIG_ARM
+static struct cpuidle_ops psci_cpuidle_ops __initdata = {
+ .suspend = psci_cpu_suspend_enter,
+ .init = psci_dt_cpu_init_idle,
+};
+
+CPUIDLE_METHOD_OF_DECLARE(psci, "arm,psci", &psci_cpuidle_ops);
+#endif
+#endif
+
static int psci_system_suspend(unsigned long unused)
{
return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND),
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
index 48ef368347ab..9e02cb6afb0b 100644
--- a/drivers/gpio/gpio-mpc8xxx.c
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -329,7 +329,7 @@ static int mpc8xxx_gpio_irq_map(struct irq_domain *h, unsigned int irq,
irq_hw_number_t hwirq)
{
irq_set_chip_data(irq, h->host_data);
- irq_set_chip_and_handler(irq, &mpc8xxx_irq_chip, handle_level_irq);
+ irq_set_chip_and_handler(irq, &mpc8xxx_irq_chip, handle_edge_irq);
return 0;
}
diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c
index 990fa9023e22..3b6bce0518ab 100644
--- a/drivers/gpio/gpio-sa1100.c
+++ b/drivers/gpio/gpio-sa1100.c
@@ -155,7 +155,7 @@ static int sa1100_gpio_irqdomain_map(struct irq_domain *d,
{
irq_set_chip_and_handler(irq, &sa1100_gpio_irq_chip,
handle_edge_irq);
- irq_set_noprobe(irq);
+ irq_set_probe(irq);
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index ff5566c69f7d..e8e962f7b5cb 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -532,6 +532,7 @@ struct amdgpu_bo {
u64 metadata_flags;
void *metadata;
u32 metadata_size;
+ unsigned prime_shared_count;
/* list of all virtual address to which this bo
* is associated to
*/
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
index f82a2dd83874..3c7a7235988d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
@@ -117,7 +117,7 @@ static int amdgpu_bo_list_set(struct amdgpu_device *adev,
entry->allowed_domains = AMDGPU_GEM_DOMAIN_GTT;
}
entry->tv.bo = &entry->robj->tbo;
- entry->tv.shared = true;
+ entry->tv.shared = !entry->robj->prime_shared_count;
if (entry->prefered_domains == AMDGPU_GEM_DOMAIN_GDS)
gds_obj = entry->robj;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c
index fe36caf1b7d7..14f57d9915e3 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c
@@ -113,24 +113,26 @@ void amdgpu_dpm_print_ps_status(struct amdgpu_device *adev,
printk("\n");
}
+
u32 amdgpu_dpm_get_vblank_time(struct amdgpu_device *adev)
{
struct drm_device *dev = adev->ddev;
struct drm_crtc *crtc;
struct amdgpu_crtc *amdgpu_crtc;
- u32 line_time_us, vblank_lines;
+ u32 vblank_in_pixels;
u32 vblank_time_us = 0xffffffff; /* if the displays are off, vblank time is max */
if (adev->mode_info.num_crtc && adev->mode_info.mode_config_initialized) {
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
amdgpu_crtc = to_amdgpu_crtc(crtc);
if (crtc->enabled && amdgpu_crtc->enabled && amdgpu_crtc->hw_mode.clock) {
- line_time_us = (amdgpu_crtc->hw_mode.crtc_htotal * 1000) /
- amdgpu_crtc->hw_mode.clock;
- vblank_lines = amdgpu_crtc->hw_mode.crtc_vblank_end -
+ vblank_in_pixels =
+ amdgpu_crtc->hw_mode.crtc_htotal *
+ (amdgpu_crtc->hw_mode.crtc_vblank_end -
amdgpu_crtc->hw_mode.crtc_vdisplay +
- (amdgpu_crtc->v_border * 2);
- vblank_time_us = vblank_lines * line_time_us;
+ (amdgpu_crtc->v_border * 2));
+
+ vblank_time_us = vblank_in_pixels * 1000 / amdgpu_crtc->hw_mode.clock;
break;
}
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
index 4488e82f87b0..a5c824078472 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
@@ -227,7 +227,7 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
type = AMD_IP_BLOCK_TYPE_UVD;
ring_mask = adev->uvd.ring.ready ? 1 : 0;
ib_start_alignment = AMDGPU_GPU_PAGE_SIZE;
- ib_size_alignment = 8;
+ ib_size_alignment = 16;
break;
case AMDGPU_HW_IP_VCE:
type = AMD_IP_BLOCK_TYPE_VCE;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c
index 59f735a933a9..e6a7d30c3747 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c
@@ -77,20 +77,36 @@ struct drm_gem_object *amdgpu_gem_prime_import_sg_table(struct drm_device *dev,
list_add_tail(&bo->list, &adev->gem.objects);
mutex_unlock(&adev->gem.mutex);
+ bo->prime_shared_count = 1;
return &bo->gem_base;
}
int amdgpu_gem_prime_pin(struct drm_gem_object *obj)
{
struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj);
- int ret = 0;
+ long ret = 0;
ret = amdgpu_bo_reserve(bo, false);
if (unlikely(ret != 0))
return ret;
+ /*
+ * Wait for all shared fences to complete before we switch to future
+ * use of exclusive fence on this prime shared bo.
+ */
+ ret = reservation_object_wait_timeout_rcu(bo->tbo.resv, true, false,
+ MAX_SCHEDULE_TIMEOUT);
+ if (unlikely(ret < 0)) {
+ DRM_DEBUG_PRIME("Fence wait failed: %li\n", ret);
+ amdgpu_bo_unreserve(bo);
+ return ret;
+ }
+
/* pin buffer into GTT */
ret = amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_GTT, NULL);
+ if (likely(ret == 0))
+ bo->prime_shared_count++;
+
amdgpu_bo_unreserve(bo);
return ret;
}
@@ -105,6 +121,8 @@ void amdgpu_gem_prime_unpin(struct drm_gem_object *obj)
return;
amdgpu_bo_unpin(bo);
+ if (bo->prime_shared_count)
+ bo->prime_shared_count--;
amdgpu_bo_unreserve(bo);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_dp.c b/drivers/gpu/drm/amd/amdgpu/atombios_dp.c
index 21aacc1f45c1..7f85c2c1d681 100644
--- a/drivers/gpu/drm/amd/amdgpu/atombios_dp.c
+++ b/drivers/gpu/drm/amd/amdgpu/atombios_dp.c
@@ -265,15 +265,27 @@ static int amdgpu_atombios_dp_get_dp_link_config(struct drm_connector *connector
unsigned max_lane_num = drm_dp_max_lane_count(dpcd);
unsigned lane_num, i, max_pix_clock;
- for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
- for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
- max_pix_clock = (lane_num * link_rates[i] * 8) / bpp;
+ if (amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) ==
+ ENCODER_OBJECT_ID_NUTMEG) {
+ for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
+ max_pix_clock = (lane_num * 270000 * 8) / bpp;
if (max_pix_clock >= pix_clock) {
*dp_lanes = lane_num;
- *dp_rate = link_rates[i];
+ *dp_rate = 270000;
return 0;
}
}
+ } else {
+ for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
+ for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
+ max_pix_clock = (lane_num * link_rates[i] * 8) / bpp;
+ if (max_pix_clock >= pix_clock) {
+ *dp_lanes = lane_num;
+ *dp_rate = link_rates[i];
+ return 0;
+ }
+ }
+ }
}
return -EINVAL;
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
index 4dcc8fba5792..5b261adb4b69 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
@@ -419,16 +419,6 @@ static void dce_v10_0_hpd_init(struct amdgpu_device *adev)
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
- if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
- connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
- /* don't try to enable hpd on eDP or LVDS avoid breaking the
- * aux dp channel on imac and help (but not completely fix)
- * https://bugzilla.redhat.com/show_bug.cgi?id=726143
- * also avoid interrupt storms during dpms.
- */
- continue;
- }
-
switch (amdgpu_connector->hpd.hpd) {
case AMDGPU_HPD_1:
idx = 0;
@@ -452,6 +442,19 @@ static void dce_v10_0_hpd_init(struct amdgpu_device *adev)
continue;
}
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
+ connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
+ /* don't try to enable hpd on eDP or LVDS avoid breaking the
+ * aux dp channel on imac and help (but not completely fix)
+ * https://bugzilla.redhat.com/show_bug.cgi?id=726143
+ * also avoid interrupt storms during dpms.
+ */
+ tmp = RREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx]);
+ tmp = REG_SET_FIELD(tmp, DC_HPD_INT_CONTROL, DC_HPD_INT_EN, 0);
+ WREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx], tmp);
+ continue;
+ }
+
tmp = RREG32(mmDC_HPD_CONTROL + hpd_offsets[idx]);
tmp = REG_SET_FIELD(tmp, DC_HPD_CONTROL, DC_HPD_EN, 1);
WREG32(mmDC_HPD_CONTROL + hpd_offsets[idx], tmp);
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
index 8f1e51128b33..c161eeda417b 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
@@ -409,16 +409,6 @@ static void dce_v11_0_hpd_init(struct amdgpu_device *adev)
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
- if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
- connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
- /* don't try to enable hpd on eDP or LVDS avoid breaking the
- * aux dp channel on imac and help (but not completely fix)
- * https://bugzilla.redhat.com/show_bug.cgi?id=726143
- * also avoid interrupt storms during dpms.
- */
- continue;
- }
-
switch (amdgpu_connector->hpd.hpd) {
case AMDGPU_HPD_1:
idx = 0;
@@ -442,6 +432,19 @@ static void dce_v11_0_hpd_init(struct amdgpu_device *adev)
continue;
}
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
+ connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
+ /* don't try to enable hpd on eDP or LVDS avoid breaking the
+ * aux dp channel on imac and help (but not completely fix)
+ * https://bugzilla.redhat.com/show_bug.cgi?id=726143
+ * also avoid interrupt storms during dpms.
+ */
+ tmp = RREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx]);
+ tmp = REG_SET_FIELD(tmp, DC_HPD_INT_CONTROL, DC_HPD_INT_EN, 0);
+ WREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx], tmp);
+ continue;
+ }
+
tmp = RREG32(mmDC_HPD_CONTROL + hpd_offsets[idx]);
tmp = REG_SET_FIELD(tmp, DC_HPD_CONTROL, DC_HPD_EN, 1);
WREG32(mmDC_HPD_CONTROL + hpd_offsets[idx], tmp);
@@ -3030,6 +3033,7 @@ static int dce_v11_0_sw_fini(void *handle)
dce_v11_0_afmt_fini(adev);
+ drm_mode_config_cleanup(adev->ddev);
adev->mode_info.mode_config_initialized = false;
return 0;
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
index 42d954dc436d..9b4dcf76ce6c 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
@@ -392,15 +392,6 @@ static void dce_v8_0_hpd_init(struct amdgpu_device *adev)
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
- if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
- connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
- /* don't try to enable hpd on eDP or LVDS avoid breaking the
- * aux dp channel on imac and help (but not completely fix)
- * https://bugzilla.redhat.com/show_bug.cgi?id=726143
- * also avoid interrupt storms during dpms.
- */
- continue;
- }
switch (amdgpu_connector->hpd.hpd) {
case AMDGPU_HPD_1:
WREG32(mmDC_HPD1_CONTROL, tmp);
@@ -423,6 +414,45 @@ static void dce_v8_0_hpd_init(struct amdgpu_device *adev)
default:
break;
}
+
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
+ connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
+ /* don't try to enable hpd on eDP or LVDS avoid breaking the
+ * aux dp channel on imac and help (but not completely fix)
+ * https://bugzilla.redhat.com/show_bug.cgi?id=726143
+ * also avoid interrupt storms during dpms.
+ */
+ u32 dc_hpd_int_cntl_reg, dc_hpd_int_cntl;
+
+ switch (amdgpu_connector->hpd.hpd) {
+ case AMDGPU_HPD_1:
+ dc_hpd_int_cntl_reg = mmDC_HPD1_INT_CONTROL;
+ break;
+ case AMDGPU_HPD_2:
+ dc_hpd_int_cntl_reg = mmDC_HPD2_INT_CONTROL;
+ break;
+ case AMDGPU_HPD_3:
+ dc_hpd_int_cntl_reg = mmDC_HPD3_INT_CONTROL;
+ break;
+ case AMDGPU_HPD_4:
+ dc_hpd_int_cntl_reg = mmDC_HPD4_INT_CONTROL;
+ break;
+ case AMDGPU_HPD_5:
+ dc_hpd_int_cntl_reg = mmDC_HPD5_INT_CONTROL;
+ break;
+ case AMDGPU_HPD_6:
+ dc_hpd_int_cntl_reg = mmDC_HPD6_INT_CONTROL;
+ break;
+ default:
+ continue;
+ }
+
+ dc_hpd_int_cntl = RREG32(dc_hpd_int_cntl_reg);
+ dc_hpd_int_cntl &= ~DC_HPD1_INT_CONTROL__DC_HPD1_INT_EN_MASK;
+ WREG32(dc_hpd_int_cntl_reg, dc_hpd_int_cntl);
+ continue;
+ }
+
dce_v8_0_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd);
amdgpu_irq_get(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
}
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
index 59d1269626b1..e231176cb66b 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
@@ -316,19 +316,19 @@ atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
u32 *coeff_tab = heo_upscaling_ycoef;
u32 max_memsize;
- if (state->crtc_w < state->src_w)
+ if (state->crtc_h < state->src_h)
coeff_tab = heo_downscaling_ycoef;
for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++)
atmel_hlcdc_layer_update_cfg(&plane->layer,
33 + i,
0xffffffff,
coeff_tab[i]);
- factor = ((8 * 256 * state->src_w) - (256 * 4)) /
- state->crtc_w;
+ factor = ((8 * 256 * state->src_h) - (256 * 4)) /
+ state->crtc_h;
factor++;
- max_memsize = ((factor * state->crtc_w) + (256 * 4)) /
+ max_memsize = ((factor * state->crtc_h) + (256 * 4)) /
2048;
- if (max_memsize > state->src_w)
+ if (max_memsize > state->src_h)
factor--;
factor_reg |= (factor << 16) | 0x80000000;
}
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 2485fb652716..7cb2815e815e 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -909,6 +909,7 @@ static void drm_dp_destroy_port(struct kref *kref)
/* no need to clean up vcpi
* as if we have no connector we never setup a vcpi */
drm_dp_port_teardown_pdt(port, port->pdt);
+ port->pdt = DP_PEER_DEVICE_NONE;
}
kfree(port);
}
@@ -1154,7 +1155,9 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
drm_dp_put_port(port);
goto out;
}
- if (port->port_num >= DP_MST_LOGICAL_PORT_0) {
+ if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
+ port->pdt == DP_PEER_DEVICE_SST_SINK) &&
+ port->port_num >= DP_MST_LOGICAL_PORT_0) {
port->cached_edid = drm_get_edid(port->connector, &port->aux.ddc);
drm_mode_connector_set_tile_property(port->connector);
}
@@ -2872,6 +2875,7 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)
mgr->cbs->destroy_connector(mgr, port->connector);
drm_dp_port_teardown_pdt(port, port->pdt);
+ port->pdt = DP_PEER_DEVICE_NONE;
if (!port->input && port->vcpi.vcpi > 0) {
drm_dp_mst_reset_vcpi_slots(mgr, port);
diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c
index 57676f8d7ecf..a6289752be16 100644
--- a/drivers/gpu/drm/drm_ioc32.c
+++ b/drivers/gpu/drm/drm_ioc32.c
@@ -1015,6 +1015,7 @@ static int compat_drm_wait_vblank(struct file *file, unsigned int cmd,
return 0;
}
+#if defined(CONFIG_X86) || defined(CONFIG_IA64)
typedef struct drm_mode_fb_cmd232 {
u32 fb_id;
u32 width;
@@ -1071,6 +1072,7 @@ static int compat_drm_mode_addfb2(struct file *file, unsigned int cmd,
return 0;
}
+#endif
static drm_ioctl_compat_t *drm_compat_ioctls[] = {
[DRM_IOCTL_NR(DRM_IOCTL_VERSION32)] = compat_drm_version,
@@ -1104,7 +1106,9 @@ static drm_ioctl_compat_t *drm_compat_ioctls[] = {
[DRM_IOCTL_NR(DRM_IOCTL_UPDATE_DRAW32)] = compat_drm_update_draw,
#endif
[DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK32)] = compat_drm_wait_vblank,
+#if defined(CONFIG_X86) || defined(CONFIG_IA64)
[DRM_IOCTL_NR(DRM_IOCTL_MODE_ADDFB232)] = compat_drm_mode_addfb2,
+#endif
};
/**
diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c
index 9f935f55d74c..968b31f39884 100644
--- a/drivers/gpu/drm/drm_prime.c
+++ b/drivers/gpu/drm/drm_prime.c
@@ -339,14 +339,17 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = {
* using the PRIME helpers.
*/
struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
- struct drm_gem_object *obj, int flags)
+ struct drm_gem_object *obj,
+ int flags)
{
- DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
-
- exp_info.ops = &drm_gem_prime_dmabuf_ops;
- exp_info.size = obj->size;
- exp_info.flags = flags;
- exp_info.priv = obj;
+ struct dma_buf_export_info exp_info = {
+ .exp_name = KBUILD_MODNAME, /* white lie for debug */
+ .owner = dev->driver->fops->owner,
+ .ops = &drm_gem_prime_dmabuf_ops,
+ .size = obj->size,
+ .flags = flags,
+ .priv = obj,
+ };
if (dev->driver->gem_prime_res_obj)
exp_info.resv = dev->driver->gem_prime_res_obj(obj);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
index 7f55ba6771c6..011211e4167d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_core.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
@@ -101,7 +101,7 @@ int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
return 0;
err:
- list_for_each_entry_reverse(subdrv, &subdrv->list, list) {
+ list_for_each_entry_continue_reverse(subdrv, &exynos_drm_subdrv_list, list) {
if (subdrv->close)
subdrv->close(dev, subdrv->dev, file);
}
diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c
index 17cea400ae32..d3de377dc857 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_dp.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c
@@ -220,7 +220,7 @@ i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter)
* FIXME: This is the old dp aux helper, gma500 is the last driver that needs to
* be ported over to the new helper code in drm_dp_helper.c like i915 or radeon.
*/
-static int __deprecated
+static int
i2c_dp_aux_add_bus(struct i2c_adapter *adapter)
{
int error;
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index d400d6773bbb..fb9f647bb5cd 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2150,21 +2150,19 @@ struct drm_i915_gem_object {
/** Record of address bit 17 of each page at last unbind. */
unsigned long *bit_17;
- union {
- /** for phy allocated objects */
- struct drm_dma_handle *phys_handle;
-
- struct i915_gem_userptr {
- uintptr_t ptr;
- unsigned read_only :1;
- unsigned workers :4;
+ struct i915_gem_userptr {
+ uintptr_t ptr;
+ unsigned read_only :1;
+ unsigned workers :4;
#define I915_GEM_USERPTR_MAX_WORKERS 15
- struct i915_mm_struct *mm;
- struct i915_mmu_object *mmu_object;
- struct work_struct *work;
- } userptr;
- };
+ struct i915_mm_struct *mm;
+ struct i915_mmu_object *mmu_object;
+ struct work_struct *work;
+ } userptr;
+
+ /** for phys allocated objects */
+ struct drm_dma_handle *phys_handle;
};
#define to_intel_bo(x) container_of(x, struct drm_i915_gem_object, base)
diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
index 87e919a06b27..5d2323a40c25 100644
--- a/drivers/gpu/drm/i915/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
@@ -108,17 +108,28 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
pci_read_config_dword(dev->pdev, 0x5c, &base);
base &= ~((1<<20) - 1);
} else if (IS_I865G(dev)) {
+ u32 tseg_size = 0;
u16 toud = 0;
+ u8 tmp;
+
+ pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0),
+ I845_ESMRAMC, &tmp);
+
+ if (tmp & TSEG_ENABLE) {
+ switch (tmp & I845_TSEG_SIZE_MASK) {
+ case I845_TSEG_SIZE_512K:
+ tseg_size = KB(512);
+ break;
+ case I845_TSEG_SIZE_1M:
+ tseg_size = MB(1);
+ break;
+ }
+ }
- /*
- * FIXME is the graphics stolen memory region
- * always at TOUD? Ie. is it always the last
- * one to be allocated by the BIOS?
- */
pci_bus_read_config_word(dev->pdev->bus, PCI_DEVFN(0, 0),
I865_TOUD, &toud);
- base = toud << 16;
+ base = (toud << 16) + tseg_size;
} else if (IS_I85X(dev)) {
u32 tseg_size = 0;
u32 tom;
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index a3254c3bcc7c..909d1d71d130 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -2950,13 +2950,13 @@ u32 intel_fb_stride_alignment(struct drm_device *dev, uint64_t fb_modifier,
}
}
-unsigned long intel_plane_obj_offset(struct intel_plane *intel_plane,
- struct drm_i915_gem_object *obj,
- unsigned int plane)
+u32 intel_plane_obj_offset(struct intel_plane *intel_plane,
+ struct drm_i915_gem_object *obj,
+ unsigned int plane)
{
const struct i915_ggtt_view *view = &i915_ggtt_view_normal;
struct i915_vma *vma;
- unsigned char *offset;
+ u64 offset;
if (intel_rotation_90_or_270(intel_plane->base.state->rotation))
view = &i915_ggtt_view_rotated;
@@ -2966,14 +2966,16 @@ unsigned long intel_plane_obj_offset(struct intel_plane *intel_plane,
view->type))
return -1;
- offset = (unsigned char *)vma->node.start;
+ offset = vma->node.start;
if (plane == 1) {
offset += vma->ggtt_view.rotation_info.uv_start_page *
PAGE_SIZE;
}
- return (unsigned long)offset;
+ WARN_ON(upper_32_bits(offset));
+
+ return lower_32_bits(offset);
}
static void skl_detach_scaler(struct intel_crtc *intel_crtc, int id)
@@ -3099,7 +3101,7 @@ static void skylake_update_primary_plane(struct drm_crtc *crtc,
u32 tile_height, plane_offset, plane_size;
unsigned int rotation;
int x_offset, y_offset;
- unsigned long surf_addr;
+ u32 surf_addr;
struct intel_crtc_state *crtc_state = intel_crtc->config;
struct intel_plane_state *plane_state;
int src_x = 0, src_y = 0, src_w = 0, src_h = 0;
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index ebbd23407a80..0f8367da0663 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -4648,7 +4648,7 @@ static bool bxt_digital_port_connected(struct drm_i915_private *dev_priv,
*
* Return %true if @port is connected, %false otherwise.
*/
-bool intel_digital_port_connected(struct drm_i915_private *dev_priv,
+static bool intel_digital_port_connected(struct drm_i915_private *dev_priv,
struct intel_digital_port *port)
{
if (HAS_PCH_IBX(dev_priv))
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 67f72a7ee7cb..722aa159cd28 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -1177,9 +1177,9 @@ void intel_modeset_preclose(struct drm_device *dev, struct drm_file *file);
int skl_update_scaler_crtc(struct intel_crtc_state *crtc_state);
int skl_max_scale(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state);
-unsigned long intel_plane_obj_offset(struct intel_plane *intel_plane,
- struct drm_i915_gem_object *obj,
- unsigned int plane);
+u32 intel_plane_obj_offset(struct intel_plane *intel_plane,
+ struct drm_i915_gem_object *obj,
+ unsigned int plane);
u32 skl_plane_ctl_format(uint32_t pixel_format);
u32 skl_plane_ctl_tiling(uint64_t fb_modifier);
@@ -1231,8 +1231,6 @@ void intel_edp_drrs_disable(struct intel_dp *intel_dp);
void intel_edp_drrs_invalidate(struct drm_device *dev,
unsigned frontbuffer_bits);
void intel_edp_drrs_flush(struct drm_device *dev, unsigned frontbuffer_bits);
-bool intel_digital_port_connected(struct drm_i915_private *dev_priv,
- struct intel_digital_port *port);
void hsw_dp_set_ddi_pll_sel(struct intel_crtc_state *pipe_config);
/* intel_dp_mst.c */
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index dff69fef47e0..1ea8532f5ab2 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1331,19 +1331,18 @@ intel_hdmi_unset_edid(struct drm_connector *connector)
}
static bool
-intel_hdmi_set_edid(struct drm_connector *connector, bool force)
+intel_hdmi_set_edid(struct drm_connector *connector)
{
struct drm_i915_private *dev_priv = to_i915(connector->dev);
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
- struct edid *edid = NULL;
+ struct edid *edid;
bool connected = false;
intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
- if (force)
- edid = drm_get_edid(connector,
- intel_gmbus_get_adapter(dev_priv,
- intel_hdmi->ddc_bus));
+ edid = drm_get_edid(connector,
+ intel_gmbus_get_adapter(dev_priv,
+ intel_hdmi->ddc_bus));
intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
@@ -1371,37 +1370,16 @@ static enum drm_connector_status
intel_hdmi_detect(struct drm_connector *connector, bool force)
{
enum drm_connector_status status;
- struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
struct drm_i915_private *dev_priv = to_i915(connector->dev);
- bool live_status = false;
- unsigned int try;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.id, connector->name);
intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
- for (try = 0; !live_status && try < 9; try++) {
- if (try)
- msleep(10);
- live_status = intel_digital_port_connected(dev_priv,
- hdmi_to_dig_port(intel_hdmi));
- }
-
- if (!live_status) {
- DRM_DEBUG_KMS("HDMI live status down\n");
- /*
- * Live status register is not reliable on all intel platforms.
- * So consider live_status only for certain platforms, for
- * others, read EDID to determine presence of sink.
- */
- if (INTEL_INFO(dev_priv)->gen < 7 || IS_IVYBRIDGE(dev_priv))
- live_status = true;
- }
-
intel_hdmi_unset_edid(connector);
- if (intel_hdmi_set_edid(connector, live_status)) {
+ if (intel_hdmi_set_edid(connector)) {
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI;
@@ -1427,7 +1405,7 @@ intel_hdmi_force(struct drm_connector *connector)
if (connector->status != connector_status_connected)
return;
- intel_hdmi_set_edid(connector, true);
+ intel_hdmi_set_edid(connector);
hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI;
}
@@ -2019,6 +1997,50 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c
intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
}
+static u8 intel_hdmi_ddc_pin(struct drm_i915_private *dev_priv,
+ enum port port)
+{
+ const struct ddi_vbt_port_info *info =
+ &dev_priv->vbt.ddi_port_info[port];
+ u8 ddc_pin;
+
+ if (info->alternate_ddc_pin) {
+ DRM_DEBUG_KMS("Using DDC pin 0x%x for port %c (VBT)\n",
+ info->alternate_ddc_pin, port_name(port));
+ return info->alternate_ddc_pin;
+ }
+
+ switch (port) {
+ case PORT_B:
+ if (IS_BROXTON(dev_priv))
+ ddc_pin = GMBUS_PIN_1_BXT;
+ else
+ ddc_pin = GMBUS_PIN_DPB;
+ break;
+ case PORT_C:
+ if (IS_BROXTON(dev_priv))
+ ddc_pin = GMBUS_PIN_2_BXT;
+ else
+ ddc_pin = GMBUS_PIN_DPC;
+ break;
+ case PORT_D:
+ if (IS_CHERRYVIEW(dev_priv))
+ ddc_pin = GMBUS_PIN_DPD_CHV;
+ else
+ ddc_pin = GMBUS_PIN_DPD;
+ break;
+ default:
+ MISSING_CASE(port);
+ ddc_pin = GMBUS_PIN_DPB;
+ break;
+ }
+
+ DRM_DEBUG_KMS("Using DDC pin 0x%x for port %c (platform default)\n",
+ ddc_pin, port_name(port));
+
+ return ddc_pin;
+}
+
void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
struct intel_connector *intel_connector)
{
@@ -2028,7 +2050,6 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
struct drm_device *dev = intel_encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
enum port port = intel_dig_port->port;
- uint8_t alternate_ddc_pin;
DRM_DEBUG_KMS("Adding HDMI connector on port %c\n",
port_name(port));
@@ -2041,12 +2062,10 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
connector->doublescan_allowed = 0;
connector->stereo_allowed = 1;
+ intel_hdmi->ddc_bus = intel_hdmi_ddc_pin(dev_priv, port);
+
switch (port) {
case PORT_B:
- if (IS_BROXTON(dev_priv))
- intel_hdmi->ddc_bus = GMBUS_PIN_1_BXT;
- else
- intel_hdmi->ddc_bus = GMBUS_PIN_DPB;
/*
* On BXT A0/A1, sw needs to activate DDIA HPD logic and
* interrupts to check the external panel connection.
@@ -2057,46 +2076,17 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
intel_encoder->hpd_pin = HPD_PORT_B;
break;
case PORT_C:
- if (IS_BROXTON(dev_priv))
- intel_hdmi->ddc_bus = GMBUS_PIN_2_BXT;
- else
- intel_hdmi->ddc_bus = GMBUS_PIN_DPC;
intel_encoder->hpd_pin = HPD_PORT_C;
break;
case PORT_D:
- if (WARN_ON(IS_BROXTON(dev_priv)))
- intel_hdmi->ddc_bus = GMBUS_PIN_DISABLED;
- else if (IS_CHERRYVIEW(dev_priv))
- intel_hdmi->ddc_bus = GMBUS_PIN_DPD_CHV;
- else
- intel_hdmi->ddc_bus = GMBUS_PIN_DPD;
intel_encoder->hpd_pin = HPD_PORT_D;
break;
case PORT_E:
- /* On SKL PORT E doesn't have seperate GMBUS pin
- * We rely on VBT to set a proper alternate GMBUS pin. */
- alternate_ddc_pin =
- dev_priv->vbt.ddi_port_info[PORT_E].alternate_ddc_pin;
- switch (alternate_ddc_pin) {
- case DDC_PIN_B:
- intel_hdmi->ddc_bus = GMBUS_PIN_DPB;
- break;
- case DDC_PIN_C:
- intel_hdmi->ddc_bus = GMBUS_PIN_DPC;
- break;
- case DDC_PIN_D:
- intel_hdmi->ddc_bus = GMBUS_PIN_DPD;
- break;
- default:
- MISSING_CASE(alternate_ddc_pin);
- }
intel_encoder->hpd_pin = HPD_PORT_E;
break;
- case PORT_A:
- intel_encoder->hpd_pin = HPD_PORT_A;
- /* Internal port only for eDP. */
default:
- BUG();
+ MISSING_CASE(port);
+ return;
}
if (IS_VALLEYVIEW(dev)) {
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 1e851e037c29..3f802163f7d4 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2097,32 +2097,34 @@ static void intel_read_wm_latency(struct drm_device *dev, uint16_t wm[8])
GEN9_MEM_LATENCY_LEVEL_MASK;
/*
+ * If a level n (n > 1) has a 0us latency, all levels m (m >= n)
+ * need to be disabled. We make sure to sanitize the values out
+ * of the punit to satisfy this requirement.
+ */
+ for (level = 1; level <= max_level; level++) {
+ if (wm[level] == 0) {
+ for (i = level + 1; i <= max_level; i++)
+ wm[i] = 0;
+ break;
+ }
+ }
+
+ /*
* WaWmMemoryReadLatency:skl
*
* punit doesn't take into account the read latency so we need
- * to add 2us to the various latency levels we retrieve from
- * the punit.
- * - W0 is a bit special in that it's the only level that
- * can't be disabled if we want to have display working, so
- * we always add 2us there.
- * - For levels >=1, punit returns 0us latency when they are
- * disabled, so we respect that and don't add 2us then
- *
- * Additionally, if a level n (n > 1) has a 0us latency, all
- * levels m (m >= n) need to be disabled. We make sure to
- * sanitize the values out of the punit to satisfy this
- * requirement.
+ * to add 2us to the various latency levels we retrieve from the
+ * punit when level 0 response data us 0us.
*/
- wm[0] += 2;
- for (level = 1; level <= max_level; level++)
- if (wm[level] != 0)
+ if (wm[0] == 0) {
+ wm[0] += 2;
+ for (level = 1; level <= max_level; level++) {
+ if (wm[level] == 0)
+ break;
wm[level] += 2;
- else {
- for (i = level + 1; i <= max_level; i++)
- wm[i] = 0;
-
- break;
}
+ }
+
} else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
uint64_t sskpd = I915_READ64(MCH_SSKPD);
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index 56dc132e8e20..2cc6aa072f4c 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -195,7 +195,7 @@ skl_update_plane(struct drm_plane *drm_plane, struct drm_crtc *crtc,
int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
const struct drm_intel_sprite_colorkey *key =
&to_intel_plane_state(drm_plane->state)->ckey;
- unsigned long surf_addr;
+ u32 surf_addr;
u32 tile_height, plane_offset, plane_size;
unsigned int rotation;
int x_offset, y_offset;
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index 62675198d6ac..5838545468f8 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -11,6 +11,7 @@ config DRM_MSM
select TMPFS
select QCOM_SCM
select BACKLIGHT_CLASS_DEVICE
+ select MSM_EXT_DISPLAY
default y
help
DRM/KMS driver for MSM/snapdragon.
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index d81ec8918ce7..79ea5a9f90ea 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -100,7 +100,8 @@ msm_drm-$(CONFIG_DRM_MSM_DSI_STAGING) += dsi-staging/dsi_phy.o \
msm_drm-$(CONFIG_DRM_SDE_HDMI) += \
hdmi-staging/sde_hdmi.o \
hdmi-staging/sde_hdmi_bridge.o \
- hdmi-staging/sde_hdmi_audio.o
+ hdmi-staging/sde_hdmi_audio.o \
+ hdmi-staging/sde_hdmi_edid.o
msm_drm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \
dsi/pll/dsi_pll_28nm.o
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
index 15e2d69827e7..347b78886b24 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
@@ -22,8 +22,10 @@
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/of_irq.h>
+#include <linux/of_platform.h>
#include "sde_kms.h"
+#include "sde_connector.h"
#include "msm_drv.h"
#include "sde_hdmi.h"
@@ -402,6 +404,13 @@ static void _sde_hdmi_hotplug_work(struct work_struct *work)
}
connector = sde_hdmi->ctrl.ctrl->connector;
+
+ if (sde_hdmi->connected)
+ sde_hdmi_get_edid(connector, sde_hdmi);
+ else
+ sde_hdmi_free_edid(sde_hdmi);
+
+ sde_hdmi_notify_clients(connector, sde_hdmi->connected);
drm_helper_hpd_irq_event(connector->dev);
}
@@ -431,7 +440,8 @@ static void _sde_hdmi_connector_irq(struct sde_hdmi *sde_hdmi)
hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT;
hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl);
- queue_work(hdmi->workq, &sde_hdmi->hpd_work);
+ if (!sde_hdmi->non_pluggable)
+ queue_work(hdmi->workq, &sde_hdmi->hpd_work);
}
}
@@ -460,6 +470,148 @@ static irqreturn_t _sde_hdmi_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static int _sde_hdmi_audio_info_setup(struct platform_device *pdev,
+ struct msm_ext_disp_audio_setup_params *params)
+{
+ int rc = -EPERM;
+ struct sde_hdmi *display = NULL;
+ struct hdmi *hdmi = NULL;
+
+ display = platform_get_drvdata(pdev);
+
+ if (!display || !params) {
+ SDE_ERROR("invalid param(s), display %pK, params %pK\n",
+ display, params);
+ return -ENODEV;
+ }
+
+ hdmi = display->ctrl.ctrl;
+
+ if (hdmi->hdmi_mode)
+ rc = sde_hdmi_audio_on(hdmi, params);
+
+ return rc;
+}
+
+static int _sde_hdmi_get_audio_edid_blk(struct platform_device *pdev,
+ struct msm_ext_disp_audio_edid_blk *blk)
+{
+ struct sde_hdmi *display = platform_get_drvdata(pdev);
+
+ if (!display || !blk) {
+ SDE_ERROR("invalid param(s), display %pK, blk %pK\n",
+ display, blk);
+ return -ENODEV;
+ }
+
+ blk->audio_data_blk = display->edid.audio_data_block;
+ blk->audio_data_blk_size = display->edid.adb_size;
+
+ blk->spk_alloc_data_blk = display->edid.spkr_alloc_data_block;
+ blk->spk_alloc_data_blk_size = display->edid.sadb_size;
+
+ return 0;
+}
+
+static int _sde_hdmi_get_cable_status(struct platform_device *pdev, u32 vote)
+{
+ struct sde_hdmi *display = NULL;
+ struct hdmi *hdmi = NULL;
+
+ display = platform_get_drvdata(pdev);
+
+ if (!display) {
+ SDE_ERROR("invalid param(s), display %pK\n", display);
+ return -ENODEV;
+ }
+
+ hdmi = display->ctrl.ctrl;
+
+ return hdmi->power_on && display->connected;
+}
+
+static int _sde_hdmi_ext_disp_init(struct sde_hdmi *display)
+{
+ int rc = 0;
+ struct device_node *pd_np;
+ const char *phandle = "qcom,msm_ext_disp";
+
+ if (!display) {
+ SDE_ERROR("[%s]Invalid params\n", display->name);
+ return -EINVAL;
+ }
+
+ display->ext_audio_data.type = EXT_DISPLAY_TYPE_HDMI;
+ display->ext_audio_data.pdev = display->pdev;
+ display->ext_audio_data.codec_ops.audio_info_setup =
+ _sde_hdmi_audio_info_setup;
+ display->ext_audio_data.codec_ops.get_audio_edid_blk =
+ _sde_hdmi_get_audio_edid_blk;
+ display->ext_audio_data.codec_ops.cable_status =
+ _sde_hdmi_get_cable_status;
+
+ if (!display->pdev->dev.of_node) {
+ SDE_ERROR("[%s]cannot find sde_hdmi of_node\n", display->name);
+ return -ENODEV;
+ }
+
+ pd_np = of_parse_phandle(display->pdev->dev.of_node, phandle, 0);
+ if (!pd_np) {
+ SDE_ERROR("[%s]cannot find %s device node\n",
+ display->name, phandle);
+ return -ENODEV;
+ }
+
+ display->ext_pdev = of_find_device_by_node(pd_np);
+ if (!display->ext_pdev) {
+ SDE_ERROR("[%s]cannot find %s platform device\n",
+ display->name, phandle);
+ return -ENODEV;
+ }
+
+ rc = msm_ext_disp_register_intf(display->ext_pdev,
+ &display->ext_audio_data);
+ if (rc)
+ SDE_ERROR("[%s]failed to register disp\n", display->name);
+
+ return rc;
+}
+
+void sde_hdmi_notify_clients(struct drm_connector *connector,
+ bool connected)
+{
+ struct sde_connector *c_conn = to_sde_connector(connector);
+ struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
+ int state = connected ?
+ EXT_DISPLAY_CABLE_CONNECT : EXT_DISPLAY_CABLE_DISCONNECT;
+
+ if (display && display->ext_audio_data.intf_ops.hpd) {
+ struct hdmi *hdmi = display->ctrl.ctrl;
+ u32 flags = MSM_EXT_DISP_HPD_VIDEO;
+
+ if (hdmi->hdmi_mode)
+ flags |= MSM_EXT_DISP_HPD_AUDIO;
+
+ display->ext_audio_data.intf_ops.hpd(display->ext_pdev,
+ display->ext_audio_data.type, state, flags);
+ }
+}
+
+void sde_hdmi_ack_state(struct drm_connector *connector,
+ enum drm_connector_status status)
+{
+ struct sde_connector *c_conn = to_sde_connector(connector);
+ struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
+
+ if (display) {
+ struct hdmi *hdmi = display->ctrl.ctrl;
+
+ if (hdmi->hdmi_mode && display->ext_audio_data.intf_ops.notify)
+ display->ext_audio_data.intf_ops.notify(
+ display->ext_pdev, status);
+ }
+}
+
void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on)
{
uint32_t ctrl = 0;
@@ -645,13 +797,29 @@ sde_hdmi_connector_detect(struct drm_connector *connector,
return status;
}
+int _sde_hdmi_update_modes(struct drm_connector *connector,
+ struct sde_hdmi *display)
+{
+ int rc = 0;
+ struct hdmi_edid_ctrl *edid_ctrl = &display->edid;
+
+ if (edid_ctrl->edid) {
+ drm_mode_connector_update_edid_property(connector,
+ edid_ctrl->edid);
+
+ rc = drm_add_edid_modes(connector, edid_ctrl->edid);
+ return rc;
+ }
+
+ drm_mode_connector_update_edid_property(connector, NULL);
+
+ return rc;
+}
+
int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display)
{
struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display;
- struct hdmi *hdmi;
- struct edid *edid;
struct drm_display_mode *mode, *m;
- uint32_t hdmi_ctrl;
int ret = 0;
if (!connector || !display) {
@@ -662,7 +830,6 @@ int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display)
SDE_DEBUG("\n");
- hdmi = hdmi_display->ctrl.ctrl;
if (hdmi_display->non_pluggable) {
list_for_each_entry(mode, &hdmi_display->mode_list, head) {
m = drm_mode_duplicate(connector->dev, mode);
@@ -675,21 +842,7 @@ int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display)
}
ret = hdmi_display->num_of_modes;
} else {
- /* Read EDID */
- hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL);
- hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE);
-
- edid = drm_get_edid(connector, hdmi->i2c);
-
- hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl);
-
- hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid);
- drm_mode_connector_update_edid_property(connector, edid);
-
- if (edid) {
- ret = drm_add_edid_modes(connector, edid);
- kfree(edid);
- }
+ ret = _sde_hdmi_update_modes(connector, display);
}
return ret;
@@ -778,6 +931,20 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data)
if (rc) {
SDE_ERROR("[%s]Debugfs init failed, rc=%d\n",
display->name, rc);
+ goto debug_error;
+ }
+
+ rc = _sde_hdmi_ext_disp_init(display);
+ if (rc) {
+ SDE_ERROR("[%s]Ext Disp init failed, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
+ rc = sde_hdmi_edid_init(display);
+ if (rc) {
+ SDE_ERROR("[%s]Ext Disp init failed, rc=%d\n",
+ display->name, rc);
goto error;
}
@@ -787,6 +954,8 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data)
display->drm_dev = drm;
error:
+ (void)_sde_hdmi_debugfs_deinit(display);
+debug_error:
mutex_unlock(&display->display_lock);
return rc;
}
@@ -809,6 +978,7 @@ static void sde_hdmi_unbind(struct device *dev, struct device *master,
}
mutex_lock(&display->display_lock);
(void)_sde_hdmi_debugfs_deinit(display);
+ (void)sde_hdmi_edid_deinit(display);
display->drm_dev = NULL;
mutex_unlock(&display->display_lock);
}
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
index 1c13d9f875f2..869d1bebf9db 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
@@ -25,6 +25,10 @@
#include <drm/drm_crtc.h>
#include "hdmi.h"
+#define MAX_NUMBER_ADB 5
+#define MAX_AUDIO_DATA_BLOCK_SIZE 30
+#define MAX_SPKR_ALLOC_DATA_BLOCK_SIZE 3
+
/**
* struct sde_hdmi_info - defines hdmi display properties
* @display_type: Display type as defined by device tree.
@@ -60,6 +64,14 @@ struct sde_hdmi_ctrl {
u32 hdmi_ctrl_idx;
};
+struct hdmi_edid_ctrl {
+ struct edid *edid;
+ 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];
+ int sadb_size;
+};
+
/**
* struct sde_hdmi - hdmi display information
* @pdev: Pointer to platform device.
@@ -88,6 +100,10 @@ struct sde_hdmi {
struct sde_hdmi_ctrl ctrl;
+ struct platform_device *ext_pdev;
+ struct msm_ext_disp_init_data ext_audio_data;
+ struct hdmi_edid_ctrl edid;
+
bool non_pluggable;
u32 num_of_modes;
struct list_head mode_list;
@@ -268,6 +284,61 @@ void sde_hdmi_audio_off(struct hdmi *hdmi);
* Return: error code.
*/
int sde_hdmi_config_avmute(struct hdmi *hdmi, bool set);
+
+/**
+ * sde_hdmi_notify_clients() - notify hdmi clients of the connection status.
+ * @connector: Handle to the drm_connector.
+ * @connected: connection status.
+ *
+ * Return: void.
+ */
+void sde_hdmi_notify_clients(struct drm_connector *connector,
+ bool connected);
+
+/**
+ * sde_hdmi_ack_state() - acknowledge the connection status.
+ * @connector: Handle to the drm_connector.
+ * @status: connection status.
+ *
+ * Return: void.
+ */
+void sde_hdmi_ack_state(struct drm_connector *connector,
+ enum drm_connector_status status);
+
+/**
+ * sde_hdmi_edid_init() - init edid structure.
+ * @display: Handle to the sde_hdmi.
+ *
+ * Return: error code.
+ */
+int sde_hdmi_edid_init(struct sde_hdmi *display);
+
+/**
+ * sde_hdmi_edid_deinit() - deinit edid structure.
+ * @display: Handle to the sde_hdmi.
+ *
+ * Return: error code.
+ */
+int sde_hdmi_edid_deinit(struct sde_hdmi *display);
+
+/**
+ * sde_hdmi_get_edid() - get edid info.
+ * @connector: Handle to the drm_connector.
+ * @display: Handle to the sde_hdmi.
+ *
+ * Return: void.
+ */
+void sde_hdmi_get_edid(struct drm_connector *connector,
+ struct sde_hdmi *display);
+
+/**
+ * sde_hdmi_free_edid() - free edid structure.
+ * @display: Handle to the sde_hdmi.
+ *
+ * Return: error code.
+ */
+int sde_hdmi_free_edid(struct sde_hdmi *display);
+
#else /*#ifdef CONFIG_DRM_SDE_HDMI*/
static inline u32 sde_hdmi_get_num_of_displays(void)
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
index ecfff7e88689..681dca501f9b 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
@@ -18,6 +18,7 @@
#include "drm_edid.h"
#include "sde_kms.h"
+#include "sde_connector.h"
#include "sde_hdmi.h"
#include "hdmi.h"
@@ -111,7 +112,6 @@ static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge)
if (!hdmi->power_on) {
_sde_hdmi_bridge_power_on(bridge);
hdmi->power_on = true;
- hdmi_audio_update(hdmi);
}
if (phy)
@@ -121,14 +121,42 @@ static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge)
if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported)
hdmi_hdcp_ctrl_on(hdmi->hdcp_ctrl);
+
+ sde_hdmi_ack_state(hdmi->connector, EXT_DISPLAY_CABLE_CONNECT);
+}
+
+static void sde_hdmi_force_update_audio(struct drm_connector *connector,
+ enum drm_connector_status status)
+{
+ struct sde_connector *c_conn = to_sde_connector(connector);
+ struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
+
+ if (display && display->non_pluggable) {
+ display->ext_audio_data.intf_ops.hpd(display->ext_pdev,
+ display->ext_audio_data.type,
+ status,
+ MSM_EXT_DISP_HPD_AUDIO);
+ }
}
static void _sde_hdmi_bridge_enable(struct drm_bridge *bridge)
{
+ struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge);
+ struct hdmi *hdmi = sde_hdmi_bridge->hdmi;
+
+ /* force update audio ops when there's no HPD event */
+ sde_hdmi_force_update_audio(hdmi->connector,
+ EXT_DISPLAY_CABLE_CONNECT);
}
static void _sde_hdmi_bridge_disable(struct drm_bridge *bridge)
{
+ struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge);
+ struct hdmi *hdmi = sde_hdmi_bridge->hdmi;
+
+ /* force update audio ops when there's no HPD event */
+ sde_hdmi_force_update_audio(hdmi->connector,
+ EXT_DISPLAY_CABLE_DISCONNECT);
}
static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge)
@@ -140,6 +168,8 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge)
if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported)
hdmi_hdcp_ctrl_off(hdmi->hdcp_ctrl);
+ sde_hdmi_audio_off(hdmi);
+
DRM_DEBUG("power down");
sde_hdmi_set_mode(hdmi, false);
@@ -149,8 +179,9 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge)
if (hdmi->power_on) {
_sde_hdmi_bridge_power_off(bridge);
hdmi->power_on = false;
- hdmi_audio_update(hdmi);
}
+
+ sde_hdmi_ack_state(hdmi->connector, EXT_DISPLAY_CABLE_DISCONNECT);
}
static void _sde_hdmi_bridge_set_avi_infoframe(struct hdmi *hdmi,
@@ -342,8 +373,6 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
_sde_hdmi_bridge_set_spd_infoframe(hdmi, mode);
DRM_DEBUG("hdmi setup info frame\n");
}
-
- hdmi_audio_update(hdmi);
}
static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = {
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c
new file mode 100644
index 000000000000..57c79e2aa812
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c
@@ -0,0 +1,227 @@
+/* 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 <drm/drm_edid.h>
+
+#include "sde_kms.h"
+#include "sde_hdmi.h"
+
+/* TODO: copy from drm_edid.c and mdss_hdmi_edid.c. remove if using ELD */
+#define DBC_START_OFFSET 4
+#define EDID_DTD_LEN 18
+
+enum data_block_types {
+ RESERVED,
+ AUDIO_DATA_BLOCK,
+ VIDEO_DATA_BLOCK,
+ VENDOR_SPECIFIC_DATA_BLOCK,
+ SPEAKER_ALLOCATION_DATA_BLOCK,
+ VESA_DTC_DATA_BLOCK,
+ RESERVED2,
+ USE_EXTENDED_TAG
+};
+
+static u8 *_sde_hdmi_edid_find_cea_extension(struct edid *edid)
+{
+ u8 *edid_ext = NULL;
+ int i;
+
+ /* No EDID or EDID extensions */
+ if (edid == NULL || edid->extensions == 0)
+ return NULL;
+
+ /* Find CEA extension */
+ for (i = 0; i < edid->extensions; i++) {
+ edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
+ if (edid_ext[0] == CEA_EXT)
+ break;
+ }
+
+ if (i == edid->extensions)
+ return NULL;
+
+ return edid_ext;
+}
+
+static const u8 *_sde_hdmi_edid_find_block(const u8 *in_buf, u32 start_offset,
+ u8 type, u8 *len)
+{
+ /* the start of data block collection, start of Video Data Block */
+ u32 offset = start_offset;
+ u32 dbc_offset = in_buf[2];
+
+ /*
+ * * edid buffer 1, byte 2 being 4 means no non-DTD/Data block
+ * collection present.
+ * * edid buffer 1, byte 2 being 0 means no non-DTD/DATA block
+ * collection present and no DTD data present.
+ */
+ if ((dbc_offset == 0) || (dbc_offset == 4)) {
+ SDE_ERROR("EDID: no DTD or non-DTD data present\n");
+ return NULL;
+ }
+
+ while (offset < dbc_offset) {
+ u8 block_len = in_buf[offset] & 0x1F;
+
+ if ((offset + block_len <= dbc_offset) &&
+ (in_buf[offset] >> 5) == type) {
+ *len = block_len;
+ SDE_DEBUG("EDID: block=%d found @ 0x%x w/ len=%d\n",
+ type, offset, block_len);
+
+ return in_buf + offset;
+ }
+ offset += 1 + block_len;
+ }
+
+ return NULL;
+}
+
+static void _sde_hdmi_extract_audio_data_blocks(
+ struct hdmi_edid_ctrl *edid_ctrl)
+{
+ u8 len = 0;
+ u8 adb_max = 0;
+ const u8 *adb = NULL;
+ u32 offset = DBC_START_OFFSET;
+ u8 *cea = NULL;
+
+ if (!edid_ctrl) {
+ SDE_ERROR("invalid edid_ctrl\n");
+ return;
+ }
+
+ cea = _sde_hdmi_edid_find_cea_extension(edid_ctrl->edid);
+ if (!cea) {
+ SDE_DEBUG("CEA extension not found\n");
+ return;
+ }
+
+ edid_ctrl->adb_size = 0;
+
+ memset(edid_ctrl->audio_data_block, 0,
+ sizeof(edid_ctrl->audio_data_block));
+
+ do {
+ len = 0;
+ adb = _sde_hdmi_edid_find_block(cea, offset, AUDIO_DATA_BLOCK,
+ &len);
+
+ if ((adb == NULL) || (len > MAX_AUDIO_DATA_BLOCK_SIZE ||
+ adb_max >= MAX_NUMBER_ADB)) {
+ if (!edid_ctrl->adb_size) {
+ SDE_DEBUG("No/Invalid Audio Data Block\n");
+ return;
+ }
+
+ continue;
+ }
+
+ memcpy(edid_ctrl->audio_data_block + edid_ctrl->adb_size,
+ adb + 1, len);
+ offset = (adb - cea) + 1 + len;
+
+ edid_ctrl->adb_size += len;
+ adb_max++;
+ } while (adb);
+
+}
+
+static void _sde_hdmi_extract_speaker_allocation_data(
+ struct hdmi_edid_ctrl *edid_ctrl)
+{
+ u8 len;
+ const u8 *sadb = NULL;
+ u8 *cea = NULL;
+
+ if (!edid_ctrl) {
+ SDE_ERROR("invalid edid_ctrl\n");
+ return;
+ }
+
+ cea = _sde_hdmi_edid_find_cea_extension(edid_ctrl->edid);
+ if (!cea) {
+ SDE_DEBUG("CEA extension not found\n");
+ return;
+ }
+
+ sadb = _sde_hdmi_edid_find_block(cea, DBC_START_OFFSET,
+ SPEAKER_ALLOCATION_DATA_BLOCK, &len);
+ if ((sadb == NULL) || (len != MAX_SPKR_ALLOC_DATA_BLOCK_SIZE)) {
+ SDE_DEBUG("No/Invalid Speaker Allocation Data Block\n");
+ return;
+ }
+
+ memcpy(edid_ctrl->spkr_alloc_data_block, sadb + 1, len);
+ edid_ctrl->sadb_size = len;
+
+ SDE_DEBUG("EDID: speaker alloc data SP byte = %08x %s%s%s%s%s%s%s\n",
+ sadb[1],
+ (sadb[1] & BIT(0)) ? "FL/FR," : "",
+ (sadb[1] & BIT(1)) ? "LFE," : "",
+ (sadb[1] & BIT(2)) ? "FC," : "",
+ (sadb[1] & BIT(3)) ? "RL/RR," : "",
+ (sadb[1] & BIT(4)) ? "RC," : "",
+ (sadb[1] & BIT(5)) ? "FLC/FRC," : "",
+ (sadb[1] & BIT(6)) ? "RLC/RRC," : "");
+}
+
+int sde_hdmi_edid_init(struct sde_hdmi *display)
+{
+ int rc = 0;
+
+ if (!display) {
+ SDE_ERROR("[%s]Invalid params\n", display->name);
+ return -EINVAL;
+ }
+
+ memset(&display->edid, 0, sizeof(display->edid));
+
+ return rc;
+}
+
+int sde_hdmi_free_edid(struct sde_hdmi *display)
+{
+ struct hdmi_edid_ctrl *edid_ctrl = &display->edid;
+
+ kfree(edid_ctrl->edid);
+ edid_ctrl->edid = NULL;
+
+ return 0;
+}
+
+int sde_hdmi_edid_deinit(struct sde_hdmi *display)
+{
+ return sde_hdmi_free_edid(display);
+}
+
+void sde_hdmi_get_edid(struct drm_connector *connector,
+ struct sde_hdmi *display)
+{
+ u32 hdmi_ctrl;
+ struct hdmi_edid_ctrl *edid_ctrl = &display->edid;
+ struct hdmi *hdmi = display->ctrl.ctrl;
+
+ /* Read EDID */
+ hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL);
+ hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE);
+ edid_ctrl->edid = drm_get_edid(connector, hdmi->i2c);
+ hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl);
+
+ if (edid_ctrl->edid) {
+ hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid_ctrl->edid);
+
+ _sde_hdmi_extract_audio_data_blocks(edid_ctrl);
+ _sde_hdmi_extract_speaker_allocation_data(edid_ctrl);
+ }
+};
diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c
index 42bbbdcab2c9..dc7827872276 100644
--- a/drivers/gpu/drm/msm/sde/sde_formats.c
+++ b/drivers/gpu/drm/msm/sde/sde_formats.c
@@ -102,169 +102,169 @@ flg, fm, np) \
static const struct sde_format sde_format_map[] = {
INTERLEAVED_RGB_FMT(ARGB8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
true, 4, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(ABGR8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
true, 4, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(XBGR8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
true, 4, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(RGBA8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
true, 4, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(BGRA8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
true, 4, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(BGRX8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
false, 4, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(XRGB8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
false, 4, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(RGBX8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
false, 4, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(RGB888,
0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3,
false, 3, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(BGR888,
0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
false, 3, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(RGB565,
0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT,
- C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(BGR565,
0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT,
- C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(ARGB1555,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(ABGR1555,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(RGBA5551,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(BGRA5551,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(XRGB1555,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(XBGR1555,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(RGBX5551,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(BGRX5551,
COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
- C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(ARGB4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(ABGR4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(RGBA4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(BGRA4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
true, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(XRGB4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+ C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(XBGR4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+ C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(RGBX4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+ C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
INTERLEAVED_RGB_FMT(BGRX4444,
COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
- C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+ C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
false, 2, 0,
SDE_FETCH_LINEAR, 1),
@@ -366,13 +366,13 @@ static const struct sde_format sde_format_map[] = {
PLANAR_YUV_FMT(YUV420,
0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C0_G_Y, C1_B_Cb, C2_R_Cr,
+ C2_R_Cr, C1_B_Cb, C0_G_Y,
false, SDE_CHROMA_420, 1, SDE_FORMAT_FLAG_YUV,
SDE_FETCH_LINEAR, 3),
PLANAR_YUV_FMT(YVU420,
0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
- C0_G_Y, C2_R_Cr, C1_B_Cb,
+ C1_B_Cb, C2_R_Cr, C0_G_Y,
false, SDE_CHROMA_420, 1, SDE_FORMAT_FLAG_YUV,
SDE_FETCH_LINEAR, 3),
};
@@ -384,19 +384,19 @@ static const struct sde_format sde_format_map[] = {
* the data will be passed by user-space.
*/
static const struct sde_format sde_format_map_ubwc[] = {
- INTERLEAVED_RGB_FMT(RGB565,
+ INTERLEAVED_RGB_FMT(BGR565,
0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT,
C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
false, 2, 0,
SDE_FETCH_UBWC, 2),
- INTERLEAVED_RGB_FMT(RGBA8888,
+ INTERLEAVED_RGB_FMT(ABGR8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
true, 4, 0,
SDE_FETCH_UBWC, 2),
- INTERLEAVED_RGB_FMT(RGBX8888,
+ INTERLEAVED_RGB_FMT(XBGR8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
false, 4, 0,
@@ -513,14 +513,15 @@ static int _sde_format_get_plane_sizes_ubwc(
ALIGN(DIV_ROUND_UP(height / 2, uv_tile_height), 16),
4096);
- } else if (fmt->base.pixel_format == DRM_FORMAT_RGBA8888 ||
- fmt->base.pixel_format == DRM_FORMAT_RGBX8888 ||
- fmt->base.pixel_format == DRM_FORMAT_RGBA1010102 ||
- fmt->base.pixel_format == DRM_FORMAT_RGBX1010102 ||
- fmt->base.pixel_format == DRM_FORMAT_RGB565) {
+ } else if (fmt->base.pixel_format == DRM_FORMAT_ABGR8888 ||
+ fmt->base.pixel_format == DRM_FORMAT_XBGR8888 ||
+ fmt->base.pixel_format == DRM_FORMAT_BGRA1010102 ||
+ fmt->base.pixel_format == DRM_FORMAT_BGRX1010102 ||
+ fmt->base.pixel_format == DRM_FORMAT_BGR565) {
+
uint32_t stride_alignment, aligned_bitstream_width;
- if (fmt->base.pixel_format == DRM_FORMAT_RGB565)
+ if (fmt->base.pixel_format == DRM_FORMAT_BGR565)
stride_alignment = 128;
else
stride_alignment = 64;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c
index bfcc6408a772..b7f4b826febe 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c
@@ -36,7 +36,10 @@ nv04_fifo_dma_object_dtor(struct nvkm_fifo_chan *base, int cookie)
{
struct nv04_fifo_chan *chan = nv04_fifo_chan(base);
struct nvkm_instmem *imem = chan->fifo->base.engine.subdev.device->imem;
+
+ mutex_lock(&chan->fifo->base.engine.subdev.mutex);
nvkm_ramht_remove(imem->ramht, cookie);
+ mutex_unlock(&chan->fifo->base.engine.subdev.mutex);
}
static int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c b/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c
index 4bef72a9d106..3fda594700e0 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c
@@ -59,9 +59,11 @@ static void
nv40_perfctr_next(struct nvkm_pm *pm, struct nvkm_perfdom *dom)
{
struct nvkm_device *device = pm->engine.subdev.device;
- if (pm->sequence != pm->sequence) {
+ struct nv40_pm *nv40pm = container_of(pm, struct nv40_pm, base);
+
+ if (nv40pm->sequence != pm->sequence) {
nvkm_wr32(device, 0x400084, 0x00000020);
- pm->sequence = pm->sequence;
+ nv40pm->sequence = pm->sequence;
}
}
diff --git a/drivers/gpu/drm/qxl/qxl_draw.c b/drivers/gpu/drm/qxl/qxl_draw.c
index 56e1d633875e..6e6c76080d6a 100644
--- a/drivers/gpu/drm/qxl/qxl_draw.c
+++ b/drivers/gpu/drm/qxl/qxl_draw.c
@@ -136,6 +136,8 @@ static int qxl_palette_create_1bit(struct qxl_bo *palette_bo,
* correctly globaly, since that would require
* tracking all of our palettes. */
ret = qxl_bo_kmap(palette_bo, (void **)&pal);
+ if (ret)
+ return ret;
pal->num_ents = 2;
pal->unique = unique++;
if (visual == FB_VISUAL_TRUECOLOR || visual == FB_VISUAL_DIRECTCOLOR) {
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index 79bab6fd76bb..6755d4768f59 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -275,6 +275,8 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
atombios_enable_crtc_memreq(crtc, ATOM_ENABLE);
atombios_blank_crtc(crtc, ATOM_DISABLE);
drm_vblank_post_modeset(dev, radeon_crtc->crtc_id);
+ /* Make sure vblank interrupt is still enabled if needed */
+ radeon_irq_set(rdev);
radeon_crtc_load_lut(crtc);
break;
case DRM_MODE_DPMS_STANDBY:
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 44ee72e04df9..b5760851195c 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -315,15 +315,27 @@ int radeon_dp_get_dp_link_config(struct drm_connector *connector,
unsigned max_lane_num = drm_dp_max_lane_count(dpcd);
unsigned lane_num, i, max_pix_clock;
- for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
- for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
- max_pix_clock = (lane_num * link_rates[i] * 8) / bpp;
+ if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) ==
+ ENCODER_OBJECT_ID_NUTMEG) {
+ for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
+ max_pix_clock = (lane_num * 270000 * 8) / bpp;
if (max_pix_clock >= pix_clock) {
*dp_lanes = lane_num;
- *dp_rate = link_rates[i];
+ *dp_rate = 270000;
return 0;
}
}
+ } else {
+ for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
+ for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
+ max_pix_clock = (lane_num * link_rates[i] * 8) / bpp;
+ if (max_pix_clock >= pix_clock) {
+ *dp_lanes = lane_num;
+ *dp_rate = link_rates[i];
+ return 0;
+ }
+ }
+ }
}
return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index 158872eb78e4..a3a321208fd8 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -1396,9 +1396,7 @@ static void cayman_pcie_gart_fini(struct radeon_device *rdev)
void cayman_cp_int_cntl_setup(struct radeon_device *rdev,
int ring, u32 cp_int_cntl)
{
- u32 srbm_gfx_cntl = RREG32(SRBM_GFX_CNTL) & ~3;
-
- WREG32(SRBM_GFX_CNTL, srbm_gfx_cntl | (ring & 3));
+ WREG32(SRBM_GFX_CNTL, RINGID(ring));
WREG32(CP_INT_CNTL, cp_int_cntl);
}
diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c
index fa2154493cf1..470af4aa4a6a 100644
--- a/drivers/gpu/drm/radeon/r600_dpm.c
+++ b/drivers/gpu/drm/radeon/r600_dpm.c
@@ -156,19 +156,20 @@ u32 r600_dpm_get_vblank_time(struct radeon_device *rdev)
struct drm_device *dev = rdev->ddev;
struct drm_crtc *crtc;
struct radeon_crtc *radeon_crtc;
- u32 line_time_us, vblank_lines;
+ u32 vblank_in_pixels;
u32 vblank_time_us = 0xffffffff; /* if the displays are off, vblank time is max */
if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) {
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
radeon_crtc = to_radeon_crtc(crtc);
if (crtc->enabled && radeon_crtc->enabled && radeon_crtc->hw_mode.clock) {
- line_time_us = (radeon_crtc->hw_mode.crtc_htotal * 1000) /
- radeon_crtc->hw_mode.clock;
- vblank_lines = radeon_crtc->hw_mode.crtc_vblank_end -
- radeon_crtc->hw_mode.crtc_vdisplay +
- (radeon_crtc->v_border * 2);
- vblank_time_us = vblank_lines * line_time_us;
+ vblank_in_pixels =
+ radeon_crtc->hw_mode.crtc_htotal *
+ (radeon_crtc->hw_mode.crtc_vblank_end -
+ radeon_crtc->hw_mode.crtc_vdisplay +
+ (radeon_crtc->v_border * 2));
+
+ vblank_time_us = vblank_in_pixels * 1000 / radeon_crtc->hw_mode.clock;
break;
}
}
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index e2dd5d19c32c..4aa2cbe4c85f 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -660,8 +660,9 @@ bool radeon_card_posted(struct radeon_device *rdev)
{
uint32_t reg;
- /* for pass through, always force asic_init */
- if (radeon_device_is_virtual())
+ /* for pass through, always force asic_init for CI */
+ if (rdev->family >= CHIP_BONAIRE &&
+ radeon_device_is_virtual())
return false;
/* required for EFI mode on macbook2,1 which uses an r5xx asic */
diff --git a/drivers/gpu/drm/radeon/radeon_dp_auxch.c b/drivers/gpu/drm/radeon/radeon_dp_auxch.c
index db64e0062689..3b0c229d7dcd 100644
--- a/drivers/gpu/drm/radeon/radeon_dp_auxch.c
+++ b/drivers/gpu/drm/radeon/radeon_dp_auxch.c
@@ -105,7 +105,7 @@ radeon_dp_aux_transfer_native(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg
tmp &= AUX_HPD_SEL(0x7);
tmp |= AUX_HPD_SEL(chan->rec.hpd);
- tmp |= AUX_EN | AUX_LS_READ_EN | AUX_HPD_DISCON(0x1);
+ tmp |= AUX_EN | AUX_LS_READ_EN;
WREG32(AUX_CONTROL + aux_offset[instance], tmp);
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
index 678b4386540d..89f22bdde298 100644
--- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
+++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
@@ -331,6 +331,8 @@ static void radeon_crtc_dpms(struct drm_crtc *crtc, int mode)
WREG32_P(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl, ~(mask | crtc_ext_cntl));
}
drm_vblank_post_modeset(dev, radeon_crtc->crtc_id);
+ /* Make sure vblank interrupt is still enabled if needed */
+ radeon_irq_set(rdev);
radeon_crtc_load_lut(crtc);
break;
case DRM_MODE_DPMS_STANDBY:
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index caa73de584a5..10191b935937 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -2999,6 +2999,49 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev,
int i;
struct si_dpm_quirk *p = si_dpm_quirk_list;
+ /* limit all SI kickers */
+ if (rdev->family == CHIP_PITCAIRN) {
+ if ((rdev->pdev->revision == 0x81) ||
+ (rdev->pdev->device == 0x6810) ||
+ (rdev->pdev->device == 0x6811) ||
+ (rdev->pdev->device == 0x6816) ||
+ (rdev->pdev->device == 0x6817) ||
+ (rdev->pdev->device == 0x6806))
+ max_mclk = 120000;
+ } else if (rdev->family == CHIP_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->device == 0x6604) ||
+ (rdev->pdev->device == 0x6605)) {
+ max_sclk = 75000;
+ max_mclk = 80000;
+ }
+ } else if (rdev->family == CHIP_HAINAN) {
+ if ((rdev->pdev->revision == 0x81) ||
+ (rdev->pdev->revision == 0x83) ||
+ (rdev->pdev->revision == 0xC3) ||
+ (rdev->pdev->device == 0x6664) ||
+ (rdev->pdev->device == 0x6665) ||
+ (rdev->pdev->device == 0x6667)) {
+ max_sclk = 75000;
+ max_mclk = 80000;
+ }
+ }
/* Apply dpm quirks */
while (p && p->chip_device != 0) {
if (rdev->pdev->vendor == p->chip_vendor &&
@@ -3011,10 +3054,6 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev,
}
++p;
}
- /* limit mclk on all R7 370 parts for stability */
- if (rdev->pdev->device == 0x6811 &&
- rdev->pdev->revision == 0x81)
- max_mclk = 120000;
if (rps->vce_active) {
rps->evclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].evclk;
@@ -4106,7 +4145,7 @@ static int si_populate_smc_voltage_tables(struct radeon_device *rdev,
&rdev->pm.dpm.dyn_state.phase_shedding_limits_table)) {
si_populate_smc_voltage_table(rdev, &si_pi->vddc_phase_shed_table, table);
- table->phaseMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC] =
+ table->phaseMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC_PHASE_SHEDDING] =
cpu_to_be32(si_pi->vddc_phase_shed_table.mask_low);
si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_phase_shedding_delay,
diff --git a/drivers/gpu/drm/radeon/sislands_smc.h b/drivers/gpu/drm/radeon/sislands_smc.h
index 3c779838d9ab..966e3a556011 100644
--- a/drivers/gpu/drm/radeon/sislands_smc.h
+++ b/drivers/gpu/drm/radeon/sislands_smc.h
@@ -194,6 +194,7 @@ typedef struct SISLANDS_SMC_SWSTATE SISLANDS_SMC_SWSTATE;
#define SISLANDS_SMC_VOLTAGEMASK_VDDC 0
#define SISLANDS_SMC_VOLTAGEMASK_MVDD 1
#define SISLANDS_SMC_VOLTAGEMASK_VDDCI 2
+#define SISLANDS_SMC_VOLTAGEMASK_VDDC_PHASE_SHEDDING 3
#define SISLANDS_SMC_VOLTAGEMASK_MAX 4
struct SISLANDS_SMC_VOLTAGEMASKTABLE
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index 4948c1529836..ecf15cf0c3fd 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -3830,14 +3830,14 @@ static void *vmw_execbuf_cmdbuf(struct vmw_private *dev_priv,
int ret;
*header = NULL;
- if (!dev_priv->cman || kernel_commands)
- return kernel_commands;
-
if (command_size > SVGA_CB_MAX_SIZE) {
DRM_ERROR("Command buffer is too large.\n");
return ERR_PTR(-EINVAL);
}
+ if (!dev_priv->cman || kernel_commands)
+ return kernel_commands;
+
/* If possible, add a little space for fencing. */
cmdbuf_size = command_size + 512;
cmdbuf_size = min_t(size_t, cmdbuf_size, SVGA_CB_MAX_SIZE);
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index 973884c2c5e7..87300096fbf1 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -161,6 +161,7 @@ static const struct {
{ adreno_is_a530, a530_efuse_speed_bin },
{ adreno_is_a505, a530_efuse_speed_bin },
{ adreno_is_a512, a530_efuse_speed_bin },
+ { adreno_is_a508, a530_efuse_speed_bin },
};
static void a5xx_check_features(struct adreno_device *adreno_dev)
@@ -1166,7 +1167,7 @@ static const struct kgsl_hwcg_reg a512_hwcg_regs[] = {
{A5XX_RBBM_CLOCK_CNTL_CCU0, 0x00022220},
{A5XX_RBBM_CLOCK_CNTL_CCU1, 0x00022220},
{A5XX_RBBM_CLOCK_CNTL_RAC, 0x05522222},
- {A5XX_RBBM_CLOCK_CNTL2_RAC, 0x00555555},
+ {A5XX_RBBM_CLOCK_CNTL2_RAC, 0x00505555},
{A5XX_RBBM_CLOCK_HYST_RB_CCU0, 0x04040404},
{A5XX_RBBM_CLOCK_HYST_RB_CCU1, 0x04040404},
{A5XX_RBBM_CLOCK_HYST_RAC, 0x07444044},
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 601e7a23101b..1de8e212a703 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -4753,6 +4753,7 @@ error_close_mmu:
error_pwrctrl_close:
kgsl_pwrctrl_close(device);
error:
+ kgsl_device_debugfs_close(device);
_unregister_device(device);
return status;
}
@@ -4782,6 +4783,7 @@ void kgsl_device_platform_remove(struct kgsl_device *device)
kgsl_pwrctrl_close(device);
+ kgsl_device_debugfs_close(device);
_unregister_device(device);
}
EXPORT_SYMBOL(kgsl_device_platform_remove);
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index 7758fc956055..37d92428f02c 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2008-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2008-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -87,6 +87,11 @@ void kgsl_device_debugfs_init(struct kgsl_device *device)
&pwr_log_fops);
}
+void kgsl_device_debugfs_close(struct kgsl_device *device)
+{
+ debugfs_remove_recursive(device->d_debugfs);
+}
+
struct type_entry {
int type;
const char *str;
diff --git a/drivers/gpu/msm/kgsl_debugfs.h b/drivers/gpu/msm/kgsl_debugfs.h
index 34875954bb8b..949aed81581c 100644
--- a/drivers/gpu/msm/kgsl_debugfs.h
+++ b/drivers/gpu/msm/kgsl_debugfs.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2008-2011,2013,2015 The Linux Foundation.
+/* Copyright (c) 2002,2008-2011,2013,2015,2017 The Linux Foundation.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -23,6 +23,7 @@ void kgsl_core_debugfs_init(void);
void kgsl_core_debugfs_close(void);
void kgsl_device_debugfs_init(struct kgsl_device *device);
+void kgsl_device_debugfs_close(struct kgsl_device *device);
extern struct dentry *kgsl_debugfs_dir;
static inline struct dentry *kgsl_get_debugfs_dir(void)
@@ -34,6 +35,7 @@ void kgsl_process_init_debugfs(struct kgsl_process_private *);
#else
static inline void kgsl_core_debugfs_init(void) { }
static inline void kgsl_device_debugfs_init(struct kgsl_device *device) { }
+static inline void kgsl_device_debugfs_close(struct kgsl_device *device) { }
static inline void kgsl_core_debugfs_close(void) { }
static inline struct dentry *kgsl_get_debugfs_dir(void) { return NULL; }
static inline void kgsl_process_init_debugfs(struct kgsl_process_private *priv)
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index fe6aa45901d0..e639e197de93 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -361,6 +361,26 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
if (new_level == old_level)
return;
+ if (pwr->gpu_cx_ipeak) {
+ unsigned int old_freq = pwr->pwrlevels[old_level].gpu_freq;
+ unsigned int new_freq = pwr->pwrlevels[new_level].gpu_freq;
+
+ /*
+ * Set Cx ipeak vote for GPU if it tries to cross
+ * threshold frequency.
+ */
+ if (old_freq < pwr->gpu_cx_ipeak_clk &&
+ new_freq >= pwr->gpu_cx_ipeak_clk) {
+ int ret = cx_ipeak_update(pwr->gpu_cx_ipeak, true);
+
+ if (ret) {
+ KGSL_PWR_ERR(device,
+ "cx_ipeak_update failed %d\n", ret);
+ return;
+ }
+ }
+ }
+
kgsl_pwrscale_update_stats(device);
/*
@@ -422,6 +442,24 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
/* Timestamp the frequency change */
device->pwrscale.freq_change_time = ktime_to_ms(ktime_get());
+
+ if (pwr->gpu_cx_ipeak) {
+ unsigned int old_freq = pwr->pwrlevels[old_level].gpu_freq;
+ unsigned int new_freq = pwr->pwrlevels[new_level].gpu_freq;
+
+ /*
+ * Reset Cx ipeak vote for GPU if it goes below
+ * threshold frequency.
+ */
+ if (old_freq >= pwr->gpu_cx_ipeak_clk &&
+ new_freq < pwr->gpu_cx_ipeak_clk) {
+ int ret = cx_ipeak_update(pwr->gpu_cx_ipeak, false);
+
+ if (ret)
+ KGSL_PWR_ERR(device,
+ "cx_ipeak_update failed %d\n", ret);
+ }
+ }
}
EXPORT_SYMBOL(kgsl_pwrctrl_pwrlevel_change);
@@ -2217,8 +2255,37 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
of_property_read_string(pdev->dev.of_node, "qcom,tsens-name",
&pwr->tsens_name);
+ /* Cx ipeak client support */
+ if (of_find_property(pdev->dev.of_node, "qcom,gpu-cx-ipeak", NULL)) {
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "qcom,gpu-cx-ipeak-clk", &pwr->gpu_cx_ipeak_clk)) {
+ pwr->gpu_cx_ipeak = cx_ipeak_register(pdev->dev.of_node,
+ "qcom,gpu-cx-ipeak");
+ } else {
+ KGSL_PWR_ERR(device, "failed to get gpu cxip clk\n");
+ result = -EINVAL;
+ goto error_cleanup_pwr_limit;
+ }
+
+ if (IS_ERR(pwr->gpu_cx_ipeak)) {
+ result = PTR_ERR(pwr->gpu_cx_ipeak);
+ KGSL_PWR_ERR(device,
+ "Failed to register Cx ipeak client %d\n",
+ result);
+ goto error_cleanup_pwr_limit;
+ }
+ }
return result;
+error_cleanup_pwr_limit:
+ pwr->power_flags = 0;
+
+ if (!IS_ERR_OR_NULL(pwr->sysfs_pwr_limit)) {
+ list_del(&pwr->sysfs_pwr_limit->node);
+ kfree(pwr->sysfs_pwr_limit);
+ pwr->sysfs_pwr_limit = NULL;
+ }
+ kfree(pwr->bus_ib);
error_cleanup_pcl:
_close_pcl(pwr);
error_cleanup_ocmem_pcl:
@@ -2238,6 +2305,8 @@ void kgsl_pwrctrl_close(struct kgsl_device *device)
KGSL_PWR_INFO(device, "close device %d\n", device->id);
+ cx_ipeak_unregister(pwr->gpu_cx_ipeak);
+
pwr->power_flags = 0;
if (!IS_ERR_OR_NULL(pwr->sysfs_pwr_limit)) {
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index 2de42d87bcbe..42f918b80fcd 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,6 +14,7 @@
#define __KGSL_PWRCTRL_H
#include <linux/pm_qos.h>
+#include <soc/qcom/cx_ipeak.h>
/*****************************************************************************
** power flags
@@ -153,6 +154,8 @@ struct kgsl_regulator {
* isense_clk_indx - index of isense clock, 0 if no isense
* isense_clk_on_level - isense clock rate is XO rate below this level.
* tsens_name - pointer to temperature sensor name of GPU temperature sensor
+ * gpu_cx_ipeak - pointer to cx ipeak client used by GPU
+ * gpu_cx_ipeak_clk - GPU threshold frequency to call cx ipeak driver API
*/
struct kgsl_pwrctrl {
@@ -206,6 +209,8 @@ struct kgsl_pwrctrl {
unsigned int gpu_bimc_int_clk_freq;
bool gpu_bimc_interface_enabled;
const char *tsens_name;
+ struct cx_ipeak_client *gpu_cx_ipeak;
+ unsigned int gpu_cx_ipeak_clk;
};
int kgsl_pwrctrl_init(struct kgsl_device *device);
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index 1caa673db6ff..7de43dd27ffe 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.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
@@ -156,8 +156,10 @@ static size_t snapshot_os(struct kgsl_device *device,
header->osid = KGSL_SNAPSHOT_OS_LINUX_V3;
/* Get the kernel build information */
- strlcpy(header->release, utsname()->release, sizeof(header->release));
- strlcpy(header->version, utsname()->version, sizeof(header->version));
+ strlcpy(header->release, init_utsname()->release,
+ sizeof(header->release));
+ strlcpy(header->version, init_utsname()->version,
+ sizeof(header->version));
/* Get the Unix time for the timestamp */
header->seconds = get_seconds();
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 909ab0176ef2..e37030624165 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -168,6 +168,7 @@
#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208
#define USB_DEVICE_ID_ATEN_CS682 0x2213
+#define USB_DEVICE_ID_ATEN_CS692 0x8021
#define USB_VENDOR_ID_ATMEL 0x03eb
#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index dc8e6adf95a4..6ca6ab00fa93 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -61,6 +61,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS682, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS692, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FIGHTERSTICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_COMBATSTICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE, HID_QUIRK_NOGET },
diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c
index 7994ec2e4151..41f5896224bd 100644
--- a/drivers/hv/hv_util.c
+++ b/drivers/hv/hv_util.c
@@ -283,10 +283,14 @@ static void heartbeat_onchannelcallback(void *context)
u8 *hbeat_txf_buf = util_heartbeat.recv_buffer;
struct icmsg_negotiate *negop = NULL;
- vmbus_recvpacket(channel, hbeat_txf_buf,
- PAGE_SIZE, &recvlen, &requestid);
+ while (1) {
+
+ vmbus_recvpacket(channel, hbeat_txf_buf,
+ PAGE_SIZE, &recvlen, &requestid);
+
+ if (!recvlen)
+ break;
- if (recvlen > 0) {
icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[
sizeof(struct vmbuspipe_hdr)];
diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c
index 827c03703128..a7f886961830 100644
--- a/drivers/hwmon/adt7411.c
+++ b/drivers/hwmon/adt7411.c
@@ -30,6 +30,7 @@
#define ADT7411_REG_CFG1 0x18
#define ADT7411_CFG1_START_MONITOR (1 << 0)
+#define ADT7411_CFG1_RESERVED_BIT3 (1 << 3)
#define ADT7411_REG_CFG2 0x19
#define ADT7411_CFG2_DISABLE_AVG (1 << 5)
@@ -296,8 +297,10 @@ static int adt7411_probe(struct i2c_client *client,
mutex_init(&data->device_lock);
mutex_init(&data->update_lock);
+ /* According to the datasheet, we must only write 1 to bit 3 */
ret = adt7411_modify_bit(client, ADT7411_REG_CFG1,
- ADT7411_CFG1_START_MONITOR, 1);
+ ADT7411_CFG1_RESERVED_BIT3
+ | ADT7411_CFG1_START_MONITOR, 1);
if (ret < 0)
return ret;
diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
index 3a11b061e5b0..596a36ed7dba 100644
--- a/drivers/hwtracing/coresight/coresight-tpdm.c
+++ b/drivers/hwtracing/coresight/coresight-tpdm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -3725,12 +3725,6 @@ static int tpdm_probe(struct platform_device *pdev)
clk_disable_unprepare(drvdata->clk);
- ret = tpdm_datasets_alloc(drvdata);
- if (ret)
- return ret;
-
- tpdm_init_default_data(drvdata);
-
drvdata->traceid = traceid++;
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 78fbee463628..65dbde778181 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -59,7 +59,6 @@ config I2C_CHARDEV
config I2C_MUX
tristate "I2C bus multiplexing support"
- depends on HAS_IOMEM
help
Say Y here if you want the I2C core to support the ability to
handle multiplexed I2C bus topologies, by presenting each
diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c
index 76e699f9ed97..eef3aa6007f1 100644
--- a/drivers/i2c/busses/i2c-eg20t.c
+++ b/drivers/i2c/busses/i2c-eg20t.c
@@ -773,13 +773,6 @@ static int pch_i2c_probe(struct pci_dev *pdev,
/* Set the number of I2C channel instance */
adap_info->ch_num = id->driver_data;
- ret = request_irq(pdev->irq, pch_i2c_handler, IRQF_SHARED,
- KBUILD_MODNAME, adap_info);
- if (ret) {
- pch_pci_err(pdev, "request_irq FAILED\n");
- goto err_request_irq;
- }
-
for (i = 0; i < adap_info->ch_num; i++) {
pch_adap = &adap_info->pch_data[i].pch_adapter;
adap_info->pch_i2c_suspended = false;
@@ -796,6 +789,17 @@ static int pch_i2c_probe(struct pci_dev *pdev,
adap_info->pch_data[i].pch_base_address = base_addr + 0x100 * i;
pch_adap->dev.parent = &pdev->dev;
+ }
+
+ ret = request_irq(pdev->irq, pch_i2c_handler, IRQF_SHARED,
+ KBUILD_MODNAME, adap_info);
+ if (ret) {
+ pch_pci_err(pdev, "request_irq FAILED\n");
+ goto err_request_irq;
+ }
+
+ for (i = 0; i < adap_info->ch_num; i++) {
+ pch_adap = &adap_info->pch_data[i].pch_adapter;
pch_i2c_init(&adap_info->pch_data[i]);
diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c
index 04b1b62f85c3..bf2a1dd7cf15 100644
--- a/drivers/i2c/busses/i2c-msm-v2.c
+++ b/drivers/i2c/busses/i2c-msm-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -50,6 +50,8 @@ static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl,
static int i2c_msm_pm_resume(struct device *dev);
static void i2c_msm_pm_suspend(struct device *dev);
static void i2c_msm_clk_path_init(struct i2c_msm_ctrl *ctrl);
+static void i2c_msm_pm_pinctrl_state(struct i2c_msm_ctrl *ctrl,
+ bool runtime_active);
/* string table for enum i2c_msm_xfer_mode_id */
const char * const i2c_msm_mode_str_tbl[] = {
@@ -2157,27 +2159,54 @@ static bool i2c_msm_xfer_next_buf(struct i2c_msm_ctrl *ctrl)
return true;
}
-static void i2c_msm_pm_clk_disable_unprepare(struct i2c_msm_ctrl *ctrl)
+static void i2c_msm_pm_clk_unprepare(struct i2c_msm_ctrl *ctrl)
{
- clk_disable_unprepare(ctrl->rsrcs.core_clk);
- clk_disable_unprepare(ctrl->rsrcs.iface_clk);
+ clk_unprepare(ctrl->rsrcs.core_clk);
+ clk_unprepare(ctrl->rsrcs.iface_clk);
}
-static int i2c_msm_pm_clk_prepare_enable(struct i2c_msm_ctrl *ctrl)
+static int i2c_msm_pm_clk_prepare(struct i2c_msm_ctrl *ctrl)
{
int ret;
- ret = clk_prepare_enable(ctrl->rsrcs.iface_clk);
+ ret = clk_prepare(ctrl->rsrcs.iface_clk);
if (ret) {
dev_err(ctrl->dev,
- "error on clk_prepare_enable(iface_clk):%d\n", ret);
+ "error on clk_prepare(iface_clk):%d\n", ret);
return ret;
}
- ret = clk_prepare_enable(ctrl->rsrcs.core_clk);
+ ret = clk_prepare(ctrl->rsrcs.core_clk);
+ if (ret) {
+ clk_unprepare(ctrl->rsrcs.iface_clk);
+ dev_err(ctrl->dev,
+ "error clk_prepare(core_clk):%d\n", ret);
+ }
+ return ret;
+}
+
+static void i2c_msm_pm_clk_disable(struct i2c_msm_ctrl *ctrl)
+{
+ clk_disable(ctrl->rsrcs.core_clk);
+ clk_disable(ctrl->rsrcs.iface_clk);
+}
+
+static int i2c_msm_pm_clk_enable(struct i2c_msm_ctrl *ctrl)
+{
+ int ret;
+
+ ret = clk_enable(ctrl->rsrcs.iface_clk);
+ if (ret) {
+ dev_err(ctrl->dev,
+ "error on clk_enable(iface_clk):%d\n", ret);
+ i2c_msm_pm_clk_unprepare(ctrl);
+ return ret;
+ }
+ ret = clk_enable(ctrl->rsrcs.core_clk);
if (ret) {
- clk_disable_unprepare(ctrl->rsrcs.iface_clk);
+ clk_disable(ctrl->rsrcs.iface_clk);
+ i2c_msm_pm_clk_unprepare(ctrl);
dev_err(ctrl->dev,
- "error clk_prepare_enable(core_clk):%d\n", ret);
+ "error clk_enable(core_clk):%d\n", ret);
}
return ret;
}
@@ -2198,6 +2227,7 @@ static int i2c_msm_pm_xfer_start(struct i2c_msm_ctrl *ctrl)
return -EIO;
}
+ i2c_msm_pm_pinctrl_state(ctrl, true);
pm_runtime_get_sync(ctrl->dev);
/*
* if runtime PM callback was not invoked (when both runtime-pm
@@ -2208,7 +2238,7 @@ static int i2c_msm_pm_xfer_start(struct i2c_msm_ctrl *ctrl)
i2c_msm_pm_resume(ctrl->dev);
}
- ret = i2c_msm_pm_clk_prepare_enable(ctrl);
+ ret = i2c_msm_pm_clk_enable(ctrl);
if (ret) {
mutex_unlock(&ctrl->xfer.mtx);
return ret;
@@ -2235,13 +2265,14 @@ static void i2c_msm_pm_xfer_end(struct i2c_msm_ctrl *ctrl)
if (ctrl->xfer.mode_id == I2C_MSM_XFER_MODE_DMA)
i2c_msm_dma_free_channels(ctrl);
- i2c_msm_pm_clk_disable_unprepare(ctrl);
+ i2c_msm_pm_clk_disable(ctrl);
if (!pm_runtime_enabled(ctrl->dev))
i2c_msm_pm_suspend(ctrl->dev);
pm_runtime_mark_last_busy(ctrl->dev);
pm_runtime_put_autosuspend(ctrl->dev);
+ i2c_msm_pm_pinctrl_state(ctrl, false);
mutex_unlock(&ctrl->xfer.mtx);
}
@@ -2663,7 +2694,7 @@ static void i2c_msm_pm_suspend(struct device *dev)
return;
}
i2c_msm_dbg(ctrl, MSM_DBG, "suspending...");
- i2c_msm_pm_pinctrl_state(ctrl, false);
+ i2c_msm_pm_clk_unprepare(ctrl);
i2c_msm_clk_path_unvote(ctrl);
/*
@@ -2690,7 +2721,7 @@ static int i2c_msm_pm_resume(struct device *dev)
i2c_msm_dbg(ctrl, MSM_DBG, "resuming...");
i2c_msm_clk_path_vote(ctrl);
- i2c_msm_pm_pinctrl_state(ctrl, true);
+ i2c_msm_pm_clk_prepare(ctrl);
ctrl->pwr_state = I2C_MSM_PM_RT_ACTIVE;
return 0;
}
@@ -2870,9 +2901,13 @@ static int i2c_msm_probe(struct platform_device *pdev)
/* vote for clock to enable reading the version number off the HW */
i2c_msm_clk_path_vote(ctrl);
- ret = i2c_msm_pm_clk_prepare_enable(ctrl);
+ ret = i2c_msm_pm_clk_prepare(ctrl);
+ if (ret)
+ goto clk_err;
+
+ ret = i2c_msm_pm_clk_enable(ctrl);
if (ret) {
- dev_err(ctrl->dev, "error in enabling clocks:%d\n", ret);
+ i2c_msm_pm_clk_unprepare(ctrl);
goto clk_err;
}
@@ -2884,7 +2919,8 @@ static int i2c_msm_probe(struct platform_device *pdev)
if (ret)
dev_err(ctrl->dev, "error error on qup software reset\n");
- i2c_msm_pm_clk_disable_unprepare(ctrl);
+ i2c_msm_pm_clk_disable(ctrl);
+ i2c_msm_pm_clk_unprepare(ctrl);
i2c_msm_clk_path_unvote(ctrl);
ret = i2c_msm_rsrcs_gpio_pinctrl_init(ctrl);
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index fdcbdab808e9..33b11563cde7 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -727,7 +727,8 @@ static int qup_i2c_pm_resume_runtime(struct device *device)
#ifdef CONFIG_PM_SLEEP
static int qup_i2c_suspend(struct device *device)
{
- qup_i2c_pm_suspend_runtime(device);
+ if (!pm_runtime_suspended(device))
+ return qup_i2c_pm_suspend_runtime(device);
return 0;
}
diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c
index 4233f5695352..3c38029e3fe9 100644
--- a/drivers/i2c/busses/i2c-xgene-slimpro.c
+++ b/drivers/i2c/busses/i2c-xgene-slimpro.c
@@ -105,7 +105,7 @@ struct slimpro_i2c_dev {
struct mbox_chan *mbox_chan;
struct mbox_client mbox_client;
struct completion rd_complete;
- u8 dma_buffer[I2C_SMBUS_BLOCK_MAX];
+ u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* dma_buffer[0] is used for length */
u32 *resp_msg;
};
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index ba8eb087f224..d625167357cc 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -1876,6 +1876,7 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
+ INIT_LIST_HEAD(&driver->clients);
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
@@ -1886,7 +1887,6 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
- INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver);
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index f06b0e24673b..af2a63cb4056 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -63,6 +63,7 @@ config I2C_MUX_PINCTRL
config I2C_MUX_REG
tristate "Register-based I2C multiplexer"
+ depends on HAS_IOMEM
help
If you say yes to this option, support will be included for a
register based I2C multiplexer. This driver provides access to
diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c
index 291c61a41c9a..fa24d5196615 100644
--- a/drivers/iio/accel/bmc150-accel-core.c
+++ b/drivers/iio/accel/bmc150-accel-core.c
@@ -68,6 +68,9 @@
#define BMC150_ACCEL_REG_PMU_BW 0x10
#define BMC150_ACCEL_DEF_BW 125
+#define BMC150_ACCEL_REG_RESET 0x14
+#define BMC150_ACCEL_RESET_VAL 0xB6
+
#define BMC150_ACCEL_REG_INT_MAP_0 0x19
#define BMC150_ACCEL_INT_MAP_0_BIT_SLOPE BIT(2)
@@ -1487,6 +1490,14 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
int ret, i;
unsigned int val;
+ /*
+ * Reset chip to get it in a known good state. A delay of 1.8ms after
+ * reset is required according to the data sheets of supported chips.
+ */
+ regmap_write(data->regmap, BMC150_ACCEL_REG_RESET,
+ BMC150_ACCEL_RESET_VAL);
+ usleep_range(1800, 2500);
+
ret = regmap_read(data->regmap, BMC150_ACCEL_REG_CHIP_ID, &val);
if (ret < 0) {
dev_err(data->dev,
diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c
index 3a9f106787d2..9d72d4bcf5e9 100644
--- a/drivers/iio/accel/kxsd9.c
+++ b/drivers/iio/accel/kxsd9.c
@@ -160,11 +160,13 @@ static int kxsd9_read_raw(struct iio_dev *indio_dev,
if (ret < 0)
goto error_ret;
*val = ret;
+ ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_SCALE:
ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
if (ret < 0)
goto error_ret;
+ *val = 0;
*val2 = kxsd9_micro_scales[ret & KXSD9_FS_MASK];
ret = IIO_VAL_INT_PLUS_MICRO;
break;
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 90135f496aaf..51cb6d8cffda 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -333,6 +333,7 @@ config QCOM_TADC
config ROCKCHIP_SARADC
tristate "Rockchip SARADC driver"
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
+ depends on RESET_CONTROLLER
help
Say yes here to build support for the SARADC found in SoCs from
Rockchip.
diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c
index 01d71588d752..ba82de25a797 100644
--- a/drivers/iio/adc/ad799x.c
+++ b/drivers/iio/adc/ad799x.c
@@ -533,6 +533,7 @@ static struct attribute_group ad799x_event_attrs_group = {
static const struct iio_info ad7991_info = {
.read_raw = &ad799x_read_raw,
.driver_module = THIS_MODULE,
+ .update_scan_mode = ad799x_update_scan_mode,
};
static const struct iio_info ad7993_4_7_8_noirq_info = {
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index 7b40925dd4ff..93986f0590ef 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -381,8 +381,8 @@ static irqreturn_t at91_adc_rl_interrupt(int irq, void *private)
st->ts_bufferedmeasure = false;
input_report_key(st->ts_input, BTN_TOUCH, 0);
input_sync(st->ts_input);
- } else if (status & AT91_ADC_EOC(3)) {
- /* Conversion finished */
+ } else if (status & AT91_ADC_EOC(3) && st->ts_input) {
+ /* Conversion finished and we've a touchscreen */
if (st->ts_bufferedmeasure) {
/*
* Last measurement is always discarded, since it can
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index 9c311c1e1ac7..dffff64b5989 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -21,6 +21,8 @@
#include <linux/of_device.h>
#include <linux/clk.h>
#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/reset.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
@@ -53,6 +55,7 @@ struct rockchip_saradc {
struct clk *clk;
struct completion completion;
struct regulator *vref;
+ struct reset_control *reset;
const struct rockchip_saradc_data *data;
u16 last_val;
};
@@ -171,6 +174,16 @@ static const struct of_device_id rockchip_saradc_match[] = {
};
MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
+/**
+ * Reset SARADC Controller.
+ */
+static void rockchip_saradc_reset_controller(struct reset_control *reset)
+{
+ reset_control_assert(reset);
+ usleep_range(10, 20);
+ reset_control_deassert(reset);
+}
+
static int rockchip_saradc_probe(struct platform_device *pdev)
{
struct rockchip_saradc *info = NULL;
@@ -199,6 +212,20 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
if (IS_ERR(info->regs))
return PTR_ERR(info->regs);
+ /*
+ * The reset should be an optional property, as it should work
+ * with old devicetrees as well
+ */
+ info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb");
+ if (IS_ERR(info->reset)) {
+ ret = PTR_ERR(info->reset);
+ if (ret != -ENOENT)
+ return ret;
+
+ dev_dbg(&pdev->dev, "no reset control found\n");
+ info->reset = NULL;
+ }
+
init_completion(&info->completion);
irq = platform_get_irq(pdev, 0);
@@ -233,6 +260,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
return PTR_ERR(info->vref);
}
+ if (info->reset)
+ rockchip_saradc_reset_controller(info->reset);
+
/*
* Use a default value for the converter clock.
* This may become user-configurable in the future.
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index c1e05532d437..0470fc843d4e 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -32,6 +32,7 @@
struct tiadc_device {
struct ti_tscadc_dev *mfd_tscadc;
+ struct mutex fifo1_lock; /* to protect fifo access */
int channels;
u8 channel_line[8];
u8 channel_step[8];
@@ -360,6 +361,7 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long mask)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ int ret = IIO_VAL_INT;
int i, map_val;
unsigned int fifo1count, read, stepid;
bool found = false;
@@ -373,13 +375,14 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
if (!step_en)
return -EINVAL;
+ mutex_lock(&adc_dev->fifo1_lock);
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
while (fifo1count--)
tiadc_readl(adc_dev, REG_FIFO1);
am335x_tsc_se_set_once(adc_dev->mfd_tscadc, step_en);
- timeout = jiffies + usecs_to_jiffies
+ timeout = jiffies + msecs_to_jiffies
(IDLE_TIMEOUT * adc_dev->channels);
/* Wait for Fifo threshold interrupt */
while (1) {
@@ -389,7 +392,8 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
if (time_after(jiffies, timeout)) {
am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
- return -EAGAIN;
+ ret = -EAGAIN;
+ goto err_unlock;
}
}
map_val = adc_dev->channel_step[chan->scan_index];
@@ -415,8 +419,11 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
if (found == false)
- return -EBUSY;
- return IIO_VAL_INT;
+ ret = -EBUSY;
+
+err_unlock:
+ mutex_unlock(&adc_dev->fifo1_lock);
+ return ret;
}
static const struct iio_info tiadc_info = {
@@ -485,6 +492,7 @@ static int tiadc_probe(struct platform_device *pdev)
tiadc_step_config(indio_dev);
tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD);
+ mutex_init(&adc_dev->fifo1_lock);
err = tiadc_channel_init(indio_dev, adc_dev->channels);
if (err < 0)
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
index e81f434760f4..b5beea53d6f6 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
@@ -30,34 +30,34 @@ static struct {
u32 usage_id;
int unit; /* 0 for default others from HID sensor spec */
int scale_val0; /* scale, whole number */
- int scale_val1; /* scale, fraction in micros */
+ int scale_val1; /* scale, fraction in nanos */
} unit_conversion[] = {
- {HID_USAGE_SENSOR_ACCEL_3D, 0, 9, 806650},
+ {HID_USAGE_SENSOR_ACCEL_3D, 0, 9, 806650000},
{HID_USAGE_SENSOR_ACCEL_3D,
HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0},
{HID_USAGE_SENSOR_ACCEL_3D,
- HID_USAGE_SENSOR_UNITS_G, 9, 806650},
+ HID_USAGE_SENSOR_UNITS_G, 9, 806650000},
- {HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453},
+ {HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453293},
{HID_USAGE_SENSOR_GYRO_3D,
HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND, 1, 0},
{HID_USAGE_SENSOR_GYRO_3D,
- HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND, 0, 17453},
+ HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND, 0, 17453293},
- {HID_USAGE_SENSOR_COMPASS_3D, 0, 0, 1000},
+ {HID_USAGE_SENSOR_COMPASS_3D, 0, 0, 1000000},
{HID_USAGE_SENSOR_COMPASS_3D, HID_USAGE_SENSOR_UNITS_GAUSS, 1, 0},
- {HID_USAGE_SENSOR_INCLINOMETER_3D, 0, 0, 17453},
+ {HID_USAGE_SENSOR_INCLINOMETER_3D, 0, 0, 17453293},
{HID_USAGE_SENSOR_INCLINOMETER_3D,
- HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453},
+ HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453293},
{HID_USAGE_SENSOR_INCLINOMETER_3D,
HID_USAGE_SENSOR_UNITS_RADIANS, 1, 0},
{HID_USAGE_SENSOR_ALS, 0, 1, 0},
{HID_USAGE_SENSOR_ALS, HID_USAGE_SENSOR_UNITS_LUX, 1, 0},
- {HID_USAGE_SENSOR_PRESSURE, 0, 100000, 0},
- {HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 1, 0},
+ {HID_USAGE_SENSOR_PRESSURE, 0, 100, 0},
+ {HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 0, 1000000},
};
static int pow_10(unsigned power)
@@ -266,15 +266,15 @@ EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value);
/*
* This fuction applies the unit exponent to the scale.
* For example:
- * 9.806650 ->exp:2-> val0[980]val1[665000]
- * 9.000806 ->exp:2-> val0[900]val1[80600]
- * 0.174535 ->exp:2-> val0[17]val1[453500]
- * 1.001745 ->exp:0-> val0[1]val1[1745]
- * 1.001745 ->exp:2-> val0[100]val1[174500]
- * 1.001745 ->exp:4-> val0[10017]val1[450000]
- * 9.806650 ->exp:-2-> val0[0]val1[98066]
+ * 9.806650000 ->exp:2-> val0[980]val1[665000000]
+ * 9.000806000 ->exp:2-> val0[900]val1[80600000]
+ * 0.174535293 ->exp:2-> val0[17]val1[453529300]
+ * 1.001745329 ->exp:0-> val0[1]val1[1745329]
+ * 1.001745329 ->exp:2-> val0[100]val1[174532900]
+ * 1.001745329 ->exp:4-> val0[10017]val1[453290000]
+ * 9.806650000 ->exp:-2-> val0[0]val1[98066500]
*/
-static void adjust_exponent_micro(int *val0, int *val1, int scale0,
+static void adjust_exponent_nano(int *val0, int *val1, int scale0,
int scale1, int exp)
{
int i;
@@ -285,32 +285,32 @@ static void adjust_exponent_micro(int *val0, int *val1, int scale0,
if (exp > 0) {
*val0 = scale0 * pow_10(exp);
res = 0;
- if (exp > 6) {
+ if (exp > 9) {
*val1 = 0;
return;
}
for (i = 0; i < exp; ++i) {
- x = scale1 / pow_10(5 - i);
+ x = scale1 / pow_10(8 - i);
res += (pow_10(exp - 1 - i) * x);
- scale1 = scale1 % pow_10(5 - i);
+ scale1 = scale1 % pow_10(8 - i);
}
*val0 += res;
*val1 = scale1 * pow_10(exp);
} else if (exp < 0) {
exp = abs(exp);
- if (exp > 6) {
+ if (exp > 9) {
*val0 = *val1 = 0;
return;
}
*val0 = scale0 / pow_10(exp);
rem = scale0 % pow_10(exp);
res = 0;
- for (i = 0; i < (6 - exp); ++i) {
- x = scale1 / pow_10(5 - i);
- res += (pow_10(5 - exp - i) * x);
- scale1 = scale1 % pow_10(5 - i);
+ for (i = 0; i < (9 - exp); ++i) {
+ x = scale1 / pow_10(8 - i);
+ res += (pow_10(8 - exp - i) * x);
+ scale1 = scale1 % pow_10(8 - i);
}
- *val1 = rem * pow_10(6 - exp) + res;
+ *val1 = rem * pow_10(9 - exp) + res;
} else {
*val0 = scale0;
*val1 = scale1;
@@ -332,14 +332,14 @@ int hid_sensor_format_scale(u32 usage_id,
unit_conversion[i].unit == attr_info->units) {
exp = hid_sensor_convert_exponent(
attr_info->unit_expo);
- adjust_exponent_micro(val0, val1,
+ adjust_exponent_nano(val0, val1,
unit_conversion[i].scale_val0,
unit_conversion[i].scale_val1, exp);
break;
}
}
- return IIO_VAL_INT_PLUS_MICRO;
+ return IIO_VAL_INT_PLUS_NANO;
}
EXPORT_SYMBOL(hid_sensor_format_scale);
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 7afd226a3321..32bb036069eb 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -110,7 +110,7 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
DEFINE_WAIT_FUNC(wait, woken_wake_function);
size_t datum_size;
size_t to_wait;
- int ret;
+ int ret = 0;
if (!indio_dev->info)
return -ENODEV;
@@ -153,7 +153,7 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
ret = rb->access->read_first_n(rb, n, buf);
if (ret == 0 && (filp->f_flags & O_NONBLOCK))
ret = -EAGAIN;
- } while (ret == 0);
+ } while (ret == 0);
remove_wait_queue(&rb->pollq, &wait);
return ret;
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 159ede61f793..131b434af994 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -433,23 +433,21 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
scale_db = true;
case IIO_VAL_INT_PLUS_MICRO:
if (vals[1] < 0)
- return sprintf(buf, "-%ld.%06u%s\n", abs(vals[0]),
- -vals[1],
- scale_db ? " dB" : "");
+ return sprintf(buf, "-%d.%06u%s\n", abs(vals[0]),
+ -vals[1], scale_db ? " dB" : "");
else
return sprintf(buf, "%d.%06u%s\n", vals[0], vals[1],
scale_db ? " dB" : "");
case IIO_VAL_INT_PLUS_NANO:
if (vals[1] < 0)
- return sprintf(buf, "-%ld.%09u\n", abs(vals[0]),
- -vals[1]);
+ return sprintf(buf, "-%d.%09u\n", abs(vals[0]),
+ -vals[1]);
else
return sprintf(buf, "%d.%09u\n", vals[0], vals[1]);
case IIO_VAL_FRACTIONAL:
tmp = div_s64((s64)vals[0] * 1000000000LL, vals[1]);
- vals[1] = do_div(tmp, 1000000000LL);
- vals[0] = tmp;
- return sprintf(buf, "%d.%09u\n", vals[0], vals[1]);
+ vals[0] = (int)div_s64_rem(tmp, 1000000000, &vals[1]);
+ return sprintf(buf, "%d.%09u\n", vals[0], abs(vals[1]));
case IIO_VAL_FRACTIONAL_LOG2:
tmp = (s64)vals[0] * 1000000000LL >> vals[1];
vals[1] = do_div(tmp, 1000000000LL);
diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
index b98b9d94d184..a97e802ca523 100644
--- a/drivers/iio/orientation/hid-sensor-rotation.c
+++ b/drivers/iio/orientation/hid-sensor-rotation.c
@@ -335,6 +335,7 @@ static struct platform_driver hid_dev_rot_platform_driver = {
.id_table = hid_dev_rot_ids,
.driver = {
.name = KBUILD_MODNAME,
+ .pm = &hid_sensor_pm_ops,
},
.probe = hid_dev_rot_probe,
.remove = hid_dev_rot_remove,
diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c
index e2f926cdcad2..a0aedda7dfd7 100644
--- a/drivers/iio/proximity/as3935.c
+++ b/drivers/iio/proximity/as3935.c
@@ -392,7 +392,7 @@ static int as3935_probe(struct spi_device *spi)
return ret;
}
- ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time,
&as3935_trigger_handler, NULL);
if (ret) {
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index 4d8e7f18a9af..941cd9b83941 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -80,6 +80,8 @@ static struct ib_cm {
__be32 random_id_operand;
struct list_head timewait_list;
struct workqueue_struct *wq;
+ /* Sync on cm change port state */
+ spinlock_t state_lock;
} cm;
/* Counter indexes ordered by attribute ID */
@@ -161,6 +163,8 @@ struct cm_port {
struct ib_mad_agent *mad_agent;
struct kobject port_obj;
u8 port_num;
+ struct list_head cm_priv_prim_list;
+ struct list_head cm_priv_altr_list;
struct cm_counter_group counter_group[CM_COUNTER_GROUPS];
};
@@ -241,6 +245,12 @@ struct cm_id_private {
u8 service_timeout;
u8 target_ack_delay;
+ struct list_head prim_list;
+ struct list_head altr_list;
+ /* Indicates that the send port mad is registered and av is set */
+ int prim_send_port_not_ready;
+ int altr_send_port_not_ready;
+
struct list_head work_list;
atomic_t work_count;
};
@@ -259,20 +269,47 @@ static int cm_alloc_msg(struct cm_id_private *cm_id_priv,
struct ib_mad_agent *mad_agent;
struct ib_mad_send_buf *m;
struct ib_ah *ah;
+ struct cm_av *av;
+ unsigned long flags, flags2;
+ int ret = 0;
+ /* don't let the port to be released till the agent is down */
+ spin_lock_irqsave(&cm.state_lock, flags2);
+ spin_lock_irqsave(&cm.lock, flags);
+ if (!cm_id_priv->prim_send_port_not_ready)
+ av = &cm_id_priv->av;
+ else if (!cm_id_priv->altr_send_port_not_ready &&
+ (cm_id_priv->alt_av.port))
+ av = &cm_id_priv->alt_av;
+ else {
+ pr_info("%s: not valid CM id\n", __func__);
+ ret = -ENODEV;
+ spin_unlock_irqrestore(&cm.lock, flags);
+ goto out;
+ }
+ spin_unlock_irqrestore(&cm.lock, flags);
+ /* Make sure the port haven't released the mad yet */
mad_agent = cm_id_priv->av.port->mad_agent;
- ah = ib_create_ah(mad_agent->qp->pd, &cm_id_priv->av.ah_attr);
- if (IS_ERR(ah))
- return PTR_ERR(ah);
+ if (!mad_agent) {
+ pr_info("%s: not a valid MAD agent\n", __func__);
+ ret = -ENODEV;
+ goto out;
+ }
+ ah = ib_create_ah(mad_agent->qp->pd, &av->ah_attr);
+ if (IS_ERR(ah)) {
+ ret = PTR_ERR(ah);
+ goto out;
+ }
m = ib_create_send_mad(mad_agent, cm_id_priv->id.remote_cm_qpn,
- cm_id_priv->av.pkey_index,
+ av->pkey_index,
0, IB_MGMT_MAD_HDR, IB_MGMT_MAD_DATA,
GFP_ATOMIC,
IB_MGMT_BASE_VERSION);
if (IS_ERR(m)) {
ib_destroy_ah(ah);
- return PTR_ERR(m);
+ ret = PTR_ERR(m);
+ goto out;
}
/* Timeout set by caller if response is expected. */
@@ -282,7 +319,10 @@ static int cm_alloc_msg(struct cm_id_private *cm_id_priv,
atomic_inc(&cm_id_priv->refcount);
m->context[0] = cm_id_priv;
*msg = m;
- return 0;
+
+out:
+ spin_unlock_irqrestore(&cm.state_lock, flags2);
+ return ret;
}
static int cm_alloc_response_msg(struct cm_port *port,
@@ -352,7 +392,8 @@ static void cm_init_av_for_response(struct cm_port *port, struct ib_wc *wc,
grh, &av->ah_attr);
}
-static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av)
+static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av,
+ struct cm_id_private *cm_id_priv)
{
struct cm_device *cm_dev;
struct cm_port *port = NULL;
@@ -387,7 +428,17 @@ static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av)
&av->ah_attr);
av->timeout = path->packet_life_time + 1;
- return 0;
+ spin_lock_irqsave(&cm.lock, flags);
+ if (&cm_id_priv->av == av)
+ list_add_tail(&cm_id_priv->prim_list, &port->cm_priv_prim_list);
+ else if (&cm_id_priv->alt_av == av)
+ list_add_tail(&cm_id_priv->altr_list, &port->cm_priv_altr_list);
+ else
+ ret = -EINVAL;
+
+ spin_unlock_irqrestore(&cm.lock, flags);
+
+ return ret;
}
static int cm_alloc_id(struct cm_id_private *cm_id_priv)
@@ -677,6 +728,8 @@ struct ib_cm_id *ib_create_cm_id(struct ib_device *device,
spin_lock_init(&cm_id_priv->lock);
init_completion(&cm_id_priv->comp);
INIT_LIST_HEAD(&cm_id_priv->work_list);
+ INIT_LIST_HEAD(&cm_id_priv->prim_list);
+ INIT_LIST_HEAD(&cm_id_priv->altr_list);
atomic_set(&cm_id_priv->work_count, -1);
atomic_set(&cm_id_priv->refcount, 1);
return &cm_id_priv->id;
@@ -892,6 +945,15 @@ retest:
break;
}
+ spin_lock_irq(&cm.lock);
+ if (!list_empty(&cm_id_priv->altr_list) &&
+ (!cm_id_priv->altr_send_port_not_ready))
+ list_del(&cm_id_priv->altr_list);
+ if (!list_empty(&cm_id_priv->prim_list) &&
+ (!cm_id_priv->prim_send_port_not_ready))
+ list_del(&cm_id_priv->prim_list);
+ spin_unlock_irq(&cm.lock);
+
cm_free_id(cm_id->local_id);
cm_deref_id(cm_id_priv);
wait_for_completion(&cm_id_priv->comp);
@@ -1192,12 +1254,13 @@ int ib_send_cm_req(struct ib_cm_id *cm_id,
goto out;
}
- ret = cm_init_av_by_path(param->primary_path, &cm_id_priv->av);
+ ret = cm_init_av_by_path(param->primary_path, &cm_id_priv->av,
+ cm_id_priv);
if (ret)
goto error1;
if (param->alternate_path) {
ret = cm_init_av_by_path(param->alternate_path,
- &cm_id_priv->alt_av);
+ &cm_id_priv->alt_av, cm_id_priv);
if (ret)
goto error1;
}
@@ -1639,7 +1702,8 @@ static int cm_req_handler(struct cm_work *work)
cm_format_paths_from_req(req_msg, &work->path[0], &work->path[1]);
memcpy(work->path[0].dmac, cm_id_priv->av.ah_attr.dmac, ETH_ALEN);
- ret = cm_init_av_by_path(&work->path[0], &cm_id_priv->av);
+ ret = cm_init_av_by_path(&work->path[0], &cm_id_priv->av,
+ cm_id_priv);
if (ret) {
ib_get_cached_gid(work->port->cm_dev->ib_device,
work->port->port_num, 0, &work->path[0].sgid,
@@ -1650,7 +1714,8 @@ static int cm_req_handler(struct cm_work *work)
goto rejected;
}
if (req_msg->alt_local_lid) {
- ret = cm_init_av_by_path(&work->path[1], &cm_id_priv->alt_av);
+ ret = cm_init_av_by_path(&work->path[1], &cm_id_priv->alt_av,
+ cm_id_priv);
if (ret) {
ib_send_cm_rej(cm_id, IB_CM_REJ_INVALID_ALT_GID,
&work->path[0].sgid,
@@ -2705,7 +2770,8 @@ int ib_send_cm_lap(struct ib_cm_id *cm_id,
goto out;
}
- ret = cm_init_av_by_path(alternate_path, &cm_id_priv->alt_av);
+ ret = cm_init_av_by_path(alternate_path, &cm_id_priv->alt_av,
+ cm_id_priv);
if (ret)
goto out;
cm_id_priv->alt_av.timeout =
@@ -2817,7 +2883,8 @@ static int cm_lap_handler(struct cm_work *work)
cm_init_av_for_response(work->port, work->mad_recv_wc->wc,
work->mad_recv_wc->recv_buf.grh,
&cm_id_priv->av);
- cm_init_av_by_path(param->alternate_path, &cm_id_priv->alt_av);
+ cm_init_av_by_path(param->alternate_path, &cm_id_priv->alt_av,
+ cm_id_priv);
ret = atomic_inc_and_test(&cm_id_priv->work_count);
if (!ret)
list_add_tail(&work->list, &cm_id_priv->work_list);
@@ -3009,7 +3076,7 @@ int ib_send_cm_sidr_req(struct ib_cm_id *cm_id,
return -EINVAL;
cm_id_priv = container_of(cm_id, struct cm_id_private, id);
- ret = cm_init_av_by_path(param->path, &cm_id_priv->av);
+ ret = cm_init_av_by_path(param->path, &cm_id_priv->av, cm_id_priv);
if (ret)
goto out;
@@ -3446,7 +3513,9 @@ out:
static int cm_migrate(struct ib_cm_id *cm_id)
{
struct cm_id_private *cm_id_priv;
+ struct cm_av tmp_av;
unsigned long flags;
+ int tmp_send_port_not_ready;
int ret = 0;
cm_id_priv = container_of(cm_id, struct cm_id_private, id);
@@ -3455,7 +3524,14 @@ static int cm_migrate(struct ib_cm_id *cm_id)
(cm_id->lap_state == IB_CM_LAP_UNINIT ||
cm_id->lap_state == IB_CM_LAP_IDLE)) {
cm_id->lap_state = IB_CM_LAP_IDLE;
+ /* Swap address vector */
+ tmp_av = cm_id_priv->av;
cm_id_priv->av = cm_id_priv->alt_av;
+ cm_id_priv->alt_av = tmp_av;
+ /* Swap port send ready state */
+ tmp_send_port_not_ready = cm_id_priv->prim_send_port_not_ready;
+ cm_id_priv->prim_send_port_not_ready = cm_id_priv->altr_send_port_not_ready;
+ cm_id_priv->altr_send_port_not_ready = tmp_send_port_not_ready;
} else
ret = -EINVAL;
spin_unlock_irqrestore(&cm_id_priv->lock, flags);
@@ -3875,6 +3951,9 @@ static void cm_add_one(struct ib_device *ib_device)
port->cm_dev = cm_dev;
port->port_num = i;
+ INIT_LIST_HEAD(&port->cm_priv_prim_list);
+ INIT_LIST_HEAD(&port->cm_priv_altr_list);
+
ret = cm_create_port_fs(port);
if (ret)
goto error1;
@@ -3932,6 +4011,8 @@ static void cm_remove_one(struct ib_device *ib_device, void *client_data)
{
struct cm_device *cm_dev = client_data;
struct cm_port *port;
+ struct cm_id_private *cm_id_priv;
+ struct ib_mad_agent *cur_mad_agent;
struct ib_port_modify port_modify = {
.clr_port_cap_mask = IB_PORT_CM_SUP
};
@@ -3955,15 +4036,27 @@ static void cm_remove_one(struct ib_device *ib_device, void *client_data)
port = cm_dev->port[i-1];
ib_modify_port(ib_device, port->port_num, 0, &port_modify);
+ /* Mark all the cm_id's as not valid */
+ spin_lock_irq(&cm.lock);
+ list_for_each_entry(cm_id_priv, &port->cm_priv_altr_list, altr_list)
+ cm_id_priv->altr_send_port_not_ready = 1;
+ list_for_each_entry(cm_id_priv, &port->cm_priv_prim_list, prim_list)
+ cm_id_priv->prim_send_port_not_ready = 1;
+ spin_unlock_irq(&cm.lock);
/*
* We flush the queue here after the going_down set, this
* verify that no new works will be queued in the recv handler,
* after that we can call the unregister_mad_agent
*/
flush_workqueue(cm.wq);
- ib_unregister_mad_agent(port->mad_agent);
+ spin_lock_irq(&cm.state_lock);
+ cur_mad_agent = port->mad_agent;
+ port->mad_agent = NULL;
+ spin_unlock_irq(&cm.state_lock);
+ ib_unregister_mad_agent(cur_mad_agent);
cm_remove_port_fs(port);
}
+
device_unregister(cm_dev->device);
kfree(cm_dev);
}
@@ -3976,6 +4069,7 @@ static int __init ib_cm_init(void)
INIT_LIST_HEAD(&cm.device_list);
rwlock_init(&cm.device_lock);
spin_lock_init(&cm.lock);
+ spin_lock_init(&cm.state_lock);
cm.listen_service_table = RB_ROOT;
cm.listen_service_id = be64_to_cpu(IB_CM_ASSIGN_SERVICE_ID);
cm.remote_id_table = RB_ROOT;
diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c
index bb6685fb08c6..6aa648cb5381 100644
--- a/drivers/infiniband/core/multicast.c
+++ b/drivers/infiniband/core/multicast.c
@@ -106,7 +106,6 @@ struct mcast_group {
atomic_t refcount;
enum mcast_group_state state;
struct ib_sa_query *query;
- int query_id;
u16 pkey_index;
u8 leave_state;
int retries;
@@ -339,11 +338,7 @@ static int send_join(struct mcast_group *group, struct mcast_member *member)
member->multicast.comp_mask,
3000, GFP_KERNEL, join_handler, group,
&group->query);
- if (ret >= 0) {
- group->query_id = ret;
- ret = 0;
- }
- return ret;
+ return (ret > 0) ? 0 : ret;
}
static int send_leave(struct mcast_group *group, u8 leave_state)
@@ -363,11 +358,7 @@ static int send_leave(struct mcast_group *group, u8 leave_state)
IB_SA_MCMEMBER_REC_JOIN_STATE,
3000, GFP_KERNEL, leave_handler,
group, &group->query);
- if (ret >= 0) {
- group->query_id = ret;
- ret = 0;
- }
- return ret;
+ return (ret > 0) ? 0 : ret;
}
static void join_group(struct mcast_group *group, struct mcast_member *member,
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index 38acb3cfc545..04f3c0db9126 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -175,7 +175,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
cur_base = addr & PAGE_MASK;
- if (npages == 0) {
+ if (npages == 0 || npages > UINT_MAX) {
ret = -EINVAL;
goto out;
}
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h
index 94bbd8c155fc..a2d19d136099 100644
--- a/drivers/infiniband/core/uverbs.h
+++ b/drivers/infiniband/core/uverbs.h
@@ -116,6 +116,7 @@ struct ib_uverbs_event_file {
struct ib_uverbs_file {
struct kref ref;
struct mutex mutex;
+ struct mutex cleanup_mutex; /* protect cleanup */
struct ib_uverbs_device *device;
struct ib_ucontext *ucontext;
struct ib_event_handler event_handler;
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index 24f3ca2c4ad7..7becef27cbbe 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -244,12 +244,9 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
container_of(uobj, struct ib_uqp_object, uevent.uobject);
idr_remove_uobj(&ib_uverbs_qp_idr, uobj);
- if (qp != qp->real_qp) {
- ib_close_qp(qp);
- } else {
+ if (qp == qp->real_qp)
ib_uverbs_detach_umcast(qp, uqp);
- ib_destroy_qp(qp);
- }
+ ib_destroy_qp(qp);
ib_uverbs_release_uevent(file, &uqp->uevent);
kfree(uqp);
}
@@ -922,6 +919,7 @@ static int ib_uverbs_open(struct inode *inode, struct file *filp)
file->async_file = NULL;
kref_init(&file->ref);
mutex_init(&file->mutex);
+ mutex_init(&file->cleanup_mutex);
filp->private_data = file;
kobject_get(&dev->kobj);
@@ -947,18 +945,20 @@ static int ib_uverbs_close(struct inode *inode, struct file *filp)
{
struct ib_uverbs_file *file = filp->private_data;
struct ib_uverbs_device *dev = file->device;
- struct ib_ucontext *ucontext = NULL;
+
+ mutex_lock(&file->cleanup_mutex);
+ if (file->ucontext) {
+ ib_uverbs_cleanup_ucontext(file, file->ucontext);
+ file->ucontext = NULL;
+ }
+ mutex_unlock(&file->cleanup_mutex);
mutex_lock(&file->device->lists_mutex);
- ucontext = file->ucontext;
- file->ucontext = NULL;
if (!file->is_closed) {
list_del(&file->list);
file->is_closed = 1;
}
mutex_unlock(&file->device->lists_mutex);
- if (ucontext)
- ib_uverbs_cleanup_ucontext(file, ucontext);
if (file->async_file)
kref_put(&file->async_file->ref, ib_uverbs_release_event_file);
@@ -1172,22 +1172,30 @@ static void ib_uverbs_free_hw_resources(struct ib_uverbs_device *uverbs_dev,
mutex_lock(&uverbs_dev->lists_mutex);
while (!list_empty(&uverbs_dev->uverbs_file_list)) {
struct ib_ucontext *ucontext;
-
file = list_first_entry(&uverbs_dev->uverbs_file_list,
struct ib_uverbs_file, list);
file->is_closed = 1;
- ucontext = file->ucontext;
list_del(&file->list);
- file->ucontext = NULL;
kref_get(&file->ref);
mutex_unlock(&uverbs_dev->lists_mutex);
- /* We must release the mutex before going ahead and calling
- * disassociate_ucontext. disassociate_ucontext might end up
- * indirectly calling uverbs_close, for example due to freeing
- * the resources (e.g mmput).
- */
+
ib_uverbs_event_handler(&file->event_handler, &event);
+
+ mutex_lock(&file->cleanup_mutex);
+ ucontext = file->ucontext;
+ file->ucontext = NULL;
+ mutex_unlock(&file->cleanup_mutex);
+
+ /* At this point ib_uverbs_close cannot be running
+ * ib_uverbs_cleanup_ucontext
+ */
if (ucontext) {
+ /* We must release the mutex before going ahead and
+ * calling disassociate_ucontext. disassociate_ucontext
+ * might end up indirectly calling uverbs_close,
+ * for example due to freeing the resources
+ * (e.g mmput).
+ */
ib_dev->disassociate_ucontext(ucontext);
ib_uverbs_cleanup_ucontext(file, ucontext);
}
diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c
index 06da56bda201..c007c766c61e 100644
--- a/drivers/infiniband/hw/mlx4/ah.c
+++ b/drivers/infiniband/hw/mlx4/ah.c
@@ -102,7 +102,10 @@ static struct ib_ah *create_iboe_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr
if (vlan_tag < 0x1000)
vlan_tag |= (ah_attr->sl & 7) << 13;
ah->av.eth.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24));
- ah->av.eth.gid_index = mlx4_ib_gid_index_to_real_index(ibdev, ah_attr->port_num, ah_attr->grh.sgid_index);
+ ret = mlx4_ib_gid_index_to_real_index(ibdev, ah_attr->port_num, ah_attr->grh.sgid_index);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ ah->av.eth.gid_index = ret;
ah->av.eth.vlan = cpu_to_be16(vlan_tag);
if (ah_attr->static_rate) {
ah->av.eth.stat_rate = ah_attr->static_rate + MLX4_STAT_RATE_OFFSET;
diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c
index b88fc8f5ab18..57e1a08925d9 100644
--- a/drivers/infiniband/hw/mlx4/cq.c
+++ b/drivers/infiniband/hw/mlx4/cq.c
@@ -253,11 +253,14 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
if (context)
if (ib_copy_to_udata(udata, &cq->mcq.cqn, sizeof (__u32))) {
err = -EFAULT;
- goto err_dbmap;
+ goto err_cq_free;
}
return &cq->ibcq;
+err_cq_free:
+ mlx4_cq_free(dev->dev, &cq->mcq);
+
err_dbmap:
if (context)
mlx4_ib_db_unmap_user(to_mucontext(context), &cq->db);
diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c
index 05179f47bbde..d862b9b7910e 100644
--- a/drivers/infiniband/hw/mlx4/mad.c
+++ b/drivers/infiniband/hw/mlx4/mad.c
@@ -1080,6 +1080,27 @@ void handle_port_mgmt_change_event(struct work_struct *work)
/* Generate GUID changed event */
if (changed_attr & MLX4_EQ_PORT_INFO_GID_PFX_CHANGE_MASK) {
+ if (mlx4_is_master(dev->dev)) {
+ union ib_gid gid;
+ int err = 0;
+
+ if (!eqe->event.port_mgmt_change.params.port_info.gid_prefix)
+ err = __mlx4_ib_query_gid(&dev->ib_dev, port, 0, &gid, 1);
+ else
+ gid.global.subnet_prefix =
+ eqe->event.port_mgmt_change.params.port_info.gid_prefix;
+ if (err) {
+ pr_warn("Could not change QP1 subnet prefix for port %d: query_gid error (%d)\n",
+ port, err);
+ } else {
+ pr_debug("Changing QP1 subnet prefix for port %d. old=0x%llx. new=0x%llx\n",
+ port,
+ (u64)atomic64_read(&dev->sriov.demux[port - 1].subnet_prefix),
+ be64_to_cpu(gid.global.subnet_prefix));
+ atomic64_set(&dev->sriov.demux[port - 1].subnet_prefix,
+ be64_to_cpu(gid.global.subnet_prefix));
+ }
+ }
mlx4_ib_dispatch_event(dev, port, IB_EVENT_GID_CHANGE);
/*if master, notify all slaves*/
if (mlx4_is_master(dev->dev))
@@ -2154,6 +2175,8 @@ int mlx4_ib_init_sriov(struct mlx4_ib_dev *dev)
if (err)
goto demux_err;
dev->sriov.demux[i].guid_cache[0] = gid.global.interface_id;
+ atomic64_set(&dev->sriov.demux[i].subnet_prefix,
+ be64_to_cpu(gid.global.subnet_prefix));
err = alloc_pv_object(dev, mlx4_master_func_num(dev->dev), i + 1,
&dev->sriov.sqps[i]);
if (err)
diff --git a/drivers/infiniband/hw/mlx4/mcg.c b/drivers/infiniband/hw/mlx4/mcg.c
index 99451d887266..36ec8aa048aa 100644
--- a/drivers/infiniband/hw/mlx4/mcg.c
+++ b/drivers/infiniband/hw/mlx4/mcg.c
@@ -489,7 +489,7 @@ static u8 get_leave_state(struct mcast_group *group)
if (!group->members[i])
leave_state |= (1 << i);
- return leave_state & (group->rec.scope_join_state & 7);
+ return leave_state & (group->rec.scope_join_state & 0xf);
}
static int join_group(struct mcast_group *group, int slave, u8 join_mask)
@@ -564,8 +564,8 @@ static void mlx4_ib_mcg_timeout_handler(struct work_struct *work)
} else
mcg_warn_group(group, "DRIVER BUG\n");
} else if (group->state == MCAST_LEAVE_SENT) {
- if (group->rec.scope_join_state & 7)
- group->rec.scope_join_state &= 0xf8;
+ if (group->rec.scope_join_state & 0xf)
+ group->rec.scope_join_state &= 0xf0;
group->state = MCAST_IDLE;
mutex_unlock(&group->lock);
if (release_group(group, 1))
@@ -605,7 +605,7 @@ static int handle_leave_req(struct mcast_group *group, u8 leave_mask,
static int handle_join_req(struct mcast_group *group, u8 join_mask,
struct mcast_req *req)
{
- u8 group_join_state = group->rec.scope_join_state & 7;
+ u8 group_join_state = group->rec.scope_join_state & 0xf;
int ref = 0;
u16 status;
struct ib_sa_mcmember_data *sa_data = (struct ib_sa_mcmember_data *)req->sa_mad.data;
@@ -690,8 +690,8 @@ static void mlx4_ib_mcg_work_handler(struct work_struct *work)
u8 cur_join_state;
resp_join_state = ((struct ib_sa_mcmember_data *)
- group->response_sa_mad.data)->scope_join_state & 7;
- cur_join_state = group->rec.scope_join_state & 7;
+ group->response_sa_mad.data)->scope_join_state & 0xf;
+ cur_join_state = group->rec.scope_join_state & 0xf;
if (method == IB_MGMT_METHOD_GET_RESP) {
/* successfull join */
@@ -710,7 +710,7 @@ process_requests:
req = list_first_entry(&group->pending_list, struct mcast_req,
group_list);
sa_data = (struct ib_sa_mcmember_data *)req->sa_mad.data;
- req_join_state = sa_data->scope_join_state & 0x7;
+ req_join_state = sa_data->scope_join_state & 0xf;
/* For a leave request, we will immediately answer the VF, and
* update our internal counters. The actual leave will be sent
diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h
index 1caa11edac03..78f29e91653a 100644
--- a/drivers/infiniband/hw/mlx4/mlx4_ib.h
+++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h
@@ -441,7 +441,7 @@ struct mlx4_ib_demux_ctx {
struct workqueue_struct *wq;
struct workqueue_struct *ud_wq;
spinlock_t ud_lock;
- __be64 subnet_prefix;
+ atomic64_t subnet_prefix;
__be64 guid_cache[128];
struct mlx4_ib_dev *dev;
/* the following lock protects both mcg_table and mcg_mgid0_list */
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index ea1e2ddaddf5..f350f2d61c15 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -2331,24 +2331,27 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_ud_wr *wr,
sqp->ud_header.grh.flow_label =
ah->av.ib.sl_tclass_flowlabel & cpu_to_be32(0xfffff);
sqp->ud_header.grh.hop_limit = ah->av.ib.hop_limit;
- if (is_eth)
+ if (is_eth) {
memcpy(sqp->ud_header.grh.source_gid.raw, sgid.raw, 16);
- else {
- if (mlx4_is_mfunc(to_mdev(ib_dev)->dev)) {
- /* When multi-function is enabled, the ib_core gid
- * indexes don't necessarily match the hw ones, so
- * we must use our own cache */
- sqp->ud_header.grh.source_gid.global.subnet_prefix =
- to_mdev(ib_dev)->sriov.demux[sqp->qp.port - 1].
- subnet_prefix;
- sqp->ud_header.grh.source_gid.global.interface_id =
- to_mdev(ib_dev)->sriov.demux[sqp->qp.port - 1].
- guid_cache[ah->av.ib.gid_index];
- } else
- ib_get_cached_gid(ib_dev,
- be32_to_cpu(ah->av.ib.port_pd) >> 24,
- ah->av.ib.gid_index,
- &sqp->ud_header.grh.source_gid, NULL);
+ } else {
+ if (mlx4_is_mfunc(to_mdev(ib_dev)->dev)) {
+ /* When multi-function is enabled, the ib_core gid
+ * indexes don't necessarily match the hw ones, so
+ * we must use our own cache
+ */
+ sqp->ud_header.grh.source_gid.global.subnet_prefix =
+ cpu_to_be64(atomic64_read(&(to_mdev(ib_dev)->sriov.
+ demux[sqp->qp.port - 1].
+ subnet_prefix)));
+ sqp->ud_header.grh.source_gid.global.interface_id =
+ to_mdev(ib_dev)->sriov.demux[sqp->qp.port - 1].
+ guid_cache[ah->av.ib.gid_index];
+ } else {
+ ib_get_cached_gid(ib_dev,
+ be32_to_cpu(ah->av.ib.port_pd) >> 24,
+ ah->av.ib.gid_index,
+ &sqp->ud_header.grh.source_gid, NULL);
+ }
}
memcpy(sqp->ud_header.grh.destination_gid.raw,
ah->av.ib.dgid, 16);
diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c
index 8184267c7901..02c8deab1fff 100644
--- a/drivers/infiniband/hw/mlx5/cq.c
+++ b/drivers/infiniband/hw/mlx5/cq.c
@@ -787,8 +787,7 @@ struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev,
if (err)
goto err_create;
} else {
- /* for now choose 64 bytes till we have a proper interface */
- cqe_size = 64;
+ cqe_size = cache_line_size() == 128 ? 128 : 64;
err = create_cq_kernel(dev, cq, entries, cqe_size, &cqb,
&index, &inlen);
if (err)
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index bfc940ff9c8a..2a1fdcaa3044 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -947,13 +947,13 @@ static void mlx5_ib_event(struct mlx5_core_dev *dev, void *context,
{
struct mlx5_ib_dev *ibdev = (struct mlx5_ib_dev *)context;
struct ib_event ibev;
-
+ bool fatal = false;
u8 port = 0;
switch (event) {
case MLX5_DEV_EVENT_SYS_ERROR:
- ibdev->ib_active = false;
ibev.event = IB_EVENT_DEVICE_FATAL;
+ fatal = true;
break;
case MLX5_DEV_EVENT_PORT_UP:
@@ -998,6 +998,9 @@ static void mlx5_ib_event(struct mlx5_core_dev *dev, void *context,
if (ibdev->ib_active)
ib_dispatch_event(&ibev);
+
+ if (fatal)
+ ibdev->ib_active = false;
}
static void get_ext_port_caps(struct mlx5_ib_dev *dev)
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index 3ede10309754..69a151ae8261 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -472,6 +472,7 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb,
struct ipoib_ah *address, u32 qpn);
void ipoib_reap_ah(struct work_struct *work);
+struct ipoib_path *__path_find(struct net_device *dev, void *gid);
void ipoib_mark_paths_invalid(struct net_device *dev);
void ipoib_flush_paths(struct net_device *dev);
struct ipoib_dev_priv *ipoib_intf_alloc(const char *format);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 3ae9726efb98..8ca75af0e6d1 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -1299,6 +1299,8 @@ void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx)
}
}
+#define QPN_AND_OPTIONS_OFFSET 4
+
static void ipoib_cm_tx_start(struct work_struct *work)
{
struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv,
@@ -1307,6 +1309,7 @@ static void ipoib_cm_tx_start(struct work_struct *work)
struct ipoib_neigh *neigh;
struct ipoib_cm_tx *p;
unsigned long flags;
+ struct ipoib_path *path;
int ret;
struct ib_sa_path_rec pathrec;
@@ -1319,7 +1322,19 @@ static void ipoib_cm_tx_start(struct work_struct *work)
p = list_entry(priv->cm.start_list.next, typeof(*p), list);
list_del_init(&p->list);
neigh = p->neigh;
+
qpn = IPOIB_QPN(neigh->daddr);
+ /*
+ * As long as the search is with these 2 locks,
+ * path existence indicates its validity.
+ */
+ path = __path_find(dev, neigh->daddr + QPN_AND_OPTIONS_OFFSET);
+ if (!path) {
+ pr_info("%s ignore not valid path %pI6\n",
+ __func__,
+ neigh->daddr + QPN_AND_OPTIONS_OFFSET);
+ goto free_neigh;
+ }
memcpy(&pathrec, &p->path->pathrec, sizeof pathrec);
spin_unlock_irqrestore(&priv->lock, flags);
@@ -1331,6 +1346,7 @@ static void ipoib_cm_tx_start(struct work_struct *work)
spin_lock_irqsave(&priv->lock, flags);
if (ret) {
+free_neigh:
neigh = p->neigh;
if (neigh) {
neigh->cm = NULL;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index fa9c42ff1fb0..85de078fb0ce 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -1028,8 +1028,17 @@ static void __ipoib_ib_dev_flush(struct ipoib_dev_priv *priv,
}
if (level == IPOIB_FLUSH_LIGHT) {
+ int oper_up;
ipoib_mark_paths_invalid(dev);
+ /* Set IPoIB operation as down to prevent races between:
+ * the flush flow which leaves MCG and on the fly joins
+ * which can happen during that time. mcast restart task
+ * should deal with join requests we missed.
+ */
+ oper_up = test_and_clear_bit(IPOIB_FLAG_OPER_UP, &priv->flags);
ipoib_mcast_dev_flush(dev);
+ if (oper_up)
+ set_bit(IPOIB_FLAG_OPER_UP, &priv->flags);
ipoib_flush_ah(dev);
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 942dffca6a9d..5f7681b975d0 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -481,7 +481,7 @@ int ipoib_set_mode(struct net_device *dev, const char *buf)
return -EINVAL;
}
-static struct ipoib_path *__path_find(struct net_device *dev, void *gid)
+struct ipoib_path *__path_find(struct net_device *dev, void *gid)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct rb_node *n = priv->path_tree.rb_node;
diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c
index 907e4e278fce..c877e56a9bd5 100644
--- a/drivers/input/keyboard/goldfish_events.c
+++ b/drivers/input/keyboard/goldfish_events.c
@@ -17,11 +17,15 @@
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/acpi.h>
+
+#define GOLDFISH_MAX_FINGERS 5
enum {
REG_READ = 0x00,
@@ -51,7 +55,21 @@ static irqreturn_t events_interrupt(int irq, void *dev_id)
value = __raw_readl(edev->addr + REG_READ);
input_event(edev->input, type, code, value);
- input_sync(edev->input);
+ // Send an extra (EV_SYN, SYN_REPORT, 0x0) event
+ // if a key was pressed. Some keyboard device
+ // drivers may only send the EV_KEY event and
+ // not EV_SYN.
+ // Note that sending an extra SYN_REPORT is not
+ // necessary nor correct protocol with other
+ // devices such as touchscreens, which will send
+ // their own SYN_REPORT's when sufficient event
+ // information has been collected (e.g., for
+ // touchscreens, when pressure and X/Y coordinates
+ // have been received). Hence, we will only send
+ // this extra SYN_REPORT if type == EV_KEY.
+ if (type == EV_KEY) {
+ input_sync(edev->input);
+ }
return IRQ_HANDLED;
}
@@ -153,6 +171,15 @@ static int events_probe(struct platform_device *pdev)
input_dev->name = edev->name;
input_dev->id.bustype = BUS_HOST;
+ // Set the Goldfish Device to be multi-touch.
+ // In the Ranchu kernel, there is multi-touch-specific
+ // code for handling ABS_MT_SLOT events.
+ // See drivers/input/input.c:input_handle_abs_event.
+ // If we do not issue input_mt_init_slots,
+ // the kernel will filter out needed ABS_MT_SLOT
+ // events when we touch the screen in more than one place,
+ // preventing multi-touch with more than one finger from working.
+ input_mt_init_slots(input_dev, GOLDFISH_MAX_FINGERS, 0);
events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX);
events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX);
@@ -178,10 +205,26 @@ static int events_probe(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id goldfish_events_of_match[] = {
+ { .compatible = "google,goldfish-events-keypad", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_events_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id goldfish_events_acpi_match[] = {
+ { "GFSH0002", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_events_acpi_match);
+#endif
+
static struct platform_driver events_driver = {
.probe = events_probe,
.driver = {
.name = "goldfish_events",
+ .of_match_table = goldfish_events_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_events_acpi_match),
},
};
diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c
index 7fbf7247e65f..7e5222aec7c1 100644
--- a/drivers/input/keyreset.c
+++ b/drivers/input/keyreset.c
@@ -32,8 +32,7 @@ struct keyreset_state {
static void do_restart(struct work_struct *unused)
{
- sys_sync();
- kernel_restart(NULL);
+ orderly_reboot();
}
static void do_reset_fn(void *priv)
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index be5b399da5d3..43482ae1e049 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1163,6 +1163,13 @@ static const struct dmi_system_id elantech_dmi_has_middle_button[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"),
},
},
+ {
+ /* Fujitsu H760 also has a middle button */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"),
+ },
+ },
#endif
{ }
};
@@ -1507,10 +1514,10 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
},
},
{
- /* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */
+ /* Fujitsu H760 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
- DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"),
},
},
{
@@ -1521,6 +1528,20 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
},
},
{
+ /* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"),
+ },
+ },
+ {
+ /* Fujitsu LIFEBOOK E556 does not work with crc_enabled == 0 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E556"),
+ },
+ },
+ {
/* Fujitsu LIFEBOOK U745 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h
index a5eed2ade53d..34da81c006b6 100644
--- a/drivers/input/serio/i8042-io.h
+++ b/drivers/input/serio/i8042-io.h
@@ -81,7 +81,7 @@ static inline int i8042_platform_init(void)
return -EBUSY;
#endif
- i8042_reset = 1;
+ i8042_reset = I8042_RESET_ALWAYS;
return 0;
}
diff --git a/drivers/input/serio/i8042-ip22io.h b/drivers/input/serio/i8042-ip22io.h
index ee1ad27d6ed0..08a1c10a1448 100644
--- a/drivers/input/serio/i8042-ip22io.h
+++ b/drivers/input/serio/i8042-ip22io.h
@@ -61,7 +61,7 @@ static inline int i8042_platform_init(void)
return -EBUSY;
#endif
- i8042_reset = 1;
+ i8042_reset = I8042_RESET_ALWAYS;
return 0;
}
diff --git a/drivers/input/serio/i8042-ppcio.h b/drivers/input/serio/i8042-ppcio.h
index f708c75d16f1..1aabea43329e 100644
--- a/drivers/input/serio/i8042-ppcio.h
+++ b/drivers/input/serio/i8042-ppcio.h
@@ -44,7 +44,7 @@ static inline void i8042_write_command(int val)
static inline int i8042_platform_init(void)
{
- i8042_reset = 1;
+ i8042_reset = I8042_RESET_ALWAYS;
return 0;
}
diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h
index afcd1c1a05b2..6231d63860ee 100644
--- a/drivers/input/serio/i8042-sparcio.h
+++ b/drivers/input/serio/i8042-sparcio.h
@@ -130,7 +130,7 @@ static int __init i8042_platform_init(void)
}
}
- i8042_reset = 1;
+ i8042_reset = I8042_RESET_ALWAYS;
return 0;
}
diff --git a/drivers/input/serio/i8042-unicore32io.h b/drivers/input/serio/i8042-unicore32io.h
index 73f5cc124a36..455747552f85 100644
--- a/drivers/input/serio/i8042-unicore32io.h
+++ b/drivers/input/serio/i8042-unicore32io.h
@@ -61,7 +61,7 @@ static inline int i8042_platform_init(void)
if (!request_mem_region(I8042_REGION_START, I8042_REGION_SIZE, "i8042"))
return -EBUSY;
- i8042_reset = 1;
+ i8042_reset = I8042_RESET_ALWAYS;
return 0;
}
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 68f5f4a0f1e7..073246c7d163 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -510,6 +510,90 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
{ }
};
+/*
+ * On some Asus laptops, just running self tests cause problems.
+ */
+static const struct dmi_system_id i8042_dmi_noselftest_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A455LD"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "K401LB"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "K501LB"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "K501LX"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "R409L"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "V502LX"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X302LA"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X450LD"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X455LAB"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X455LDB"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X455LF"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Z450LA"),
+ },
+ },
+ { }
+};
static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
{
/* MSI Wind U-100 */
@@ -793,6 +877,13 @@ static const struct dmi_system_id __initconst i8042_dmi_kbdreset_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "P34"),
},
},
+ {
+ /* Schenker XMG C504 - Elantech touchpad */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "XMG"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "C504"),
+ },
+ },
{ }
};
@@ -1072,12 +1163,18 @@ static int __init i8042_platform_init(void)
return retval;
#if defined(__ia64__)
- i8042_reset = true;
+ i8042_reset = I8042_RESET_ALWAYS;
#endif
#ifdef CONFIG_X86
- if (dmi_check_system(i8042_dmi_reset_table))
- i8042_reset = true;
+ /* Honor module parameter when value is not default */
+ if (i8042_reset == I8042_RESET_DEFAULT) {
+ if (dmi_check_system(i8042_dmi_reset_table))
+ i8042_reset = I8042_RESET_ALWAYS;
+
+ if (dmi_check_system(i8042_dmi_noselftest_table))
+ i8042_reset = I8042_RESET_NEVER;
+ }
if (dmi_check_system(i8042_dmi_noloop_table))
i8042_noloop = true;
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 405252a884dd..89abfdb539ac 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -48,9 +48,39 @@ static bool i8042_unlock;
module_param_named(unlock, i8042_unlock, bool, 0);
MODULE_PARM_DESC(unlock, "Ignore keyboard lock.");
-static bool i8042_reset;
-module_param_named(reset, i8042_reset, bool, 0);
-MODULE_PARM_DESC(reset, "Reset controller during init and cleanup.");
+enum i8042_controller_reset_mode {
+ I8042_RESET_NEVER,
+ I8042_RESET_ALWAYS,
+ I8042_RESET_ON_S2RAM,
+#define I8042_RESET_DEFAULT I8042_RESET_ON_S2RAM
+};
+static enum i8042_controller_reset_mode i8042_reset = I8042_RESET_DEFAULT;
+static int i8042_set_reset(const char *val, const struct kernel_param *kp)
+{
+ enum i8042_controller_reset_mode *arg = kp->arg;
+ int error;
+ bool reset;
+
+ if (val) {
+ error = kstrtobool(val, &reset);
+ if (error)
+ return error;
+ } else {
+ reset = true;
+ }
+
+ *arg = reset ? I8042_RESET_ALWAYS : I8042_RESET_NEVER;
+ return 0;
+}
+
+static const struct kernel_param_ops param_ops_reset_param = {
+ .flags = KERNEL_PARAM_OPS_FL_NOARG,
+ .set = i8042_set_reset,
+};
+#define param_check_reset_param(name, p) \
+ __param_check(name, p, enum i8042_controller_reset_mode)
+module_param_named(reset, i8042_reset, reset_param, 0);
+MODULE_PARM_DESC(reset, "Reset controller on resume, cleanup or both");
static bool i8042_direct;
module_param_named(direct, i8042_direct, bool, 0);
@@ -1019,7 +1049,7 @@ static int i8042_controller_init(void)
* Reset the controller and reset CRT to the original value set by BIOS.
*/
-static void i8042_controller_reset(bool force_reset)
+static void i8042_controller_reset(bool s2r_wants_reset)
{
i8042_flush();
@@ -1044,8 +1074,10 @@ static void i8042_controller_reset(bool force_reset)
* Reset the controller if requested.
*/
- if (i8042_reset || force_reset)
+ if (i8042_reset == I8042_RESET_ALWAYS ||
+ (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) {
i8042_controller_selftest();
+ }
/*
* Restore the original control register setting.
@@ -1110,7 +1142,7 @@ static void i8042_dritek_enable(void)
* before suspending.
*/
-static int i8042_controller_resume(bool force_reset)
+static int i8042_controller_resume(bool s2r_wants_reset)
{
int error;
@@ -1118,7 +1150,8 @@ static int i8042_controller_resume(bool force_reset)
if (error)
return error;
- if (i8042_reset || force_reset) {
+ if (i8042_reset == I8042_RESET_ALWAYS ||
+ (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) {
error = i8042_controller_selftest();
if (error)
return error;
@@ -1195,7 +1228,7 @@ static int i8042_pm_resume_noirq(struct device *dev)
static int i8042_pm_resume(struct device *dev)
{
- bool force_reset;
+ bool want_reset;
int i;
for (i = 0; i < I8042_NUM_PORTS; i++) {
@@ -1218,9 +1251,9 @@ static int i8042_pm_resume(struct device *dev)
* off control to the platform firmware, otherwise we can simply restore
* the mode.
*/
- force_reset = pm_resume_via_firmware();
+ want_reset = pm_resume_via_firmware();
- return i8042_controller_resume(force_reset);
+ return i8042_controller_resume(want_reset);
}
static int i8042_pm_thaw(struct device *dev)
@@ -1482,7 +1515,7 @@ static int __init i8042_probe(struct platform_device *dev)
i8042_platform_device = dev;
- if (i8042_reset) {
+ if (i8042_reset == I8042_RESET_ALWAYS) {
error = i8042_controller_selftest();
if (error)
return error;
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 0397985a2601..5975d76ce755 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -1833,6 +1833,9 @@ static void dma_ops_domain_free(struct dma_ops_domain *dom)
kfree(dom->aperture[i]);
}
+ if (dom->domain.id)
+ domain_id_free(dom->domain.id);
+
kfree(dom);
}
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index ce1eb562be36..03a691723349 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1835,6 +1835,8 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
.oas = oas,
.tlb = &arm_smmu_gather_ops,
.iommu_dev = smmu->dev,
+ .iova_base = domain->geometry.aperture_start,
+ .iova_end = domain->geometry.aperture_end,
};
}
diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c
index 6d90221486c9..2acb9242bcf8 100644
--- a/drivers/iommu/dma-mapping-fast.c
+++ b/drivers/iommu/dma-mapping-fast.c
@@ -151,7 +151,9 @@ static dma_addr_t __fast_smmu_alloc_iova(struct dma_fast_smmu_mapping *mapping,
iommu_tlbiall(mapping->domain);
mapping->have_stale_tlbs = false;
- av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds, skip_sync);
+ av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds, mapping->base,
+ mapping->base + mapping->size - 1,
+ skip_sync);
}
return (bit << FAST_PAGE_SHIFT) + mapping->base;
@@ -328,7 +330,7 @@ static dma_addr_t fast_smmu_map_page(struct device *dev, struct page *page,
if (unlikely(iova == DMA_ERROR_CODE))
goto fail;
- pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova);
+ pmd = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, iova);
if (unlikely(av8l_fast_map_public(pmd, phys_to_map, len, prot)))
goto fail_free_iova;
@@ -351,7 +353,8 @@ static void fast_smmu_unmap_page(struct device *dev, dma_addr_t iova,
{
struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast;
unsigned long flags;
- av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova);
+ av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
+ mapping->base, iova);
unsigned long offset = iova & ~FAST_PAGE_MASK;
size_t len = ALIGN(size + offset, FAST_PAGE_SIZE);
int nptes = len >> FAST_PAGE_SHIFT;
@@ -373,7 +376,8 @@ static void fast_smmu_sync_single_for_cpu(struct device *dev,
dma_addr_t iova, size_t size, enum dma_data_direction dir)
{
struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast;
- av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova);
+ av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
+ mapping->base, iova);
unsigned long offset = iova & ~FAST_PAGE_MASK;
struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK));
@@ -385,7 +389,8 @@ static void fast_smmu_sync_single_for_device(struct device *dev,
dma_addr_t iova, size_t size, enum dma_data_direction dir)
{
struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast;
- av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova);
+ av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
+ mapping->base, iova);
unsigned long offset = iova & ~FAST_PAGE_MASK;
struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK));
@@ -513,7 +518,8 @@ static void *fast_smmu_alloc(struct device *dev, size_t size,
while (sg_miter_next(&miter)) {
int nptes = miter.length >> FAST_PAGE_SHIFT;
- ptep = iopte_pmd_offset(mapping->pgtbl_pmds, iova_iter);
+ ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base,
+ iova_iter);
if (unlikely(av8l_fast_map_public(
ptep, page_to_phys(miter.page),
miter.length, prot))) {
@@ -541,7 +547,7 @@ static void *fast_smmu_alloc(struct device *dev, size_t size,
out_unmap:
/* need to take the lock again for page tables and iova */
spin_lock_irqsave(&mapping->lock, flags);
- ptep = iopte_pmd_offset(mapping->pgtbl_pmds, dma_addr);
+ ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_addr);
av8l_fast_unmap_public(ptep, size);
fast_dmac_clean_range(mapping, ptep, ptep + count);
out_free_iova:
@@ -573,7 +579,7 @@ static void fast_smmu_free(struct device *dev, size_t size,
pages = area->pages;
dma_common_free_remap(vaddr, size, VM_USERMAP, false);
- ptep = iopte_pmd_offset(mapping->pgtbl_pmds, dma_handle);
+ ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_handle);
spin_lock_irqsave(&mapping->lock, flags);
av8l_fast_unmap_public(ptep, size);
fast_dmac_clean_range(mapping, ptep, ptep + count);
@@ -745,6 +751,9 @@ int fast_smmu_attach_device(struct device *dev,
mapping->fast->domain = domain;
mapping->fast->dev = dev;
+ domain->geometry.aperture_start = mapping->base;
+ domain->geometry.aperture_end = mapping->base + size - 1;
+
if (iommu_attach_device(domain, dev))
return -EINVAL;
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 3821c4786662..e913a930ac80 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -326,7 +326,9 @@ static int dmar_pci_bus_notifier(struct notifier_block *nb,
struct pci_dev *pdev = to_pci_dev(data);
struct dmar_pci_notify_info *info;
- /* Only care about add/remove events for physical functions */
+ /* Only care about add/remove events for physical functions.
+ * For VFs we actually do the lookup based on the corresponding
+ * PF in device_to_iommu() anyway. */
if (pdev->is_virtfn)
return NOTIFY_DONE;
if (action != BUS_NOTIFY_ADD_DEVICE &&
@@ -1858,10 +1860,11 @@ static int dmar_hp_remove_drhd(struct acpi_dmar_header *header, void *arg)
/*
* All PCI devices managed by this unit should have been destroyed.
*/
- if (!dmaru->include_all && dmaru->devices && dmaru->devices_cnt)
+ if (!dmaru->include_all && dmaru->devices && dmaru->devices_cnt) {
for_each_active_dev_scope(dmaru->devices,
dmaru->devices_cnt, i, dev)
return -EBUSY;
+ }
ret = dmar_ir_hotplug(dmaru, false);
if (ret == 0)
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 24d81308a1a6..59e9abd3345e 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -885,7 +885,13 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf
return NULL;
if (dev_is_pci(dev)) {
+ struct pci_dev *pf_pdev;
+
pdev = to_pci_dev(dev);
+ /* VFs aren't listed in scope tables; we need to look up
+ * the PF instead to find the IOMMU. */
+ pf_pdev = pci_physfn(pdev);
+ dev = &pf_pdev->dev;
segment = pci_domain_nr(pdev->bus);
} else if (has_acpi_companion(dev))
dev = &ACPI_COMPANION(dev)->dev;
@@ -898,6 +904,13 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf
for_each_active_dev_scope(drhd->devices,
drhd->devices_cnt, i, tmp) {
if (tmp == dev) {
+ /* For a VF use its original BDF# not that of the PF
+ * which we used for the IOMMU lookup. Strictly speaking
+ * we could do this for all PCI devices; we only need to
+ * get the BDF# from the scope table for ACPI matches. */
+ if (pdev->is_virtfn)
+ goto got_pdev;
+
*bus = drhd->devices[i].bus;
*devfn = drhd->devices[i].devfn;
goto out;
@@ -1672,6 +1685,7 @@ static void disable_dmar_iommu(struct intel_iommu *iommu)
if (!iommu->domains || !iommu->domain_ids)
return;
+again:
spin_lock_irqsave(&device_domain_lock, flags);
list_for_each_entry_safe(info, tmp, &device_domain_list, global) {
struct dmar_domain *domain;
@@ -1684,10 +1698,19 @@ static void disable_dmar_iommu(struct intel_iommu *iommu)
domain = info->domain;
- dmar_remove_one_dev_info(domain, info->dev);
+ __dmar_remove_one_dev_info(info);
- if (!domain_type_is_vm_or_si(domain))
+ if (!domain_type_is_vm_or_si(domain)) {
+ /*
+ * The domain_exit() function can't be called under
+ * device_domain_lock, as it takes this lock itself.
+ * So release the lock here and re-run the loop
+ * afterwards.
+ */
+ spin_unlock_irqrestore(&device_domain_lock, flags);
domain_exit(domain);
+ goto again;
+ }
}
spin_unlock_irqrestore(&device_domain_lock, flags);
@@ -4182,10 +4205,11 @@ int dmar_check_one_atsr(struct acpi_dmar_header *hdr, void *arg)
if (!atsru)
return 0;
- if (!atsru->include_all && atsru->devices && atsru->devices_cnt)
+ if (!atsru->include_all && atsru->devices && atsru->devices_cnt) {
for_each_active_dev_scope(atsru->devices, atsru->devices_cnt,
i, dev)
return -EBUSY;
+ }
return 0;
}
diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c
index d9939fa9b588..f929879ecae6 100644
--- a/drivers/iommu/intel-svm.c
+++ b/drivers/iommu/intel-svm.c
@@ -39,10 +39,18 @@ int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu)
struct page *pages;
int order;
- order = ecap_pss(iommu->ecap) + 7 - PAGE_SHIFT;
- if (order < 0)
- order = 0;
-
+ /* Start at 2 because it's defined as 2^(1+PSS) */
+ iommu->pasid_max = 2 << ecap_pss(iommu->ecap);
+
+ /* Eventually I'm promised we will get a multi-level PASID table
+ * and it won't have to be physically contiguous. Until then,
+ * limit the size because 8MiB contiguous allocations can be hard
+ * to come by. The limit of 0x20000, which is 1MiB for each of
+ * the PASID and PASID-state tables, is somewhat arbitrary. */
+ if (iommu->pasid_max > 0x20000)
+ iommu->pasid_max = 0x20000;
+
+ order = get_order(sizeof(struct pasid_entry) * iommu->pasid_max);
pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
if (!pages) {
pr_warn("IOMMU: %s: Failed to allocate PASID table\n",
@@ -53,6 +61,8 @@ int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu)
pr_info("%s: Allocated order %d PASID table.\n", iommu->name, order);
if (ecap_dis(iommu->ecap)) {
+ /* Just making it explicit... */
+ BUILD_BUG_ON(sizeof(struct pasid_entry) != sizeof(struct pasid_state_entry));
pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
if (pages)
iommu->pasid_state_table = page_address(pages);
@@ -68,11 +78,7 @@ int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu)
int intel_svm_free_pasid_tables(struct intel_iommu *iommu)
{
- int order;
-
- order = ecap_pss(iommu->ecap) + 7 - PAGE_SHIFT;
- if (order < 0)
- order = 0;
+ int order = get_order(sizeof(struct pasid_entry) * iommu->pasid_max);
if (iommu->pasid_table) {
free_pages((unsigned long)iommu->pasid_table, order);
@@ -371,8 +377,8 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_
}
svm->iommu = iommu;
- if (pasid_max > 2 << ecap_pss(iommu->ecap))
- pasid_max = 2 << ecap_pss(iommu->ecap);
+ if (pasid_max > iommu->pasid_max)
+ pasid_max = iommu->pasid_max;
/* Do not use PASID 0 in caching mode (virtualised IOMMU) */
ret = idr_alloc(&iommu->pasid_idr, svm,
diff --git a/drivers/iommu/io-pgtable-fast.c b/drivers/iommu/io-pgtable-fast.c
index 5d32a382e291..3582e206db68 100644
--- a/drivers/iommu/io-pgtable-fast.c
+++ b/drivers/iommu/io-pgtable-fast.c
@@ -19,6 +19,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/io-pgtable-fast.h>
+#include <linux/mm.h>
#include <asm/cacheflush.h>
#include <linux/vmalloc.h>
@@ -42,6 +43,9 @@ struct av8l_fast_io_pgtable {
av8l_fast_iopte *puds[4];
av8l_fast_iopte *pmds;
struct page **pages; /* page table memory */
+ int nr_pages;
+ dma_addr_t base;
+ dma_addr_t end;
};
/* Page table bits */
@@ -168,12 +172,14 @@ static void __av8l_check_for_stale_tlb(av8l_fast_iopte *ptep)
}
}
-void av8l_fast_clear_stale_ptes(av8l_fast_iopte *pmds, bool skip_sync)
+void av8l_fast_clear_stale_ptes(av8l_fast_iopte *pmds, u64 base,
+ u64 end, bool skip_sync)
{
int i;
av8l_fast_iopte *pmdp = pmds;
- for (i = 0; i < ((SZ_1G * 4UL) >> AV8L_FAST_PAGE_SHIFT); ++i) {
+ for (i = base >> AV8L_FAST_PAGE_SHIFT;
+ i <= (end >> AV8L_FAST_PAGE_SHIFT); ++i) {
if (!(*pmdp & AV8L_FAST_PTE_VALID)) {
*pmdp = 0;
if (!skip_sync)
@@ -224,7 +230,7 @@ static int av8l_fast_map(struct io_pgtable_ops *ops, unsigned long iova,
phys_addr_t paddr, size_t size, int prot)
{
struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops);
- av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, iova);
+ av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova);
unsigned long nptes = size >> AV8L_FAST_PAGE_SHIFT;
av8l_fast_map_public(ptep, paddr, size, prot);
@@ -255,7 +261,7 @@ static size_t av8l_fast_unmap(struct io_pgtable_ops *ops, unsigned long iova,
size_t size)
{
struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops);
- av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, iova);
+ av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova);
unsigned long nptes = size >> AV8L_FAST_PAGE_SHIFT;
__av8l_fast_unmap(ptep, size, false);
@@ -333,7 +339,7 @@ av8l_fast_alloc_pgtable_data(struct io_pgtable_cfg *cfg)
}
/*
- * We need 1 page for the pgd, 4 pages for puds (1GB VA per pud page) and
+ * We need max 1 page for the pgd, 4 pages for puds (1GB VA per pud page) and
* 2048 pages for pmds (each pud page contains 512 table entries, each
* pointing to a pmd).
*/
@@ -342,12 +348,38 @@ av8l_fast_alloc_pgtable_data(struct io_pgtable_cfg *cfg)
#define NUM_PMD_PAGES 2048
#define NUM_PGTBL_PAGES (NUM_PGD_PAGES + NUM_PUD_PAGES + NUM_PMD_PAGES)
+/* undefine arch specific definitions which depends on page table format */
+#undef pud_index
+#undef pud_mask
+#undef pud_next
+#undef pmd_index
+#undef pmd_mask
+#undef pmd_next
+
+#define pud_index(addr) (((addr) >> 30) & 0x3)
+#define pud_mask(addr) ((addr) & ~((1UL << 30) - 1))
+#define pud_next(addr, end) \
+({ unsigned long __boundary = pud_mask(addr + (1UL << 30));\
+ (__boundary - 1 < (end) - 1) ? __boundary : (end); \
+})
+
+#define pmd_index(addr) (((addr) >> 21) & 0x1ff)
+#define pmd_mask(addr) ((addr) & ~((1UL << 21) - 1))
+#define pmd_next(addr, end) \
+({ unsigned long __boundary = pmd_mask(addr + (1UL << 21));\
+ (__boundary - 1 < (end) - 1) ? __boundary : (end); \
+})
+
static int
av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
struct io_pgtable_cfg *cfg, void *cookie)
{
int i, j, pg = 0;
struct page **pages, *page;
+ dma_addr_t base = cfg->iova_base;
+ dma_addr_t end = cfg->iova_end;
+ dma_addr_t pud, pmd;
+ int pmd_pg_index;
pages = kmalloc(sizeof(*pages) * NUM_PGTBL_PAGES, __GFP_NOWARN |
__GFP_NORETRY);
@@ -365,10 +397,11 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
data->pgd = page_address(page);
/*
- * We need 2048 entries at level 2 to map 4GB of VA space. A page
- * can hold 512 entries, so we need 4 pages.
+ * We need max 2048 entries at level 2 to map 4GB of VA space. A page
+ * can hold 512 entries, so we need max 4 pages.
*/
- for (i = 0; i < 4; ++i) {
+ for (i = pud_index(base), pud = base; pud < end;
+ ++i, pud = pud_next(pud, end)) {
av8l_fast_iopte pte, *ptep;
page = alloc_page(GFP_KERNEL | __GFP_ZERO);
@@ -383,18 +416,26 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
dmac_clean_range(data->pgd, data->pgd + 4);
/*
- * We have 4 puds, each of which can point to 512 pmds, so we'll
- * have 2048 pmds, each of which can hold 512 ptes, for a grand
+ * We have max 4 puds, each of which can point to 512 pmds, so we'll
+ * have max 2048 pmds, each of which can hold 512 ptes, for a grand
* total of 2048*512=1048576 PTEs.
*/
- for (i = 0; i < 4; ++i) {
- for (j = 0; j < 512; ++j) {
+ pmd_pg_index = pg;
+ for (i = pud_index(base), pud = base; pud < end;
+ ++i, pud = pud_next(pud, end)) {
+ for (j = pmd_index(pud), pmd = pud; pmd < pud_next(pud, end);
+ ++j, pmd = pmd_next(pmd, end)) {
av8l_fast_iopte pte, *pudp;
+ void *addr;
page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!page)
goto err_free_pages;
pages[pg++] = page;
+
+ addr = page_address(page);
+ dmac_clean_range(addr, addr + SZ_4K);
+
pte = page_to_phys(page) | AV8L_FAST_PTE_TYPE_TABLE;
pudp = data->puds[i] + j;
*pudp = pte;
@@ -402,21 +443,21 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
dmac_clean_range(data->puds[i], data->puds[i] + 512);
}
- if (WARN_ON(pg != NUM_PGTBL_PAGES))
- goto err_free_pages;
-
/*
* We map the pmds into a virtually contiguous space so that we
* don't have to traverse the first two levels of the page tables
* to find the appropriate pud. Instead, it will be a simple
* offset from the virtual base of the pmds.
*/
- data->pmds = vmap(&pages[NUM_PGD_PAGES + NUM_PUD_PAGES], NUM_PMD_PAGES,
+ data->pmds = vmap(&pages[pmd_pg_index], pg - pmd_pg_index,
VM_IOREMAP, PAGE_KERNEL);
if (!data->pmds)
goto err_free_pages;
data->pages = pages;
+ data->nr_pages = pg;
+ data->base = base;
+ data->end = end;
return 0;
err_free_pages:
@@ -516,7 +557,7 @@ static void av8l_fast_free_pgtable(struct io_pgtable *iop)
struct av8l_fast_io_pgtable *data = iof_pgtable_to_data(iop);
vunmap(data->pmds);
- for (i = 0; i < NUM_PGTBL_PAGES; ++i)
+ for (i = 0; i < data->nr_pages; ++i)
__free_page(data->pages[i]);
kvfree(data->pages);
kfree(data);
@@ -588,6 +629,7 @@ static int __init av8l_fast_positive_testing(void)
struct av8l_fast_io_pgtable *data;
av8l_fast_iopte *pmds;
u64 max = SZ_1G * 4ULL - 1;
+ u64 base = 0;
cfg = (struct io_pgtable_cfg) {
.quirks = 0,
@@ -595,6 +637,8 @@ static int __init av8l_fast_positive_testing(void)
.ias = 32,
.oas = 32,
.pgsize_bitmap = SZ_4K,
+ .iova_base = base,
+ .iova_end = max,
};
cfg_cookie = &cfg;
@@ -607,81 +651,81 @@ static int __init av8l_fast_positive_testing(void)
pmds = data->pmds;
/* map the entire 4GB VA space with 4K map calls */
- for (iova = 0; iova < max; iova += SZ_4K) {
+ for (iova = base; iova < max; iova += SZ_4K) {
if (WARN_ON(ops->map(ops, iova, iova, SZ_4K, IOMMU_READ))) {
failed++;
continue;
}
}
- if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
- max)))
+ if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+ base, max - base)))
failed++;
/* unmap it all */
- for (iova = 0; iova < max; iova += SZ_4K) {
+ for (iova = base; iova < max; iova += SZ_4K) {
if (WARN_ON(ops->unmap(ops, iova, SZ_4K) != SZ_4K))
failed++;
}
/* sweep up TLB proving PTEs */
- av8l_fast_clear_stale_ptes(pmds, false);
+ av8l_fast_clear_stale_ptes(pmds, base, max, false);
/* map the entire 4GB VA space with 8K map calls */
- for (iova = 0; iova < max; iova += SZ_8K) {
+ for (iova = base; iova < max; iova += SZ_8K) {
if (WARN_ON(ops->map(ops, iova, iova, SZ_8K, IOMMU_READ))) {
failed++;
continue;
}
}
- if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
- max)))
+ if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+ base, max - base)))
failed++;
/* unmap it all with 8K unmap calls */
- for (iova = 0; iova < max; iova += SZ_8K) {
+ for (iova = base; iova < max; iova += SZ_8K) {
if (WARN_ON(ops->unmap(ops, iova, SZ_8K) != SZ_8K))
failed++;
}
/* sweep up TLB proving PTEs */
- av8l_fast_clear_stale_ptes(pmds, false);
+ av8l_fast_clear_stale_ptes(pmds, base, max, false);
/* map the entire 4GB VA space with 16K map calls */
- for (iova = 0; iova < max; iova += SZ_16K) {
+ for (iova = base; iova < max; iova += SZ_16K) {
if (WARN_ON(ops->map(ops, iova, iova, SZ_16K, IOMMU_READ))) {
failed++;
continue;
}
}
- if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
- max)))
+ if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+ base, max - base)))
failed++;
/* unmap it all */
- for (iova = 0; iova < max; iova += SZ_16K) {
+ for (iova = base; iova < max; iova += SZ_16K) {
if (WARN_ON(ops->unmap(ops, iova, SZ_16K) != SZ_16K))
failed++;
}
/* sweep up TLB proving PTEs */
- av8l_fast_clear_stale_ptes(pmds, false);
+ av8l_fast_clear_stale_ptes(pmds, base, max, false);
/* map the entire 4GB VA space with 64K map calls */
- for (iova = 0; iova < max; iova += SZ_64K) {
+ for (iova = base; iova < max; iova += SZ_64K) {
if (WARN_ON(ops->map(ops, iova, iova, SZ_64K, IOMMU_READ))) {
failed++;
continue;
}
}
- if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
- max)))
+ if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+ base, max - base)))
failed++;
/* unmap it all at once */
- if (WARN_ON(ops->unmap(ops, 0, max) != max))
+ if (WARN_ON(ops->unmap(ops, base, max - base) != (max - base)))
failed++;
free_io_pgtable_ops(ops);
diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h
index f4533040806f..e6939c2212d4 100644
--- a/drivers/iommu/io-pgtable.h
+++ b/drivers/iommu/io-pgtable.h
@@ -69,6 +69,8 @@ struct io_pgtable_cfg {
unsigned int oas;
const struct iommu_gather_ops *tlb;
struct device *iommu_dev;
+ dma_addr_t iova_base;
+ dma_addr_t iova_end;
/* Low-level data specific to the table format */
union {
diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c
index 8a0c7f288198..981c3959da59 100644
--- a/drivers/irqchip/irq-atmel-aic.c
+++ b/drivers/irqchip/irq-atmel-aic.c
@@ -176,6 +176,7 @@ static int aic_irq_domain_xlate(struct irq_domain *d,
{
struct irq_domain_chip_generic *dgc = d->gc;
struct irq_chip_generic *gc;
+ unsigned long flags;
unsigned smr;
int idx;
int ret;
@@ -194,12 +195,12 @@ static int aic_irq_domain_xlate(struct irq_domain *d,
gc = dgc->gc[idx];
- irq_gc_lock(gc);
+ irq_gc_lock_irqsave(gc, flags);
smr = irq_reg_readl(gc, AT91_AIC_SMR(*out_hwirq));
ret = aic_common_set_priority(intspec[2], &smr);
if (!ret)
irq_reg_writel(gc, smr, AT91_AIC_SMR(*out_hwirq));
- irq_gc_unlock(gc);
+ irq_gc_unlock_irqrestore(gc, flags);
return ret;
}
diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c
index 62bb840c613f..7dee71bde350 100644
--- a/drivers/irqchip/irq-atmel-aic5.c
+++ b/drivers/irqchip/irq-atmel-aic5.c
@@ -258,6 +258,7 @@ static int aic5_irq_domain_xlate(struct irq_domain *d,
unsigned int *out_type)
{
struct irq_chip_generic *bgc = irq_get_domain_generic_chip(d, 0);
+ unsigned long flags;
unsigned smr;
int ret;
@@ -269,13 +270,13 @@ static int aic5_irq_domain_xlate(struct irq_domain *d,
if (ret)
return ret;
- irq_gc_lock(bgc);
+ irq_gc_lock_irqsave(bgc, flags);
irq_reg_writel(bgc, *out_hwirq, AT91_AIC5_SSR);
smr = irq_reg_readl(bgc, AT91_AIC5_SMR);
ret = aic_common_set_priority(intspec[2], &smr);
if (!ret)
irq_reg_writel(bgc, intspec[2] | smr, AT91_AIC5_SMR);
- irq_gc_unlock(bgc);
+ irq_gc_unlock_irqrestore(bgc, flags);
return ret;
}
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index cfdc235c1d28..c40a9f3b0724 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -154,7 +154,7 @@ static void gic_enable_redist(bool enable)
return; /* No PM support in this redistributor */
}
- while (count--) {
+ while (--count) {
val = readl_relaxed(rbase + GICR_WAKER);
if (enable ^ (val & GICR_WAKER_ChildrenAsleep))
break;
@@ -702,7 +702,7 @@ static struct notifier_block gic_cpu_notifier = {
static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask,
unsigned long cluster_id)
{
- int cpu = *base_cpu;
+ int next_cpu, cpu = *base_cpu;
unsigned long mpidr = cpu_logical_map(cpu);
u16 tlist = 0;
@@ -716,9 +716,10 @@ static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask,
tlist |= 1 << (mpidr & 0xf);
- cpu = cpumask_next(cpu, mask);
- if (cpu >= nr_cpu_ids)
+ next_cpu = cpumask_next(cpu, mask);
+ if (next_cpu >= nr_cpu_ids)
goto out;
+ cpu = next_cpu;
mpidr = cpu_logical_map(cpu);
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 966227a3df1a..620268b63b2a 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -596,18 +596,9 @@ config LEDS_QPNP
LEDs in both PWM and light pattern generator (LPG) modes. For older
PMICs, it also supports WLEDs and flash LEDs.
-config LEDS_QPNP_FLASH
- tristate "Support for QPNP Flash LEDs"
- depends on LEDS_CLASS && SPMI
- help
- This driver supports the flash LED functionality of Qualcomm
- Technologies, Inc. QPNP PMICs. This driver supports PMICs up through
- PM8994. It can configure the flash LED target current for several
- independent channels.
-
config LEDS_QPNP_FLASH_V2
tristate "Support for QPNP V2 Flash LEDs"
- depends on LEDS_CLASS && MFD_SPMI_PMIC && !LEDS_QPNP_FLASH
+ depends on LEDS_CLASS && MFD_SPMI_PMIC
help
This driver supports the flash V2 LED functionality of Qualcomm
Technologies, Inc. QPNP PMICs. This driver supports PMICs starting
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 8d8ba9175810..aa5ba0cf4de6 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -61,7 +61,6 @@ obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
obj-$(CONFIG_LEDS_QPNP) += leds-qpnp.o
-obj-$(CONFIG_LEDS_QPNP_FLASH) += leds-qpnp-flash.o
obj-$(CONFIG_LEDS_QPNP_FLASH_V2) += leds-qpnp-flash-v2.o
obj-$(CONFIG_LEDS_QPNP_WLED) += leds-qpnp-wled.o
obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o
diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c
deleted file mode 100644
index cd76941b87ca..000000000000
--- a/drivers/leds/leds-qpnp-flash.c
+++ /dev/null
@@ -1,2683 +0,0 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/regmap.h>
-#include <linux/errno.h>
-#include <linux/leds.h>
-#include <linux/slab.h>
-#include <linux/of_device.h>
-#include <linux/spmi.h>
-#include <linux/platform_device.h>
-#include <linux/err.h>
-#include <linux/delay.h>
-#include <linux/of.h>
-#include <linux/regulator/consumer.h>
-#include <linux/workqueue.h>
-#include <linux/power_supply.h>
-#include <linux/leds-qpnp-flash.h>
-#include <linux/qpnp/qpnp-adc.h>
-#include <linux/qpnp/qpnp-revid.h>
-#include <linux/debugfs.h>
-#include <linux/uaccess.h>
-#include "leds.h"
-
-#define FLASH_LED_PERIPHERAL_SUBTYPE(base) (base + 0x05)
-#define FLASH_SAFETY_TIMER(base) (base + 0x40)
-#define FLASH_MAX_CURRENT(base) (base + 0x41)
-#define FLASH_LED0_CURRENT(base) (base + 0x42)
-#define FLASH_LED1_CURRENT(base) (base + 0x43)
-#define FLASH_CLAMP_CURRENT(base) (base + 0x44)
-#define FLASH_MODULE_ENABLE_CTRL(base) (base + 0x46)
-#define FLASH_LED_STROBE_CTRL(base) (base + 0x47)
-#define FLASH_LED_TMR_CTRL(base) (base + 0x48)
-#define FLASH_HEADROOM(base) (base + 0x4A)
-#define FLASH_STARTUP_DELAY(base) (base + 0x4B)
-#define FLASH_MASK_ENABLE(base) (base + 0x4C)
-#define FLASH_VREG_OK_FORCE(base) (base + 0x4F)
-#define FLASH_FAULT_DETECT(base) (base + 0x51)
-#define FLASH_THERMAL_DRATE(base) (base + 0x52)
-#define FLASH_CURRENT_RAMP(base) (base + 0x54)
-#define FLASH_VPH_PWR_DROOP(base) (base + 0x5A)
-#define FLASH_HDRM_SNS_ENABLE_CTRL0(base) (base + 0x5C)
-#define FLASH_HDRM_SNS_ENABLE_CTRL1(base) (base + 0x5D)
-#define FLASH_LED_UNLOCK_SECURE(base) (base + 0xD0)
-#define FLASH_PERPH_RESET_CTRL(base) (base + 0xDA)
-#define FLASH_TORCH(base) (base + 0xE4)
-
-#define FLASH_STATUS_REG_MASK 0xFF
-#define FLASH_LED_FAULT_STATUS(base) (base + 0x08)
-#define INT_LATCHED_STS(base) (base + 0x18)
-#define IN_POLARITY_HIGH(base) (base + 0x12)
-#define INT_SET_TYPE(base) (base + 0x11)
-#define INT_EN_SET(base) (base + 0x15)
-#define INT_LATCHED_CLR(base) (base + 0x14)
-
-#define FLASH_HEADROOM_MASK 0x03
-#define FLASH_STARTUP_DLY_MASK 0x03
-#define FLASH_VREG_OK_FORCE_MASK 0xC0
-#define FLASH_FAULT_DETECT_MASK 0x80
-#define FLASH_THERMAL_DERATE_MASK 0xBF
-#define FLASH_SECURE_MASK 0xFF
-#define FLASH_TORCH_MASK 0x03
-#define FLASH_CURRENT_MASK 0x7F
-#define FLASH_TMR_MASK 0x03
-#define FLASH_TMR_SAFETY 0x00
-#define FLASH_SAFETY_TIMER_MASK 0x7F
-#define FLASH_MODULE_ENABLE_MASK 0xE0
-#define FLASH_STROBE_MASK 0xC0
-#define FLASH_CURRENT_RAMP_MASK 0xBF
-#define FLASH_VPH_PWR_DROOP_MASK 0xF3
-#define FLASH_LED_HDRM_SNS_ENABLE_MASK 0x81
-#define FLASH_MASK_MODULE_CONTRL_MASK 0xE0
-#define FLASH_FOLLOW_OTST2_RB_MASK 0x08
-
-#define FLASH_LED_TRIGGER_DEFAULT "none"
-#define FLASH_LED_HEADROOM_DEFAULT_MV 500
-#define FLASH_LED_STARTUP_DELAY_DEFAULT_US 128
-#define FLASH_LED_CLAMP_CURRENT_DEFAULT_MA 200
-#define FLASH_LED_THERMAL_DERATE_THRESHOLD_DEFAULT_C 80
-#define FLASH_LED_RAMP_UP_STEP_DEFAULT_US 3
-#define FLASH_LED_RAMP_DN_STEP_DEFAULT_US 3
-#define FLASH_LED_VPH_PWR_DROOP_THRESHOLD_DEFAULT_MV 3200
-#define FLASH_LED_VPH_PWR_DROOP_DEBOUNCE_TIME_DEFAULT_US 10
-#define FLASH_LED_THERMAL_DERATE_RATE_DEFAULT_PERCENT 2
-#define FLASH_RAMP_UP_DELAY_US_MIN 1000
-#define FLASH_RAMP_UP_DELAY_US_MAX 1001
-#define FLASH_RAMP_DN_DELAY_US_MIN 2160
-#define FLASH_RAMP_DN_DELAY_US_MAX 2161
-#define FLASH_BOOST_REGULATOR_PROBE_DELAY_MS 2000
-#define FLASH_TORCH_MAX_LEVEL 0x0F
-#define FLASH_MAX_LEVEL 0x4F
-#define FLASH_LED_FLASH_HW_VREG_OK 0x40
-#define FLASH_LED_FLASH_SW_VREG_OK 0x80
-#define FLASH_LED_STROBE_TYPE_HW 0x04
-#define FLASH_DURATION_DIVIDER 10
-#define FLASH_LED_HEADROOM_DIVIDER 100
-#define FLASH_LED_HEADROOM_OFFSET 2
-#define FLASH_LED_MAX_CURRENT_MA 1000
-#define FLASH_LED_THERMAL_THRESHOLD_MIN 95
-#define FLASH_LED_THERMAL_DEVIDER 10
-#define FLASH_LED_VPH_DROOP_THRESHOLD_MIN_MV 2500
-#define FLASH_LED_VPH_DROOP_THRESHOLD_DIVIDER 100
-#define FLASH_LED_HDRM_SNS_ENABLE 0x81
-#define FLASH_LED_HDRM_SNS_DISABLE 0x01
-#define FLASH_LED_UA_PER_MA 1000
-#define FLASH_LED_MASK_MODULE_MASK2_ENABLE 0x20
-#define FLASH_LED_MASK3_ENABLE_SHIFT 7
-#define FLASH_LED_MODULE_CTRL_DEFAULT 0x60
-#define FLASH_LED_CURRENT_READING_DELAY_MIN 5000
-#define FLASH_LED_CURRENT_READING_DELAY_MAX 5001
-#define FLASH_LED_OPEN_FAULT_DETECTED 0xC
-
-#define FLASH_UNLOCK_SECURE 0xA5
-#define FLASH_LED_TORCH_ENABLE 0x00
-#define FLASH_LED_TORCH_DISABLE 0x03
-#define FLASH_MODULE_ENABLE 0x80
-#define FLASH_LED0_TRIGGER 0x80
-#define FLASH_LED1_TRIGGER 0x40
-#define FLASH_LED0_ENABLEMENT 0x40
-#define FLASH_LED1_ENABLEMENT 0x20
-#define FLASH_LED_DISABLE 0x00
-#define FLASH_LED_MIN_CURRENT_MA 13
-#define FLASH_SUBTYPE_DUAL 0x01
-#define FLASH_SUBTYPE_SINGLE 0x02
-
-/*
- * ID represents physical LEDs for individual control purpose.
- */
-enum flash_led_id {
- FLASH_LED_0 = 0,
- FLASH_LED_1,
- FLASH_LED_SWITCH,
-};
-
-enum flash_led_type {
- FLASH = 0,
- TORCH,
- SWITCH,
-};
-
-enum thermal_derate_rate {
- RATE_1_PERCENT = 0,
- RATE_1P25_PERCENT,
- RATE_2_PERCENT,
- RATE_2P5_PERCENT,
- RATE_5_PERCENT,
-};
-
-enum current_ramp_steps {
- RAMP_STEP_0P2_US = 0,
- RAMP_STEP_0P4_US,
- RAMP_STEP_0P8_US,
- RAMP_STEP_1P6_US,
- RAMP_STEP_3P3_US,
- RAMP_STEP_6P7_US,
- RAMP_STEP_13P5_US,
- RAMP_STEP_27US,
-};
-
-struct flash_regulator_data {
- struct regulator *regs;
- const char *reg_name;
- u32 max_volt_uv;
-};
-
-/*
- * Configurations for each individual LED
- */
-struct flash_node_data {
- struct platform_device *pdev;
- struct regmap *regmap;
- struct led_classdev cdev;
- struct work_struct work;
- struct flash_regulator_data *reg_data;
- u16 max_current;
- u16 prgm_current;
- u16 prgm_current2;
- u16 duration;
- u8 id;
- u8 type;
- u8 trigger;
- u8 enable;
- u8 num_regulators;
- bool flash_on;
-};
-
-/*
- * Flash LED configuration read from device tree
- */
-struct flash_led_platform_data {
- unsigned int temp_threshold_num;
- unsigned int temp_derate_curr_num;
- unsigned int *die_temp_derate_curr_ma;
- unsigned int *die_temp_threshold_degc;
- u16 ramp_up_step;
- u16 ramp_dn_step;
- u16 vph_pwr_droop_threshold;
- u16 headroom;
- u16 clamp_current;
- u8 thermal_derate_threshold;
- u8 vph_pwr_droop_debounce_time;
- u8 startup_dly;
- u8 thermal_derate_rate;
- bool pmic_charger_support;
- bool self_check_en;
- bool thermal_derate_en;
- bool current_ramp_en;
- bool vph_pwr_droop_en;
- bool hdrm_sns_ch0_en;
- bool hdrm_sns_ch1_en;
- bool power_detect_en;
- bool mask3_en;
- bool follow_rb_disable;
- bool die_current_derate_en;
-};
-
-struct qpnp_flash_led_buffer {
- struct mutex debugfs_lock; /* Prevent thread concurrency */
- size_t rpos;
- size_t wpos;
- size_t len;
- char data[0];
-};
-
-/*
- * Flash LED data structure containing flash LED attributes
- */
-struct qpnp_flash_led {
- struct pmic_revid_data *revid_data;
- struct platform_device *pdev;
- struct regmap *regmap;
- struct flash_led_platform_data *pdata;
- struct pinctrl *pinctrl;
- struct pinctrl_state *gpio_state_active;
- struct pinctrl_state *gpio_state_suspend;
- struct flash_node_data *flash_node;
- struct power_supply *battery_psy;
- struct workqueue_struct *ordered_workq;
- struct qpnp_vadc_chip *vadc_dev;
- struct mutex flash_led_lock;
- struct qpnp_flash_led_buffer *log;
- struct dentry *dbgfs_root;
- int num_leds;
- u32 buffer_cnt;
- u16 base;
- u16 current_addr;
- u16 current2_addr;
- u8 peripheral_type;
- u8 fault_reg;
- bool gpio_enabled;
- bool charging_enabled;
- bool strobe_debug;
- bool dbg_feature_en;
- bool open_fault;
-};
-
-static u8 qpnp_flash_led_ctrl_dbg_regs[] = {
- 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
- 0x4A, 0x4B, 0x4C, 0x4F, 0x51, 0x52, 0x54, 0x55, 0x5A, 0x5C, 0x5D,
-};
-
-static int flash_led_dbgfs_file_open(struct qpnp_flash_led *led,
- struct file *file)
-{
- struct qpnp_flash_led_buffer *log;
- size_t logbufsize = SZ_4K;
-
- log = kzalloc(logbufsize, GFP_KERNEL);
- if (!log)
- return -ENOMEM;
-
- log->rpos = 0;
- log->wpos = 0;
- log->len = logbufsize - sizeof(*log);
- mutex_init(&log->debugfs_lock);
- led->log = log;
-
- led->buffer_cnt = 1;
- file->private_data = led;
-
- return 0;
-}
-
-static int flash_led_dfs_open(struct inode *inode, struct file *file)
-{
- struct qpnp_flash_led *led = inode->i_private;
-
- return flash_led_dbgfs_file_open(led, file);
-}
-
-static int flash_led_dfs_close(struct inode *inode, struct file *file)
-{
- struct qpnp_flash_led *led = file->private_data;
-
- if (led && led->log) {
- file->private_data = NULL;
- mutex_destroy(&led->log->debugfs_lock);
- kfree(led->log);
- }
-
- return 0;
-}
-
-#define MIN_BUFFER_WRITE_LEN 20
-static int print_to_log(struct qpnp_flash_led_buffer *log,
- const char *fmt, ...)
-{
- va_list args;
- int cnt;
- char *log_buf;
- size_t size = log->len - log->wpos;
-
- if (size < MIN_BUFFER_WRITE_LEN)
- return 0; /* not enough buffer left */
-
- log_buf = &log->data[log->wpos];
- va_start(args, fmt);
- cnt = vscnprintf(log_buf, size, fmt, args);
- va_end(args);
-
- log->wpos += cnt;
- return cnt;
-}
-
-static ssize_t flash_led_dfs_latched_reg_read(struct file *fp, char __user *buf,
- size_t count, loff_t *ppos) {
- struct qpnp_flash_led *led = fp->private_data;
- struct qpnp_flash_led_buffer *log = led->log;
- uint val;
- int rc = 0;
- size_t len;
- size_t ret;
-
- mutex_lock(&log->debugfs_lock);
- if ((log->rpos >= log->wpos && led->buffer_cnt == 0) ||
- ((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN))
- goto unlock_mutex;
-
- rc = regmap_read(led->regmap, INT_LATCHED_STS(led->base), &val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Unable to read from address %x, rc(%d)\n",
- INT_LATCHED_STS(led->base), rc);
- goto unlock_mutex;
- }
- led->buffer_cnt--;
-
- rc = print_to_log(log, "0x%05X ", INT_LATCHED_STS(led->base));
- if (rc == 0)
- goto unlock_mutex;
-
- rc = print_to_log(log, "0x%02X ", val);
- if (rc == 0)
- goto unlock_mutex;
-
- if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
- log->data[log->wpos - 1] = '\n';
-
- len = min(count, log->wpos - log->rpos);
-
- ret = copy_to_user(buf, &log->data[log->rpos], len);
- if (ret) {
- pr_err("error copy register value to user\n");
- rc = -EFAULT;
- goto unlock_mutex;
- }
-
- len -= ret;
- *ppos += len;
- log->rpos += len;
-
- rc = len;
-
-unlock_mutex:
- mutex_unlock(&log->debugfs_lock);
- return rc;
-}
-
-static ssize_t flash_led_dfs_fault_reg_read(struct file *fp, char __user *buf,
- size_t count, loff_t *ppos) {
- struct qpnp_flash_led *led = fp->private_data;
- struct qpnp_flash_led_buffer *log = led->log;
- int rc = 0;
- size_t len;
- size_t ret;
-
- mutex_lock(&log->debugfs_lock);
- if ((log->rpos >= log->wpos && led->buffer_cnt == 0) ||
- ((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN))
- goto unlock_mutex;
-
- led->buffer_cnt--;
-
- rc = print_to_log(log, "0x%05X ", FLASH_LED_FAULT_STATUS(led->base));
- if (rc == 0)
- goto unlock_mutex;
-
- rc = print_to_log(log, "0x%02X ", led->fault_reg);
- if (rc == 0)
- goto unlock_mutex;
-
- if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
- log->data[log->wpos - 1] = '\n';
-
- len = min(count, log->wpos - log->rpos);
-
- ret = copy_to_user(buf, &log->data[log->rpos], len);
- if (ret) {
- pr_err("error copy register value to user\n");
- rc = -EFAULT;
- goto unlock_mutex;
- }
-
- len -= ret;
- *ppos += len;
- log->rpos += len;
-
- rc = len;
-
-unlock_mutex:
- mutex_unlock(&log->debugfs_lock);
- return rc;
-}
-
-static ssize_t flash_led_dfs_fault_reg_enable(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos) {
-
- u8 *val;
- int pos = 0;
- int cnt = 0;
- int data;
- size_t ret = 0;
-
- struct qpnp_flash_led *led = file->private_data;
- char *kbuf;
-
- mutex_lock(&led->log->debugfs_lock);
- kbuf = kmalloc(count + 1, GFP_KERNEL);
- if (!kbuf) {
- ret = -ENOMEM;
- goto unlock_mutex;
- }
-
- ret = copy_from_user(kbuf, buf, count);
- if (!ret) {
- pr_err("failed to copy data from user\n");
- ret = -EFAULT;
- goto free_buf;
- }
-
- count -= ret;
- *ppos += count;
- kbuf[count] = '\0';
- val = kbuf;
- while (sscanf(kbuf + pos, "%i", &data) == 1) {
- pos++;
- val[cnt++] = data & 0xff;
- }
-
- if (!cnt)
- goto free_buf;
-
- ret = count;
- if (*val == 1)
- led->strobe_debug = true;
- else
- led->strobe_debug = false;
-
-free_buf:
- kfree(kbuf);
-unlock_mutex:
- mutex_unlock(&led->log->debugfs_lock);
- return ret;
-}
-
-static ssize_t flash_led_dfs_dbg_enable(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos) {
-
- u8 *val;
- int pos = 0;
- int cnt = 0;
- int data;
- size_t ret = 0;
- struct qpnp_flash_led *led = file->private_data;
- char *kbuf;
-
- mutex_lock(&led->log->debugfs_lock);
- kbuf = kmalloc(count + 1, GFP_KERNEL);
- if (!kbuf) {
- ret = -ENOMEM;
- goto unlock_mutex;
- }
-
- ret = copy_from_user(kbuf, buf, count);
- if (ret == count) {
- pr_err("failed to copy data from user\n");
- ret = -EFAULT;
- goto free_buf;
- }
- count -= ret;
- *ppos += count;
- kbuf[count] = '\0';
- val = kbuf;
- while (sscanf(kbuf + pos, "%i", &data) == 1) {
- pos++;
- val[cnt++] = data & 0xff;
- }
-
- if (!cnt)
- goto free_buf;
-
- ret = count;
- if (*val == 1)
- led->dbg_feature_en = true;
- else
- led->dbg_feature_en = false;
-
-free_buf:
- kfree(kbuf);
-unlock_mutex:
- mutex_unlock(&led->log->debugfs_lock);
- return ret;
-}
-
-static const struct file_operations flash_led_dfs_latched_reg_fops = {
- .open = flash_led_dfs_open,
- .release = flash_led_dfs_close,
- .read = flash_led_dfs_latched_reg_read,
-};
-
-static const struct file_operations flash_led_dfs_strobe_reg_fops = {
- .open = flash_led_dfs_open,
- .release = flash_led_dfs_close,
- .read = flash_led_dfs_fault_reg_read,
- .write = flash_led_dfs_fault_reg_enable,
-};
-
-static const struct file_operations flash_led_dfs_dbg_feature_fops = {
- .open = flash_led_dfs_open,
- .release = flash_led_dfs_close,
- .write = flash_led_dfs_dbg_enable,
-};
-
-static int
-qpnp_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask, u8 val)
-{
- int rc;
-
- rc = regmap_update_bits(led->regmap, addr, mask, val);
- if (rc)
- dev_err(&led->pdev->dev,
- "Unable to update_bits to addr=%x, rc(%d)\n", addr, rc);
-
- dev_dbg(&led->pdev->dev, "Write 0x%02X to addr 0x%02X\n", val, addr);
-
- return rc;
-}
-
-static int qpnp_flash_led_get_allowed_die_temp_curr(struct qpnp_flash_led *led,
- int64_t die_temp_degc)
-{
- int die_temp_curr_ma;
-
- if (die_temp_degc >= led->pdata->die_temp_threshold_degc[0])
- die_temp_curr_ma = 0;
- else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[1])
- die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[0];
- else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[2])
- die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[1];
- else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[3])
- die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[2];
- else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[4])
- die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[3];
- else
- die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[4];
-
- return die_temp_curr_ma;
-}
-
-static int64_t qpnp_flash_led_get_die_temp(struct qpnp_flash_led *led)
-{
- struct qpnp_vadc_result die_temp_result;
- int rc;
-
- rc = qpnp_vadc_read(led->vadc_dev, SPARE2, &die_temp_result);
- if (rc) {
- pr_err("failed to read the die temp\n");
- return -EINVAL;
- }
-
- return die_temp_result.physical;
-}
-
-static int qpnp_get_pmic_revid(struct qpnp_flash_led *led)
-{
- struct device_node *revid_dev_node;
-
- revid_dev_node = of_parse_phandle(led->pdev->dev.of_node,
- "qcom,pmic-revid", 0);
- if (!revid_dev_node) {
- dev_err(&led->pdev->dev,
- "qcom,pmic-revid property missing\n");
- return -EINVAL;
- }
-
- led->revid_data = get_revid_data(revid_dev_node);
- if (IS_ERR(led->revid_data)) {
- pr_err("Couldn't get revid data rc = %ld\n",
- PTR_ERR(led->revid_data));
- return PTR_ERR(led->revid_data);
- }
-
- return 0;
-}
-
-static int
-qpnp_flash_led_get_max_avail_current(struct flash_node_data *flash_node,
- struct qpnp_flash_led *led)
-{
- union power_supply_propval prop;
- int64_t chg_temp_milidegc, die_temp_degc;
- int max_curr_avail_ma = 2000;
- int allowed_die_temp_curr_ma = 2000;
- int rc;
-
- if (led->pdata->power_detect_en) {
- if (!led->battery_psy) {
- dev_err(&led->pdev->dev,
- "Failed to query power supply\n");
- return -EINVAL;
- }
-
- /*
- * When charging is enabled, enforce this new enablement
- * sequence to reduce fuel gauge reading resolution.
- */
- if (led->charging_enabled) {
- rc = qpnp_led_masked_write(led,
- FLASH_MODULE_ENABLE_CTRL(led->base),
- FLASH_MODULE_ENABLE, FLASH_MODULE_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Module enable reg write failed\n");
- return -EINVAL;
- }
-
- usleep_range(FLASH_LED_CURRENT_READING_DELAY_MIN,
- FLASH_LED_CURRENT_READING_DELAY_MAX);
- }
-
- power_supply_get_property(led->battery_psy,
- POWER_SUPPLY_PROP_FLASH_CURRENT_MAX, &prop);
- if (!prop.intval) {
- dev_err(&led->pdev->dev,
- "battery too low for flash\n");
- return -EINVAL;
- }
-
- max_curr_avail_ma = (prop.intval / FLASH_LED_UA_PER_MA);
- }
-
- /*
- * When thermal mitigation is available, this logic will execute to
- * derate current based upon the PMIC die temperature.
- */
- if (led->pdata->die_current_derate_en) {
- chg_temp_milidegc = qpnp_flash_led_get_die_temp(led);
- if (chg_temp_milidegc < 0)
- return -EINVAL;
-
- die_temp_degc = div_s64(chg_temp_milidegc, 1000);
- allowed_die_temp_curr_ma =
- qpnp_flash_led_get_allowed_die_temp_curr(led,
- die_temp_degc);
- if (allowed_die_temp_curr_ma < 0)
- return -EINVAL;
- }
-
- max_curr_avail_ma = (max_curr_avail_ma >= allowed_die_temp_curr_ma)
- ? allowed_die_temp_curr_ma : max_curr_avail_ma;
-
- return max_curr_avail_ma;
-}
-
-static ssize_t qpnp_flash_led_die_temp_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct qpnp_flash_led *led;
- struct flash_node_data *flash_node;
- unsigned long val;
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- ssize_t ret;
-
- ret = kstrtoul(buf, 10, &val);
- if (ret)
- return ret;
-
- flash_node = container_of(led_cdev, struct flash_node_data, cdev);
- led = dev_get_drvdata(&flash_node->pdev->dev);
-
- /*'0' for disable die_temp feature; non-zero to enable feature*/
- if (val == 0)
- led->pdata->die_current_derate_en = false;
- else
- led->pdata->die_current_derate_en = true;
-
- return count;
-}
-
-static ssize_t qpnp_led_strobe_type_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct flash_node_data *flash_node;
- unsigned long state;
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- ssize_t ret = -EINVAL;
-
- ret = kstrtoul(buf, 10, &state);
- if (ret)
- return ret;
-
- flash_node = container_of(led_cdev, struct flash_node_data, cdev);
-
- /* '0' for sw strobe; '1' for hw strobe */
- if (state == 1)
- flash_node->trigger |= FLASH_LED_STROBE_TYPE_HW;
- else
- flash_node->trigger &= ~FLASH_LED_STROBE_TYPE_HW;
-
- return count;
-}
-
-static ssize_t qpnp_flash_led_dump_regs_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct qpnp_flash_led *led;
- struct flash_node_data *flash_node;
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- int rc, i, count = 0;
- u16 addr;
- uint val;
-
- flash_node = container_of(led_cdev, struct flash_node_data, cdev);
- led = dev_get_drvdata(&flash_node->pdev->dev);
- for (i = 0; i < ARRAY_SIZE(qpnp_flash_led_ctrl_dbg_regs); i++) {
- addr = led->base + qpnp_flash_led_ctrl_dbg_regs[i];
- rc = regmap_read(led->regmap, addr, &val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Unable to read from addr=%x, rc(%d)\n",
- addr, rc);
- return -EINVAL;
- }
-
- count += snprintf(buf + count, PAGE_SIZE - count,
- "REG_0x%x = 0x%02x\n", addr, val);
-
- if (count >= PAGE_SIZE)
- return PAGE_SIZE - 1;
- }
-
- return count;
-}
-
-static ssize_t qpnp_flash_led_current_derate_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct qpnp_flash_led *led;
- struct flash_node_data *flash_node;
- unsigned long val;
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- ssize_t ret;
-
- ret = kstrtoul(buf, 10, &val);
- if (ret)
- return ret;
-
- flash_node = container_of(led_cdev, struct flash_node_data, cdev);
- led = dev_get_drvdata(&flash_node->pdev->dev);
-
- /*'0' for disable derate feature; non-zero to enable derate feature */
- if (val == 0)
- led->pdata->power_detect_en = false;
- else
- led->pdata->power_detect_en = true;
-
- return count;
-}
-
-static ssize_t qpnp_flash_led_max_current_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct qpnp_flash_led *led;
- struct flash_node_data *flash_node;
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- int max_curr_avail_ma = 0;
-
- flash_node = container_of(led_cdev, struct flash_node_data, cdev);
- led = dev_get_drvdata(&flash_node->pdev->dev);
-
- if (led->flash_node[0].flash_on)
- max_curr_avail_ma += led->flash_node[0].max_current;
- if (led->flash_node[1].flash_on)
- max_curr_avail_ma += led->flash_node[1].max_current;
-
- if (led->pdata->power_detect_en ||
- led->pdata->die_current_derate_en) {
- max_curr_avail_ma =
- qpnp_flash_led_get_max_avail_current(flash_node, led);
-
- if (max_curr_avail_ma < 0)
- return -EINVAL;
- }
-
- return snprintf(buf, PAGE_SIZE, "%u\n", max_curr_avail_ma);
-}
-
-static struct device_attribute qpnp_flash_led_attrs[] = {
- __ATTR(strobe, 0664, NULL, qpnp_led_strobe_type_store),
- __ATTR(reg_dump, 0664, qpnp_flash_led_dump_regs_show, NULL),
- __ATTR(enable_current_derate, 0664, NULL,
- qpnp_flash_led_current_derate_store),
- __ATTR(max_allowed_current, 0664, qpnp_flash_led_max_current_show,
- NULL),
- __ATTR(enable_die_temp_current_derate, 0664, NULL,
- qpnp_flash_led_die_temp_store),
-};
-
-static int qpnp_flash_led_get_thermal_derate_rate(const char *rate)
-{
- /*
- * return 5% derate as default value if user specifies
- * a value un-supported
- */
- if (strcmp(rate, "1_PERCENT") == 0)
- return RATE_1_PERCENT;
- else if (strcmp(rate, "1P25_PERCENT") == 0)
- return RATE_1P25_PERCENT;
- else if (strcmp(rate, "2_PERCENT") == 0)
- return RATE_2_PERCENT;
- else if (strcmp(rate, "2P5_PERCENT") == 0)
- return RATE_2P5_PERCENT;
- else if (strcmp(rate, "5_PERCENT") == 0)
- return RATE_5_PERCENT;
- else
- return RATE_5_PERCENT;
-}
-
-static int qpnp_flash_led_get_ramp_step(const char *step)
-{
- /*
- * return 27 us as default value if user specifies
- * a value un-supported
- */
- if (strcmp(step, "0P2_US") == 0)
- return RAMP_STEP_0P2_US;
- else if (strcmp(step, "0P4_US") == 0)
- return RAMP_STEP_0P4_US;
- else if (strcmp(step, "0P8_US") == 0)
- return RAMP_STEP_0P8_US;
- else if (strcmp(step, "1P6_US") == 0)
- return RAMP_STEP_1P6_US;
- else if (strcmp(step, "3P3_US") == 0)
- return RAMP_STEP_3P3_US;
- else if (strcmp(step, "6P7_US") == 0)
- return RAMP_STEP_6P7_US;
- else if (strcmp(step, "13P5_US") == 0)
- return RAMP_STEP_13P5_US;
- else
- return RAMP_STEP_27US;
-}
-
-static u8 qpnp_flash_led_get_droop_debounce_time(u8 val)
-{
- /*
- * return 10 us as default value if user specifies
- * a value un-supported
- */
- switch (val) {
- case 0:
- return 0;
- case 10:
- return 1;
- case 32:
- return 2;
- case 64:
- return 3;
- default:
- return 1;
- }
-}
-
-static u8 qpnp_flash_led_get_startup_dly(u8 val)
-{
- /*
- * return 128 us as default value if user specifies
- * a value un-supported
- */
- switch (val) {
- case 10:
- return 0;
- case 32:
- return 1;
- case 64:
- return 2;
- case 128:
- return 3;
- default:
- return 3;
- }
-}
-
-static int
-qpnp_flash_led_get_peripheral_type(struct qpnp_flash_led *led)
-{
- int rc;
- uint val;
-
- rc = regmap_read(led->regmap,
- FLASH_LED_PERIPHERAL_SUBTYPE(led->base), &val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Unable to read peripheral subtype\n");
- return -EINVAL;
- }
-
- return val;
-}
-
-static int qpnp_flash_led_module_disable(struct qpnp_flash_led *led,
- struct flash_node_data *flash_node)
-{
- union power_supply_propval psy_prop;
- int rc;
- uint val, tmp;
-
- rc = regmap_read(led->regmap, FLASH_LED_STROBE_CTRL(led->base), &val);
- if (rc) {
- dev_err(&led->pdev->dev, "Unable to read strobe reg\n");
- return -EINVAL;
- }
-
- tmp = (~flash_node->trigger) & val;
- if (!tmp) {
- if (flash_node->type == TORCH) {
- rc = qpnp_led_masked_write(led,
- FLASH_LED_UNLOCK_SECURE(led->base),
- FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Secure reg write failed\n");
- return -EINVAL;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_TORCH(led->base),
- FLASH_TORCH_MASK, FLASH_LED_TORCH_DISABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Torch reg write failed\n");
- return -EINVAL;
- }
- }
-
- if (led->battery_psy &&
- led->revid_data->pmic_subtype == PMI8996_SUBTYPE &&
- !led->revid_data->rev3) {
- psy_prop.intval = false;
- rc = power_supply_set_property(led->battery_psy,
- POWER_SUPPLY_PROP_FLASH_TRIGGER,
- &psy_prop);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Failed to enble charger i/p current limit\n");
- return -EINVAL;
- }
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_MODULE_ENABLE_CTRL(led->base),
- FLASH_MODULE_ENABLE_MASK,
- FLASH_LED_MODULE_CTRL_DEFAULT);
- if (rc) {
- dev_err(&led->pdev->dev, "Module disable failed\n");
- return -EINVAL;
- }
-
- if (led->pinctrl) {
- rc = pinctrl_select_state(led->pinctrl,
- led->gpio_state_suspend);
- if (rc) {
- dev_err(&led->pdev->dev,
- "failed to disable GPIO\n");
- return -EINVAL;
- }
- led->gpio_enabled = false;
- }
-
- if (led->battery_psy) {
- psy_prop.intval = false;
- rc = power_supply_set_property(led->battery_psy,
- POWER_SUPPLY_PROP_FLASH_ACTIVE,
- &psy_prop);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Failed to setup OTG pulse skip enable\n");
- return -EINVAL;
- }
- }
- }
-
- if (flash_node->trigger & FLASH_LED0_TRIGGER) {
- rc = qpnp_led_masked_write(led,
- led->current_addr,
- FLASH_CURRENT_MASK, 0x00);
- if (rc) {
- dev_err(&led->pdev->dev,
- "current register write failed\n");
- return -EINVAL;
- }
- }
-
- if (flash_node->trigger & FLASH_LED1_TRIGGER) {
- rc = qpnp_led_masked_write(led,
- led->current2_addr,
- FLASH_CURRENT_MASK, 0x00);
- if (rc) {
- dev_err(&led->pdev->dev,
- "current register write failed\n");
- return -EINVAL;
- }
- }
-
- if (flash_node->id == FLASH_LED_SWITCH)
- flash_node->trigger &= FLASH_LED_STROBE_TYPE_HW;
-
- return 0;
-}
-
-static enum
-led_brightness qpnp_flash_led_brightness_get(struct led_classdev *led_cdev)
-{
- return led_cdev->brightness;
-}
-
-static int flash_regulator_parse_dt(struct qpnp_flash_led *led,
- struct flash_node_data *flash_node) {
-
- int i = 0, rc;
- struct device_node *node = flash_node->cdev.dev->of_node;
- struct device_node *temp = NULL;
- const char *temp_string;
- u32 val;
-
- flash_node->reg_data = devm_kzalloc(&led->pdev->dev,
- sizeof(struct flash_regulator_data *) *
- flash_node->num_regulators,
- GFP_KERNEL);
- if (!flash_node->reg_data) {
- dev_err(&led->pdev->dev,
- "Unable to allocate memory\n");
- return -ENOMEM;
- }
-
- for_each_child_of_node(node, temp) {
- rc = of_property_read_string(temp, "regulator-name",
- &temp_string);
- if (!rc)
- flash_node->reg_data[i].reg_name = temp_string;
- else {
- dev_err(&led->pdev->dev,
- "Unable to read regulator name\n");
- return rc;
- }
-
- rc = of_property_read_u32(temp, "max-voltage", &val);
- if (!rc) {
- flash_node->reg_data[i].max_volt_uv = val;
- } else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev,
- "Unable to read max voltage\n");
- return rc;
- }
-
- i++;
- }
-
- return 0;
-}
-
-static int flash_regulator_setup(struct qpnp_flash_led *led,
- struct flash_node_data *flash_node, bool on)
-{
- int i, rc = 0;
-
- if (on == false) {
- i = flash_node->num_regulators;
- goto error_regulator_setup;
- }
-
- for (i = 0; i < flash_node->num_regulators; i++) {
- flash_node->reg_data[i].regs =
- regulator_get(flash_node->cdev.dev,
- flash_node->reg_data[i].reg_name);
- if (IS_ERR(flash_node->reg_data[i].regs)) {
- rc = PTR_ERR(flash_node->reg_data[i].regs);
- dev_err(&led->pdev->dev,
- "Failed to get regulator\n");
- goto error_regulator_setup;
- }
-
- if (regulator_count_voltages(flash_node->reg_data[i].regs)
- > 0) {
- rc = regulator_set_voltage(flash_node->reg_data[i].regs,
- flash_node->reg_data[i].max_volt_uv,
- flash_node->reg_data[i].max_volt_uv);
- if (rc) {
- dev_err(&led->pdev->dev,
- "regulator set voltage failed\n");
- regulator_put(flash_node->reg_data[i].regs);
- goto error_regulator_setup;
- }
- }
- }
-
- return rc;
-
-error_regulator_setup:
- while (i--) {
- if (regulator_count_voltages(flash_node->reg_data[i].regs)
- > 0) {
- regulator_set_voltage(flash_node->reg_data[i].regs,
- 0, flash_node->reg_data[i].max_volt_uv);
- }
-
- regulator_put(flash_node->reg_data[i].regs);
- }
-
- return rc;
-}
-
-static int flash_regulator_enable(struct qpnp_flash_led *led,
- struct flash_node_data *flash_node, bool on)
-{
- int i, rc = 0;
-
- if (on == false) {
- i = flash_node->num_regulators;
- goto error_regulator_enable;
- }
-
- for (i = 0; i < flash_node->num_regulators; i++) {
- rc = regulator_enable(flash_node->reg_data[i].regs);
- if (rc) {
- dev_err(&led->pdev->dev,
- "regulator enable failed\n");
- goto error_regulator_enable;
- }
- }
-
- return rc;
-
-error_regulator_enable:
- while (i--)
- regulator_disable(flash_node->reg_data[i].regs);
-
- return rc;
-}
-
-int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
- int *max_current)
-{
- struct led_classdev *led_cdev = trigger_to_lcdev(trig);
- struct flash_node_data *flash_node;
- struct qpnp_flash_led *led;
- int rc;
-
- if (!led_cdev) {
- pr_err("Invalid led_trigger provided\n");
- return -EINVAL;
- }
-
- flash_node = container_of(led_cdev, struct flash_node_data, cdev);
- led = dev_get_drvdata(&flash_node->pdev->dev);
-
- if (!(options & FLASH_LED_PREPARE_OPTIONS_MASK)) {
- dev_err(&led->pdev->dev, "Invalid options %d\n", options);
- return -EINVAL;
- }
-
- if (options & ENABLE_REGULATOR) {
- rc = flash_regulator_enable(led, flash_node, true);
- if (rc < 0) {
- dev_err(&led->pdev->dev,
- "enable regulator failed, rc=%d\n", rc);
- return rc;
- }
- }
-
- if (options & DISABLE_REGULATOR) {
- rc = flash_regulator_enable(led, flash_node, false);
- if (rc < 0) {
- dev_err(&led->pdev->dev,
- "disable regulator failed, rc=%d\n", rc);
- return rc;
- }
- }
-
- if (options & QUERY_MAX_CURRENT) {
- rc = qpnp_flash_led_get_max_avail_current(flash_node, led);
- if (rc < 0) {
- dev_err(&led->pdev->dev,
- "query max current failed, rc=%d\n", rc);
- return rc;
- }
- *max_current = rc;
- }
-
- return 0;
-}
-
-static void qpnp_flash_led_work(struct work_struct *work)
-{
- struct flash_node_data *flash_node = container_of(work,
- struct flash_node_data, work);
- struct qpnp_flash_led *led = dev_get_drvdata(&flash_node->pdev->dev);
- union power_supply_propval psy_prop;
- int rc, brightness = flash_node->cdev.brightness;
- int max_curr_avail_ma = 0;
- int total_curr_ma = 0;
- int i;
- u8 val;
- uint temp;
-
- mutex_lock(&led->flash_led_lock);
-
- if (!brightness)
- goto turn_off;
-
- if (led->open_fault) {
- dev_err(&led->pdev->dev, "Open fault detected\n");
- mutex_unlock(&led->flash_led_lock);
- return;
- }
-
- if (!flash_node->flash_on && flash_node->num_regulators > 0) {
- rc = flash_regulator_enable(led, flash_node, true);
- if (rc) {
- mutex_unlock(&led->flash_led_lock);
- return;
- }
- }
-
- if (!led->gpio_enabled && led->pinctrl) {
- rc = pinctrl_select_state(led->pinctrl,
- led->gpio_state_active);
- if (rc) {
- dev_err(&led->pdev->dev, "failed to enable GPIO\n");
- goto error_enable_gpio;
- }
- led->gpio_enabled = true;
- }
-
- if (led->dbg_feature_en) {
- rc = qpnp_led_masked_write(led,
- INT_SET_TYPE(led->base),
- FLASH_STATUS_REG_MASK, 0x1F);
- if (rc) {
- dev_err(&led->pdev->dev,
- "INT_SET_TYPE write failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- IN_POLARITY_HIGH(led->base),
- FLASH_STATUS_REG_MASK, 0x1F);
- if (rc) {
- dev_err(&led->pdev->dev,
- "IN_POLARITY_HIGH write failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- INT_EN_SET(led->base),
- FLASH_STATUS_REG_MASK, 0x1F);
- if (rc) {
- dev_err(&led->pdev->dev, "INT_EN_SET write failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- INT_LATCHED_CLR(led->base),
- FLASH_STATUS_REG_MASK, 0x1F);
- if (rc) {
- dev_err(&led->pdev->dev,
- "INT_LATCHED_CLR write failed\n");
- goto exit_flash_led_work;
- }
- }
-
- if (led->flash_node[led->num_leds - 1].id == FLASH_LED_SWITCH &&
- flash_node->id != FLASH_LED_SWITCH) {
- led->flash_node[led->num_leds - 1].trigger |=
- (0x80 >> flash_node->id);
- if (flash_node->id == FLASH_LED_0)
- led->flash_node[led->num_leds - 1].prgm_current =
- flash_node->prgm_current;
- else if (flash_node->id == FLASH_LED_1)
- led->flash_node[led->num_leds - 1].prgm_current2 =
- flash_node->prgm_current;
- }
-
- if (flash_node->type == TORCH) {
- rc = qpnp_led_masked_write(led,
- FLASH_LED_UNLOCK_SECURE(led->base),
- FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
- if (rc) {
- dev_err(&led->pdev->dev, "Secure reg write failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_TORCH(led->base),
- FLASH_TORCH_MASK, FLASH_LED_TORCH_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev, "Torch reg write failed\n");
- goto exit_flash_led_work;
- }
-
- if (flash_node->id == FLASH_LED_SWITCH) {
- val = (u8)(flash_node->prgm_current *
- FLASH_TORCH_MAX_LEVEL
- / flash_node->max_current);
- rc = qpnp_led_masked_write(led,
- led->current_addr,
- FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Torch reg write failed\n");
- goto exit_flash_led_work;
- }
-
- val = (u8)(flash_node->prgm_current2 *
- FLASH_TORCH_MAX_LEVEL
- / flash_node->max_current);
- rc = qpnp_led_masked_write(led,
- led->current2_addr,
- FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Torch reg write failed\n");
- goto exit_flash_led_work;
- }
- } else {
- val = (u8)(flash_node->prgm_current *
- FLASH_TORCH_MAX_LEVEL /
- flash_node->max_current);
- if (flash_node->id == FLASH_LED_0) {
- rc = qpnp_led_masked_write(led,
- led->current_addr,
- FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "current reg write failed\n");
- goto exit_flash_led_work;
- }
- } else {
- rc = qpnp_led_masked_write(led,
- led->current2_addr,
- FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "current reg write failed\n");
- goto exit_flash_led_work;
- }
- }
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_MAX_CURRENT(led->base),
- FLASH_CURRENT_MASK, FLASH_TORCH_MAX_LEVEL);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Max current reg write failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_MODULE_ENABLE_CTRL(led->base),
- FLASH_MODULE_ENABLE_MASK, FLASH_MODULE_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Module enable reg write failed\n");
- goto exit_flash_led_work;
- }
-
- if (led->pdata->hdrm_sns_ch0_en ||
- led->pdata->hdrm_sns_ch1_en) {
- if (flash_node->id == FLASH_LED_SWITCH) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- flash_node->trigger &
- FLASH_LED0_TRIGGER ?
- FLASH_LED_HDRM_SNS_ENABLE :
- FLASH_LED_HDRM_SNS_DISABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense enable failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- flash_node->trigger &
- FLASH_LED1_TRIGGER ?
- FLASH_LED_HDRM_SNS_ENABLE :
- FLASH_LED_HDRM_SNS_DISABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense enable failed\n");
- goto exit_flash_led_work;
- }
- } else if (flash_node->id == FLASH_LED_0) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- FLASH_LED_HDRM_SNS_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense disable failed\n");
- goto exit_flash_led_work;
- }
- } else if (flash_node->id == FLASH_LED_1) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- FLASH_LED_HDRM_SNS_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense disable failed\n");
- goto exit_flash_led_work;
- }
- }
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_LED_STROBE_CTRL(led->base),
- (flash_node->id == FLASH_LED_SWITCH ? FLASH_STROBE_MASK
- | FLASH_LED_STROBE_TYPE_HW
- : flash_node->trigger |
- FLASH_LED_STROBE_TYPE_HW),
- flash_node->trigger);
- if (rc) {
- dev_err(&led->pdev->dev, "Strobe reg write failed\n");
- goto exit_flash_led_work;
- }
- } else if (flash_node->type == FLASH) {
- if (flash_node->trigger & FLASH_LED0_TRIGGER)
- max_curr_avail_ma += flash_node->max_current;
- if (flash_node->trigger & FLASH_LED1_TRIGGER)
- max_curr_avail_ma += flash_node->max_current;
-
- psy_prop.intval = true;
- rc = power_supply_set_property(led->battery_psy,
- POWER_SUPPLY_PROP_FLASH_ACTIVE,
- &psy_prop);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Failed to setup OTG pulse skip enable\n");
- goto exit_flash_led_work;
- }
-
- if (led->pdata->power_detect_en ||
- led->pdata->die_current_derate_en) {
- if (led->battery_psy) {
- power_supply_get_property(led->battery_psy,
- POWER_SUPPLY_PROP_STATUS,
- &psy_prop);
- if (psy_prop.intval < 0) {
- dev_err(&led->pdev->dev,
- "Invalid battery status\n");
- goto exit_flash_led_work;
- }
-
- if (psy_prop.intval ==
- POWER_SUPPLY_STATUS_CHARGING)
- led->charging_enabled = true;
- else if (psy_prop.intval ==
- POWER_SUPPLY_STATUS_DISCHARGING
- || psy_prop.intval ==
- POWER_SUPPLY_STATUS_NOT_CHARGING)
- led->charging_enabled = false;
- }
- max_curr_avail_ma =
- qpnp_flash_led_get_max_avail_current
- (flash_node, led);
- if (max_curr_avail_ma < 0) {
- dev_err(&led->pdev->dev,
- "Failed to get max avail curr\n");
- goto exit_flash_led_work;
- }
- }
-
- if (flash_node->id == FLASH_LED_SWITCH) {
- if (flash_node->trigger & FLASH_LED0_TRIGGER)
- total_curr_ma += flash_node->prgm_current;
- if (flash_node->trigger & FLASH_LED1_TRIGGER)
- total_curr_ma += flash_node->prgm_current2;
-
- if (max_curr_avail_ma < total_curr_ma) {
- flash_node->prgm_current =
- (flash_node->prgm_current *
- max_curr_avail_ma) / total_curr_ma;
- flash_node->prgm_current2 =
- (flash_node->prgm_current2 *
- max_curr_avail_ma) / total_curr_ma;
- }
-
- val = (u8)(flash_node->prgm_current *
- FLASH_MAX_LEVEL / flash_node->max_current);
- rc = qpnp_led_masked_write(led,
- led->current_addr, FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Current register write failed\n");
- goto exit_flash_led_work;
- }
-
- val = (u8)(flash_node->prgm_current2 *
- FLASH_MAX_LEVEL / flash_node->max_current);
- rc = qpnp_led_masked_write(led,
- led->current2_addr, FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Current register write failed\n");
- goto exit_flash_led_work;
- }
- } else {
- if (max_curr_avail_ma < flash_node->prgm_current) {
- dev_err(&led->pdev->dev,
- "battery only supprots %d mA\n",
- max_curr_avail_ma);
- flash_node->prgm_current =
- (u16)max_curr_avail_ma;
- }
-
- val = (u8)(flash_node->prgm_current *
- FLASH_MAX_LEVEL
- / flash_node->max_current);
- if (flash_node->id == FLASH_LED_0) {
- rc = qpnp_led_masked_write(
- led,
- led->current_addr,
- FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "current reg write failed\n");
- goto exit_flash_led_work;
- }
- } else if (flash_node->id == FLASH_LED_1) {
- rc = qpnp_led_masked_write(
- led,
- led->current2_addr,
- FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "current reg write failed\n");
- goto exit_flash_led_work;
- }
- }
- }
-
- val = (u8)((flash_node->duration - FLASH_DURATION_DIVIDER)
- / FLASH_DURATION_DIVIDER);
- rc = qpnp_led_masked_write(led,
- FLASH_SAFETY_TIMER(led->base),
- FLASH_SAFETY_TIMER_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Safety timer reg write failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_MAX_CURRENT(led->base),
- FLASH_CURRENT_MASK, FLASH_MAX_LEVEL);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Max current reg write failed\n");
- goto exit_flash_led_work;
- }
-
- if (!led->charging_enabled) {
- rc = qpnp_led_masked_write(led,
- FLASH_MODULE_ENABLE_CTRL(led->base),
- FLASH_MODULE_ENABLE, FLASH_MODULE_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Module enable reg write failed\n");
- goto exit_flash_led_work;
- }
-
- usleep_range(FLASH_RAMP_UP_DELAY_US_MIN,
- FLASH_RAMP_UP_DELAY_US_MAX);
- }
-
- if (led->revid_data->pmic_subtype == PMI8996_SUBTYPE &&
- !led->revid_data->rev3) {
- rc = power_supply_set_property(led->battery_psy,
- POWER_SUPPLY_PROP_FLASH_TRIGGER,
- &psy_prop);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Failed to disable charger i/p curr limit\n");
- goto exit_flash_led_work;
- }
- }
-
- if (led->pdata->hdrm_sns_ch0_en ||
- led->pdata->hdrm_sns_ch1_en) {
- if (flash_node->id == FLASH_LED_SWITCH) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- (flash_node->trigger &
- FLASH_LED0_TRIGGER ?
- FLASH_LED_HDRM_SNS_ENABLE :
- FLASH_LED_HDRM_SNS_DISABLE));
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense enable failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- (flash_node->trigger &
- FLASH_LED1_TRIGGER ?
- FLASH_LED_HDRM_SNS_ENABLE :
- FLASH_LED_HDRM_SNS_DISABLE));
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense enable failed\n");
- goto exit_flash_led_work;
- }
- } else if (flash_node->id == FLASH_LED_0) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- FLASH_LED_HDRM_SNS_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense disable failed\n");
- goto exit_flash_led_work;
- }
- } else if (flash_node->id == FLASH_LED_1) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- FLASH_LED_HDRM_SNS_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense disable failed\n");
- goto exit_flash_led_work;
- }
- }
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_LED_STROBE_CTRL(led->base),
- (flash_node->id == FLASH_LED_SWITCH ? FLASH_STROBE_MASK
- | FLASH_LED_STROBE_TYPE_HW
- : flash_node->trigger |
- FLASH_LED_STROBE_TYPE_HW),
- flash_node->trigger);
- if (rc) {
- dev_err(&led->pdev->dev, "Strobe reg write failed\n");
- goto exit_flash_led_work;
- }
-
- if (led->strobe_debug && led->dbg_feature_en) {
- udelay(2000);
- rc = regmap_read(led->regmap,
- FLASH_LED_FAULT_STATUS(led->base),
- &temp);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Unable to read from addr= %x, rc(%d)\n",
- FLASH_LED_FAULT_STATUS(led->base), rc);
- goto exit_flash_led_work;
- }
- led->fault_reg = temp;
- }
- } else {
- pr_err("Both Torch and Flash cannot be select at same time\n");
- for (i = 0; i < led->num_leds; i++)
- led->flash_node[i].flash_on = false;
- goto turn_off;
- }
-
- flash_node->flash_on = true;
- mutex_unlock(&led->flash_led_lock);
-
- return;
-
-turn_off:
- if (led->flash_node[led->num_leds - 1].id == FLASH_LED_SWITCH &&
- flash_node->id != FLASH_LED_SWITCH)
- led->flash_node[led->num_leds - 1].trigger &=
- ~(0x80 >> flash_node->id);
- if (flash_node->type == TORCH) {
- /*
- * Checking LED fault status detects hardware open fault.
- * If fault occurs, all subsequent LED enablement requests
- * will be rejected to protect hardware.
- */
- rc = regmap_read(led->regmap,
- FLASH_LED_FAULT_STATUS(led->base), &temp);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Failed to read out fault status register\n");
- goto exit_flash_led_work;
- }
-
- led->open_fault |= (val & FLASH_LED_OPEN_FAULT_DETECTED);
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_LED_STROBE_CTRL(led->base),
- (flash_node->id == FLASH_LED_SWITCH ? FLASH_STROBE_MASK
- | FLASH_LED_STROBE_TYPE_HW
- : flash_node->trigger
- | FLASH_LED_STROBE_TYPE_HW),
- FLASH_LED_DISABLE);
- if (rc) {
- dev_err(&led->pdev->dev, "Strobe disable failed\n");
- goto exit_flash_led_work;
- }
-
- usleep_range(FLASH_RAMP_DN_DELAY_US_MIN, FLASH_RAMP_DN_DELAY_US_MAX);
-exit_flash_hdrm_sns:
- if (led->pdata->hdrm_sns_ch0_en) {
- if (flash_node->id == FLASH_LED_0 ||
- flash_node->id == FLASH_LED_SWITCH) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- FLASH_LED_HDRM_SNS_DISABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense disable failed\n");
- goto exit_flash_hdrm_sns;
- }
- }
- }
-
- if (led->pdata->hdrm_sns_ch1_en) {
- if (flash_node->id == FLASH_LED_1 ||
- flash_node->id == FLASH_LED_SWITCH) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- FLASH_LED_HDRM_SNS_DISABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense disable failed\n");
- goto exit_flash_hdrm_sns;
- }
- }
- }
-exit_flash_led_work:
- rc = qpnp_flash_led_module_disable(led, flash_node);
- if (rc) {
- dev_err(&led->pdev->dev, "Module disable failed\n");
- goto exit_flash_led_work;
- }
-error_enable_gpio:
- if (flash_node->flash_on && flash_node->num_regulators > 0)
- flash_regulator_enable(led, flash_node, false);
-
- flash_node->flash_on = false;
- mutex_unlock(&led->flash_led_lock);
-}
-
-static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- struct flash_node_data *flash_node;
- struct qpnp_flash_led *led;
-
- flash_node = container_of(led_cdev, struct flash_node_data, cdev);
- led = dev_get_drvdata(&flash_node->pdev->dev);
-
- if (value < LED_OFF) {
- pr_err("Invalid brightness value\n");
- return;
- }
-
- if (value > flash_node->cdev.max_brightness)
- value = flash_node->cdev.max_brightness;
-
- flash_node->cdev.brightness = value;
- if (led->flash_node[led->num_leds - 1].id ==
- FLASH_LED_SWITCH) {
- if (flash_node->type == TORCH)
- led->flash_node[led->num_leds - 1].type = TORCH;
- else if (flash_node->type == FLASH)
- led->flash_node[led->num_leds - 1].type = FLASH;
-
- led->flash_node[led->num_leds - 1].max_current
- = flash_node->max_current;
-
- if (flash_node->id == FLASH_LED_0 ||
- flash_node->id == FLASH_LED_1) {
- if (value < FLASH_LED_MIN_CURRENT_MA && value != 0)
- value = FLASH_LED_MIN_CURRENT_MA;
-
- flash_node->prgm_current = value;
- flash_node->flash_on = value ? true : false;
- } else if (flash_node->id == FLASH_LED_SWITCH) {
- if (!value) {
- flash_node->prgm_current = 0;
- flash_node->prgm_current2 = 0;
- }
- }
- } else {
- if (value < FLASH_LED_MIN_CURRENT_MA && value != 0)
- value = FLASH_LED_MIN_CURRENT_MA;
- flash_node->prgm_current = value;
- }
-
- queue_work(led->ordered_workq, &flash_node->work);
-}
-
-static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
-{
- int rc;
- u8 val, temp_val;
- uint val_int;
-
- rc = qpnp_led_masked_write(led,
- FLASH_MODULE_ENABLE_CTRL(led->base),
- FLASH_MODULE_ENABLE_MASK,
- FLASH_LED_MODULE_CTRL_DEFAULT);
- if (rc) {
- dev_err(&led->pdev->dev, "Module disable failed\n");
- return rc;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_LED_STROBE_CTRL(led->base),
- FLASH_STROBE_MASK, FLASH_LED_DISABLE);
- if (rc) {
- dev_err(&led->pdev->dev, "Strobe disable failed\n");
- return rc;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_LED_TMR_CTRL(led->base),
- FLASH_TMR_MASK, FLASH_TMR_SAFETY);
- if (rc) {
- dev_err(&led->pdev->dev,
- "LED timer ctrl reg write failed(%d)\n", rc);
- return rc;
- }
-
- val = (u8)(led->pdata->headroom / FLASH_LED_HEADROOM_DIVIDER -
- FLASH_LED_HEADROOM_OFFSET);
- rc = qpnp_led_masked_write(led,
- FLASH_HEADROOM(led->base),
- FLASH_HEADROOM_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "Headroom reg write failed\n");
- return rc;
- }
-
- val = qpnp_flash_led_get_startup_dly(led->pdata->startup_dly);
-
- rc = qpnp_led_masked_write(led,
- FLASH_STARTUP_DELAY(led->base),
- FLASH_STARTUP_DLY_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "Startup delay reg write failed\n");
- return rc;
- }
-
- val = (u8)(led->pdata->clamp_current * FLASH_MAX_LEVEL /
- FLASH_LED_MAX_CURRENT_MA);
- rc = qpnp_led_masked_write(led,
- FLASH_CLAMP_CURRENT(led->base),
- FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "Clamp current reg write failed\n");
- return rc;
- }
-
- if (led->pdata->pmic_charger_support)
- val = FLASH_LED_FLASH_HW_VREG_OK;
- else
- val = FLASH_LED_FLASH_SW_VREG_OK;
- rc = qpnp_led_masked_write(led,
- FLASH_VREG_OK_FORCE(led->base),
- FLASH_VREG_OK_FORCE_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "VREG OK force reg write failed\n");
- return rc;
- }
-
- if (led->pdata->self_check_en)
- val = FLASH_MODULE_ENABLE;
- else
- val = FLASH_LED_DISABLE;
- rc = qpnp_led_masked_write(led,
- FLASH_FAULT_DETECT(led->base),
- FLASH_FAULT_DETECT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "Fault detect reg write failed\n");
- return rc;
- }
-
- val = 0x0;
- val |= led->pdata->mask3_en << FLASH_LED_MASK3_ENABLE_SHIFT;
- val |= FLASH_LED_MASK_MODULE_MASK2_ENABLE;
- rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
- FLASH_MASK_MODULE_CONTRL_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "Mask module enable failed\n");
- return rc;
- }
-
- rc = regmap_read(led->regmap, FLASH_PERPH_RESET_CTRL(led->base),
- &val_int);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Unable to read from address %x, rc(%d)\n",
- FLASH_PERPH_RESET_CTRL(led->base), rc);
- return -EINVAL;
- }
- val = (u8)val_int;
-
- if (led->pdata->follow_rb_disable) {
- rc = qpnp_led_masked_write(led,
- FLASH_LED_UNLOCK_SECURE(led->base),
- FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
- if (rc) {
- dev_err(&led->pdev->dev, "Secure reg write failed\n");
- return -EINVAL;
- }
-
- val |= FLASH_FOLLOW_OTST2_RB_MASK;
- rc = qpnp_led_masked_write(led,
- FLASH_PERPH_RESET_CTRL(led->base),
- FLASH_FOLLOW_OTST2_RB_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "failed to reset OTST2_RB bit\n");
- return rc;
- }
- } else {
- rc = qpnp_led_masked_write(led,
- FLASH_LED_UNLOCK_SECURE(led->base),
- FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
- if (rc) {
- dev_err(&led->pdev->dev, "Secure reg write failed\n");
- return -EINVAL;
- }
-
- val &= ~FLASH_FOLLOW_OTST2_RB_MASK;
- rc = qpnp_led_masked_write(led,
- FLASH_PERPH_RESET_CTRL(led->base),
- FLASH_FOLLOW_OTST2_RB_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "failed to reset OTST2_RB bit\n");
- return rc;
- }
- }
-
- if (!led->pdata->thermal_derate_en)
- val = 0x0;
- else {
- val = led->pdata->thermal_derate_en << 7;
- val |= led->pdata->thermal_derate_rate << 3;
- val |= (led->pdata->thermal_derate_threshold -
- FLASH_LED_THERMAL_THRESHOLD_MIN) /
- FLASH_LED_THERMAL_DEVIDER;
- }
- rc = qpnp_led_masked_write(led,
- FLASH_THERMAL_DRATE(led->base),
- FLASH_THERMAL_DERATE_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "Thermal derate reg write failed\n");
- return rc;
- }
-
- if (!led->pdata->current_ramp_en)
- val = 0x0;
- else {
- val = led->pdata->current_ramp_en << 7;
- val |= led->pdata->ramp_up_step << 3;
- val |= led->pdata->ramp_dn_step;
- }
- rc = qpnp_led_masked_write(led,
- FLASH_CURRENT_RAMP(led->base),
- FLASH_CURRENT_RAMP_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "Current ramp reg write failed\n");
- return rc;
- }
-
- if (!led->pdata->vph_pwr_droop_en)
- val = 0x0;
- else {
- val = led->pdata->vph_pwr_droop_en << 7;
- val |= ((led->pdata->vph_pwr_droop_threshold -
- FLASH_LED_VPH_DROOP_THRESHOLD_MIN_MV) /
- FLASH_LED_VPH_DROOP_THRESHOLD_DIVIDER) << 4;
- temp_val =
- qpnp_flash_led_get_droop_debounce_time(
- led->pdata->vph_pwr_droop_debounce_time);
- if (temp_val == 0xFF) {
- dev_err(&led->pdev->dev, "Invalid debounce time\n");
- return temp_val;
- }
-
- val |= temp_val;
- }
- rc = qpnp_led_masked_write(led,
- FLASH_VPH_PWR_DROOP(led->base),
- FLASH_VPH_PWR_DROOP_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "VPH PWR droop reg write failed\n");
- return rc;
- }
-
- led->battery_psy = power_supply_get_by_name("battery");
- if (!led->battery_psy) {
- dev_err(&led->pdev->dev,
- "Failed to get battery power supply\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
- struct flash_node_data *flash_node)
-{
- const char *temp_string;
- struct device_node *node = flash_node->cdev.dev->of_node;
- struct device_node *temp = NULL;
- int rc = 0, num_regs = 0;
- u32 val;
-
- rc = of_property_read_string(node, "label", &temp_string);
- if (!rc) {
- if (strcmp(temp_string, "flash") == 0)
- flash_node->type = FLASH;
- else if (strcmp(temp_string, "torch") == 0)
- flash_node->type = TORCH;
- else if (strcmp(temp_string, "switch") == 0)
- flash_node->type = SWITCH;
- else {
- dev_err(&led->pdev->dev, "Wrong flash LED type\n");
- return -EINVAL;
- }
- } else if (rc < 0) {
- dev_err(&led->pdev->dev, "Unable to read flash type\n");
- return rc;
- }
-
- rc = of_property_read_u32(node, "qcom,current", &val);
- if (!rc) {
- if (val < FLASH_LED_MIN_CURRENT_MA)
- val = FLASH_LED_MIN_CURRENT_MA;
- flash_node->prgm_current = val;
- } else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev, "Unable to read current\n");
- return rc;
- }
-
- rc = of_property_read_u32(node, "qcom,id", &val);
- if (!rc)
- flash_node->id = (u8)val;
- else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev, "Unable to read led ID\n");
- return rc;
- }
-
- if (flash_node->type == SWITCH || flash_node->type == FLASH) {
- rc = of_property_read_u32(node, "qcom,duration", &val);
- if (!rc)
- flash_node->duration = (u16)val;
- else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev, "Unable to read duration\n");
- return rc;
- }
- }
-
- switch (led->peripheral_type) {
- case FLASH_SUBTYPE_SINGLE:
- flash_node->trigger = FLASH_LED0_TRIGGER;
- break;
- case FLASH_SUBTYPE_DUAL:
- if (flash_node->id == FLASH_LED_0)
- flash_node->trigger = FLASH_LED0_TRIGGER;
- else if (flash_node->id == FLASH_LED_1)
- flash_node->trigger = FLASH_LED1_TRIGGER;
- break;
- default:
- dev_err(&led->pdev->dev, "Invalid peripheral type\n");
- }
-
- while ((temp = of_get_next_child(node, temp))) {
- if (of_find_property(temp, "regulator-name", NULL))
- num_regs++;
- }
-
- if (num_regs)
- flash_node->num_regulators = num_regs;
-
- return rc;
-}
-
-static int qpnp_flash_led_parse_common_dt(
- struct qpnp_flash_led *led,
- struct device_node *node)
-{
- int rc;
- u32 val, temp_val;
- const char *temp;
-
- led->pdata->headroom = FLASH_LED_HEADROOM_DEFAULT_MV;
- rc = of_property_read_u32(node, "qcom,headroom", &val);
- if (!rc)
- led->pdata->headroom = (u16)val;
- else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev, "Unable to read headroom\n");
- return rc;
- }
-
- led->pdata->startup_dly = FLASH_LED_STARTUP_DELAY_DEFAULT_US;
- rc = of_property_read_u32(node, "qcom,startup-dly", &val);
- if (!rc)
- led->pdata->startup_dly = (u8)val;
- else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev, "Unable to read startup delay\n");
- return rc;
- }
-
- led->pdata->clamp_current = FLASH_LED_CLAMP_CURRENT_DEFAULT_MA;
- rc = of_property_read_u32(node, "qcom,clamp-current", &val);
- if (!rc) {
- if (val < FLASH_LED_MIN_CURRENT_MA)
- val = FLASH_LED_MIN_CURRENT_MA;
- led->pdata->clamp_current = (u16)val;
- } else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev, "Unable to read clamp current\n");
- return rc;
- }
-
- led->pdata->pmic_charger_support =
- of_property_read_bool(node,
- "qcom,pmic-charger-support");
-
- led->pdata->self_check_en =
- of_property_read_bool(node, "qcom,self-check-enabled");
-
- led->pdata->thermal_derate_en =
- of_property_read_bool(node,
- "qcom,thermal-derate-enabled");
-
- if (led->pdata->thermal_derate_en) {
- led->pdata->thermal_derate_rate =
- FLASH_LED_THERMAL_DERATE_RATE_DEFAULT_PERCENT;
- rc = of_property_read_string(node, "qcom,thermal-derate-rate",
- &temp);
- if (!rc) {
- temp_val =
- qpnp_flash_led_get_thermal_derate_rate(temp);
- if (temp_val < 0) {
- dev_err(&led->pdev->dev,
- "Invalid thermal derate rate\n");
- return -EINVAL;
- }
-
- led->pdata->thermal_derate_rate = (u8)temp_val;
- } else {
- dev_err(&led->pdev->dev,
- "Unable to read thermal derate rate\n");
- return -EINVAL;
- }
-
- led->pdata->thermal_derate_threshold =
- FLASH_LED_THERMAL_DERATE_THRESHOLD_DEFAULT_C;
- rc = of_property_read_u32(node, "qcom,thermal-derate-threshold",
- &val);
- if (!rc)
- led->pdata->thermal_derate_threshold = (u8)val;
- else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev,
- "Unable to read thermal derate threshold\n");
- return rc;
- }
- }
-
- led->pdata->current_ramp_en =
- of_property_read_bool(node,
- "qcom,current-ramp-enabled");
- if (led->pdata->current_ramp_en) {
- led->pdata->ramp_up_step = FLASH_LED_RAMP_UP_STEP_DEFAULT_US;
- rc = of_property_read_string(node, "qcom,ramp_up_step", &temp);
- if (!rc) {
- temp_val = qpnp_flash_led_get_ramp_step(temp);
- if (temp_val < 0) {
- dev_err(&led->pdev->dev,
- "Invalid ramp up step values\n");
- return -EINVAL;
- }
- led->pdata->ramp_up_step = (u8)temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev,
- "Unable to read ramp up steps\n");
- return rc;
- }
-
- led->pdata->ramp_dn_step = FLASH_LED_RAMP_DN_STEP_DEFAULT_US;
- rc = of_property_read_string(node, "qcom,ramp_dn_step", &temp);
- if (!rc) {
- temp_val = qpnp_flash_led_get_ramp_step(temp);
- if (temp_val < 0) {
- dev_err(&led->pdev->dev,
- "Invalid ramp down step values\n");
- return rc;
- }
- led->pdata->ramp_dn_step = (u8)temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev,
- "Unable to read ramp down steps\n");
- return rc;
- }
- }
-
- led->pdata->vph_pwr_droop_en = of_property_read_bool(node,
- "qcom,vph-pwr-droop-enabled");
- if (led->pdata->vph_pwr_droop_en) {
- led->pdata->vph_pwr_droop_threshold =
- FLASH_LED_VPH_PWR_DROOP_THRESHOLD_DEFAULT_MV;
- rc = of_property_read_u32(node,
- "qcom,vph-pwr-droop-threshold", &val);
- if (!rc) {
- led->pdata->vph_pwr_droop_threshold = (u16)val;
- } else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev,
- "Unable to read VPH PWR droop threshold\n");
- return rc;
- }
-
- led->pdata->vph_pwr_droop_debounce_time =
- FLASH_LED_VPH_PWR_DROOP_DEBOUNCE_TIME_DEFAULT_US;
- rc = of_property_read_u32(node,
- "qcom,vph-pwr-droop-debounce-time", &val);
- if (!rc)
- led->pdata->vph_pwr_droop_debounce_time = (u8)val;
- else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev,
- "Unable to read VPH PWR droop debounce time\n");
- return rc;
- }
- }
-
- led->pdata->hdrm_sns_ch0_en = of_property_read_bool(node,
- "qcom,headroom-sense-ch0-enabled");
-
- led->pdata->hdrm_sns_ch1_en = of_property_read_bool(node,
- "qcom,headroom-sense-ch1-enabled");
-
- led->pdata->power_detect_en = of_property_read_bool(node,
- "qcom,power-detect-enabled");
-
- led->pdata->mask3_en = of_property_read_bool(node,
- "qcom,otst2-module-enabled");
-
- led->pdata->follow_rb_disable = of_property_read_bool(node,
- "qcom,follow-otst2-rb-disabled");
-
- led->pdata->die_current_derate_en = of_property_read_bool(node,
- "qcom,die-current-derate-enabled");
-
- if (led->pdata->die_current_derate_en) {
- led->vadc_dev = qpnp_get_vadc(&led->pdev->dev, "die-temp");
- if (IS_ERR(led->vadc_dev)) {
- pr_err("VADC channel property Missing\n");
- return -EINVAL;
- }
-
- if (of_find_property(node, "qcom,die-temp-threshold",
- &led->pdata->temp_threshold_num)) {
- if (led->pdata->temp_threshold_num > 0) {
- led->pdata->die_temp_threshold_degc =
- devm_kzalloc(&led->pdev->dev,
- led->pdata->temp_threshold_num,
- GFP_KERNEL);
-
- if (led->pdata->die_temp_threshold_degc
- == NULL) {
- dev_err(&led->pdev->dev,
- "failed to allocate die temp array\n");
- return -ENOMEM;
- }
- led->pdata->temp_threshold_num /=
- sizeof(unsigned int);
-
- rc = of_property_read_u32_array(node,
- "qcom,die-temp-threshold",
- led->pdata->die_temp_threshold_degc,
- led->pdata->temp_threshold_num);
- if (rc) {
- dev_err(&led->pdev->dev,
- "couldn't read temp threshold rc=%d\n",
- rc);
- return rc;
- }
- }
- }
-
- if (of_find_property(node, "qcom,die-temp-derate-current",
- &led->pdata->temp_derate_curr_num)) {
- if (led->pdata->temp_derate_curr_num > 0) {
- led->pdata->die_temp_derate_curr_ma =
- devm_kzalloc(&led->pdev->dev,
- led->pdata->temp_derate_curr_num,
- GFP_KERNEL);
- if (led->pdata->die_temp_derate_curr_ma
- == NULL) {
- dev_err(&led->pdev->dev,
- "failed to allocate die derate current array\n");
- return -ENOMEM;
- }
- led->pdata->temp_derate_curr_num /=
- sizeof(unsigned int);
-
- rc = of_property_read_u32_array(node,
- "qcom,die-temp-derate-current",
- led->pdata->die_temp_derate_curr_ma,
- led->pdata->temp_derate_curr_num);
- if (rc) {
- dev_err(&led->pdev->dev,
- "couldn't read temp limits rc =%d\n",
- rc);
- return rc;
- }
- }
- }
- if (led->pdata->temp_threshold_num !=
- led->pdata->temp_derate_curr_num) {
- pr_err("Both array size are not same\n");
- return -EINVAL;
- }
- }
-
- led->pinctrl = devm_pinctrl_get(&led->pdev->dev);
- if (IS_ERR_OR_NULL(led->pinctrl)) {
- dev_err(&led->pdev->dev, "Unable to acquire pinctrl\n");
- led->pinctrl = NULL;
- return 0;
- }
-
- led->gpio_state_active = pinctrl_lookup_state(led->pinctrl,
- "flash_led_enable");
- if (IS_ERR_OR_NULL(led->gpio_state_active)) {
- dev_err(&led->pdev->dev, "Cannot lookup LED active state\n");
- devm_pinctrl_put(led->pinctrl);
- led->pinctrl = NULL;
- return PTR_ERR(led->gpio_state_active);
- }
-
- led->gpio_state_suspend = pinctrl_lookup_state(led->pinctrl,
- "flash_led_disable");
- if (IS_ERR_OR_NULL(led->gpio_state_suspend)) {
- dev_err(&led->pdev->dev, "Cannot lookup LED disable state\n");
- devm_pinctrl_put(led->pinctrl);
- led->pinctrl = NULL;
- return PTR_ERR(led->gpio_state_suspend);
- }
-
- return 0;
-}
-
-static int qpnp_flash_led_probe(struct platform_device *pdev)
-{
- struct qpnp_flash_led *led;
- unsigned int base;
- struct device_node *node, *temp;
- struct dentry *root, *file;
- int rc, i = 0, j, num_leds = 0;
- u32 val;
-
- root = NULL;
- node = pdev->dev.of_node;
- if (node == NULL) {
- dev_info(&pdev->dev, "No flash device defined\n");
- return -ENODEV;
- }
-
- rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
- if (rc < 0) {
- dev_err(&pdev->dev,
- "Couldn't find reg in node = %s rc = %d\n",
- pdev->dev.of_node->full_name, rc);
- return rc;
- }
-
- led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
- if (!led)
- return -ENOMEM;
-
- led->regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!led->regmap) {
- dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
- return -EINVAL;
- }
-
- led->base = base;
- led->pdev = pdev;
- led->current_addr = FLASH_LED0_CURRENT(led->base);
- led->current2_addr = FLASH_LED1_CURRENT(led->base);
-
- led->pdata = devm_kzalloc(&pdev->dev, sizeof(*led->pdata), GFP_KERNEL);
- if (!led->pdata)
- return -ENOMEM;
-
- led->peripheral_type = (u8)qpnp_flash_led_get_peripheral_type(led);
- if (led->peripheral_type < 0) {
- dev_err(&pdev->dev, "Failed to get peripheral type\n");
- return rc;
- }
-
- rc = qpnp_flash_led_parse_common_dt(led, node);
- if (rc) {
- dev_err(&pdev->dev,
- "Failed to get common config for flash LEDs\n");
- return rc;
- }
-
- rc = qpnp_flash_led_init_settings(led);
- if (rc) {
- dev_err(&pdev->dev, "Failed to initialize flash LED\n");
- return rc;
- }
-
- rc = qpnp_get_pmic_revid(led);
- if (rc)
- return rc;
-
- temp = NULL;
- while ((temp = of_get_next_child(node, temp)))
- num_leds++;
-
- if (!num_leds)
- return -ECHILD;
-
- led->flash_node = devm_kzalloc(&pdev->dev,
- (sizeof(struct flash_node_data) * num_leds),
- GFP_KERNEL);
- if (!led->flash_node) {
- dev_err(&pdev->dev, "Unable to allocate memory\n");
- return -ENOMEM;
- }
-
- mutex_init(&led->flash_led_lock);
-
- led->ordered_workq = alloc_ordered_workqueue("flash_led_workqueue", 0);
- if (!led->ordered_workq) {
- dev_err(&pdev->dev, "Failed to allocate ordered workqueue\n");
- return -ENOMEM;
- }
-
- for_each_child_of_node(node, temp) {
- led->flash_node[i].cdev.brightness_set =
- qpnp_flash_led_brightness_set;
- led->flash_node[i].cdev.brightness_get =
- qpnp_flash_led_brightness_get;
- led->flash_node[i].pdev = pdev;
-
- INIT_WORK(&led->flash_node[i].work, qpnp_flash_led_work);
- rc = of_property_read_string(temp, "qcom,led-name",
- &led->flash_node[i].cdev.name);
- if (rc < 0) {
- dev_err(&led->pdev->dev,
- "Unable to read flash name\n");
- return rc;
- }
-
- rc = of_property_read_string(temp, "qcom,default-led-trigger",
- &led->flash_node[i].cdev.default_trigger);
- if (rc < 0) {
- dev_err(&led->pdev->dev,
- "Unable to read trigger name\n");
- return rc;
- }
-
- rc = of_property_read_u32(temp, "qcom,max-current", &val);
- if (!rc) {
- if (val < FLASH_LED_MIN_CURRENT_MA)
- val = FLASH_LED_MIN_CURRENT_MA;
- led->flash_node[i].max_current = (u16)val;
- led->flash_node[i].cdev.max_brightness = val;
- } else {
- dev_err(&led->pdev->dev,
- "Unable to read max current\n");
- return rc;
- }
- rc = led_classdev_register(&pdev->dev,
- &led->flash_node[i].cdev);
- if (rc) {
- dev_err(&pdev->dev, "Unable to register led\n");
- goto error_led_register;
- }
-
- led->flash_node[i].cdev.dev->of_node = temp;
-
- rc = qpnp_flash_led_parse_each_led_dt(led, &led->flash_node[i]);
- if (rc) {
- dev_err(&pdev->dev,
- "Failed to parse config for each LED\n");
- goto error_led_register;
- }
-
- if (led->flash_node[i].num_regulators) {
- rc = flash_regulator_parse_dt(led, &led->flash_node[i]);
- if (rc) {
- dev_err(&pdev->dev,
- "Unable to parse regulator data\n");
- goto error_led_register;
- }
-
- rc = flash_regulator_setup(led, &led->flash_node[i],
- true);
- if (rc) {
- dev_err(&pdev->dev,
- "Unable to set up regulator\n");
- goto error_led_register;
- }
- }
-
- for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++) {
- rc =
- sysfs_create_file(&led->flash_node[i].cdev.dev->kobj,
- &qpnp_flash_led_attrs[j].attr);
- if (rc)
- goto error_led_register;
- }
-
- i++;
- }
-
- led->num_leds = i;
-
- root = debugfs_create_dir("flashLED", NULL);
- if (IS_ERR_OR_NULL(root)) {
- pr_err("Error creating top level directory err%ld",
- (long)root);
- if (PTR_ERR(root) == -ENODEV)
- pr_err("debugfs is not enabled in kernel");
- goto error_led_debugfs;
- }
-
- led->dbgfs_root = root;
- file = debugfs_create_file("enable_debug", 0600, root, led,
- &flash_led_dfs_dbg_feature_fops);
- if (!file) {
- pr_err("error creating 'enable_debug' entry\n");
- goto error_led_debugfs;
- }
-
- file = debugfs_create_file("latched", 0600, root, led,
- &flash_led_dfs_latched_reg_fops);
- if (!file) {
- pr_err("error creating 'latched' entry\n");
- goto error_led_debugfs;
- }
-
- file = debugfs_create_file("strobe", 0600, root, led,
- &flash_led_dfs_strobe_reg_fops);
- if (!file) {
- pr_err("error creating 'strobe' entry\n");
- goto error_led_debugfs;
- }
-
- dev_set_drvdata(&pdev->dev, led);
-
- return 0;
-
-error_led_debugfs:
- i = led->num_leds - 1;
- j = ARRAY_SIZE(qpnp_flash_led_attrs) - 1;
-error_led_register:
- for (; i >= 0; i--) {
- for (; j >= 0; j--)
- sysfs_remove_file(&led->flash_node[i].cdev.dev->kobj,
- &qpnp_flash_led_attrs[j].attr);
- j = ARRAY_SIZE(qpnp_flash_led_attrs) - 1;
- led_classdev_unregister(&led->flash_node[i].cdev);
- }
- debugfs_remove_recursive(root);
- mutex_destroy(&led->flash_led_lock);
- destroy_workqueue(led->ordered_workq);
-
- return rc;
-}
-
-static int qpnp_flash_led_remove(struct platform_device *pdev)
-{
- struct qpnp_flash_led *led = dev_get_drvdata(&pdev->dev);
- int i, j;
-
- for (i = led->num_leds - 1; i >= 0; i--) {
- if (led->flash_node[i].reg_data) {
- if (led->flash_node[i].flash_on)
- flash_regulator_enable(led,
- &led->flash_node[i], false);
- flash_regulator_setup(led, &led->flash_node[i],
- false);
- }
- for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++)
- sysfs_remove_file(&led->flash_node[i].cdev.dev->kobj,
- &qpnp_flash_led_attrs[j].attr);
- led_classdev_unregister(&led->flash_node[i].cdev);
- }
- debugfs_remove_recursive(led->dbgfs_root);
- mutex_destroy(&led->flash_led_lock);
- destroy_workqueue(led->ordered_workq);
-
- return 0;
-}
-
-static const struct of_device_id spmi_match_table[] = {
- { .compatible = "qcom,qpnp-flash-led",},
- { },
-};
-
-static struct platform_driver qpnp_flash_led_driver = {
- .driver = {
- .name = "qcom,qpnp-flash-led",
- .of_match_table = spmi_match_table,
- },
- .probe = qpnp_flash_led_probe,
- .remove = qpnp_flash_led_remove,
-};
-
-static int __init qpnp_flash_led_init(void)
-{
- return platform_driver_register(&qpnp_flash_led_driver);
-}
-late_initcall(qpnp_flash_led_init);
-
-static void __exit qpnp_flash_led_exit(void)
-{
- platform_driver_unregister(&qpnp_flash_led_driver);
-}
-module_exit(qpnp_flash_led_exit);
-
-MODULE_DESCRIPTION("QPNP Flash LED driver");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("leds:leds-qpnp-flash");
diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c
index 1e24c79c3f0a..950244f1e4e8 100644
--- a/drivers/leds/leds-qpnp-wled.c
+++ b/drivers/leds/leds-qpnp-wled.c
@@ -33,6 +33,7 @@
/* ctrl registers */
#define QPNP_WLED_FAULT_STATUS(b) (b + 0x08)
+#define QPNP_WLED_INT_RT_STS(b) (b + 0x10)
#define QPNP_WLED_EN_REG(b) (b + 0x46)
#define QPNP_WLED_FDBK_OP_REG(b) (b + 0x48)
#define QPNP_WLED_VREF_REG(b) (b + 0x49)
@@ -44,6 +45,7 @@
#define QPNP_WLED_SOFTSTART_RAMP_DLY(b) (b + 0x53)
#define QPNP_WLED_VLOOP_COMP_RES_REG(b) (b + 0x55)
#define QPNP_WLED_VLOOP_COMP_GM_REG(b) (b + 0x56)
+#define QPNP_WLED_EN_PSM_REG(b) (b + 0x5A)
#define QPNP_WLED_PSM_CTRL_REG(b) (b + 0x5B)
#define QPNP_WLED_LCD_AUTO_PFM_REG(b) (b + 0x5C)
#define QPNP_WLED_SC_PRO_REG(b) (b + 0x5E)
@@ -82,12 +84,13 @@
#define QPNP_WLED_VREF_PSM_MIN_MV 400
#define QPNP_WLED_VREF_PSM_MAX_MV 750
#define QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV 450
-#define QPNP_WLED_PSM_CTRL_OVERWRITE 0x80
+#define QPNP_WLED_PSM_OVERWRITE_BIT BIT(7)
#define QPNP_WLED_LCD_AUTO_PFM_DFLT_THRESH 1
#define QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX 0xF
#define QPNP_WLED_LCD_AUTO_PFM_EN_SHIFT 7
#define QPNP_WLED_LCD_AUTO_PFM_EN_BIT BIT(7)
#define QPNP_WLED_LCD_AUTO_PFM_THRESH_MASK GENMASK(3, 0)
+#define QPNP_WLED_EN_PSM_BIT BIT(7)
#define QPNP_WLED_ILIM_MASK GENMASK(2, 0)
#define QPNP_WLED_ILIM_OVERWRITE BIT(7)
@@ -117,6 +120,9 @@
QPNP_WLED_TEST4_EN_CLAMP_BIT | \
QPNP_WLED_TEST4_EN_SOFT_START_BIT)
#define QPNP_WLED_TEST4_EN_IIND_UP 0x1
+#define QPNP_WLED_ILIM_FAULT_BIT BIT(0)
+#define QPNP_WLED_OVP_FAULT_BIT BIT(1)
+#define QPNP_WLED_SC_FAULT_BIT BIT(2)
/* sink registers */
#define QPNP_WLED_CURR_SINK_REG(b) (b + 0x46)
@@ -335,6 +341,7 @@ static struct wled_vref_setting vref_setting_pmi8998 = {
* @ lcd_auto_pfm_thresh - the threshold for lcd auto pfm mode
* @ loop_auto_gm_en - select if auto gm is enabled
* @ lcd_auto_pfm_en - select if auto pfm is enabled in lcd mode
+ * @ lcd_psm_ctrl - select if psm needs to be controlled in lcd mode
* @ avdd_mode_spmi - enable avdd programming via spmi
* @ en_9b_dim_res - enable or disable 9bit dimming
* @ en_phase_stag - enable or disable phase staggering
@@ -380,6 +387,7 @@ struct qpnp_wled {
u8 lcd_auto_pfm_thresh;
bool loop_auto_gm_en;
bool lcd_auto_pfm_en;
+ bool lcd_psm_ctrl;
bool avdd_mode_spmi;
bool en_9b_dim_res;
bool en_phase_stag;
@@ -549,6 +557,30 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
return 0;
}
+static int qpnp_wled_psm_config(struct qpnp_wled *wled, bool enable)
+{
+ int rc;
+
+ if (!wled->lcd_psm_ctrl)
+ return 0;
+
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_EN_PSM_REG(wled->ctrl_base),
+ QPNP_WLED_EN_PSM_BIT,
+ enable ? QPNP_WLED_EN_PSM_BIT : 0);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base),
+ QPNP_WLED_PSM_OVERWRITE_BIT,
+ enable ? QPNP_WLED_PSM_OVERWRITE_BIT : 0);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
static int qpnp_wled_module_en(struct qpnp_wled *wled,
u16 base_addr, bool state)
{
@@ -561,21 +593,31 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled,
if (rc < 0)
return rc;
- if (wled->ovp_irq > 0) {
- if (state && wled->ovp_irq_disabled) {
- /*
- * Wait for at least 10ms before enabling OVP fault
- * interrupt after enabling the module so that soft
- * start is completed. Keep OVP interrupt disabled
- * when the module is disabled.
- */
- usleep_range(10000, 11000);
+ /*
+ * Wait for at least 10ms before enabling OVP fault interrupt after
+ * enabling the module so that soft start is completed. Also, this
+ * delay can be used to control PSM during enable when required. Keep
+ * OVP interrupt disabled when the module is disabled.
+ */
+ if (state) {
+ usleep_range(10000, 11000);
+ rc = qpnp_wled_psm_config(wled, false);
+ if (rc < 0)
+ return rc;
+
+ if (wled->ovp_irq > 0 && wled->ovp_irq_disabled) {
enable_irq(wled->ovp_irq);
wled->ovp_irq_disabled = false;
- } else if (!state && !wled->ovp_irq_disabled) {
+ }
+ } else {
+ if (wled->ovp_irq > 0 && !wled->ovp_irq_disabled) {
disable_irq(wled->ovp_irq);
wled->ovp_irq_disabled = true;
}
+
+ rc = qpnp_wled_psm_config(wled, true);
+ if (rc < 0)
+ return rc;
}
return 0;
@@ -990,7 +1032,7 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
reg &= QPNP_WLED_VREF_PSM_MASK;
reg |= ((wled->vref_psm_mv - QPNP_WLED_VREF_PSM_MIN_MV)/
QPNP_WLED_VREF_PSM_STEP_MV);
- reg |= QPNP_WLED_PSM_CTRL_OVERWRITE;
+ reg |= QPNP_WLED_PSM_OVERWRITE_BIT;
rc = qpnp_wled_write_reg(wled,
QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base), reg);
if (rc)
@@ -1053,16 +1095,25 @@ static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled)
{
struct qpnp_wled *wled = _wled;
int rc;
- u8 val;
+ u8 fault_sts, int_sts;
rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &val);
+ QPNP_WLED_INT_RT_STS(wled->ctrl_base), &int_sts);
+ if (rc < 0) {
+ pr_err("Error in reading WLED_INT_RT_STS rc=%d\n", rc);
+ return IRQ_HANDLED;
+ }
+
+ rc = qpnp_wled_read_reg(wled,
+ QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &fault_sts);
if (rc < 0) {
pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc);
return IRQ_HANDLED;
}
- pr_err("WLED OVP fault detected, fault_status= %x\n", val);
+ if (fault_sts & (QPNP_WLED_OVP_FAULT_BIT | QPNP_WLED_ILIM_FAULT_BIT))
+ pr_err("WLED OVP fault detected, int_sts=%x fault_sts= %x\n",
+ int_sts, fault_sts);
return IRQ_HANDLED;
}
@@ -1677,6 +1728,8 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
wled->ovp_irq, rc);
return rc;
}
+ disable_irq(wled->ovp_irq);
+ wled->ovp_irq_disabled = true;
}
if (wled->sc_irq >= 0) {
@@ -2063,6 +2116,8 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
wled->en_ext_pfet_sc_pro = of_property_read_bool(pdev->dev.of_node,
"qcom,en-ext-pfet-sc-pro");
+ wled->lcd_psm_ctrl = of_property_read_bool(pdev->dev.of_node,
+ "qcom,lcd-psm-ctrl");
return 0;
}
diff --git a/drivers/lightnvm/rrpc.c b/drivers/lightnvm/rrpc.c
index a9859489acf6..596347f345db 100644
--- a/drivers/lightnvm/rrpc.c
+++ b/drivers/lightnvm/rrpc.c
@@ -287,8 +287,10 @@ static int rrpc_move_valid_pages(struct rrpc *rrpc, struct rrpc_block *rblk)
}
page = mempool_alloc(rrpc->page_pool, GFP_NOIO);
- if (!page)
+ if (!page) {
+ bio_put(bio);
return -ENOMEM;
+ }
while ((slot = find_first_zero_bit(rblk->invalid_pages,
nr_pgs_per_blk)) < nr_pgs_per_blk) {
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index e85bcae50f65..e540b7942eba 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -112,8 +112,7 @@ struct iv_tcw_private {
* and encrypts / decrypts at the same time.
*/
enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID,
- DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD,
- DM_CRYPT_EXIT_THREAD};
+ DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD };
/*
* The fields in here must be read only after initialization.
@@ -1204,18 +1203,20 @@ continue_locked:
if (!RB_EMPTY_ROOT(&cc->write_tree))
goto pop_from_list;
- if (unlikely(test_bit(DM_CRYPT_EXIT_THREAD, &cc->flags))) {
- spin_unlock_irq(&cc->write_thread_wait.lock);
- break;
- }
-
- __set_current_state(TASK_INTERRUPTIBLE);
+ set_current_state(TASK_INTERRUPTIBLE);
__add_wait_queue(&cc->write_thread_wait, &wait);
spin_unlock_irq(&cc->write_thread_wait.lock);
+ if (unlikely(kthread_should_stop())) {
+ set_task_state(current, TASK_RUNNING);
+ remove_wait_queue(&cc->write_thread_wait, &wait);
+ break;
+ }
+
schedule();
+ set_task_state(current, TASK_RUNNING);
spin_lock_irq(&cc->write_thread_wait.lock);
__remove_wait_queue(&cc->write_thread_wait, &wait);
goto continue_locked;
@@ -1530,13 +1531,8 @@ static void crypt_dtr(struct dm_target *ti)
if (!cc)
return;
- if (cc->write_thread) {
- spin_lock_irq(&cc->write_thread_wait.lock);
- set_bit(DM_CRYPT_EXIT_THREAD, &cc->flags);
- wake_up_locked(&cc->write_thread_wait);
- spin_unlock_irq(&cc->write_thread_wait.lock);
+ if (cc->write_thread)
kthread_stop(cc->write_thread);
- }
if (cc->io_queue)
destroy_workqueue(cc->io_queue);
@@ -1928,6 +1924,13 @@ static int crypt_map(struct dm_target *ti, struct bio *bio)
return DM_MAPIO_REMAPPED;
}
+ /*
+ * Check if bio is too large, split as needed.
+ */
+ if (unlikely(bio->bi_iter.bi_size > (BIO_MAX_PAGES << PAGE_SHIFT)) &&
+ bio_data_dir(bio) == WRITE)
+ dm_accept_partial_bio(bio, ((BIO_MAX_PAGES << PAGE_SHIFT) >> SECTOR_SHIFT));
+
io = dm_per_bio_data(bio, cc->per_bio_data_size);
crypt_io_init(io, cc, bio, dm_target_offset(ti, bio->bi_iter.bi_sector));
io->ctx.req = (struct ablkcipher_request *)(io + 1);
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c
index cd0a93df4cb7..8e9e928dafba 100644
--- a/drivers/md/dm-flakey.c
+++ b/drivers/md/dm-flakey.c
@@ -289,15 +289,13 @@ static int flakey_map(struct dm_target *ti, struct bio *bio)
pb->bio_submitted = true;
/*
- * Map reads as normal only if corrupt_bio_byte set.
+ * Error reads if neither corrupt_bio_byte or drop_writes are set.
+ * Otherwise, flakey_end_io() will decide if the reads should be modified.
*/
if (bio_data_dir(bio) == READ) {
- /* If flags were specified, only corrupt those that match. */
- if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == READ) &&
- all_corrupt_bio_flags_match(bio, fc))
- goto map_bio;
- else
+ if (!fc->corrupt_bio_byte && !test_bit(DROP_WRITES, &fc->flags))
return -EIO;
+ goto map_bio;
}
/*
@@ -334,14 +332,21 @@ static int flakey_end_io(struct dm_target *ti, struct bio *bio, int error)
struct flakey_c *fc = ti->private;
struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
- /*
- * Corrupt successful READs while in down state.
- */
if (!error && pb->bio_submitted && (bio_data_dir(bio) == READ)) {
- if (fc->corrupt_bio_byte)
+ if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == READ) &&
+ all_corrupt_bio_flags_match(bio, fc)) {
+ /*
+ * Corrupt successful matching READs while in down state.
+ */
corrupt_bio_data(bio, fc);
- else
+
+ } else if (!test_bit(DROP_WRITES, &fc->flags)) {
+ /*
+ * Error read during the down_interval if drop_writes
+ * wasn't configured.
+ */
return -EIO;
+ }
}
return error;
diff --git a/drivers/md/dm-log-writes.c b/drivers/md/dm-log-writes.c
index 624589d51c2c..c8b513ee117c 100644
--- a/drivers/md/dm-log-writes.c
+++ b/drivers/md/dm-log-writes.c
@@ -258,12 +258,12 @@ static int log_one_block(struct log_writes_c *lc,
goto out;
sector++;
- bio = bio_alloc(GFP_KERNEL, block->vec_cnt);
+ atomic_inc(&lc->io_blocks);
+ bio = bio_alloc(GFP_KERNEL, min(block->vec_cnt, BIO_MAX_PAGES));
if (!bio) {
DMERR("Couldn't alloc log bio");
goto error;
}
- atomic_inc(&lc->io_blocks);
bio->bi_iter.bi_size = 0;
bio->bi_iter.bi_sector = sector;
bio->bi_bdev = lc->logdev->bdev;
@@ -280,7 +280,7 @@ static int log_one_block(struct log_writes_c *lc,
if (ret != block->vecs[i].bv_len) {
atomic_inc(&lc->io_blocks);
submit_bio(WRITE, bio);
- bio = bio_alloc(GFP_KERNEL, block->vec_cnt - i);
+ bio = bio_alloc(GFP_KERNEL, min(block->vec_cnt - i, BIO_MAX_PAGES));
if (!bio) {
DMERR("Couldn't alloc log bio");
goto error;
@@ -456,9 +456,9 @@ static int log_writes_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto bad;
}
- ret = -EINVAL;
lc->log_kthread = kthread_run(log_writes_kthread, lc, "log-write");
- if (!lc->log_kthread) {
+ if (IS_ERR(lc->log_kthread)) {
+ ret = PTR_ERR(lc->log_kthread);
ti->error = "Couldn't alloc kthread";
dm_put_device(ti, lc->dev);
dm_put_device(ti, lc->logdev);
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index cfa29f574c2a..5b2ef966012b 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -1220,10 +1220,10 @@ static void activate_path(struct work_struct *work)
{
struct pgpath *pgpath =
container_of(work, struct pgpath, activate_path.work);
+ struct request_queue *q = bdev_get_queue(pgpath->path.dev->bdev);
- if (pgpath->is_active)
- scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
- pg_init_done, pgpath);
+ if (pgpath->is_active && !blk_queue_dying(q))
+ scsi_dh_activate(q, pg_init_done, pgpath);
else
pg_init_done(pgpath, SCSI_DH_DEV_OFFLINED);
}
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index f2a363a89629..115bd3846c3f 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -1288,6 +1288,7 @@ static int mirror_end_io(struct dm_target *ti, struct bio *bio, int error)
dm_bio_restore(bd, bio);
bio_record->details.bi_bdev = NULL;
+ bio->bi_error = 0;
queue_bio(ms, bio, rw);
return DM_ENDIO_INCOMPLETE;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index d8615788b17d..dd1cde5458b8 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -2267,8 +2267,6 @@ static void cleanup_mapped_device(struct mapped_device *md)
if (md->bs)
bioset_free(md->bs);
- cleanup_srcu_struct(&md->io_barrier);
-
if (md->disk) {
spin_lock(&_minor_lock);
md->disk->private_data = NULL;
@@ -2280,6 +2278,8 @@ static void cleanup_mapped_device(struct mapped_device *md)
if (md->queue)
blk_cleanup_queue(md->queue);
+ cleanup_srcu_struct(&md->io_barrier);
+
if (md->bdev) {
bdput(md->bdev);
md->bdev = NULL;
@@ -2876,6 +2876,7 @@ EXPORT_SYMBOL_GPL(dm_device_name);
static void __dm_destroy(struct mapped_device *md, bool wait)
{
+ struct request_queue *q = dm_get_md_queue(md);
struct dm_table *map;
int srcu_idx;
@@ -2886,6 +2887,10 @@ static void __dm_destroy(struct mapped_device *md, bool wait)
set_bit(DMF_FREEING, &md->flags);
spin_unlock(&_minor_lock);
+ spin_lock_irq(q->queue_lock);
+ queue_flag_set(QUEUE_FLAG_DYING, q);
+ spin_unlock_irq(q->queue_lock);
+
if (dm_request_based(md) && md->kworker_task)
flush_kthread_worker(&md->kworker);
@@ -3252,10 +3257,11 @@ static int __dm_resume(struct mapped_device *md, struct dm_table *map)
int dm_resume(struct mapped_device *md)
{
- int r = -EINVAL;
+ int r;
struct dm_table *map = NULL;
retry:
+ r = -EINVAL;
mutex_lock_nested(&md->suspend_lock, SINGLE_DEPTH_NESTING);
if (!dm_suspended_md(md))
@@ -3279,8 +3285,6 @@ retry:
goto out;
clear_bit(DMF_SUSPENDED, &md->flags);
-
- r = 0;
out:
mutex_unlock(&md->suspend_lock);
diff --git a/drivers/md/md.c b/drivers/md/md.c
index c57fdf847b47..c1c7d4fb4b77 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -7572,16 +7572,12 @@ EXPORT_SYMBOL(unregister_md_cluster_operations);
int md_setup_cluster(struct mddev *mddev, int nodes)
{
- int err;
-
- err = request_module("md-cluster");
- if (err) {
- pr_err("md-cluster module not found.\n");
- return -ENOENT;
- }
-
+ if (!md_cluster_ops)
+ request_module("md-cluster");
spin_lock(&pers_lock);
+ /* ensure module won't be unloaded */
if (!md_cluster_ops || !try_module_get(md_cluster_mod)) {
+ pr_err("can't find md-cluster module or get it's reference.\n");
spin_unlock(&pers_lock);
return -ENOENT;
}
diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c
index cfc005ee11d8..7fc72de2434c 100644
--- a/drivers/media/dvb-frontends/mb86a20s.c
+++ b/drivers/media/dvb-frontends/mb86a20s.c
@@ -71,25 +71,27 @@ static struct regdata mb86a20s_init1[] = {
};
static struct regdata mb86a20s_init2[] = {
- { 0x28, 0x22 }, { 0x29, 0x00 }, { 0x2a, 0x1f }, { 0x2b, 0xf0 },
+ { 0x50, 0xd1 }, { 0x51, 0x22 },
+ { 0x39, 0x01 },
+ { 0x71, 0x00 },
{ 0x3b, 0x21 },
- { 0x3c, 0x38 },
+ { 0x3c, 0x3a },
{ 0x01, 0x0d },
- { 0x04, 0x08 }, { 0x05, 0x03 },
+ { 0x04, 0x08 }, { 0x05, 0x05 },
{ 0x04, 0x0e }, { 0x05, 0x00 },
- { 0x04, 0x0f }, { 0x05, 0x37 },
- { 0x04, 0x0b }, { 0x05, 0x78 },
+ { 0x04, 0x0f }, { 0x05, 0x14 },
+ { 0x04, 0x0b }, { 0x05, 0x8c },
{ 0x04, 0x00 }, { 0x05, 0x00 },
- { 0x04, 0x01 }, { 0x05, 0x1e },
- { 0x04, 0x02 }, { 0x05, 0x07 },
- { 0x04, 0x03 }, { 0x05, 0xd0 },
+ { 0x04, 0x01 }, { 0x05, 0x07 },
+ { 0x04, 0x02 }, { 0x05, 0x0f },
+ { 0x04, 0x03 }, { 0x05, 0xa0 },
{ 0x04, 0x09 }, { 0x05, 0x00 },
{ 0x04, 0x0a }, { 0x05, 0xff },
- { 0x04, 0x27 }, { 0x05, 0x00 },
+ { 0x04, 0x27 }, { 0x05, 0x64 },
{ 0x04, 0x28 }, { 0x05, 0x00 },
- { 0x04, 0x1e }, { 0x05, 0x00 },
- { 0x04, 0x29 }, { 0x05, 0x64 },
- { 0x04, 0x32 }, { 0x05, 0x02 },
+ { 0x04, 0x1e }, { 0x05, 0xff },
+ { 0x04, 0x29 }, { 0x05, 0x0a },
+ { 0x04, 0x32 }, { 0x05, 0x0a },
{ 0x04, 0x14 }, { 0x05, 0x02 },
{ 0x04, 0x04 }, { 0x05, 0x00 },
{ 0x04, 0x05 }, { 0x05, 0x22 },
@@ -97,8 +99,6 @@ static struct regdata mb86a20s_init2[] = {
{ 0x04, 0x07 }, { 0x05, 0xd8 },
{ 0x04, 0x12 }, { 0x05, 0x00 },
{ 0x04, 0x13 }, { 0x05, 0xff },
- { 0x04, 0x15 }, { 0x05, 0x4e },
- { 0x04, 0x16 }, { 0x05, 0x20 },
/*
* On this demod, when the bit count reaches the count below,
@@ -152,42 +152,36 @@ static struct regdata mb86a20s_init2[] = {
{ 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */
{ 0x45, 0x04 }, /* CN symbol 4 */
{ 0x48, 0x04 }, /* CN manual mode */
-
+ { 0x50, 0xd5 }, { 0x51, 0x01 },
{ 0x50, 0xd6 }, { 0x51, 0x1f },
{ 0x50, 0xd2 }, { 0x51, 0x03 },
- { 0x50, 0xd7 }, { 0x51, 0xbf },
- { 0x28, 0x74 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0xff },
- { 0x28, 0x46 }, { 0x29, 0x00 }, { 0x2a, 0x1a }, { 0x2b, 0x0c },
-
- { 0x04, 0x40 }, { 0x05, 0x00 },
- { 0x28, 0x00 }, { 0x2b, 0x08 },
- { 0x28, 0x05 }, { 0x2b, 0x00 },
+ { 0x50, 0xd7 }, { 0x51, 0x3f },
{ 0x1c, 0x01 },
- { 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x1f },
- { 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x18 },
- { 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x12 },
- { 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x30 },
- { 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x37 },
- { 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 },
- { 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x09 },
- { 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x06 },
- { 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x7b },
- { 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x76 },
- { 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x7d },
- { 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x08 },
- { 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0b },
- { 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x00 },
- { 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xf2 },
- { 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xf3 },
- { 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x05 },
- { 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 },
- { 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f },
- { 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xef },
- { 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xd8 },
- { 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xf1 },
- { 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x3d },
- { 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x94 },
- { 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0xba },
+ { 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x03 },
+ { 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0d },
+ { 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 },
+ { 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x01 },
+ { 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x21 },
+ { 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x29 },
+ { 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 },
+ { 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x31 },
+ { 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0e },
+ { 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x4e },
+ { 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x46 },
+ { 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f },
+ { 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x56 },
+ { 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x35 },
+ { 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbe },
+ { 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0x84 },
+ { 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x03 }, { 0x2b, 0xee },
+ { 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x98 },
+ { 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x9f },
+ { 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xb2 },
+ { 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0xc2 },
+ { 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0x4a },
+ { 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbc },
+ { 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x04 }, { 0x2b, 0xba },
+ { 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0x14 },
{ 0x50, 0x1e }, { 0x51, 0x5d },
{ 0x50, 0x22 }, { 0x51, 0x00 },
{ 0x50, 0x23 }, { 0x51, 0xc8 },
@@ -196,9 +190,7 @@ static struct regdata mb86a20s_init2[] = {
{ 0x50, 0x26 }, { 0x51, 0x00 },
{ 0x50, 0x27 }, { 0x51, 0xc3 },
{ 0x50, 0x39 }, { 0x51, 0x02 },
- { 0xec, 0x0f },
- { 0xeb, 0x1f },
- { 0x28, 0x6a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x00 },
+ { 0x50, 0xd5 }, { 0x51, 0x01 },
{ 0xd0, 0x00 },
};
@@ -317,7 +309,11 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, enum fe_status *status)
if (val >= 7)
*status |= FE_HAS_SYNC;
- if (val >= 8) /* Maybe 9? */
+ /*
+ * Actually, on state S8, it starts receiving TS, but the TS
+ * output is only on normal state after the transition to S9.
+ */
+ if (val >= 9)
*status |= FE_HAS_LOCK;
dev_dbg(&state->i2c->dev, "%s: Status = 0x%02x (state = %d)\n",
@@ -2067,6 +2063,11 @@ static void mb86a20s_release(struct dvb_frontend *fe)
kfree(state);
}
+static int mb86a20s_get_frontend_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
static struct dvb_frontend_ops mb86a20s_ops;
struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config,
@@ -2140,6 +2141,7 @@ static struct dvb_frontend_ops mb86a20s_ops = {
.read_status = mb86a20s_read_status_and_stats,
.read_signal_strength = mb86a20s_read_signal_strength_from_cache,
.tune = mb86a20s_tune,
+ .get_frontend_algo = mb86a20s_get_frontend_algo,
};
MODULE_DESCRIPTION("DVB Frontend module for Fujitsu mb86A20s hardware");
diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c
index f0480d687f17..ba780c45f645 100644
--- a/drivers/media/platform/am437x/am437x-vpfe.c
+++ b/drivers/media/platform/am437x/am437x-vpfe.c
@@ -1706,7 +1706,7 @@ static int vpfe_get_app_input_index(struct vpfe_device *vpfe,
sdinfo = &cfg->sub_devs[i];
client = v4l2_get_subdevdata(sdinfo->sd);
if (client->addr == curr_client->addr &&
- client->adapter->nr == client->adapter->nr) {
+ client->adapter->nr == curr_client->adapter->nr) {
if (vpfe->current_input >= 1)
return -1;
*app_input_index = j + vpfe->current_input;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
index f7eb0f8ac5a8..8d66232dbda1 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
@@ -196,6 +196,13 @@ static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr,
__func__, stream_id);
return -EINVAL;
}
+
+ if (qbuf_buf->num_planes > MAX_PLANES_PER_STREAM) {
+ pr_err("%s: Invalid num_planes %d , stream id %x\n",
+ __func__, qbuf_buf->num_planes, stream_id);
+ return -EINVAL;
+ }
+
for (i = 0; i < qbuf_buf->num_planes; i++) {
mapped_info = &buf_info->mapped_info[i];
mapped_info->buf_fd = qbuf_buf->planes[i].addr;
@@ -249,6 +256,12 @@ static void msm_isp_unprepare_v4l2_buf(
return;
}
+ if (buf_info->num_planes > VIDEO_MAX_PLANES) {
+ pr_err("%s: Invalid num_planes %d , stream id %x\n",
+ __func__, buf_info->num_planes, stream_id);
+ return;
+ }
+
bufq = msm_isp_get_bufq(buf_mgr, buf_info->bufq_handle);
if (!bufq) {
pr_err("%s: Invalid bufq, stream id %x\n",
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
index 840d84388a17..bb3f0dca9d92 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -588,6 +588,12 @@ int vfe_hw_probe(struct platform_device *pdev)
}
vfe_dev->hw_info =
(struct msm_vfe_hardware_info *) match_dev->data;
+ /* Cx ipeak support */
+ if (of_find_property(pdev->dev.of_node,
+ "qcom,vfe_cx_ipeak", NULL)) {
+ vfe_dev->vfe_cx_ipeak = cx_ipeak_register(
+ pdev->dev.of_node, "qcom,vfe_cx_ipeak");
+ }
} else {
vfe_dev->hw_info = (struct msm_vfe_hardware_info *)
platform_get_device_id(pdev)->driver_data;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
index f6fabc61620d..aca8e99650ba 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
@@ -29,6 +29,7 @@
#include "msm_buf_mgr.h"
#include "cam_hw_ops.h"
+#include <soc/qcom/cx_ipeak.h>
#define VFE40_8974V1_VERSION 0x10000018
#define VFE40_8974V2_VERSION 0x1001001A
@@ -767,6 +768,8 @@ struct vfe_device {
size_t num_hvx_clk;
size_t num_norm_clk;
enum cam_ahb_clk_vote ahb_vote;
+ bool turbo_vote;
+ struct cx_ipeak_client *vfe_cx_ipeak;
/* Sync variables*/
struct completion reset_complete;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
index c7f3b97c83c9..57373c1fc74c 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
@@ -331,6 +331,7 @@ 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;
@@ -763,7 +764,7 @@ long msm_vfe47_reset_hardware(struct vfe_device *vfe_dev,
}
if (blocking_call) {
- rc = wait_for_completion_timeout(
+ rc = wait_for_completion_interruptible_timeout(
&vfe_dev->reset_complete, msecs_to_jiffies(100));
if (rc <= 0) {
pr_err("%s:%d failed: reset timeout\n", __func__,
@@ -1930,7 +1931,7 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev,
init_completion(&vfe_dev->halt_complete);
/* Halt AXI Bus Bridge */
msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x400);
- rc = wait_for_completion_timeout(
+ rc = wait_for_completion_interruptible_timeout(
&vfe_dev->halt_complete, msecs_to_jiffies(500));
if (rc <= 0)
pr_err("%s:VFE%d halt timeout rc=%d\n", __func__,
@@ -2556,6 +2557,7 @@ 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;
rc = msm_camera_clk_set_rate(&vfe_dev->pdev->dev,
vfe_dev->vfe_clk[clk_idx], *rate);
@@ -2563,7 +2565,26 @@ int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate)
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 (vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg)
vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg(vfe_dev, NULL);
return 0;
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
index 5264bba57c8d..1628c098622f 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -133,7 +133,7 @@ static void msm_ispif_get_pack_mask_from_cfg(
pack_mask[0] |= temp;
CDBG("%s:num %d cid %d mode %d pack_mask %x %x\n",
__func__, entry->num_cids, entry->cids[i],
- pack_cfg[i].pack_mode,
+ pack_cfg[entry->cids[i]].pack_mode,
pack_mask[0], pack_mask[1]);
}
@@ -166,6 +166,16 @@ static int msm_ispif_config2(struct ispif_device *ispif,
}
for (i = 0; i < params->num; i++) {
+ int j;
+
+ if (params->entries[i].num_cids > MAX_CID_CH_PARAM_ENTRY)
+ return -EINVAL;
+ for (j = 0; j < params->entries[i].num_cids; j++)
+ if (params->entries[i].cids[j] >= CID_MAX)
+ return -EINVAL;
+ }
+
+ for (i = 0; i < params->num; i++) {
intftype = params->entries[i].intftype;
vfe_intf = params->entries[i].vfe_intf;
@@ -443,7 +453,7 @@ static int msm_ispif_reset_hw(struct ispif_device *ispif)
msm_camera_io_w(ISPIF_RST_CMD_MASK,
ispif->base + ISPIF_RST_CMD_ADDR);
- timeout = wait_for_completion_timeout(
+ timeout = wait_for_completion_interruptible_timeout(
&ispif->reset_complete[VFE0], msecs_to_jiffies(500));
CDBG("%s: VFE0 done\n", __func__);
@@ -457,7 +467,7 @@ static int msm_ispif_reset_hw(struct ispif_device *ispif)
atomic_set(&ispif->reset_trig[VFE1], 1);
msm_camera_io_w(ISPIF_RST_CMD_1_MASK,
ispif->base + ISPIF_RST_CMD_1_ADDR);
- timeout = wait_for_completion_timeout(
+ timeout = wait_for_completion_interruptible_timeout(
&ispif->reset_complete[VFE1],
msecs_to_jiffies(500));
CDBG("%s: VFE1 done\n", __func__);
@@ -1120,7 +1130,7 @@ static int msm_ispif_restart_frame_boundary(struct ispif_device *ispif,
/* initiate reset of ISPIF */
msm_camera_io_w(ISPIF_RST_CMD_MASK_RESTART,
ispif->base + ISPIF_RST_CMD_ADDR);
- timeout = wait_for_completion_timeout(
+ timeout = wait_for_completion_interruptible_timeout(
&ispif->reset_complete[VFE0], msecs_to_jiffies(500));
if (timeout <= 0) {
pr_err("%s: VFE0 reset wait timeout\n", __func__);
@@ -1133,7 +1143,7 @@ static int msm_ispif_restart_frame_boundary(struct ispif_device *ispif,
atomic_set(&ispif->reset_trig[VFE1], 1);
msm_camera_io_w(ISPIF_RST_CMD_1_MASK_RESTART,
ispif->base + ISPIF_RST_CMD_1_ADDR);
- timeout = wait_for_completion_timeout(
+ timeout = wait_for_completion_interruptible_timeout(
&ispif->reset_complete[VFE1],
msecs_to_jiffies(500));
if (timeout <= 0) {
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c
index 3b38882c4c45..88d90d0a7c08 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c
+++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -72,6 +72,18 @@ static const struct msm_jpegdma_block msm_jpegdma_block_sel[] = {
};
/*
+* jpegdma_do_div - long division.
+* @num: dividend
+* @den: divisor
+* returns quotient value.
+*/
+static inline long long jpegdma_do_div(long long num, long long den)
+{
+ do_div(num, den);
+ return num;
+}
+
+/*
* msm_jpegdma_hw_read_reg - dma read from register.
* @dma: Pointer to dma device.
* @base_idx: dma memory resource index.
@@ -819,9 +831,9 @@ static int msm_jpegdma_hw_calc_speed(struct msm_jpegdma_device *dma,
}
speed->bus_ab = calc_rate * 2;
- speed->bus_ib = (real_clock *
- (MSM_JPEGDMA_BW_NUM + MSM_JPEGDMA_BW_DEN - 1)) /
- MSM_JPEGDMA_BW_DEN;
+ speed->bus_ib = jpegdma_do_div((real_clock *
+ (MSM_JPEGDMA_BW_NUM + MSM_JPEGDMA_BW_DEN - 1)),
+ MSM_JPEGDMA_BW_DEN);
speed->core_clock = real_clock;
dev_dbg(dma->dev, "Speed core clk %llu ab %llu ib %llu fps %d\n",
speed->core_clock, speed->bus_ab, speed->bus_ib, size->fps);
@@ -923,13 +935,15 @@ static int msm_jpegdma_hw_calc_config(struct msm_jpegdma_size_config *size_cfg,
in_width = size_cfg->in_size.width;
out_width = size_cfg->out_size.width;
- scale_hor = (in_width * MSM_JPEGDMA_SCALE_UNI) / out_width;
+ scale_hor = jpegdma_do_div((in_width * MSM_JPEGDMA_SCALE_UNI),
+ out_width);
if (scale_hor != MSM_JPEGDMA_SCALE_UNI)
config->scale_cfg.enable = 1;
in_height = size_cfg->in_size.height;
out_height = size_cfg->out_size.height;
- scale_ver = (in_height * MSM_JPEGDMA_SCALE_UNI) / out_height;
+ scale_ver = jpegdma_do_div((in_height * MSM_JPEGDMA_SCALE_UNI),
+ out_height);
if (scale_ver != MSM_JPEGDMA_SCALE_UNI)
config->scale_cfg.enable = 1;
@@ -946,23 +960,23 @@ static int msm_jpegdma_hw_calc_config(struct msm_jpegdma_size_config *size_cfg,
config->block_cfg.block = msm_jpegdma_block_sel[i];
if (plane->active_pipes > 1) {
- phase = (out_height * scale_ver + (plane->active_pipes - 1)) /
- plane->active_pipes;
+ phase = jpegdma_do_div((out_height * scale_ver +
+ (plane->active_pipes - 1)), plane->active_pipes);
phase &= (MSM_JPEGDMA_SCALE_UNI - 1);
- out_height = (out_height + (plane->active_pipes - 1)) /
- plane->active_pipes;
+ out_height = jpegdma_do_div((out_height +
+ (plane->active_pipes - 1)), plane->active_pipes);
in_height = (out_height * scale_ver) / MSM_JPEGDMA_SCALE_UNI;
}
- config->block_cfg.blocks_per_row = out_width /
- config->block_cfg.block.width;
+ config->block_cfg.blocks_per_row = (uint32_t) jpegdma_do_div(out_width,
+ config->block_cfg.block.width);
config->block_cfg.blocks_per_col = out_height;
config->block_cfg.h_step = config->block_cfg.block.width;
-
- config->block_cfg.h_step_last = out_width %
- config->block_cfg.block.width;
+ config->size_cfg.out_size.width = out_width;
+ config->block_cfg.h_step_last = (uint32_t) do_div(out_width,
+ config->block_cfg.block.width);
if (!config->block_cfg.h_step_last)
config->block_cfg.h_step_last = config->block_cfg.h_step;
else
@@ -974,7 +988,6 @@ static int msm_jpegdma_hw_calc_config(struct msm_jpegdma_size_config *size_cfg,
config->size_cfg = *size_cfg;
config->size_cfg.in_size.width = in_width;
config->size_cfg.in_size.height = in_height;
- config->size_cfg.out_size.width = out_width;
config->size_cfg.out_size.height = out_height;
config->in_offset = 0;
config->out_offset = 0;
@@ -1013,14 +1026,16 @@ int msm_jpegdma_hw_check_config(struct msm_jpegdma_device *dma,
in_width = size_cfg->in_size.width;
out_width = size_cfg->out_size.width;
- scale = ((in_width * MSM_JPEGDMA_SCALE_UNI)) / out_width;
+ scale = jpegdma_do_div(((in_width * MSM_JPEGDMA_SCALE_UNI)),
+ out_width);
if (scale < MSM_JPEGDMA_SCALE_UNI)
return -EINVAL;
in_height = size_cfg->in_size.height;
out_height = size_cfg->out_size.height;
- scale = (in_height * MSM_JPEGDMA_SCALE_UNI) / out_height;
+ scale = jpegdma_do_div((in_height * MSM_JPEGDMA_SCALE_UNI),
+ out_height);
if (scale < MSM_JPEGDMA_SCALE_UNI)
return -EINVAL;
@@ -1827,7 +1842,7 @@ int msm_jpegdma_hw_map_buffer(struct msm_jpegdma_device *dma, int fd,
buf->fd = fd;
ret = cam_smmu_get_phy_addr(dma->iommu_hndl, buf->fd,
- CAM_SMMU_MAP_RW, &buf->addr, &buf->size);
+ CAM_SMMU_MAP_RW, &buf->addr, (size_t *)&buf->size);
if (ret < 0) {
dev_err(dma->dev, "Can not get physical address\n");
goto error_get_phy;
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index 5cf5582b55ab..c2b42a854d35 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -1119,17 +1119,21 @@ long msm_copy_camera_private_ioctl_args(unsigned long arg,
struct msm_camera_private_ioctl_arg *k_ioctl,
void __user **tmp_compat_ioctl_ptr)
{
- struct msm_camera_private_ioctl_arg *up_ioctl_ptr =
- (struct msm_camera_private_ioctl_arg *)arg;
+ struct msm_camera_private_ioctl_arg up_ioctl;
if (WARN_ON(!arg || !k_ioctl || !tmp_compat_ioctl_ptr))
return -EIO;
- k_ioctl->id = up_ioctl_ptr->id;
- k_ioctl->size = up_ioctl_ptr->size;
- k_ioctl->result = up_ioctl_ptr->result;
- k_ioctl->reserved = up_ioctl_ptr->reserved;
- *tmp_compat_ioctl_ptr = compat_ptr(up_ioctl_ptr->ioctl_ptr);
+ if (copy_from_user(&up_ioctl,
+ (struct msm_camera_private_ioctl_arg *)arg,
+ sizeof(struct msm_camera_private_ioctl_arg)))
+ return -EFAULT;
+
+ k_ioctl->id = up_ioctl.id;
+ k_ioctl->size = up_ioctl.size;
+ k_ioctl->result = up_ioctl.result;
+ k_ioctl->reserved = up_ioctl.reserved;
+ *tmp_compat_ioctl_ptr = compat_ptr(up_ioctl.ioctl_ptr);
return 0;
}
diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
index 730f8b32ff1a..76a7c6942c68 100644
--- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -149,10 +149,7 @@ static int32_t msm_buf_mngr_buf_done(struct msm_buf_mngr_device *buf_mngr_dev,
list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) {
if ((bufs->session_id == buf_info->session_id) &&
(bufs->stream_id == buf_info->stream_id) &&
- (bufs->vb2_v4l2_buf->vb2_buf.index ==
- buf_info->index)) {
- bufs->vb2_v4l2_buf->sequence = buf_info->frame_id;
- bufs->vb2_v4l2_buf->timestamp = buf_info->timestamp;
+ (bufs->index == buf_info->index)) {
ret = buf_mngr_dev->vb2_ops.buf_done
(bufs->vb2_v4l2_buf,
buf_info->session_id,
@@ -181,7 +178,7 @@ static int32_t msm_buf_mngr_put_buf(struct msm_buf_mngr_device *buf_mngr_dev,
list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) {
if ((bufs->session_id == buf_info->session_id) &&
(bufs->stream_id == buf_info->stream_id) &&
- (bufs->vb2_v4l2_buf->vb2_buf.index == buf_info->index)) {
+ (bufs->index == buf_info->index)) {
ret = buf_mngr_dev->vb2_ops.put_buf(bufs->vb2_v4l2_buf,
buf_info->session_id, buf_info->stream_id);
list_del_init(&bufs->entry);
@@ -214,7 +211,7 @@ static int32_t msm_generic_buf_mngr_flush(
buf_info->session_id,
buf_info->stream_id, 0, &ts, 0);
pr_err("Bufs not flushed: str_id = %d buf_index = %d ret = %d\n",
- buf_info->stream_id, bufs->vb2_v4l2_buf->vb2_buf.index,
+ buf_info->stream_id, bufs->index,
ret);
list_del_init(&bufs->entry);
kfree(bufs);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h
index 07d522cb01bb..9120a4cc85ca 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -99,6 +99,7 @@ struct csiphy_reg_3ph_parms_t csiphy_v5_0_1_3ph = {
{0x70C, 0xA5},
{0x38, 0xFE},
{0x81c, 0x2},
+ {0x700, 0x80},
};
struct csiphy_settings_t csiphy_combo_mode_v5_0_1 = {
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h
index 198d130b24fc..8591f0646080 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -100,6 +100,7 @@ struct csiphy_reg_3ph_parms_t csiphy_v5_0_3ph = {
{0x70C, 0x16},
{0x38, 0xFE},
{0x81c, 0x6},
+ {0x700, 0x80},
};
struct csiphy_settings_t csiphy_combo_mode_v5_0 = {
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
index a7cd44636d1d..be266641a105 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -46,6 +46,7 @@
#define MSM_CSIPHY_DRV_NAME "msm_csiphy"
#define CLK_LANE_OFFSET 1
#define NUM_LANES_OFFSET 4
+#define CLOCK_LANE 0x02
#define CSI_3PHASE_HW 1
#define MAX_DPHY_DATA_LN 4
@@ -683,12 +684,21 @@ static int msm_csiphy_2phase_lane_config_v50(
csiphybase + csiphy_dev->ctrl_reg->
csiphy_3ph_reg.
mipi_csiphy_2ph_lnn_ctrl15.addr + offset);
- msm_camera_io_w(csiphy_dev->ctrl_reg->
- csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_ctrl0.data,
- csiphybase + csiphy_dev->ctrl_reg->
- csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_ctrl0.addr + offset);
+ if (mask == CLOCK_LANE)
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnck_ctrl0.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnck_ctrl0.addr);
+ else
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl0.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl0.addr +
+ offset);
msm_camera_io_w(csiphy_dev->ctrl_reg->
csiphy_3ph_reg.
mipi_csiphy_2ph_lnn_cfg1.data,
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
index 70462dcd3b12..c1a9748e8af5 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -141,6 +141,7 @@ struct csiphy_reg_3ph_parms_t {
struct csiphy_reg_t mipi_csiphy_2ph_lnck_ctrl3;
struct csiphy_reg_t mipi_csiphy_2ph_lnn_ctrl14;
struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl7_cphy;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnck_ctrl0;
};
struct csiphy_ctrl_t {
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
index a2da663e2046..f41382b5b20c 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
@@ -990,11 +990,14 @@ static int sde_rotator_debug_base_release(struct inode *inode,
{
struct sde_rotator_debug_base *dbg = file->private_data;
- if (dbg && dbg->buf) {
+ if (dbg) {
+ mutex_lock(&dbg->buflock);
kfree(dbg->buf);
dbg->buf_len = 0;
dbg->buf = NULL;
+ mutex_unlock(&dbg->buflock);
}
+
return 0;
}
@@ -1026,8 +1029,10 @@ static ssize_t sde_rotator_debug_base_offset_write(struct file *file,
if (cnt > (dbg->max_offset - off))
cnt = dbg->max_offset - off;
+ mutex_lock(&dbg->buflock);
dbg->off = off;
dbg->cnt = cnt;
+ mutex_unlock(&dbg->buflock);
SDEROT_DBG("offset=%x cnt=%x\n", off, cnt);
@@ -1047,7 +1052,10 @@ static ssize_t sde_rotator_debug_base_offset_read(struct file *file,
if (*ppos)
return 0; /* the end */
+ mutex_lock(&dbg->buflock);
len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt);
+ mutex_unlock(&dbg->buflock);
+
if (len < 0 || len >= sizeof(buf))
return 0;
@@ -1086,6 +1094,8 @@ static ssize_t sde_rotator_debug_base_reg_write(struct file *file,
if (off >= dbg->max_offset)
return -EFAULT;
+ mutex_lock(&dbg->buflock);
+
/* Enable Clock for register access */
sde_rotator_clk_ctrl(dbg->mgr, true);
@@ -1094,6 +1104,8 @@ static ssize_t sde_rotator_debug_base_reg_write(struct file *file,
/* Disable Clock after register access */
sde_rotator_clk_ctrl(dbg->mgr, false);
+ mutex_unlock(&dbg->buflock);
+
SDEROT_DBG("addr=%zx data=%x\n", off, data);
return count;
@@ -1104,12 +1116,14 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file,
{
struct sde_rotator_debug_base *dbg = file->private_data;
size_t len;
+ int rc = 0;
if (!dbg) {
SDEROT_ERR("invalid handle\n");
return -ENODEV;
}
+ mutex_lock(&dbg->buflock);
if (!dbg->buf) {
char dump_buf[64];
char *ptr;
@@ -1121,7 +1135,8 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file,
if (!dbg->buf) {
SDEROT_ERR("not enough memory to hold reg dump\n");
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto debug_read_error;
}
ptr = dbg->base + dbg->off;
@@ -1151,18 +1166,26 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file,
dbg->buf_len = tot;
}
- if (*ppos >= dbg->buf_len)
- return 0; /* done reading */
+ if (*ppos >= dbg->buf_len) {
+ rc = 0; /* done reading */
+ goto debug_read_error;
+ }
len = min(count, dbg->buf_len - (size_t) *ppos);
if (copy_to_user(user_buf, dbg->buf + *ppos, len)) {
SDEROT_ERR("failed to copy to user\n");
- return -EFAULT;
+ rc = -EFAULT;
+ goto debug_read_error;
}
*ppos += len; /* increase offset */
+ mutex_unlock(&dbg->buflock);
return len;
+
+debug_read_error:
+ mutex_unlock(&dbg->buflock);
+ return rc;
}
static const struct file_operations sde_rotator_off_fops = {
@@ -1196,6 +1219,9 @@ int sde_rotator_debug_register_base(struct sde_rotator_device *rot_dev,
if (!dbg)
return -ENOMEM;
+ mutex_init(&dbg->buflock);
+ mutex_lock(&dbg->buflock);
+
if (name)
strlcpy(dbg->name, name, sizeof(dbg->name));
dbg->base = io_data->base;
@@ -1217,6 +1243,7 @@ int sde_rotator_debug_register_base(struct sde_rotator_device *rot_dev,
dbg->base += rot_dev->mdata->regdump ?
rot_dev->mdata->regdump[0].offset : 0;
}
+ mutex_unlock(&dbg->buflock);
strlcpy(dbgname + prefix_len, "off", sizeof(dbgname) - prefix_len);
ent_off = debugfs_create_file(dbgname, 0644, debugfs_root, dbg,
@@ -1234,7 +1261,9 @@ int sde_rotator_debug_register_base(struct sde_rotator_device *rot_dev,
goto reg_fail;
}
+ mutex_lock(&dbg->buflock);
dbg->mgr = rot_dev->mgr;
+ mutex_unlock(&dbg->buflock);
return 0;
reg_fail:
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
index c2c6f9775602..c6d0151d37de 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -53,6 +53,7 @@ struct sde_rotator_debug_base {
char *buf;
size_t buf_len;
struct sde_rot_mgr *mgr;
+ struct mutex buflock;
};
#if defined(CONFIG_DEBUG_FS)
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index 368df242a707..a63279715de6 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -222,6 +222,14 @@ static int msm_v4l2_enum_framesizes(struct file *file, void *fh,
return msm_vidc_enum_framesizes((void *)vidc_inst, fsize);
}
+static int msm_v4l2_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *ctrl)
+{
+ struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+ return msm_vidc_query_ctrl((void *)vidc_inst, ctrl);
+}
+
static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = {
.vidioc_querycap = msm_v4l2_querycap,
.vidioc_enum_fmt_vid_cap_mplane = msm_v4l2_enum_fmt,
@@ -238,6 +246,7 @@ static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = {
.vidioc_streamoff = msm_v4l2_streamoff,
.vidioc_s_ctrl = msm_v4l2_s_ctrl,
.vidioc_g_ctrl = msm_v4l2_g_ctrl,
+ .vidioc_queryctrl = msm_v4l2_queryctrl,
.vidioc_s_ext_ctrls = msm_v4l2_s_ext_ctrl,
.vidioc_subscribe_event = msm_v4l2_subscribe_event,
.vidioc_unsubscribe_event = msm_v4l2_unsubscribe_event,
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index ec3246ed9d67..644203b65999 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -110,6 +110,34 @@ int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f)
}
EXPORT_SYMBOL(msm_vidc_enum_fmt);
+int msm_vidc_query_ctrl(void *instance, struct v4l2_queryctrl *ctrl)
+{
+ struct msm_vidc_inst *inst = instance;
+ int rc = 0;
+
+ if (!inst || !ctrl)
+ return -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE:
+ ctrl->maximum = inst->capability.hier_p_hybrid.max;
+ ctrl->minimum = inst->capability.hier_p_hybrid.min;
+ break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS:
+ ctrl->maximum = inst->capability.hier_b.max;
+ ctrl->minimum = inst->capability.hier_b.min;
+ break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS:
+ ctrl->maximum = inst->capability.hier_p.max;
+ ctrl->minimum = inst->capability.hier_p.min;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_vidc_query_ctrl);
+
int msm_vidc_s_fmt(void *instance, struct v4l2_format *f)
{
struct msm_vidc_inst *inst = instance;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 2aebc62d09d5..8b459e4da618 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -959,6 +959,48 @@ static void print_cap(const char *type,
type, cap->min, cap->max, cap->step_size);
}
+
+static void msm_vidc_comm_update_ctrl_limits(struct msm_vidc_inst *inst)
+{
+ struct v4l2_ctrl *ctrl = NULL;
+
+ ctrl = v4l2_ctrl_find(&inst->ctrl_handler,
+ V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE);
+ if (ctrl) {
+ v4l2_ctrl_modify_range(ctrl, inst->capability.hier_p_hybrid.min,
+ inst->capability.hier_p_hybrid.max, ctrl->step,
+ inst->capability.hier_p_hybrid.min);
+ dprintk(VIDC_DBG,
+ "%s: Updated Range = %lld --> %lld Def value = %lld\n",
+ ctrl->name, ctrl->minimum, ctrl->maximum,
+ ctrl->default_value);
+ }
+
+ ctrl = v4l2_ctrl_find(&inst->ctrl_handler,
+ V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS);
+ if (ctrl) {
+ v4l2_ctrl_modify_range(ctrl, inst->capability.hier_b.min,
+ inst->capability.hier_b.max, ctrl->step,
+ inst->capability.hier_b.min);
+ dprintk(VIDC_DBG,
+ "%s: Updated Range = %lld --> %lld Def value = %lld\n",
+ ctrl->name, ctrl->minimum, ctrl->maximum,
+ ctrl->default_value);
+ }
+
+ ctrl = v4l2_ctrl_find(&inst->ctrl_handler,
+ V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS);
+ if (ctrl) {
+ v4l2_ctrl_modify_range(ctrl, inst->capability.hier_p.min,
+ inst->capability.hier_p.max, ctrl->step,
+ inst->capability.hier_p.min);
+ dprintk(VIDC_DBG,
+ "%s: Updated Range = %lld --> %lld Def value = %lld\n",
+ ctrl->name, ctrl->minimum, ctrl->maximum,
+ ctrl->default_value);
+ }
+}
+
static void handle_session_init_done(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
@@ -1052,8 +1094,16 @@ static void handle_session_init_done(enum hal_command_response cmd, void *data)
print_cap("ltr_count", &inst->capability.ltr_count);
print_cap("mbs_per_sec_low_power",
&inst->capability.mbs_per_sec_power_save);
+ print_cap("hybrid-hp", &inst->capability.hier_p_hybrid);
signal_session_msg_receipt(cmd, inst);
+
+ /*
+ * Update controls after informing session_init_done to avoid
+ * timeouts.
+ */
+
+ msm_vidc_comm_update_ctrl_limits(inst);
put_inst(inst);
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c
index 65f70d901f2a..3e269576c126 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c
@@ -225,7 +225,7 @@ void msm_dcvs_init_load(struct msm_vidc_inst *inst)
core = inst->core;
dcvs = &inst->dcvs;
res = &core->resources;
- dcvs->load = msm_comm_get_inst_load(inst, LOAD_CALC_NO_QUIRKS);
+ dcvs->load = msm_comm_get_inst_load(inst, LOAD_CALC_IGNORE_TURBO_LOAD);
num_rows = res->dcvs_tbl_size;
table = res->dcvs_tbl;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
index 022c776a6096..a65e22c66e30 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
@@ -22,6 +22,7 @@
#include "msm_vidc_res_parse.h"
#include "venus_boot.h"
#include "soc/qcom/secure_buffer.h"
+#include "soc/qcom/cx_ipeak.h"
enum clock_properties {
CLOCK_PROP_HAS_SCALING = 1 << 0,
@@ -171,6 +172,8 @@ void msm_vidc_free_platform_resources(
msm_vidc_free_qdss_addr_table(res);
msm_vidc_free_bus_vectors(res);
msm_vidc_free_buffer_usage_table(res);
+ cx_ipeak_unregister(res->cx_ipeak_context);
+ res->cx_ipeak_context = NULL;
}
static int msm_vidc_load_reg_table(struct msm_vidc_platform_resources *res)
@@ -1133,8 +1136,36 @@ int read_platform_resources_from_dt(
of_property_read_u32(pdev->dev.of_node,
"qcom,max-secure-instances",
&res->max_secure_inst_count);
+
+ res->cx_ipeak_context = cx_ipeak_register(pdev->dev.of_node,
+ "qcom,cx-ipeak-data");
+
+ if (IS_ERR(res->cx_ipeak_context)) {
+ rc = PTR_ERR(res->cx_ipeak_context);
+ if (rc == -EPROBE_DEFER)
+ dprintk(VIDC_INFO,
+ "cx-ipeak register failed. Deferring probe!");
+ else
+ dprintk(VIDC_ERR,
+ "cx-ipeak register failed. rc: %d", rc);
+
+ res->cx_ipeak_context = NULL;
+ goto err_register_cx_ipeak;
+ } else if (res->cx_ipeak_context) {
+ dprintk(VIDC_INFO, "cx-ipeak register successful");
+ } else {
+ dprintk(VIDC_INFO, "cx-ipeak register not implemented");
+ }
+
+ of_property_read_u32(pdev->dev.of_node,
+ "qcom,clock-freq-threshold",
+ &res->clk_freq_threshold);
+ dprintk(VIDC_DBG, "cx ipeak threshold frequency = %u\n",
+ res->clk_freq_threshold);
+
return rc;
+err_register_cx_ipeak:
err_setup_legacy_cb:
err_load_max_hw_load:
msm_vidc_free_allowed_clocks_table(res);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
index 03b31d7fd9d1..3a329d989918 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_resources.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
@@ -17,6 +17,7 @@
#include <linux/devfreq.h>
#include <linux/platform_device.h>
#include <media/msm_vidc.h>
+#include "soc/qcom/cx_ipeak.h"
#define MAX_BUFFER_TYPES 32
struct platform_version_table {
@@ -191,6 +192,8 @@ struct msm_vidc_platform_resources {
uint32_t pm_qos_latency_us;
uint32_t max_inst_count;
uint32_t max_secure_inst_count;
+ uint32_t clk_freq_threshold;
+ struct cx_ipeak_client *cx_ipeak_context;
};
static inline bool is_iommu_present(struct msm_vidc_platform_resources *res)
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 777fb52b2201..4739fc999c82 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -27,6 +27,7 @@
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include <soc/qcom/cx_ipeak.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/smem.h>
#include <soc/qcom/subsystem_restart.h>
@@ -1301,8 +1302,12 @@ static int venus_hfi_suspend(void *dev)
}
dprintk(VIDC_DBG, "Suspending Venus\n");
- rc = flush_delayed_work(&venus_hfi_pm_work);
+ flush_delayed_work(&venus_hfi_pm_work);
+ mutex_lock(&device->lock);
+ if (device->power_enabled)
+ rc = -EBUSY;
+ mutex_unlock(&device->lock);
return rc;
}
@@ -1385,6 +1390,39 @@ static int __halt_axi(struct venus_hfi_device *device)
return rc;
}
+static int __set_clk_rate(struct venus_hfi_device *device,
+ struct clock_info *cl, u64 rate) {
+ int rc = 0, rc1 = 0;
+ u64 toggle_freq = device->res->clk_freq_threshold;
+ struct cx_ipeak_client *ipeak = device->res->cx_ipeak_context;
+ struct clk *clk = cl->clk;
+
+ if (device->clk_freq < toggle_freq && rate >= toggle_freq) {
+ rc1 = cx_ipeak_update(ipeak, true);
+ dprintk(VIDC_PROF, "Voting up: %d\n", rc);
+ }
+
+ rc = clk_set_rate(clk, rate);
+ if (rc)
+ dprintk(VIDC_ERR,
+ "%s: Failed to set clock rate %llu %s: %d\n",
+ __func__, rate, cl->name, rc);
+
+ if (device->clk_freq >= toggle_freq && rate < toggle_freq) {
+ rc1 = cx_ipeak_update(ipeak, false);
+ dprintk(VIDC_PROF, "Voting down: %d\n", rc);
+ }
+
+ if (rc1)
+ dprintk(VIDC_ERR,
+ "cx_ipeak_update failed! ipeak %pK\n", ipeak);
+
+ if (!rc)
+ device->clk_freq = rate;
+
+ return rc;
+}
+
static int __scale_clocks_cycles_per_mb(struct venus_hfi_device *device,
struct vidc_clk_scale_data *data, unsigned long instant_bitrate)
{
@@ -1458,14 +1496,10 @@ get_clock_freq:
if (!cl->has_scaling)
continue;
- device->clk_freq = rate;
- rc = clk_set_rate(cl->clk, rate);
- if (rc) {
- dprintk(VIDC_ERR,
- "%s: Failed to set clock rate %llu %s: %d\n",
- __func__, rate, cl->name, rc);
+ rc = __set_clk_rate(device, cl, rate);
+ if (rc)
return rc;
- }
+
if (!strcmp(cl->name, "core_clk"))
device->scaled_rate = rate;
@@ -1506,14 +1540,11 @@ static int __scale_clocks_load(struct venus_hfi_device *device, int load,
load, data,
instant_bitrate);
}
- device->clk_freq = rate;
- rc = clk_set_rate(cl->clk, rate);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to set clock rate %lu %s: %d\n",
- rate, cl->name, rc);
+
+ rc = __set_clk_rate(device, cl, rate);
+ if (rc)
return rc;
- }
+
if (!strcmp(cl->name, "core_clk"))
device->scaled_rate = rate;
@@ -3794,7 +3825,8 @@ static inline int __prepare_enable_clks(struct venus_hfi_device *device)
* it to the lowest frequency possible
*/
if (cl->has_scaling)
- clk_set_rate(cl->clk, clk_round_rate(cl->clk, 0));
+ __set_clk_rate(device, cl,
+ clk_round_rate(cl->clk, 0));
if (cl->has_mem_retention) {
rc = clk_set_flags(cl->clk, CLKFLAG_NORETAIN_PERIPH);
diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c
index 491913778bcc..2f52d66b4dae 100644
--- a/drivers/media/usb/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c
@@ -1264,7 +1264,10 @@ int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev,
dev->board.agc_analog_digital_select_gpio,
analog_or_digital);
- return status;
+ if (status < 0)
+ return status;
+
+ return 0;
}
int cx231xx_enable_i2c_port_3(struct cx231xx *dev, bool is_port_3)
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index 4a117a58c39a..8389c162bc89 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -486,7 +486,7 @@ struct cx231xx_board cx231xx_boards[] = {
.output_mode = OUT_MODE_VIP11,
.demod_xfer_mode = 0,
.ctl_pin_status_mask = 0xFFFFFFC4,
- .agc_analog_digital_select_gpio = 0x00, /* According with PV cxPolaris.inf file */
+ .agc_analog_digital_select_gpio = 0x1c,
.tuner_sif_gpio = -1,
.tuner_scl_gpio = -1,
.tuner_sda_gpio = -1,
diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c
index a2fd49b6be83..19b0293312a0 100644
--- a/drivers/media/usb/cx231xx/cx231xx-core.c
+++ b/drivers/media/usb/cx231xx/cx231xx-core.c
@@ -712,6 +712,7 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode)
break;
case CX231XX_BOARD_CNXT_RDE_253S:
case CX231XX_BOARD_CNXT_RDU_253S:
+ case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1);
break;
case CX231XX_BOARD_HAUPPAUGE_EXETER:
@@ -738,7 +739,7 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode)
case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
- errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
+ errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
break;
default:
break;
diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c
index 0d248ce02a9b..ab58f0b9da5c 100644
--- a/drivers/media/usb/dvb-usb/dib0700_core.c
+++ b/drivers/media/usb/dvb-usb/dib0700_core.c
@@ -677,7 +677,7 @@ static void dib0700_rc_urb_completion(struct urb *purb)
struct dvb_usb_device *d = purb->context;
struct dib0700_rc_response *poll_reply;
enum rc_type protocol;
- u32 uninitialized_var(keycode);
+ u32 keycode;
u8 toggle;
deb_info("%s()\n", __func__);
@@ -719,7 +719,8 @@ static void dib0700_rc_urb_completion(struct urb *purb)
poll_reply->nec.data == 0x00 &&
poll_reply->nec.not_data == 0xff) {
poll_reply->data_state = 2;
- break;
+ rc_repeat(d->rc_dev);
+ goto resubmit;
}
if ((poll_reply->nec.data ^ poll_reply->nec.not_data) != 0xff) {
diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c
index a19b5c8b56ff..1a9e1e556706 100644
--- a/drivers/media/usb/em28xx/em28xx-i2c.c
+++ b/drivers/media/usb/em28xx/em28xx-i2c.c
@@ -507,9 +507,8 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
if (dev->disconnected)
return -ENODEV;
- rc = rt_mutex_trylock(&dev->i2c_bus_lock);
- if (rc < 0)
- return rc;
+ if (!rt_mutex_trylock(&dev->i2c_bus_lock))
+ return -EAGAIN;
/* Switch I2C bus if needed */
if (bus != dev->cur_i2c_bus &&
diff --git a/drivers/media/usb/gspca/cpia1.c b/drivers/media/usb/gspca/cpia1.c
index f23df4a9d8c5..52b88e9e656b 100644
--- a/drivers/media/usb/gspca/cpia1.c
+++ b/drivers/media/usb/gspca/cpia1.c
@@ -1624,7 +1624,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
static void sd_stopN(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct sd *sd __maybe_unused = (struct sd *) gspca_dev;
command_pause(gspca_dev);
diff --git a/drivers/media/usb/gspca/konica.c b/drivers/media/usb/gspca/konica.c
index 39c96bb4c985..0712b1bc90b4 100644
--- a/drivers/media/usb/gspca/konica.c
+++ b/drivers/media/usb/gspca/konica.c
@@ -243,7 +243,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
static void sd_stopN(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct sd *sd __maybe_unused = (struct sd *) gspca_dev;
konica_stream_off(gspca_dev);
#if IS_ENABLED(CONFIG_INPUT)
diff --git a/drivers/media/usb/gspca/t613.c b/drivers/media/usb/gspca/t613.c
index e2cc4e5a0ccb..bb52fc1fe598 100644
--- a/drivers/media/usb/gspca/t613.c
+++ b/drivers/media/usb/gspca/t613.c
@@ -837,7 +837,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
u8 *data, /* isoc packet */
int len) /* iso packet length */
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct sd *sd __maybe_unused = (struct sd *) gspca_dev;
int pkt_type;
if (data[0] == 0x5a) {
diff --git a/drivers/memstick/host/rtsx_usb_ms.c b/drivers/memstick/host/rtsx_usb_ms.c
index 1105db2355d2..83bfb1659abe 100644
--- a/drivers/memstick/host/rtsx_usb_ms.c
+++ b/drivers/memstick/host/rtsx_usb_ms.c
@@ -524,6 +524,7 @@ static void rtsx_usb_ms_handle_req(struct work_struct *work)
int rc;
if (!host->req) {
+ pm_runtime_get_sync(ms_dev(host));
do {
rc = memstick_next_req(msh, &host->req);
dev_dbg(ms_dev(host), "next req %d\n", rc);
@@ -544,6 +545,7 @@ static void rtsx_usb_ms_handle_req(struct work_struct *work)
host->req->error);
}
} while (!rc);
+ pm_runtime_put(ms_dev(host));
}
}
@@ -570,6 +572,7 @@ static int rtsx_usb_ms_set_param(struct memstick_host *msh,
dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n",
__func__, param, value);
+ pm_runtime_get_sync(ms_dev(host));
mutex_lock(&ucr->dev_mutex);
err = rtsx_usb_card_exclusive_check(ucr, RTSX_USB_MS_CARD);
@@ -635,6 +638,7 @@ static int rtsx_usb_ms_set_param(struct memstick_host *msh,
}
out:
mutex_unlock(&ucr->dev_mutex);
+ pm_runtime_put(ms_dev(host));
/* power-on delay */
if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON)
@@ -681,6 +685,7 @@ static int rtsx_usb_detect_ms_card(void *__host)
int err;
for (;;) {
+ pm_runtime_get_sync(ms_dev(host));
mutex_lock(&ucr->dev_mutex);
/* Check pending MS card changes */
@@ -703,6 +708,7 @@ static int rtsx_usb_detect_ms_card(void *__host)
}
poll_again:
+ pm_runtime_put(ms_dev(host));
if (host->eject)
break;
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 2346635ccff3..3087618b1b26 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1474,6 +1474,7 @@ config MFD_WM8350
config MFD_WM8350_I2C
bool "Wolfson Microelectronics WM8350 with I2C"
select MFD_WM8350
+ select REGMAP_I2C
depends on I2C=y
help
The WM8350 is an integrated audio and power management
diff --git a/drivers/mfd/atmel-hlcdc.c b/drivers/mfd/atmel-hlcdc.c
index 06c205868573..c216c3a55793 100644
--- a/drivers/mfd/atmel-hlcdc.c
+++ b/drivers/mfd/atmel-hlcdc.c
@@ -50,8 +50,9 @@ static int regmap_atmel_hlcdc_reg_write(void *context, unsigned int reg,
if (reg <= ATMEL_HLCDC_DIS) {
u32 status;
- readl_poll_timeout(hregmap->regs + ATMEL_HLCDC_SR, status,
- !(status & ATMEL_HLCDC_SIP), 1, 100);
+ readl_poll_timeout_atomic(hregmap->regs + ATMEL_HLCDC_SR,
+ status, !(status & ATMEL_HLCDC_SIP),
+ 1, 100);
}
writel(val, hregmap->regs + reg);
diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c
index 88e80ec772f6..fe89e5e337d5 100644
--- a/drivers/mfd/intel-lpss.c
+++ b/drivers/mfd/intel-lpss.c
@@ -494,9 +494,6 @@ int intel_lpss_suspend(struct device *dev)
for (i = 0; i < LPSS_PRIV_REG_COUNT; i++)
lpss->priv_ctx[i] = readl(lpss->priv + i * 4);
- /* Put the device into reset state */
- writel(0, lpss->priv + LPSS_PRIV_RESETS);
-
return 0;
}
EXPORT_SYMBOL_GPL(intel_lpss_suspend);
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 60b60dc63ddd..022c9374ce8b 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -354,6 +354,8 @@ int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones)
clones[i]);
}
+ put_device(dev);
+
return 0;
}
EXPORT_SYMBOL(mfd_clone_cell);
diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c
index dbd907d7170e..691dab791f7a 100644
--- a/drivers/mfd/rtsx_usb.c
+++ b/drivers/mfd/rtsx_usb.c
@@ -46,9 +46,6 @@ static void rtsx_usb_sg_timed_out(unsigned long data)
dev_dbg(&ucr->pusb_intf->dev, "%s: sg transfer timed out", __func__);
usb_sg_cancel(&ucr->current_sg);
-
- /* we know the cancellation is caused by time-out */
- ucr->current_sg.status = -ETIMEDOUT;
}
static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr,
@@ -67,12 +64,15 @@ static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr,
ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout);
add_timer(&ucr->sg_timer);
usb_sg_wait(&ucr->current_sg);
- del_timer_sync(&ucr->sg_timer);
+ if (!del_timer_sync(&ucr->sg_timer))
+ ret = -ETIMEDOUT;
+ else
+ ret = ucr->current_sg.status;
if (act_len)
*act_len = ucr->current_sg.bytes;
- return ucr->current_sg.status;
+ return ret;
}
int rtsx_usb_transfer_data(struct rtsx_ucr *ucr, unsigned int pipe,
diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c
index 222367cc8c81..524660510599 100644
--- a/drivers/misc/genwqe/card_utils.c
+++ b/drivers/misc/genwqe/card_utils.c
@@ -352,17 +352,27 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
if (copy_from_user(sgl->lpage, user_addr + user_size -
sgl->lpage_size, sgl->lpage_size)) {
rc = -EFAULT;
- goto err_out1;
+ goto err_out2;
}
}
return 0;
+ err_out2:
+ __genwqe_free_consistent(cd, PAGE_SIZE, sgl->lpage,
+ sgl->lpage_dma_addr);
+ sgl->lpage = NULL;
+ sgl->lpage_dma_addr = 0;
err_out1:
__genwqe_free_consistent(cd, PAGE_SIZE, sgl->fpage,
sgl->fpage_dma_addr);
+ sgl->fpage = NULL;
+ sgl->fpage_dma_addr = 0;
err_out:
__genwqe_free_consistent(cd, sgl->sgl_size, sgl->sgl,
sgl->sgl_dma_addr);
+ sgl->sgl = NULL;
+ sgl->sgl_dma_addr = 0;
+ sgl->sgl_size = 0;
return -ENOMEM;
}
diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c
index bd21f8cca2aa..460ffc79f566 100644
--- a/drivers/misc/hdcp.c
+++ b/drivers/misc/hdcp.c
@@ -2103,7 +2103,8 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle)
(rc == 0) && (rsp_buf->status == 0)) {
pr_debug("Got Auth_Stream_Ready, nothing sent to rx\n");
- if (!hdcp_lib_enable_encryption(handle)) {
+ if (!handle->authenticated &&
+ !hdcp_lib_enable_encryption(handle)) {
handle->authenticated = true;
cdata.cmd = HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS;
diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c
index 020de5919c21..bdc7fcd80eca 100644
--- a/drivers/misc/mei/bus-fixup.c
+++ b/drivers/misc/mei/bus-fixup.c
@@ -151,7 +151,7 @@ static int mei_nfc_if_version(struct mei_cl *cl,
ret = 0;
bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length);
- if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) {
+ if (bytes_recv < if_version_length) {
dev_err(bus->dev, "Could not read IF version\n");
ret = -EIO;
goto err;
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index a77643954523..e59838231703 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -144,7 +144,7 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
mutex_lock(&bus->device_lock);
if (!mei_cl_is_connected(cl)) {
- rets = -EBUSY;
+ rets = -ENODEV;
goto out;
}
}
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
index a8a68acd3267..a2661381ddfc 100644
--- a/drivers/misc/mei/hw-me-regs.h
+++ b/drivers/misc/mei/hw-me-regs.h
@@ -121,6 +121,10 @@
#define MEI_DEV_ID_SPT_2 0x9D3B /* Sunrise Point 2 */
#define MEI_DEV_ID_SPT_H 0xA13A /* Sunrise Point H */
#define MEI_DEV_ID_SPT_H_2 0xA13B /* Sunrise Point H 2 */
+
+#define MEI_DEV_ID_KBP 0xA2BA /* Kaby Point */
+#define MEI_DEV_ID_KBP_2 0xA2BB /* Kaby Point 2 */
+
/*
* MEI HW Section
*/
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index 25b1997a62cb..36333750c512 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -1258,8 +1258,14 @@ static bool mei_me_fw_type_nm(struct pci_dev *pdev)
static bool mei_me_fw_type_sps(struct pci_dev *pdev)
{
u32 reg;
- /* Read ME FW Status check for SPS Firmware */
- pci_read_config_dword(pdev, PCI_CFG_HFS_1, &reg);
+ unsigned int devfn;
+
+ /*
+ * Read ME FW Status register to check for SPS Firmware
+ * The SPS FW is only signaled in pci function 0
+ */
+ devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
+ pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_1, &reg);
/* if bits [19:16] = 15, running SPS Firmware */
return (reg & 0xf0000) == 0xf0000;
}
diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c
index bae680c648ff..396d75d9fb11 100644
--- a/drivers/misc/mei/hw-txe.c
+++ b/drivers/misc/mei/hw-txe.c
@@ -972,11 +972,13 @@ static bool mei_txe_check_and_ack_intrs(struct mei_device *dev, bool do_ack)
hisr = mei_txe_br_reg_read(hw, HISR_REG);
aliveness = mei_txe_aliveness_get(dev);
- if (hhisr & IPC_HHIER_SEC && aliveness)
+ if (hhisr & IPC_HHIER_SEC && aliveness) {
ipc_isr = mei_txe_sec_reg_read_silent(hw,
SEC_IPC_HOST_INT_STATUS_REG);
- else
+ } else {
ipc_isr = 0;
+ hhisr &= ~IPC_HHIER_SEC;
+ }
generated = generated ||
(hisr & HISR_INT_STS_MSK) ||
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 80f9afcb1382..4ef189a7a2fb 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -207,7 +207,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
mutex_lock(&dev->device_lock);
if (!mei_cl_is_connected(cl)) {
- rets = -EBUSY;
+ rets = -ENODEV;
goto out;
}
}
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index 27678d8154e0..01e20384ac44 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -84,8 +84,11 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_SPT, mei_me_pch8_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, mei_me_pch8_cfg)},
- {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, mei_me_pch8_cfg)},
- {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, mei_me_pch8_cfg)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, mei_me_pch8_sps_cfg)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, mei_me_pch8_sps_cfg)},
+
+ {MEI_PCI_DEVICE(MEI_DEV_ID_KBP, mei_me_pch8_cfg)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, mei_me_pch8_cfg)},
/* required last entry */
{0, }
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 134995c9cd3c..8d03c36858b3 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -7043,7 +7043,11 @@ long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg)
break;
}
pr_debug("SET_MEM_PARAM: qseecom addr = 0x%pK\n", data);
+ mutex_lock(&app_access_lock);
+ atomic_inc(&data->ioctl_count);
ret = qseecom_set_client_mem_param(data, argp);
+ atomic_dec(&data->ioctl_count);
+ mutex_unlock(&app_access_lock);
if (ret)
pr_err("failed Qqseecom_set_mem_param request: %d\n",
ret);
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 60b02f28a8ff..ce0ecd1e9b7a 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -2848,7 +2848,7 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
struct mmc_blk_data *md = mq->data;
struct mmc_packed *packed = mqrq->packed;
bool do_rel_wr, do_data_tag;
- u32 *packed_cmd_hdr;
+ __le32 *packed_cmd_hdr;
u8 hdr_blocks;
u8 i = 1;
@@ -3544,7 +3544,7 @@ void mmc_blk_cmdq_complete_rq(struct request *rq)
else if (mrq->data && mrq->data->error)
err = mrq->data->error;
- if (err || cmdq_req->resp_err) {
+ if ((err || cmdq_req->resp_err) && !cmdq_req->skip_err_handling) {
pr_err("%s: %s: txfr error(%d)/resp_err(%d)\n",
mmc_hostname(mrq->host), __func__, err,
cmdq_req->resp_err);
@@ -3581,6 +3581,17 @@ void mmc_blk_cmdq_complete_rq(struct request *rq)
blk_end_request_all(rq, err);
goto out;
}
+ /*
+ * In case of error, cmdq_req->data.bytes_xfered is set to 0.
+ * If we call blk_end_request() with nr_bytes as 0 then the request
+ * never gets completed. So in case of error, to complete a request
+ * with error we should use blk_end_request_all().
+ */
+ if (err && cmdq_req->skip_err_handling) {
+ cmdq_req->skip_err_handling = false;
+ blk_end_request_all(rq, err);
+ goto out;
+ }
blk_end_request(rq, err, cmdq_req->data.bytes_xfered);
@@ -4114,7 +4125,8 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
set_capacity(md->disk, size);
if (mmc_host_cmd23(card->host)) {
- if (mmc_card_mmc(card) ||
+ if ((mmc_card_mmc(card) &&
+ card->csd.mmca_vsn >= CSD_SPEC_VER_3) ||
(mmc_card_sd(card) &&
card->scr.cmds & SD_SCR_CMD23_SUPPORT))
md->flags |= MMC_BLK_CMD23;
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 253979b51c84..505712f0e1b0 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -25,7 +25,7 @@ enum mmc_packed_type {
struct mmc_packed {
struct list_head list;
- u32 cmd_hdr[1024];
+ __le32 cmd_hdr[1024];
unsigned int blocks;
u8 nr_entries;
u8 retries;
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 26e57f3c5228..5396e1d00178 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -948,6 +948,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
pr_debug("%s: %d bytes transferred: %d\n",
mmc_hostname(host),
mrq->data->bytes_xfered, mrq->data->error);
+#ifdef CONFIG_BLOCK
if (mrq->lat_hist_enabled) {
ktime_t completion;
u_int64_t delta_us;
@@ -959,6 +960,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
(mrq->data->flags & MMC_DATA_READ),
delta_us);
}
+#endif
trace_mmc_blk_rw_end(cmd->opcode, cmd->arg, mrq->data);
}
@@ -1709,11 +1711,13 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
}
if (!err && areq) {
+#ifdef CONFIG_BLOCK
if (host->latency_hist_enabled) {
areq->mrq->io_start = ktime_get();
areq->mrq->lat_hist_enabled = 1;
} else
areq->mrq->lat_hist_enabled = 0;
+#endif
trace_mmc_blk_rw_start(areq->mrq->cmd->opcode,
areq->mrq->cmd->arg,
areq->mrq->data);
@@ -2110,6 +2114,38 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
EXPORT_SYMBOL(__mmc_claim_host);
/**
+ * mmc_try_claim_host - try exclusively to claim a host
+ * and keep trying for given time, with a gap of 10ms
+ * @host: mmc host to claim
+ * @dealy_ms: delay in ms
+ *
+ * Returns %1 if the host is claimed, %0 otherwise.
+ */
+int mmc_try_claim_host(struct mmc_host *host, unsigned int delay_ms)
+{
+ int claimed_host = 0;
+ unsigned long flags;
+ int retry_cnt = delay_ms/10;
+
+ do {
+ spin_lock_irqsave(&host->lock, flags);
+ if (!host->claimed || host->claimer == current) {
+ host->claimed = 1;
+ host->claimer = current;
+ host->claim_cnt += 1;
+ claimed_host = 1;
+ }
+ spin_unlock_irqrestore(&host->lock, flags);
+ if (!claimed_host)
+ mmc_delay(10);
+ } while (!claimed_host && retry_cnt--);
+ if (host->ops->enable && claimed_host && host->claim_cnt == 1)
+ host->ops->enable(host);
+ return claimed_host;
+}
+EXPORT_SYMBOL(mmc_try_claim_host);
+
+/**
* mmc_release_host - release a host
* @host: mmc host to release
*
@@ -4415,6 +4451,7 @@ static void __exit mmc_exit(void)
destroy_workqueue(workqueue);
}
+#ifdef CONFIG_BLOCK
static ssize_t
latency_hist_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -4462,6 +4499,7 @@ mmc_latency_hist_sysfs_exit(struct mmc_host *host)
{
device_remove_file(&host->class_dev, &dev_attr_latency_hist);
}
+#endif
subsys_initcall(mmc_init);
module_exit(mmc_exit);
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 333f691a73c7..e8294502a701 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -883,7 +883,9 @@ int mmc_add_host(struct mmc_host *host)
pr_err("%s: failed to create sysfs group with err %d\n",
__func__, err);
+#ifdef CONFIG_BLOCK
mmc_latency_hist_sysfs_init(host);
+#endif
mmc_start_host(host);
if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
@@ -915,7 +917,9 @@ void mmc_remove_host(struct mmc_host *host)
sysfs_remove_group(&host->parent->kobj, &dev_attr_grp);
sysfs_remove_group(&host->class_dev.kobj, &clk_scaling_attr_grp);
+#ifdef CONFIG_BLOCK
mmc_latency_hist_sysfs_exit(host);
+#endif
device_del(&host->class_dev);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 7e7d7eb4da2a..ec5ce79e84e7 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1147,7 +1147,17 @@ static void mmc_sd_detect(struct mmc_host *host)
BUG_ON(!host);
BUG_ON(!host->card);
- mmc_get_card(host->card);
+ /*
+ * Try to acquire claim host. If failed to get the lock in 2 sec,
+ * just return; This is to ensure that when this call is invoked
+ * due to pm_suspend, not to block suspend for longer duration.
+ */
+ pm_runtime_get_sync(&host->card->dev);
+ if (!mmc_try_claim_host(host, 2000)) {
+ pm_runtime_mark_last_busy(&host->card->dev);
+ pm_runtime_put_autosuspend(&host->card->dev);
+ return;
+ }
/*
* Just check if our card has been removed.
diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c
index 96d4fbf1a823..1ad5fd0e0a78 100644
--- a/drivers/mmc/host/cmdq_hci.c
+++ b/drivers/mmc/host/cmdq_hci.c
@@ -145,6 +145,29 @@ static void cmdq_clear_set_irqs(struct cmdq_host *cq_host, u32 clear, u32 set)
mb();
}
+static int cmdq_clear_task_poll(struct cmdq_host *cq_host, unsigned int tag)
+{
+ int retries = 100;
+
+ cmdq_clear_set_irqs(cq_host, CQIS_TCL, 0);
+ cmdq_writel(cq_host, 1<<tag, CQTCLR);
+ while (retries) {
+ /*
+ * Task Clear register and doorbell,
+ * both should indicate that task is cleared
+ */
+ if ((cmdq_readl(cq_host, CQTCLR) & 1<<tag) ||
+ (cmdq_readl(cq_host, CQTDBR) & 1<<tag)) {
+ udelay(5);
+ retries--;
+ continue;
+ } else
+ break;
+ }
+
+ cmdq_clear_set_irqs(cq_host, 0, CQIS_TCL);
+ return retries ? 0 : -ETIMEDOUT;
+}
#define DRV_NAME "cmdq-host"
@@ -857,6 +880,8 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err)
struct mmc_request *mrq;
int ret;
u32 dbr_set = 0;
+ u32 dev_pend_set = 0;
+ int stat_err = 0;
status = cmdq_readl(cq_host, CQIS);
@@ -865,7 +890,9 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err)
MMC_TRACE(mmc, "%s: CQIS: 0x%x err: %d\n",
__func__, status, err);
- if (err || (status & CQIS_RED)) {
+ stat_err = status & (CQIS_RED | CQIS_GCE | CQIS_ICCE);
+
+ if (err || stat_err) {
err_info = cmdq_readl(cq_host, CQTERRI);
pr_err("%s: err: %d status: 0x%08x task-err-info (0x%08lx)\n",
mmc_hostname(mmc), err, status, err_info);
@@ -968,7 +995,7 @@ skip_cqterri:
* CQE detected a reponse error from device
* In most cases, this would require a reset.
*/
- if (status & CQIS_RED) {
+ if (stat_err & CQIS_RED) {
/*
* will check if the RED error is due to a bkops
* exception once the queue is empty
@@ -987,6 +1014,62 @@ skip_cqterri:
mrq->cmdq_req->resp_arg = cmdq_readl(cq_host, CQCRA);
}
+ /*
+ * Generic Crypto error detected by CQE.
+ * Its a fatal, would require cmdq reset.
+ */
+ if (stat_err & CQIS_GCE) {
+ if (mrq->data)
+ mrq->data->error = -EIO;
+ pr_err("%s: Crypto generic error while processing task %lu!",
+ mmc_hostname(mmc), tag);
+ MMC_TRACE(mmc, "%s: GCE error detected with tag %lu\n",
+ __func__, tag);
+ }
+ /*
+ * Invalid crypto config error detected by CQE, clear the task.
+ * Task can be cleared only when CQE is halt state.
+ */
+ if (stat_err & CQIS_ICCE) {
+ /*
+ * Invalid Crypto Config Error is detected at the
+ * beginning of the transfer before the actual execution
+ * started. So just clear the task in CQE. No need to
+ * clear in device. Only the task which caused ICCE has
+ * to be cleared. Other tasks can be continue processing
+ * The first task which is about to be prepared would
+ * cause ICCE Error.
+ */
+ dbr_set = cmdq_readl(cq_host, CQTDBR);
+ dev_pend_set = cmdq_readl(cq_host, CQDPT);
+ if (dbr_set ^ dev_pend_set)
+ tag = ffs(dbr_set ^ dev_pend_set) - 1;
+ mrq = get_req_by_tag(cq_host, tag);
+ pr_err("%s: Crypto config error while processing task %lu!",
+ mmc_hostname(mmc), tag);
+ MMC_TRACE(mmc, "%s: ICCE error with tag %lu\n",
+ __func__, tag);
+ if (mrq->data)
+ mrq->data->error = -EIO;
+ else if (mrq->cmd)
+ mrq->cmd->error = -EIO;
+ /*
+ * If CQE is halted and tag is valid then clear the task
+ * then un-halt CQE and set flag to skip error recovery.
+ * If any of the condtions is not met thene it will
+ * enter into default error recovery path.
+ */
+ if (!ret && (dbr_set ^ dev_pend_set)) {
+ ret = cmdq_clear_task_poll(cq_host, tag);
+ if (ret) {
+ pr_err("%s: %s: task[%lu] clear failed ret=%d\n",
+ mmc_hostname(mmc),
+ __func__, tag, ret);
+ } else if (!cmdq_halt_poll(mmc, false)) {
+ mrq->cmdq_req->skip_err_handling = true;
+ }
+ }
+ }
cmdq_finish_data(mmc, tag);
} else {
cmdq_writel(cq_host, status, CQIS);
@@ -1052,6 +1135,7 @@ static int cmdq_halt_poll(struct mmc_host *mmc, bool halt)
cq_host->ops->clear_set_irqs(mmc, true);
cmdq_writel(cq_host, cmdq_readl(cq_host, CQCTL) & ~HALT,
CQCTL);
+ mmc_host_clr_halt(mmc);
return 0;
}
diff --git a/drivers/mmc/host/cmdq_hci.h b/drivers/mmc/host/cmdq_hci.h
index 6c10ab3859d1..db0cd956ae90 100644
--- a/drivers/mmc/host/cmdq_hci.h
+++ b/drivers/mmc/host/cmdq_hci.h
@@ -37,6 +37,8 @@
#define CQIS_TCC (1 << 1)
#define CQIS_RED (1 << 2)
#define CQIS_TCL (1 << 3)
+#define CQIS_GCE (1 << 4)
+#define CQIS_ICCE (1 << 5)
/* interrupt status enable */
#define CQISTE 0x14
@@ -112,7 +114,7 @@
/* command response argument */
#define CQCRA 0x5C
-#define CQ_INT_ALL 0xF
+#define CQ_INT_ALL 0x3F
#define CQIC_DEFAULT_ICCTH 31
#define CQIC_DEFAULT_ICTOVAL 1
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 7e1d13b68b06..7dcfb1d5034f 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -59,12 +59,13 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
host->pdata = pdev->dev.platform_data;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- /* Get registers' physical base address */
- host->phy_regs = (void *)(regs->start);
host->regs = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(host->regs))
return PTR_ERR(host->regs);
+ /* Get registers' physical base address */
+ host->phy_regs = regs->start;
+
platform_set_drvdata(pdev, host);
return dw_mci_probe(host);
}
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 7a6cedbe48a8..fb204ee6ff89 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -699,7 +699,7 @@ static int dw_mci_edmac_start_dma(struct dw_mci *host,
int ret = 0;
/* Set external dma config: burst size, burst width */
- cfg.dst_addr = (dma_addr_t)(host->phy_regs + fifo_offset);
+ cfg.dst_addr = host->phy_regs + fifo_offset;
cfg.src_addr = cfg.dst_addr;
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index d839147e591d..44ecebd1ea8c 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -661,13 +661,13 @@ static int mxs_mmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mmc);
+ spin_lock_init(&host->lock);
+
ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0,
dev_name(&pdev->dev), host);
if (ret)
goto out_free_dma;
- spin_lock_init(&host->lock);
-
ret = mmc_add_host(mmc);
if (ret)
goto out_free_dma;
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index 28a057fae0a1..72bbb12fb938 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -798,14 +798,16 @@ static int pxamci_probe(struct platform_device *pdev)
gpio_direction_output(gpio_power,
host->pdata->gpio_power_invert);
}
- if (gpio_is_valid(gpio_ro))
+ if (gpio_is_valid(gpio_ro)) {
ret = mmc_gpio_request_ro(mmc, gpio_ro);
- if (ret) {
- dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", gpio_ro);
- goto out;
- } else {
- mmc->caps2 |= host->pdata->gpio_card_ro_invert ?
- 0 : MMC_CAP2_RO_ACTIVE_HIGH;
+ if (ret) {
+ dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n",
+ gpio_ro);
+ goto out;
+ } else {
+ mmc->caps2 |= host->pdata->gpio_card_ro_invert ?
+ 0 : MMC_CAP2_RO_ACTIVE_HIGH;
+ }
}
if (gpio_is_valid(gpio_cd))
diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
index 6c71fc9f76c7..da9f71b8deb0 100644
--- a/drivers/mmc/host/rtsx_usb_sdmmc.c
+++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
@@ -1138,11 +1138,6 @@ static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
dev_dbg(sdmmc_dev(host), "%s\n", __func__);
mutex_lock(&ucr->dev_mutex);
- if (rtsx_usb_card_exclusive_check(ucr, RTSX_USB_SD_CARD)) {
- mutex_unlock(&ucr->dev_mutex);
- return;
- }
-
sd_set_power_mode(host, ios->power_mode);
sd_set_bus_width(host, ios->bus_width);
sd_set_timing(host, ios->timing, &host->ddr_mode);
@@ -1314,6 +1309,7 @@ static void rtsx_usb_update_led(struct work_struct *work)
container_of(work, struct rtsx_usb_sdmmc, led_work);
struct rtsx_ucr *ucr = host->ucr;
+ pm_runtime_get_sync(sdmmc_dev(host));
mutex_lock(&ucr->dev_mutex);
if (host->led.brightness == LED_OFF)
@@ -1322,6 +1318,7 @@ static void rtsx_usb_update_led(struct work_struct *work)
rtsx_usb_turn_on_led(ucr);
mutex_unlock(&ucr->dev_mutex);
+ pm_runtime_put(sdmmc_dev(host));
}
#endif
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 43853306a6bb..21d2a4b8f7ae 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -774,7 +774,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
* host->clock is in Hz. target_timeout is in us.
* Hence, us = 1000000 * cycles / Hz. Round up.
*/
- val = 1000000 * data->timeout_clks;
+ val = 1000000ULL * data->timeout_clks;
if (do_div(val, host->clock))
target_timeout++;
target_timeout += val;
diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c
index 744ca5cacc9b..f9fa3fad728e 100644
--- a/drivers/mtd/maps/pmcmsp-flash.c
+++ b/drivers/mtd/maps/pmcmsp-flash.c
@@ -75,15 +75,15 @@ static int __init init_msp_flash(void)
printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt);
- msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL);
+ msp_flash = kcalloc(fcnt, sizeof(*msp_flash), GFP_KERNEL);
if (!msp_flash)
return -ENOMEM;
- msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
+ msp_parts = kcalloc(fcnt, sizeof(*msp_parts), GFP_KERNEL);
if (!msp_parts)
goto free_msp_flash;
- msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL);
+ msp_maps = kcalloc(fcnt, sizeof(*msp_maps), GFP_KERNEL);
if (!msp_maps)
goto free_msp_parts;
diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c
index 142fc3d79463..784c6e1a0391 100644
--- a/drivers/mtd/maps/sa1100-flash.c
+++ b/drivers/mtd/maps/sa1100-flash.c
@@ -230,8 +230,10 @@ static struct sa_info *sa1100_setup_mtd(struct platform_device *pdev,
info->mtd = mtd_concat_create(cdev, info->num_subdev,
plat->name);
- if (info->mtd == NULL)
+ if (info->mtd == NULL) {
ret = -ENXIO;
+ goto err;
+ }
}
info->mtd->dev.parent = &pdev->dev;
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index c72313d66cf6..bc054a5ed7f8 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -241,6 +241,9 @@ static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode)
unsigned long flags;
u32 val;
+ /* Reset ECC hardware */
+ davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET);
+
spin_lock_irqsave(&davinci_nand_lock, flags);
/* Start 4-bit ECC calculation for read/write */
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index 990898b9dc72..bba7dd1b5ebf 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -513,10 +513,11 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai,
unsigned long long ec = be64_to_cpu(ech->ec);
unmap_peb(ai, pnum);
dbg_bld("Adding PEB to free: %i", pnum);
+
if (err == UBI_IO_FF_BITFLIPS)
- add_aeb(ai, free, pnum, ec, 1);
- else
- add_aeb(ai, free, pnum, ec, 0);
+ scrub = 1;
+
+ add_aeb(ai, free, pnum, ec, scrub);
continue;
} else if (err == 0 || err == UBI_IO_BITFLIPS) {
dbg_bld("Found non empty PEB:%i in pool", pnum);
@@ -748,11 +749,11 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
fmvhdr->vol_type,
be32_to_cpu(fmvhdr->last_eb_bytes));
- if (!av)
- goto fail_bad;
- if (PTR_ERR(av) == -EINVAL) {
- ubi_err(ubi, "volume (ID %i) already exists",
- fmvhdr->vol_id);
+ if (IS_ERR(av)) {
+ if (PTR_ERR(av) == -EEXIST)
+ ubi_err(ubi, "volume (ID %i) already exists",
+ fmvhdr->vol_id);
+
goto fail_bad;
}
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 22a30bbaa1bd..a90728786000 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -663,7 +663,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
int shutdown)
{
int err, scrubbing = 0, torture = 0, protect = 0, erroneous = 0;
- int vol_id = -1, lnum = -1;
+ int erase = 0, keep = 0, vol_id = -1, lnum = -1;
#ifdef CONFIG_MTD_UBI_FASTMAP
int anchor = wrk->anchor;
#endif
@@ -799,6 +799,16 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
e1->pnum);
scrubbing = 1;
goto out_not_moved;
+ } else if (ubi->fast_attach && err == UBI_IO_BAD_HDR_EBADMSG) {
+ /*
+ * While a full scan would detect interrupted erasures
+ * at attach time we can face them here when attached from
+ * Fastmap.
+ */
+ dbg_wl("PEB %d has ECC errors, maybe from an interrupted erasure",
+ e1->pnum);
+ erase = 1;
+ goto out_not_moved;
}
ubi_err(ubi, "error %d while reading VID header from PEB %d",
@@ -834,6 +844,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
* Target PEB had bit-flips or write error - torture it.
*/
torture = 1;
+ keep = 1;
goto out_not_moved;
}
@@ -930,7 +941,7 @@ out_not_moved:
ubi->erroneous_peb_count += 1;
} else if (scrubbing)
wl_tree_add(e1, &ubi->scrub);
- else
+ else if (keep)
wl_tree_add(e1, &ubi->used);
if (dst_leb_clean) {
wl_tree_add(e2, &ubi->free);
@@ -951,6 +962,12 @@ out_not_moved:
goto out_ro;
}
+ if (erase) {
+ err = do_sync_erase(ubi, e1, vol_id, lnum, 1);
+ if (err)
+ goto out_ro;
+ }
+
mutex_unlock(&ubi->move_mutex);
return 0;
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index b3d70a7a5262..5dca77e0ffed 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1317,9 +1317,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
slave_dev->name);
}
- /* already enslaved */
- if (slave_dev->flags & IFF_SLAVE) {
- netdev_dbg(bond_dev, "Error: Device was already enslaved\n");
+ /* already in-use? */
+ if (netdev_is_rx_handler_busy(slave_dev)) {
+ netdev_err(bond_dev,
+ "Error: Device is in use and cannot be enslaved\n");
return -EBUSY;
}
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index ad535a854e5c..eab132778e67 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -21,6 +21,7 @@
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
+#include <linux/workqueue.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/can/skb.h>
@@ -471,9 +472,8 @@ EXPORT_SYMBOL_GPL(can_free_echo_skb);
/*
* CAN device restart for bus-off recovery
*/
-static void can_restart(unsigned long data)
+static void can_restart(struct net_device *dev)
{
- struct net_device *dev = (struct net_device *)data;
struct can_priv *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
struct sk_buff *skb;
@@ -513,6 +513,14 @@ restart:
netdev_err(dev, "Error %d during restart", err);
}
+static void can_restart_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct can_priv *priv = container_of(dwork, struct can_priv, restart_work);
+
+ can_restart(priv->dev);
+}
+
int can_restart_now(struct net_device *dev)
{
struct can_priv *priv = netdev_priv(dev);
@@ -526,8 +534,8 @@ int can_restart_now(struct net_device *dev)
if (priv->state != CAN_STATE_BUS_OFF)
return -EBUSY;
- /* Runs as soon as possible in the timer context */
- mod_timer(&priv->restart_timer, jiffies);
+ cancel_delayed_work_sync(&priv->restart_work);
+ can_restart(dev);
return 0;
}
@@ -548,8 +556,8 @@ void can_bus_off(struct net_device *dev)
netif_carrier_off(dev);
if (priv->restart_ms)
- mod_timer(&priv->restart_timer,
- jiffies + (priv->restart_ms * HZ) / 1000);
+ schedule_delayed_work(&priv->restart_work,
+ msecs_to_jiffies(priv->restart_ms));
}
EXPORT_SYMBOL_GPL(can_bus_off);
@@ -658,6 +666,7 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
return NULL;
priv = netdev_priv(dev);
+ priv->dev = dev;
if (echo_skb_max) {
priv->echo_skb_max = echo_skb_max;
@@ -667,7 +676,7 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
priv->state = CAN_STATE_STOPPED;
- init_timer(&priv->restart_timer);
+ INIT_DELAYED_WORK(&priv->restart_work, can_restart_work);
return dev;
}
@@ -748,8 +757,6 @@ int open_candev(struct net_device *dev)
if (!netif_carrier_ok(dev))
netif_carrier_on(dev);
- setup_timer(&priv->restart_timer, can_restart, (unsigned long)dev);
-
return 0;
}
EXPORT_SYMBOL_GPL(open_candev);
@@ -764,7 +771,7 @@ void close_candev(struct net_device *dev)
{
struct can_priv *priv = netdev_priv(dev);
- del_timer_sync(&priv->restart_timer);
+ cancel_delayed_work_sync(&priv->restart_work);
can_flush_echo_skb(dev);
}
EXPORT_SYMBOL_GPL(close_candev);
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 41c0fc9f3b14..16f7cadda5c3 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -1268,11 +1268,10 @@ static int __maybe_unused flexcan_suspend(struct device *device)
struct flexcan_priv *priv = netdev_priv(dev);
int err;
- err = flexcan_chip_disable(priv);
- if (err)
- return err;
-
if (netif_running(dev)) {
+ err = flexcan_chip_disable(priv);
+ if (err)
+ return err;
netif_stop_queue(dev);
netif_device_detach(dev);
}
@@ -1285,13 +1284,17 @@ static int __maybe_unused flexcan_resume(struct device *device)
{
struct net_device *dev = dev_get_drvdata(device);
struct flexcan_priv *priv = netdev_priv(dev);
+ int err;
priv->can.state = CAN_STATE_ERROR_ACTIVE;
if (netif_running(dev)) {
netif_device_attach(dev);
netif_start_queue(dev);
+ err = flexcan_chip_enable(priv);
+ if (err)
+ return err;
}
- return flexcan_chip_enable(priv);
+ return 0;
}
static SIMPLE_DEV_PM_OPS(flexcan_pm_ops, flexcan_suspend, flexcan_resume);
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index 6f946fedbb77..0864f05633a2 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -1137,6 +1137,7 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port,
struct phy_device *phydev)
{
struct bcm_sf2_priv *priv = ds_to_priv(ds);
+ struct ethtool_eee *p = &priv->port_sts[port].eee;
u32 id_mode_dis = 0, port_mode;
const char *str = NULL;
u32 reg;
@@ -1211,6 +1212,9 @@ force_link:
reg |= DUPLX_MODE;
core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port));
+
+ if (!phydev->is_pseudo_fixed_link)
+ p->eee_enabled = bcm_sf2_eee_init(ds, port, phydev);
}
static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h
index 6bba1c98d764..c7994e372284 100644
--- a/drivers/net/dsa/bcm_sf2.h
+++ b/drivers/net/dsa/bcm_sf2.h
@@ -187,8 +187,8 @@ static inline void name##_writeq(struct bcm_sf2_priv *priv, u64 val, \
static inline void intrl2_##which##_mask_clear(struct bcm_sf2_priv *priv, \
u32 mask) \
{ \
- intrl2_##which##_writel(priv, mask, INTRL2_CPU_MASK_CLEAR); \
priv->irq##which##_mask &= ~(mask); \
+ intrl2_##which##_writel(priv, mask, INTRL2_CPU_MASK_CLEAR); \
} \
static inline void intrl2_##which##_mask_set(struct bcm_sf2_priv *priv, \
u32 mask) \
diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c
index c32f5d32f811..b56c9c581359 100644
--- a/drivers/net/ethernet/broadcom/bgmac.c
+++ b/drivers/net/ethernet/broadcom/bgmac.c
@@ -314,6 +314,10 @@ static void bgmac_dma_rx_enable(struct bgmac *bgmac,
u32 ctl;
ctl = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_RX_CTL);
+
+ /* preserve ONLY bits 16-17 from current hardware value */
+ ctl &= BGMAC_DMA_RX_ADDREXT_MASK;
+
if (bgmac->core->id.rev >= 4) {
ctl &= ~BGMAC_DMA_RX_BL_MASK;
ctl |= BGMAC_DMA_RX_BL_128 << BGMAC_DMA_RX_BL_SHIFT;
@@ -324,7 +328,6 @@ static void bgmac_dma_rx_enable(struct bgmac *bgmac,
ctl &= ~BGMAC_DMA_RX_PT_MASK;
ctl |= BGMAC_DMA_RX_PT_1 << BGMAC_DMA_RX_PT_SHIFT;
}
- ctl &= BGMAC_DMA_RX_ADDREXT_MASK;
ctl |= BGMAC_DMA_RX_ENABLE;
ctl |= BGMAC_DMA_RX_PARITY_DISABLE;
ctl |= BGMAC_DMA_RX_OVERFLOW_CONT;
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index 2e611dc5f162..1c8123816745 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -14819,6 +14819,10 @@ static int bnx2x_get_fc_npiv(struct net_device *dev,
}
offset = SHMEM2_RD(bp, fc_npiv_nvram_tbl_addr[BP_PORT(bp)]);
+ if (!offset) {
+ DP(BNX2X_MSG_MCP, "No FC-NPIV in NVRAM\n");
+ goto out;
+ }
DP(BNX2X_MSG_MCP, "Offset of FC-NPIV in NVRAM: %08x\n", offset);
/* Read the table contents from nvram */
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index 0fb3f8de88e9..91627561c58d 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -1168,6 +1168,7 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev,
struct bcmgenet_tx_ring *ring)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
+ struct device *kdev = &priv->pdev->dev;
struct enet_cb *tx_cb_ptr;
struct netdev_queue *txq;
unsigned int pkts_compl = 0;
@@ -1195,7 +1196,7 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev,
pkts_compl++;
dev->stats.tx_packets++;
dev->stats.tx_bytes += tx_cb_ptr->skb->len;
- dma_unmap_single(&dev->dev,
+ dma_unmap_single(kdev,
dma_unmap_addr(tx_cb_ptr, dma_addr),
dma_unmap_len(tx_cb_ptr, dma_len),
DMA_TO_DEVICE);
@@ -1203,7 +1204,7 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev,
} else if (dma_unmap_addr(tx_cb_ptr, dma_addr)) {
dev->stats.tx_bytes +=
dma_unmap_len(tx_cb_ptr, dma_len);
- dma_unmap_page(&dev->dev,
+ dma_unmap_page(kdev,
dma_unmap_addr(tx_cb_ptr, dma_addr),
dma_unmap_len(tx_cb_ptr, dma_len),
DMA_TO_DEVICE);
@@ -1754,6 +1755,7 @@ static int bcmgenet_alloc_rx_buffers(struct bcmgenet_priv *priv,
static void bcmgenet_free_rx_buffers(struct bcmgenet_priv *priv)
{
+ struct device *kdev = &priv->pdev->dev;
struct enet_cb *cb;
int i;
@@ -1761,7 +1763,7 @@ static void bcmgenet_free_rx_buffers(struct bcmgenet_priv *priv)
cb = &priv->rx_cbs[i];
if (dma_unmap_addr(cb, dma_addr)) {
- dma_unmap_single(&priv->dev->dev,
+ dma_unmap_single(kdev,
dma_unmap_addr(cb, dma_addr),
priv->rx_buf_len, DMA_FROM_DEVICE);
dma_unmap_addr_set(cb, dma_addr, 0);
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index ca5ac5d6f4e6..49056c33be74 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -18142,14 +18142,14 @@ static pci_ers_result_t tg3_io_error_detected(struct pci_dev *pdev,
rtnl_lock();
- /* We needn't recover from permanent error */
- if (state == pci_channel_io_frozen)
- tp->pcierr_recovery = true;
-
/* We probably don't have netdev yet */
if (!netdev || !netif_running(netdev))
goto done;
+ /* We needn't recover from permanent error */
+ if (state == pci_channel_io_frozen)
+ tp->pcierr_recovery = true;
+
tg3_phy_stop(tp);
tg3_netif_stop(tp);
@@ -18246,7 +18246,7 @@ static void tg3_io_resume(struct pci_dev *pdev)
rtnl_lock();
- if (!netif_running(netdev))
+ if (!netdev || !netif_running(netdev))
goto done;
tg3_full_lock(tp, 0);
diff --git a/drivers/net/ethernet/cavium/thunder/nic_reg.h b/drivers/net/ethernet/cavium/thunder/nic_reg.h
index afb10e326b4f..fab35a593898 100644
--- a/drivers/net/ethernet/cavium/thunder/nic_reg.h
+++ b/drivers/net/ethernet/cavium/thunder/nic_reg.h
@@ -170,7 +170,6 @@
#define NIC_QSET_SQ_0_7_DOOR (0x010838)
#define NIC_QSET_SQ_0_7_STATUS (0x010840)
#define NIC_QSET_SQ_0_7_DEBUG (0x010848)
-#define NIC_QSET_SQ_0_7_CNM_CHG (0x010860)
#define NIC_QSET_SQ_0_7_STAT_0_1 (0x010900)
#define NIC_QSET_RBDR_0_1_CFG (0x010C00)
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
index a12b2e38cf61..ff1d777f3ed9 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
@@ -380,7 +380,10 @@ static void nicvf_get_regs(struct net_device *dev,
p[i++] = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_DOOR, q);
p[i++] = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_STATUS, q);
p[i++] = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_DEBUG, q);
- p[i++] = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_CNM_CHG, q);
+ /* Padding, was NIC_QSET_SQ_0_7_CNM_CHG, which
+ * produces bus errors when read
+ */
+ p[i++] = 0;
p[i++] = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_STAT_0_1, q);
reg_offset = NIC_QSET_SQ_0_7_STAT_0_1 | (1 << 3);
p[i++] = nicvf_queue_reg_read(nic, reg_offset, q);
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index f6147ffc7fbc..ab716042bdd2 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -944,11 +944,11 @@ fec_restart(struct net_device *ndev)
* enet-mac reset will reset mac address registers too,
* so need to reconfigure it.
*/
- if (fep->quirks & FEC_QUIRK_ENET_MAC) {
- memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
- writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW);
- writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
- }
+ memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
+ writel((__force u32)cpu_to_be32(temp_mac[0]),
+ fep->hwp + FEC_ADDR_LOW);
+ writel((__force u32)cpu_to_be32(temp_mac[1]),
+ fep->hwp + FEC_ADDR_HIGH);
/* Clear any outstanding interrupt. */
writel(0xffffffff, fep->hwp + FEC_IEVENT);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index 2d74c6e4d7b6..1cf715c72683 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -302,13 +302,15 @@ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc,
void *buffer, u16 buf_len)
{
struct i40e_aq_desc *aq_desc = (struct i40e_aq_desc *)desc;
- u16 len = le16_to_cpu(aq_desc->datalen);
+ u16 len;
u8 *buf = (u8 *)buffer;
u16 i = 0;
if ((!(mask & hw->debug_mask)) || (desc == NULL))
return;
+ len = le16_to_cpu(aq_desc->datalen);
+
i40e_debug(hw, mask,
"AQ CMD: opcode 0x%04X, flags 0x%04X, datalen 0x%04X, retval 0x%04X\n",
le16_to_cpu(aq_desc->opcode),
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 2215bebe208e..4edbab6ca7ef 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -8595,7 +8595,7 @@ static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
return 0;
return ndo_dflt_bridge_getlink(skb, pid, seq, dev, veb->bridge_mode,
- nlflags, 0, 0, filter_mask, NULL);
+ 0, 0, nlflags, filter_mask, NULL);
}
#define I40E_MAX_TUNNEL_HDR_LEN 80
@@ -10853,6 +10853,12 @@ static pci_ers_result_t i40e_pci_error_detected(struct pci_dev *pdev,
dev_info(&pdev->dev, "%s: error %d\n", __func__, error);
+ if (!pf) {
+ dev_info(&pdev->dev,
+ "Cannot recover - error happened during device probe\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
/* shutdown all operations */
if (!test_bit(__I40E_SUSPENDED, &pf->state)) {
rtnl_lock();
diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index 5606a043063e..4b62aa1f9ff8 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -5220,6 +5220,19 @@ static SIMPLE_DEV_PM_OPS(sky2_pm_ops, sky2_suspend, sky2_resume);
static void sky2_shutdown(struct pci_dev *pdev)
{
+ struct sky2_hw *hw = pci_get_drvdata(pdev);
+ int port;
+
+ for (port = 0; port < hw->ports; port++) {
+ struct net_device *ndev = hw->dev[port];
+
+ rtnl_lock();
+ if (netif_running(ndev)) {
+ dev_close(ndev);
+ netif_device_detach(ndev);
+ }
+ rtnl_unlock();
+ }
sky2_suspend(&pdev->dev);
pci_wake_from_d3(pdev, device_may_wakeup(&pdev->dev));
pci_set_power_state(pdev, PCI_D3hot);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 67e9633ea9c7..232191417b93 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -2282,7 +2282,7 @@ static int mlx4_en_set_vf_mac(struct net_device *dev, int queue, u8 *mac)
struct mlx4_en_dev *mdev = en_priv->mdev;
u64 mac_u64 = mlx4_mac_to_u64(mac);
- if (!is_valid_ether_addr(mac))
+ if (is_multicast_ether_addr(mac))
return -EINVAL;
return mlx4_set_vf_mac(mdev->dev, en_priv->port, queue, mac_u64);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
index 037fc4cdf5af..cc199063612a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
@@ -143,13 +143,14 @@ static struct mlx5_cmd_layout *get_inst(struct mlx5_cmd *cmd, int idx)
return cmd->cmd_buf + (idx << cmd->log_stride);
}
-static u8 xor8_buf(void *buf, int len)
+static u8 xor8_buf(void *buf, size_t offset, int len)
{
u8 *ptr = buf;
u8 sum = 0;
int i;
+ int end = len + offset;
- for (i = 0; i < len; i++)
+ for (i = offset; i < end; i++)
sum ^= ptr[i];
return sum;
@@ -157,41 +158,49 @@ static u8 xor8_buf(void *buf, int len)
static int verify_block_sig(struct mlx5_cmd_prot_block *block)
{
- if (xor8_buf(block->rsvd0, sizeof(*block) - sizeof(block->data) - 1) != 0xff)
+ size_t rsvd0_off = offsetof(struct mlx5_cmd_prot_block, rsvd0);
+ int xor_len = sizeof(*block) - sizeof(block->data) - 1;
+
+ if (xor8_buf(block, rsvd0_off, xor_len) != 0xff)
return -EINVAL;
- if (xor8_buf(block, sizeof(*block)) != 0xff)
+ if (xor8_buf(block, 0, sizeof(*block)) != 0xff)
return -EINVAL;
return 0;
}
-static void calc_block_sig(struct mlx5_cmd_prot_block *block, u8 token,
- int csum)
+static void calc_block_sig(struct mlx5_cmd_prot_block *block)
{
- block->token = token;
- if (csum) {
- block->ctrl_sig = ~xor8_buf(block->rsvd0, sizeof(*block) -
- sizeof(block->data) - 2);
- block->sig = ~xor8_buf(block, sizeof(*block) - 1);
- }
+ int ctrl_xor_len = sizeof(*block) - sizeof(block->data) - 2;
+ size_t rsvd0_off = offsetof(struct mlx5_cmd_prot_block, rsvd0);
+
+ block->ctrl_sig = ~xor8_buf(block, rsvd0_off, ctrl_xor_len);
+ block->sig = ~xor8_buf(block, 0, sizeof(*block) - 1);
}
-static void calc_chain_sig(struct mlx5_cmd_msg *msg, u8 token, int csum)
+static void calc_chain_sig(struct mlx5_cmd_msg *msg)
{
struct mlx5_cmd_mailbox *next = msg->next;
-
- while (next) {
- calc_block_sig(next->buf, token, csum);
+ int size = msg->len;
+ int blen = size - min_t(int, sizeof(msg->first.data), size);
+ int n = (blen + MLX5_CMD_DATA_BLOCK_SIZE - 1)
+ / MLX5_CMD_DATA_BLOCK_SIZE;
+ int i = 0;
+
+ for (i = 0; i < n && next; i++) {
+ calc_block_sig(next->buf);
next = next->next;
}
}
static void set_signature(struct mlx5_cmd_work_ent *ent, int csum)
{
- ent->lay->sig = ~xor8_buf(ent->lay, sizeof(*ent->lay));
- calc_chain_sig(ent->in, ent->token, csum);
- calc_chain_sig(ent->out, ent->token, csum);
+ ent->lay->sig = ~xor8_buf(ent->lay, 0, sizeof(*ent->lay));
+ if (csum) {
+ calc_chain_sig(ent->in);
+ calc_chain_sig(ent->out);
+ }
}
static void poll_timeout(struct mlx5_cmd_work_ent *ent)
@@ -222,12 +231,17 @@ static int verify_signature(struct mlx5_cmd_work_ent *ent)
struct mlx5_cmd_mailbox *next = ent->out->next;
int err;
u8 sig;
+ int size = ent->out->len;
+ int blen = size - min_t(int, sizeof(ent->out->first.data), size);
+ int n = (blen + MLX5_CMD_DATA_BLOCK_SIZE - 1)
+ / MLX5_CMD_DATA_BLOCK_SIZE;
+ int i = 0;
- sig = xor8_buf(ent->lay, sizeof(*ent->lay));
+ sig = xor8_buf(ent->lay, 0, sizeof(*ent->lay));
if (sig != 0xff)
return -EINVAL;
- while (next) {
+ for (i = 0; i < n && next; i++) {
err = verify_block_sig(next->buf);
if (err)
return err;
@@ -641,7 +655,6 @@ static void cmd_work_handler(struct work_struct *work)
spin_unlock_irqrestore(&cmd->alloc_lock, flags);
}
- ent->token = alloc_token(cmd);
cmd->ent_arr[ent->idx] = ent;
lay = get_inst(cmd, ent->idx);
ent->lay = lay;
@@ -755,7 +768,8 @@ static u8 *get_status_ptr(struct mlx5_outbox_hdr *out)
static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in,
struct mlx5_cmd_msg *out, void *uout, int uout_size,
mlx5_cmd_cbk_t callback,
- void *context, int page_queue, u8 *status)
+ void *context, int page_queue, u8 *status,
+ u8 token)
{
struct mlx5_cmd *cmd = &dev->cmd;
struct mlx5_cmd_work_ent *ent;
@@ -772,6 +786,8 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in,
if (IS_ERR(ent))
return PTR_ERR(ent);
+ ent->token = token;
+
if (!callback)
init_completion(&ent->done);
@@ -844,7 +860,8 @@ static const struct file_operations fops = {
.write = dbg_write,
};
-static int mlx5_copy_to_msg(struct mlx5_cmd_msg *to, void *from, int size)
+static int mlx5_copy_to_msg(struct mlx5_cmd_msg *to, void *from, int size,
+ u8 token)
{
struct mlx5_cmd_prot_block *block;
struct mlx5_cmd_mailbox *next;
@@ -870,6 +887,7 @@ static int mlx5_copy_to_msg(struct mlx5_cmd_msg *to, void *from, int size)
memcpy(block->data, from, copy);
from += copy;
size -= copy;
+ block->token = token;
next = next->next;
}
@@ -939,7 +957,8 @@ static void free_cmd_box(struct mlx5_core_dev *dev,
}
static struct mlx5_cmd_msg *mlx5_alloc_cmd_msg(struct mlx5_core_dev *dev,
- gfp_t flags, int size)
+ gfp_t flags, int size,
+ u8 token)
{
struct mlx5_cmd_mailbox *tmp, *head = NULL;
struct mlx5_cmd_prot_block *block;
@@ -968,6 +987,7 @@ static struct mlx5_cmd_msg *mlx5_alloc_cmd_msg(struct mlx5_core_dev *dev,
tmp->next = head;
block->next = cpu_to_be64(tmp->next ? tmp->next->dma : 0);
block->block_num = cpu_to_be32(n - i - 1);
+ block->token = token;
head = tmp;
}
msg->next = head;
@@ -1351,7 +1371,7 @@ static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size,
}
if (IS_ERR(msg))
- msg = mlx5_alloc_cmd_msg(dev, gfp, in_size);
+ msg = mlx5_alloc_cmd_msg(dev, gfp, in_size, 0);
return msg;
}
@@ -1376,6 +1396,7 @@ static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
int err;
u8 status = 0;
u32 drv_synd;
+ u8 token;
if (pci_channel_offline(dev->pdev) ||
dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
@@ -1394,20 +1415,22 @@ static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
return err;
}
- err = mlx5_copy_to_msg(inb, in, in_size);
+ token = alloc_token(&dev->cmd);
+
+ err = mlx5_copy_to_msg(inb, in, in_size, token);
if (err) {
mlx5_core_warn(dev, "err %d\n", err);
goto out_in;
}
- outb = mlx5_alloc_cmd_msg(dev, gfp, out_size);
+ outb = mlx5_alloc_cmd_msg(dev, gfp, out_size, token);
if (IS_ERR(outb)) {
err = PTR_ERR(outb);
goto out_in;
}
err = mlx5_cmd_invoke(dev, inb, outb, out, out_size, callback, context,
- pages_queue, &status);
+ pages_queue, &status, token);
if (err)
goto out_out;
@@ -1475,7 +1498,7 @@ static int create_msg_cache(struct mlx5_core_dev *dev)
INIT_LIST_HEAD(&cmd->cache.med.head);
for (i = 0; i < NUM_LONG_LISTS; i++) {
- msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, LONG_LIST_SIZE);
+ msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, LONG_LIST_SIZE, 0);
if (IS_ERR(msg)) {
err = PTR_ERR(msg);
goto ex_err;
@@ -1485,7 +1508,7 @@ static int create_msg_cache(struct mlx5_core_dev *dev)
}
for (i = 0; i < NUM_MED_LISTS; i++) {
- msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, MED_LIST_SIZE);
+ msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, MED_LIST_SIZE, 0);
if (IS_ERR(msg)) {
err = PTR_ERR(msg);
goto ex_err;
diff --git a/drivers/net/ethernet/msm/msm_rmnet_mhi.c b/drivers/net/ethernet/msm/msm_rmnet_mhi.c
index 50d8e72a96c8..9117ea7d08c0 100644
--- a/drivers/net/ethernet/msm/msm_rmnet_mhi.c
+++ b/drivers/net/ethernet/msm/msm_rmnet_mhi.c
@@ -26,18 +26,16 @@
#include <linux/ipc_logging.h>
#include <linux/device.h>
#include <linux/errno.h>
+#include <linux/of_device.h>
#define RMNET_MHI_DRIVER_NAME "rmnet_mhi"
#define RMNET_MHI_DEV_NAME "rmnet_mhi%d"
#define MHI_DEFAULT_MTU 8000
-#define MHI_DEFAULT_MRU 8000
#define MHI_MAX_MRU 0xFFFF
#define MHI_NAPI_WEIGHT_VALUE 12
#define MHI_RX_HEADROOM 64
#define WATCHDOG_TIMEOUT (30 * HZ)
-#define MHI_RMNET_DEVICE_COUNT 1
#define RMNET_IPC_LOG_PAGES (100)
-#define IS_INBOUND(_chan) (((u32)(_chan)) % 2)
enum DBG_LVL {
MSG_VERBOSE = 0x1,
@@ -49,110 +47,69 @@ enum DBG_LVL {
MSG_reserved = 0x80000000
};
+struct debug_params {
+ enum DBG_LVL rmnet_msg_lvl;
+ enum DBG_LVL rmnet_ipc_log_lvl;
+ u64 tx_interrupts_count;
+ u64 rx_interrupts_count;
+ u64 tx_ring_full_count;
+ u64 tx_queued_packets_count;
+ u64 rx_interrupts_in_masked_irq;
+ u64 rx_napi_skb_burst_min;
+ u64 rx_napi_skb_burst_max;
+ u64 tx_cb_skb_free_burst_min;
+ u64 tx_cb_skb_free_burst_max;
+ u64 rx_napi_budget_overflow;
+ u64 rx_fragmentation;
+};
+
struct __packed mhi_skb_priv {
dma_addr_t dma_addr;
size_t dma_size;
};
-enum DBG_LVL rmnet_msg_lvl = MSG_CRITICAL;
-
-#ifdef CONFIG_MSM_MHI_DEBUG
-enum DBG_LVL rmnet_ipc_log_lvl = MSG_VERBOSE;
-#else
-enum DBG_LVL rmnet_ipc_log_lvl = MSG_ERROR;
-#endif
-
-module_param(rmnet_msg_lvl , uint, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(rmnet_msg_lvl, "dbg lvl");
-module_param(rmnet_ipc_log_lvl, uint, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(rmnet_ipc_log_lvl, "dbg lvl");
-
-unsigned int mru = MHI_DEFAULT_MRU;
-module_param(mru, uint, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(mru, "MRU interface setting");
-
-void *rmnet_ipc_log;
-
-#define rmnet_log(_msg_lvl, _msg, ...) do { \
- if ((_msg_lvl) >= rmnet_msg_lvl) \
+#define rmnet_log(rmnet_mhi_ptr, _msg_lvl, _msg, ...) do { \
+ if ((_msg_lvl) >= rmnet_mhi_ptr->debug.rmnet_msg_lvl) \
pr_alert("[%s] " _msg, __func__, ##__VA_ARGS__);\
- if (rmnet_ipc_log && ((_msg_lvl) >= rmnet_ipc_log_lvl)) \
- ipc_log_string(rmnet_ipc_log, \
+ if (rmnet_mhi_ptr->rmnet_ipc_log && \
+ ((_msg_lvl) >= rmnet_mhi_ptr->debug.rmnet_ipc_log_lvl)) \
+ ipc_log_string(rmnet_mhi_ptr->rmnet_ipc_log, \
"[%s] " _msg, __func__, ##__VA_ARGS__); \
} while (0)
-unsigned long tx_interrupts_count[MHI_RMNET_DEVICE_COUNT];
-module_param_array(tx_interrupts_count, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(tx_interrupts_count, "Tx interrupts");
-
-unsigned long rx_interrupts_count[MHI_RMNET_DEVICE_COUNT];
-module_param_array(rx_interrupts_count, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(rx_interrupts_count, "RX interrupts");
-
-unsigned long tx_ring_full_count[MHI_RMNET_DEVICE_COUNT];
-module_param_array(tx_ring_full_count, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(tx_ring_full_count, "RING FULL errors from MHI Core");
-
-
-unsigned long tx_queued_packets_count[MHI_RMNET_DEVICE_COUNT];
-module_param_array(tx_queued_packets_count, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(tx_queued_packets_count, "TX packets queued in MHI core");
-
-unsigned long rx_interrupts_in_masked_irq[MHI_RMNET_DEVICE_COUNT];
-module_param_array(rx_interrupts_in_masked_irq, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(rx_interrupts_in_masked_irq,
- "RX interrupts while IRQs are masked");
-
-unsigned long rx_napi_skb_burst_min[MHI_RMNET_DEVICE_COUNT];
-module_param_array(rx_napi_skb_burst_min, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(rx_napi_skb_burst_min, "MIN SKBs sent to NS during NAPI");
-
-unsigned long rx_napi_skb_burst_max[MHI_RMNET_DEVICE_COUNT];
-module_param_array(rx_napi_skb_burst_max, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(rx_napi_skb_burst_max, "MAX SKBs sent to NS during NAPI");
-
-unsigned long tx_cb_skb_free_burst_min[MHI_RMNET_DEVICE_COUNT];
-module_param_array(tx_cb_skb_free_burst_min, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(tx_cb_skb_free_burst_min, "MIN SKBs freed during TX CB");
-
-unsigned long tx_cb_skb_free_burst_max[MHI_RMNET_DEVICE_COUNT];
-module_param_array(tx_cb_skb_free_burst_max, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(tx_cb_skb_free_burst_max, "MAX SKBs freed during TX CB");
-
-unsigned long rx_napi_budget_overflow[MHI_RMNET_DEVICE_COUNT];
-module_param_array(rx_napi_budget_overflow, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(rx_napi_budget_overflow,
- "Budget hit with more items to read counter");
-
-unsigned long rx_fragmentation[MHI_RMNET_DEVICE_COUNT];
-module_param_array(rx_fragmentation, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(rx_fragmentation,
- "Number of fragmented packets received");
-
struct rmnet_mhi_private {
- int dev_index;
+ struct list_head node;
struct mhi_client_handle *tx_client_handle;
struct mhi_client_handle *rx_client_handle;
enum MHI_CLIENT_CHANNEL tx_channel;
enum MHI_CLIENT_CHANNEL rx_channel;
struct sk_buff_head tx_buffers;
struct sk_buff_head rx_buffers;
- uint32_t mru;
+ atomic_t rx_pool_len;
+ u32 mru;
struct napi_struct napi;
gfp_t allocation_flags;
uint32_t tx_buffers_max;
uint32_t rx_buffers_max;
+ u32 alloc_fail;
u32 tx_enabled;
u32 rx_enabled;
u32 mhi_enabled;
+ struct platform_device *pdev;
struct net_device *dev;
atomic_t irq_masked_cntr;
- rwlock_t out_chan_full_lock;
+ spinlock_t out_chan_full_lock; /* tx queue lock */
atomic_t pending_data;
struct sk_buff *frag_skb;
+ struct work_struct alloc_work;
+ /* lock to queue hardware and internal queue */
+ spinlock_t alloc_lock;
+ void *rmnet_ipc_log;
+ struct debug_params debug;
+ struct dentry *dentry;
};
-static struct rmnet_mhi_private rmnet_mhi_ctxt_list[MHI_RMNET_DEVICE_COUNT];
+static LIST_HEAD(rmnet_mhi_ctxt_list);
static int rmnet_mhi_process_fragment(struct rmnet_mhi_private *rmnet_mhi_ptr,
struct sk_buff *skb, int frag)
@@ -184,7 +141,7 @@ static int rmnet_mhi_process_fragment(struct rmnet_mhi_private *rmnet_mhi_ptr,
if (frag) {
/* This is the first fragment */
rmnet_mhi_ptr->frag_skb = skb;
- rx_fragmentation[rmnet_mhi_ptr->dev_index]++;
+ rmnet_mhi_ptr->debug.rx_fragmentation++;
} else {
netif_receive_skb(skb);
}
@@ -196,8 +153,10 @@ static void rmnet_mhi_internal_clean_unmap_buffers(struct net_device *dev,
enum dma_data_direction dir)
{
struct mhi_skb_priv *skb_priv;
+ struct rmnet_mhi_private *rmnet_mhi_ptr =
+ *(struct rmnet_mhi_private **)netdev_priv(dev);
- rmnet_log(MSG_INFO, "Entered\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n");
while (!skb_queue_empty(queue)) {
struct sk_buff *skb = skb_dequeue(queue);
skb_priv = (struct mhi_skb_priv *)(skb->cb);
@@ -205,7 +164,7 @@ static void rmnet_mhi_internal_clean_unmap_buffers(struct net_device *dev,
kfree_skb(skb);
}
}
- rmnet_log(MSG_INFO, "Exited\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited\n");
}
static __be16 rmnet_mhi_ip_type_trans(struct sk_buff *skb)
@@ -228,6 +187,83 @@ static __be16 rmnet_mhi_ip_type_trans(struct sk_buff *skb)
return protocol;
}
+static int rmnet_alloc_rx(struct rmnet_mhi_private *rmnet_mhi_ptr,
+ gfp_t alloc_flags)
+{
+ u32 cur_mru = rmnet_mhi_ptr->mru;
+ struct mhi_skb_priv *skb_priv;
+ unsigned long flags;
+ int ret;
+ struct sk_buff *skb;
+
+ while (atomic_read(&rmnet_mhi_ptr->rx_pool_len) <
+ rmnet_mhi_ptr->rx_buffers_max) {
+ skb = alloc_skb(cur_mru, alloc_flags);
+ if (!skb) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "SKB Alloc failed with flags:0x%x\n",
+ alloc_flags);
+ return -ENOMEM;
+ }
+ skb_priv = (struct mhi_skb_priv *)(skb->cb);
+ skb_priv->dma_size = cur_mru - MHI_RX_HEADROOM;
+ 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);
+
+ /* It's possible by the time alloc_skb (GFP_KERNEL)
+ * returns we already called rmnet_alloc_rx
+ * in atomic context and allocated memory using
+ * GFP_ATOMIC and returned.
+ */
+ if (unlikely(atomic_read(&rmnet_mhi_ptr->rx_pool_len) >=
+ rmnet_mhi_ptr->rx_buffers_max)) {
+ spin_unlock_irqrestore(&rmnet_mhi_ptr->alloc_lock,
+ flags);
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ ret = mhi_queue_xfer(
+ rmnet_mhi_ptr->rx_client_handle,
+ skb->data,
+ skb_priv->dma_size,
+ MHI_EOT);
+ if (unlikely(ret != 0)) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "mhi_queue_xfer failed, error %d", ret);
+ spin_unlock_irqrestore(&rmnet_mhi_ptr->alloc_lock,
+ flags);
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+ skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb);
+ atomic_inc(&rmnet_mhi_ptr->rx_pool_len);
+ spin_unlock_irqrestore(&rmnet_mhi_ptr->alloc_lock, flags);
+ }
+
+ return 0;
+}
+
+static void rmnet_mhi_alloc_work(struct work_struct *work)
+{
+ struct rmnet_mhi_private *rmnet_mhi_ptr = container_of(work,
+ struct rmnet_mhi_private,
+ alloc_work);
+ int ret;
+
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n");
+ ret = rmnet_alloc_rx(rmnet_mhi_ptr,
+ rmnet_mhi_ptr->allocation_flags);
+
+ WARN_ON(ret == -ENOMEM);
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exit\n");
+}
+
static int rmnet_mhi_poll(struct napi_struct *napi, int budget)
{
int received_packets = 0;
@@ -238,20 +274,22 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget)
bool should_reschedule = true;
struct sk_buff *skb;
struct mhi_skb_priv *skb_priv;
- int r, cur_mru;
+ int r;
+
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n");
- rmnet_log(MSG_VERBOSE, "Entered\n");
- rmnet_mhi_ptr->mru = mru;
while (received_packets < budget) {
struct mhi_result *result =
mhi_poll(rmnet_mhi_ptr->rx_client_handle);
if (result->transaction_status == -ENOTCONN) {
- rmnet_log(MSG_INFO,
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
"Transaction status not ready, continuing\n");
break;
} else if (result->transaction_status != 0 &&
result->transaction_status != -EOVERFLOW) {
- rmnet_log(MSG_CRITICAL,
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
"mhi_poll failed, error %d\n",
result->transaction_status);
break;
@@ -259,15 +297,15 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget)
/* Nothing more to read, or out of buffers in MHI layer */
if (unlikely(!result->buf_addr || !result->bytes_xferd)) {
- rmnet_log(MSG_CRITICAL,
- "Not valid buff not rescheduling\n");
should_reschedule = false;
break;
}
+ atomic_dec(&rmnet_mhi_ptr->rx_pool_len);
skb = skb_dequeue(&(rmnet_mhi_ptr->rx_buffers));
if (unlikely(!skb)) {
- rmnet_log(MSG_CRITICAL,
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
"No RX buffers to match");
break;
}
@@ -285,7 +323,7 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget)
else
r = rmnet_mhi_process_fragment(rmnet_mhi_ptr, skb, 0);
if (r) {
- rmnet_log(MSG_CRITICAL,
+ rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL,
"Failed to process fragmented packet ret %d",
r);
BUG();
@@ -296,45 +334,18 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget)
dev->stats.rx_packets++;
dev->stats.rx_bytes += result->bytes_xferd;
- /* Need to allocate a new buffer instead of this one */
- cur_mru = rmnet_mhi_ptr->mru;
- skb = alloc_skb(cur_mru, GFP_ATOMIC);
- if (unlikely(!skb)) {
- rmnet_log(MSG_CRITICAL,
- "Can't allocate a new RX buffer for MHI");
- break;
- }
- skb_priv = (struct mhi_skb_priv *)(skb->cb);
- skb_priv->dma_size = cur_mru;
-
- rmnet_log(MSG_VERBOSE,
- "Allocated SKB of MRU 0x%x, SKB_DATA 0%p SKB_LEN 0x%x\n",
- rmnet_mhi_ptr->mru, skb->data, skb->len);
- /* Reserve headroom, tail == data */
- skb_reserve(skb, MHI_RX_HEADROOM);
- skb_priv->dma_size -= MHI_RX_HEADROOM;
- skb_priv->dma_addr = 0;
-
- rmnet_log(MSG_VERBOSE,
- "Mapped SKB %p to DMA Addr 0x%lx, DMA_SIZE: 0x%lx\n",
- skb->data,
- (uintptr_t)skb->data,
- (uintptr_t)skb_priv->dma_size);
-
-
- res = mhi_queue_xfer(
- rmnet_mhi_ptr->rx_client_handle,
- skb->data, skb_priv->dma_size, MHI_EOT);
-
- if (unlikely(0 != res)) {
- rmnet_log(MSG_CRITICAL,
- "mhi_queue_xfer failed, error %d", res);
- dev_kfree_skb_irq(skb);
- break;
- }
- skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb);
} /* while (received_packets < budget) or any other error */
+ /* Queue new buffers */
+ res = rmnet_alloc_rx(rmnet_mhi_ptr, GFP_ATOMIC);
+ if (res == -ENOMEM) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "out of mem, queuing bg worker\n");
+ rmnet_mhi_ptr->alloc_fail++;
+ schedule_work(&rmnet_mhi_ptr->alloc_work);
+ }
+
napi_complete(napi);
/* We got a NULL descriptor back */
@@ -342,22 +353,24 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget)
if (atomic_read(&rmnet_mhi_ptr->irq_masked_cntr)) {
atomic_dec(&rmnet_mhi_ptr->irq_masked_cntr);
mhi_unmask_irq(rmnet_mhi_ptr->rx_client_handle);
+ mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, true);
}
} else {
if (received_packets == budget)
- rx_napi_budget_overflow[rmnet_mhi_ptr->dev_index]++;
+ rmnet_mhi_ptr->debug.rx_napi_budget_overflow++;
napi_reschedule(napi);
}
- rx_napi_skb_burst_min[rmnet_mhi_ptr->dev_index] =
- min((unsigned long)received_packets,
- rx_napi_skb_burst_min[rmnet_mhi_ptr->dev_index]);
+ rmnet_mhi_ptr->debug.rx_napi_skb_burst_min =
+ min((u64)received_packets,
+ rmnet_mhi_ptr->debug.rx_napi_skb_burst_min);
- rx_napi_skb_burst_max[rmnet_mhi_ptr->dev_index] =
- max((unsigned long)received_packets,
- rx_napi_skb_burst_max[rmnet_mhi_ptr->dev_index]);
+ rmnet_mhi_ptr->debug.rx_napi_skb_burst_max =
+ max((u64)received_packets,
+ rmnet_mhi_ptr->debug.rx_napi_skb_burst_max);
- rmnet_log(MSG_VERBOSE, "Exited, polled %d pkts\n", received_packets);
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE,
+ "Exited, polled %d pkts\n", received_packets);
return received_packets;
}
@@ -365,7 +378,8 @@ void rmnet_mhi_clean_buffers(struct net_device *dev)
{
struct rmnet_mhi_private *rmnet_mhi_ptr =
*(struct rmnet_mhi_private **)netdev_priv(dev);
- rmnet_log(MSG_INFO, "Entered\n");
+
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n");
/* Clean TX buffers */
rmnet_mhi_internal_clean_unmap_buffers(dev,
&rmnet_mhi_ptr->tx_buffers,
@@ -375,16 +389,16 @@ void rmnet_mhi_clean_buffers(struct net_device *dev)
rmnet_mhi_internal_clean_unmap_buffers(dev,
&rmnet_mhi_ptr->rx_buffers,
DMA_FROM_DEVICE);
- rmnet_log(MSG_INFO, "Exited\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited\n");
}
static int rmnet_mhi_disable_channels(struct rmnet_mhi_private *rmnet_mhi_ptr)
{
- rmnet_log(MSG_INFO, "Closing MHI TX channel\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Closing MHI TX channel\n");
mhi_close_channel(rmnet_mhi_ptr->tx_client_handle);
- rmnet_log(MSG_INFO, "Closing MHI RX channel\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Closing MHI RX channel\n");
mhi_close_channel(rmnet_mhi_ptr->rx_client_handle);
- rmnet_log(MSG_INFO, "Clearing Pending TX buffers.\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Clearing Pending TX buffers.\n");
rmnet_mhi_clean_buffers(rmnet_mhi_ptr->dev);
rmnet_mhi_ptr->tx_client_handle = NULL;
rmnet_mhi_ptr->rx_client_handle = NULL;
@@ -394,52 +408,19 @@ static int rmnet_mhi_disable_channels(struct rmnet_mhi_private *rmnet_mhi_ptr)
static int rmnet_mhi_init_inbound(struct rmnet_mhi_private *rmnet_mhi_ptr)
{
- u32 i;
int res;
- struct mhi_skb_priv *rx_priv;
- u32 cur_mru = rmnet_mhi_ptr->mru;
- struct sk_buff *skb;
- rmnet_log(MSG_INFO, "Entered\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n");
rmnet_mhi_ptr->tx_buffers_max = mhi_get_max_desc(
rmnet_mhi_ptr->tx_client_handle);
rmnet_mhi_ptr->rx_buffers_max = mhi_get_max_desc(
rmnet_mhi_ptr->rx_client_handle);
+ atomic_set(&rmnet_mhi_ptr->rx_pool_len, 0);
+ res = rmnet_alloc_rx(rmnet_mhi_ptr,
+ rmnet_mhi_ptr->allocation_flags);
- for (i = 0; i < rmnet_mhi_ptr->rx_buffers_max; i++) {
-
- skb = alloc_skb(cur_mru, rmnet_mhi_ptr->allocation_flags);
-
- if (!skb) {
- rmnet_log(MSG_CRITICAL,
- "SKB allocation failure during open");
- return -ENOMEM;
- }
- rx_priv = (struct mhi_skb_priv *)(skb->cb);
-
- skb_reserve(skb, MHI_RX_HEADROOM);
- rx_priv->dma_size = cur_mru - MHI_RX_HEADROOM;
- rx_priv->dma_addr = 0;
- skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb);
- }
-
- /* Submit the RX buffers */
- for (i = 0; i < rmnet_mhi_ptr->rx_buffers_max; i++) {
- skb = skb_dequeue(&rmnet_mhi_ptr->rx_buffers);
- rx_priv = (struct mhi_skb_priv *)(skb->cb);
- res = mhi_queue_xfer(rmnet_mhi_ptr->rx_client_handle,
- skb->data,
- rx_priv->dma_size,
- MHI_EOT);
- if (0 != res) {
- rmnet_log(MSG_CRITICAL,
- "mhi_queue_xfer failed, error %d", res);
- return -EIO;
- }
- skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb);
- }
- rmnet_log(MSG_INFO, "Exited\n");
- return 0;
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited with %d\n", res);
+ return res;
}
static void rmnet_mhi_tx_cb(struct mhi_result *result)
@@ -451,9 +432,9 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result)
rmnet_mhi_ptr = result->user_data;
dev = rmnet_mhi_ptr->dev;
- tx_interrupts_count[rmnet_mhi_ptr->dev_index]++;
+ rmnet_mhi_ptr->debug.tx_interrupts_count++;
- rmnet_log(MSG_VERBOSE, "Entered\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n");
if (!result->buf_addr || !result->bytes_xferd)
return;
/* Free the buffers which are TX'd up to the provided address */
@@ -461,7 +442,8 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result)
struct sk_buff *skb =
skb_dequeue(&(rmnet_mhi_ptr->tx_buffers));
if (!skb) {
- rmnet_log(MSG_CRITICAL,
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
"NULL buffer returned, error");
break;
} else {
@@ -482,20 +464,21 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result)
*/
}
} /* While TX queue is not empty */
- tx_cb_skb_free_burst_min[rmnet_mhi_ptr->dev_index] =
- min(burst_counter,
- tx_cb_skb_free_burst_min[rmnet_mhi_ptr->dev_index]);
- tx_cb_skb_free_burst_max[rmnet_mhi_ptr->dev_index] =
- max(burst_counter,
- tx_cb_skb_free_burst_max[rmnet_mhi_ptr->dev_index]);
+ rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_min =
+ min((u64)burst_counter,
+ rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_min);
+
+ rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_max =
+ max((u64)burst_counter,
+ rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_max);
/* In case we couldn't write again, now we can! */
- read_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock, flags);
- rmnet_log(MSG_VERBOSE, "Waking up queue\n");
+ spin_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock, flags);
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Waking up queue\n");
netif_wake_queue(dev);
- read_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock, flags);
- rmnet_log(MSG_VERBOSE, "Exited\n");
+ spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock, flags);
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n");
}
static void rmnet_mhi_rx_cb(struct mhi_result *result)
@@ -505,17 +488,18 @@ static void rmnet_mhi_rx_cb(struct mhi_result *result)
rmnet_mhi_ptr = result->user_data;
dev = rmnet_mhi_ptr->dev;
- rmnet_log(MSG_VERBOSE, "Entered\n");
- rx_interrupts_count[rmnet_mhi_ptr->dev_index]++;
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n");
+ rmnet_mhi_ptr->debug.rx_interrupts_count++;
if (napi_schedule_prep(&(rmnet_mhi_ptr->napi))) {
mhi_mask_irq(rmnet_mhi_ptr->rx_client_handle);
atomic_inc(&rmnet_mhi_ptr->irq_masked_cntr);
+ mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, false);
__napi_schedule(&(rmnet_mhi_ptr->napi));
} else {
- rx_interrupts_in_masked_irq[rmnet_mhi_ptr->dev_index]++;
+ rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq++;
}
- rmnet_log(MSG_VERBOSE, "Exited\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n");
}
static int rmnet_mhi_open(struct net_device *dev)
@@ -523,11 +507,19 @@ static int rmnet_mhi_open(struct net_device *dev)
struct rmnet_mhi_private *rmnet_mhi_ptr =
*(struct rmnet_mhi_private **)netdev_priv(dev);
- rmnet_log(MSG_INFO,
- "Opened net dev interface for MHI chans %d and %d\n",
- rmnet_mhi_ptr->tx_channel,
- rmnet_mhi_ptr->rx_channel);
- netif_start_queue(dev);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "Opened net dev interface for MHI chans %d and %d\n",
+ rmnet_mhi_ptr->tx_channel,
+ rmnet_mhi_ptr->rx_channel);
+
+ /* tx queue may not necessarily be stopped already
+ * so stop the queue if tx path is not enabled
+ */
+ if (!rmnet_mhi_ptr->tx_client_handle)
+ netif_stop_queue(dev);
+ else
+ netif_start_queue(dev);
/* Poll to check if any buffers are accumulated in the
* transport buffers
@@ -535,9 +527,10 @@ static int rmnet_mhi_open(struct net_device *dev)
if (napi_schedule_prep(&(rmnet_mhi_ptr->napi))) {
mhi_mask_irq(rmnet_mhi_ptr->rx_client_handle);
atomic_inc(&rmnet_mhi_ptr->irq_masked_cntr);
+ mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, false);
__napi_schedule(&(rmnet_mhi_ptr->napi));
} else {
- rx_interrupts_in_masked_irq[rmnet_mhi_ptr->dev_index]++;
+ rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq++;
}
return 0;
@@ -575,14 +568,17 @@ static int rmnet_mhi_stop(struct net_device *dev)
{
struct rmnet_mhi_private *rmnet_mhi_ptr =
*(struct rmnet_mhi_private **)netdev_priv(dev);
+
netif_stop_queue(dev);
- rmnet_log(MSG_VERBOSE, "Entered\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n");
if (atomic_read(&rmnet_mhi_ptr->irq_masked_cntr)) {
mhi_unmask_irq(rmnet_mhi_ptr->rx_client_handle);
atomic_dec(&rmnet_mhi_ptr->irq_masked_cntr);
- rmnet_log(MSG_ERROR, "IRQ was masked, unmasking...\n");
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_ERROR,
+ "IRQ was masked, unmasking...\n");
}
- rmnet_log(MSG_VERBOSE, "Exited\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n");
return 0;
}
@@ -601,61 +597,53 @@ static int rmnet_mhi_xmit(struct sk_buff *skb, struct net_device *dev)
*(struct rmnet_mhi_private **)netdev_priv(dev);
int res = 0;
unsigned long flags;
- int retry = 0;
struct mhi_skb_priv *tx_priv;
- rmnet_log(MSG_VERBOSE, "Entered chan %d\n", rmnet_mhi_ptr->tx_channel);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_VERBOSE,
+ "Entered chan %d\n",
+ rmnet_mhi_ptr->tx_channel);
tx_priv = (struct mhi_skb_priv *)(skb->cb);
tx_priv->dma_size = skb->len;
tx_priv->dma_addr = 0;
- do {
- retry = 0;
- res = mhi_queue_xfer(rmnet_mhi_ptr->tx_client_handle,
- skb->data,
- skb->len,
- MHI_EOT);
-
- if (-ENOSPC == res) {
- write_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock,
- flags);
- if (!mhi_get_free_desc(
- rmnet_mhi_ptr->tx_client_handle)) {
- /* Stop writing until we can write again */
- tx_ring_full_count[rmnet_mhi_ptr->dev_index]++;
- netif_stop_queue(dev);
- rmnet_log(MSG_VERBOSE, "Stopping Queue\n");
- write_unlock_irqrestore(
- &rmnet_mhi_ptr->out_chan_full_lock,
- flags);
- goto rmnet_mhi_xmit_error_cleanup;
- } else {
- retry = 1;
- }
- write_unlock_irqrestore(
- &rmnet_mhi_ptr->out_chan_full_lock,
- flags);
- }
- } while (retry);
- if (0 != res) {
+ if (mhi_get_free_desc(rmnet_mhi_ptr->tx_client_handle) <= 0) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_VERBOSE,
+ "Stopping Queue\n");
+ spin_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock,
+ flags);
+ rmnet_mhi_ptr->debug.tx_ring_full_count++;
+ netif_stop_queue(dev);
+ spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock,
+ flags);
+ return NETDEV_TX_BUSY;
+ }
+ res = mhi_queue_xfer(rmnet_mhi_ptr->tx_client_handle,
+ skb->data,
+ skb->len,
+ MHI_EOT);
+
+ if (res != 0) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Failed to queue with reason:%d\n",
+ res);
+ spin_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock,
+ flags);
netif_stop_queue(dev);
- rmnet_log(MSG_CRITICAL,
- "mhi_queue_xfer failed, error %d\n", res);
- goto rmnet_mhi_xmit_error_cleanup;
+ spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock,
+ flags);
+ return NETDEV_TX_BUSY;
}
skb_queue_tail(&(rmnet_mhi_ptr->tx_buffers), skb);
-
dev->trans_start = jiffies;
+ rmnet_mhi_ptr->debug.tx_queued_packets_count++;
- tx_queued_packets_count[rmnet_mhi_ptr->dev_index]++;
- rmnet_log(MSG_VERBOSE, "Exited\n");
- return 0;
-
-rmnet_mhi_xmit_error_cleanup:
- rmnet_log(MSG_VERBOSE, "Ring full\n");
- return NETDEV_TX_BUSY;
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n");
+ return NETDEV_TX_OK;
}
static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
@@ -670,24 +658,27 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
sizeof(struct rmnet_ioctl_extended_s));
if (rc) {
- rmnet_log(MSG_CRITICAL,
- "copy_from_user failed ,error %d", rc);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "copy_from_user failed ,error %d",
+ rc);
return rc;
}
switch (ext_cmd.extended_ioctl) {
case RMNET_IOCTL_SET_MRU:
- if ((0 > ext_cmd.u.data) || (ext_cmd.u.data > MHI_MAX_MRU)) {
- rmnet_log(MSG_CRITICAL,
- "Can't set MRU, value %u is invalid\n",
- ext_cmd.u.data);
+ if (!ext_cmd.u.data || ext_cmd.u.data > MHI_MAX_MRU) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Can't set MRU, value %u is invalid\n",
+ ext_cmd.u.data);
return -EINVAL;
}
- rmnet_log(MSG_INFO,
- "MRU change request to 0x%x\n",
- ext_cmd.u.data);
- mru = ext_cmd.u.data;
- rmnet_mhi_ptr->mru = mru;
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "MRU change request to 0x%x\n",
+ ext_cmd.u.data);
+ rmnet_mhi_ptr->mru = ext_cmd.u.data;
break;
case RMNET_IOCTL_GET_EPID:
ext_cmd.u.data =
@@ -706,7 +697,8 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
mhi_set_lpm(rmnet_mhi_ptr->tx_client_handle,
ext_cmd.u.data);
} else {
- rmnet_log(MSG_ERROR,
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_ERROR,
"Cannot set LPM value, MHI is not up.\n");
return -ENODEV;
}
@@ -720,9 +712,10 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
sizeof(struct rmnet_ioctl_extended_s));
if (rc)
- rmnet_log(MSG_CRITICAL,
- "copy_to_user failed, error %d\n",
- rc);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "copy_to_user failed, error %d\n",
+ rc);
return rc;
}
@@ -801,50 +794,49 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr)
struct rmnet_mhi_private **rmnet_mhi_ctxt = NULL;
int r = 0;
- memset(tx_interrupts_count, 0, sizeof(tx_interrupts_count));
- memset(rx_interrupts_count, 0, sizeof(rx_interrupts_count));
- memset(rx_interrupts_in_masked_irq, 0,
- sizeof(rx_interrupts_in_masked_irq));
- memset(rx_napi_skb_burst_min, 0, sizeof(rx_napi_skb_burst_min));
- memset(rx_napi_skb_burst_max, 0, sizeof(rx_napi_skb_burst_max));
- memset(tx_cb_skb_free_burst_min, 0, sizeof(tx_cb_skb_free_burst_min));
- memset(tx_cb_skb_free_burst_max, 0, sizeof(tx_cb_skb_free_burst_max));
- memset(tx_ring_full_count, 0, sizeof(tx_ring_full_count));
- memset(tx_queued_packets_count, 0, sizeof(tx_queued_packets_count));
- memset(rx_napi_budget_overflow, 0, sizeof(rx_napi_budget_overflow));
-
- rmnet_log(MSG_INFO, "Entered.\n");
-
- if (rmnet_mhi_ptr == NULL) {
- rmnet_log(MSG_CRITICAL, "Bad input args.\n");
- return -EINVAL;
- }
-
- rx_napi_skb_burst_min[rmnet_mhi_ptr->dev_index] = UINT_MAX;
- tx_cb_skb_free_burst_min[rmnet_mhi_ptr->dev_index] = UINT_MAX;
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered.\n");
+
+ rmnet_mhi_ptr->debug.tx_interrupts_count = 0;
+ rmnet_mhi_ptr->debug.rx_interrupts_count = 0;
+ rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq = 0;
+ rmnet_mhi_ptr->debug.rx_napi_skb_burst_min = 0;
+ rmnet_mhi_ptr->debug.rx_napi_skb_burst_max = 0;
+ rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_min = 0;
+ rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_max = 0;
+ rmnet_mhi_ptr->debug.tx_ring_full_count = 0;
+ rmnet_mhi_ptr->debug.tx_queued_packets_count = 0;
+ rmnet_mhi_ptr->debug.rx_napi_budget_overflow = 0;
+ rmnet_mhi_ptr->debug.rx_napi_skb_burst_min = UINT_MAX;
+ rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_min = UINT_MAX;
skb_queue_head_init(&(rmnet_mhi_ptr->tx_buffers));
skb_queue_head_init(&(rmnet_mhi_ptr->rx_buffers));
if (rmnet_mhi_ptr->tx_client_handle != NULL) {
- rmnet_log(MSG_INFO,
- "Opening TX channel\n");
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "Opening TX channel\n");
r = mhi_open_channel(rmnet_mhi_ptr->tx_client_handle);
if (r != 0) {
- rmnet_log(MSG_CRITICAL,
- "Failed to start TX chan ret %d\n", r);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Failed to start TX chan ret %d\n",
+ r);
goto mhi_tx_chan_start_fail;
} else {
rmnet_mhi_ptr->tx_enabled = 1;
}
}
if (rmnet_mhi_ptr->rx_client_handle != NULL) {
- rmnet_log(MSG_INFO,
- "Opening RX channel\n");
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "Opening RX channel\n");
r = mhi_open_channel(rmnet_mhi_ptr->rx_client_handle);
if (r != 0) {
- rmnet_log(MSG_CRITICAL,
- "Failed to start RX chan ret %d\n", r);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Failed to start RX chan ret %d\n",
+ r);
goto mhi_rx_chan_start_fail;
} else {
rmnet_mhi_ptr->rx_enabled = 1;
@@ -855,11 +847,13 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr)
RMNET_MHI_DEV_NAME,
NET_NAME_PREDICTABLE, rmnet_mhi_setup);
if (!rmnet_mhi_ptr->dev) {
- rmnet_log(MSG_CRITICAL, "Network device allocation failed\n");
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Network device allocation failed\n");
ret = -ENOMEM;
goto net_dev_alloc_fail;
}
-
+ SET_NETDEV_DEV(rmnet_mhi_ptr->dev, &rmnet_mhi_ptr->pdev->dev);
rmnet_mhi_ctxt = netdev_priv(rmnet_mhi_ptr->dev);
*rmnet_mhi_ctxt = rmnet_mhi_ptr;
@@ -872,8 +866,10 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr)
r = rmnet_mhi_init_inbound(rmnet_mhi_ptr);
if (r) {
- rmnet_log(MSG_CRITICAL,
- "Failed to init inbound ret %d\n", r);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Failed to init inbound ret %d\n",
+ r);
}
netif_napi_add(rmnet_mhi_ptr->dev, &(rmnet_mhi_ptr->napi),
@@ -882,13 +878,14 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr)
rmnet_mhi_ptr->mhi_enabled = 1;
ret = register_netdev(rmnet_mhi_ptr->dev);
if (ret) {
- rmnet_log(MSG_CRITICAL,
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
"Network device registration failed\n");
goto net_dev_reg_fail;
}
napi_enable(&(rmnet_mhi_ptr->napi));
- rmnet_log(MSG_INFO, "Exited.\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited.\n");
return 0;
@@ -901,7 +898,7 @@ net_dev_alloc_fail:
mhi_rx_chan_start_fail:
mhi_close_channel(rmnet_mhi_ptr->tx_client_handle);
mhi_tx_chan_start_fail:
- rmnet_log(MSG_INFO, "Exited ret %d.\n", ret);
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited ret %d.\n", ret);
return ret;
}
@@ -911,52 +908,60 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info)
struct mhi_result *result;
int r = 0;
- if (NULL != cb_info && NULL != cb_info->result) {
- result = cb_info->result;
- rmnet_mhi_ptr = result->user_data;
- } else {
- rmnet_log(MSG_CRITICAL,
- "Invalid data in MHI callback, quitting\n");
+ if (!cb_info || !cb_info->result) {
+ pr_err("%s: Invalid data in MHI callback\n", __func__);
return;
}
+ result = cb_info->result;
+ rmnet_mhi_ptr = result->user_data;
+
switch (cb_info->cb_reason) {
case MHI_CB_MHI_DISABLED:
- rmnet_log(MSG_CRITICAL,
- "Got MHI_DISABLED notification. Stopping stack\n");
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Got MHI_DISABLED notification. Stopping stack\n");
if (rmnet_mhi_ptr->mhi_enabled) {
rmnet_mhi_ptr->mhi_enabled = 0;
/* Ensure MHI is disabled before other mem ops */
wmb();
while (atomic_read(&rmnet_mhi_ptr->pending_data)) {
- rmnet_log(MSG_CRITICAL,
- "Waiting for channels to stop.\n");
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Waiting for channels to stop.\n");
msleep(25);
}
rmnet_mhi_disable(rmnet_mhi_ptr);
}
break;
case MHI_CB_MHI_ENABLED:
- rmnet_log(MSG_CRITICAL,
- "Got MHI_ENABLED notification. Starting stack\n");
- if (IS_INBOUND(cb_info->chan))
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Got MHI_ENABLED notification. Starting stack\n");
+ if (cb_info->chan == rmnet_mhi_ptr->rx_channel)
rmnet_mhi_ptr->rx_enabled = 1;
else
rmnet_mhi_ptr->tx_enabled = 1;
- if (rmnet_mhi_ptr->tx_enabled &&
- rmnet_mhi_ptr->rx_enabled) {
- rmnet_log(MSG_INFO,
- "Both RX/TX are enabled, enabling iface.\n");
+ if ((rmnet_mhi_ptr->tx_enabled && rmnet_mhi_ptr->rx_enabled) ||
+ (rmnet_mhi_ptr->tx_enabled &&
+ !rmnet_mhi_ptr->rx_client_handle) ||
+ (rmnet_mhi_ptr->rx_enabled &&
+ !rmnet_mhi_ptr->tx_client_handle)) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "enabling iface.\n");
r = rmnet_mhi_enable_iface(rmnet_mhi_ptr);
if (r)
- rmnet_log(MSG_CRITICAL,
- "Failed to enable iface for chan %d\n",
- cb_info->chan);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Failed to enable iface for chan %d\n",
+ cb_info->chan);
else
- rmnet_log(MSG_INFO,
- "Enabled iface for chan %d\n",
- cb_info->chan);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "Enabled iface for chan %d\n",
+ cb_info->chan);
}
break;
case MHI_CB_XFER:
@@ -964,7 +969,7 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info)
/* Flush pending data is set before any other mem operations */
wmb();
if (rmnet_mhi_ptr->mhi_enabled) {
- if (IS_INBOUND(cb_info->chan))
+ if (cb_info->chan == rmnet_mhi_ptr->rx_channel)
rmnet_mhi_rx_cb(cb_info->result);
else
rmnet_mhi_tx_cb(cb_info->result);
@@ -978,64 +983,261 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info)
static struct mhi_client_info_t rmnet_mhi_info = {rmnet_mhi_cb};
-static int __init rmnet_mhi_init(void)
+#ifdef CONFIG_DEBUG_FS
+struct dentry *dentry;
+
+static void rmnet_mhi_create_debugfs(struct rmnet_mhi_private *rmnet_mhi_ptr)
{
+ char node_name[15];
int i;
- int res = 0;
- struct rmnet_mhi_private *rmnet_mhi_ptr = 0;
- rmnet_ipc_log = ipc_log_context_create(RMNET_IPC_LOG_PAGES,
- "mhi_rmnet", 0);
-
- for (i = 0; i < MHI_RMNET_DEVICE_COUNT; i++) {
- rmnet_mhi_ptr = &rmnet_mhi_ctxt_list[i];
-
- rmnet_mhi_ptr->tx_channel = MHI_CLIENT_IP_HW_0_OUT +
- (enum MHI_CLIENT_CHANNEL)(i * 2);
- rmnet_mhi_ptr->rx_channel = MHI_CLIENT_IP_HW_0_IN +
- (enum MHI_CLIENT_CHANNEL)((i * 2));
-
- rmnet_mhi_ptr->tx_client_handle = 0;
- rmnet_mhi_ptr->rx_client_handle = 0;
- rwlock_init(&rmnet_mhi_ptr->out_chan_full_lock);
-
- rmnet_mhi_ptr->mru = MHI_DEFAULT_MRU;
- rmnet_mhi_ptr->dev_index = i;
-
- res = mhi_register_channel(
- &(rmnet_mhi_ptr->tx_client_handle),
- rmnet_mhi_ptr->tx_channel, 0,
- &rmnet_mhi_info, rmnet_mhi_ptr);
-
- if (0 != res) {
- rmnet_mhi_ptr->tx_client_handle = 0;
- rmnet_log(MSG_CRITICAL,
- "mhi_register_channel failed chan %d ret %d\n",
- rmnet_mhi_ptr->tx_channel, res);
+ const umode_t mode = (S_IRUSR | S_IWUSR);
+ struct dentry *file;
+
+ const struct {
+ char *name;
+ u64 *ptr;
+ } debugfs_table[] = {
+ {
+ "tx_interrupts_count",
+ &rmnet_mhi_ptr->debug.tx_interrupts_count
+ },
+ {
+ "rx_interrupts_count",
+ &rmnet_mhi_ptr->debug.rx_interrupts_count
+ },
+ {
+ "tx_ring_full_count",
+ &rmnet_mhi_ptr->debug.tx_ring_full_count
+ },
+ {
+ "tx_queued_packets_count",
+ &rmnet_mhi_ptr->debug.tx_queued_packets_count
+ },
+ {
+ "rx_interrupts_in_masked_irq",
+ &rmnet_mhi_ptr->
+ debug.rx_interrupts_in_masked_irq
+ },
+ {
+ "rx_napi_skb_burst_min",
+ &rmnet_mhi_ptr->debug.rx_napi_skb_burst_min
+ },
+ {
+ "rx_napi_skb_burst_max",
+ &rmnet_mhi_ptr->debug.rx_napi_skb_burst_max
+ },
+ {
+ "tx_cb_skb_free_burst_min",
+ &rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_min
+ },
+ {
+ "tx_cb_skb_free_burst_max",
+ &rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_max
+ },
+ {
+ "rx_napi_budget_overflow",
+ &rmnet_mhi_ptr->debug.rx_napi_budget_overflow
+ },
+ {
+ "rx_fragmentation",
+ &rmnet_mhi_ptr->debug.rx_fragmentation
+ },
+ {
+ NULL, NULL
+ },
+ };
+
+ snprintf(node_name, sizeof(node_name), "%s%d",
+ RMNET_MHI_DRIVER_NAME, rmnet_mhi_ptr->pdev->id);
+
+ if (IS_ERR_OR_NULL(dentry))
+ return;
+
+ rmnet_mhi_ptr->dentry = debugfs_create_dir(node_name, dentry);
+ if (IS_ERR_OR_NULL(rmnet_mhi_ptr->dentry))
+ return;
+
+ file = debugfs_create_u32("msg_lvl",
+ mode,
+ rmnet_mhi_ptr->dentry,
+ (u32 *)&rmnet_mhi_ptr->debug.rmnet_msg_lvl);
+ if (IS_ERR_OR_NULL(file))
+ return;
+
+ file = debugfs_create_u32("ipc_log_lvl",
+ mode,
+ rmnet_mhi_ptr->dentry,
+ (u32 *)&rmnet_mhi_ptr->
+ debug.rmnet_ipc_log_lvl);
+ if (IS_ERR_OR_NULL(file))
+ return;
+
+ file = debugfs_create_u32("mru",
+ mode,
+ rmnet_mhi_ptr->dentry,
+ &rmnet_mhi_ptr->mru);
+ if (IS_ERR_OR_NULL(file))
+ return;
+
+ /* Add debug stats table */
+ for (i = 0; debugfs_table[i].name; i++) {
+ file = debugfs_create_u64(debugfs_table[i].name,
+ mode,
+ rmnet_mhi_ptr->dentry,
+ debugfs_table[i].ptr);
+ if (IS_ERR_OR_NULL(file))
+ return;
+ }
+}
+
+static void rmnet_mhi_create_debugfs_dir(void)
+{
+ dentry = debugfs_create_dir(RMNET_MHI_DRIVER_NAME, 0);
+}
+#else
+static void rmnet_mhi_create_debugfs(struct rmnet_mhi_private *rmnet_mhi_ptr)
+{
+}
+
+static void rmnet_mhi_create_debugfs_dir(void)
+{
+}
+#endif
+
+static int rmnet_mhi_probe(struct platform_device *pdev)
+{
+ int rc;
+ u32 channel;
+ struct rmnet_mhi_private *rmnet_mhi_ptr;
+ char node_name[15];
+
+ if (unlikely(!pdev->dev.of_node))
+ return -ENODEV;
+
+ pdev->id = of_alias_get_id(pdev->dev.of_node, "mhi_rmnet");
+ if (unlikely(pdev->id < 0))
+ return -ENODEV;
+
+ rmnet_mhi_ptr = kzalloc(sizeof(*rmnet_mhi_ptr), GFP_KERNEL);
+ if (unlikely(!rmnet_mhi_ptr))
+ return -ENOMEM;
+ rmnet_mhi_ptr->pdev = pdev;
+ spin_lock_init(&rmnet_mhi_ptr->out_chan_full_lock);
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mhi-mru",
+ &rmnet_mhi_ptr->mru);
+ if (unlikely(rc)) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "failed to get valid mru\n");
+ goto probe_fail;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mhi-tx-channel",
+ &channel);
+ if (rc == 0) {
+ rmnet_mhi_ptr->tx_channel = channel;
+ rc = mhi_register_channel(&rmnet_mhi_ptr->tx_client_handle,
+ rmnet_mhi_ptr->tx_channel,
+ 0,
+ &rmnet_mhi_info,
+ rmnet_mhi_ptr);
+ if (unlikely(rc)) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "mhi_register_channel failed chan %d ret %d\n",
+ rmnet_mhi_ptr->tx_channel,
+ rc);
+ goto probe_fail;
}
- res = mhi_register_channel(
- &(rmnet_mhi_ptr->rx_client_handle),
- rmnet_mhi_ptr->rx_channel, 0,
- &rmnet_mhi_info, rmnet_mhi_ptr);
-
- if (0 != res) {
- rmnet_mhi_ptr->rx_client_handle = 0;
- rmnet_log(MSG_CRITICAL,
- "mhi_register_channel failed chan %d, ret %d\n",
- rmnet_mhi_ptr->rx_channel, res);
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mhi-rx-channel",
+ &channel);
+ if (rc == 0) {
+ rmnet_mhi_ptr->rx_channel = channel;
+ rc = mhi_register_channel(&rmnet_mhi_ptr->rx_client_handle,
+ rmnet_mhi_ptr->rx_channel,
+ 0,
+ &rmnet_mhi_info,
+ rmnet_mhi_ptr);
+ if (unlikely(rc)) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "mhi_register_channel failed chan %d ret %d\n",
+ rmnet_mhi_ptr->rx_channel,
+ rc);
+ goto probe_fail;
}
+
+ 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) {
+ 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);
+ rmnet_mhi_ptr->rmnet_ipc_log =
+ ipc_log_context_create(RMNET_IPC_LOG_PAGES,
+ node_name, 0);
+ rmnet_mhi_ptr->debug.rmnet_msg_lvl = MSG_CRITICAL;
+
+#ifdef CONFIG_MSM_MHI_DEBUG
+ rmnet_mhi_ptr->debug.rmnet_ipc_log_lvl = MSG_VERBOSE;
+#else
+ rmnet_mhi_ptr->debug.rmnet_ipc_log_lvl = MSG_ERROR;
+#endif
+
+ rmnet_mhi_create_debugfs(rmnet_mhi_ptr);
+ list_add_tail(&rmnet_mhi_ptr->node, &rmnet_mhi_ctxt_list);
return 0;
+
+probe_fail:
+ kfree(rmnet_mhi_ptr);
+ return rc;
+}
+
+static const struct of_device_id msm_mhi_match_table[] = {
+ {.compatible = "qcom,mhi-rmnet"},
+ {},
+};
+
+static struct platform_driver rmnet_mhi_driver = {
+ .probe = rmnet_mhi_probe,
+ .driver = {
+ .name = RMNET_MHI_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_mhi_match_table,
+ },
+};
+
+static int __init rmnet_mhi_init(void)
+{
+ rmnet_mhi_create_debugfs_dir();
+
+ return platform_driver_register(&rmnet_mhi_driver);
}
static void __exit rmnet_mhi_exit(void)
{
struct rmnet_mhi_private *rmnet_mhi_ptr = 0;
- int index = 0;
- for (index = 0; index < MHI_RMNET_DEVICE_COUNT; index++) {
- rmnet_mhi_ptr = &rmnet_mhi_ctxt_list[index];
- mhi_deregister_channel(rmnet_mhi_ptr->tx_client_handle);
- mhi_deregister_channel(rmnet_mhi_ptr->rx_client_handle);
+ list_for_each_entry(rmnet_mhi_ptr, &rmnet_mhi_ctxt_list, node) {
+ if (rmnet_mhi_ptr->tx_client_handle)
+ mhi_deregister_channel(rmnet_mhi_ptr->tx_client_handle);
+ if (rmnet_mhi_ptr->rx_client_handle)
+ mhi_deregister_channel(rmnet_mhi_ptr->rx_client_handle);
}
}
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 36fc9427418f..480f3dae0780 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -832,7 +832,7 @@ static struct sh_eth_cpu_data r7s72100_data = {
.ecsr_value = ECSR_ICD,
.ecsipr_value = ECSIPR_ICDIP,
- .eesipr_value = 0xff7f009f,
+ .eesipr_value = 0xe77f009f,
.tx_check = EESR_TC1 | EESR_FTC,
.eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c
index 0e2fc1a844ab..23a038810083 100644
--- a/drivers/net/ethernet/smsc/smc91x.c
+++ b/drivers/net/ethernet/smsc/smc91x.c
@@ -540,7 +540,7 @@ static inline void smc_rcv(struct net_device *dev)
#define smc_special_lock(lock, flags) spin_lock_irqsave(lock, flags)
#define smc_special_unlock(lock, flags) spin_unlock_irqrestore(lock, flags)
#else
-#define smc_special_trylock(lock, flags) (flags == flags)
+#define smc_special_trylock(lock, flags) ((void)flags, true)
#define smc_special_lock(lock, flags) do { flags = 0; } while (0)
#define smc_special_unlock(lock, flags) do { flags = 0; } while (0)
#endif
@@ -2269,6 +2269,13 @@ static int smc_drv_probe(struct platform_device *pdev)
if (pd) {
memcpy(&lp->cfg, pd, sizeof(lp->cfg));
lp->io_shift = SMC91X_IO_SHIFT(lp->cfg.flags);
+
+ if (!SMC_8BIT(lp) && !SMC_16BIT(lp)) {
+ dev_err(&pdev->dev,
+ "at least one of 8-bit or 16-bit access support is required.\n");
+ ret = -ENXIO;
+ goto out_free_netdev;
+ }
}
#if IS_BUILTIN(CONFIG_OF)
diff --git a/drivers/net/ethernet/smsc/smc91x.h b/drivers/net/ethernet/smsc/smc91x.h
index a3c129e1e40a..29df0465daf4 100644
--- a/drivers/net/ethernet/smsc/smc91x.h
+++ b/drivers/net/ethernet/smsc/smc91x.h
@@ -37,6 +37,27 @@
#include <linux/smc91x.h>
/*
+ * Any 16-bit access is performed with two 8-bit accesses if the hardware
+ * can't do it directly. Most registers are 16-bit so those are mandatory.
+ */
+#define SMC_outw_b(x, a, r) \
+ do { \
+ unsigned int __val16 = (x); \
+ unsigned int __reg = (r); \
+ SMC_outb(__val16, a, __reg); \
+ SMC_outb(__val16 >> 8, a, __reg + (1 << SMC_IO_SHIFT)); \
+ } while (0)
+
+#define SMC_inw_b(a, r) \
+ ({ \
+ unsigned int __val16; \
+ unsigned int __reg = r; \
+ __val16 = SMC_inb(a, __reg); \
+ __val16 |= SMC_inb(a, __reg + (1 << SMC_IO_SHIFT)) << 8; \
+ __val16; \
+ })
+
+/*
* Define your architecture specific bus configuration parameters here.
*/
@@ -55,10 +76,30 @@
#define SMC_IO_SHIFT (lp->io_shift)
#define SMC_inb(a, r) readb((a) + (r))
-#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_inw(a, r) \
+ ({ \
+ unsigned int __smc_r = r; \
+ SMC_16BIT(lp) ? readw((a) + __smc_r) : \
+ SMC_8BIT(lp) ? SMC_inw_b(a, __smc_r) : \
+ ({ BUG(); 0; }); \
+ })
+
#define SMC_inl(a, r) readl((a) + (r))
#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_outw(v, a, r) \
+ do { \
+ unsigned int __v = v, __smc_r = r; \
+ if (SMC_16BIT(lp)) \
+ __SMC_outw(__v, a, __smc_r); \
+ else if (SMC_8BIT(lp)) \
+ SMC_outw_b(__v, a, __smc_r); \
+ else \
+ BUG(); \
+ } while (0)
+
#define SMC_outl(v, a, r) writel(v, (a) + (r))
+#define SMC_insb(a, r, p, l) readsb((a) + (r), p, l)
+#define SMC_outsb(a, r, p, l) writesb((a) + (r), p, l)
#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
@@ -66,7 +107,7 @@
#define SMC_IRQ_FLAGS (-1) /* from resource */
/* We actually can't write halfwords properly if not word aligned */
-static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg)
+static inline void __SMC_outw(u16 val, void __iomem *ioaddr, int reg)
{
if ((machine_is_mainstone() || machine_is_stargate2() ||
machine_is_pxa_idp()) && reg & 2) {
@@ -405,24 +446,8 @@ smc_pxa_dma_insw(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma,
#if ! SMC_CAN_USE_16BIT
-/*
- * Any 16-bit access is performed with two 8-bit accesses if the hardware
- * can't do it directly. Most registers are 16-bit so those are mandatory.
- */
-#define SMC_outw(x, ioaddr, reg) \
- do { \
- unsigned int __val16 = (x); \
- SMC_outb( __val16, ioaddr, reg ); \
- SMC_outb( __val16 >> 8, ioaddr, reg + (1 << SMC_IO_SHIFT));\
- } while (0)
-#define SMC_inw(ioaddr, reg) \
- ({ \
- unsigned int __val16; \
- __val16 = SMC_inb( ioaddr, reg ); \
- __val16 |= SMC_inb( ioaddr, reg + (1 << SMC_IO_SHIFT)) << 8; \
- __val16; \
- })
-
+#define SMC_outw(x, ioaddr, reg) SMC_outw_b(x, ioaddr, reg)
+#define SMC_inw(ioaddr, reg) SMC_inw_b(ioaddr, reg)
#define SMC_insw(a, r, p, l) BUG()
#define SMC_outsw(a, r, p, l) BUG()
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 69e31e2a68fc..f0961cbaf87e 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -440,7 +440,7 @@ static struct sk_buff **geneve_gro_receive(struct sk_buff **head,
skb_gro_pull(skb, gh_len);
skb_gro_postpull_rcsum(skb, gh, gh_len);
- pp = ptype->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
out_unlock:
rcu_read_unlock();
@@ -815,7 +815,6 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
struct geneve_dev *geneve = netdev_priv(dev);
struct geneve_sock *gs4 = geneve->sock4;
struct rtable *rt = NULL;
- const struct iphdr *iip; /* interior IP header */
int err = -EINVAL;
struct flowi4 fl4;
__u8 tos, ttl;
@@ -842,8 +841,6 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
skb_reset_mac_header(skb);
- iip = ip_hdr(skb);
-
if (info) {
const struct ip_tunnel_key *key = &info->key;
u8 *opts = NULL;
@@ -859,7 +856,7 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (unlikely(err))
goto err;
- tos = ip_tunnel_ecn_encap(key->tos, iip, skb);
+ tos = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb);
ttl = key->ttl;
df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
} else {
@@ -869,7 +866,7 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (unlikely(err))
goto err;
- tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, iip, skb);
+ tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, ip_hdr(skb), skb);
ttl = geneve->ttl;
if (!ttl && IN_MULTICAST(ntohl(fl4.daddr)))
ttl = 1;
@@ -903,7 +900,6 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
struct geneve_dev *geneve = netdev_priv(dev);
struct geneve_sock *gs6 = geneve->sock6;
struct dst_entry *dst = NULL;
- const struct iphdr *iip; /* interior IP header */
int err = -EINVAL;
struct flowi6 fl6;
__u8 prio, ttl;
@@ -927,8 +923,6 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
skb_reset_mac_header(skb);
- iip = ip_hdr(skb);
-
if (info) {
const struct ip_tunnel_key *key = &info->key;
u8 *opts = NULL;
@@ -945,7 +939,7 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (unlikely(err))
goto err;
- prio = ip_tunnel_ecn_encap(key->tos, iip, skb);
+ prio = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb);
ttl = key->ttl;
} else {
udp_csum = false;
@@ -954,7 +948,7 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (unlikely(err))
goto err;
- prio = ip_tunnel_ecn_encap(fl6.flowi6_tos, iip, skb);
+ prio = ip_tunnel_ecn_encap(fl6.flowi6_tos, ip_hdr(skb), skb);
ttl = geneve->ttl;
if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
ttl = 1;
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 47cd306dbb3c..bba0ca786aaa 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -640,8 +640,10 @@ phy_err:
int phy_start_interrupts(struct phy_device *phydev)
{
atomic_set(&phydev->irq_disable, 0);
- if (request_irq(phydev->irq, phy_interrupt, 0, "phy_interrupt",
- phydev) < 0) {
+ if (request_irq(phydev->irq, phy_interrupt,
+ IRQF_SHARED,
+ "phy_interrupt",
+ phydev) < 0) {
pr_warn("%s: Can't get IRQ %d (PHY)\n",
phydev->bus->name, phydev->irq);
phydev->irq = PHY_POLL;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 935e0b45e151..dd7b7d64c90a 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -862,10 +862,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
goto drop;
- if (skb->sk && sk_fullsock(skb->sk)) {
- sock_tx_timestamp(skb->sk, &skb_shinfo(skb)->tx_flags);
- sw_tx_timestamp(skb);
- }
+ skb_tx_timestamp(skb);
/* Orphan the skb - required as we might hang on to it
* for indefinite time.
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index f94ab786088f..0e2a19e58923 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -1465,6 +1465,11 @@ static void virtnet_free_queues(struct virtnet_info *vi)
netif_napi_del(&vi->rq[i].napi);
}
+ /* We called napi_hash_del() before netif_napi_del(),
+ * we need to respect an RCU grace period before freeing vi->rq
+ */
+ synchronize_net();
+
kfree(vi->rq);
kfree(vi->sq);
}
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 003780901628..6fa8e165878e 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -593,7 +593,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head,
}
}
- pp = eth_gro_receive(head, skb);
+ pp = call_gro_receive(eth_gro_receive, head, skb);
out:
skb_gro_remcsum_cleanup(skb, &grc);
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index f1ead7c28823..b8a3a1ecabaa 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -21,7 +21,7 @@
/*
* Support for Copy Engine hardware, which is mainly used for
- * communication between Host and Target over a PCIe interconnect.
+ * communication between Host and Target over a PCIe/SNOC/AHB interconnect.
*/
/*
@@ -32,7 +32,7 @@
* Each ring consists of a number of descriptors which specify
* an address, length, and meta-data.
*
- * Typically, one side of the PCIe interconnect (Host or Target)
+ * Typically, one side of the PCIe/AHB/SNOC interconnect (Host or Target)
* controls one ring and the other side controls the other ring.
* The source side chooses when to initiate a transfer and it
* chooses what to send (buffer address, length). The destination
@@ -62,215 +62,294 @@ static inline void ath10k_ce_dest_ring_write_index_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- ar->bus_write32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS, n);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->dst_wr_index_addr, n);
}
static inline u32 ath10k_ce_dest_ring_write_index_get(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- return ar->bus_read32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ return ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->dst_wr_index_addr);
}
static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- ar->bus_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->sr_wr_index_addr, n);
}
static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- return ar->bus_read32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ return ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->sr_wr_index_addr);
}
static inline u32 ath10k_ce_src_ring_read_index_get(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- return ar->bus_read32(ar, ce_ctrl_addr + CURRENT_SRRI_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ return ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->current_srri_addr);
}
static inline void ath10k_ce_shadow_src_ring_write_index_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- ar->bus_write32(ar, shadow_sr_wr_ind_addr(ar, ce_ctrl_addr), n);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar, shadow_sr_wr_ind_addr(ar,
+ ce_ctrl_addr), n);
}
static inline void ath10k_ce_shadow_dest_ring_write_index_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- ar->bus_write32(ar, shadow_dst_wr_ind_addr(ar, ce_ctrl_addr), n);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar, shadow_dst_wr_ind_addr(ar,
+ ce_ctrl_addr),
+ n);
}
static inline void ath10k_ce_src_ring_base_addr_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int addr)
{
- ar->bus_write32(ar, ce_ctrl_addr + SR_BA_ADDRESS, addr);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->sr_base_addr, addr);
}
static inline void ath10k_ce_src_ring_size_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- ar->bus_write32(ar, ce_ctrl_addr + SR_SIZE_ADDRESS, n);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->sr_size_addr, n);
}
static inline void ath10k_ce_src_ring_dmax_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- u32 ctrl1_addr = ar->bus_read32((ar),
- (ce_ctrl_addr) + CE_CTRL1_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs;
- ar->bus_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
- (ctrl1_addr & ~CE_CTRL1_DMAX_LENGTH_MASK) |
- CE_CTRL1_DMAX_LENGTH_SET(n));
+ u32 ctrl1_addr = ar_opaque->bus_ops->read32((ar),
+ (ce_ctrl_addr) + ctrl_regs->addr);
+
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + ctrl_regs->addr,
+ (ctrl1_addr & ~(ctrl_regs->dmax->mask)) |
+ ctrl_regs->dmax->set(n, ctrl_regs->dmax));
}
static inline void ath10k_ce_src_ring_byte_swap_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- u32 ctrl1_addr = ar->bus_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs;
+
+ u32 ctrl1_addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr +
+ ctrl_regs->addr);
- ar->bus_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
- (ctrl1_addr & ~CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) |
- CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(n));
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + ctrl_regs->addr,
+ (ctrl1_addr & ~(ctrl_regs->src_ring->mask)) |
+ ctrl_regs->src_ring->set(n, ctrl_regs->src_ring));
}
static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- u32 ctrl1_addr = ar->bus_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs;
- ar->bus_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
- (ctrl1_addr & ~CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK) |
- CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(n));
+ u32 ctrl1_addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr +
+ ctrl_regs->addr);
+
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + ctrl_regs->addr,
+ (ctrl1_addr & ~(ctrl_regs->dst_ring->mask)) |
+ ctrl_regs->dst_ring->set(n, ctrl_regs->dst_ring));
}
static inline u32 ath10k_ce_dest_ring_read_index_get(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- return ar->bus_read32(ar, ce_ctrl_addr + CURRENT_DRRI_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ return ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->current_drri_addr);
}
static inline void ath10k_ce_dest_ring_base_addr_set(struct ath10k *ar,
u32 ce_ctrl_addr,
u32 addr)
{
- ar->bus_write32(ar, ce_ctrl_addr + DR_BA_ADDRESS, addr);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->dr_base_addr, addr);
}
static inline void ath10k_ce_dest_ring_size_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- ar->bus_write32(ar, ce_ctrl_addr + DR_SIZE_ADDRESS, n);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->dr_size_addr, n);
}
static inline void ath10k_ce_src_ring_highmark_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- u32 addr = ar->bus_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr;
+ u32 addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + srcr_wm->addr);
- ar->bus_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS,
- (addr & ~SRC_WATERMARK_HIGH_MASK) |
- SRC_WATERMARK_HIGH_SET(n));
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + srcr_wm->addr,
+ (addr & ~(srcr_wm->wm_high->mask)) |
+ (srcr_wm->wm_high->set(n, srcr_wm->wm_high)));
}
static inline void ath10k_ce_src_ring_lowmark_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- u32 addr = ar->bus_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr;
+ u32 addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + srcr_wm->addr);
- ar->bus_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS,
- (addr & ~SRC_WATERMARK_LOW_MASK) |
- SRC_WATERMARK_LOW_SET(n));
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + srcr_wm->addr,
+ (addr & ~(srcr_wm->wm_low->mask)) |
+ (srcr_wm->wm_low->set(n, srcr_wm->wm_low)));
}
static inline void ath10k_ce_dest_ring_highmark_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- u32 addr = ar->bus_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr;
+ u32 addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + dstr_wm->addr);
- ar->bus_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS,
- (addr & ~DST_WATERMARK_HIGH_MASK) |
- DST_WATERMARK_HIGH_SET(n));
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + dstr_wm->addr,
+ (addr & ~(dstr_wm->wm_high->mask)) |
+ (dstr_wm->wm_high->set(n, dstr_wm->wm_high)));
}
static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- u32 addr = ar->bus_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr;
+ u32 addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + dstr_wm->addr);
- ar->bus_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS,
- (addr & ~DST_WATERMARK_LOW_MASK) |
- DST_WATERMARK_LOW_SET(n));
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + dstr_wm->addr,
+ (addr & ~(dstr_wm->wm_low->mask)) |
+ (dstr_wm->wm_low->set(n, dstr_wm->wm_low)));
}
static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- u32 host_ie_addr = ar->bus_read32(ar,
- ce_ctrl_addr + HOST_IE_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie;
- ar->bus_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
- host_ie_addr | HOST_IE_COPY_COMPLETE_MASK);
+ u32 host_ie_addr = ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr,
+ host_ie_addr | host_ie->copy_complete->mask);
}
static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- u32 host_ie_addr = ar->bus_read32(ar,
- ce_ctrl_addr + HOST_IE_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie;
+
+ u32 host_ie_addr = ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr);
- ar->bus_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
- host_ie_addr & ~HOST_IE_COPY_COMPLETE_MASK);
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr,
+ host_ie_addr & ~(host_ie->copy_complete->mask));
}
static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- u32 host_ie_addr = ar->bus_read32(ar,
- ce_ctrl_addr + HOST_IE_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
- ar->bus_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
- host_ie_addr & ~CE_WATERMARK_MASK);
+ u32 host_ie_addr = ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr,
+ host_ie_addr & ~(wm_regs->wm_mask));
}
static inline void ath10k_ce_error_intr_enable(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- u32 misc_ie_addr = ar->bus_read32(ar,
- ce_ctrl_addr + MISC_IE_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_misc_regs *misc_regs = ar->hw_ce_regs->misc_regs;
+
+ u32 misc_ie_addr = ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr);
- ar->bus_write32(ar, ce_ctrl_addr + MISC_IE_ADDRESS,
- misc_ie_addr | CE_ERROR_MASK);
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr,
+ misc_ie_addr | misc_regs->err_mask);
}
static inline void ath10k_ce_error_intr_disable(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- u32 misc_ie_addr = ar->bus_read32(ar,
- ce_ctrl_addr + MISC_IE_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_misc_regs *misc_regs = ar->hw_ce_regs->misc_regs;
+
+ u32 misc_ie_addr = ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr);
- ar->bus_write32(ar, ce_ctrl_addr + MISC_IE_ADDRESS,
- misc_ie_addr & ~CE_ERROR_MASK);
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr,
+ misc_ie_addr & ~(misc_regs->err_mask));
}
static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int mask)
{
- ar->bus_write32(ar, ce_ctrl_addr + HOST_IS_ADDRESS, mask);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
+
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + wm_regs->addr, mask);
}
u32 shadow_sr_wr_ind_addr(struct ath10k *ar, u32 ctrl_addr)
@@ -339,6 +418,21 @@ u32 shadow_dst_wr_ind_addr(struct ath10k *ar, u32 ctrl_addr)
return addr;
}
+static inline void ath10k_ce_snoc_addr_config(struct ce_desc *sdesc,
+ dma_addr_t buffer,
+ unsigned int flags)
+{
+ __le32 *addr = (__le32 *)&sdesc->addr;
+
+ flags |= upper_32_bits(buffer) & CE_DESC_FLAGS_GET_MASK;
+ addr[0] = __cpu_to_le32(buffer);
+ addr[1] = flags;
+ if (flags & CE_SEND_FLAG_GATHER)
+ addr[1] |= CE_WCN3990_DESC_FLAGS_GATHER;
+ else
+ addr[1] &= ~CE_WCN3990_DESC_FLAGS_GATHER;
+}
+
/*
* Guts of ath10k_ce_send, used by both ath10k_ce_send and
* ath10k_ce_sendlist_send.
@@ -382,17 +476,10 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
if (flags & CE_SEND_FLAG_BYTE_SWAP)
desc_flags |= CE_DESC_FLAGS_BYTE_SWAP;
- if (QCA_REV_WCN3990(ar)) {
- flags |= upper_32_bits(buffer) & CE_DESC_FLAGS_GET_MASK;
- sdesc.addr_lo = __cpu_to_le32(buffer);
- sdesc.addr_hi = flags;
- if (flags & CE_SEND_FLAG_GATHER)
- sdesc.addr_hi |= CE_WCN3990_DESC_FLAGS_GATHER;
- else
- sdesc.addr_hi &= ~CE_WCN3990_DESC_FLAGS_GATHER;
- } else {
+ if (QCA_REV_WCN3990(ar))
+ ath10k_ce_snoc_addr_config(&sdesc, buffer, flags);
+ else
sdesc.addr = __cpu_to_le32(buffer);
- }
sdesc.nbytes = __cpu_to_le16(nbytes);
sdesc.flags = __cpu_to_le16(desc_flags);
@@ -422,10 +509,11 @@ exit:
void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe)
{
struct ath10k *ar = pipe->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
struct ath10k_ce_ring *src_ring = pipe->src_ring;
u32 ctrl_addr = pipe->ctrl_addr;
- lockdep_assert_held(&ar->ce_lock);
+ lockdep_assert_held(&ar_opaque->ce_lock);
/*
* This function must be called only if there is an incomplete
@@ -453,12 +541,13 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
unsigned int flags)
{
struct ath10k *ar = ce_state->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
int ret;
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
ret = ath10k_ce_send_nolock(ce_state, per_transfer_context,
buffer, nbytes, transfer_id, flags);
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
return ret;
}
@@ -466,13 +555,14 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe)
{
struct ath10k *ar = pipe->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
int delta;
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
delta = CE_RING_DELTA(pipe->src_ring->nentries_mask,
pipe->src_ring->write_index,
pipe->src_ring->sw_index - 1);
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
return delta;
}
@@ -480,12 +570,13 @@ int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe)
int __ath10k_ce_rx_num_free_bufs(struct ath10k_ce_pipe *pipe)
{
struct ath10k *ar = pipe->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
struct ath10k_ce_ring *dest_ring = pipe->dest_ring;
unsigned int nentries_mask = dest_ring->nentries_mask;
unsigned int write_index = dest_ring->write_index;
unsigned int sw_index = dest_ring->sw_index;
- lockdep_assert_held(&ar->ce_lock);
+ lockdep_assert_held(&ar_opaque->ce_lock);
return CE_RING_DELTA(nentries_mask, write_index, sw_index - 1);
}
@@ -494,6 +585,7 @@ int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx,
dma_addr_t paddr)
{
struct ath10k *ar = pipe->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
struct ath10k_ce_ring *dest_ring = pipe->dest_ring;
unsigned int nentries_mask = dest_ring->nentries_mask;
unsigned int write_index = dest_ring->write_index;
@@ -502,7 +594,7 @@ int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx,
struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, write_index);
u32 ctrl_addr = pipe->ctrl_addr;
- lockdep_assert_held(&ar->ce_lock);
+ lockdep_assert_held(&ar_opaque->ce_lock);
if ((pipe->id != 5) &&
CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) == 0)
@@ -542,11 +634,12 @@ int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx,
dma_addr_t paddr)
{
struct ath10k *ar = pipe->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
int ret;
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
ret = __ath10k_ce_rx_post_buf(pipe, ctx, paddr);
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
return ret;
}
@@ -609,13 +702,14 @@ int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state,
unsigned int *nbytesp)
{
struct ath10k *ar = ce_state->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
int ret;
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
ret = ath10k_ce_completed_recv_next_nolock(ce_state,
per_transfer_contextp,
nbytesp);
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
return ret;
}
@@ -630,6 +724,7 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
unsigned int write_index;
int ret;
struct ath10k *ar;
+ struct bus_opaque *ar_opaque;
dest_ring = ce_state->dest_ring;
@@ -637,8 +732,9 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
return -EIO;
ar = ce_state->ar;
+ ar_opaque = ath10k_bus_priv(ar);
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
nentries_mask = dest_ring->nentries_mask;
sw_index = dest_ring->sw_index;
@@ -666,7 +762,7 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
ret = -EIO;
}
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
return ret;
}
@@ -734,6 +830,7 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state,
unsigned int write_index;
int ret;
struct ath10k *ar;
+ struct bus_opaque *ar_opaque;
src_ring = ce_state->src_ring;
@@ -741,8 +838,9 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state,
return -EIO;
ar = ce_state->ar;
+ ar_opaque = ath10k_bus_priv(ar);
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
nentries_mask = src_ring->nentries_mask;
sw_index = src_ring->sw_index;
@@ -773,7 +871,7 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state,
ret = -EIO;
}
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
return ret;
}
@@ -782,12 +880,13 @@ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp)
{
struct ath10k *ar = ce_state->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
int ret;
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
ret = ath10k_ce_completed_send_next_nolock(ce_state,
per_transfer_contextp);
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
return ret;
}
@@ -800,17 +899,18 @@ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
*/
void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
{
- struct ath10k_ce_pipe *ce_state =
- ((struct ath10k_ce_pipe *)ar->ce_states + ce_id);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id];
+ struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
u32 ctrl_addr = ce_state->ctrl_addr;
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
/* Clear the copy-complete interrupts that will be handled here. */
ath10k_ce_engine_int_status_clear(ar, ctrl_addr,
- HOST_IS_COPY_COMPLETE_MASK);
+ wm_regs->cc_mask);
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
if (ce_state->recv_cb)
ce_state->recv_cb(ce_state);
@@ -818,15 +918,15 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
if (ce_state->send_cb)
ce_state->send_cb(ce_state);
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
/*
* Misc CE interrupts are not being handled, but still need
* to be cleared.
*/
- ath10k_ce_engine_int_status_clear(ar, ctrl_addr, CE_WATERMARK_MASK);
+ ath10k_ce_engine_int_status_clear(ar, ctrl_addr, wm_regs->wm_mask);
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
}
/*
@@ -840,11 +940,12 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar)
int ce_id;
u32 intr_summary;
struct ath10k_ce_pipe *ce_state;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
if (ar->target_version == ATH10K_HW_WCN3990)
intr_summary = 0xFFF;
else
- intr_summary = CE_INTERRUPT_SUMMARY(ar);
+ intr_summary = CE_INTERRUPT_SUMMARY(ar, ar_opaque);
for (ce_id = 0; intr_summary && (ce_id < CE_COUNT); ce_id++) {
if (intr_summary & (1 << ce_id))
@@ -853,7 +954,7 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar)
/* no intr pending on this CE */
continue;
- ce_state = ((struct ath10k_ce_pipe *)ar->ce_states + ce_id);
+ ce_state = &ar_opaque->ce_states[ce_id];
if (ce_state->send_cb || ce_state->recv_cb)
ath10k_ce_per_engine_service(ar, ce_id);
}
@@ -899,42 +1000,47 @@ int ath10k_ce_disable_interrupts(struct ath10k *ar)
void ath10k_ce_enable_interrupts(struct ath10k *ar)
{
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
int ce_id;
+ struct ath10k_ce_pipe *ce_state;
/* Skip the last copy engine, CE7 the diagnostic window, as that
* uses polling and isn't initialized for interrupts.
*/
- for (ce_id = 0; ce_id < CE_COUNT - 1; ce_id++)
- ath10k_ce_per_engine_handler_adjust(
- ((struct ath10k_ce_pipe *)ar->ce_states + ce_id));
+ for (ce_id = 0; ce_id < CE_COUNT - 1; ce_id++) {
+ ce_state = &ar_opaque->ce_states[ce_id];
+ ath10k_ce_per_engine_handler_adjust(ce_state);
+ }
}
void ath10k_ce_enable_per_ce_interrupts(struct ath10k *ar, unsigned int ce_id)
{
u32 offset;
u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
- offset = HOST_IE_ADDRESS + ctrl_addr;
- ar->bus_write32(ar, offset, 1);
- ar->bus_read32(ar, offset);
+ offset = ar->hw_ce_regs->host_ie_addr + ctrl_addr;
+ ar_opaque->bus_ops->write32(ar, offset, 1);
+ ar_opaque->bus_ops->read32(ar, offset);
}
void ath10k_ce_disable_per_ce_interrupts(struct ath10k *ar, unsigned int ce_id)
{
u32 offset;
u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
- offset = HOST_IE_ADDRESS + ctrl_addr;
- ar->bus_write32(ar, offset, 0);
- ar->bus_read32(ar, offset);
+ offset = ar->hw_ce_regs->host_ie_addr + ctrl_addr;
+ ar_opaque->bus_ops->write32(ar, offset, 0);
+ ar_opaque->bus_ops->read32(ar, offset);
}
static int ath10k_ce_init_src_ring(struct ath10k *ar,
unsigned int ce_id,
const struct ce_attr *attr)
{
- struct ath10k_ce_pipe *ce_state =
- ((struct ath10k_ce_pipe *)ar->ce_states + ce_id);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id];
struct ath10k_ce_ring *src_ring = ce_state->src_ring;
u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id);
@@ -970,8 +1076,8 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
unsigned int ce_id,
const struct ce_attr *attr)
{
- struct ath10k_ce_pipe *ce_state =
- ((struct ath10k_ce_pipe *)ar->ce_states + ce_id);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id];
struct ath10k_ce_ring *dest_ring = ce_state->dest_ring;
u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id);
@@ -1178,8 +1284,8 @@ void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id)
int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
const struct ce_attr *attr)
{
- struct ath10k_ce_pipe *ce_state =
- ((struct ath10k_ce_pipe *)ar->ce_states + ce_id);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id];
int ret;
/*
@@ -1235,8 +1341,8 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id)
{
- struct ath10k_ce_pipe *ce_state =
- ((struct ath10k_ce_pipe *)ar->ce_states + ce_id);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id];
if (ce_state->src_ring) {
kfree(ce_state->src_ring->shadow_base_unaligned);
diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h
index 160a13e681df..936f0698c0f0 100644
--- a/drivers/net/wireless/ath/ath10k/ce.h
+++ b/drivers/net/wireless/ath/ath10k/ce.h
@@ -58,13 +58,7 @@ struct ce_desc {
};
#else
struct ce_desc {
- union {
- __le64 addr;
- struct {
- __le32 addr_lo;
- __le32 addr_hi;
- };
- };
+ __le64 addr;
u16 nbytes; /* length in register map */
u16 flags; /* fw_metadata_high */
u32 toeplitz_hash_result;
@@ -201,6 +195,24 @@ struct ce_attr;
u32 shadow_sr_wr_ind_addr(struct ath10k *ar, u32 ctrl_addr);
u32 shadow_dst_wr_ind_addr(struct ath10k *ar, u32 ctrl_addr);
+struct ath10k_bus_ops {
+ u32 (*read32)(struct ath10k *ar, u32 offset);
+ void (*write32)(struct ath10k *ar, u32 offset, u32 value);
+ int (*get_num_banks)(struct ath10k *ar);
+};
+
+static inline struct bus_opaque *ath10k_bus_priv(struct ath10k *ar)
+{
+ return (struct bus_opaque *)ar->drv_priv;
+}
+
+struct bus_opaque {
+ /* protects CE info */
+ spinlock_t ce_lock;
+ const struct ath10k_bus_ops *bus_ops;
+ struct ath10k_ce_pipe ce_states[CE_COUNT_MAX];
+};
+
/*==================Send====================*/
/* ath10k_ce_send flags */
@@ -344,311 +356,6 @@ struct ce_attr {
void (*recv_cb)(struct ath10k_ce_pipe *);
};
-#ifndef CONFIG_ATH10K_SNOC
-#define SR_BA_ADDRESS 0x0000
-#define SR_SIZE_ADDRESS 0x0004
-#define DR_BA_ADDRESS 0x0008
-#define DR_SIZE_ADDRESS 0x000c
-#define CE_CMD_ADDRESS 0x0018
-
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MSB 17
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB 17
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK 0x00020000
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(x) \
- (((0 | (x)) << CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB) & \
- CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK)
-
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MSB 16
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB 16
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK 0x00010000
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_GET(x) \
- (((x) & CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) >> \
- CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB)
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(x) \
- (((0 | (x)) << CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) & \
- CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK)
-
-#define CE_CTRL1_DMAX_LENGTH_MSB 15
-#define CE_CTRL1_DMAX_LENGTH_LSB 0
-#define CE_CTRL1_DMAX_LENGTH_MASK 0x0000ffff
-#define CE_CTRL1_DMAX_LENGTH_GET(x) \
- (((x) & CE_CTRL1_DMAX_LENGTH_MASK) >> CE_CTRL1_DMAX_LENGTH_LSB)
-#define CE_CTRL1_DMAX_LENGTH_SET(x) \
- (((0 | (x)) << CE_CTRL1_DMAX_LENGTH_LSB) & CE_CTRL1_DMAX_LENGTH_MASK)
-
-#define CE_CTRL1_ADDRESS 0x0010
-#define CE_CTRL1_HW_MASK 0x0007ffff
-#define CE_CTRL1_SW_MASK 0x0007ffff
-#define CE_CTRL1_HW_WRITE_MASK 0x00000000
-#define CE_CTRL1_SW_WRITE_MASK 0x0007ffff
-#define CE_CTRL1_RSTMASK 0xffffffff
-#define CE_CTRL1_RESET 0x00000080
-
-#define CE_CMD_HALT_STATUS_MSB 3
-#define CE_CMD_HALT_STATUS_LSB 3
-#define CE_CMD_HALT_STATUS_MASK 0x00000008
-#define CE_CMD_HALT_STATUS_GET(x) \
- (((x) & CE_CMD_HALT_STATUS_MASK) >> CE_CMD_HALT_STATUS_LSB)
-#define CE_CMD_HALT_STATUS_SET(x) \
- (((0 | (x)) << CE_CMD_HALT_STATUS_LSB) & CE_CMD_HALT_STATUS_MASK)
-#define CE_CMD_HALT_STATUS_RESET 0
-#define CE_CMD_HALT_MSB 0
-#define CE_CMD_HALT_MASK 0x00000001
-
-#define HOST_IE_COPY_COMPLETE_MSB 0
-#define HOST_IE_COPY_COMPLETE_LSB 0
-#define HOST_IE_COPY_COMPLETE_MASK 0x00000001
-#define HOST_IE_COPY_COMPLETE_GET(x) \
- (((x) & HOST_IE_COPY_COMPLETE_MASK) >> HOST_IE_COPY_COMPLETE_LSB)
-#define HOST_IE_COPY_COMPLETE_SET(x) \
- (((0 | (x)) << HOST_IE_COPY_COMPLETE_LSB) & HOST_IE_COPY_COMPLETE_MASK)
-#define HOST_IE_COPY_COMPLETE_RESET 0
-#define HOST_IE_ADDRESS 0x002c
-
-#define HOST_IS_DST_RING_LOW_WATERMARK_MASK 0x00000010
-#define HOST_IS_DST_RING_HIGH_WATERMARK_MASK 0x00000008
-#define HOST_IS_SRC_RING_LOW_WATERMARK_MASK 0x00000004
-#define HOST_IS_SRC_RING_HIGH_WATERMARK_MASK 0x00000002
-#define HOST_IS_COPY_COMPLETE_MASK 0x00000001
-#define HOST_IS_ADDRESS 0x0030
-
-#define MISC_IE_ADDRESS 0x0034
-
-#define MISC_IS_AXI_ERR_MASK 0x00000400
-
-#define MISC_IS_DST_ADDR_ERR_MASK 0x00000200
-#define MISC_IS_SRC_LEN_ERR_MASK 0x00000100
-#define MISC_IS_DST_MAX_LEN_VIO_MASK 0x00000080
-#define MISC_IS_DST_RING_OVERFLOW_MASK 0x00000040
-#define MISC_IS_SRC_RING_OVERFLOW_MASK 0x00000020
-
-#define MISC_IS_ADDRESS 0x0038
-
-#define SR_WR_INDEX_ADDRESS 0x003c
-
-#define DST_WR_INDEX_ADDRESS 0x0040
-
-#define CURRENT_SRRI_ADDRESS 0x0044
-
-#define CURRENT_DRRI_ADDRESS 0x0048
-
-#define SRC_WATERMARK_LOW_MSB 31
-#define SRC_WATERMARK_LOW_LSB 16
-#define SRC_WATERMARK_LOW_MASK 0xffff0000
-#define SRC_WATERMARK_LOW_GET(x) \
- (((x) & SRC_WATERMARK_LOW_MASK) >> SRC_WATERMARK_LOW_LSB)
-#define SRC_WATERMARK_LOW_SET(x) \
- (((0 | (x)) << SRC_WATERMARK_LOW_LSB) & SRC_WATERMARK_LOW_MASK)
-#define SRC_WATERMARK_LOW_RESET 0
-#define SRC_WATERMARK_HIGH_MSB 15
-#define SRC_WATERMARK_HIGH_LSB 0
-#define SRC_WATERMARK_HIGH_MASK 0x0000ffff
-#define SRC_WATERMARK_HIGH_GET(x) \
- (((x) & SRC_WATERMARK_HIGH_MASK) >> SRC_WATERMARK_HIGH_LSB)
-#define SRC_WATERMARK_HIGH_SET(x) \
- (((0 | (x)) << SRC_WATERMARK_HIGH_LSB) & SRC_WATERMARK_HIGH_MASK)
-#define SRC_WATERMARK_HIGH_RESET 0
-#define SRC_WATERMARK_ADDRESS 0x004c
-
-#define DST_WATERMARK_LOW_LSB 16
-#define DST_WATERMARK_LOW_MASK 0xffff0000
-#define DST_WATERMARK_LOW_SET(x) \
- (((0 | (x)) << DST_WATERMARK_LOW_LSB) & DST_WATERMARK_LOW_MASK)
-#define DST_WATERMARK_LOW_RESET 0
-#define DST_WATERMARK_HIGH_MSB 15
-#define DST_WATERMARK_HIGH_LSB 0
-#define DST_WATERMARK_HIGH_MASK 0x0000ffff
-#define DST_WATERMARK_HIGH_GET(x) \
- (((x) & DST_WATERMARK_HIGH_MASK) >> DST_WATERMARK_HIGH_LSB)
-#define DST_WATERMARK_HIGH_SET(x) \
- (((0 | (x)) << DST_WATERMARK_HIGH_LSB) & DST_WATERMARK_HIGH_MASK)
-#define DST_WATERMARK_HIGH_RESET 0
-#define DST_WATERMARK_ADDRESS 0x0050
-
-#else
-#define WCN3990_CE0_SR_BA_LOW (0x00240000)
-#define WCN3990_CE1_SR_BA_LOW (0x00241000)
-#define WCN3990_CE2_SR_BA_LOW (0x00242000)
-#define WCN3990_CE3_SR_BA_LOW (0x00243000)
-#define WCN3990_CE4_SR_BA_LOW (0x00244000)
-#define WCN3990_CE5_SR_BA_LOW (0x00245000)
-#define WCN3990_CE6_SR_BA_LOW (0x00246000)
-#define WCN3990_CE7_SR_BA_LOW (0x00247000)
-#define WCN3990_CE8_SR_BA_LOW (0x00248000)
-#define WCN3990_CE9_SR_BA_LOW (0x00249000)
-#define WCN3990_CE10_SR_BA_LOW (0x0024A000)
-#define WCN3990_CE11_SR_BA_LOW (0x0024B000)
-#define WCN3990_CE0_DR_BA_LOW (0x0024000C)
-#define WNC3990_CE0_DR_SIZE (0x00240014)
-#define WCN3990_CE0_CE_CTRL1 (0x00240018)
-#define WCN3990_CE0_HOST_IE (0x0024002C)
-#define WCN3990_CE0_HOST_IS (0x00240030)
-#define WCN3990_CE0_MISC_IE (0x00240034)
-#define WCN3990_CE0_MISC_IS (0x00240038)
-#define WCN3990_CE0_SRC_WR_INDEX (0x0024003C)
-#define WCN3990_CE0_CURRENT_SRRI (0x00240044)
-#define WCN3990_CE0_CURRENT_DRRI (0x00240048)
-#define WCN3990_CE0_SRC_WATERMARK (0x0024004C)
-#define WCN3990_CE0_DST_WATERMARK (0x00240050)
-#define WCN3990_CE0_SR_SIZE (0x00240008)
-#define HOST_IE_COPY_COMPLETE_MASK (0x00000001)
-#define WCN3990_CE_WRAPPER_HOST_INTERRUPT_SUMMARY 0x0024C000
-#define WCN3990_CE_WRAPPER_INDEX_BASE_LOW 0x0024C004
-#define WCN3990_CE_WRAPPER_INDEX_BASE_HIGH 0x0024C008
-#define CE_CTRL1_IDX_UPD_EN 0x00080000
-
-#define WCN3990_CE_WRAPPER_BASE_ADDRESS \
- WCN3990_CE_WRAPPER_HOST_INTERRUPT_SUMMARY
-#define WCN3990_CE0_BASE_ADDRESS \
- WCN3990_CE0_SR_BA_LOW
-#define WCN3990_CE1_BASE_ADDRESS \
- WCN3990_CE1_SR_BA_LOW
-#define WCN3990_CE2_BASE_ADDRESS \
- WCN3990_CE2_SR_BA_LOW
-#define WCN3990_CE3_BASE_ADDRESS \
- WCN3990_CE3_SR_BA_LOW
-#define WCN3990_CE4_BASE_ADDRESS \
- WCN3990_CE4_SR_BA_LOW
-#define WCN3990_CE5_BASE_ADDRESS \
- WCN3990_CE5_SR_BA_LOW
-#define WCN3990_CE6_BASE_ADDRESS \
- WCN3990_CE6_SR_BA_LOW
-#define WCN3990_CE7_BASE_ADDRESS \
- WCN3990_CE7_SR_BA_LOW
-#define WCN3990_CE8_BASE_ADDRESS \
- WCN3990_CE8_SR_BA_LOW
-#define WCN3990_CE9_BASE_ADDRESS \
- WCN3990_CE9_SR_BA_LOW
-#define WCN3990_CE10_BASE_ADDRESS \
- WCN3990_CE10_SR_BA_LOW
-#define WCN3990_CE11_BASE_ADDRESS \
- WCN3990_CE11_SR_BA_LOW
-
-#define SR_BA_ADDRESS (WCN3990_CE0_SR_BA_LOW\
- - WCN3990_CE0_BASE_ADDRESS)
-#define SR_SIZE_ADDRESS (WCN3990_CE0_SR_SIZE \
- - WCN3990_CE0_BASE_ADDRESS)
-#define DR_BA_ADDRESS (WCN3990_CE0_DR_BA_LOW\
- - WCN3990_CE0_BASE_ADDRESS)
-#define DR_SIZE_ADDRESS (WNC3990_CE0_DR_SIZE\
- - WCN3990_CE0_BASE_ADDRESS)
-#define WCN3990_CE_DDR_ADDRESS_FOR_RRI_LOW \
- (WCN3990_CE_WRAPPER_INDEX_BASE_LOW - WCN3990_CE_WRAPPER_BASE_ADDRESS)
-
-#define WCN3990_CE_DDR_ADDRESS_FOR_RRI_HIGH \
- (WCN3990_CE_WRAPPER_INDEX_BASE_HIGH - WCN3990_CE_WRAPPER_BASE_ADDRESS)
-
-#define CE_RRI_LOW (WCN3990_CE_WRAPPER_BASE_ADDRESS \
- + WCN3990_CE_DDR_ADDRESS_FOR_RRI_LOW)
-
-#define CE_RRI_HIGH (WCN3990_CE_WRAPPER_BASE_ADDRESS \
- + WCN3990_CE_DDR_ADDRESS_FOR_RRI_HIGH)
-
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MSB 18
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB 18
-
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK 0x00040000
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(x) \
- (((0 | (x)) << CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB) & \
- CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK)
-
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MSB 16
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB 16
-
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK 0x00020000
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_GET(x) \
- (((x) & CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) >> \
- CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB)
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(x) \
- (((0 | (x)) << CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) & \
- CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK)
-
-#define CE_CTRL1_DMAX_LENGTH_MSB 0
-#define CE_CTRL1_DMAX_LENGTH_LSB 0
-
-#define CE_CTRL1_DMAX_LENGTH_MASK 0x0000FFFF
-#define CE_CTRL1_DMAX_LENGTH_GET(x) \
- (((x) & CE_CTRL1_DMAX_LENGTH_MASK) >> CE_CTRL1_DMAX_LENGTH_LSB)
-#define CE_CTRL1_DMAX_LENGTH_SET(x) \
- (((0 | (x)) << CE_CTRL1_DMAX_LENGTH_LSB) & CE_CTRL1_DMAX_LENGTH_MASK)
-
-#define CE_CTRL1_ADDRESS (WCN3990_CE0_CE_CTRL1 \
- - WCN3990_CE0_BASE_ADDRESS)
-
-#define HOST_IE_ADDRESS (WCN3990_CE0_HOST_IE\
- - WCN3990_CE0_BASE_ADDRESS)
-
-#define HOST_IS_DST_RING_LOW_WATERMARK_MASK 0x00000010
-#define HOST_IS_DST_RING_HIGH_WATERMARK_MASK 0x00000008
-#define HOST_IS_SRC_RING_LOW_WATERMARK_MASK 0x00000004
-#define HOST_IS_SRC_RING_HIGH_WATERMARK_MASK 0x00000002
-#define HOST_IS_COPY_COMPLETE_MASK 0x00000001
-#define HOST_IS_ADDRESS (WCN3990_CE0_HOST_IS \
- - WCN3990_CE0_BASE_ADDRESS)
-#define MISC_IE_ADDRESS (WCN3990_CE0_MISC_IE \
- - WCN3990_CE0_BASE_ADDRESS)
-
-#define MISC_IS_AXI_ERR_MASK 0x00000100
-#define MISC_IS_DST_ADDR_ERR_MASK 0x00000200
-#define MISC_IS_SRC_LEN_ERR_MASK 0x00000100
-#define MISC_IS_DST_MAX_LEN_VIO_MASK 0x00000080
-#define MISC_IS_DST_RING_OVERFLOW_MASK 0x00000040
-#define MISC_IS_SRC_RING_OVERFLOW_MASK 0x00000020
-#define MISC_IS_ADDRESS (WCN3990_CE0_MISC_IS \
- - WCN3990_CE0_BASE_ADDRESS)
-
-#define SR_WR_INDEX_ADDRESS 0x3C
-#define DST_WR_INDEX_ADDRESS 0x40
-
-#define CURRENT_SRRI_ADDRESS (WCN3990_CE0_CURRENT_SRRI\
- - WCN3990_CE0_BASE_ADDRESS)
-#define CURRENT_DRRI_ADDRESS (WCN3990_CE0_CURRENT_DRRI\
- - WCN3990_CE0_BASE_ADDRESS)
-
-#define SRC_WATERMARK_LOW_MSB 0
-#define SRC_WATERMARK_LOW_LSB 16
-
-#define SRC_WATERMARK_LOW_MASK 0xffff0000
-#define SRC_WATERMARK_LOW_GET(x) \
- (((x) & SRC_WATERMARK_LOW_MASK) >> SRC_WATERMARK_LOW_LSB)
-#define SRC_WATERMARK_LOW_SET(x) \
- (((0 | (x)) << SRC_WATERMARK_LOW_LSB) & SRC_WATERMARK_LOW_MASK)
-
-#define SRC_WATERMARK_LOW_RESET 0
-#define SRC_WATERMARK_HIGH_MSB 15
-#define SRC_WATERMARK_HIGH_LSB 0
-#define SRC_WATERMARK_HIGH_MASK 0x0000ffff
-#define SRC_WATERMARK_HIGH_GET(x) \
- (((x) & SRC_WATERMARK_HIGH_MASK) >> SRC_WATERMARK_HIGH_LSB)
-#define SRC_WATERMARK_HIGH_SET(x) \
- (((0 | (x)) << SRC_WATERMARK_HIGH_LSB) & SRC_WATERMARK_HIGH_MASK)
-
-#define SRC_WATERMARK_HIGH_RESET 0
-#define SRC_WATERMARK_ADDRESS (WCN3990_CE0_SRC_WATERMARK\
- - WCN3990_CE0_BASE_ADDRESS)
-
-#define DST_WATERMARK_LOW_LSB 16
-#define DST_WATERMARK_LOW_MASK 0xffff0000
-#define DST_WATERMARK_LOW_SET(x) \
- (((0 | (x)) << DST_WATERMARK_LOW_LSB) & DST_WATERMARK_LOW_MASK)
-#define DST_WATERMARK_LOW_RESET 0
-#define DST_WATERMARK_HIGH_MSB 15
-#define DST_WATERMARK_HIGH_LSB 0
-#define DST_WATERMARK_HIGH_MASK 0x0000ffff
-#define DST_WATERMARK_HIGH_GET(x) \
- (((x) & DST_WATERMARK_HIGH_MASK) >> DST_WATERMARK_HIGH_LSB)
-#define DST_WATERMARK_HIGH_SET(x) \
- (((0 | (x)) << DST_WATERMARK_HIGH_LSB) & DST_WATERMARK_HIGH_MASK)
-#define DST_WATERMARK_HIGH_RESET 0
-#define DST_WATERMARK_ADDRESS (WCN3990_CE0_DST_WATERMARK \
- - WCN3990_CE0_BASE_ADDRESS)
-
-#define BITS0_TO_31(val) ((uint32_t)((uint64_t)(val)\
- & (uint64_t)(0xFFFFFFFF)))
-#define BITS32_TO_35(val) ((uint32_t)(((uint64_t)(val)\
- & (uint64_t)(0xF00000000)) >> 32))
-#endif
-
#define COPY_ENGINE_ID(COPY_ENGINE_BASE_ADDRESS) ((COPY_ENGINE_BASE_ADDRESS \
- CE0_BASE_ADDRESS) / (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS))
@@ -657,18 +364,6 @@ static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id)
return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id;
}
-#define CE_WATERMARK_MASK (HOST_IS_SRC_RING_LOW_WATERMARK_MASK | \
- HOST_IS_SRC_RING_HIGH_WATERMARK_MASK | \
- HOST_IS_DST_RING_LOW_WATERMARK_MASK | \
- HOST_IS_DST_RING_HIGH_WATERMARK_MASK)
-
-#define CE_ERROR_MASK (MISC_IS_AXI_ERR_MASK | \
- MISC_IS_DST_ADDR_ERR_MASK | \
- MISC_IS_SRC_LEN_ERR_MASK | \
- MISC_IS_DST_MAX_LEN_VIO_MASK | \
- MISC_IS_DST_RING_OVERFLOW_MASK | \
- MISC_IS_SRC_RING_OVERFLOW_MASK)
-
#define CE_SRC_RING_TO_DESC(baddr, idx) \
(&(((struct ce_desc *)baddr)[idx]))
@@ -692,9 +387,9 @@ static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id)
CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB)
#define CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS 0x0000
-#define CE_INTERRUPT_SUMMARY(ar) \
+#define CE_INTERRUPT_SUMMARY(ar, ar_opaque) \
CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET( \
- ar->bus_read32((ar), CE_WRAPPER_BASE_ADDRESS + \
+ ar_opaque->bus_ops->read32((ar), CE_WRAPPER_BASE_ADDRESS + \
CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS))
#endif /* _CE_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 052ebd7dd26b..d37ed66d767b 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -2324,28 +2324,34 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
case ATH10K_HW_QCA988X:
case ATH10K_HW_QCA9887:
ar->regs = &qca988x_regs;
+ ar->hw_ce_regs = &qcax_ce_regs;
ar->hw_values = &qca988x_values;
break;
case ATH10K_HW_QCA6174:
case ATH10K_HW_QCA9377:
ar->regs = &qca6174_regs;
+ ar->hw_ce_regs = &qcax_ce_regs;
ar->hw_values = &qca6174_values;
break;
case ATH10K_HW_QCA99X0:
case ATH10K_HW_QCA9984:
ar->regs = &qca99x0_regs;
+ ar->hw_ce_regs = &qcax_ce_regs;
ar->hw_values = &qca99x0_values;
break;
case ATH10K_HW_QCA9888:
ar->regs = &qca99x0_regs;
+ ar->hw_ce_regs = &qcax_ce_regs;
ar->hw_values = &qca9888_values;
break;
case ATH10K_HW_QCA4019:
ar->regs = &qca4019_regs;
+ ar->hw_ce_regs = &qcax_ce_regs;
ar->hw_values = &qca4019_values;
break;
case ATH10K_HW_WCN3990:
ar->regs = &wcn3990_regs;
+ ar->hw_ce_regs = &wcn3990_ce_regs;
ar->hw_values = &wcn3990_values;
/* WCN3990 chip set is non bmi based */
ar->is_bmi = false;
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index bb2c5fb9a125..21c63d5d3ead 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -740,6 +740,7 @@ struct ath10k {
struct completion target_suspend;
const struct ath10k_hw_regs *regs;
+ const struct ath10k_hw_ce_regs *hw_ce_regs;
const struct ath10k_hw_values *hw_values;
struct ath10k_shadow_reg_value *shadow_reg_value;
struct ath10k_shadow_reg_address *shadow_reg_address;
@@ -923,10 +924,6 @@ struct ath10k {
struct net_device napi_dev;
struct napi_struct napi;
- void (*bus_write32)(void *ar, u32 offset, u32 value);
- u32 (*bus_read32)(void *ar, u32 offset);
- spinlock_t ce_lock; /* lock for CE access */
- void *ce_states;
struct fw_flag *fw_flags;
/* set for bmi chip sets */
bool is_bmi;
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 82a4c67f3672..c36c2481856b 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -1909,7 +1909,7 @@ int ath10k_debug_start(struct ath10k *ar)
ath10k_warn(ar, "failed to disable pktlog: %d\n", ret);
}
- if (ar->debug.nf_cal_period) {
+ if (ar->debug.nf_cal_period && !QCA_REV_WCN3990(ar)) {
ret = ath10k_wmi_pdev_set_param(ar,
ar->wmi.pdev_param->cal_period,
ar->debug.nf_cal_period);
@@ -1926,7 +1926,8 @@ void ath10k_debug_stop(struct ath10k *ar)
{
lockdep_assert_held(&ar->conf_mutex);
- ath10k_debug_cal_data_fetch(ar);
+ if (!QCA_REV_WCN3990(ar))
+ ath10k_debug_cal_data_fetch(ar);
/* Must not use _sync to avoid deadlock, we do that in
* ath10k_debug_destroy(). The check for htt_stats_mask is to avoid
@@ -2419,15 +2420,18 @@ int ath10k_debug_register(struct ath10k *ar)
debugfs_create_file("fw_dbglog", S_IRUSR | S_IWUSR,
ar->debug.debugfs_phy, ar, &fops_fw_dbglog);
- debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy,
- ar, &fops_cal_data);
+ if (!QCA_REV_WCN3990(ar)) {
+ debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy,
+ ar, &fops_cal_data);
+
+ debugfs_create_file("nf_cal_period", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar,
+ &fops_nf_cal_period);
+ }
debugfs_create_file("ani_enable", S_IRUSR | S_IWUSR,
ar->debug.debugfs_phy, ar, &fops_ani_enable);
- debugfs_create_file("nf_cal_period", S_IRUSR | S_IWUSR,
- ar->debug.debugfs_phy, ar, &fops_nf_cal_period);
-
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/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index 861446a41066..65723124985e 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.h
@@ -74,9 +74,9 @@ struct ath10k_hif_ops {
u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
- u32 (*read32)(void *ar, u32 address);
+ u32 (*read32)(struct ath10k *ar, u32 address);
- void (*write32)(void *ar, u32 address, u32 value);
+ void (*write32)(struct ath10k *ar, u32 address, u32 value);
/* Power up the device and enter BMI transfer mode for FW download */
int (*power_up)(struct ath10k *ar);
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index b2678984f2de..6dd396430f19 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -541,6 +541,7 @@ struct htt_rx_indication_hdr {
#define HTT_RX_INDICATION_INFO2_SERVICE_LSB 24
#define HTT_WCN3990_PADDR_MASK 0x1F
+#define HTT_WCN3990_ARCH_PADDR_MASK 0x1FFFFFFFFF
enum htt_rx_legacy_rate {
HTT_RX_OFDM_48 = 0,
@@ -865,8 +866,7 @@ struct htt_rx_offload_ind {
struct htt_rx_in_ord_msdu_desc {
#ifdef CONFIG_ATH10K_SNOC
- __le32 msdu_paddr_lo;
- __le32 msdu_paddr_hi;
+ __le64 msdu_paddr;
#else
__le32 msdu_paddr;
#endif
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index cbb61267eb10..ddf097e3a143 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -429,9 +429,8 @@ static int ath10k_htt_rx_pop_paddr_list(struct ath10k_htt *htt,
while (msdu_count--) {
#ifdef CONFIG_ATH10K_SNOC
- paddr = __le32_to_cpu(msdu_desc->msdu_paddr_lo);
- paddr |= ((u64)(msdu_desc->msdu_paddr_hi &
- HTT_WCN3990_PADDR_MASK) << 32);
+ paddr = __le64_to_cpu(msdu_desc->msdu_paddr);
+ paddr &= HTT_WCN3990_ARCH_PADDR_MASK;
#else
paddr = __le32_to_cpu(msdu_desc->msdu_paddr);
#endif
diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c
index 1a8f3a388ce2..1d37b2c8426b 100644
--- a/drivers/net/wireless/ath/ath10k/hw.c
+++ b/drivers/net/wireless/ath/ath10k/hw.c
@@ -164,6 +164,301 @@ const struct ath10k_hw_regs wcn3990_regs = {
.pcie_intr_fw_mask = 0x00100000,
};
+static unsigned int
+ath10k_set_ring_byte(unsigned int offset,
+ struct ath10k_hw_ce_regs_addr_map *addr_map)
+{
+ return (((0 | (offset)) << addr_map->lsb) & addr_map->mask);
+}
+
+static unsigned int
+ath10k_get_ring_byte(unsigned int offset,
+ struct ath10k_hw_ce_regs_addr_map *addr_map)
+{
+ return (((offset) & addr_map->mask) >> (addr_map->lsb));
+}
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_src_ring = {
+ .msb = 0x00000010,
+ .lsb = 0x00000010,
+ .mask = 0x00020000,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_dst_ring = {
+ .msb = 0x00000012,
+ .lsb = 0x00000012,
+ .mask = 0x00040000,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_dmax = {
+ .msb = 0x00000000,
+ .lsb = 0x00000000,
+ .mask = 0x0000ffff,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_ctrl1 wcn3990_ctrl1 = {
+ .addr = 0x00000018,
+ .src_ring = &wcn3990_src_ring,
+ .dst_ring = &wcn3990_dst_ring,
+ .dmax = &wcn3990_dmax,
+};
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_host_ie_cc = {
+ .mask = 0x00000001,
+};
+
+struct ath10k_hw_ce_host_ie wcn3990_host_ie = {
+ .copy_complete = &wcn3990_host_ie_cc,
+};
+
+struct ath10k_hw_ce_host_wm_regs wcn3990_wm_reg = {
+ .dstr_lmask = 0x00000010,
+ .dstr_hmask = 0x00000008,
+ .srcr_lmask = 0x00000004,
+ .srcr_hmask = 0x00000002,
+ .cc_mask = 0x00000001,
+ .wm_mask = 0x0000001E,
+ .addr = 0x00000030,
+};
+
+struct ath10k_hw_ce_misc_regs wcn3990_misc_reg = {
+ .axi_err = 0x00000100,
+ .dstr_add_err = 0x00000200,
+ .srcr_len_err = 0x00000100,
+ .dstr_mlen_vio = 0x00000080,
+ .dstr_overflow = 0x00000040,
+ .srcr_overflow = 0x00000020,
+ .err_mask = 0x000003E0,
+ .addr = 0x00000038,
+};
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_low = {
+ .msb = 0x00000000,
+ .lsb = 0x00000010,
+ .mask = 0xffff0000,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_high = {
+ .msb = 0x0000000f,
+ .lsb = 0x00000000,
+ .mask = 0x0000ffff,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_src_ring = {
+ .addr = 0x0000004c,
+ .low_rst = 0x00000000,
+ .high_rst = 0x00000000,
+ .wm_low = &wcn3990_src_wm_low,
+ .wm_high = &wcn3990_src_wm_high,
+};
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_low = {
+ .lsb = 0x00000010,
+ .mask = 0xffff0000,
+ .set = &ath10k_set_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_high = {
+ .msb = 0x0000000f,
+ .lsb = 0x00000000,
+ .mask = 0x0000ffff,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = {
+ .addr = 0x00000050,
+ .low_rst = 0x00000000,
+ .high_rst = 0x00000000,
+ .wm_low = &wcn3990_dst_wm_low,
+ .wm_high = &wcn3990_dst_wm_high,
+};
+
+struct ath10k_hw_ce_regs wcn3990_ce_regs = {
+ .sr_base_addr = 0x00000000,
+ .sr_size_addr = 0x00000008,
+ .dr_base_addr = 0x0000000c,
+ .dr_size_addr = 0x00000014,
+ .misc_ie_addr = 0x00000034,
+ .sr_wr_index_addr = 0x0000003c,
+ .dst_wr_index_addr = 0x00000040,
+ .current_srri_addr = 0x00000044,
+ .current_drri_addr = 0x00000048,
+ .ddr_addr_for_rri_low = 0x00000004,
+ .ddr_addr_for_rri_high = 0x00000008,
+ .ce_rri_low = 0x0024C004,
+ .ce_rri_high = 0x0024C008,
+ .host_ie_addr = 0x0000002c,
+ .ctrl1_regs = &wcn3990_ctrl1,
+ .host_ie = &wcn3990_host_ie,
+ .wm_regs = &wcn3990_wm_reg,
+ .misc_regs = &wcn3990_misc_reg,
+ .wm_srcr = &wcn3990_wm_src_ring,
+ .wm_dstr = &wcn3990_wm_dst_ring,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_src_ring = {
+ .msb = 0x00000010,
+ .lsb = 0x00000010,
+ .mask = 0x00010000,
+ .set = &ath10k_set_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_dst_ring = {
+ .msb = 0x00000011,
+ .lsb = 0x00000011,
+ .mask = 0x00020000,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_dmax = {
+ .msb = 0x0000000f,
+ .lsb = 0x00000000,
+ .mask = 0x0000ffff,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_ctrl1 qcax_ctrl1 = {
+ .addr = 0x00000010,
+ .hw_mask = 0x0007ffff,
+ .sw_mask = 0x0007ffff,
+ .hw_wr_mask = 0x00000000,
+ .sw_wr_mask = 0x0007ffff,
+ .reset_mask = 0xffffffff,
+ .reset = 0x00000080,
+ .src_ring = &qcax_src_ring,
+ .dst_ring = &qcax_dst_ring,
+ .dmax = &qcax_dmax,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_cmd_halt_status = {
+ .msb = 0x00000003,
+ .lsb = 0x00000003,
+ .mask = 0x00000008,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_cmd_halt qcax_cmd_halt = {
+ .msb = 0x00000000,
+ .mask = 0x00000001,
+ .status_reset = 0x00000000,
+ .status = &qcax_cmd_halt_status,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_host_ie_cc = {
+ .msb = 0x00000000,
+ .lsb = 0x00000000,
+ .mask = 0x00000001,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_host_ie qcax_host_ie = {
+ .copy_complete_reset = 0x00000000,
+ .copy_complete = &qcax_host_ie_cc,
+};
+
+struct ath10k_hw_ce_host_wm_regs qcax_wm_reg = {
+ .dstr_lmask = 0x00000010,
+ .dstr_hmask = 0x00000008,
+ .srcr_lmask = 0x00000004,
+ .srcr_hmask = 0x00000002,
+ .cc_mask = 0x00000001,
+ .wm_mask = 0x0000001E,
+ .addr = 0x00000030,
+};
+
+struct ath10k_hw_ce_misc_regs qcax_misc_reg = {
+ .axi_err = 0x00000400,
+ .dstr_add_err = 0x00000200,
+ .srcr_len_err = 0x00000100,
+ .dstr_mlen_vio = 0x00000080,
+ .dstr_overflow = 0x00000040,
+ .srcr_overflow = 0x00000020,
+ .err_mask = 0x000007E0,
+ .addr = 0x00000038,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_src_wm_low = {
+ .msb = 0x0000001f,
+ .lsb = 0x00000010,
+ .mask = 0xffff0000,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_src_wm_high = {
+ .msb = 0x0000000f,
+ .lsb = 0x00000000,
+ .mask = 0x0000ffff,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_src_ring = {
+ .addr = 0x0000004c,
+ .low_rst = 0x00000000,
+ .high_rst = 0x00000000,
+ .wm_low = &qcax_src_wm_low,
+ .wm_high = &qcax_src_wm_high,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_low = {
+ .lsb = 0x00000010,
+ .mask = 0xffff0000,
+ .set = &ath10k_set_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_high = {
+ .msb = 0x0000000f,
+ .lsb = 0x00000000,
+ .mask = 0x0000ffff,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_dst_ring = {
+ .addr = 0x00000050,
+ .low_rst = 0x00000000,
+ .high_rst = 0x00000000,
+ .wm_low = &qcax_dst_wm_low,
+ .wm_high = &qcax_dst_wm_high,
+};
+
+struct ath10k_hw_ce_regs qcax_ce_regs = {
+ .sr_base_addr = 0x00000000,
+ .sr_size_addr = 0x00000004,
+ .dr_base_addr = 0x00000008,
+ .dr_size_addr = 0x0000000c,
+ .ce_cmd_addr = 0x00000018,
+ .misc_ie_addr = 0x00000034,
+ .sr_wr_index_addr = 0x0000003c,
+ .dst_wr_index_addr = 0x00000040,
+ .current_srri_addr = 0x00000044,
+ .current_drri_addr = 0x00000048,
+ .host_ie_addr = 0x0000002c,
+ .ctrl1_regs = &qcax_ctrl1,
+ .cmd_halt = &qcax_cmd_halt,
+ .host_ie = &qcax_host_ie,
+ .wm_regs = &qcax_wm_reg,
+ .misc_regs = &qcax_misc_reg,
+ .wm_srcr = &qcax_wm_src_ring,
+ .wm_dstr = &qcax_wm_dst_ring,
+};
+
const struct ath10k_hw_values qca988x_values = {
.rtc_state_val_on = 3,
.ce_count = 8,
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index ce87f8112928..0f2422480c4e 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -268,6 +268,98 @@ extern const struct ath10k_hw_regs qca99x0_regs;
extern const struct ath10k_hw_regs qca4019_regs;
extern const struct ath10k_hw_regs wcn3990_regs;
+struct ath10k_hw_ce_regs_addr_map {
+ u32 msb;
+ u32 lsb;
+ u32 mask;
+ unsigned int (*set)(unsigned int offset,
+ struct ath10k_hw_ce_regs_addr_map *addr_map);
+ unsigned int (*get)(unsigned int offset,
+ struct ath10k_hw_ce_regs_addr_map *addr_map);
+};
+
+struct ath10k_hw_ce_ctrl1 {
+ u32 addr;
+ u32 hw_mask;
+ u32 sw_mask;
+ u32 hw_wr_mask;
+ u32 sw_wr_mask;
+ u32 reset_mask;
+ u32 reset;
+ struct ath10k_hw_ce_regs_addr_map *src_ring;
+ struct ath10k_hw_ce_regs_addr_map *dst_ring;
+ struct ath10k_hw_ce_regs_addr_map *dmax;
+};
+
+struct ath10k_hw_ce_cmd_halt {
+ u32 status_reset;
+ u32 msb;
+ u32 mask;
+ struct ath10k_hw_ce_regs_addr_map *status;
+};
+
+struct ath10k_hw_ce_host_ie {
+ u32 copy_complete_reset;
+ struct ath10k_hw_ce_regs_addr_map *copy_complete;
+};
+
+struct ath10k_hw_ce_host_wm_regs {
+ u32 dstr_lmask;
+ u32 dstr_hmask;
+ u32 srcr_lmask;
+ u32 srcr_hmask;
+ u32 cc_mask;
+ u32 wm_mask;
+ u32 addr;
+};
+
+struct ath10k_hw_ce_misc_regs {
+ u32 axi_err;
+ u32 dstr_add_err;
+ u32 srcr_len_err;
+ u32 dstr_mlen_vio;
+ u32 dstr_overflow;
+ u32 srcr_overflow;
+ u32 err_mask;
+ u32 addr;
+};
+
+struct ath10k_hw_ce_dst_src_wm_regs {
+ u32 addr;
+ u32 low_rst;
+ u32 high_rst;
+ struct ath10k_hw_ce_regs_addr_map *wm_low;
+ struct ath10k_hw_ce_regs_addr_map *wm_high;
+};
+
+struct ath10k_hw_ce_regs {
+ u32 sr_base_addr;
+ u32 sr_size_addr;
+ u32 dr_base_addr;
+ u32 dr_size_addr;
+ u32 ce_cmd_addr;
+ u32 misc_ie_addr;
+ u32 sr_wr_index_addr;
+ u32 dst_wr_index_addr;
+ u32 current_srri_addr;
+ u32 current_drri_addr;
+ u32 ddr_addr_for_rri_low;
+ u32 ddr_addr_for_rri_high;
+ u32 ce_rri_low;
+ u32 ce_rri_high;
+ u32 host_ie_addr;
+ struct ath10k_hw_ce_host_wm_regs *wm_regs;
+ struct ath10k_hw_ce_misc_regs *misc_regs;
+ struct ath10k_hw_ce_ctrl1 *ctrl1_regs;
+ struct ath10k_hw_ce_cmd_halt *cmd_halt;
+ struct ath10k_hw_ce_host_ie *host_ie;
+ struct ath10k_hw_ce_dst_src_wm_regs *wm_srcr;
+ struct ath10k_hw_ce_dst_src_wm_regs *wm_dstr;
+};
+
+extern struct ath10k_hw_ce_regs wcn3990_ce_regs;
+extern struct ath10k_hw_ce_regs qcax_ce_regs;
+
extern struct fw_flag wcn3990_fw_flags;
struct ath10k_hw_values {
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 072e008900e6..9e607b2fa2d4 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -669,18 +669,18 @@ static u32 ath10k_bus_pci_read32(struct ath10k *ar, u32 offset)
return val;
}
-inline void ath10k_pci_write32(void *ar, u32 offset, u32 value)
+inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- ar_pci->bus_ops->write32(ar, offset, value);
+ ar_pci->opaque_ctx.bus_ops->write32(ar, offset, value);
}
-inline u32 ath10k_pci_read32(void *ar, u32 offset)
+inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- return ar_pci->bus_ops->read32(ar, offset);
+ return ar_pci->opaque_ctx.bus_ops->read32(ar, offset);
}
u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr)
@@ -780,9 +780,9 @@ static int __ath10k_pci_rx_post_buf(struct ath10k_pci_pipe *pipe)
ATH10K_SKB_RXCB(skb)->paddr = paddr;
- spin_lock_bh(&ar_pci->ce_lock);
+ spin_lock_bh(&ar_pci->opaque_ctx.ce_lock);
ret = __ath10k_ce_rx_post_buf(ce_pipe, skb, paddr);
- spin_unlock_bh(&ar_pci->ce_lock);
+ spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock);
if (ret) {
dma_unmap_single(ar->dev, paddr, skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
@@ -806,9 +806,9 @@ static void ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe)
if (!ce_pipe->dest_ring)
return;
- spin_lock_bh(&ar_pci->ce_lock);
+ spin_lock_bh(&ar_pci->opaque_ctx.ce_lock);
num = __ath10k_ce_rx_num_free_bufs(ce_pipe);
- spin_unlock_bh(&ar_pci->ce_lock);
+ spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock);
while (num >= 0) {
ret = __ath10k_pci_rx_post_buf(pipe);
@@ -886,7 +886,7 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
void *data_buf = NULL;
int i;
- spin_lock_bh(&ar_pci->ce_lock);
+ spin_lock_bh(&ar_pci->opaque_ctx.ce_lock);
ce_diag = ar_pci->ce_diag;
@@ -987,7 +987,7 @@ done:
dma_free_coherent(ar->dev, alloc_nbytes, data_buf,
ce_data_base);
- spin_unlock_bh(&ar_pci->ce_lock);
+ spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock);
return ret;
}
@@ -1044,7 +1044,7 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
dma_addr_t ce_data_base = 0;
int i;
- spin_lock_bh(&ar_pci->ce_lock);
+ spin_lock_bh(&ar_pci->opaque_ctx.ce_lock);
ce_diag = ar_pci->ce_diag;
@@ -1148,7 +1148,7 @@ done:
ath10k_warn(ar, "failed to write diag value at 0x%x: %d\n",
address, ret);
- spin_unlock_bh(&ar_pci->ce_lock);
+ spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock);
return ret;
}
@@ -1351,7 +1351,7 @@ int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
unsigned int write_index;
int err, i = 0;
- spin_lock_bh(&ar_pci->ce_lock);
+ spin_lock_bh(&ar_pci->opaque_ctx.ce_lock);
nentries_mask = src_ring->nentries_mask;
sw_index = src_ring->sw_index;
@@ -1397,14 +1397,14 @@ int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
if (err)
goto err;
- spin_unlock_bh(&ar_pci->ce_lock);
+ spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock);
return 0;
err:
for (; i > 0; i--)
__ath10k_ce_send_revert(ce_pipe);
- spin_unlock_bh(&ar_pci->ce_lock);
+ spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock);
return err;
}
@@ -1990,7 +1990,7 @@ static int ath10k_bus_get_num_banks(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- return ar_pci->bus_ops->get_num_banks(ar);
+ return ar_pci->opaque_ctx.bus_ops->get_num_banks(ar);
}
int ath10k_pci_init_config(struct ath10k *ar)
@@ -2165,7 +2165,7 @@ int ath10k_pci_alloc_pipes(struct ath10k *ar)
for (i = 0; i < CE_COUNT; i++) {
pipe = &ar_pci->pipe_info[i];
- pipe->ce_hdl = &ar_pci->ce_states[i];
+ pipe->ce_hdl = &ar_pci->opaque_ctx.ce_states[i];
pipe->pipe_num = i;
pipe->hif_ce_state = ar;
@@ -2792,6 +2792,7 @@ static int ath10k_pci_napi_poll(struct napi_struct *ctx, int budget)
{
struct ath10k *ar = container_of(ctx, struct ath10k, napi);
int done = 0;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
if (ath10k_pci_has_fw_crashed(ar)) {
ath10k_pci_fw_crashed_clear(ar);
@@ -2814,7 +2815,7 @@ static int ath10k_pci_napi_poll(struct napi_struct *ctx, int budget)
* interrupts safer to check for pending interrupts for
* immediate servicing.
*/
- if (CE_INTERRUPT_SUMMARY(ar)) {
+ if (CE_INTERRUPT_SUMMARY(ar, ar_opaque)) {
napi_reschedule(ctx);
goto out;
}
@@ -3132,7 +3133,7 @@ int ath10k_pci_setup_resource(struct ath10k *ar)
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
int ret;
- spin_lock_init(&ar_pci->ce_lock);
+ spin_lock_init(&ar_pci->opaque_ctx.ce_lock);
spin_lock_init(&ar_pci->ps_lock);
setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry,
@@ -3243,7 +3244,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
ar_pci->ar = ar;
ar->dev_id = pci_dev->device;
ar_pci->pci_ps = pci_ps;
- ar_pci->bus_ops = &ath10k_pci_bus_ops;
+ ar_pci->opaque_ctx.bus_ops = &ath10k_pci_bus_ops;
ar_pci->pci_soft_reset = pci_soft_reset;
ar_pci->pci_hard_reset = pci_hard_reset;
@@ -3252,14 +3253,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
ar->id.subsystem_vendor = pdev->subsystem_vendor;
ar->id.subsystem_device = pdev->subsystem_device;
- spin_lock_init(&ar_pci->ce_lock);
spin_lock_init(&ar_pci->ps_lock);
-
- ar->bus_write32 = ath10k_pci_write32;
- ar->bus_read32 = ath10k_pci_read32;
- ar->ce_lock = ar_pci->ce_lock;
- ar->ce_states = ar_pci->ce_states;
-
setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry,
(unsigned long)ar);
setup_timer(&ar_pci->ps_timer, ath10k_pci_ps_timer,
diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h
index 06d0bd3993d3..22730c700af3 100644
--- a/drivers/net/wireless/ath/ath10k/pci.h
+++ b/drivers/net/wireless/ath/ath10k/pci.h
@@ -155,12 +155,6 @@ struct ath10k_pci_supp_chip {
u32 rev_id;
};
-struct ath10k_bus_ops {
- u32 (*read32)(struct ath10k *ar, u32 offset);
- void (*write32)(struct ath10k *ar, u32 offset, u32 value);
- int (*get_num_banks)(struct ath10k *ar);
-};
-
enum ath10k_pci_irq_mode {
ATH10K_PCI_IRQ_AUTO = 0,
ATH10K_PCI_IRQ_LEGACY = 1,
@@ -168,6 +162,7 @@ enum ath10k_pci_irq_mode {
};
struct ath10k_pci {
+ struct bus_opaque opaque_ctx;
struct pci_dev *pdev;
struct device *dev;
struct ath10k *ar;
@@ -182,11 +177,6 @@ struct ath10k_pci {
/* Copy Engine used for Diagnostic Accesses */
struct ath10k_ce_pipe *ce_diag;
- /* FIXME: document what this really protects */
- spinlock_t ce_lock;
-
- /* Map CE id to ce_state */
- struct ath10k_ce_pipe ce_states[CE_COUNT_MAX];
struct timer_list rx_post_retry;
/* Due to HW quirks it is recommended to disable ASPM during device
@@ -230,8 +220,6 @@ struct ath10k_pci {
*/
bool pci_ps;
- const struct ath10k_bus_ops *bus_ops;
-
/* Chip specific pci reset routine used to do a safe reset */
int (*pci_soft_reset)(struct ath10k *ar);
@@ -263,11 +251,11 @@ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
/* Wait up to this many Ms for a Diagnostic Access CE operation to complete */
#define DIAG_ACCESS_CE_TIMEOUT_MS 10
-void ath10k_pci_write32(void *ar, u32 offset, u32 value);
+void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value);
void ath10k_pci_soc_write32(struct ath10k *ar, u32 addr, u32 val);
void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val);
-u32 ath10k_pci_read32(void *ar, u32 offset);
+u32 ath10k_pci_read32(struct ath10k *ar, u32 offset);
u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr);
u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr);
diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
index 6c8797d5e5fc..081e44b3277a 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.c
+++ b/drivers/net/wireless/ath/ath10k/snoc.c
@@ -413,9 +413,9 @@ static struct ath10k_shadow_reg_cfg target_shadow_reg_cfg_map[] = {
{ 11, WCN3990_DST_WR_INDEX_OFFSET},
};
-void ath10k_snoc_write32(void *ar, u32 offset, u32 value)
+void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value)
{
- struct ath10k_snoc *ar_snoc = ath10k_snoc_priv((struct ath10k *)ar);
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
if (!ar_snoc)
return;
@@ -423,9 +423,9 @@ void ath10k_snoc_write32(void *ar, u32 offset, u32 value)
iowrite32(value, ar_snoc->mem + offset);
}
-u32 ath10k_snoc_read32(void *ar, u32 offset)
+u32 ath10k_snoc_read32(struct ath10k *ar, u32 offset)
{
- struct ath10k_snoc *ar_snoc = ath10k_snoc_priv((struct ath10k *)ar);
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
u32 val;
if (!ar_snoc)
@@ -462,9 +462,9 @@ static int __ath10k_snoc_rx_post_buf(struct ath10k_snoc_pipe *pipe)
ATH10K_SKB_RXCB(skb)->paddr = paddr;
- spin_lock_bh(&ar_snoc->ce_lock);
+ spin_lock_bh(&ar_snoc->opaque_ctx.ce_lock);
ret = __ath10k_ce_rx_post_buf(ce_pipe, skb, paddr);
- spin_unlock_bh(&ar_snoc->ce_lock);
+ spin_unlock_bh(&ar_snoc->opaque_ctx.ce_lock);
if (ret) {
dma_unmap_single(ar->dev, paddr, skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
@@ -488,9 +488,9 @@ static void ath10k_snoc_rx_post_pipe(struct ath10k_snoc_pipe *pipe)
if (!ce_pipe->dest_ring)
return;
- spin_lock_bh(&ar_snoc->ce_lock);
+ spin_lock_bh(&ar_snoc->opaque_ctx.ce_lock);
num = __ath10k_ce_rx_num_free_bufs(ce_pipe);
- spin_unlock_bh(&ar_snoc->ce_lock);
+ spin_unlock_bh(&ar_snoc->opaque_ctx.ce_lock);
while (num--) {
ret = __ath10k_snoc_rx_post_buf(pipe);
if (ret) {
@@ -638,7 +638,7 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
snoc_pipe = &ar_snoc->pipe_info[pipe_id];
ce_pipe = snoc_pipe->ce_hdl;
src_ring = ce_pipe->src_ring;
- spin_lock_bh(&ar_snoc->ce_lock);
+ spin_lock_bh(&ar_snoc->opaque_ctx.ce_lock);
nentries_mask = src_ring->nentries_mask;
sw_index = src_ring->sw_index;
@@ -678,14 +678,14 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
if (err)
goto err;
- spin_unlock_bh(&ar_snoc->ce_lock);
+ spin_unlock_bh(&ar_snoc->opaque_ctx.ce_lock);
return 0;
err:
for (; i > 0; i--)
__ath10k_ce_send_revert(ce_pipe);
- spin_unlock_bh(&ar_snoc->ce_lock);
+ spin_unlock_bh(&ar_snoc->opaque_ctx.ce_lock);
return err;
}
@@ -882,7 +882,7 @@ static int ath10k_snoc_alloc_pipes(struct ath10k *ar)
for (i = 0; i < CE_COUNT; i++) {
pipe = &ar_snoc->pipe_info[i];
- pipe->ce_hdl = &ar_snoc->ce_states[i];
+ pipe->ce_hdl = &ar_snoc->opaque_ctx.ce_states[i];
pipe->pipe_num = i;
pipe->hif_ce_state = ar;
@@ -1184,6 +1184,11 @@ static const struct ath10k_hif_ops ath10k_snoc_hif_ops = {
.write32 = ath10k_snoc_write32,
};
+static const struct ath10k_bus_ops ath10k_snoc_bus_ops = {
+ .read32 = ath10k_snoc_read32,
+ .write32 = ath10k_snoc_write32,
+};
+
static int ath10k_snoc_probe(struct platform_device *pdev)
{
int ret;
@@ -1210,11 +1215,8 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ar);
ar_snoc->ar = ar;
- spin_lock_init(&ar_snoc->ce_lock);
- ar->bus_write32 = ath10k_snoc_write32;
- ar->bus_read32 = ath10k_snoc_read32;
- ar->ce_lock = ar_snoc->ce_lock;
- ar->ce_states = ar_snoc->ce_states;
+ spin_lock_init(&ar_snoc->opaque_ctx.ce_lock);
+ ar_snoc->opaque_ctx.bus_ops = &ath10k_snoc_bus_ops;
ath10k_snoc_resource_init(ar);
ar->target_version = ATH10K_HW_WCN3990;
diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h
index 1754a3e91a00..0a5f5bff37ec 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.h
+++ b/drivers/net/wireless/ath/ath10k/snoc.h
@@ -103,6 +103,7 @@ struct ath10k_target_info {
* @is_driver_probed: flag to indicate driver state
*/
struct ath10k_snoc {
+ struct bus_opaque opaque_ctx;
struct platform_device *dev;
struct ath10k *ar;
void __iomem *mem;
@@ -110,9 +111,6 @@ struct ath10k_snoc {
struct ath10k_target_info target_info;
size_t mem_len;
struct ath10k_snoc_pipe pipe_info[CE_COUNT_MAX];
- /* protects CE info */
- spinlock_t ce_lock;
- struct ath10k_ce_pipe ce_states[CE_COUNT_MAX];
struct timer_list rx_post_retry;
u32 ce_irqs[CE_COUNT_MAX];
u32 *vaddr_rri_on_ddr;
@@ -191,10 +189,10 @@ static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar)
return (struct ath10k_snoc *)ar->drv_priv;
}
-void ath10k_snoc_write32(void *ar, u32 offset, u32 value);
+void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value);
void ath10k_snoc_soc_write32(struct ath10k *ar, u32 addr, u32 val);
void ath10k_snoc_reg_write32(struct ath10k *ar, u32 addr, u32 val);
-u32 ath10k_snoc_read32(void *ar, u32 offset);
+u32 ath10k_snoc_read32(struct ath10k *ar, u32 offset);
u32 ath10k_snoc_soc_read32(struct ath10k *ar, u32 addr);
u32 ath10k_snoc_reg_read32(struct ath10k *ar, u32 addr);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index 8b4561e8ce1a..ef493271c712 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -4176,7 +4176,7 @@ static void ath9k_hw_ar9300_set_board_values(struct ath_hw *ah,
if (!AR_SREV_9330(ah) && !AR_SREV_9340(ah) && !AR_SREV_9531(ah))
ar9003_hw_internal_regulator_apply(ah);
ar9003_hw_apply_tuning_caps(ah);
- ar9003_hw_apply_minccapwr_thresh(ah, chan);
+ ar9003_hw_apply_minccapwr_thresh(ah, is2ghz);
ar9003_hw_txend_to_xpa_off_apply(ah, is2ghz);
ar9003_hw_thermometer_apply(ah);
ar9003_hw_thermo_cal_apply(ah);
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 1bdeacf7b257..bc70ce62bc03 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -869,8 +869,8 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->wiphy->interface_modes |=
BIT(NL80211_IFTYPE_P2P_DEVICE);
- hw->wiphy->iface_combinations = if_comb;
- hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+ hw->wiphy->iface_combinations = if_comb;
+ hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
}
hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 5cc0ddc254eb..7be31f27266c 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1550,13 +1550,13 @@ static int ath9k_sta_state(struct ieee80211_hw *hw,
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
int ret = 0;
- if (old_state == IEEE80211_STA_AUTH &&
- new_state == IEEE80211_STA_ASSOC) {
+ if (old_state == IEEE80211_STA_NOTEXIST &&
+ new_state == IEEE80211_STA_NONE) {
ret = ath9k_sta_add(hw, vif, sta);
ath_dbg(common, CONFIG,
"Add station: %pM\n", sta->addr);
- } else if (old_state == IEEE80211_STA_ASSOC &&
- new_state == IEEE80211_STA_AUTH) {
+ } else if (old_state == IEEE80211_STA_NONE &&
+ new_state == IEEE80211_STA_NOTEXIST) {
ret = ath9k_sta_remove(hw, vif, sta);
ath_dbg(common, CONFIG,
"Remove station: %pM\n", sta->addr);
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 75ae474367f9..fe6d5ab7f95b 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -705,9 +705,6 @@ static int wil_target_reset(struct wil6210_priv *wil)
wil_s(wil, RGF_DMA_OFUL_NID_0, BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN |
BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC);
- /* Enable fix for PCIe HW bug, set "No snoop" for RX transactions */
- wil_s(wil, RGF_DMA_PEDI_DIF, BIT_DMA_WR_CMD_ATTR_NO_SNOOP);
-
wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY);
return 0;
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
index 410a6645d316..59cef6c69fe8 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
@@ -726,8 +726,10 @@ int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev,
return -ENOMEM;
err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr,
glom_skb);
- if (err)
+ if (err) {
+ brcmu_pkt_buf_free_skb(glom_skb);
goto done;
+ }
skb_queue_walk(pktq, skb) {
memcpy(skb->data, glom_skb->data, skb->len);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
index deb5f78dcacc..70a6985334d5 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
@@ -2408,7 +2408,7 @@ static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
WL_BSS_INFO_MAX);
if (err) {
brcmf_err("Failed to get bss info (%d)\n", err);
- return;
+ goto out_kfree;
}
si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
@@ -2420,6 +2420,9 @@ static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
+
+out_kfree:
+ kfree(buf);
}
static s32
@@ -4099,7 +4102,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
(u8 *)&settings->beacon.head[ie_offset],
settings->beacon.head_len - ie_offset,
WLAN_EID_SSID);
- if (!ssid_ie)
+ if (!ssid_ie || ssid_ie->len > IEEE80211_MAX_SSID_LEN)
return -EINVAL;
memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/brcm80211/brcmsmac/dma.c
index 796f5f9d5d5a..b7df576bb84d 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/dma.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.c
@@ -1079,8 +1079,10 @@ bool dma_rxfill(struct dma_pub *pub)
pa = dma_map_single(di->dmadev, p->data, di->rxbufsize,
DMA_FROM_DEVICE);
- if (dma_mapping_error(di->dmadev, pa))
+ if (dma_mapping_error(di->dmadev, pa)) {
+ brcmu_pkt_buf_free_skb(p);
return false;
+ }
/* save the free packet pointer */
di->rxp[rxout] = p;
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/stf.c b/drivers/net/wireless/brcm80211/brcmsmac/stf.c
index dd9162722495..0ab865de1491 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/stf.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/stf.c
@@ -87,7 +87,7 @@ void
brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc, u16 *ss_algo_channel,
u16 chanspec)
{
- struct tx_power power;
+ struct tx_power power = { };
u8 siso_mcs_id, cdd_mcs_id, stbc_mcs_id;
/* Clear previous settings */
diff --git a/drivers/net/wireless/iwlegacy/3945.c b/drivers/net/wireless/iwlegacy/3945.c
index 93bdf684babe..ae047ab7a4df 100644
--- a/drivers/net/wireless/iwlegacy/3945.c
+++ b/drivers/net/wireless/iwlegacy/3945.c
@@ -1019,12 +1019,13 @@ il3945_hw_txq_ctx_free(struct il_priv *il)
int txq_id;
/* Tx queues */
- if (il->txq)
+ if (il->txq) {
for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
if (txq_id == IL39_CMD_QUEUE_NUM)
il_cmd_queue_free(il);
else
il_tx_queue_free(il, txq_id);
+ }
/* free tx queue structure */
il_free_txq_mem(il);
diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/iwlwifi/dvm/calib.c
index 20e6aa910700..c148085742a0 100644
--- a/drivers/net/wireless/iwlwifi/dvm/calib.c
+++ b/drivers/net/wireless/iwlwifi/dvm/calib.c
@@ -901,7 +901,7 @@ static void iwlagn_gain_computation(struct iwl_priv *priv,
/* bound gain by 2 bits value max, 3rd bit is sign */
data->delta_gain_code[i] =
min(abs(delta_g),
- (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
+ (s32) CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
if (delta_g < 0)
/*
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
index 610c442c7ab2..9584f950fd2f 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -935,7 +935,8 @@ int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id)
}
mvm->fw_dbg_conf = conf_id;
- return ret;
+
+ return 0;
}
static int iwl_mvm_config_ltr(struct iwl_mvm *mvm)
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index f96ab2f4b90e..ce12717e656a 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -3992,8 +3992,8 @@ static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
if (idx != 0)
return -ENOENT;
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
+ if (!fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
return -ENOENT;
mutex_lock(&mvm->mutex);
@@ -4039,8 +4039,8 @@ static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
+ if (!fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
return;
/* if beacon filtering isn't on mac80211 does it anyway */
diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/iwlwifi/mvm/sf.c
index b0f59fdd287c..d7d72adb6343 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sf.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sf.c
@@ -215,7 +215,7 @@ static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id,
enum iwl_sf_state new_state)
{
struct iwl_sf_cfg_cmd sf_cmd = {
- .state = cpu_to_le32(SF_FULL_ON),
+ .state = cpu_to_le32(new_state),
};
struct ieee80211_sta *sta;
int ret = 0;
diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c
index d58c094f2f04..f7e6a09926dd 100644
--- a/drivers/net/wireless/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/iwlwifi/pcie/drv.c
@@ -475,48 +475,64 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
#ifdef CONFIG_ACPI
-#define SPL_METHOD "SPLC"
-#define SPL_DOMAINTYPE_MODULE BIT(0)
-#define SPL_DOMAINTYPE_WIFI BIT(1)
-#define SPL_DOMAINTYPE_WIGIG BIT(2)
-#define SPL_DOMAINTYPE_RFEM BIT(3)
+#define ACPI_SPLC_METHOD "SPLC"
+#define ACPI_SPLC_DOMAIN_WIFI (0x07)
-static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx)
+static u64 splc_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splc)
{
- union acpi_object *limits, *domain_type, *power_limit;
-
- if (splx->type != ACPI_TYPE_PACKAGE ||
- splx->package.count != 2 ||
- splx->package.elements[0].type != ACPI_TYPE_INTEGER ||
- splx->package.elements[0].integer.value != 0) {
- IWL_ERR(trans, "Unsupported splx structure\n");
+ union acpi_object *data_pkg, *dflt_pwr_limit;
+ int i;
+
+ /* We need at least two elements, one for the revision and one
+ * for the data itself. Also check that the revision is
+ * supported (currently only revision 0).
+ */
+ if (splc->type != ACPI_TYPE_PACKAGE ||
+ splc->package.count < 2 ||
+ splc->package.elements[0].type != ACPI_TYPE_INTEGER ||
+ splc->package.elements[0].integer.value != 0) {
+ IWL_DEBUG_INFO(trans,
+ "Unsupported structure returned by the SPLC method. Ignoring.\n");
return 0;
}
- limits = &splx->package.elements[1];
- if (limits->type != ACPI_TYPE_PACKAGE ||
- limits->package.count < 2 ||
- limits->package.elements[0].type != ACPI_TYPE_INTEGER ||
- limits->package.elements[1].type != ACPI_TYPE_INTEGER) {
- IWL_ERR(trans, "Invalid limits element\n");
- return 0;
+ /* loop through all the packages to find the one for WiFi */
+ for (i = 1; i < splc->package.count; i++) {
+ union acpi_object *domain;
+
+ data_pkg = &splc->package.elements[i];
+
+ /* Skip anything that is not a package with the right
+ * amount of elements (i.e. at least 2 integers).
+ */
+ if (data_pkg->type != ACPI_TYPE_PACKAGE ||
+ data_pkg->package.count < 2 ||
+ data_pkg->package.elements[0].type != ACPI_TYPE_INTEGER ||
+ data_pkg->package.elements[1].type != ACPI_TYPE_INTEGER)
+ continue;
+
+ domain = &data_pkg->package.elements[0];
+ if (domain->integer.value == ACPI_SPLC_DOMAIN_WIFI)
+ break;
+
+ data_pkg = NULL;
}
- domain_type = &limits->package.elements[0];
- power_limit = &limits->package.elements[1];
- if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) {
- IWL_DEBUG_INFO(trans, "WiFi power is not limited\n");
+ if (!data_pkg) {
+ IWL_DEBUG_INFO(trans,
+ "No element for the WiFi domain returned by the SPLC method.\n");
return 0;
}
- return power_limit->integer.value;
+ dflt_pwr_limit = &data_pkg->package.elements[1];
+ return dflt_pwr_limit->integer.value;
}
static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev)
{
acpi_handle pxsx_handle;
acpi_handle handle;
- struct acpi_buffer splx = {ACPI_ALLOCATE_BUFFER, NULL};
+ struct acpi_buffer splc = {ACPI_ALLOCATE_BUFFER, NULL};
acpi_status status;
pxsx_handle = ACPI_HANDLE(&pdev->dev);
@@ -527,23 +543,24 @@ static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev)
}
/* Get the method's handle */
- status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle);
+ status = acpi_get_handle(pxsx_handle, (acpi_string)ACPI_SPLC_METHOD,
+ &handle);
if (ACPI_FAILURE(status)) {
- IWL_DEBUG_INFO(trans, "SPL method not found\n");
+ IWL_DEBUG_INFO(trans, "SPLC method not found\n");
return;
}
/* Call SPLC with no arguments */
- status = acpi_evaluate_object(handle, NULL, NULL, &splx);
+ status = acpi_evaluate_object(handle, NULL, NULL, &splc);
if (ACPI_FAILURE(status)) {
IWL_ERR(trans, "SPLC invocation failed (0x%x)\n", status);
return;
}
- trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer);
+ trans->dflt_pwr_limit = splc_get_pwr_limit(trans, splc.pointer);
IWL_DEBUG_INFO(trans, "Default power limit set to %lld\n",
trans->dflt_pwr_limit);
- kfree(splx.pointer);
+ kfree(splc.pointer);
}
#else /* CONFIG_ACPI */
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index a8c8a4a7420b..8dfe6b2bc703 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -1508,9 +1508,9 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
/* start the TFD with the scratchbuf */
scratch_size = min_t(int, copy_size, IWL_HCMD_SCRATCHBUF_SIZE);
- memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size);
+ memcpy(&txq->scratchbufs[idx], &out_cmd->hdr, scratch_size);
iwl_pcie_txq_build_tfd(trans, txq,
- iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr),
+ iwl_pcie_get_scratchbuf_dma(txq, idx),
scratch_size, true);
/* map first command fragment, if any remains */
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index 4073116e6e9f..c3331d6201c3 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -2144,8 +2144,9 @@ done:
is_scanning_required = 1;
} else {
mwifiex_dbg(priv->adapter, MSG,
- "info: trying to associate to '%s' bssid %pM\n",
- (char *)req_ssid.ssid, bss->bssid);
+ "info: trying to associate to '%.*s' bssid %pM\n",
+ req_ssid.ssid_len, (char *)req_ssid.ssid,
+ bss->bssid);
memcpy(&priv->cfg_bssid, bss->bssid, ETH_ALEN);
break;
}
@@ -2202,8 +2203,8 @@ mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
}
mwifiex_dbg(adapter, INFO,
- "info: Trying to associate to %s and bssid %pM\n",
- (char *)sme->ssid, sme->bssid);
+ "info: Trying to associate to %.*s and bssid %pM\n",
+ (int)sme->ssid_len, (char *)sme->ssid, sme->bssid);
ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid,
priv->bss_mode, sme->channel, sme, 0);
@@ -2333,8 +2334,8 @@ mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
}
mwifiex_dbg(priv->adapter, MSG,
- "info: trying to join to %s and bssid %pM\n",
- (char *)params->ssid, params->bssid);
+ "info: trying to join to %.*s and bssid %pM\n",
+ params->ssid_len, (char *)params->ssid, params->bssid);
mwifiex_set_ibss_params(priv, params);
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c
index 3cda1f956f0b..6378dfd3b4e8 100644
--- a/drivers/net/wireless/mwifiex/join.c
+++ b/drivers/net/wireless/mwifiex/join.c
@@ -661,9 +661,8 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN,
sizeof(priv->assoc_rsp_buf));
- memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size);
-
assoc_rsp->a_id = cpu_to_le16(aid);
+ memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size);
if (status_code) {
priv->adapter->dbg.num_cmd_assoc_failure++;
diff --git a/drivers/net/wireless/realtek/rtlwifi/regd.c b/drivers/net/wireless/realtek/rtlwifi/regd.c
index 5be34118e0af..f67e7e5b13e1 100644
--- a/drivers/net/wireless/realtek/rtlwifi/regd.c
+++ b/drivers/net/wireless/realtek/rtlwifi/regd.c
@@ -345,9 +345,9 @@ static const struct ieee80211_regdomain *_rtl_regdomain_select(
return &rtl_regdom_no_midband;
case COUNTRY_CODE_IC:
return &rtl_regdom_11;
- case COUNTRY_CODE_ETSI:
case COUNTRY_CODE_TELEC_NETGEAR:
return &rtl_regdom_60_64;
+ case COUNTRY_CODE_ETSI:
case COUNTRY_CODE_SPAIN:
case COUNTRY_CODE_FRANCE:
case COUNTRY_CODE_ISRAEL:
@@ -406,6 +406,8 @@ static u8 channel_plan_to_country_code(u8 channelplan)
return COUNTRY_CODE_WORLD_WIDE_13;
case 0x22:
return COUNTRY_CODE_IC;
+ case 0x25:
+ return COUNTRY_CODE_ETSI;
case 0x32:
return COUNTRY_CODE_TELEC_NETGEAR;
case 0x41:
diff --git a/drivers/nfc/fdp/fdp.c b/drivers/nfc/fdp/fdp.c
index ccb07a1b153d..23e53780728b 100644
--- a/drivers/nfc/fdp/fdp.c
+++ b/drivers/nfc/fdp/fdp.c
@@ -352,7 +352,7 @@ static int fdp_nci_patch_otp(struct nci_dev *ndev)
{
struct fdp_nci_info *info = nci_get_drvdata(ndev);
struct device *dev = &info->phy->i2c_dev->dev;
- u8 conn_id;
+ int conn_id;
int r = 0;
if (info->otp_version >= info->otp_patch_version)
@@ -423,7 +423,7 @@ static int fdp_nci_patch_ram(struct nci_dev *ndev)
{
struct fdp_nci_info *info = nci_get_drvdata(ndev);
struct device *dev = &info->phy->i2c_dev->dev;
- u8 conn_id;
+ int conn_id;
int r = 0;
if (info->ram_version >= info->ram_patch_version)
diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c
index 83deda4bb4d6..6f9563a96488 100644
--- a/drivers/nfc/mei_phy.c
+++ b/drivers/nfc/mei_phy.c
@@ -133,7 +133,7 @@ static int mei_nfc_if_version(struct nfc_mei_phy *phy)
return -ENOMEM;
bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply, if_version_length);
- if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) {
+ if (bytes_recv < 0 || bytes_recv < if_version_length) {
pr_err("Could not read IF version\n");
r = -EIO;
goto err;
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index cffd4d31d3c1..22528d051845 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -127,8 +127,12 @@ static int __init __reserved_mem_alloc_size(unsigned long node,
}
/* Need adjust the alignment to satisfy the CMA requirement */
- if (IS_ENABLED(CONFIG_CMA) && of_flat_dt_is_compatible(node, "shared-dma-pool"))
- align = max(align, (phys_addr_t)PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order));
+ if (IS_ENABLED(CONFIG_CMA) && of_flat_dt_is_compatible(node, "shared-dma-pool")) {
+ unsigned long order =
+ max_t(unsigned long, MAX_ORDER - 1, pageblock_order);
+
+ align = max(align, (phys_addr_t)PAGE_SIZE << order);
+ }
prop = of_get_flat_dt_prop(node, "alloc-ranges", &len);
if (prop) {
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index cd105a0bf5d1..584ad96c703f 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -2534,13 +2534,14 @@ static ssize_t msm_pcie_cmd_debug(struct file *file,
char str[MAX_MSG_LEN];
unsigned int testcase = 0;
int i;
+ u32 size = sizeof(str) < count ? sizeof(str) : count;
- memset(str, 0, sizeof(str));
- ret = copy_from_user(str, buf, sizeof(str));
+ memset(str, 0, size);
+ ret = copy_from_user(str, buf, size);
if (ret)
return -EFAULT;
- for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+ for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i)
testcase = (testcase * 10) + (str[i] - '0');
if (!rc_sel)
@@ -2569,13 +2570,14 @@ static ssize_t msm_pcie_set_rc_sel(struct file *file,
char str[MAX_MSG_LEN];
int i;
u32 new_rc_sel = 0;
+ u32 size = sizeof(str) < count ? sizeof(str) : count;
- memset(str, 0, sizeof(str));
- ret = copy_from_user(str, buf, sizeof(str));
+ memset(str, 0, size);
+ ret = copy_from_user(str, buf, size);
if (ret)
return -EFAULT;
- for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+ for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i)
new_rc_sel = (new_rc_sel * 10) + (str[i] - '0');
if ((!new_rc_sel) || (new_rc_sel > rc_sel_max)) {
@@ -2612,13 +2614,14 @@ static ssize_t msm_pcie_set_base_sel(struct file *file,
int i;
u32 new_base_sel = 0;
char *base_sel_name;
+ u32 size = sizeof(str) < count ? sizeof(str) : count;
- memset(str, 0, sizeof(str));
- ret = copy_from_user(str, buf, sizeof(str));
+ memset(str, 0, size);
+ ret = copy_from_user(str, buf, size);
if (ret)
return -EFAULT;
- for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+ for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i)
new_base_sel = (new_base_sel * 10) + (str[i] - '0');
if (!new_base_sel || new_base_sel > 5) {
@@ -2713,14 +2716,15 @@ static ssize_t msm_pcie_set_wr_offset(struct file *file,
unsigned long ret;
char str[MAX_MSG_LEN];
int i;
+ u32 size = sizeof(str) < count ? sizeof(str) : count;
- memset(str, 0, sizeof(str));
- ret = copy_from_user(str, buf, sizeof(str));
+ memset(str, 0, size);
+ ret = copy_from_user(str, buf, size);
if (ret)
return -EFAULT;
wr_offset = 0;
- for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+ for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i)
wr_offset = (wr_offset * 10) + (str[i] - '0');
pr_alert("PCIe: wr_offset is now 0x%x\n", wr_offset);
@@ -2739,14 +2743,15 @@ static ssize_t msm_pcie_set_wr_mask(struct file *file,
unsigned long ret;
char str[MAX_MSG_LEN];
int i;
+ u32 size = sizeof(str) < count ? sizeof(str) : count;
- memset(str, 0, sizeof(str));
- ret = copy_from_user(str, buf, sizeof(str));
+ memset(str, 0, size);
+ ret = copy_from_user(str, buf, size);
if (ret)
return -EFAULT;
wr_mask = 0;
- for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+ for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i)
wr_mask = (wr_mask * 10) + (str[i] - '0');
pr_alert("PCIe: wr_mask is now 0x%x\n", wr_mask);
@@ -2764,14 +2769,15 @@ static ssize_t msm_pcie_set_wr_value(struct file *file,
unsigned long ret;
char str[MAX_MSG_LEN];
int i;
+ u32 size = sizeof(str) < count ? sizeof(str) : count;
- memset(str, 0, sizeof(str));
- ret = copy_from_user(str, buf, sizeof(str));
+ memset(str, 0, size);
+ ret = copy_from_user(str, buf, size);
if (ret)
return -EFAULT;
wr_value = 0;
- for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+ for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i)
wr_value = (wr_value * 10) + (str[i] - '0');
pr_alert("PCIe: wr_value is now 0x%x\n", wr_value);
@@ -2890,14 +2896,15 @@ static ssize_t msm_pcie_set_corr_counter_limit(struct file *file,
unsigned long ret;
char str[MAX_MSG_LEN];
int i;
+ u32 size = sizeof(str) < count ? sizeof(str) : count;
- memset(str, 0, sizeof(str));
- ret = copy_from_user(str, buf, sizeof(str));
+ memset(str, 0, size);
+ ret = copy_from_user(str, buf, size);
if (ret)
return -EFAULT;
corr_counter_limit = 0;
- for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+ for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i)
corr_counter_limit = (corr_counter_limit * 10) + (str[i] - '0');
pr_info("PCIe: corr_counter_limit is now %lu\n", corr_counter_limit);
diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c
index 182224acedbe..58f1419a68ae 100644
--- a/drivers/pci/pcie/aer/aer_inject.c
+++ b/drivers/pci/pcie/aer/aer_inject.c
@@ -283,20 +283,6 @@ out:
return 0;
}
-static struct pci_dev *pcie_find_root_port(struct pci_dev *dev)
-{
- while (1) {
- if (!pci_is_pcie(dev))
- break;
- if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
- return dev;
- if (!dev->bus->self)
- break;
- dev = dev->bus->self;
- }
- return NULL;
-}
-
static int find_aer_device_iter(struct device *device, void *data)
{
struct pcie_device **result = data;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 9757cf9037a2..b5843c255263 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1415,6 +1415,21 @@ static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp)
dev_warn(&dev->dev, "PCI-X settings not supported\n");
}
+static bool pcie_root_rcb_set(struct pci_dev *dev)
+{
+ struct pci_dev *rp = pcie_find_root_port(dev);
+ u16 lnkctl;
+
+ if (!rp)
+ return false;
+
+ pcie_capability_read_word(rp, PCI_EXP_LNKCTL, &lnkctl);
+ if (lnkctl & PCI_EXP_LNKCTL_RCB)
+ return true;
+
+ return false;
+}
+
static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
{
int pos;
@@ -1444,9 +1459,20 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or);
/* Initialize Link Control Register */
- if (pcie_cap_has_lnkctl(dev))
+ if (pcie_cap_has_lnkctl(dev)) {
+
+ /*
+ * If the Root Port supports Read Completion Boundary of
+ * 128, set RCB to 128. Otherwise, clear it.
+ */
+ hpp->pci_exp_lnkctl_and |= PCI_EXP_LNKCTL_RCB;
+ hpp->pci_exp_lnkctl_or &= ~PCI_EXP_LNKCTL_RCB;
+ if (pcie_root_rcb_set(dev))
+ hpp->pci_exp_lnkctl_or |= PCI_EXP_LNKCTL_RCB;
+
pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or);
+ }
/* Find Advanced Error Reporting Enhanced Capability */
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 42774bc39786..254192b5dad1 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -3136,6 +3136,7 @@ static void quirk_no_bus_reset(struct pci_dev *dev)
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0030, quirk_no_bus_reset);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0032, quirk_no_bus_reset);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003c, quirk_no_bus_reset);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0033, quirk_no_bus_reset);
static void quirk_no_pm_reset(struct pci_dev *dev)
{
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 13b79438af1c..4c75b4d392c6 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -972,6 +972,7 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
if (i > 0 && spi != using_spi) {
pr_err("PPI/SPI IRQ type mismatch for %s!\n",
dn->name);
+ of_node_put(dn);
kfree(irqs);
return -EINVAL;
}
diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c
index 4e377599d266..a009ae34c5ef 100644
--- a/drivers/pinctrl/intel/pinctrl-cherryview.c
+++ b/drivers/pinctrl/intel/pinctrl-cherryview.c
@@ -1564,12 +1564,15 @@ static int chv_pinctrl_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
-static int chv_pinctrl_suspend(struct device *dev)
+static int chv_pinctrl_suspend_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct chv_pinctrl *pctrl = platform_get_drvdata(pdev);
+ unsigned long flags;
int i;
+ raw_spin_lock_irqsave(&chv_lock, flags);
+
pctrl->saved_intmask = readl(pctrl->regs + CHV_INTMASK);
for (i = 0; i < pctrl->community->npins; i++) {
@@ -1590,15 +1593,20 @@ static int chv_pinctrl_suspend(struct device *dev)
ctx->padctrl1 = readl(reg);
}
+ raw_spin_unlock_irqrestore(&chv_lock, flags);
+
return 0;
}
-static int chv_pinctrl_resume(struct device *dev)
+static int chv_pinctrl_resume_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct chv_pinctrl *pctrl = platform_get_drvdata(pdev);
+ unsigned long flags;
int i;
+ raw_spin_lock_irqsave(&chv_lock, flags);
+
/*
* Mask all interrupts before restoring per-pin configuration
* registers because we don't know in which state BIOS left them
@@ -1643,12 +1651,15 @@ static int chv_pinctrl_resume(struct device *dev)
chv_writel(0xffff, pctrl->regs + CHV_INTSTAT);
chv_writel(pctrl->saved_intmask, pctrl->regs + CHV_INTMASK);
+ raw_spin_unlock_irqrestore(&chv_lock, flags);
+
return 0;
}
#endif
static const struct dev_pm_ops chv_pinctrl_pm_ops = {
- SET_LATE_SYSTEM_SLEEP_PM_OPS(chv_pinctrl_suspend, chv_pinctrl_resume)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(chv_pinctrl_suspend_noirq,
+ chv_pinctrl_resume_noirq)
};
static const struct acpi_device_id chv_pinctrl_acpi_match[] = {
diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c
index b3235fd2950c..271cca63e9bd 100644
--- a/drivers/pinctrl/pinctrl-at91-pio4.c
+++ b/drivers/pinctrl/pinctrl-at91-pio4.c
@@ -1002,7 +1002,7 @@ static int atmel_pinctrl_probe(struct platform_device *pdev)
atmel_pioctrl->irqs[i] = res->start;
irq_set_chained_handler(res->start, atmel_gpio_irq_handler);
irq_set_handler_data(res->start, atmel_pioctrl);
- dev_dbg(dev, "bank %i: hwirq=%u\n", i, res->start);
+ dev_dbg(dev, "bank %i: irq=%pr\n", i, res);
}
atmel_pioctrl->irq_domain = irq_domain_add_linear(dev->of_node,
diff --git a/drivers/pinctrl/pinctrl-pistachio.c b/drivers/pinctrl/pinctrl-pistachio.c
index 6b1a47f8c096..98a459b1c095 100644
--- a/drivers/pinctrl/pinctrl-pistachio.c
+++ b/drivers/pinctrl/pinctrl-pistachio.c
@@ -809,17 +809,17 @@ static const struct pistachio_pin_group pistachio_groups[] = {
PADS_FUNCTION_SELECT2, 12, 0x3),
MFIO_MUX_PIN_GROUP(83, MIPS_PLL_LOCK, MIPS_TRACE_DATA, USB_DEBUG,
PADS_FUNCTION_SELECT2, 14, 0x3),
- MFIO_MUX_PIN_GROUP(84, SYS_PLL_LOCK, MIPS_TRACE_DATA, USB_DEBUG,
+ MFIO_MUX_PIN_GROUP(84, AUDIO_PLL_LOCK, MIPS_TRACE_DATA, USB_DEBUG,
PADS_FUNCTION_SELECT2, 16, 0x3),
- MFIO_MUX_PIN_GROUP(85, WIFI_PLL_LOCK, MIPS_TRACE_DATA, SDHOST_DEBUG,
+ MFIO_MUX_PIN_GROUP(85, RPU_V_PLL_LOCK, MIPS_TRACE_DATA, SDHOST_DEBUG,
PADS_FUNCTION_SELECT2, 18, 0x3),
- MFIO_MUX_PIN_GROUP(86, BT_PLL_LOCK, MIPS_TRACE_DATA, SDHOST_DEBUG,
+ MFIO_MUX_PIN_GROUP(86, RPU_L_PLL_LOCK, MIPS_TRACE_DATA, SDHOST_DEBUG,
PADS_FUNCTION_SELECT2, 20, 0x3),
- MFIO_MUX_PIN_GROUP(87, RPU_V_PLL_LOCK, DREQ2, SOCIF_DEBUG,
+ MFIO_MUX_PIN_GROUP(87, SYS_PLL_LOCK, DREQ2, SOCIF_DEBUG,
PADS_FUNCTION_SELECT2, 22, 0x3),
- MFIO_MUX_PIN_GROUP(88, RPU_L_PLL_LOCK, DREQ3, SOCIF_DEBUG,
+ MFIO_MUX_PIN_GROUP(88, WIFI_PLL_LOCK, DREQ3, SOCIF_DEBUG,
PADS_FUNCTION_SELECT2, 24, 0x3),
- MFIO_MUX_PIN_GROUP(89, AUDIO_PLL_LOCK, DREQ4, DREQ5,
+ MFIO_MUX_PIN_GROUP(89, BT_PLL_LOCK, DREQ4, DREQ5,
PADS_FUNCTION_SELECT2, 26, 0x3),
PIN_GROUP(TCK, "tck"),
PIN_GROUP(TRSTN, "trstn"),
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c
index 55083d278bb1..51fbf85301be 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c
@@ -485,12 +485,12 @@ static const struct sunxi_desc_pin sun8i_a23_pins[] = {
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8),
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
- SUNXI_FUNCTION(0x2, "uart2"), /* RTS */
+ SUNXI_FUNCTION(0x2, "uart1"), /* RTS */
SUNXI_FUNCTION_IRQ_BANK(0x4, 2, 8)), /* PG_EINT8 */
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9),
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
- SUNXI_FUNCTION(0x2, "uart2"), /* CTS */
+ SUNXI_FUNCTION(0x2, "uart1"), /* CTS */
SUNXI_FUNCTION_IRQ_BANK(0x4, 2, 9)), /* PG_EINT9 */
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10),
SUNXI_FUNCTION(0x0, "gpio_in"),
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c
index 8b381d69df86..584cdedea7a4 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c
@@ -407,12 +407,12 @@ static const struct sunxi_desc_pin sun8i_a33_pins[] = {
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8),
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
- SUNXI_FUNCTION(0x2, "uart2"), /* RTS */
+ SUNXI_FUNCTION(0x2, "uart1"), /* RTS */
SUNXI_FUNCTION_IRQ_BANK(0x4, 1, 8)), /* PG_EINT8 */
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9),
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
- SUNXI_FUNCTION(0x2, "uart2"), /* CTS */
+ SUNXI_FUNCTION(0x2, "uart1"), /* CTS */
SUNXI_FUNCTION_IRQ_BANK(0x4, 1, 9)), /* PG_EINT9 */
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10),
SUNXI_FUNCTION(0x0, "gpio_in"),
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c
index 589872cc8adb..a19c29c79b0a 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c
@@ -73,6 +73,12 @@ static void uniphier_pctl_pin_dbg_show(struct pinctrl_dev *pctldev,
case UNIPHIER_PIN_PULL_DOWN:
pull_dir = "DOWN";
break;
+ case UNIPHIER_PIN_PULL_UP_FIXED:
+ pull_dir = "UP(FIXED)";
+ break;
+ case UNIPHIER_PIN_PULL_DOWN_FIXED:
+ pull_dir = "DOWN(FIXED)";
+ break;
case UNIPHIER_PIN_PULL_NONE:
pull_dir = "NONE";
break;
diff --git a/drivers/platform/goldfish/Makefile b/drivers/platform/goldfish/Makefile
index d3487125838c..277a820ee4e1 100644
--- a/drivers/platform/goldfish/Makefile
+++ b/drivers/platform/goldfish/Makefile
@@ -2,4 +2,5 @@
# Makefile for Goldfish platform specific drivers
#
obj-$(CONFIG_GOLDFISH_BUS) += pdev_bus.o
-obj-$(CONFIG_GOLDFISH_PIPE) += goldfish_pipe.o
+obj-$(CONFIG_GOLDFISH_PIPE) += goldfish_pipe_all.o
+goldfish_pipe_all-objs := goldfish_pipe.o goldfish_pipe_v2.o
diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c
index 3215a33cf4fe..fd1452e28352 100644
--- a/drivers/platform/goldfish/goldfish_pipe.c
+++ b/drivers/platform/goldfish/goldfish_pipe.c
@@ -15,51 +15,11 @@
*
*/
-/* This source file contains the implementation of a special device driver
- * that intends to provide a *very* fast communication channel between the
- * guest system and the QEMU emulator.
- *
- * Usage from the guest is simply the following (error handling simplified):
- *
- * int fd = open("/dev/qemu_pipe",O_RDWR);
- * .... write() or read() through the pipe.
- *
- * This driver doesn't deal with the exact protocol used during the session.
- * It is intended to be as simple as something like:
- *
- * // do this _just_ after opening the fd to connect to a specific
- * // emulator service.
- * const char* msg = "<pipename>";
- * if (write(fd, msg, strlen(msg)+1) < 0) {
- * ... could not connect to <pipename> service
- * close(fd);
- * }
- *
- * // after this, simply read() and write() to communicate with the
- * // service. Exact protocol details left as an exercise to the reader.
- *
- * This driver is very fast because it doesn't copy any data through
- * intermediate buffers, since the emulator is capable of translating
- * guest user addresses into host ones.
- *
- * Note that we must however ensure that each user page involved in the
- * exchange is properly mapped during a transfer.
+/* This source file contains the implementation of the legacy version of
+ * a goldfish pipe device driver. See goldfish_pipe_v2.c for the current
+ * version.
*/
-
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/spinlock.h>
-#include <linux/miscdevice.h>
-#include <linux/platform_device.h>
-#include <linux/poll.h>
-#include <linux/sched.h>
-#include <linux/bitops.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/goldfish.h>
-#include <linux/mm.h>
-#include <linux/acpi.h>
+#include "goldfish_pipe.h"
/*
* IMPORTANT: The following constants must match the ones used and defined
@@ -109,29 +69,15 @@
#define PIPE_WAKE_READ (1 << 1) /* pipe can now be read from */
#define PIPE_WAKE_WRITE (1 << 2) /* pipe can now be written to */
-struct access_params {
- unsigned long channel;
- u32 size;
- unsigned long address;
- u32 cmd;
- u32 result;
- /* reserved for future extension */
- u32 flags;
-};
+#define MAX_PAGES_TO_GRAB 32
-/* The global driver data. Holds a reference to the i/o page used to
- * communicate with the emulator, and a wake queue for blocked tasks
- * waiting to be awoken.
- */
-struct goldfish_pipe_dev {
- spinlock_t lock;
- unsigned char __iomem *base;
- struct access_params *aps;
- int irq;
- u32 version;
-};
+#define DEBUG 0
-static struct goldfish_pipe_dev pipe_dev[1];
+#if DEBUG
+#define DPRINT(...) { printk(KERN_ERR __VA_ARGS__); }
+#else
+#define DPRINT(...)
+#endif
/* This data type models a given pipe instance */
struct goldfish_pipe {
@@ -141,6 +87,15 @@ struct goldfish_pipe {
wait_queue_head_t wake_queue;
};
+struct access_params {
+ unsigned long channel;
+ u32 size;
+ unsigned long address;
+ u32 cmd;
+ u32 result;
+ /* reserved for future extension */
+ u32 flags;
+};
/* Bit flags for the 'flags' field */
enum {
@@ -231,8 +186,10 @@ static int setup_access_params_addr(struct platform_device *pdev,
if (valid_batchbuffer_addr(dev, aps)) {
dev->aps = aps;
return 0;
- } else
+ } else {
+ devm_kfree(&pdev->dev, aps);
return -1;
+ }
}
/* A value that will not be set by qemu emulator */
@@ -269,6 +226,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
struct goldfish_pipe *pipe = filp->private_data;
struct goldfish_pipe_dev *dev = pipe->dev;
unsigned long address, address_end;
+ struct page* pages[MAX_PAGES_TO_GRAB] = {};
int count = 0, ret = -EINVAL;
/* If the emulator already closed the pipe, no need to go further */
@@ -292,45 +250,62 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
address_end = address + bufflen;
while (address < address_end) {
- unsigned long page_end = (address & PAGE_MASK) + PAGE_SIZE;
- unsigned long next = page_end < address_end ? page_end
- : address_end;
- unsigned long avail = next - address;
- int status, wakeBit;
-
- struct page *page;
-
- /* Either vaddr or paddr depending on the device version */
- unsigned long xaddr;
+ unsigned long page_end = (address & PAGE_MASK) + PAGE_SIZE;
+ unsigned long next, avail;
+ int status, wakeBit, page_i, num_contiguous_pages;
+ long first_page, last_page, requested_pages;
+ unsigned long xaddr, xaddr_prev, xaddr_i;
/*
- * We grab the pages on a page-by-page basis in case user
- * space gives us a potentially huge buffer but the read only
- * returns a small amount, then there's no need to pin that
- * much memory to the process.
+ * Attempt to grab multiple physically contiguous pages.
*/
- down_read(&current->mm->mmap_sem);
- ret = get_user_pages(current, current->mm, address, 1,
- !is_write, 0, &page, NULL);
- up_read(&current->mm->mmap_sem);
- if (ret < 0)
+ first_page = address & PAGE_MASK;
+ last_page = (address_end - 1) & PAGE_MASK;
+ requested_pages = ((last_page - first_page) >> PAGE_SHIFT) + 1;
+ if (requested_pages > MAX_PAGES_TO_GRAB) {
+ requested_pages = MAX_PAGES_TO_GRAB;
+ }
+ ret = get_user_pages_fast(first_page, requested_pages,
+ !is_write, pages);
+
+ DPRINT("%s: requested pages: %d %d %p\n", __FUNCTION__,
+ ret, requested_pages, first_page);
+ if (ret == 0) {
+ DPRINT("%s: error: (requested pages == 0) (wanted %d)\n",
+ __FUNCTION__, requested_pages);
+ mutex_unlock(&pipe->lock);
return ret;
+ }
+ if (ret < 0) {
+ DPRINT("%s: (requested pages < 0) %d \n",
+ __FUNCTION__, requested_pages);
+ mutex_unlock(&pipe->lock);
+ return ret;
+ }
- if (dev->version) {
- /* Device version 1 or newer (qemu-android) expects the
- * physical address. */
- xaddr = page_to_phys(page) | (address & ~PAGE_MASK);
- } else {
- /* Device version 0 (classic emulator) expects the
- * virtual address. */
- xaddr = address;
+ xaddr = page_to_phys(pages[0]) | (address & ~PAGE_MASK);
+ xaddr_prev = xaddr;
+ num_contiguous_pages = ret == 0 ? 0 : 1;
+ for (page_i = 1; page_i < ret; page_i++) {
+ xaddr_i = page_to_phys(pages[page_i]) | (address & ~PAGE_MASK);
+ if (xaddr_i == xaddr_prev + PAGE_SIZE) {
+ page_end += PAGE_SIZE;
+ xaddr_prev = xaddr_i;
+ num_contiguous_pages++;
+ } else {
+ DPRINT("%s: discontinuous page boundary: %d pages instead\n",
+ __FUNCTION__, page_i);
+ break;
+ }
}
+ next = page_end < address_end ? page_end : address_end;
+ avail = next - address;
/* Now, try to transfer the bytes in the current page */
spin_lock_irqsave(&dev->lock, irq_flags);
if (access_with_param(dev,
- is_write ? CMD_WRITE_BUFFER : CMD_READ_BUFFER,
- xaddr, avail, pipe, &status)) {
+ is_write ? CMD_WRITE_BUFFER : CMD_READ_BUFFER,
+ xaddr, avail, pipe, &status)) {
gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL,
dev->base + PIPE_REG_CHANNEL_HIGH);
writel(avail, dev->base + PIPE_REG_SIZE);
@@ -343,9 +318,13 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
}
spin_unlock_irqrestore(&dev->lock, irq_flags);
- if (status > 0 && !is_write)
- set_page_dirty(page);
- put_page(page);
+ for (page_i = 0; page_i < ret; page_i++) {
+ if (status > 0 && !is_write &&
+ page_i < num_contiguous_pages) {
+ set_page_dirty(pages[page_i]);
+ }
+ put_page(pages[page_i]);
+ }
if (status > 0) { /* Correct transfer */
count += status;
@@ -367,7 +346,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
*/
if (status != PIPE_ERROR_AGAIN)
pr_info_ratelimited("goldfish_pipe: backend returned error %d on %s\n",
- status, is_write ? "write" : "read");
+ status, is_write ? "write" : "read");
ret = 0;
break;
}
@@ -377,7 +356,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
* non-blocking mode, just return the error code.
*/
if (status != PIPE_ERROR_AGAIN ||
- (filp->f_flags & O_NONBLOCK) != 0) {
+ (filp->f_flags & O_NONBLOCK) != 0) {
ret = goldfish_pipe_error_convert(status);
break;
}
@@ -391,7 +370,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
/* Tell the emulator we're going to wait for a wake event */
goldfish_cmd(pipe,
- is_write ? CMD_WAKE_ON_WRITE : CMD_WAKE_ON_READ);
+ is_write ? CMD_WAKE_ON_WRITE : CMD_WAKE_ON_READ);
/* Unlock the pipe, then wait for the wake signal */
mutex_unlock(&pipe->lock);
@@ -399,22 +378,16 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
while (test_bit(wakeBit, &pipe->flags)) {
if (wait_event_interruptible(
pipe->wake_queue,
- !test_bit(wakeBit, &pipe->flags))) {
- ret = -ERESTARTSYS;
- break;
- }
+ !test_bit(wakeBit, &pipe->flags)))
+ return -ERESTARTSYS;
- if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) {
- ret = -EIO;
- break;
- }
+ if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags))
+ return -EIO;
}
/* Try to re-acquire the lock */
- if (mutex_lock_interruptible(&pipe->lock)) {
- ret = -ERESTARTSYS;
- break;
- }
+ if (mutex_lock_interruptible(&pipe->lock))
+ return -ERESTARTSYS;
}
mutex_unlock(&pipe->lock);
@@ -543,6 +516,8 @@ static int goldfish_pipe_open(struct inode *inode, struct file *file)
pipe->dev = dev;
mutex_init(&pipe->lock);
+ DPRINT("%s: call. pipe_dev pipe_dev=0x%lx new_pipe_addr=0x%lx file=0x%lx\n", __FUNCTION__, pipe_dev, pipe, file);
+ // spin lock init, write head of list, i guess
init_waitqueue_head(&pipe->wake_queue);
/*
@@ -565,6 +540,7 @@ static int goldfish_pipe_release(struct inode *inode, struct file *filp)
{
struct goldfish_pipe *pipe = filp->private_data;
+ DPRINT("%s: call. pipe=0x%lx file=0x%lx\n", __FUNCTION__, pipe, filp);
/* The guest is closing the channel, so tell the emulator right now */
goldfish_cmd(pipe, CMD_CLOSE);
kfree(pipe);
@@ -581,96 +557,33 @@ static const struct file_operations goldfish_pipe_fops = {
.release = goldfish_pipe_release,
};
-static struct miscdevice goldfish_pipe_device = {
+static struct miscdevice goldfish_pipe_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "goldfish_pipe",
.fops = &goldfish_pipe_fops,
};
-static int goldfish_pipe_probe(struct platform_device *pdev)
+int goldfish_pipe_device_init_v1(struct platform_device *pdev)
{
- int err;
- struct resource *r;
struct goldfish_pipe_dev *dev = pipe_dev;
-
- /* not thread safe, but this should not happen */
- WARN_ON(dev->base != NULL);
-
- spin_lock_init(&dev->lock);
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (r == NULL || resource_size(r) < PAGE_SIZE) {
- dev_err(&pdev->dev, "can't allocate i/o page\n");
- return -EINVAL;
- }
- dev->base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
- if (dev->base == NULL) {
- dev_err(&pdev->dev, "ioremap failed\n");
- return -EINVAL;
- }
-
- r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (r == NULL) {
- err = -EINVAL;
- goto error;
- }
- dev->irq = r->start;
-
- err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt,
+ int err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt,
IRQF_SHARED, "goldfish_pipe", dev);
if (err) {
- dev_err(&pdev->dev, "unable to allocate IRQ\n");
- goto error;
+ dev_err(&pdev->dev, "unable to allocate IRQ for v1\n");
+ return err;
}
- err = misc_register(&goldfish_pipe_device);
+ err = misc_register(&goldfish_pipe_dev);
if (err) {
- dev_err(&pdev->dev, "unable to register device\n");
- goto error;
+ dev_err(&pdev->dev, "unable to register v1 device\n");
+ return err;
}
- setup_access_params_addr(pdev, dev);
- /* Although the pipe device in the classic Android emulator does not
- * recognize the 'version' register, it won't treat this as an error
- * either and will simply return 0, which is fine. */
- dev->version = readl(dev->base + PIPE_REG_VERSION);
+ setup_access_params_addr(pdev, dev);
return 0;
-
-error:
- dev->base = NULL;
- return err;
}
-static int goldfish_pipe_remove(struct platform_device *pdev)
+void goldfish_pipe_device_deinit_v1(struct platform_device *pdev)
{
- struct goldfish_pipe_dev *dev = pipe_dev;
- misc_deregister(&goldfish_pipe_device);
- dev->base = NULL;
- return 0;
+ misc_deregister(&goldfish_pipe_dev);
}
-
-static const struct acpi_device_id goldfish_pipe_acpi_match[] = {
- { "GFSH0003", 0 },
- { },
-};
-MODULE_DEVICE_TABLE(acpi, goldfish_pipe_acpi_match);
-
-static const struct of_device_id goldfish_pipe_of_match[] = {
- { .compatible = "generic,android-pipe", },
- {},
-};
-MODULE_DEVICE_TABLE(of, goldfish_pipe_of_match);
-
-static struct platform_driver goldfish_pipe = {
- .probe = goldfish_pipe_probe,
- .remove = goldfish_pipe_remove,
- .driver = {
- .name = "goldfish_pipe",
- .of_match_table = goldfish_pipe_of_match,
- .acpi_match_table = ACPI_PTR(goldfish_pipe_acpi_match),
- }
-};
-
-module_platform_driver(goldfish_pipe);
-MODULE_AUTHOR("David Turner <digit@google.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/platform/goldfish/goldfish_pipe.h b/drivers/platform/goldfish/goldfish_pipe.h
new file mode 100644
index 000000000000..9b75a51dba24
--- /dev/null
+++ b/drivers/platform/goldfish/goldfish_pipe.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef GOLDFISH_PIPE_H
+#define GOLDFISH_PIPE_H
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/goldfish.h>
+#include <linux/mm.h>
+#include <linux/acpi.h>
+
+
+/* Initialize the legacy version of the pipe device driver */
+int goldfish_pipe_device_init_v1(struct platform_device *pdev);
+
+/* Deinitialize the legacy version of the pipe device driver */
+void goldfish_pipe_device_deinit_v1(struct platform_device *pdev);
+
+/* Forward declarations for the device struct */
+struct goldfish_pipe;
+struct goldfish_pipe_device_buffers;
+
+/* The global driver data. Holds a reference to the i/o page used to
+ * communicate with the emulator, and a wake queue for blocked tasks
+ * waiting to be awoken.
+ */
+struct goldfish_pipe_dev {
+ /*
+ * Global device spinlock. Protects the following members:
+ * - pipes, pipes_capacity
+ * - [*pipes, *pipes + pipes_capacity) - array data
+ * - first_signalled_pipe,
+ * goldfish_pipe::prev_signalled,
+ * goldfish_pipe::next_signalled,
+ * goldfish_pipe::signalled_flags - all singnalled-related fields,
+ * in all allocated pipes
+ * - open_command_params - PIPE_CMD_OPEN-related buffers
+ *
+ * It looks like a lot of different fields, but the trick is that the only
+ * operation that happens often is the signalled pipes array manipulation.
+ * That's why it's OK for now to keep the rest of the fields under the same
+ * lock. If we notice too much contention because of PIPE_CMD_OPEN,
+ * then we should add a separate lock there.
+ */
+ spinlock_t lock;
+
+ /*
+ * Array of the pipes of |pipes_capacity| elements,
+ * indexed by goldfish_pipe::id
+ */
+ struct goldfish_pipe **pipes;
+ u32 pipes_capacity;
+
+ /* Pointers to the buffers host uses for interaction with this driver */
+ struct goldfish_pipe_dev_buffers *buffers;
+
+ /* Head of a doubly linked list of signalled pipes */
+ struct goldfish_pipe *first_signalled_pipe;
+
+ /* Some device-specific data */
+ int irq;
+ int version;
+ unsigned char __iomem *base;
+
+ /* v1-specific access parameters */
+ struct access_params *aps;
+};
+
+extern struct goldfish_pipe_dev pipe_dev[1];
+
+#endif /* GOLDFISH_PIPE_H */
diff --git a/drivers/platform/goldfish/goldfish_pipe_v2.c b/drivers/platform/goldfish/goldfish_pipe_v2.c
new file mode 100644
index 000000000000..ad373ed36555
--- /dev/null
+++ b/drivers/platform/goldfish/goldfish_pipe_v2.c
@@ -0,0 +1,889 @@
+/*
+ * Copyright (C) 2012 Intel, Inc.
+ * Copyright (C) 2013 Intel, Inc.
+ * Copyright (C) 2014 Linaro Limited
+ * Copyright (C) 2011-2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* This source file contains the implementation of a special device driver
+ * that intends to provide a *very* fast communication channel between the
+ * guest system and the QEMU emulator.
+ *
+ * Usage from the guest is simply the following (error handling simplified):
+ *
+ * int fd = open("/dev/qemu_pipe",O_RDWR);
+ * .... write() or read() through the pipe.
+ *
+ * This driver doesn't deal with the exact protocol used during the session.
+ * It is intended to be as simple as something like:
+ *
+ * // do this _just_ after opening the fd to connect to a specific
+ * // emulator service.
+ * const char* msg = "<pipename>";
+ * if (write(fd, msg, strlen(msg)+1) < 0) {
+ * ... could not connect to <pipename> service
+ * close(fd);
+ * }
+ *
+ * // after this, simply read() and write() to communicate with the
+ * // service. Exact protocol details left as an exercise to the reader.
+ *
+ * This driver is very fast because it doesn't copy any data through
+ * intermediate buffers, since the emulator is capable of translating
+ * guest user addresses into host ones.
+ *
+ * Note that we must however ensure that each user page involved in the
+ * exchange is properly mapped during a transfer.
+ */
+
+#include "goldfish_pipe.h"
+
+
+/*
+ * Update this when something changes in the driver's behavior so the host
+ * can benefit from knowing it
+ */
+enum {
+ PIPE_DRIVER_VERSION = 2,
+ PIPE_CURRENT_DEVICE_VERSION = 2
+};
+
+/*
+ * IMPORTANT: The following constants must match the ones used and defined
+ * in external/qemu/hw/goldfish_pipe.c in the Android source tree.
+ */
+
+/* List of bitflags returned in status of CMD_POLL command */
+enum PipePollFlags {
+ PIPE_POLL_IN = 1 << 0,
+ PIPE_POLL_OUT = 1 << 1,
+ PIPE_POLL_HUP = 1 << 2
+};
+
+/* Possible status values used to signal errors - see goldfish_pipe_error_convert */
+enum PipeErrors {
+ PIPE_ERROR_INVAL = -1,
+ PIPE_ERROR_AGAIN = -2,
+ PIPE_ERROR_NOMEM = -3,
+ PIPE_ERROR_IO = -4
+};
+
+/* Bit-flags used to signal events from the emulator */
+enum PipeWakeFlags {
+ PIPE_WAKE_CLOSED = 1 << 0, /* emulator closed pipe */
+ PIPE_WAKE_READ = 1 << 1, /* pipe can now be read from */
+ PIPE_WAKE_WRITE = 1 << 2 /* pipe can now be written to */
+};
+
+/* Bit flags for the 'flags' field */
+enum PipeFlagsBits {
+ BIT_CLOSED_ON_HOST = 0, /* pipe closed by host */
+ BIT_WAKE_ON_WRITE = 1, /* want to be woken on writes */
+ BIT_WAKE_ON_READ = 2, /* want to be woken on reads */
+};
+
+enum PipeRegs {
+ PIPE_REG_CMD = 0,
+
+ PIPE_REG_SIGNAL_BUFFER_HIGH = 4,
+ PIPE_REG_SIGNAL_BUFFER = 8,
+ PIPE_REG_SIGNAL_BUFFER_COUNT = 12,
+
+ PIPE_REG_OPEN_BUFFER_HIGH = 20,
+ PIPE_REG_OPEN_BUFFER = 24,
+
+ PIPE_REG_VERSION = 36,
+
+ PIPE_REG_GET_SIGNALLED = 48,
+};
+
+enum PipeCmdCode {
+ PIPE_CMD_OPEN = 1, /* to be used by the pipe device itself */
+ PIPE_CMD_CLOSE,
+ PIPE_CMD_POLL,
+ PIPE_CMD_WRITE,
+ PIPE_CMD_WAKE_ON_WRITE,
+ PIPE_CMD_READ,
+ PIPE_CMD_WAKE_ON_READ,
+
+ /*
+ * TODO(zyy): implement a deferred read/write execution to allow parallel
+ * processing of pipe operations on the host.
+ */
+ PIPE_CMD_WAKE_ON_DONE_IO,
+};
+
+enum {
+ MAX_BUFFERS_PER_COMMAND = 336,
+ MAX_SIGNALLED_PIPES = 64,
+ INITIAL_PIPES_CAPACITY = 64
+};
+
+struct goldfish_pipe_dev;
+struct goldfish_pipe;
+struct goldfish_pipe_command;
+
+/* A per-pipe command structure, shared with the host */
+struct goldfish_pipe_command {
+ s32 cmd; /* PipeCmdCode, guest -> host */
+ s32 id; /* pipe id, guest -> host */
+ s32 status; /* command execution status, host -> guest */
+ s32 reserved; /* to pad to 64-bit boundary */
+ union {
+ /* Parameters for PIPE_CMD_{READ,WRITE} */
+ struct {
+ u32 buffers_count; /* number of buffers, guest -> host */
+ s32 consumed_size; /* number of consumed bytes, host -> guest */
+ u64 ptrs[MAX_BUFFERS_PER_COMMAND]; /* buffer pointers, guest -> host */
+ u32 sizes[MAX_BUFFERS_PER_COMMAND]; /* buffer sizes, guest -> host */
+ } rw_params;
+ };
+};
+
+/* A single signalled pipe information */
+struct signalled_pipe_buffer {
+ u32 id;
+ u32 flags;
+};
+
+/* Parameters for the PIPE_CMD_OPEN command */
+struct open_command_param {
+ u64 command_buffer_ptr;
+ u32 rw_params_max_count;
+};
+
+/* Device-level set of buffers shared with the host */
+struct goldfish_pipe_dev_buffers {
+ struct open_command_param open_command_params;
+ struct signalled_pipe_buffer signalled_pipe_buffers[MAX_SIGNALLED_PIPES];
+};
+
+/* This data type models a given pipe instance */
+struct goldfish_pipe {
+ u32 id; /* pipe ID - index into goldfish_pipe_dev::pipes array */
+ unsigned long flags; /* The wake flags pipe is waiting for
+ * Note: not protected with any lock, uses atomic operations
+ * and barriers to make it thread-safe.
+ */
+ unsigned long signalled_flags; /* wake flags host have signalled,
+ * - protected by goldfish_pipe_dev::lock */
+
+ struct goldfish_pipe_command *command_buffer; /* A pointer to command buffer */
+
+ /* doubly linked list of signalled pipes, protected by goldfish_pipe_dev::lock */
+ struct goldfish_pipe *prev_signalled;
+ struct goldfish_pipe *next_signalled;
+
+ /*
+ * A pipe's own lock. Protects the following:
+ * - *command_buffer - makes sure a command can safely write its parameters
+ * to the host and read the results back.
+ */
+ struct mutex lock;
+
+ wait_queue_head_t wake_queue; /* A wake queue for sleeping until host signals an event */
+ struct goldfish_pipe_dev *dev; /* Pointer to the parent goldfish_pipe_dev instance */
+};
+
+struct goldfish_pipe_dev pipe_dev[1] = {};
+
+static int goldfish_cmd_locked(struct goldfish_pipe *pipe, enum PipeCmdCode cmd)
+{
+ pipe->command_buffer->cmd = cmd;
+ pipe->command_buffer->status = PIPE_ERROR_INVAL; /* failure by default */
+ writel(pipe->id, pipe->dev->base + PIPE_REG_CMD);
+ return pipe->command_buffer->status;
+}
+
+static int goldfish_cmd(struct goldfish_pipe *pipe, enum PipeCmdCode cmd)
+{
+ int status;
+ if (mutex_lock_interruptible(&pipe->lock))
+ return PIPE_ERROR_IO;
+ status = goldfish_cmd_locked(pipe, cmd);
+ mutex_unlock(&pipe->lock);
+ return status;
+}
+
+/*
+ * This function converts an error code returned by the emulator through
+ * the PIPE_REG_STATUS i/o register into a valid negative errno value.
+ */
+static int goldfish_pipe_error_convert(int status)
+{
+ switch (status) {
+ case PIPE_ERROR_AGAIN:
+ return -EAGAIN;
+ case PIPE_ERROR_NOMEM:
+ return -ENOMEM;
+ case PIPE_ERROR_IO:
+ return -EIO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pin_user_pages(unsigned long first_page, unsigned long last_page,
+ unsigned last_page_size, int is_write,
+ struct page *pages[MAX_BUFFERS_PER_COMMAND], unsigned *iter_last_page_size)
+{
+ int ret;
+ int requested_pages = ((last_page - first_page) >> PAGE_SHIFT) + 1;
+ if (requested_pages > MAX_BUFFERS_PER_COMMAND) {
+ requested_pages = MAX_BUFFERS_PER_COMMAND;
+ *iter_last_page_size = PAGE_SIZE;
+ } else {
+ *iter_last_page_size = last_page_size;
+ }
+
+ ret = get_user_pages_fast(
+ first_page, requested_pages, !is_write, pages);
+ if (ret <= 0)
+ return -EFAULT;
+ if (ret < requested_pages)
+ *iter_last_page_size = PAGE_SIZE;
+ return ret;
+
+}
+
+static void release_user_pages(struct page **pages, int pages_count,
+ int is_write, s32 consumed_size)
+{
+ int i;
+ for (i = 0; i < pages_count; i++) {
+ if (!is_write && consumed_size > 0) {
+ set_page_dirty(pages[i]);
+ }
+ put_page(pages[i]);
+ }
+}
+
+/* Populate the call parameters, merging adjacent pages together */
+static void populate_rw_params(
+ struct page **pages, int pages_count,
+ unsigned long address, unsigned long address_end,
+ unsigned long first_page, unsigned long last_page,
+ unsigned iter_last_page_size, int is_write,
+ struct goldfish_pipe_command *command)
+{
+ /*
+ * Process the first page separately - it's the only page that
+ * needs special handling for its start address.
+ */
+ unsigned long xaddr = page_to_phys(pages[0]);
+ unsigned long xaddr_prev = xaddr;
+ int buffer_idx = 0;
+ int i = 1;
+ int size_on_page = first_page == last_page
+ ? (int)(address_end - address)
+ : (PAGE_SIZE - (address & ~PAGE_MASK));
+ command->rw_params.ptrs[0] = (u64)(xaddr | (address & ~PAGE_MASK));
+ command->rw_params.sizes[0] = size_on_page;
+ for (; i < pages_count; ++i) {
+ xaddr = page_to_phys(pages[i]);
+ size_on_page = (i == pages_count - 1) ? iter_last_page_size : PAGE_SIZE;
+ if (xaddr == xaddr_prev + PAGE_SIZE) {
+ command->rw_params.sizes[buffer_idx] += size_on_page;
+ } else {
+ ++buffer_idx;
+ command->rw_params.ptrs[buffer_idx] = (u64)xaddr;
+ command->rw_params.sizes[buffer_idx] = size_on_page;
+ }
+ xaddr_prev = xaddr;
+ }
+ command->rw_params.buffers_count = buffer_idx + 1;
+}
+
+static int transfer_max_buffers(struct goldfish_pipe* pipe,
+ unsigned long address, unsigned long address_end, int is_write,
+ unsigned long last_page, unsigned int last_page_size,
+ s32* consumed_size, int* status)
+{
+ struct page *pages[MAX_BUFFERS_PER_COMMAND];
+ unsigned long first_page = address & PAGE_MASK;
+ unsigned int iter_last_page_size;
+ int pages_count = pin_user_pages(first_page, last_page,
+ last_page_size, is_write,
+ pages, &iter_last_page_size);
+ if (pages_count < 0)
+ return pages_count;
+
+ /* Serialize access to the pipe command buffers */
+ if (mutex_lock_interruptible(&pipe->lock))
+ return -ERESTARTSYS;
+
+ populate_rw_params(pages, pages_count, address, address_end,
+ first_page, last_page, iter_last_page_size, is_write,
+ pipe->command_buffer);
+
+ /* Transfer the data */
+ *status = goldfish_cmd_locked(pipe,
+ is_write ? PIPE_CMD_WRITE : PIPE_CMD_READ);
+
+ *consumed_size = pipe->command_buffer->rw_params.consumed_size;
+
+ mutex_unlock(&pipe->lock);
+
+ release_user_pages(pages, pages_count, is_write, *consumed_size);
+
+ return 0;
+}
+
+static int wait_for_host_signal(struct goldfish_pipe *pipe, int is_write)
+{
+ u32 wakeBit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ;
+ set_bit(wakeBit, &pipe->flags);
+
+ /* Tell the emulator we're going to wait for a wake event */
+ (void)goldfish_cmd(pipe,
+ is_write ? PIPE_CMD_WAKE_ON_WRITE : PIPE_CMD_WAKE_ON_READ);
+
+ while (test_bit(wakeBit, &pipe->flags)) {
+ if (wait_event_interruptible(
+ pipe->wake_queue,
+ !test_bit(wakeBit, &pipe->flags)))
+ return -ERESTARTSYS;
+
+ if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags))
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static ssize_t goldfish_pipe_read_write(struct file *filp,
+ char __user *buffer, size_t bufflen, int is_write)
+{
+ struct goldfish_pipe *pipe = filp->private_data;
+ int count = 0, ret = -EINVAL;
+ unsigned long address, address_end, last_page;
+ unsigned int last_page_size;
+
+ /* If the emulator already closed the pipe, no need to go further */
+ if (unlikely(test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)))
+ return -EIO;
+ /* Null reads or writes succeeds */
+ if (unlikely(bufflen == 0))
+ return 0;
+ /* Check the buffer range for access */
+ if (unlikely(!access_ok(is_write ? VERIFY_WRITE : VERIFY_READ,
+ buffer, bufflen)))
+ return -EFAULT;
+
+ address = (unsigned long)buffer;
+ address_end = address + bufflen;
+ last_page = (address_end - 1) & PAGE_MASK;
+ last_page_size = ((address_end - 1) & ~PAGE_MASK) + 1;
+
+ while (address < address_end) {
+ s32 consumed_size;
+ int status;
+ ret = transfer_max_buffers(pipe, address, address_end, is_write,
+ last_page, last_page_size, &consumed_size, &status);
+ if (ret < 0)
+ break;
+
+ if (consumed_size > 0) {
+ /* No matter what's the status, we've transfered something */
+ count += consumed_size;
+ address += consumed_size;
+ }
+ if (status > 0)
+ continue;
+ if (status == 0) {
+ /* EOF */
+ ret = 0;
+ break;
+ }
+ if (count > 0) {
+ /*
+ * An error occured, but we already transfered
+ * something on one of the previous iterations.
+ * Just return what we already copied and log this
+ * err.
+ */
+ if (status != PIPE_ERROR_AGAIN)
+ pr_info_ratelimited("goldfish_pipe: backend error %d on %s\n",
+ status, is_write ? "write" : "read");
+ break;
+ }
+
+ /*
+ * If the error is not PIPE_ERROR_AGAIN, or if we are in
+ * non-blocking mode, just return the error code.
+ */
+ if (status != PIPE_ERROR_AGAIN || (filp->f_flags & O_NONBLOCK) != 0) {
+ ret = goldfish_pipe_error_convert(status);
+ break;
+ }
+
+ status = wait_for_host_signal(pipe, is_write);
+ if (status < 0)
+ return status;
+ }
+
+ if (count > 0)
+ return count;
+ return ret;
+}
+
+static ssize_t goldfish_pipe_read(struct file *filp, char __user *buffer,
+ size_t bufflen, loff_t *ppos)
+{
+ return goldfish_pipe_read_write(filp, buffer, bufflen, /* is_write */ 0);
+}
+
+static ssize_t goldfish_pipe_write(struct file *filp,
+ const char __user *buffer, size_t bufflen,
+ loff_t *ppos)
+{
+ return goldfish_pipe_read_write(filp,
+ /* cast away the const */(char __user *)buffer, bufflen,
+ /* is_write */ 1);
+}
+
+static unsigned int goldfish_pipe_poll(struct file *filp, poll_table *wait)
+{
+ struct goldfish_pipe *pipe = filp->private_data;
+ unsigned int mask = 0;
+ int status;
+
+ poll_wait(filp, &pipe->wake_queue, wait);
+
+ status = goldfish_cmd(pipe, PIPE_CMD_POLL);
+ if (status < 0) {
+ return -ERESTARTSYS;
+ }
+
+ if (status & PIPE_POLL_IN)
+ mask |= POLLIN | POLLRDNORM;
+ if (status & PIPE_POLL_OUT)
+ mask |= POLLOUT | POLLWRNORM;
+ if (status & PIPE_POLL_HUP)
+ mask |= POLLHUP;
+ if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags))
+ mask |= POLLERR;
+
+ return mask;
+}
+
+static void signalled_pipes_add_locked(struct goldfish_pipe_dev *dev,
+ u32 id, u32 flags)
+{
+ struct goldfish_pipe *pipe;
+
+ BUG_ON(id >= dev->pipes_capacity);
+
+ pipe = dev->pipes[id];
+ if (!pipe)
+ return;
+ pipe->signalled_flags |= flags;
+
+ if (pipe->prev_signalled || pipe->next_signalled
+ || dev->first_signalled_pipe == pipe)
+ return; /* already in the list */
+ pipe->next_signalled = dev->first_signalled_pipe;
+ if (dev->first_signalled_pipe) {
+ dev->first_signalled_pipe->prev_signalled = pipe;
+ }
+ dev->first_signalled_pipe = pipe;
+}
+
+static void signalled_pipes_remove_locked(struct goldfish_pipe_dev *dev,
+ struct goldfish_pipe *pipe) {
+ if (pipe->prev_signalled)
+ pipe->prev_signalled->next_signalled = pipe->next_signalled;
+ if (pipe->next_signalled)
+ pipe->next_signalled->prev_signalled = pipe->prev_signalled;
+ if (pipe == dev->first_signalled_pipe)
+ dev->first_signalled_pipe = pipe->next_signalled;
+ pipe->prev_signalled = NULL;
+ pipe->next_signalled = NULL;
+}
+
+static struct goldfish_pipe *signalled_pipes_pop_front(struct goldfish_pipe_dev *dev,
+ int *wakes)
+{
+ struct goldfish_pipe *pipe;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->lock, flags);
+
+ pipe = dev->first_signalled_pipe;
+ if (pipe) {
+ *wakes = pipe->signalled_flags;
+ pipe->signalled_flags = 0;
+ /*
+ * This is an optimized version of signalled_pipes_remove_locked() -
+ * we want to make it as fast as possible to wake the sleeping pipe
+ * operations faster
+ */
+ dev->first_signalled_pipe = pipe->next_signalled;
+ if (dev->first_signalled_pipe)
+ dev->first_signalled_pipe->prev_signalled = NULL;
+ pipe->next_signalled = NULL;
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return pipe;
+}
+
+static void goldfish_interrupt_task(unsigned long unused)
+{
+ struct goldfish_pipe_dev *dev = pipe_dev;
+ /* Iterate over the signalled pipes and wake them one by one */
+ struct goldfish_pipe *pipe;
+ int wakes;
+ while ((pipe = signalled_pipes_pop_front(dev, &wakes)) != NULL) {
+ if (wakes & PIPE_WAKE_CLOSED) {
+ pipe->flags = 1 << BIT_CLOSED_ON_HOST;
+ } else {
+ if (wakes & PIPE_WAKE_READ)
+ clear_bit(BIT_WAKE_ON_READ, &pipe->flags);
+ if (wakes & PIPE_WAKE_WRITE)
+ clear_bit(BIT_WAKE_ON_WRITE, &pipe->flags);
+ }
+ /*
+ * wake_up_interruptible() implies a write barrier, so don't explicitly
+ * add another one here.
+ */
+ wake_up_interruptible(&pipe->wake_queue);
+ }
+}
+DECLARE_TASKLET(goldfish_interrupt_tasklet, goldfish_interrupt_task, 0);
+
+/*
+ * The general idea of the interrupt handling:
+ *
+ * 1. device raises an interrupt if there's at least one signalled pipe
+ * 2. IRQ handler reads the signalled pipes and their count from the device
+ * 3. device writes them into a shared buffer and returns the count
+ * it only resets the IRQ if it has returned all signalled pipes,
+ * otherwise it leaves it raised, so IRQ handler will be called
+ * again for the next chunk
+ * 4. IRQ handler adds all returned pipes to the device's signalled pipes list
+ * 5. IRQ handler launches a tasklet to process the signalled pipes from the
+ * list in a separate context
+ */
+static irqreturn_t goldfish_pipe_interrupt(int irq, void *dev_id)
+{
+ u32 count;
+ u32 i;
+ unsigned long flags;
+ struct goldfish_pipe_dev *dev = dev_id;
+ if (dev != pipe_dev)
+ return IRQ_NONE;
+
+ /* Request the signalled pipes from the device */
+ spin_lock_irqsave(&dev->lock, flags);
+
+ count = readl(dev->base + PIPE_REG_GET_SIGNALLED);
+ if (count == 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return IRQ_NONE;
+ }
+ if (count > MAX_SIGNALLED_PIPES)
+ count = MAX_SIGNALLED_PIPES;
+
+ for (i = 0; i < count; ++i)
+ signalled_pipes_add_locked(dev,
+ dev->buffers->signalled_pipe_buffers[i].id,
+ dev->buffers->signalled_pipe_buffers[i].flags);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ tasklet_schedule(&goldfish_interrupt_tasklet);
+ return IRQ_HANDLED;
+}
+
+static int get_free_pipe_id_locked(struct goldfish_pipe_dev *dev)
+{
+ int id;
+ for (id = 0; id < dev->pipes_capacity; ++id)
+ if (!dev->pipes[id])
+ return id;
+
+ {
+ /* Reallocate the array */
+ u32 new_capacity = 2 * dev->pipes_capacity;
+ struct goldfish_pipe **pipes =
+ kcalloc(new_capacity, sizeof(*pipes),
+ GFP_ATOMIC);
+ if (!pipes)
+ return -ENOMEM;
+ memcpy(pipes, dev->pipes, sizeof(*pipes) * dev->pipes_capacity);
+ kfree(dev->pipes);
+ dev->pipes = pipes;
+ id = dev->pipes_capacity;
+ dev->pipes_capacity = new_capacity;
+ }
+ return id;
+}
+
+/**
+ * goldfish_pipe_open - open a channel to the AVD
+ * @inode: inode of device
+ * @file: file struct of opener
+ *
+ * Create a new pipe link between the emulator and the use application.
+ * Each new request produces a new pipe.
+ *
+ * Note: we use the pipe ID as a mux. All goldfish emulations are 32bit
+ * right now so this is fine. A move to 64bit will need this addressing
+ */
+static int goldfish_pipe_open(struct inode *inode, struct file *file)
+{
+ struct goldfish_pipe_dev *dev = pipe_dev;
+ unsigned long flags;
+ int id;
+ int status;
+
+ /* Allocate new pipe kernel object */
+ struct goldfish_pipe *pipe = kzalloc(sizeof(*pipe), GFP_KERNEL);
+ if (pipe == NULL)
+ return -ENOMEM;
+
+ pipe->dev = dev;
+ mutex_init(&pipe->lock);
+ init_waitqueue_head(&pipe->wake_queue);
+
+ /*
+ * Command buffer needs to be allocated on its own page to make sure it is
+ * physically contiguous in host's address space.
+ */
+ pipe->command_buffer =
+ (struct goldfish_pipe_command*)__get_free_page(GFP_KERNEL);
+ if (!pipe->command_buffer) {
+ status = -ENOMEM;
+ goto err_pipe;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ id = get_free_pipe_id_locked(dev);
+ if (id < 0) {
+ status = id;
+ goto err_id_locked;
+ }
+
+ dev->pipes[id] = pipe;
+ pipe->id = id;
+ pipe->command_buffer->id = id;
+
+ /* Now tell the emulator we're opening a new pipe. */
+ dev->buffers->open_command_params.rw_params_max_count =
+ MAX_BUFFERS_PER_COMMAND;
+ dev->buffers->open_command_params.command_buffer_ptr =
+ (u64)(unsigned long)__pa(pipe->command_buffer);
+ status = goldfish_cmd_locked(pipe, PIPE_CMD_OPEN);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if (status < 0)
+ goto err_cmd;
+ /* All is done, save the pipe into the file's private data field */
+ file->private_data = pipe;
+ return 0;
+
+err_cmd:
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->pipes[id] = NULL;
+err_id_locked:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ free_page((unsigned long)pipe->command_buffer);
+err_pipe:
+ kfree(pipe);
+ return status;
+}
+
+static int goldfish_pipe_release(struct inode *inode, struct file *filp)
+{
+ unsigned long flags;
+ struct goldfish_pipe *pipe = filp->private_data;
+ struct goldfish_pipe_dev *dev = pipe->dev;
+
+ /* The guest is closing the channel, so tell the emulator right now */
+ (void)goldfish_cmd(pipe, PIPE_CMD_CLOSE);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->pipes[pipe->id] = NULL;
+ signalled_pipes_remove_locked(dev, pipe);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ filp->private_data = NULL;
+ free_page((unsigned long)pipe->command_buffer);
+ kfree(pipe);
+ return 0;
+}
+
+static const struct file_operations goldfish_pipe_fops = {
+ .owner = THIS_MODULE,
+ .read = goldfish_pipe_read,
+ .write = goldfish_pipe_write,
+ .poll = goldfish_pipe_poll,
+ .open = goldfish_pipe_open,
+ .release = goldfish_pipe_release,
+};
+
+static struct miscdevice goldfish_pipe_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "goldfish_pipe",
+ .fops = &goldfish_pipe_fops,
+};
+
+static int goldfish_pipe_device_init_v2(struct platform_device *pdev)
+{
+ char *page;
+ struct goldfish_pipe_dev *dev = pipe_dev;
+ int err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt,
+ IRQF_SHARED, "goldfish_pipe", dev);
+ if (err) {
+ dev_err(&pdev->dev, "unable to allocate IRQ for v2\n");
+ return err;
+ }
+
+ err = misc_register(&goldfish_pipe_dev);
+ if (err) {
+ dev_err(&pdev->dev, "unable to register v2 device\n");
+ return err;
+ }
+
+ dev->first_signalled_pipe = NULL;
+ dev->pipes_capacity = INITIAL_PIPES_CAPACITY;
+ dev->pipes = kcalloc(dev->pipes_capacity, sizeof(*dev->pipes), GFP_KERNEL);
+ if (!dev->pipes)
+ return -ENOMEM;
+
+ /*
+ * We're going to pass two buffers, open_command_params and
+ * signalled_pipe_buffers, to the host. This means each of those buffers
+ * needs to be contained in a single physical page. The easiest choice is
+ * to just allocate a page and place the buffers in it.
+ */
+ BUG_ON(sizeof(*dev->buffers) > PAGE_SIZE);
+ page = (char*)__get_free_page(GFP_KERNEL);
+ if (!page) {
+ kfree(dev->pipes);
+ return -ENOMEM;
+ }
+ dev->buffers = (struct goldfish_pipe_dev_buffers*)page;
+
+ /* Send the buffer addresses to the host */
+ {
+ u64 paddr = __pa(&dev->buffers->signalled_pipe_buffers);
+ writel((u32)(unsigned long)(paddr >> 32), dev->base + PIPE_REG_SIGNAL_BUFFER_HIGH);
+ writel((u32)(unsigned long)paddr, dev->base + PIPE_REG_SIGNAL_BUFFER);
+ writel((u32)MAX_SIGNALLED_PIPES, dev->base + PIPE_REG_SIGNAL_BUFFER_COUNT);
+
+ paddr = __pa(&dev->buffers->open_command_params);
+ writel((u32)(unsigned long)(paddr >> 32), dev->base + PIPE_REG_OPEN_BUFFER_HIGH);
+ writel((u32)(unsigned long)paddr, dev->base + PIPE_REG_OPEN_BUFFER);
+ }
+ return 0;
+}
+
+static void goldfish_pipe_device_deinit_v2(struct platform_device *pdev) {
+ struct goldfish_pipe_dev *dev = pipe_dev;
+ misc_deregister(&goldfish_pipe_dev);
+ kfree(dev->pipes);
+ free_page((unsigned long)dev->buffers);
+}
+
+static int goldfish_pipe_probe(struct platform_device *pdev)
+{
+ int err;
+ struct resource *r;
+ struct goldfish_pipe_dev *dev = pipe_dev;
+
+ BUG_ON(sizeof(struct goldfish_pipe_command) > PAGE_SIZE);
+
+ /* not thread safe, but this should not happen */
+ WARN_ON(dev->base != NULL);
+
+ spin_lock_init(&dev->lock);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL || resource_size(r) < PAGE_SIZE) {
+ dev_err(&pdev->dev, "can't allocate i/o page\n");
+ return -EINVAL;
+ }
+ dev->base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
+ if (dev->base == NULL) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ return -EINVAL;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (r == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+ dev->irq = r->start;
+
+ /*
+ * Exchange the versions with the host device
+ *
+ * Note: v1 driver used to not report its version, so we write it before
+ * reading device version back: this allows the host implementation to
+ * detect the old driver (if there was no version write before read).
+ */
+ writel((u32)PIPE_DRIVER_VERSION, dev->base + PIPE_REG_VERSION);
+ dev->version = readl(dev->base + PIPE_REG_VERSION);
+ if (dev->version < PIPE_CURRENT_DEVICE_VERSION) {
+ /* initialize the old device version */
+ err = goldfish_pipe_device_init_v1(pdev);
+ } else {
+ /* Host device supports the new interface */
+ err = goldfish_pipe_device_init_v2(pdev);
+ }
+ if (!err)
+ return 0;
+
+error:
+ dev->base = NULL;
+ return err;
+}
+
+static int goldfish_pipe_remove(struct platform_device *pdev)
+{
+ struct goldfish_pipe_dev *dev = pipe_dev;
+ if (dev->version < PIPE_CURRENT_DEVICE_VERSION)
+ goldfish_pipe_device_deinit_v1(pdev);
+ else
+ goldfish_pipe_device_deinit_v2(pdev);
+ dev->base = NULL;
+ return 0;
+}
+
+static const struct acpi_device_id goldfish_pipe_acpi_match[] = {
+ { "GFSH0003", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_pipe_acpi_match);
+
+static const struct of_device_id goldfish_pipe_of_match[] = {
+ { .compatible = "google,android-pipe", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_pipe_of_match);
+
+static struct platform_driver goldfish_pipe_driver = {
+ .probe = goldfish_pipe_probe,
+ .remove = goldfish_pipe_remove,
+ .driver = {
+ .name = "goldfish_pipe",
+ .of_match_table = goldfish_pipe_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_pipe_acpi_match),
+ }
+};
+
+module_platform_driver(goldfish_pipe_driver);
+MODULE_AUTHOR("David Turner <digit@google.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig
index 024c66ac8e57..66bdc593f811 100644
--- a/drivers/platform/msm/Kconfig
+++ b/drivers/platform/msm/Kconfig
@@ -217,4 +217,11 @@ config USB_BAM
Enabling this option adds USB BAM Driver.
USB BAM driver was added to supports SPS Peripheral-to-Peripheral
transfers between the USB and other peripheral.
+
+config MSM_EXT_DISPLAY
+ bool "MSM External Display Driver"
+ help
+ Enabling this option adds MSM External Display Driver.
+ External Display driver was added to support the communication
+ between external display driver and its couterparts.
endmenu
diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile
index d5e87c209c21..d985aa81a3bb 100644
--- a/drivers/platform/msm/Makefile
+++ b/drivers/platform/msm/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_SEEMP_CORE) += seemp_core/
obj-$(CONFIG_SSM) += ssm.o
obj-$(CONFIG_USB_BAM) += usb_bam.o
obj-$(CONFIG_MSM_MHI_DEV) += mhi_dev/
+obj-$(CONFIG_MSM_EXT_DISPLAY) += msm_ext_display.o
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index 23b0428bcf34..f48182cc04df 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -22,7 +22,7 @@
#include "gsi_reg.h"
#define GSI_CMD_TIMEOUT (5*HZ)
-#define GSI_STOP_CMD_TIMEOUT_MS 1
+#define GSI_STOP_CMD_TIMEOUT_MS 10
#define GSI_MAX_CH_LOW_WEIGHT 15
#define GSI_MHI_ER_START 10
#define GSI_MHI_ER_END 16
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index fde087f07226..d4b0dd9910e1 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -1581,7 +1581,7 @@ bail:
static int ipa_init_smem_region(int memory_region_size,
int memory_region_offset)
{
- struct ipa_hw_imm_cmd_dma_shared_mem cmd;
+ struct ipa_hw_imm_cmd_dma_shared_mem *cmd = NULL;
struct ipa_desc desc;
struct ipa_mem_buffer mem;
int rc;
@@ -1590,7 +1590,6 @@ static int ipa_init_smem_region(int memory_region_size,
return 0;
memset(&desc, 0, sizeof(desc));
- memset(&cmd, 0, sizeof(cmd));
memset(&mem, 0, sizeof(mem));
mem.size = memory_region_size;
@@ -1602,13 +1601,22 @@ static int ipa_init_smem_region(int memory_region_size,
}
memset(mem.base, 0, mem.size);
- cmd.size = mem.size;
- cmd.system_addr = mem.phys_base;
- cmd.local_addr = ipa_ctx->smem_restricted_bytes +
+
+ cmd = kzalloc(sizeof(*cmd),
+ GFP_KERNEL);
+ if (cmd == NULL) {
+ IPAERR("Failed to alloc immediate command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
+ cmd->size = mem.size;
+ cmd->system_addr = mem.phys_base;
+ cmd->local_addr = ipa_ctx->smem_restricted_bytes +
memory_region_offset;
desc.opcode = IPA_DMA_SHARED_MEM;
- desc.pyld = &cmd;
- desc.len = sizeof(cmd);
+ desc.pyld = cmd;
+ desc.len = sizeof(*cmd);
desc.type = IPA_IMM_CMD_DESC;
rc = ipa_send_cmd(1, &desc);
@@ -1617,6 +1625,8 @@ static int ipa_init_smem_region(int memory_region_size,
rc = -EFAULT;
}
+ kfree(cmd);
+fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base,
mem.phys_base);
@@ -2153,7 +2163,7 @@ int _ipa_init_sram_v2(void)
{
u32 *ipa_sram_mmio;
unsigned long phys_addr;
- struct ipa_hw_imm_cmd_dma_shared_mem cmd = {0};
+ struct ipa_hw_imm_cmd_dma_shared_mem *cmd = NULL;
struct ipa_desc desc = {0};
struct ipa_mem_buffer mem;
int rc = 0;
@@ -2193,11 +2203,18 @@ int _ipa_init_sram_v2(void)
}
memset(mem.base, 0, mem.size);
- cmd.size = mem.size;
- cmd.system_addr = mem.phys_base;
- cmd.local_addr = IPA_STATUS_CLEAR_OFST;
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL) {
+ IPAERR("Failed to alloc immediate command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
+ cmd->size = mem.size;
+ cmd->system_addr = mem.phys_base;
+ cmd->local_addr = IPA_STATUS_CLEAR_OFST;
desc.opcode = IPA_DMA_SHARED_MEM;
- desc.pyld = &cmd;
+ desc.pyld = (void *)cmd;
desc.len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
desc.type = IPA_IMM_CMD_DESC;
@@ -2206,6 +2223,8 @@ int _ipa_init_sram_v2(void)
rc = -EFAULT;
}
+ kfree(cmd);
+fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
return rc;
}
@@ -2294,7 +2313,7 @@ int _ipa_init_hdr_v2(void)
{
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
- struct ipa_hdr_init_local cmd;
+ struct ipa_hdr_init_local *cmd = NULL;
int rc = 0;
mem.size = IPA_MEM_PART(modem_hdr_size) + IPA_MEM_PART(apps_hdr_size);
@@ -2306,13 +2325,20 @@ int _ipa_init_hdr_v2(void)
}
memset(mem.base, 0, mem.size);
- cmd.hdr_table_src_addr = mem.phys_base;
- cmd.size_hdr_table = mem.size;
- cmd.hdr_table_dst_addr = ipa_ctx->smem_restricted_bytes +
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL) {
+ IPAERR("Failed to alloc header init command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
+ cmd->hdr_table_src_addr = mem.phys_base;
+ cmd->size_hdr_table = mem.size;
+ cmd->hdr_table_dst_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(modem_hdr_ofst);
desc.opcode = IPA_HDR_INIT_LOCAL;
- desc.pyld = &cmd;
+ desc.pyld = (void *)cmd;
desc.len = sizeof(struct ipa_hdr_init_local);
desc.type = IPA_IMM_CMD_DESC;
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
@@ -2322,6 +2348,8 @@ int _ipa_init_hdr_v2(void)
rc = -EFAULT;
}
+ kfree(cmd);
+fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
return rc;
}
@@ -2330,8 +2358,8 @@ int _ipa_init_hdr_v2_5(void)
{
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
- struct ipa_hdr_init_local cmd = { 0 };
- struct ipa_hw_imm_cmd_dma_shared_mem dma_cmd = { 0 };
+ struct ipa_hdr_init_local *cmd = NULL;
+ struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd = NULL;
mem.size = IPA_MEM_PART(modem_hdr_size) + IPA_MEM_PART(apps_hdr_size);
mem.base = dma_alloc_coherent(ipa_ctx->pdev, mem.size, &mem.phys_base,
@@ -2342,25 +2370,34 @@ int _ipa_init_hdr_v2_5(void)
}
memset(mem.base, 0, mem.size);
- cmd.hdr_table_src_addr = mem.phys_base;
- cmd.size_hdr_table = mem.size;
- cmd.hdr_table_dst_addr = ipa_ctx->smem_restricted_bytes +
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL) {
+ IPAERR("Failed to alloc header init command object\n");
+ dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base,
+ mem.phys_base);
+ return -ENOMEM;
+ }
+
+ cmd->hdr_table_src_addr = mem.phys_base;
+ cmd->size_hdr_table = mem.size;
+ cmd->hdr_table_dst_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(modem_hdr_ofst);
desc.opcode = IPA_HDR_INIT_LOCAL;
- desc.pyld = &cmd;
+ desc.pyld = (void *)cmd;
desc.len = sizeof(struct ipa_hdr_init_local);
desc.type = IPA_IMM_CMD_DESC;
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
if (ipa_send_cmd(1, &desc)) {
IPAERR("fail to send immediate command\n");
- dma_free_coherent(ipa_ctx->pdev,
- mem.size, mem.base,
+ kfree(cmd);
+ dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base,
mem.phys_base);
return -EFAULT;
}
+ kfree(cmd);
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
mem.size = IPA_MEM_PART(modem_hdr_proc_ctx_size) +
@@ -2374,18 +2411,29 @@ int _ipa_init_hdr_v2_5(void)
memset(mem.base, 0, mem.size);
memset(&desc, 0, sizeof(desc));
- dma_cmd.system_addr = mem.phys_base;
- dma_cmd.local_addr = ipa_ctx->smem_restricted_bytes +
+ dma_cmd = kzalloc(sizeof(*dma_cmd), GFP_KERNEL);
+ if (dma_cmd == NULL) {
+ IPAERR("Failed to alloc immediate command object\n");
+ dma_free_coherent(ipa_ctx->pdev,
+ mem.size,
+ mem.base,
+ mem.phys_base);
+ return -ENOMEM;
+ }
+
+ dma_cmd->system_addr = mem.phys_base;
+ dma_cmd->local_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(modem_hdr_proc_ctx_ofst);
- dma_cmd.size = mem.size;
+ dma_cmd->size = mem.size;
desc.opcode = IPA_DMA_SHARED_MEM;
- desc.pyld = &dma_cmd;
+ desc.pyld = (void *)dma_cmd;
desc.len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
desc.type = IPA_IMM_CMD_DESC;
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
if (ipa_send_cmd(1, &desc)) {
IPAERR("fail to send immediate command\n");
+ kfree(dma_cmd);
dma_free_coherent(ipa_ctx->pdev,
mem.size,
mem.base,
@@ -2395,8 +2443,9 @@ int _ipa_init_hdr_v2_5(void)
ipa_write_reg(ipa_ctx->mmio,
IPA_LOCAL_PKT_PROC_CNTXT_BASE_OFST,
- dma_cmd.local_addr);
+ dma_cmd->local_addr);
+ kfree(dma_cmd);
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
return 0;
@@ -2412,7 +2461,7 @@ int _ipa_init_rt4_v2(void)
{
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
- struct ipa_ip_v4_routing_init v4_cmd;
+ struct ipa_ip_v4_routing_init *v4_cmd = NULL;
u32 *entry;
int i;
int rc = 0;
@@ -2437,15 +2486,22 @@ int _ipa_init_rt4_v2(void)
entry++;
}
+ v4_cmd = kzalloc(sizeof(*v4_cmd), GFP_KERNEL);
+ if (v4_cmd == NULL) {
+ IPAERR("Failed to alloc v4 routing init command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
desc.opcode = IPA_IP_V4_ROUTING_INIT;
- v4_cmd.ipv4_rules_addr = mem.phys_base;
- v4_cmd.size_ipv4_rules = mem.size;
- v4_cmd.ipv4_addr = ipa_ctx->smem_restricted_bytes +
+ v4_cmd->ipv4_rules_addr = mem.phys_base;
+ v4_cmd->size_ipv4_rules = mem.size;
+ v4_cmd->ipv4_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(v4_rt_ofst);
IPADBG("putting Routing IPv4 rules to phys 0x%x",
- v4_cmd.ipv4_addr);
+ v4_cmd->ipv4_addr);
- desc.pyld = &v4_cmd;
+ desc.pyld = (void *)v4_cmd;
desc.len = sizeof(struct ipa_ip_v4_routing_init);
desc.type = IPA_IMM_CMD_DESC;
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
@@ -2455,6 +2511,8 @@ int _ipa_init_rt4_v2(void)
rc = -EFAULT;
}
+ kfree(v4_cmd);
+fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
return rc;
}
@@ -2463,7 +2521,7 @@ int _ipa_init_rt6_v2(void)
{
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
- struct ipa_ip_v6_routing_init v6_cmd;
+ struct ipa_ip_v6_routing_init *v6_cmd = NULL;
u32 *entry;
int i;
int rc = 0;
@@ -2488,15 +2546,22 @@ int _ipa_init_rt6_v2(void)
entry++;
}
+ v6_cmd = kzalloc(sizeof(*v6_cmd), GFP_KERNEL);
+ if (v6_cmd == NULL) {
+ IPAERR("Failed to alloc v6 routing init command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
desc.opcode = IPA_IP_V6_ROUTING_INIT;
- v6_cmd.ipv6_rules_addr = mem.phys_base;
- v6_cmd.size_ipv6_rules = mem.size;
- v6_cmd.ipv6_addr = ipa_ctx->smem_restricted_bytes +
+ v6_cmd->ipv6_rules_addr = mem.phys_base;
+ v6_cmd->size_ipv6_rules = mem.size;
+ v6_cmd->ipv6_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(v6_rt_ofst);
IPADBG("putting Routing IPv6 rules to phys 0x%x",
- v6_cmd.ipv6_addr);
+ v6_cmd->ipv6_addr);
- desc.pyld = &v6_cmd;
+ desc.pyld = (void *)v6_cmd;
desc.len = sizeof(struct ipa_ip_v6_routing_init);
desc.type = IPA_IMM_CMD_DESC;
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
@@ -2506,6 +2571,8 @@ int _ipa_init_rt6_v2(void)
rc = -EFAULT;
}
+ kfree(v6_cmd);
+fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
return rc;
}
@@ -2514,7 +2581,7 @@ int _ipa_init_flt4_v2(void)
{
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
- struct ipa_ip_v4_filter_init v4_cmd;
+ struct ipa_ip_v4_filter_init *v4_cmd = NULL;
u32 *entry;
int i;
int rc = 0;
@@ -2537,15 +2604,22 @@ int _ipa_init_flt4_v2(void)
entry++;
}
+ v4_cmd = kzalloc(sizeof(*v4_cmd), GFP_KERNEL);
+ if (v4_cmd == NULL) {
+ IPAERR("Failed to alloc v4 fliter init command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
desc.opcode = IPA_IP_V4_FILTER_INIT;
- v4_cmd.ipv4_rules_addr = mem.phys_base;
- v4_cmd.size_ipv4_rules = mem.size;
- v4_cmd.ipv4_addr = ipa_ctx->smem_restricted_bytes +
+ v4_cmd->ipv4_rules_addr = mem.phys_base;
+ v4_cmd->size_ipv4_rules = mem.size;
+ v4_cmd->ipv4_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(v4_flt_ofst);
IPADBG("putting Filtering IPv4 rules to phys 0x%x",
- v4_cmd.ipv4_addr);
+ v4_cmd->ipv4_addr);
- desc.pyld = &v4_cmd;
+ desc.pyld = (void *)v4_cmd;
desc.len = sizeof(struct ipa_ip_v4_filter_init);
desc.type = IPA_IMM_CMD_DESC;
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
@@ -2555,6 +2629,8 @@ int _ipa_init_flt4_v2(void)
rc = -EFAULT;
}
+ kfree(v4_cmd);
+fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
return rc;
}
@@ -2563,7 +2639,7 @@ int _ipa_init_flt6_v2(void)
{
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
- struct ipa_ip_v6_filter_init v6_cmd;
+ struct ipa_ip_v6_filter_init *v6_cmd = NULL;
u32 *entry;
int i;
int rc = 0;
@@ -2586,15 +2662,22 @@ int _ipa_init_flt6_v2(void)
entry++;
}
+ v6_cmd = kzalloc(sizeof(*v6_cmd), GFP_KERNEL);
+ if (v6_cmd == NULL) {
+ IPAERR("Failed to alloc v6 fliter init command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
desc.opcode = IPA_IP_V6_FILTER_INIT;
- v6_cmd.ipv6_rules_addr = mem.phys_base;
- v6_cmd.size_ipv6_rules = mem.size;
- v6_cmd.ipv6_addr = ipa_ctx->smem_restricted_bytes +
+ v6_cmd->ipv6_rules_addr = mem.phys_base;
+ v6_cmd->size_ipv6_rules = mem.size;
+ v6_cmd->ipv6_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(v6_flt_ofst);
IPADBG("putting Filtering IPv6 rules to phys 0x%x",
- v6_cmd.ipv6_addr);
+ v6_cmd->ipv6_addr);
- desc.pyld = &v6_cmd;
+ desc.pyld = (void *)v6_cmd;
desc.len = sizeof(struct ipa_ip_v6_filter_init);
desc.type = IPA_IMM_CMD_DESC;
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
@@ -2604,6 +2687,8 @@ int _ipa_init_flt6_v2(void)
rc = -EFAULT;
}
+ kfree(v6_cmd);
+fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
return rc;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
index 51f34f0d5101..e23de3f26613 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
@@ -266,8 +266,8 @@ int __ipa_commit_hdr_v2(void)
{
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
- struct ipa_hdr_init_system cmd;
- struct ipa_hw_imm_cmd_dma_shared_mem dma_cmd;
+ struct ipa_hdr_init_system *cmd = NULL;
+ struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd = NULL;
int rc = -EFAULT;
if (ipa_generate_hdr_hw_tbl(&mem)) {
@@ -279,14 +279,21 @@ int __ipa_commit_hdr_v2(void)
if (mem.size > IPA_MEM_PART(apps_hdr_size)) {
IPAERR("tbl too big, needed %d avail %d\n", mem.size,
IPA_MEM_PART(apps_hdr_size));
- goto end;
+ goto fail_send_cmd;
} else {
- dma_cmd.system_addr = mem.phys_base;
- dma_cmd.size = mem.size;
- dma_cmd.local_addr = ipa_ctx->smem_restricted_bytes +
+ dma_cmd = kzalloc(sizeof(*dma_cmd), GFP_ATOMIC);
+ if (dma_cmd == NULL) {
+ IPAERR("fail to alloc immediate cmd\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
+ dma_cmd->system_addr = mem.phys_base;
+ dma_cmd->size = mem.size;
+ dma_cmd->local_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(apps_hdr_ofst);
desc.opcode = IPA_DMA_SHARED_MEM;
- desc.pyld = &dma_cmd;
+ desc.pyld = (void *)dma_cmd;
desc.len =
sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
}
@@ -294,11 +301,17 @@ int __ipa_commit_hdr_v2(void)
if (mem.size > IPA_MEM_PART(apps_hdr_size_ddr)) {
IPAERR("tbl too big, needed %d avail %d\n", mem.size,
IPA_MEM_PART(apps_hdr_size_ddr));
- goto end;
+ goto fail_send_cmd;
} else {
- cmd.hdr_table_addr = mem.phys_base;
+ cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
+ if (cmd == NULL) {
+ IPAERR("fail to alloc hdr init cmd\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+ cmd->hdr_table_addr = mem.phys_base;
desc.opcode = IPA_HDR_INIT_SYSTEM;
- desc.pyld = &cmd;
+ desc.pyld = (void *)cmd;
desc.len = sizeof(struct ipa_hdr_init_system);
}
}
@@ -311,6 +324,10 @@ int __ipa_commit_hdr_v2(void)
else
rc = 0;
+ kfree(dma_cmd);
+ kfree(cmd);
+
+fail_send_cmd:
if (ipa_ctx->hdr_tbl_lcl) {
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base,
mem.phys_base);
@@ -322,6 +339,9 @@ int __ipa_commit_hdr_v2(void)
ipa_ctx->hdr_mem.base,
ipa_ctx->hdr_mem.phys_base);
ipa_ctx->hdr_mem = mem;
+ } else {
+ dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base,
+ mem.phys_base);
}
}
@@ -335,10 +355,10 @@ int __ipa_commit_hdr_v2_5(void)
struct ipa_mem_buffer hdr_mem;
struct ipa_mem_buffer ctx_mem;
struct ipa_mem_buffer aligned_ctx_mem;
- struct ipa_hdr_init_system hdr_init_cmd = {0};
- struct ipa_hw_imm_cmd_dma_shared_mem dma_cmd_hdr = {0};
- struct ipa_hw_imm_cmd_dma_shared_mem dma_cmd_ctx = {0};
- struct ipa_register_write reg_write_cmd = {0};
+ struct ipa_hdr_init_system *hdr_init_cmd = NULL;
+ struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd_hdr = NULL;
+ struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd_ctx = NULL;
+ struct ipa_register_write *reg_write_cmd = NULL;
int rc = -EFAULT;
u32 proc_ctx_size;
u32 proc_ctx_ofst;
@@ -361,15 +381,21 @@ int __ipa_commit_hdr_v2_5(void)
if (hdr_mem.size > IPA_MEM_PART(apps_hdr_size)) {
IPAERR("tbl too big needed %d avail %d\n", hdr_mem.size,
IPA_MEM_PART(apps_hdr_size));
- goto end;
+ goto fail_send_cmd1;
} else {
- dma_cmd_hdr.system_addr = hdr_mem.phys_base;
- dma_cmd_hdr.size = hdr_mem.size;
- dma_cmd_hdr.local_addr =
+ dma_cmd_hdr = kzalloc(sizeof(*dma_cmd_hdr), GFP_ATOMIC);
+ if (dma_cmd_hdr == NULL) {
+ IPAERR("fail to alloc immediate cmd\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd1;
+ }
+ dma_cmd_hdr->system_addr = hdr_mem.phys_base;
+ dma_cmd_hdr->size = hdr_mem.size;
+ dma_cmd_hdr->local_addr =
ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(apps_hdr_ofst);
desc[0].opcode = IPA_DMA_SHARED_MEM;
- desc[0].pyld = &dma_cmd_hdr;
+ desc[0].pyld = (void *)dma_cmd_hdr;
desc[0].len =
sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
}
@@ -377,11 +403,18 @@ int __ipa_commit_hdr_v2_5(void)
if (hdr_mem.size > IPA_MEM_PART(apps_hdr_size_ddr)) {
IPAERR("tbl too big needed %d avail %d\n", hdr_mem.size,
IPA_MEM_PART(apps_hdr_size_ddr));
- goto end;
+ goto fail_send_cmd1;
} else {
- hdr_init_cmd.hdr_table_addr = hdr_mem.phys_base;
+ hdr_init_cmd = kzalloc(sizeof(*hdr_init_cmd),
+ GFP_ATOMIC);
+ if (hdr_init_cmd == NULL) {
+ IPAERR("fail to alloc immediate cmd\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd1;
+ }
+ hdr_init_cmd->hdr_table_addr = hdr_mem.phys_base;
desc[0].opcode = IPA_HDR_INIT_SYSTEM;
- desc[0].pyld = &hdr_init_cmd;
+ desc[0].pyld = (void *)hdr_init_cmd;
desc[0].len = sizeof(struct ipa_hdr_init_system);
}
}
@@ -395,15 +428,22 @@ int __ipa_commit_hdr_v2_5(void)
IPAERR("tbl too big needed %d avail %d\n",
aligned_ctx_mem.size,
proc_ctx_size);
- goto end;
+ goto fail_send_cmd1;
} else {
- dma_cmd_ctx.system_addr = aligned_ctx_mem.phys_base;
- dma_cmd_ctx.size = aligned_ctx_mem.size;
- dma_cmd_ctx.local_addr =
+ dma_cmd_ctx = kzalloc(sizeof(*dma_cmd_ctx),
+ GFP_ATOMIC);
+ if (dma_cmd_ctx == NULL) {
+ IPAERR("fail to alloc immediate cmd\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd1;
+ }
+ dma_cmd_ctx->system_addr = aligned_ctx_mem.phys_base;
+ dma_cmd_ctx->size = aligned_ctx_mem.size;
+ dma_cmd_ctx->local_addr =
ipa_ctx->smem_restricted_bytes +
proc_ctx_ofst;
desc[1].opcode = IPA_DMA_SHARED_MEM;
- desc[1].pyld = &dma_cmd_ctx;
+ desc[1].pyld = (void *)dma_cmd_ctx;
desc[1].len =
sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
}
@@ -413,15 +453,23 @@ int __ipa_commit_hdr_v2_5(void)
IPAERR("tbl too big, needed %d avail %d\n",
aligned_ctx_mem.size,
proc_ctx_size_ddr);
- goto end;
+ goto fail_send_cmd1;
} else {
- reg_write_cmd.offset = IPA_SYS_PKT_PROC_CNTXT_BASE_OFST;
- reg_write_cmd.value = aligned_ctx_mem.phys_base;
- reg_write_cmd.value_mask =
+ reg_write_cmd = kzalloc(sizeof(*reg_write_cmd),
+ GFP_ATOMIC);
+ if (reg_write_cmd == NULL) {
+ IPAERR("fail to alloc immediate cmd\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd1;
+ }
+ reg_write_cmd->offset =
+ IPA_SYS_PKT_PROC_CNTXT_BASE_OFST;
+ reg_write_cmd->value = aligned_ctx_mem.phys_base;
+ reg_write_cmd->value_mask =
~(IPA_HDR_PROC_CTX_TABLE_ALIGNMENT_BYTE - 1);
- desc[1].pyld = &reg_write_cmd;
+ desc[1].pyld = (void *)reg_write_cmd;
desc[1].opcode = IPA_REGISTER_WRITE;
- desc[1].len = sizeof(reg_write_cmd);
+ desc[1].len = sizeof(*reg_write_cmd);
}
}
desc[1].type = IPA_IMM_CMD_DESC;
@@ -432,22 +480,16 @@ int __ipa_commit_hdr_v2_5(void)
else
rc = 0;
- if (ipa_ctx->hdr_tbl_lcl) {
- dma_free_coherent(ipa_ctx->pdev, hdr_mem.size, hdr_mem.base,
- hdr_mem.phys_base);
- } else {
- if (!rc) {
- if (ipa_ctx->hdr_mem.phys_base)
- dma_free_coherent(ipa_ctx->pdev,
- ipa_ctx->hdr_mem.size,
- ipa_ctx->hdr_mem.base,
- ipa_ctx->hdr_mem.phys_base);
- ipa_ctx->hdr_mem = hdr_mem;
- }
- }
+fail_send_cmd1:
+
+ kfree(dma_cmd_hdr);
+ kfree(hdr_init_cmd);
+ kfree(dma_cmd_ctx);
+ kfree(reg_write_cmd);
if (ipa_ctx->hdr_proc_ctx_tbl_lcl) {
- dma_free_coherent(ipa_ctx->pdev, ctx_mem.size, ctx_mem.base,
+ dma_free_coherent(ipa_ctx->pdev, ctx_mem.size,
+ ctx_mem.base,
ctx_mem.phys_base);
} else {
if (!rc) {
@@ -457,9 +499,31 @@ int __ipa_commit_hdr_v2_5(void)
ipa_ctx->hdr_proc_ctx_mem.base,
ipa_ctx->hdr_proc_ctx_mem.phys_base);
ipa_ctx->hdr_proc_ctx_mem = ctx_mem;
+ } else {
+ dma_free_coherent(ipa_ctx->pdev, ctx_mem.size,
+ ctx_mem.base,
+ ctx_mem.phys_base);
}
}
+ if (ipa_ctx->hdr_tbl_lcl) {
+ dma_free_coherent(ipa_ctx->pdev, hdr_mem.size,
+ hdr_mem.base,
+ hdr_mem.phys_base);
+ } else {
+ if (!rc) {
+ if (ipa_ctx->hdr_mem.phys_base)
+ dma_free_coherent(ipa_ctx->pdev,
+ ipa_ctx->hdr_mem.size,
+ ipa_ctx->hdr_mem.base,
+ ipa_ctx->hdr_mem.phys_base);
+ ipa_ctx->hdr_mem = hdr_mem;
+ } else {
+ dma_free_coherent(ipa_ctx->pdev, hdr_mem.size,
+ hdr_mem.base,
+ hdr_mem.phys_base);
+ }
+ }
end:
return rc;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
index 069c5cbcf4f3..f5dea76764f8 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
@@ -696,8 +696,8 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip)
struct ipa_desc desc[2];
struct ipa_mem_buffer body;
struct ipa_mem_buffer head;
- struct ipa_hw_imm_cmd_dma_shared_mem cmd1 = {0};
- struct ipa_hw_imm_cmd_dma_shared_mem cmd2 = {0};
+ struct ipa_hw_imm_cmd_dma_shared_mem *cmd1 = NULL;
+ struct ipa_hw_imm_cmd_dma_shared_mem *cmd2 = NULL;
u16 avail;
u32 num_modem_rt_index;
int rc = 0;
@@ -747,34 +747,50 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip)
goto fail_send_cmd;
}
- cmd1.size = head.size;
- cmd1.system_addr = head.phys_base;
- cmd1.local_addr = local_addr1;
+ cmd1 = kzalloc(sizeof(struct ipa_hw_imm_cmd_dma_shared_mem),
+ GFP_KERNEL);
+ if (cmd1 == NULL) {
+ IPAERR("Failed to alloc immediate command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
+ cmd1->size = head.size;
+ cmd1->system_addr = head.phys_base;
+ cmd1->local_addr = local_addr1;
desc[0].opcode = IPA_DMA_SHARED_MEM;
- desc[0].pyld = &cmd1;
+ desc[0].pyld = (void *)cmd1;
desc[0].len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
desc[0].type = IPA_IMM_CMD_DESC;
if (lcl) {
- cmd2.size = body.size;
- cmd2.system_addr = body.phys_base;
- cmd2.local_addr = local_addr2;
+ cmd2 = kzalloc(sizeof(struct ipa_hw_imm_cmd_dma_shared_mem),
+ GFP_KERNEL);
+ if (cmd2 == NULL) {
+ IPAERR("Failed to alloc immediate command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd1;
+ }
+
+ cmd2->size = body.size;
+ cmd2->system_addr = body.phys_base;
+ cmd2->local_addr = local_addr2;
desc[1].opcode = IPA_DMA_SHARED_MEM;
- desc[1].pyld = &cmd2;
+ desc[1].pyld = (void *)cmd2;
desc[1].len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
desc[1].type = IPA_IMM_CMD_DESC;
if (ipa_send_cmd(2, desc)) {
IPAERR("fail to send immediate command\n");
rc = -EFAULT;
- goto fail_send_cmd;
+ goto fail_send_cmd2;
}
} else {
if (ipa_send_cmd(1, desc)) {
IPAERR("fail to send immediate command\n");
rc = -EFAULT;
- goto fail_send_cmd;
+ goto fail_send_cmd1;
}
}
@@ -785,6 +801,11 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip)
IPA_DUMP_BUFF(body.base, body.phys_base, body.size);
}
__ipa_reap_sys_rt_tbls(ip);
+
+fail_send_cmd2:
+ kfree(cmd2);
+fail_send_cmd1:
+ kfree(cmd1);
fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, head.size, head.base, head.phys_base);
if (body.size)
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index e3dfe8927682..81eae05d7ed9 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -1777,7 +1777,8 @@ dealloc_chan_fail:
int ipa3_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
bool should_force_clear, u32 qmi_req_id, bool is_dpl)
{
- struct ipa3_ep_context *ul_ep, *dl_ep;
+ struct ipa3_ep_context *ul_ep = NULL;
+ struct ipa3_ep_context *dl_ep;
int result = -EFAULT;
u32 source_pipe_bitmask = 0;
bool dl_data_pending = true;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c
index 483b2ca118fa..06f65906841d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -853,6 +853,10 @@ void ipa3_dma_async_memcpy_notify_cb(void *priv
mem_info = (struct ipa_mem_buffer *)data;
ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_MEMCPY_DMA_ASYNC_CONS);
+ if (ep_idx < 0) {
+ IPADMA_ERR("IPA Client mapping failed\n");
+ return;
+ }
sys = ipa3_ctx->ep[ep_idx].sys;
spin_lock_irqsave(&ipa3_dma_ctx->async_lock, flags);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 5c678f1cfc28..8ec0974711a4 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -337,7 +337,7 @@ int ipa3_send_one(struct ipa3_sys_context *sys, struct ipa3_desc *desc,
int result;
u16 sps_flags = SPS_IOVEC_FLAG_EOT;
dma_addr_t dma_address;
- u16 len;
+ u16 len = 0;
u32 mem_flag = GFP_ATOMIC;
if (unlikely(!in_atomic))
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
index c3a12dd0b17c..362294b0f695 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
@@ -14,7 +14,6 @@
#include "ipahal/ipahal.h"
#include "ipahal/ipahal_fltrt.h"
-#define IPA_FLT_TABLE_INDEX_NOT_FOUND (-1)
#define IPA_FLT_STATUS_OF_ADD_FAILED (-1)
#define IPA_FLT_STATUS_OF_DEL_FAILED (-1)
#define IPA_FLT_STATUS_OF_MDFY_FAILED (-1)
@@ -1001,7 +1000,7 @@ error:
static int __ipa_add_flt_get_ep_idx(enum ipa_client_type ep, int *ipa_ep_idx)
{
*ipa_ep_idx = ipa3_get_ep_mapping(ep);
- if (*ipa_ep_idx == IPA_FLT_TABLE_INDEX_NOT_FOUND) {
+ if (*ipa_ep_idx < 0) {
IPAERR("ep not valid ep=%d\n", ep);
return -EINVAL;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
index 4ef1a96c8450..9e2ffe70170c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, 2016 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -153,10 +153,16 @@ int ipa3_mhi_reset_channel_internal(enum ipa_client_type client)
int ipa3_mhi_start_channel_internal(enum ipa_client_type client)
{
int res;
+ int ipa_ep_idx;
IPA_MHI_FUNC_ENTRY();
- res = ipa3_enable_data_path(ipa3_get_ep_mapping(client));
+ ipa_ep_idx = ipa3_get_ep_mapping(client);
+ if (ipa_ep_idx < 0) {
+ IPA_MHI_ERR("Invalid client %d\n", client);
+ return -EINVAL;
+ }
+ res = ipa3_enable_data_path(ipa_ep_idx);
if (res) {
IPA_MHI_ERR("ipa3_enable_data_path failed %d\n", res);
return res;
@@ -521,6 +527,10 @@ int ipa3_mhi_resume_channels_internal(enum ipa_client_type client,
IPA_MHI_FUNC_ENTRY();
ipa_ep_idx = ipa3_get_ep_mapping(client);
+ if (ipa_ep_idx < 0) {
+ IPA_MHI_ERR("Invalid client %d\n", client);
+ return -EINVAL;
+ }
ep = &ipa3_ctx->ep[ipa_ep_idx];
if (brstmode_enabled && !LPTransitionRejected) {
@@ -557,11 +567,14 @@ int ipa3_mhi_query_ch_info(enum ipa_client_type client,
IPA_MHI_FUNC_ENTRY();
ipa_ep_idx = ipa3_get_ep_mapping(client);
-
+ if (ipa_ep_idx < 0) {
+ IPA_MHI_ERR("Invalid client %d\n", client);
+ return -EINVAL;
+ }
ep = &ipa3_ctx->ep[ipa_ep_idx];
res = gsi_query_channel_info(ep->gsi_chan_hdl, ch_info);
if (res) {
- IPAERR("gsi_query_channel_info failed\n");
+ IPA_MHI_ERR("gsi_query_channel_info failed\n");
return res;
}
@@ -596,7 +609,10 @@ int ipa3_mhi_destroy_channel(enum ipa_client_type client)
struct ipa3_ep_context *ep;
ipa_ep_idx = ipa3_get_ep_mapping(client);
-
+ if (ipa_ep_idx < 0) {
+ IPA_MHI_ERR("Invalid client %d\n", client);
+ return -EINVAL;
+ }
ep = &ipa3_ctx->ep[ipa_ep_idx];
IPA_MHI_DBG("reset event ring (hdl: %lu, ep: %d)\n",
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c
index 21ce28204069..c1d1d9659850 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -498,7 +498,7 @@ static int ipa3_uc_send_cmd_64b_param(u32 cmd_lo, u32 cmd_hi, u32 opcode,
{
int index;
union IpaHwCpuCmdCompletedResponseData_t uc_rsp;
- unsigned long flags;
+ unsigned long flags = 0;
int retries = 0;
send_cmd_lock:
@@ -775,7 +775,7 @@ int ipa3_uc_send_cmd(u32 cmd, u32 opcode, u32 expected_status,
void ipa3_uc_register_handlers(enum ipa3_hw_features feature,
struct ipa3_uc_hdlrs *hdlrs)
{
- unsigned long flags;
+ unsigned long flags = 0;
if (0 > feature || IPA_HW_FEATURE_MAX <= feature) {
IPAERR("Feature %u is invalid, not registering hdlrs\n",
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index 9f38af1b520b..2f28ba673d5a 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -929,7 +929,7 @@ int ipa3_get_ep_mapping(enum ipa_client_type client)
if (client >= IPA_CLIENT_MAX || client < 0) {
IPAERR("Bad client number! client =%d\n", client);
- return -EINVAL;
+ return IPA_EP_NOT_ALLOCATED;
}
ipa_ep_idx = ipa3_ep_mapping[ipa3_get_hw_type_index()][client].pipe_num;
@@ -3446,6 +3446,11 @@ void ipa3_suspend_apps_pipes(bool suspend)
cfg.ipa_ep_suspend = suspend;
ipa_ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_CONS);
+ if (ipa_ep_idx < 0) {
+ IPAERR("IPA client mapping failed\n");
+ ipa_assert();
+ return;
+ }
ep = &ipa3_ctx->ep[ipa_ep_idx];
if (ep->valid) {
IPADBG("%s pipe %d\n", suspend ? "suspend" : "unsuspend",
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
index 154045fe4f56..8f85d4ede6b1 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
@@ -322,11 +322,13 @@ struct ipahal_imm_cmd_dma_task_32b_addr {
/*
* struct ipahal_imm_cmd_pyld - Immediate cmd payload information
* @len: length of the buffer
+ * @reserved: padding bytes to make data buffer aligned
* @data: buffer contains the immediate command payload. Buffer goes
* back to back with this structure
*/
struct ipahal_imm_cmd_pyld {
u16 len;
+ u16 reserved;
u8 data[0];
};
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
index 72cc4764e7aa..053a581b1d26 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
@@ -2728,7 +2728,7 @@ static int ipa_fltrt_alloc_init_tbl_hdr(
params->nhash_hdr.base = dma_alloc_coherent(ipahal_ctx->ipa_pdev,
params->nhash_hdr.size,
&params->nhash_hdr.phys_base, GFP_KERNEL);
- if (!params->nhash_hdr.size) {
+ if (!params->nhash_hdr.base) {
IPAHAL_ERR("fail to alloc DMA buff of size %d\n",
params->nhash_hdr.size);
goto nhash_alloc_fail;
diff --git a/drivers/platform/msm/mhi_uci/mhi_uci.c b/drivers/platform/msm/mhi_uci/mhi_uci.c
index 88a213b1b241..96c4671f994f 100644
--- a/drivers/platform/msm/mhi_uci/mhi_uci.c
+++ b/drivers/platform/msm/mhi_uci/mhi_uci.c
@@ -27,18 +27,17 @@
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/errno.h>
+#include <linux/of_device.h>
#define MHI_DEV_NODE_NAME_LEN 13
-#define MHI_MAX_NR_OF_CLIENTS 23
-#define MHI_SOFTWARE_CLIENT_START 0
#define MHI_SOFTWARE_CLIENT_LIMIT 23
-#define MHI_MAX_SOFTWARE_CHANNELS 46
#define TRE_TYPICAL_SIZE 0x1000
#define TRE_MAX_SIZE 0xFFFF
-#define MHI_UCI_IPC_LOG_PAGES (100)
+#define MHI_UCI_IPC_LOG_PAGES (25)
#define MAX_NR_TRBS_PER_CHAN 10
#define DEVICE_NAME "mhi"
+#define MHI_UCI_DRIVER_NAME "mhi_uci"
#define CTRL_MAGIC 0x4C525443
enum UCI_DBG_LEVEL {
@@ -58,8 +57,6 @@ enum UCI_DBG_LEVEL mhi_uci_ipc_log_lvl = UCI_DBG_VERBOSE;
enum UCI_DBG_LEVEL mhi_uci_ipc_log_lvl = UCI_DBG_ERROR;
#endif
-void *mhi_uci_ipc_log;
-
struct __packed rs232_ctrl_msg {
u32 preamble;
u32 msg_id;
@@ -103,11 +100,12 @@ struct chan_attr {
};
struct uci_client {
- u32 client_index;
u32 out_chan;
u32 in_chan;
u32 out_chan_state;
u32 in_chan_state;
+ struct chan_attr in_attr;
+ struct chan_attr out_attr;
struct mhi_client_handle *out_handle;
struct mhi_client_handle *in_handle;
size_t pending_data;
@@ -126,22 +124,29 @@ struct uci_client {
struct mhi_uci_ctxt_t *uci_ctxt;
struct mutex in_chan_lock;
struct mutex out_chan_lock;
+ void *uci_ipc_log;
};
struct mhi_uci_ctxt_t {
- struct chan_attr chan_attrib[MHI_MAX_SOFTWARE_CHANNELS];
+ struct list_head node;
+ struct platform_dev *pdev;
struct uci_client client_handles[MHI_SOFTWARE_CLIENT_LIMIT];
struct mhi_client_info_t client_info;
- dev_t start_ctrl_nr;
- struct mhi_client_handle *ctrl_handle;
+ dev_t dev_t;
struct mutex ctrl_mutex;
- struct cdev cdev[MHI_MAX_SOFTWARE_CHANNELS];
- struct class *mhi_uci_class;
- u32 ctrl_chan_id;
+ struct cdev cdev[MHI_SOFTWARE_CLIENT_LIMIT];
+ struct uci_client *ctrl_client;
atomic_t mhi_disabled;
atomic_t mhi_enable_notif_wq_active;
};
+struct mhi_uci_drv_ctxt {
+ struct list_head head;
+ struct mutex list_lock;
+ struct class *mhi_uci_class;
+ void *mhi_uci_ipc_log;
+};
+
#define CHAN_TO_CLIENT(_CHAN_NR) (_CHAN_NR / 2)
#define CTRL_MSG_ID
@@ -219,13 +224,13 @@ struct mhi_uci_ctxt_t {
(_PKT)->size = new_val; \
};
-#define uci_log(_msg_lvl, _msg, ...) do { \
+#define uci_log(uci_ipc_log, _msg_lvl, _msg, ...) do { \
if (_msg_lvl >= mhi_uci_msg_lvl) { \
pr_err("[%s] "_msg, __func__, ##__VA_ARGS__); \
} \
- if (mhi_uci_ipc_log && (_msg_lvl >= mhi_uci_ipc_log_lvl)) { \
- ipc_log_string(mhi_uci_ipc_log, \
- "[%s] " _msg, __func__, ##__VA_ARGS__); \
+ if (uci_ipc_log && (_msg_lvl >= mhi_uci_ipc_log_lvl)) { \
+ ipc_log_string(uci_ipc_log, \
+ "[%s] " _msg, __func__, ##__VA_ARGS__); \
} \
} while (0)
@@ -246,20 +251,20 @@ static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait);
static long mhi_uci_ctl_ioctl(struct file *file, unsigned int cmd,
unsigned long arg);
-static struct mhi_uci_ctxt_t uci_ctxt;
+static struct mhi_uci_drv_ctxt mhi_uci_drv_ctxt;
-static int mhi_init_inbound(struct uci_client *client_handle,
- enum MHI_CLIENT_CHANNEL chan)
+static int mhi_init_inbound(struct uci_client *client_handle)
{
int ret_val = 0;
u32 i = 0;
- struct chan_attr *chan_attributes =
- &uci_ctxt.chan_attrib[chan];
+ struct chan_attr *chan_attributes = &client_handle->in_attr;
void *data_loc = NULL;
size_t buf_size = chan_attributes->max_packet_size;
if (client_handle == NULL) {
- uci_log(UCI_DBG_ERROR, "Bad Input data, quitting\n");
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_ERROR,
+ "Bad Input data, quitting\n");
return -EINVAL;
}
chan_attributes->nr_trbs =
@@ -270,12 +275,17 @@ static int mhi_init_inbound(struct uci_client *client_handle,
if (!client_handle->in_buf_list)
return -ENOMEM;
- uci_log(UCI_DBG_INFO, "Channel %d supports %d desc\n",
- i, chan_attributes->nr_trbs);
+ uci_log(client_handle->uci_ipc_log,
+ UCI_DBG_INFO, "Channel %d supports %d desc\n",
+ chan_attributes->chan_id,
+ chan_attributes->nr_trbs);
for (i = 0; i < chan_attributes->nr_trbs; ++i) {
data_loc = kmalloc(buf_size, GFP_KERNEL);
- uci_log(UCI_DBG_INFO, "Allocated buffer %p size %zd\n",
- data_loc, buf_size);
+ uci_log(client_handle->uci_ipc_log,
+ UCI_DBG_INFO,
+ "Allocated buffer %p size %zd\n",
+ data_loc,
+ buf_size);
if (data_loc == NULL)
return -ENOMEM;
client_handle->in_buf_list[i] = data_loc;
@@ -283,9 +293,11 @@ static int mhi_init_inbound(struct uci_client *client_handle,
data_loc, buf_size, MHI_EOT);
if (0 != ret_val) {
kfree(data_loc);
- uci_log(UCI_DBG_ERROR,
+ uci_log(client_handle->uci_ipc_log,
+ UCI_DBG_ERROR,
"Failed insertion for chan %d, ret %d\n",
- chan, ret_val);
+ chan_attributes->chan_id,
+ ret_val);
break;
}
}
@@ -325,7 +337,8 @@ static int mhi_uci_send_packet(struct mhi_client_handle **client_handle,
if (is_uspace_buf) {
data_loc = kmalloc(data_to_insert_now, GFP_KERNEL);
if (NULL == data_loc) {
- uci_log(UCI_DBG_ERROR,
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
"Failed to allocate memory 0x%zx\n",
data_to_insert_now);
return -ENOMEM;
@@ -343,13 +356,15 @@ static int mhi_uci_send_packet(struct mhi_client_handle **client_handle,
flags = MHI_EOT;
if (data_left_to_insert - data_to_insert_now > 0)
flags |= MHI_CHAIN | MHI_EOB;
- uci_log(UCI_DBG_VERBOSE,
- "At trb i = %d/%d, chain = %d, eob = %d, addr 0x%p chan %d\n",
- i, nr_avail_trbs,
- flags & MHI_CHAIN,
- flags & MHI_EOB,
- data_loc,
- uci_handle->out_chan);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
+ "At trb i = %d/%d, chain = %d, eob = %d, addr 0x%p chan %d\n",
+ i,
+ nr_avail_trbs,
+ flags & MHI_CHAIN,
+ flags & MHI_EOB,
+ data_loc,
+ uci_handle->out_chan);
ret_val = mhi_queue_xfer(*client_handle, data_loc,
data_to_insert_now, flags);
@@ -375,25 +390,34 @@ static int mhi_uci_send_status_cmd(struct uci_client *client)
{
struct rs232_ctrl_msg *rs232_pkt = NULL;
struct uci_client *uci_ctrl_handle;
- u32 ctrl_chan_id = uci_ctxt.ctrl_chan_id;
-
+ struct mhi_uci_ctxt_t *uci_ctxt = client->uci_ctxt;
int ret_val = 0;
size_t pkt_size = sizeof(struct rs232_ctrl_msg);
u32 amount_sent;
- uci_ctrl_handle = &uci_ctxt.client_handles[ctrl_chan_id/2];
+ if (!uci_ctxt->ctrl_client) {
+ uci_log(client->uci_ipc_log,
+ UCI_DBG_INFO,
+ "Control channel is not defined\n");
+ return -EIO;
+ }
+
+ uci_ctrl_handle = uci_ctxt->ctrl_client;
mutex_lock(&uci_ctrl_handle->out_chan_lock);
if (!atomic_read(&uci_ctrl_handle->mhi_disabled) &&
!uci_ctrl_handle->out_chan_state) {
- uci_log(UCI_DBG_INFO,
- "Opening outbound control channel %d\n",
- uci_ctrl_handle->out_chan);
+ uci_log(uci_ctrl_handle->uci_ipc_log,
+ UCI_DBG_INFO,
+ "Opening outbound control channel %d for chan:%d\n",
+ uci_ctrl_handle->out_chan,
+ client->out_chan);
ret_val = mhi_open_channel(uci_ctrl_handle->out_handle);
if (0 != ret_val) {
- uci_log(UCI_DBG_CRITICAL,
+ uci_log(uci_ctrl_handle->uci_ipc_log,
+ UCI_DBG_CRITICAL,
"Could not open chan %d, for sideband ctrl\n",
- client->out_chan);
+ uci_ctrl_handle->out_chan);
ret_val = -EIO;
goto error_open;
}
@@ -405,7 +429,8 @@ static int mhi_uci_send_status_cmd(struct uci_client *client)
ret_val = -ENOMEM;
goto error_open;
}
- uci_log(UCI_DBG_VERBOSE,
+ uci_log(uci_ctrl_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
"Received request to send msg for chan %d\n",
client->out_chan);
rs232_pkt->preamble = CTRL_MAGIC;
@@ -423,9 +448,11 @@ static int mhi_uci_send_status_cmd(struct uci_client *client)
pkt_size, 0);
if (pkt_size != amount_sent) {
- uci_log(UCI_DBG_INFO,
- "Failed to send signal on chan %d, ret : %d\n",
- client->out_chan, ret_val);
+ uci_log(uci_ctrl_handle->uci_ipc_log,
+ UCI_DBG_INFO,
+ "Failed to send signal for chan %d, ret : %d\n",
+ client->out_chan,
+ ret_val);
goto error;
}
error_open:
@@ -451,15 +478,20 @@ static int mhi_uci_tiocm_set(struct uci_client *client_ctxt, u32 set, u32 clear)
client_ctxt->local_tiocm |= status_set;
client_ctxt->local_tiocm &= ~status_clear;
- uci_log(UCI_DBG_VERBOSE,
+ uci_log(client_ctxt->uci_ipc_log,
+ UCI_DBG_VERBOSE,
"Old TIOCM0x%x for chan %d, Current TIOCM 0x%x\n",
- old_status, client_ctxt->out_chan, client_ctxt->local_tiocm);
+ old_status,
+ client_ctxt->out_chan,
+ client_ctxt->local_tiocm);
mutex_unlock(&client_ctxt->uci_ctxt->ctrl_mutex);
if (client_ctxt->local_tiocm != old_status) {
- uci_log(UCI_DBG_VERBOSE,
+ uci_log(client_ctxt->uci_ipc_log,
+ UCI_DBG_VERBOSE,
"Setting TIOCM to 0x%x for chan %d\n",
- client_ctxt->local_tiocm, client_ctxt->out_chan);
+ client_ctxt->local_tiocm,
+ client_ctxt->out_chan);
return mhi_uci_send_status_cmd(client_ctxt);
}
return 0;
@@ -474,26 +506,35 @@ static long mhi_uci_ctl_ioctl(struct file *file, unsigned int cmd,
uci_handle = file->private_data;
if (uci_handle == NULL) {
- uci_log(UCI_DBG_VERBOSE,
- "Invalid handle for client.\n");
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_VERBOSE,
+ "Invalid handle for client\n");
return -ENODEV;
}
- uci_log(UCI_DBG_VERBOSE,
- "Attempting to dtr cmd 0x%x arg 0x%lx for chan %d\n",
- cmd, arg, uci_handle->out_chan);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
+ "Attempting to dtr cmd 0x%x arg 0x%lx for chan %d\n",
+ cmd,
+ arg,
+ uci_handle->out_chan);
switch (cmd) {
case TIOCMGET:
- uci_log(UCI_DBG_VERBOSE,
- "Returning 0x%x mask\n", uci_handle->local_tiocm);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
+ "Returning 0x%x mask\n",
+ uci_handle->local_tiocm);
ret_val = uci_handle->local_tiocm;
break;
case TIOCMSET:
if (0 != copy_from_user(&set_val, (void *)arg, sizeof(set_val)))
return -ENOMEM;
- uci_log(UCI_DBG_VERBOSE,
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
"Attempting to set cmd 0x%x arg 0x%x for chan %d\n",
- cmd, set_val, uci_handle->out_chan);
+ cmd,
+ set_val,
+ uci_handle->out_chan);
ret_val = mhi_uci_tiocm_set(uci_handle, set_val, ~set_val);
break;
default:
@@ -506,28 +547,36 @@ static long mhi_uci_ctl_ioctl(struct file *file, unsigned int cmd,
static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
- struct uci_client *uci_handle = NULL;
- uci_handle = file->private_data;
+ struct uci_client *uci_handle = file->private_data;
+ struct mhi_uci_ctxt_t *uci_ctxt;
if (uci_handle == NULL)
return -ENODEV;
+
+ uci_ctxt = uci_handle->uci_ctxt;
poll_wait(file, &uci_handle->read_wq, wait);
poll_wait(file, &uci_handle->write_wq, wait);
if (atomic_read(&uci_handle->avail_pkts) > 0) {
- uci_log(UCI_DBG_VERBOSE,
- "Client can read chan %d\n", uci_handle->in_chan);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
+ "Client can read chan %d\n",
+ uci_handle->in_chan);
mask |= POLLIN | POLLRDNORM;
}
- if (!atomic_read(&uci_ctxt.mhi_disabled) &&
+ if (!atomic_read(&uci_ctxt->mhi_disabled) &&
(mhi_get_free_desc(uci_handle->out_handle) > 0)) {
- uci_log(UCI_DBG_VERBOSE,
- "Client can write chan %d\n", uci_handle->out_chan);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
+ "Client can write chan %d\n",
+ uci_handle->out_chan);
mask |= POLLOUT | POLLWRNORM;
}
- uci_log(UCI_DBG_VERBOSE,
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
"Client attempted to poll chan %d, returning mask 0x%x\n",
- uci_handle->in_chan, mask);
+ uci_handle->in_chan,
+ mask);
return mask;
}
@@ -535,10 +584,12 @@ static int open_client_mhi_channels(struct uci_client *uci_client)
{
int ret_val = 0;
int r = 0;
- uci_log(UCI_DBG_INFO,
- "Starting channels %d %d.\n",
- uci_client->out_chan,
- uci_client->in_chan);
+
+ uci_log(uci_client->uci_ipc_log,
+ UCI_DBG_INFO,
+ "Starting channels %d %d\n",
+ uci_client->out_chan,
+ uci_client->in_chan);
mutex_lock(&uci_client->out_chan_lock);
mutex_lock(&uci_client->in_chan_lock);
ret_val = mhi_open_channel(uci_client->out_handle);
@@ -553,21 +604,26 @@ static int open_client_mhi_channels(struct uci_client *uci_client)
ret_val = mhi_open_channel(uci_client->in_handle);
if (ret_val != 0) {
- uci_log(UCI_DBG_ERROR,
- "Failed to open chan %d, ret 0x%x\n",
- uci_client->out_chan, ret_val);
+ uci_log(uci_client->uci_ipc_log,
+ UCI_DBG_ERROR,
+ "Failed to open chan %d, ret 0x%x\n",
+ uci_client->out_chan,
+ ret_val);
goto handle_in_err;
}
- uci_log(UCI_DBG_INFO,
- "Initializing inbound chan %d.\n",
- uci_client->in_chan);
+ uci_log(uci_client->uci_ipc_log,
+ UCI_DBG_INFO,
+ "Initializing inbound chan %d\n",
+ uci_client->in_chan);
uci_client->in_chan_state = 1;
- ret_val = mhi_init_inbound(uci_client, uci_client->in_chan);
+ ret_val = mhi_init_inbound(uci_client);
if (0 != ret_val) {
- uci_log(UCI_DBG_ERROR,
- "Failed to init inbound 0x%x, ret 0x%x\n",
- uci_client->in_chan, ret_val);
+ uci_log(uci_client->uci_ipc_log,
+ UCI_DBG_ERROR,
+ "Failed to init inbound 0x%x, ret 0x%x\n",
+ uci_client->in_chan,
+ ret_val);
}
mutex_unlock(&uci_client->in_chan_lock);
@@ -583,37 +639,54 @@ handle_not_rdy_err:
return r;
}
-static int mhi_uci_client_open(struct inode *mhi_inode,
+static int mhi_uci_client_open(struct inode *inode,
struct file *file_handle)
{
struct uci_client *uci_handle = NULL;
+ struct mhi_uci_ctxt_t *uci_ctxt = NULL, *itr;
int r = 0;
- int client_id = iminor(mhi_inode);
- uci_handle = &uci_ctxt.client_handles[client_id];
-
- if (NULL == uci_handle)
+ int client_id = iminor(inode);
+ int major = imajor(inode);
+
+ /* Find the uci ctxt from major */
+ mutex_lock(&mhi_uci_drv_ctxt.list_lock);
+ list_for_each_entry(itr, &mhi_uci_drv_ctxt.head, node) {
+ if (MAJOR(itr->dev_t) == major) {
+ uci_ctxt = itr;
+ break;
+ }
+ }
+ mutex_unlock(&mhi_uci_drv_ctxt.list_lock);
+ if (!uci_ctxt || client_id >= MHI_SOFTWARE_CLIENT_LIMIT)
return -EINVAL;
+ uci_handle = &uci_ctxt->client_handles[client_id];
if (atomic_read(&uci_handle->mhi_disabled)) {
- uci_log(UCI_DBG_INFO,
- "MHI is still recovering from SSR, client %d\n",
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_INFO,
+ "MHI channel still disable for, client %d\n",
client_id);
msleep(500);
return -EAGAIN;
}
- uci_log(UCI_DBG_INFO,
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_INFO,
"Client opened device node 0x%x, ref count 0x%x\n",
- iminor(mhi_inode), atomic_read(&uci_handle->ref_count));
+ client_id,
+ atomic_read(&uci_handle->ref_count));
if (1 == atomic_add_return(1, &uci_handle->ref_count)) {
- uci_handle->uci_ctxt = &uci_ctxt;
- uci_log(UCI_DBG_INFO,
- "Opening channels client %d\n", client_id);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_INFO,
+ "Opening channels client %d\n",
+ client_id);
r = open_client_mhi_channels(uci_handle);
if (r)
- uci_log(UCI_DBG_INFO,
- "Failed to open channels ret %d\n", r);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_INFO,
+ "Failed to open channels ret %d\n",
+ r);
}
file_handle->private_data = uci_handle;
return r;
@@ -623,7 +696,6 @@ static int mhi_uci_client_release(struct inode *mhi_inode,
struct file *file_handle)
{
struct uci_client *uci_handle = file_handle->private_data;
- struct mhi_uci_ctxt_t *uci_ctxt;
u32 nr_in_bufs = 0;
int in_chan = 0;
int i = 0;
@@ -632,21 +704,22 @@ static int mhi_uci_client_release(struct inode *mhi_inode,
if (uci_handle == NULL)
return -EINVAL;
- uci_ctxt = uci_handle->uci_ctxt;
- in_chan = iminor(mhi_inode) + 1;
- nr_in_bufs = uci_ctxt->chan_attrib[in_chan].nr_trbs;
- buf_size = uci_ctxt->chan_attrib[in_chan].max_packet_size;
+ in_chan = uci_handle->in_attr.chan_id;
+ nr_in_bufs = uci_handle->in_attr.nr_trbs;
+ buf_size = uci_handle->in_attr.max_packet_size;
if (atomic_sub_return(1, &uci_handle->ref_count) == 0) {
- uci_log(UCI_DBG_ERROR,
- "Last client left, closing channel 0x%x\n",
- iminor(mhi_inode));
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_ERROR,
+ "Last client left, closing channel 0x%x\n",
+ in_chan);
if (atomic_read(&uci_handle->out_pkt_pend_ack))
- uci_log(UCI_DBG_CRITICAL,
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_CRITICAL,
"Still waiting on %d acks!, chan %d\n",
atomic_read(&uci_handle->out_pkt_pend_ack),
- iminor(mhi_inode));
+ uci_handle->out_attr.chan_id);
mhi_close_channel(uci_handle->out_handle);
mhi_close_channel(uci_handle->in_handle);
@@ -659,7 +732,8 @@ static int mhi_uci_client_release(struct inode *mhi_inode,
kfree(uci_handle->in_buf_list);
atomic_set(&uci_handle->avail_pkts, 0);
} else {
- uci_log(UCI_DBG_ERROR,
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_ERROR,
"Client close chan %d, ref count 0x%x\n",
iminor(mhi_inode),
atomic_read(&uci_handle->ref_count));
@@ -689,17 +763,23 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf,
mutex = &uci_handle->in_chan_lock;
chan = uci_handle->in_chan;
mutex_lock(mutex);
- buf_size = uci_ctxt.chan_attrib[chan].max_packet_size;
+ buf_size = uci_handle->in_attr.max_packet_size;
+ result.buf_addr = NULL;
- uci_log(UCI_DBG_VERBOSE, "Client attempted read on chan %d\n", chan);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
+ "Client attempted read on chan %d\n",
+ chan);
do {
if (!uci_handle->pkt_loc &&
- !atomic_read(&uci_ctxt.mhi_disabled)) {
+ !atomic_read(&uci_handle->uci_ctxt->mhi_disabled)) {
ret_val = mhi_poll_inbound(client_handle, &result);
if (ret_val) {
- uci_log(UCI_DBG_ERROR,
- "Failed to poll inbound ret %d avail pkt %d\n",
- ret_val, atomic_read(&uci_handle->avail_pkts));
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_ERROR,
+ "Failed to poll inbound ret %d avail pkt %d\n",
+ ret_val,
+ atomic_read(&uci_handle->avail_pkts));
}
if (result.buf_addr)
uci_handle->pkt_loc = result.buf_addr;
@@ -707,23 +787,27 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf,
uci_handle->pkt_loc = 0;
uci_handle->pkt_size = result.bytes_xferd;
*bytes_pending = uci_handle->pkt_size;
- uci_log(UCI_DBG_VERBOSE,
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
"Got pkt size 0x%zx at addr 0x%lx, chan %d\n",
uci_handle->pkt_size,
- (uintptr_t)result.buf_addr, chan);
+ (uintptr_t)result.buf_addr,
+ chan);
}
if ((*bytes_pending == 0 || uci_handle->pkt_loc == 0) &&
(atomic_read(&uci_handle->avail_pkts) <= 0)) {
/* If nothing was copied yet, wait for data */
- uci_log(UCI_DBG_VERBOSE,
- "No data avail_pkts %d, chan %d\n",
- atomic_read(&uci_handle->avail_pkts),
- chan);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
+ "No data avail_pkts %d, chan %d\n",
+ atomic_read(&uci_handle->avail_pkts),
+ chan);
ret_val = wait_event_interruptible(
uci_handle->read_wq,
(atomic_read(&uci_handle->avail_pkts) > 0));
if (ret_val == -ERESTARTSYS) {
- uci_log(UCI_DBG_ERROR,
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_ERROR,
"Exit signal caught\n");
goto error;
}
@@ -732,7 +816,8 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf,
0 == uci_handle->pkt_size &&
0 == uci_handle->pkt_loc &&
uci_handle->mhi_status == -ENETRESET) {
- uci_log(UCI_DBG_ERROR,
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_ERROR,
"Detected pending reset, reporting chan %d\n",
chan);
atomic_dec(&uci_handle->avail_pkts);
@@ -743,23 +828,24 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf,
} else if (atomic_read(&uci_handle->avail_pkts) &&
uci_handle->pkt_size != 0 &&
uci_handle->pkt_loc != 0) {
- uci_log(UCI_DBG_VERBOSE,
- "Got packet: avail pkts %d phy_adr 0x%p, chan %d\n",
- atomic_read(&uci_handle->avail_pkts),
- result.buf_addr,
- chan);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
+ "Got packet: avail pkts %d phy_adr 0x%p, chan %d\n",
+ atomic_read(&uci_handle->avail_pkts),
+ result.buf_addr,
+ chan);
break;
/*
* MHI did not return a valid packet, but we have one
* which we did not finish returning to user
*/
} else {
- uci_log(UCI_DBG_CRITICAL,
- "chan %d err: avail pkts %d phy_adr 0x%p mhi_stat%d\n",
- chan,
- atomic_read(&uci_handle->avail_pkts),
- result.buf_addr,
- uci_handle->mhi_status);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_CRITICAL,
+ "chan %d err: avail pkts %d mhi_stat%d\n",
+ chan,
+ atomic_read(&uci_handle->avail_pkts),
+ uci_handle->mhi_status);
return -EIO;
}
} while (!uci_handle->pkt_loc);
@@ -775,9 +861,12 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf,
bytes_copied = *bytes_pending;
*bytes_pending = 0;
- uci_log(UCI_DBG_VERBOSE,
- "Copied 0x%zx of 0x%x, chan %d\n",
- bytes_copied, (u32)*bytes_pending, chan);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
+ "Copied 0x%zx of 0x%x, chan %d\n",
+ bytes_copied,
+ (u32)*bytes_pending,
+ chan);
} else {
addr_offset = uci_handle->pkt_size - *bytes_pending;
if (copy_to_user(buf,
@@ -789,39 +878,50 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf,
}
bytes_copied = uspace_buf_size;
*bytes_pending -= uspace_buf_size;
- uci_log(UCI_DBG_VERBOSE,
- "Copied 0x%zx of 0x%x,chan %d\n",
- bytes_copied,
- (u32)*bytes_pending,
- chan);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
+ "Copied 0x%zx of 0x%x,chan %d\n",
+ bytes_copied,
+ (u32)*bytes_pending,
+ chan);
}
/* We finished with this buffer, map it back */
if (*bytes_pending == 0) {
- uci_log(UCI_DBG_VERBOSE, "Pkt loc %p ,chan %d\n",
- uci_handle->pkt_loc, chan);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
+ "Pkt loc %p ,chan %d\n",
+ uci_handle->pkt_loc,
+ chan);
memset(uci_handle->pkt_loc, 0, buf_size);
atomic_dec(&uci_handle->avail_pkts);
- uci_log(UCI_DBG_VERBOSE,
- "Decremented avail pkts avail 0x%x\n",
- atomic_read(&uci_handle->avail_pkts));
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
+ "Decremented avail pkts avail 0x%x\n",
+ atomic_read(&uci_handle->avail_pkts));
ret_val = mhi_queue_xfer(client_handle, uci_handle->pkt_loc,
buf_size, MHI_EOT);
if (0 != ret_val) {
- uci_log(UCI_DBG_ERROR,
- "Failed to recycle element\n");
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_ERROR,
+ "Failed to recycle element\n");
ret_val = -EIO;
goto error;
}
uci_handle->pkt_loc = 0;
}
- uci_log(UCI_DBG_VERBOSE,
- "Returning 0x%zx bytes, 0x%x bytes left\n",
- bytes_copied, (u32)*bytes_pending);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
+ "Returning 0x%zx bytes, 0x%x bytes left\n",
+ bytes_copied,
+ (u32)*bytes_pending);
mutex_unlock(mutex);
return bytes_copied;
error:
mutex_unlock(mutex);
- uci_log(UCI_DBG_ERROR, "Returning %d\n", ret_val);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_ERROR,
+ "Returning %d\n",
+ ret_val);
return ret_val;
}
@@ -846,9 +946,10 @@ static ssize_t mhi_uci_client_write(struct file *file,
ret_val = mhi_uci_send_packet(&uci_handle->out_handle,
(void *)buf, count, 1);
if (!ret_val) {
- uci_log(UCI_DBG_VERBOSE,
- "No descriptors available, did we poll, chan %d?\n",
- chan);
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
+ "No descriptors available, did we poll, chan %d?\n",
+ chan);
mutex_unlock(&uci_handle->out_chan_lock);
ret_val =
wait_event_interruptible(
@@ -857,8 +958,9 @@ static ssize_t mhi_uci_client_write(struct file *file,
mutex_lock(&uci_handle->out_chan_lock);
if (-ERESTARTSYS == ret_val) {
goto sys_interrupt;
- uci_log(UCI_DBG_WARNING,
- "Waitqueue cancelled by system\n");
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_WARNING,
+ "Waitqueue cancelled by system\n");
}
}
}
@@ -867,60 +969,79 @@ sys_interrupt:
return ret_val;
}
-static int uci_init_client_attributes(struct mhi_uci_ctxt_t
- *uci_ctxt)
+static int uci_init_client_attributes(struct mhi_uci_ctxt_t *uci_ctxt,
+ struct device_node *of_node)
{
- u32 i = 0;
- struct chan_attr *chan_attrib = NULL;
- size_t max_packet_size = TRE_TYPICAL_SIZE;
-
- for (i = 0; i < MHI_MAX_SOFTWARE_CHANNELS; ++i) {
- max_packet_size = TRE_TYPICAL_SIZE;
- chan_attrib = &uci_ctxt->chan_attrib[i];
- switch (i) {
- case MHI_CLIENT_SAHARA_IN:
- max_packet_size = TRE_MAX_SIZE;
- case MHI_CLIENT_SAHARA_OUT:
- case MHI_CLIENT_LOOPBACK_OUT:
- case MHI_CLIENT_LOOPBACK_IN:
- case MHI_CLIENT_EFS_OUT:
- case MHI_CLIENT_EFS_IN:
- case MHI_CLIENT_QMI_OUT:
- case MHI_CLIENT_QMI_IN:
- case MHI_CLIENT_IP_CTRL_0_OUT:
- case MHI_CLIENT_IP_CTRL_0_IN:
- case MHI_CLIENT_IP_CTRL_1_OUT:
- case MHI_CLIENT_IP_CTRL_1_IN:
- case MHI_CLIENT_BL_OUT:
- case MHI_CLIENT_BL_IN:
- case MHI_CLIENT_DUN_OUT:
- case MHI_CLIENT_DUN_IN:
- case MHI_CLIENT_TF_OUT:
- case MHI_CLIENT_TF_IN:
+ int num_rows, ret_val = 0;
+ int i, dir;
+ u32 ctrl_chan = -1;
+ u32 *chan_info, *itr;
+ const char *prop_name = "qcom,mhi-uci-channels";
+
+ ret_val = of_property_read_u32(of_node, "qcom,mhi-uci-ctrlchan",
+ &ctrl_chan);
+ if (ret_val) {
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_INFO,
+ "Could not find property 'qcom,mhi-uci-ctrlchan'\n");
+ }
+
+ num_rows = of_property_count_elems_of_size(of_node, prop_name,
+ sizeof(u32) * 4);
+ /* At least one pair of channels should exist */
+ if (num_rows < 1) {
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_CRITICAL,
+ "Missing or invalid property 'qcom,mhi-uci-channels'\n");
+ return -ENODEV;
+ }
+
+ if (num_rows > MHI_SOFTWARE_CLIENT_LIMIT)
+ num_rows = MHI_SOFTWARE_CLIENT_LIMIT;
+
+ chan_info = kmalloc_array(num_rows, 4 * sizeof(*chan_info), GFP_KERNEL);
+ if (!chan_info)
+ return -ENOMEM;
+
+ ret_val = of_property_read_u32_array(of_node, prop_name, chan_info,
+ num_rows * 4);
+ if (ret_val)
+ goto error_dts;
+
+ for (i = 0, itr = chan_info; i < num_rows; i++) {
+ struct uci_client *client = &uci_ctxt->client_handles[i];
+ struct chan_attr *chan_attrib;
+
+ for (dir = 0; dir < 2; dir++) {
+ chan_attrib = (dir) ?
+ &client->in_attr : &client->out_attr;
chan_attrib->uci_ownership = 1;
- break;
- default:
- chan_attrib->uci_ownership = 0;
- break;
- }
- if (chan_attrib->uci_ownership) {
- chan_attrib->chan_id = i;
- chan_attrib->max_packet_size = max_packet_size;
+ chan_attrib->chan_id = *itr++;
+ chan_attrib->max_packet_size = *itr++;
+ if (dir == 0)
+ chan_attrib->dir = MHI_DIR_OUT;
+ else
+ chan_attrib->dir = MHI_DIR_IN;
+
+ if (chan_attrib->chan_id == ctrl_chan)
+ uci_ctxt->ctrl_client = client;
}
- if (i % 2 == 0)
- chan_attrib->dir = MHI_DIR_OUT;
- else
- chan_attrib->dir = MHI_DIR_IN;
}
- return 0;
+
+error_dts:
+ kfree(chan_info);
+ return ret_val;
}
static int process_mhi_disabled_notif_sync(struct uci_client *uci_handle)
{
- uci_log(UCI_DBG_INFO, "Entered.\n");
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_INFO,
+ "Entered.\n");
if (uci_handle->mhi_status != -ENETRESET) {
- uci_log(UCI_DBG_CRITICAL,
- "Setting reset for chan %d.\n",
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_CRITICAL,
+ "Setting reset for chan %d\n",
uci_handle->out_chan);
uci_handle->pkt_size = 0;
uci_handle->pkt_loc = NULL;
@@ -932,39 +1053,53 @@ static int process_mhi_disabled_notif_sync(struct uci_client *uci_handle)
uci_handle->in_chan_state = 0;
wake_up(&uci_handle->read_wq);
} else {
- uci_log(UCI_DBG_CRITICAL,
- "Chan %d state already reset.\n",
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_CRITICAL,
+ "Chan %d state already reset\n",
uci_handle->out_chan);
}
- uci_log(UCI_DBG_INFO, "Exited.\n");
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO, "Exited\n");
return 0;
}
-static void process_rs232_state(struct mhi_result *result)
+static void process_rs232_state(struct uci_client *ctrl_client,
+ struct mhi_result *result)
{
struct rs232_ctrl_msg *rs232_pkt;
- struct uci_client *client;
+ struct uci_client *client = NULL;
+ struct mhi_uci_ctxt_t *uci_ctxt = ctrl_client->uci_ctxt;
u32 msg_id;
- int ret_val;
+ int ret_val, i;
u32 chan;
- mutex_lock(&uci_ctxt.ctrl_mutex);
+ mutex_lock(&uci_ctxt->ctrl_mutex);
if (result->transaction_status != 0) {
- uci_log(UCI_DBG_ERROR,
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_ERROR,
"Non successful transfer code 0x%x\n",
- result->transaction_status);
+ result->transaction_status);
goto error_bad_xfer;
}
if (result->bytes_xferd != sizeof(struct rs232_ctrl_msg)) {
- uci_log(UCI_DBG_ERROR,
- "Buffer is of wrong size is: 0x%zx: expected 0x%zx\n",
- result->bytes_xferd, sizeof(struct rs232_ctrl_msg));
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_ERROR,
+ "Buffer is of wrong size is: 0x%zx: expected 0x%zx\n",
+ result->bytes_xferd,
+ sizeof(struct rs232_ctrl_msg));
goto error_size;
}
rs232_pkt = result->buf_addr;
MHI_GET_CTRL_DEST_ID(CTRL_DEST_ID, rs232_pkt, chan);
- client = &uci_ctxt.client_handles[chan / 2];
+ for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; i++)
+ if (chan == uci_ctxt->client_handles[i].out_chan ||
+ chan == uci_ctxt->client_handles[i].in_chan) {
+ client = &uci_ctxt->client_handles[i];
+ break;
+ }
+ /* No valid channel found */
+ if (!client)
+ goto error_bad_xfer;
MHI_GET_CTRL_MSG_ID(CTRL_MSG_ID, rs232_pkt, msg_id);
client->local_tiocm = 0;
@@ -982,35 +1117,38 @@ static void process_rs232_state(struct mhi_result *result)
error_bad_xfer:
error_size:
memset(rs232_pkt, 0, sizeof(struct rs232_ctrl_msg));
- ret_val = mhi_queue_xfer(client->in_handle,
- result->buf_addr,
- result->bytes_xferd,
- result->flags);
+ ret_val = mhi_queue_xfer(ctrl_client->in_handle,
+ result->buf_addr,
+ result->bytes_xferd,
+ result->flags);
if (0 != ret_val) {
- uci_log(UCI_DBG_ERROR,
- "Failed to recycle ctrl msg buffer\n");
+ uci_log(ctrl_client->uci_ipc_log,
+ UCI_DBG_ERROR,
+ "Failed to recycle ctrl msg buffer\n");
}
- mutex_unlock(&uci_ctxt.ctrl_mutex);
+ mutex_unlock(&uci_ctxt->ctrl_mutex);
}
static void parse_inbound_ack(struct uci_client *uci_handle,
struct mhi_result *result)
{
atomic_inc(&uci_handle->avail_pkts);
- uci_log(UCI_DBG_VERBOSE,
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
"Received cb on chan %d, avail pkts: 0x%x\n",
uci_handle->in_chan,
atomic_read(&uci_handle->avail_pkts));
wake_up(&uci_handle->read_wq);
- if (uci_handle->in_chan == MHI_CLIENT_IP_CTRL_1_IN)
- process_rs232_state(result);
+ if (uci_handle == uci_handle->uci_ctxt->ctrl_client)
+ process_rs232_state(uci_handle, result);
}
static void parse_outbound_ack(struct uci_client *uci_handle,
struct mhi_result *result)
{
kfree(result->buf_addr);
- uci_log(UCI_DBG_VERBOSE,
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
"Received ack on chan %d, pending acks: 0x%x\n",
uci_handle->out_chan,
atomic_read(&uci_handle->out_pkt_pend_ack));
@@ -1021,88 +1159,96 @@ static void parse_outbound_ack(struct uci_client *uci_handle,
static void uci_xfer_cb(struct mhi_cb_info *cb_info)
{
- int chan_nr;
struct uci_client *uci_handle = NULL;
- u32 client_index;
struct mhi_result *result;
if (!cb_info || !cb_info->result) {
- uci_log(UCI_DBG_CRITICAL, "Bad CB info from MHI.\n");
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_CRITICAL,
+ "Bad CB info from MHI\n");
return;
}
- chan_nr = (uintptr_t)cb_info->result->user_data;
- client_index = CHAN_TO_CLIENT(chan_nr);
- uci_handle = &uci_ctxt.client_handles[client_index];
-
+ uci_handle = cb_info->result->user_data;
switch (cb_info->cb_reason) {
case MHI_CB_MHI_ENABLED:
atomic_set(&uci_handle->mhi_disabled, 0);
break;
case MHI_CB_MHI_DISABLED:
atomic_set(&uci_handle->mhi_disabled, 1);
- uci_log(UCI_DBG_INFO, "MHI disabled CB received.\n");
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_INFO,
+ "MHI disabled CB received\n");
process_mhi_disabled_notif_sync(uci_handle);
break;
case MHI_CB_XFER:
if (!cb_info->result) {
- uci_log(UCI_DBG_CRITICAL,
- "Failed to obtain mhi result from CB.\n");
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_CRITICAL,
+ "Failed to obtain mhi result from CB\n");
return;
}
result = cb_info->result;
- chan_nr = (uintptr_t)result->user_data;
- client_index = chan_nr / 2;
- uci_handle =
- &uci_ctxt.client_handles[client_index];
- if (chan_nr % 2)
+ if (cb_info->chan % 2)
parse_inbound_ack(uci_handle, result);
else
parse_outbound_ack(uci_handle, result);
break;
default:
- uci_log(UCI_DBG_VERBOSE,
+ uci_log(uci_handle->uci_ipc_log,
+ UCI_DBG_VERBOSE,
"Cannot handle cb reason 0x%x\n",
cb_info->cb_reason);
}
}
-static int mhi_register_client(struct uci_client *mhi_client, int index)
+static int mhi_register_client(struct uci_client *mhi_client)
{
int ret_val = 0;
- uci_log(UCI_DBG_INFO, "Setting up workqueues.\n");
+ uci_log(mhi_client->uci_ipc_log,
+ UCI_DBG_INFO,
+ "Setting up workqueues\n");
init_waitqueue_head(&mhi_client->read_wq);
init_waitqueue_head(&mhi_client->write_wq);
- mhi_client->out_chan = index * 2;
- mhi_client->in_chan = index * 2 + 1;
- mhi_client->client_index = index;
+ mhi_client->out_chan = mhi_client->out_attr.chan_id;
+ mhi_client->in_chan = mhi_client->in_attr.chan_id;
mutex_init(&mhi_client->in_chan_lock);
mutex_init(&mhi_client->out_chan_lock);
atomic_set(&mhi_client->mhi_disabled, 1);
- uci_log(UCI_DBG_INFO, "Registering chan %d.\n", mhi_client->out_chan);
+ uci_log(mhi_client->uci_ipc_log,
+ UCI_DBG_INFO,
+ "Registering chan %d\n",
+ mhi_client->out_chan);
ret_val = mhi_register_channel(&mhi_client->out_handle,
mhi_client->out_chan,
0,
- &uci_ctxt.client_info,
- (void *)(uintptr_t)(mhi_client->out_chan));
+ &mhi_client->uci_ctxt->client_info,
+ mhi_client);
if (0 != ret_val)
- uci_log(UCI_DBG_ERROR,
+ uci_log(mhi_client->uci_ipc_log,
+ UCI_DBG_ERROR,
"Failed to init outbound chan 0x%x, ret 0x%x\n",
- mhi_client->out_chan, ret_val);
+ mhi_client->out_chan,
+ ret_val);
- uci_log(UCI_DBG_INFO, "Registering chan %d.\n", mhi_client->in_chan);
+ uci_log(mhi_client->uci_ipc_log,
+ UCI_DBG_INFO,
+ "Registering chan %d\n",
+ mhi_client->in_chan);
ret_val = mhi_register_channel(&mhi_client->in_handle,
mhi_client->in_chan,
0,
- &uci_ctxt.client_info,
- (void *)(uintptr_t)(mhi_client->in_chan));
+ &mhi_client->uci_ctxt->client_info,
+ mhi_client);
if (0 != ret_val)
- uci_log(UCI_DBG_ERROR,
+ uci_log(mhi_client->uci_ipc_log,
+ UCI_DBG_ERROR,
"Failed to init inbound chan 0x%x, ret 0x%x\n",
- mhi_client->in_chan, ret_val);
+ mhi_client->in_chan,
+ ret_val);
return 0;
}
@@ -1115,119 +1261,215 @@ static const struct file_operations mhi_uci_client_fops = {
.unlocked_ioctl = mhi_uci_ctl_ioctl,
};
-static int mhi_uci_init(void)
+static int mhi_uci_probe(struct platform_device *pdev)
{
- u32 i = 0;
- int ret_val = 0;
- struct uci_client *mhi_client = NULL;
- s32 r = 0;
- mhi_uci_ipc_log = ipc_log_context_create(MHI_UCI_IPC_LOG_PAGES,
- "mhi-uci", 0);
- if (mhi_uci_ipc_log == NULL) {
- uci_log(UCI_DBG_WARNING,
- "Failed to create IPC logging context\n");
- }
- uci_log(UCI_DBG_INFO, "Setting up work queues.\n");
- uci_ctxt.client_info.mhi_client_cb = uci_xfer_cb;
+ struct mhi_uci_ctxt_t *uci_ctxt;
+ int ret_val;
+ int i;
+ char node_name[16];
+
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_INFO,
+ "Entered with pdev:%p\n",
+ pdev);
+
+ if (pdev->dev.of_node == NULL)
+ return -ENODEV;
+
+ pdev->id = of_alias_get_id(pdev->dev.of_node, "mhi_uci");
+ if (pdev->id < 0)
+ return -ENODEV;
+
+ uci_ctxt = devm_kzalloc(&pdev->dev,
+ sizeof(*uci_ctxt),
+ GFP_KERNEL);
+ if (!uci_ctxt)
+ return -ENOMEM;
- mutex_init(&uci_ctxt.ctrl_mutex);
+ uci_ctxt->client_info.mhi_client_cb = uci_xfer_cb;
+ mutex_init(&uci_ctxt->ctrl_mutex);
- uci_log(UCI_DBG_INFO, "Setting up channel attributes.\n");
- ret_val = uci_init_client_attributes(&uci_ctxt);
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_INFO,
+ "Setting up channel attributes\n");
+ ret_val = uci_init_client_attributes(uci_ctxt,
+ pdev->dev.of_node);
if (ret_val) {
- uci_log(UCI_DBG_ERROR,
- "Failed to init client attributes\n");
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_ERROR,
+ "Failed to init client attributes\n");
return -EIO;
}
- uci_ctxt.ctrl_chan_id = MHI_CLIENT_IP_CTRL_1_OUT;
- uci_log(UCI_DBG_INFO, "Registering for MHI events.\n");
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_INFO,
+ "Registering for MHI events\n");
for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; ++i) {
- if (uci_ctxt.chan_attrib[i * 2].uci_ownership) {
- mhi_client = &uci_ctxt.client_handles[i];
- r = mhi_register_client(mhi_client, i);
- if (r) {
- uci_log(UCI_DBG_CRITICAL,
+ struct uci_client *uci_client = &uci_ctxt->client_handles[i];
+
+ uci_client->uci_ctxt = uci_ctxt;
+ if (uci_client->in_attr.uci_ownership) {
+ ret_val = mhi_register_client(uci_client);
+ if (ret_val) {
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_CRITICAL,
"Failed to reg client %d ret %d\n",
- r, i);
+ ret_val,
+ i);
+
+ return -EIO;
}
+ snprintf(node_name, sizeof(node_name), "mhi-uci%d",
+ uci_client->out_attr.chan_id);
+ uci_client->uci_ipc_log = ipc_log_context_create
+ (MHI_UCI_IPC_LOG_PAGES,
+ node_name,
+ 0);
}
}
- uci_log(UCI_DBG_INFO, "Allocating char devices.\n");
- r = alloc_chrdev_region(&uci_ctxt.start_ctrl_nr,
- 0, MHI_MAX_SOFTWARE_CHANNELS,
- DEVICE_NAME);
-
- if (IS_ERR_VALUE(r)) {
- uci_log(UCI_DBG_ERROR,
- "Failed to alloc char devs, ret 0x%x\n", r);
+
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_INFO,
+ "Allocating char devices\n");
+ ret_val = alloc_chrdev_region(&uci_ctxt->dev_t,
+ 0,
+ MHI_SOFTWARE_CLIENT_LIMIT,
+ DEVICE_NAME);
+ if (IS_ERR_VALUE(ret_val)) {
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_ERROR,
+ "Failed to alloc char devs, ret 0x%x\n", ret_val);
goto failed_char_alloc;
}
- uci_log(UCI_DBG_INFO, "Creating class\n");
- uci_ctxt.mhi_uci_class = class_create(THIS_MODULE,
- DEVICE_NAME);
- if (IS_ERR(uci_ctxt.mhi_uci_class)) {
- uci_log(UCI_DBG_ERROR,
- "Failed to instantiate class, ret 0x%x\n", r);
- r = -ENOMEM;
- goto failed_class_add;
- }
- uci_log(UCI_DBG_INFO, "Setting up device nodes.\n");
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_INFO,
+ "Setting up device nodes. for dev_t: 0x%x major:0x%x\n",
+ uci_ctxt->dev_t,
+ MAJOR(uci_ctxt->dev_t));
for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; ++i) {
- if (uci_ctxt.chan_attrib[i*2].uci_ownership) {
- cdev_init(&uci_ctxt.cdev[i], &mhi_uci_client_fops);
- uci_ctxt.cdev[i].owner = THIS_MODULE;
- r = cdev_add(&uci_ctxt.cdev[i],
- uci_ctxt.start_ctrl_nr + i , 1);
- if (IS_ERR_VALUE(r)) {
- uci_log(UCI_DBG_ERROR,
+ struct uci_client *uci_client = &uci_ctxt->client_handles[i];
+
+ if (uci_client->in_attr.uci_ownership) {
+ cdev_init(&uci_ctxt->cdev[i], &mhi_uci_client_fops);
+ uci_ctxt->cdev[i].owner = THIS_MODULE;
+ ret_val = cdev_add(&uci_ctxt->cdev[i],
+ uci_ctxt->dev_t + i, 1);
+ if (IS_ERR_VALUE(ret_val)) {
+ uci_log(uci_client->uci_ipc_log,
+ UCI_DBG_ERROR,
"Failed to add cdev %d, ret 0x%x\n",
- i, r);
+ i, ret_val);
goto failed_char_add;
}
- uci_ctxt.client_handles[i].dev =
- device_create(uci_ctxt.mhi_uci_class, NULL,
- uci_ctxt.start_ctrl_nr + i,
- NULL, DEVICE_NAME "_pipe_%d",
- i * 2);
-
- if (IS_ERR(uci_ctxt.client_handles[i].dev)) {
- uci_log(UCI_DBG_ERROR,
- "Failed to add cdev %d\n", i);
- cdev_del(&uci_ctxt.cdev[i]);
+ uci_client->dev =
+ device_create(mhi_uci_drv_ctxt.mhi_uci_class,
+ NULL,
+ uci_ctxt->dev_t + i,
+ NULL,
+ DEVICE_NAME "_pipe_%d",
+ uci_client->out_chan);
+ if (IS_ERR(uci_client->dev)) {
+ uci_log(uci_client->uci_ipc_log,
+ UCI_DBG_ERROR,
+ "Failed to add cdev %d\n", i);
+ cdev_del(&uci_ctxt->cdev[i]);
+ ret_val = -EIO;
goto failed_device_create;
}
}
}
+ platform_set_drvdata(pdev, uci_ctxt);
+ mutex_lock(&mhi_uci_drv_ctxt.list_lock);
+ list_add_tail(&uci_ctxt->node, &mhi_uci_drv_ctxt.head);
+ mutex_unlock(&mhi_uci_drv_ctxt.list_lock);
return 0;
failed_char_add:
failed_device_create:
while (--i >= 0) {
- cdev_del(&uci_ctxt.cdev[i]);
- device_destroy(uci_ctxt.mhi_uci_class,
- MKDEV(MAJOR(uci_ctxt.start_ctrl_nr), i * 2));
+ cdev_del(&uci_ctxt->cdev[i]);
+ device_destroy(mhi_uci_drv_ctxt.mhi_uci_class,
+ MKDEV(MAJOR(uci_ctxt->dev_t), i));
};
- class_destroy(uci_ctxt.mhi_uci_class);
-failed_class_add:
- unregister_chrdev_region(MAJOR(uci_ctxt.start_ctrl_nr),
- MHI_MAX_SOFTWARE_CHANNELS);
+
+ unregister_chrdev_region(MAJOR(uci_ctxt->dev_t),
+ MHI_SOFTWARE_CLIENT_LIMIT);
failed_char_alloc:
- return r;
-}
-static void __exit mhi_uci_exit(void)
+ return ret_val;
+};
+
+static int mhi_uci_remove(struct platform_device *pdev)
{
+ struct mhi_uci_ctxt_t *uci_ctxt = platform_get_drvdata(pdev);
int i;
+
for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; ++i) {
- cdev_del(&uci_ctxt.cdev[i]);
- device_destroy(uci_ctxt.mhi_uci_class,
- MKDEV(MAJOR(uci_ctxt.start_ctrl_nr), i * 2));
+ struct uci_client *uci_client = &uci_ctxt->client_handles[i];
+
+ uci_client->uci_ctxt = uci_ctxt;
+ if (uci_client->in_attr.uci_ownership) {
+ mhi_deregister_channel(uci_client->out_handle);
+ mhi_deregister_channel(uci_client->in_handle);
+ cdev_del(&uci_ctxt->cdev[i]);
+ device_destroy(mhi_uci_drv_ctxt.mhi_uci_class,
+ MKDEV(MAJOR(uci_ctxt->dev_t), i));
+ }
}
- class_destroy(uci_ctxt.mhi_uci_class);
- unregister_chrdev_region(MAJOR(uci_ctxt.start_ctrl_nr),
- MHI_MAX_SOFTWARE_CHANNELS);
+
+ unregister_chrdev_region(MAJOR(uci_ctxt->dev_t),
+ MHI_SOFTWARE_CLIENT_LIMIT);
+
+ mutex_lock(&mhi_uci_drv_ctxt.list_lock);
+ list_del(&uci_ctxt->node);
+ mutex_unlock(&mhi_uci_drv_ctxt.list_lock);
+ return 0;
+};
+
+static const struct of_device_id mhi_uci_match_table[] = {
+ {.compatible = "qcom,mhi-uci"},
+ {},
+};
+
+static struct platform_driver mhi_uci_driver = {
+ .probe = mhi_uci_probe,
+ .remove = mhi_uci_remove,
+ .driver = {
+ .name = MHI_UCI_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = mhi_uci_match_table,
+ },
+};
+
+static int mhi_uci_init(void)
+{
+ mhi_uci_drv_ctxt.mhi_uci_ipc_log =
+ ipc_log_context_create(MHI_UCI_IPC_LOG_PAGES,
+ "mhi-uci",
+ 0);
+ if (mhi_uci_drv_ctxt.mhi_uci_ipc_log == NULL) {
+ uci_log(NULL,
+ UCI_DBG_WARNING,
+ "Failed to create IPC logging context");
+ }
+
+ mhi_uci_drv_ctxt.mhi_uci_class =
+ class_create(THIS_MODULE, DEVICE_NAME);
+
+ if (IS_ERR(mhi_uci_drv_ctxt.mhi_uci_class))
+ return -ENODEV;
+
+ mutex_init(&mhi_uci_drv_ctxt.list_lock);
+ INIT_LIST_HEAD(&mhi_uci_drv_ctxt.head);
+
+ return platform_driver_register(&mhi_uci_driver);
+}
+
+static void __exit mhi_uci_exit(void)
+{
+ class_destroy(mhi_uci_drv_ctxt.mhi_uci_class);
+ platform_driver_unregister(&mhi_uci_driver);
}
module_exit(mhi_uci_exit);
diff --git a/drivers/video/fbdev/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c
index d74b1432ea71..bb1259e3cfa1 100644
--- a/drivers/video/fbdev/msm/msm_ext_display.c
+++ b/drivers/platform/msm/msm_ext_display.c
@@ -23,9 +23,6 @@
#include <linux/of_platform.h>
#include <linux/msm_ext_display.h>
-#include "mdss_hdmi_util.h"
-#include "mdss_fb.h"
-
struct msm_ext_disp_list {
struct msm_ext_disp_init_data *data;
struct list_head list;
@@ -48,7 +45,6 @@ struct msm_ext_disp {
static int msm_ext_disp_get_intf_data(struct msm_ext_disp *ext_disp,
enum msm_ext_disp_type type,
struct msm_ext_disp_init_data **data);
-static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack);
static int msm_ext_disp_update_audio_ops(struct msm_ext_disp *ext_disp,
enum msm_ext_disp_type type,
enum msm_ext_disp_cable_state state, u32 flags);
@@ -103,128 +99,6 @@ end:
return;
}
-static void msm_ext_disp_get_pdev_by_name(struct device *dev,
- const char *phandle, struct platform_device **pdev)
-{
- struct device_node *pd_np;
-
- if (!dev) {
- pr_err("Invalid device\n");
- return;
- }
-
- if (!dev->of_node) {
- pr_err("Invalid of_node\n");
- return;
- }
-
- pd_np = of_parse_phandle(dev->of_node, phandle, 0);
- if (!pd_np) {
- pr_err("Cannot find %s dev\n", phandle);
- return;
- }
-
- *pdev = of_find_device_by_node(pd_np);
-}
-
-static void msm_ext_disp_get_fb_pdev(struct device *device,
- struct platform_device **fb_pdev)
-{
- struct msm_fb_data_type *mfd = NULL;
- struct fb_info *fbi = dev_get_drvdata(device);
-
- if (!fbi) {
- pr_err("fb_info is null\n");
- return;
- }
-
- mfd = (struct msm_fb_data_type *)fbi->par;
-
- *fb_pdev = mfd->pdev;
-}
-static ssize_t msm_ext_disp_sysfs_wta_audio_cb(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int ack, ret = 0;
- ssize_t size = strnlen(buf, PAGE_SIZE);
- const char *ext_phandle = "qcom,msm_ext_disp";
- struct platform_device *ext_pdev = NULL;
- const char *intf_phandle = "qcom,mdss-intf";
- struct platform_device *intf_pdev = NULL;
- struct platform_device *fb_pdev = NULL;
-
- ret = kstrtoint(buf, 10, &ack);
- if (ret) {
- pr_err("kstrtoint failed. ret=%d\n", ret);
- goto end;
- }
-
- msm_ext_disp_get_fb_pdev(dev, &fb_pdev);
- if (!fb_pdev) {
- pr_err("failed to get fb pdev\n");
- goto end;
- }
-
- msm_ext_disp_get_pdev_by_name(&fb_pdev->dev, intf_phandle, &intf_pdev);
- if (!intf_pdev) {
- pr_err("failed to get display intf pdev\n");
- goto end;
- }
-
- msm_ext_disp_get_pdev_by_name(&intf_pdev->dev, ext_phandle, &ext_pdev);
- if (!ext_pdev) {
- pr_err("failed to get ext_pdev\n");
- goto end;
- }
-
- ret = msm_ext_disp_audio_ack(ext_pdev, ack);
- if (ret)
- pr_err("Failed to process ack. ret=%d\n", ret);
-
-end:
- return size;
-}
-
-static DEVICE_ATTR(hdmi_audio_cb, S_IWUSR, NULL,
- msm_ext_disp_sysfs_wta_audio_cb);
-
-static struct attribute *msm_ext_disp_fs_attrs[] = {
- &dev_attr_hdmi_audio_cb.attr,
- NULL,
-};
-
-static struct attribute_group msm_ext_disp_fs_attrs_group = {
- .attrs = msm_ext_disp_fs_attrs,
-};
-
-static int msm_ext_disp_sysfs_create(struct msm_ext_disp_init_data *data)
-{
- int ret = 0;
-
- if (!data || !data->kobj) {
- pr_err("Invalid params\n");
- ret = -EINVAL;
- goto end;
- }
-
- ret = sysfs_create_group(data->kobj, &msm_ext_disp_fs_attrs_group);
- if (ret)
- pr_err("Failed, ret=%d\n", ret);
-
-end:
- return ret;
-}
-
-static void msm_ext_disp_sysfs_remove(struct msm_ext_disp_init_data *data)
-{
- if (!data || !data->kobj) {
- pr_err("Invalid params\n");
- return;
- }
-
- sysfs_remove_group(data->kobj, &msm_ext_disp_fs_attrs_group);
-}
-
static const char *msm_ext_disp_name(enum msm_ext_disp_type type)
{
switch (type) {
@@ -293,31 +167,6 @@ end:
return ret;
}
-static void msm_ext_disp_remove_intf_data(struct msm_ext_disp *ext_disp,
- enum msm_ext_disp_type type)
-{
- struct msm_ext_disp_list *node;
- struct list_head *position = NULL;
- struct list_head *temp = NULL;
-
- if (!ext_disp) {
- pr_err("Invalid params\n");
- return;
- }
-
- list_for_each_safe(position, temp, &ext_disp->display_list) {
- node = list_entry(position, struct msm_ext_disp_list, list);
- if (node->data->type == type) {
- msm_ext_disp_sysfs_remove(node->data);
- list_del(&node->list);
- pr_debug("Removed display (%s)\n",
- msm_ext_disp_name(type));
- kfree(node);
- break;
- }
- }
-}
-
static int msm_ext_disp_send_cable_notification(struct msm_ext_disp *ext_disp,
enum msm_ext_disp_cable_state new_state)
{
@@ -661,6 +510,46 @@ static void msm_ext_disp_teardown_done(struct platform_device *pdev)
complete_all(&ext_disp->hpd_comp);
}
+static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack)
+{
+ u32 ack_hpd;
+ int ret = 0;
+ struct msm_ext_disp *ext_disp = NULL;
+
+ if (!pdev) {
+ pr_err("Invalid platform device\n");
+ return -EINVAL;
+ }
+
+ ext_disp = platform_get_drvdata(pdev);
+ if (!ext_disp) {
+ pr_err("Invalid drvdata\n");
+ return -EINVAL;
+ }
+
+ if (ack & AUDIO_ACK_SET_ENABLE) {
+ ext_disp->ack_enabled = ack & AUDIO_ACK_ENABLE ?
+ true : false;
+
+ pr_debug("audio ack feature %s\n",
+ ext_disp->ack_enabled ? "enabled" : "disabled");
+ goto end;
+ }
+
+ if (!ext_disp->ack_enabled)
+ goto end;
+
+ ack_hpd = ack & AUDIO_ACK_CONNECT;
+
+ pr_debug("%s acknowledging audio (%d)\n",
+ msm_ext_disp_name(ext_disp->current_disp), ack_hpd);
+
+ if (!ext_disp->audio_session_on)
+ complete_all(&ext_disp->hpd_comp);
+end:
+ return ret;
+}
+
static int msm_ext_disp_get_intf_id(struct platform_device *pdev)
{
int ret = 0;
@@ -710,12 +599,14 @@ static int msm_ext_disp_update_audio_ops(struct msm_ext_disp *ext_disp,
ops->cable_status = msm_ext_disp_cable_status;
ops->get_intf_id = msm_ext_disp_get_intf_id;
ops->teardown_done = msm_ext_disp_teardown_done;
+ ops->acknowledge = msm_ext_disp_audio_ack;
} else {
ops->audio_info_setup = NULL;
ops->get_audio_edid_blk = NULL;
ops->cable_status = NULL;
ops->get_intf_id = NULL;
ops->teardown_done = NULL;
+ ops->acknowledge = NULL;
}
end:
return ret;
@@ -755,46 +646,6 @@ end:
return ret;
}
-static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack)
-{
- u32 ack_hpd;
- int ret = 0;
- struct msm_ext_disp *ext_disp = NULL;
-
- if (!pdev) {
- pr_err("Invalid platform device\n");
- return -EINVAL;
- }
-
- ext_disp = platform_get_drvdata(pdev);
- if (!ext_disp) {
- pr_err("Invalid drvdata\n");
- return -EINVAL;
- }
-
- if (ack & AUDIO_ACK_SET_ENABLE) {
- ext_disp->ack_enabled = ack & AUDIO_ACK_ENABLE ?
- true : false;
-
- pr_debug("audio ack feature %s\n",
- ext_disp->ack_enabled ? "enabled" : "disabled");
- goto end;
- }
-
- if (!ext_disp->ack_enabled)
- goto end;
-
- ack_hpd = ack & AUDIO_ACK_CONNECT;
-
- pr_debug("%s acknowledging audio (%d)\n",
- msm_ext_disp_name(ext_disp->current_disp), ack_hpd);
-
- if (!ext_disp->audio_session_on)
- complete_all(&ext_disp->hpd_comp);
-end:
- return ret;
-}
-
int msm_hdmi_register_audio_codec(struct platform_device *pdev,
struct msm_ext_disp_audio_codec_ops *ops)
{
@@ -849,11 +700,6 @@ static int msm_ext_disp_validate_intf(struct msm_ext_disp_init_data *init_data)
return -EINVAL;
}
- if (!init_data->kobj) {
- pr_err("Invalid display intf kobj\n");
- return -EINVAL;
- }
-
if (!init_data->codec_ops.get_audio_edid_blk ||
!init_data->codec_ops.cable_status ||
!init_data->codec_ops.audio_info_setup) {
@@ -899,10 +745,6 @@ int msm_ext_disp_register_intf(struct platform_device *pdev,
if (ret)
goto end;
- ret = msm_ext_disp_sysfs_create(init_data);
- if (ret)
- goto sysfs_failure;
-
init_data->intf_ops.hpd = msm_ext_disp_hpd;
init_data->intf_ops.notify = msm_ext_disp_notify;
@@ -913,8 +755,6 @@ int msm_ext_disp_register_intf(struct platform_device *pdev,
return ret;
-sysfs_failure:
- msm_ext_disp_remove_intf_data(ext_disp, init_data->type);
end:
mutex_unlock(&ext_disp->lock);
@@ -1035,7 +875,7 @@ static void __exit msm_ext_disp_exit(void)
platform_driver_unregister(&this_driver);
}
-module_init(msm_ext_disp_init);
+subsys_initcall(msm_ext_disp_init);
module_exit(msm_ext_disp_exit);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/x86/toshiba-wmi.c b/drivers/platform/x86/toshiba-wmi.c
index feac4576b837..2df07ee8f3c3 100644
--- a/drivers/platform/x86/toshiba-wmi.c
+++ b/drivers/platform/x86/toshiba-wmi.c
@@ -24,14 +24,15 @@
#include <linux/acpi.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
+#include <linux/dmi.h>
MODULE_AUTHOR("Azael Avalos");
MODULE_DESCRIPTION("Toshiba WMI Hotkey Driver");
MODULE_LICENSE("GPL");
-#define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100"
+#define WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100"
-MODULE_ALIAS("wmi:"TOSHIBA_WMI_EVENT_GUID);
+MODULE_ALIAS("wmi:"WMI_EVENT_GUID);
static struct input_dev *toshiba_wmi_input_dev;
@@ -63,6 +64,16 @@ static void toshiba_wmi_notify(u32 value, void *context)
kfree(response.pointer);
}
+static struct dmi_system_id toshiba_wmi_dmi_table[] __initdata = {
+ {
+ .ident = "Toshiba laptop",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ },
+ },
+ {}
+};
+
static int __init toshiba_wmi_input_setup(void)
{
acpi_status status;
@@ -81,7 +92,7 @@ static int __init toshiba_wmi_input_setup(void)
if (err)
goto err_free_dev;
- status = wmi_install_notify_handler(TOSHIBA_WMI_EVENT_GUID,
+ status = wmi_install_notify_handler(WMI_EVENT_GUID,
toshiba_wmi_notify, NULL);
if (ACPI_FAILURE(status)) {
err = -EIO;
@@ -95,7 +106,7 @@ static int __init toshiba_wmi_input_setup(void)
return 0;
err_remove_notifier:
- wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID);
+ wmi_remove_notify_handler(WMI_EVENT_GUID);
err_free_keymap:
sparse_keymap_free(toshiba_wmi_input_dev);
err_free_dev:
@@ -105,7 +116,7 @@ static int __init toshiba_wmi_input_setup(void)
static void toshiba_wmi_input_destroy(void)
{
- wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID);
+ wmi_remove_notify_handler(WMI_EVENT_GUID);
sparse_keymap_free(toshiba_wmi_input_dev);
input_unregister_device(toshiba_wmi_input_dev);
}
@@ -114,7 +125,8 @@ static int __init toshiba_wmi_init(void)
{
int ret;
- if (!wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
+ if (!wmi_has_guid(WMI_EVENT_GUID) ||
+ !dmi_check_system(toshiba_wmi_dmi_table))
return -ENODEV;
ret = toshiba_wmi_input_setup();
@@ -130,7 +142,7 @@ static int __init toshiba_wmi_init(void)
static void __exit toshiba_wmi_exit(void)
{
- if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
+ if (wmi_has_guid(WMI_EVENT_GUID))
toshiba_wmi_input_destroy();
}
diff --git a/drivers/power/bq24257_charger.c b/drivers/power/bq24257_charger.c
index 1fea2c7ef97f..6fc31bdc639b 100644
--- a/drivers/power/bq24257_charger.c
+++ b/drivers/power/bq24257_charger.c
@@ -1068,6 +1068,12 @@ static int bq24257_probe(struct i2c_client *client,
return ret;
}
+ ret = bq24257_power_supply_init(bq);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register power supply\n");
+ return ret;
+ }
+
ret = devm_request_threaded_irq(dev, client->irq, NULL,
bq24257_irq_handler_thread,
IRQF_TRIGGER_FALLING |
@@ -1078,12 +1084,6 @@ static int bq24257_probe(struct i2c_client *client,
return ret;
}
- ret = bq24257_power_supply_init(bq);
- if (ret < 0) {
- dev_err(dev, "Failed to register power supply\n");
- return ret;
- }
-
ret = sysfs_create_group(&bq->charger->dev.kobj, &bq24257_attr_group);
if (ret < 0) {
dev_err(dev, "Can't create sysfs entries\n");
diff --git a/drivers/power/goldfish_battery.c b/drivers/power/goldfish_battery.c
index a50bb988c69a..f5c525e4482a 100644
--- a/drivers/power/goldfish_battery.c
+++ b/drivers/power/goldfish_battery.c
@@ -24,6 +24,7 @@
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/acpi.h>
struct goldfish_battery_data {
void __iomem *reg_base;
@@ -227,11 +228,25 @@ static int goldfish_battery_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id goldfish_battery_of_match[] = {
+ { .compatible = "google,goldfish-battery", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_battery_of_match);
+
+static const struct acpi_device_id goldfish_battery_acpi_match[] = {
+ { "GFSH0001", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match);
+
static struct platform_driver goldfish_battery_device = {
.probe = goldfish_battery_probe,
.remove = goldfish_battery_remove,
.driver = {
- .name = "goldfish-battery"
+ .name = "goldfish-battery",
+ .of_match_table = goldfish_battery_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_battery_acpi_match),
}
};
module_platform_driver(goldfish_battery_device);
diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c
index 9c65f134d447..da7a75f82489 100644
--- a/drivers/power/max17042_battery.c
+++ b/drivers/power/max17042_battery.c
@@ -457,13 +457,16 @@ static inline void max17042_write_model_data(struct max17042_chip *chip,
}
static inline void max17042_read_model_data(struct max17042_chip *chip,
- u8 addr, u32 *data, int size)
+ u8 addr, u16 *data, int size)
{
struct regmap *map = chip->regmap;
int i;
+ u32 tmp;
- for (i = 0; i < size; i++)
- regmap_read(map, addr + i, &data[i]);
+ for (i = 0; i < size; i++) {
+ regmap_read(map, addr + i, &tmp);
+ data[i] = (u16)tmp;
+ }
}
static inline int max17042_model_data_compare(struct max17042_chip *chip,
@@ -486,7 +489,7 @@ static int max17042_init_model(struct max17042_chip *chip)
{
int ret;
int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
- u32 *temp_data;
+ u16 *temp_data;
temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
if (!temp_data)
@@ -501,7 +504,7 @@ static int max17042_init_model(struct max17042_chip *chip)
ret = max17042_model_data_compare(
chip,
chip->pdata->config_data->cell_char_tbl,
- (u16 *)temp_data,
+ temp_data,
table_size);
max10742_lock_model(chip);
@@ -514,7 +517,7 @@ static int max17042_verify_model_lock(struct max17042_chip *chip)
{
int i;
int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
- u32 *temp_data;
+ u16 *temp_data;
int ret = 0;
temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
diff --git a/drivers/power/qcom/apm.c b/drivers/power/qcom/apm.c
index 6181b08a41a5..b5ae7f4ade03 100644
--- a/drivers/power/qcom/apm.c
+++ b/drivers/power/qcom/apm.c
@@ -27,6 +27,7 @@
#include <linux/string.h>
#include <linux/power/qcom/apm.h>
#include <soc/qcom/scm.h>
+#include <linux/arm-smccc.h>
/*
* VDD_APCC
@@ -79,7 +80,6 @@
#define MSM_APM_DRIVER_NAME "qcom,msm-apm"
-asmlinkage int __invoke_psci_fn_smc(u64, u64, u64, u64);
enum {
CLOCK_ASSERT_ENABLE,
diff --git a/drivers/power/reset/hisi-reboot.c b/drivers/power/reset/hisi-reboot.c
index 9ab7f562a83b..f69387e12c1e 100644
--- a/drivers/power/reset/hisi-reboot.c
+++ b/drivers/power/reset/hisi-reboot.c
@@ -53,13 +53,16 @@ static int hisi_reboot_probe(struct platform_device *pdev)
if (of_property_read_u32(np, "reboot-offset", &reboot_offset) < 0) {
pr_err("failed to find reboot-offset property\n");
+ iounmap(base);
return -EINVAL;
}
err = register_restart_handler(&hisi_restart_nb);
- if (err)
+ if (err) {
dev_err(&pdev->dev, "cannot register restart handler (err=%d)\n",
err);
+ iounmap(base);
+ }
return err;
}
diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig
index b919c688e627..47b201738672 100644
--- a/drivers/power/supply/qcom/Kconfig
+++ b/drivers/power/supply/qcom/Kconfig
@@ -1,23 +1,5 @@
menu "Qualcomm Technologies Inc Charger and Fuel Gauge support"
-config QPNP_SMBCHARGER
- tristate "QPNP SMB Charger driver"
- depends on MFD_SPMI_PMIC
- help
- Say Y here to enable the dual path switch mode battery charger which
- supports USB detection and battery charging up to 3A.
- The driver also offers relevant information to userspace via the
- power supply framework.
-
-config QPNP_FG
- tristate "QPNP fuel gauge driver"
- depends on MFD_SPMI_PMIC
- help
- Say Y here to enable the Fuel Gauge driver. This adds support for
- battery fuel gauging and state of charge of battery connected to the
- fuel gauge. The state of charge is reported through a BMS power
- supply property and also sends uevents when the capacity is updated.
-
config QPNP_FG_GEN3
tristate "QPNP GEN3 fuel gauge driver"
depends on MFD_SPMI_PMIC
diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile
index a3aff858c190..dfa83f2304b2 100644
--- a/drivers/power/supply/qcom/Makefile
+++ b/drivers/power/supply/qcom/Makefile
@@ -1,5 +1,3 @@
-obj-$(CONFIG_QPNP_SMBCHARGER) += qpnp-smbcharger.o batterydata-lib.o pmic-voter.o
-obj-$(CONFIG_QPNP_FG) += qpnp-fg.o
obj-$(CONFIG_QPNP_FG_GEN3) += qpnp-fg-gen3.o fg-memif.o fg-util.o
obj-$(CONFIG_SMB135X_CHARGER) += smb135x-charger.o pmic-voter.o
obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o battery.o
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index 4973f91c8af1..5b320ca8e74e 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -35,12 +35,15 @@
#define PARALLEL_PSY_VOTER "PARALLEL_PSY_VOTER"
#define PL_HW_ABSENT_VOTER "PL_HW_ABSENT_VOTER"
#define PL_VOTER "PL_VOTER"
+#define RESTRICT_CHG_VOTER "RESTRICT_CHG_VOTER"
struct pl_data {
int pl_mode;
int slave_pct;
int taper_pct;
int slave_fcc_ua;
+ int restricted_current;
+ bool restricted_charging_enabled;
struct votable *fcc_votable;
struct votable *fv_votable;
struct votable *pl_disable_votable;
@@ -80,6 +83,8 @@ module_param_named(debug_mask, debug_mask, int, S_IRUSR | S_IWUSR);
enum {
VER = 0,
SLAVE_PCT,
+ RESTRICT_CHG_ENABLE,
+ RESTRICT_CHG_CURRENT,
};
/*******
@@ -180,10 +185,78 @@ static ssize_t slave_pct_store(struct class *c, struct class_attribute *attr,
return count;
}
+/**********************
+* RESTICTED CHARGIGNG *
+***********************/
+static ssize_t restrict_chg_show(struct class *c, struct class_attribute *attr,
+ char *ubuf)
+{
+ struct pl_data *chip = container_of(c, struct pl_data,
+ qcom_batt_class);
+
+ return snprintf(ubuf, PAGE_SIZE, "%d\n",
+ chip->restricted_charging_enabled);
+}
+
+static ssize_t restrict_chg_store(struct class *c, struct class_attribute *attr,
+ const char *ubuf, size_t count)
+{
+ struct pl_data *chip = container_of(c, struct pl_data,
+ qcom_batt_class);
+ unsigned long val;
+
+ if (kstrtoul(ubuf, 10, &val))
+ return -EINVAL;
+
+ if (chip->restricted_charging_enabled == !!val)
+ goto no_change;
+
+ chip->restricted_charging_enabled = !!val;
+
+ vote(chip->fcc_votable, RESTRICT_CHG_VOTER,
+ chip->restricted_charging_enabled,
+ chip->restricted_current);
+
+no_change:
+ return count;
+}
+
+static ssize_t restrict_cur_show(struct class *c, struct class_attribute *attr,
+ char *ubuf)
+{
+ struct pl_data *chip = container_of(c, struct pl_data,
+ qcom_batt_class);
+
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", chip->restricted_current);
+}
+
+static ssize_t restrict_cur_store(struct class *c, struct class_attribute *attr,
+ const char *ubuf, size_t count)
+{
+ struct pl_data *chip = container_of(c, struct pl_data,
+ qcom_batt_class);
+ unsigned long val;
+
+ if (kstrtoul(ubuf, 10, &val))
+ return -EINVAL;
+
+ chip->restricted_current = val;
+
+ vote(chip->fcc_votable, RESTRICT_CHG_VOTER,
+ chip->restricted_charging_enabled,
+ chip->restricted_current);
+
+ return count;
+}
+
static struct class_attribute pl_attributes[] = {
[VER] = __ATTR_RO(version),
[SLAVE_PCT] = __ATTR(parallel_pct, S_IRUGO | S_IWUSR,
slave_pct_show, slave_pct_store),
+ [RESTRICT_CHG_ENABLE] = __ATTR(restricted_charging, S_IRUGO | S_IWUSR,
+ restrict_chg_show, restrict_chg_store),
+ [RESTRICT_CHG_CURRENT] = __ATTR(restricted_current, S_IRUGO | S_IWUSR,
+ restrict_cur_show, restrict_cur_store),
__ATTR_NULL,
};
@@ -750,6 +823,7 @@ static int pl_determine_initial_status(struct pl_data *chip)
return 0;
}
+#define DEFAULT_RESTRICTED_CURRENT_UA 1000000
static int pl_init(void)
{
struct pl_data *chip;
@@ -759,6 +833,7 @@ static int pl_init(void)
if (!chip)
return -ENOMEM;
chip->slave_pct = 50;
+ chip->restricted_current = DEFAULT_RESTRICTED_CURRENT_UA;
chip->pl_ws = wakeup_source_register("qcom-battery");
if (!chip->pl_ws)
diff --git a/drivers/power/supply/qcom/battery_current_limit.c b/drivers/power/supply/qcom/battery_current_limit.c
index d2c25bfbf66c..410e64321ba6 100644
--- a/drivers/power/supply/qcom/battery_current_limit.c
+++ b/drivers/power/supply/qcom/battery_current_limit.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -204,6 +204,7 @@ static uint32_t bcl_hotplug_request, bcl_hotplug_mask, bcl_soc_hotplug_mask;
static uint32_t bcl_frequency_mask;
static struct work_struct bcl_hotplug_work;
static DEFINE_MUTEX(bcl_hotplug_mutex);
+static DEFINE_MUTEX(bcl_cpufreq_mutex);
static bool bcl_hotplug_enabled;
static uint32_t battery_soc_val = 100;
static uint32_t soc_low_threshold;
@@ -251,6 +252,7 @@ static void update_cpu_freq(void)
union device_request cpufreq_req;
trace_bcl_sw_mitigation_event("Start Frequency Mitigate");
+ mutex_lock(&bcl_cpufreq_mutex);
cpufreq_req.freq.max_freq = UINT_MAX;
cpufreq_req.freq.min_freq = CPUFREQ_MIN_NO_MITIGATION;
@@ -275,6 +277,7 @@ static void update_cpu_freq(void)
pr_err("Error updating freq for CPU%d. ret:%d\n",
cpu, ret);
}
+ mutex_unlock(&bcl_cpufreq_mutex);
trace_bcl_sw_mitigation_event("End Frequency Mitigation");
}
@@ -285,23 +288,13 @@ static void soc_mitigate(struct work_struct *work)
update_cpu_freq();
}
-static int power_supply_callback(struct notifier_block *nb,
- unsigned long event, void *data)
+static int get_and_evaluate_battery_soc(void)
{
- struct power_supply *psy = data;
static struct power_supply *batt_psy;
union power_supply_propval ret = {0,};
int battery_percentage;
enum bcl_threshold_state prev_soc_state;
- if (gbcl->bcl_mode != BCL_DEVICE_ENABLED) {
- pr_debug("BCL is not enabled\n");
- return NOTIFY_OK;
- }
-
- if (strcmp(psy->desc->name, "battery"))
- return NOTIFY_OK;
-
if (!batt_psy)
batt_psy = power_supply_get_by_name("battery");
if (batt_psy) {
@@ -325,6 +318,22 @@ static int power_supply_callback(struct notifier_block *nb,
return NOTIFY_OK;
}
+static int power_supply_callback(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct power_supply *psy = data;
+
+ if (gbcl->bcl_mode != BCL_DEVICE_ENABLED) {
+ pr_debug("BCL is not enabled\n");
+ return NOTIFY_OK;
+ }
+
+ if (strcmp(psy->desc->name, "battery"))
+ return NOTIFY_OK;
+
+ return get_and_evaluate_battery_soc();
+}
+
static int bcl_get_battery_voltage(int *vbatt_mv)
{
static struct power_supply *psy;
@@ -643,7 +652,7 @@ static void bcl_periph_mode_set(enum bcl_device_mode mode)
* power state changes. Make sure we read the current SoC
* and mitigate.
*/
- power_supply_callback(&gbcl->psy_nb, 1, gbcl);
+ get_and_evaluate_battery_soc();
ret = power_supply_reg_notifier(&gbcl->psy_nb);
if (ret < 0) {
pr_err("Unable to register soc notifier rc = %d\n",
diff --git a/drivers/power/supply/qcom/bcl_peripheral.c b/drivers/power/supply/qcom/bcl_peripheral.c
index cae4967f1ef4..2d237f27b062 100644
--- a/drivers/power/supply/qcom/bcl_peripheral.c
+++ b/drivers/power/supply/qcom/bcl_peripheral.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -482,8 +482,10 @@ static int bcl_access_monitor_enable(bool enable)
if (enable == bcl_perph->enabled)
goto access_exit;
- if ((bcl_perph_version == BCL_PMI8998) && !hw_enabled && enable)
+ if ((bcl_perph_version == BCL_PMI8998) && !hw_enabled && enable) {
bcl_lmh_dcvs_enable();
+ hw_enabled = true;
+ }
for (; i < BCL_PARAM_MAX; i++) {
perph_data = &bcl_perph->param[i];
diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index c146654e438b..32a25b4e2c7b 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -143,6 +143,7 @@ enum fg_sram_param_id {
FG_SRAM_FULL_SOC,
FG_SRAM_VOLTAGE_PRED,
FG_SRAM_OCV,
+ FG_SRAM_ESR,
FG_SRAM_RSLOW,
FG_SRAM_ALG_FLAGS,
FG_SRAM_CC_SOC,
@@ -233,6 +234,7 @@ struct fg_dt_props {
int esr_timer_awake;
int esr_timer_asleep;
int rconn_mohms;
+ int esr_clamp_mohms;
int cl_start_soc;
int cl_max_temp;
int cl_min_temp;
diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c
index d5c4f8ffaac3..f2395b6ba4ab 100644
--- a/drivers/power/supply/qcom/fg-util.c
+++ b/drivers/power/supply/qcom/fg-util.c
@@ -358,7 +358,7 @@ int fg_write(struct fg_chip *chip, int addr, u8 *val, int len)
return -ENXIO;
mutex_lock(&chip->bus_lock);
- sec_access = (addr & 0xFF00) > 0xD0;
+ sec_access = (addr & 0x00FF) > 0xD0;
if (sec_access) {
rc = regmap_write(chip->regmap, (addr & 0xFF00) | 0xD0, 0xA5);
if (rc < 0) {
@@ -398,7 +398,7 @@ int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val)
return -ENXIO;
mutex_lock(&chip->bus_lock);
- sec_access = (addr & 0xFF00) > 0xD0;
+ sec_access = (addr & 0x00FF) > 0xD0;
if (sec_access) {
rc = regmap_write(chip->regmap, (addr & 0xFF00) | 0xD0, 0xA5);
if (rc < 0) {
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index 1fd092c550f3..39afc235fbc3 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -99,6 +99,8 @@
#define VOLTAGE_PRED_OFFSET 0
#define OCV_WORD 97
#define OCV_OFFSET 2
+#define ESR_WORD 99
+#define ESR_OFFSET 0
#define RSLOW_WORD 101
#define RSLOW_OFFSET 0
#define ACT_BATT_CAP_WORD 117
@@ -173,6 +175,8 @@ static struct fg_sram_param pmi8998_v1_sram_params[] = {
244141, 0, NULL, fg_decode_voltage_15b),
PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 1000, 244141, 0, NULL,
fg_decode_voltage_15b),
+ PARAM(ESR, ESR_WORD, ESR_OFFSET, 2, 1000, 244141, 0, fg_encode_default,
+ fg_decode_value_16b),
PARAM(RSLOW, RSLOW_WORD, RSLOW_OFFSET, 2, 1000, 244141, 0, NULL,
fg_decode_value_16b),
PARAM(ALG_FLAGS, ALG_FLAGS_WORD, ALG_FLAGS_OFFSET, 1, 1, 1, 0, NULL,
@@ -235,6 +239,8 @@ static struct fg_sram_param pmi8998_v2_sram_params[] = {
244141, 0, NULL, fg_decode_voltage_15b),
PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 1000, 244141, 0, NULL,
fg_decode_voltage_15b),
+ PARAM(ESR, ESR_WORD, ESR_OFFSET, 2, 1000, 244141, 0, fg_encode_default,
+ fg_decode_value_16b),
PARAM(RSLOW, RSLOW_WORD, RSLOW_OFFSET, 2, 1000, 244141, 0, NULL,
fg_decode_value_16b),
PARAM(ALG_FLAGS, ALG_FLAGS_WORD, ALG_FLAGS_OFFSET, 1, 1, 1, 0, NULL,
@@ -571,38 +577,11 @@ static int fg_get_battery_temp(struct fg_chip *chip, int *val)
return 0;
}
-#define BATT_ESR_NUMR 244141
-#define BATT_ESR_DENR 1000
-static int fg_get_battery_esr(struct fg_chip *chip, int *val)
-{
- int rc = 0;
- u16 temp = 0;
- u8 buf[2];
-
- rc = fg_read(chip, BATT_INFO_ESR_LSB(chip), buf, 2);
- if (rc < 0) {
- pr_err("failed to read addr=0x%04x, rc=%d\n",
- BATT_INFO_ESR_LSB(chip), rc);
- return rc;
- }
-
- if (chip->wa_flags & PMI8998_V1_REV_WA)
- temp = ((buf[0] & ESR_MSB_MASK) << 8) |
- (buf[1] & ESR_LSB_MASK);
- else
- temp = ((buf[1] & ESR_MSB_MASK) << 8) |
- (buf[0] & ESR_LSB_MASK);
-
- pr_debug("buf: %x %x temp: %x\n", buf[0], buf[1], temp);
- *val = div_u64((u64)temp * BATT_ESR_NUMR, BATT_ESR_DENR);
- return 0;
-}
-
static int fg_get_battery_resistance(struct fg_chip *chip, int *val)
{
int rc, esr_uohms, rslow_uohms;
- rc = fg_get_battery_esr(chip, &esr_uohms);
+ rc = fg_get_sram_prop(chip, FG_SRAM_ESR, &esr_uohms);
if (rc < 0) {
pr_err("failed to get ESR, rc=%d\n", rc);
return rc;
@@ -1631,7 +1610,7 @@ static int fg_rconn_config(struct fg_chip *chip)
return 0;
}
- rc = fg_get_battery_esr(chip, &esr_uohms);
+ rc = fg_get_sram_prop(chip, FG_SRAM_ESR, &esr_uohms);
if (rc < 0) {
pr_err("failed to get ESR, rc=%d\n", rc);
return rc;
@@ -2744,6 +2723,39 @@ out:
return rc;
}
+static int fg_esr_validate(struct fg_chip *chip)
+{
+ int rc, esr_uohms;
+ u8 buf[2];
+
+ if (chip->dt.esr_clamp_mohms <= 0)
+ return 0;
+
+ rc = fg_get_sram_prop(chip, FG_SRAM_ESR, &esr_uohms);
+ if (rc < 0) {
+ pr_err("failed to get ESR, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (esr_uohms >= chip->dt.esr_clamp_mohms * 1000) {
+ pr_debug("ESR %d is > ESR_clamp\n", esr_uohms);
+ return 0;
+ }
+
+ esr_uohms = chip->dt.esr_clamp_mohms * 1000;
+ fg_encode(chip->sp, FG_SRAM_ESR, esr_uohms, buf);
+ rc = fg_sram_write(chip, chip->sp[FG_SRAM_ESR].addr_word,
+ chip->sp[FG_SRAM_ESR].addr_byte, buf,
+ chip->sp[FG_SRAM_ESR].len, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing ESR, rc=%d\n", rc);
+ return rc;
+ }
+
+ fg_dbg(chip, FG_STATUS, "ESR clamped to %duOhms\n", esr_uohms);
+ return 0;
+}
+
/* PSY CALLBACKS STAY HERE */
static int fg_psy_get_property(struct power_supply *psy,
@@ -3371,6 +3383,10 @@ static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data)
if (rc < 0)
pr_err("Error in updating maint_soc, rc=%d\n", rc);
+ rc = fg_esr_validate(chip);
+ if (rc < 0)
+ pr_err("Error in validating ESR, rc=%d\n", rc);
+
if (batt_psy_initialized(chip))
power_supply_changed(chip->batt_psy);
@@ -3659,6 +3675,7 @@ static int fg_parse_ki_coefficients(struct fg_chip *chip)
#define DEFAULT_ESR_BROAD_FLT_UPCT 99610
#define DEFAULT_ESR_TIGHT_LT_FLT_UPCT 48829
#define DEFAULT_ESR_BROAD_LT_FLT_UPCT 148438
+#define DEFAULT_ESR_CLAMP_MOHMS 20
static int fg_parse_dt(struct fg_chip *chip)
{
struct device_node *child, *revid_node, *node = chip->dev->of_node;
@@ -3972,6 +3989,12 @@ static int fg_parse_dt(struct fg_chip *chip)
if (rc < 0)
pr_err("Error in parsing slope limit coeffs, rc=%d\n", rc);
+ rc = of_property_read_u32(node, "qcom,fg-esr-clamp-mohms", &temp);
+ if (rc < 0)
+ chip->dt.esr_clamp_mohms = DEFAULT_ESR_CLAMP_MOHMS;
+ else
+ chip->dt.esr_clamp_mohms = temp;
+
return 0;
}
diff --git a/drivers/power/supply/qcom/qpnp-fg.c b/drivers/power/supply/qcom/qpnp-fg.c
deleted file mode 100644
index cfd2f64a9bb8..000000000000
--- a/drivers/power/supply/qcom/qpnp-fg.c
+++ /dev/null
@@ -1,7051 +0,0 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#define pr_fmt(fmt) "FG: %s: " fmt, __func__
-
-#include <linux/atomic.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/regmap.h>
-#include <linux/of.h>
-#include <linux/rtc.h>
-#include <linux/err.h>
-#include <linux/debugfs.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/init.h>
-#include <linux/spmi.h>
-#include <linux/platform_device.h>
-#include <linux/of_irq.h>
-#include <linux/interrupt.h>
-#include <linux/bitops.h>
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/ktime.h>
-#include <linux/power_supply.h>
-#include <linux/of_batterydata.h>
-#include <linux/string_helpers.h>
-#include <linux/alarmtimer.h>
-#include <linux/qpnp/qpnp-revid.h>
-
-/* Register offsets */
-
-/* Interrupt offsets */
-#define INT_RT_STS(base) (base + 0x10)
-#define INT_EN_CLR(base) (base + 0x16)
-
-/* SPMI Register offsets */
-#define SOC_MONOTONIC_SOC 0x09
-#define SOC_BOOT_MOD 0x50
-#define SOC_RESTART 0x51
-
-#define REG_OFFSET_PERP_SUBTYPE 0x05
-
-/* RAM register offsets */
-#define RAM_OFFSET 0x400
-
-/* Bit/Mask definitions */
-#define FULL_PERCENT 0xFF
-#define MAX_TRIES_SOC 5
-#define MA_MV_BIT_RES 39
-#define MSB_SIGN BIT(7)
-#define IBAT_VBAT_MASK 0x7F
-#define NO_OTP_PROF_RELOAD BIT(6)
-#define REDO_FIRST_ESTIMATE BIT(3)
-#define RESTART_GO BIT(0)
-#define THERM_DELAY_MASK 0xE0
-
-/* SUBTYPE definitions */
-#define FG_SOC 0x9
-#define FG_BATT 0xA
-#define FG_ADC 0xB
-#define FG_MEMIF 0xC
-
-#define QPNP_FG_DEV_NAME "qcom,qpnp-fg"
-#define MEM_IF_TIMEOUT_MS 5000
-#define BUCKET_COUNT 8
-#define BUCKET_SOC_PCT (256 / BUCKET_COUNT)
-
-#define BCL_MA_TO_ADC(_current, _adc_val) { \
- _adc_val = (u8)((_current) * 100 / 976); \
-}
-
-/* Debug Flag Definitions */
-enum {
- FG_SPMI_DEBUG_WRITES = BIT(0), /* Show SPMI writes */
- FG_SPMI_DEBUG_READS = BIT(1), /* Show SPMI reads */
- FG_IRQS = BIT(2), /* Show interrupts */
- FG_MEM_DEBUG_WRITES = BIT(3), /* Show SRAM writes */
- FG_MEM_DEBUG_READS = BIT(4), /* Show SRAM reads */
- FG_POWER_SUPPLY = BIT(5), /* Show POWER_SUPPLY */
- FG_STATUS = BIT(6), /* Show FG status changes */
- FG_AGING = BIT(7), /* Show FG aging algorithm */
-};
-
-/* PMIC REVISIONS */
-#define REVID_RESERVED 0
-#define REVID_VARIANT 1
-#define REVID_ANA_MAJOR 2
-#define REVID_DIG_MAJOR 3
-
-enum dig_major {
- DIG_REV_1 = 0x1,
- DIG_REV_2 = 0x2,
- DIG_REV_3 = 0x3,
-};
-
-enum pmic_subtype {
- PMI8994 = 10,
- PMI8950 = 17,
- PMI8996 = 19,
- PMI8937 = 55,
-};
-
-enum wa_flags {
- IADC_GAIN_COMP_WA = BIT(0),
- USE_CC_SOC_REG = BIT(1),
- PULSE_REQUEST_WA = BIT(2),
- BCL_HI_POWER_FOR_CHGLED_WA = BIT(3)
-};
-
-enum current_sense_type {
- INTERNAL_CURRENT_SENSE,
- EXTERNAL_CURRENT_SENSE,
-};
-
-struct fg_mem_setting {
- u16 address;
- u8 offset;
- int value;
-};
-
-struct fg_mem_data {
- u16 address;
- u8 offset;
- unsigned int len;
- int value;
-};
-
-struct fg_learning_data {
- int64_t cc_uah;
- int64_t learned_cc_uah;
- int init_cc_pc_val;
- bool active;
- bool feedback_on;
- struct mutex learning_lock;
- ktime_t time_stamp;
- /* configuration properties */
- int max_start_soc;
- int max_increment;
- int max_decrement;
- int min_temp;
- int max_temp;
- int vbat_est_thr_uv;
-};
-
-struct fg_rslow_data {
- u8 rslow_cfg;
- u8 rslow_thr;
- u8 rs_to_rslow[2];
- u8 rslow_comp[4];
- uint32_t chg_rs_to_rslow;
- uint32_t chg_rslow_comp_c1;
- uint32_t chg_rslow_comp_c2;
- uint32_t chg_rslow_comp_thr;
- bool active;
- struct mutex lock;
-};
-
-struct fg_cyc_ctr_data {
- bool en;
- bool started[BUCKET_COUNT];
- u16 count[BUCKET_COUNT];
- u8 last_soc[BUCKET_COUNT];
- int id;
- struct mutex lock;
-};
-
-struct fg_iadc_comp_data {
- u8 dfl_gain_reg[2];
- bool gain_active;
- int64_t dfl_gain;
-};
-
-struct fg_cc_soc_data {
- int init_sys_soc;
- int init_cc_soc;
- int full_capacity;
- int delta_soc;
-};
-
-/* FG_MEMIF setting index */
-enum fg_mem_setting_index {
- FG_MEM_SOFT_COLD = 0,
- FG_MEM_SOFT_HOT,
- FG_MEM_HARD_COLD,
- FG_MEM_HARD_HOT,
- FG_MEM_RESUME_SOC,
- FG_MEM_BCL_LM_THRESHOLD,
- FG_MEM_BCL_MH_THRESHOLD,
- FG_MEM_TERM_CURRENT,
- FG_MEM_CHG_TERM_CURRENT,
- FG_MEM_IRQ_VOLT_EMPTY,
- FG_MEM_CUTOFF_VOLTAGE,
- FG_MEM_VBAT_EST_DIFF,
- FG_MEM_DELTA_SOC,
- FG_MEM_BATT_LOW,
- FG_MEM_THERM_DELAY,
- FG_MEM_SETTING_MAX,
-};
-
-/* FG_MEMIF data index */
-enum fg_mem_data_index {
- FG_DATA_BATT_TEMP = 0,
- FG_DATA_OCV,
- FG_DATA_VOLTAGE,
- FG_DATA_CURRENT,
- FG_DATA_BATT_ESR,
- FG_DATA_BATT_ESR_COUNT,
- FG_DATA_BATT_SOC,
- FG_DATA_CC_CHARGE,
- FG_DATA_VINT_ERR,
- FG_DATA_CPRED_VOLTAGE,
- /* values below this only gets read once per profile reload */
- FG_DATA_BATT_ID,
- FG_DATA_BATT_ID_INFO,
- FG_DATA_MAX,
-};
-
-#define SETTING(_idx, _address, _offset, _value) \
- [FG_MEM_##_idx] = { \
- .address = _address, \
- .offset = _offset, \
- .value = _value, \
- } \
-
-static struct fg_mem_setting settings[FG_MEM_SETTING_MAX] = {
- /* ID Address, Offset, Value*/
- SETTING(SOFT_COLD, 0x454, 0, 100),
- SETTING(SOFT_HOT, 0x454, 1, 400),
- SETTING(HARD_COLD, 0x454, 2, 50),
- SETTING(HARD_HOT, 0x454, 3, 450),
- SETTING(RESUME_SOC, 0x45C, 1, 0),
- SETTING(BCL_LM_THRESHOLD, 0x47C, 2, 50),
- SETTING(BCL_MH_THRESHOLD, 0x47C, 3, 752),
- SETTING(TERM_CURRENT, 0x40C, 2, 250),
- SETTING(CHG_TERM_CURRENT, 0x4F8, 2, 250),
- SETTING(IRQ_VOLT_EMPTY, 0x458, 3, 3100),
- SETTING(CUTOFF_VOLTAGE, 0x40C, 0, 3200),
- SETTING(VBAT_EST_DIFF, 0x000, 0, 30),
- SETTING(DELTA_SOC, 0x450, 3, 1),
- SETTING(BATT_LOW, 0x458, 0, 4200),
- SETTING(THERM_DELAY, 0x4AC, 3, 0),
-};
-
-#define DATA(_idx, _address, _offset, _length, _value) \
- [FG_DATA_##_idx] = { \
- .address = _address, \
- .offset = _offset, \
- .len = _length, \
- .value = _value, \
- } \
-
-static struct fg_mem_data fg_data[FG_DATA_MAX] = {
- /* ID Address, Offset, Length, Value*/
- DATA(BATT_TEMP, 0x550, 2, 2, -EINVAL),
- DATA(OCV, 0x588, 3, 2, -EINVAL),
- DATA(VOLTAGE, 0x5CC, 1, 2, -EINVAL),
- DATA(CURRENT, 0x5CC, 3, 2, -EINVAL),
- DATA(BATT_ESR, 0x554, 2, 2, -EINVAL),
- DATA(BATT_ESR_COUNT, 0x558, 2, 2, -EINVAL),
- DATA(BATT_SOC, 0x56C, 1, 3, -EINVAL),
- DATA(CC_CHARGE, 0x570, 0, 4, -EINVAL),
- DATA(VINT_ERR, 0x560, 0, 4, -EINVAL),
- DATA(CPRED_VOLTAGE, 0x540, 0, 2, -EINVAL),
- DATA(BATT_ID, 0x594, 1, 1, -EINVAL),
- DATA(BATT_ID_INFO, 0x594, 3, 1, -EINVAL),
-};
-
-static int fg_debug_mask;
-module_param_named(
- debug_mask, fg_debug_mask, int, S_IRUSR | S_IWUSR
-);
-
-static int fg_sense_type = -EINVAL;
-static int fg_restart;
-
-static int fg_est_dump;
-module_param_named(
- first_est_dump, fg_est_dump, int, S_IRUSR | S_IWUSR
-);
-
-static char *fg_batt_type;
-module_param_named(
- battery_type, fg_batt_type, charp, S_IRUSR | S_IWUSR
-);
-
-static int fg_sram_update_period_ms = 30000;
-module_param_named(
- sram_update_period_ms, fg_sram_update_period_ms, int, S_IRUSR | S_IWUSR
-);
-
-struct fg_irq {
- int irq;
- unsigned long disabled;
-};
-
-enum fg_soc_irq {
- HIGH_SOC,
- LOW_SOC,
- FULL_SOC,
- EMPTY_SOC,
- DELTA_SOC,
- FIRST_EST_DONE,
- SW_FALLBK_OCV,
- SW_FALLBK_NEW_BATT,
- FG_SOC_IRQ_COUNT,
-};
-
-enum fg_batt_irq {
- JEITA_SOFT_COLD,
- JEITA_SOFT_HOT,
- VBATT_LOW,
- BATT_IDENTIFIED,
- BATT_ID_REQ,
- BATTERY_UNKNOWN,
- BATT_MISSING,
- BATT_MATCH,
- FG_BATT_IRQ_COUNT,
-};
-
-enum fg_mem_if_irq {
- FG_MEM_AVAIL,
- TA_RCVRY_SUG,
- FG_MEM_IF_IRQ_COUNT,
-};
-
-enum fg_batt_aging_mode {
- FG_AGING_NONE,
- FG_AGING_ESR,
- FG_AGING_CC,
-};
-
-enum register_type {
- MEM_INTF_CFG,
- MEM_INTF_CTL,
- MEM_INTF_ADDR_LSB,
- MEM_INTF_RD_DATA0,
- MEM_INTF_WR_DATA0,
- MAX_ADDRESS,
-};
-
-struct register_offset {
- u16 address[MAX_ADDRESS];
-};
-
-static struct register_offset offset[] = {
- [0] = {
- /* CFG CTL LSB RD0 WD0 */
- .address = {0x40, 0x41, 0x42, 0x4C, 0x48},
- },
- [1] = {
- /* CFG CTL LSB RD0 WD0 */
- .address = {0x50, 0x51, 0x61, 0x67, 0x63},
- },
-};
-
-#define MEM_INTF_CFG(chip) \
- ((chip)->mem_base + (chip)->offset[MEM_INTF_CFG])
-#define MEM_INTF_CTL(chip) \
- ((chip)->mem_base + (chip)->offset[MEM_INTF_CTL])
-#define MEM_INTF_ADDR_LSB(chip) \
- ((chip)->mem_base + (chip)->offset[MEM_INTF_ADDR_LSB])
-#define MEM_INTF_RD_DATA0(chip) \
- ((chip)->mem_base + (chip)->offset[MEM_INTF_RD_DATA0])
-#define MEM_INTF_WR_DATA0(chip) \
- ((chip)->mem_base + (chip)->offset[MEM_INTF_WR_DATA0])
-
-struct fg_wakeup_source {
- struct wakeup_source source;
- unsigned long enabled;
-};
-
-static void fg_stay_awake(struct fg_wakeup_source *source)
-{
- if (!__test_and_set_bit(0, &source->enabled)) {
- __pm_stay_awake(&source->source);
- pr_debug("enabled source %s\n", source->source.name);
- }
-}
-
-static void fg_relax(struct fg_wakeup_source *source)
-{
- if (__test_and_clear_bit(0, &source->enabled)) {
- __pm_relax(&source->source);
- pr_debug("disabled source %s\n", source->source.name);
- }
-}
-
-#define THERMAL_COEFF_N_BYTES 6
-struct fg_chip {
- struct device *dev;
- struct platform_device *pdev;
- struct regmap *regmap;
- u8 pmic_subtype;
- u8 pmic_revision[4];
- u8 revision[4];
- u16 soc_base;
- u16 batt_base;
- u16 mem_base;
- u16 vbat_adc_addr;
- u16 ibat_adc_addr;
- u16 tp_rev_addr;
- u32 wa_flag;
- atomic_t memif_user_cnt;
- struct fg_irq soc_irq[FG_SOC_IRQ_COUNT];
- struct fg_irq batt_irq[FG_BATT_IRQ_COUNT];
- struct fg_irq mem_irq[FG_MEM_IF_IRQ_COUNT];
- struct completion sram_access_granted;
- struct completion sram_access_revoked;
- struct completion batt_id_avail;
- struct completion first_soc_done;
- struct power_supply *bms_psy;
- struct power_supply_desc bms_psy_d;
- struct mutex rw_lock;
- struct mutex sysfs_restart_lock;
- struct delayed_work batt_profile_init;
- struct work_struct dump_sram;
- struct work_struct status_change_work;
- struct work_struct cycle_count_work;
- struct work_struct battery_age_work;
- struct work_struct update_esr_work;
- struct work_struct set_resume_soc_work;
- struct work_struct rslow_comp_work;
- struct work_struct sysfs_restart_work;
- struct work_struct init_work;
- struct work_struct charge_full_work;
- struct work_struct gain_comp_work;
- struct work_struct bcl_hi_power_work;
- struct power_supply *batt_psy;
- struct power_supply *usb_psy;
- struct power_supply *dc_psy;
- struct fg_wakeup_source memif_wakeup_source;
- struct fg_wakeup_source profile_wakeup_source;
- struct fg_wakeup_source empty_check_wakeup_source;
- struct fg_wakeup_source resume_soc_wakeup_source;
- struct fg_wakeup_source gain_comp_wakeup_source;
- struct fg_wakeup_source capacity_learning_wakeup_source;
- bool first_profile_loaded;
- struct fg_wakeup_source update_temp_wakeup_source;
- struct fg_wakeup_source update_sram_wakeup_source;
- bool fg_restarting;
- bool profile_loaded;
- bool use_otp_profile;
- bool battery_missing;
- bool power_supply_registered;
- bool sw_rbias_ctrl;
- bool use_thermal_coefficients;
- bool esr_strict_filter;
- bool soc_empty;
- bool charge_done;
- bool resume_soc_lowered;
- bool vbat_low_irq_enabled;
- bool charge_full;
- bool hold_soc_while_full;
- bool input_present;
- bool otg_present;
- bool safety_timer_expired;
- bool bad_batt_detection_en;
- bool bcl_lpm_disabled;
- bool charging_disabled;
- struct delayed_work update_jeita_setting;
- struct delayed_work update_sram_data;
- struct delayed_work update_temp_work;
- struct delayed_work check_empty_work;
- char *batt_profile;
- u8 thermal_coefficients[THERMAL_COEFF_N_BYTES];
- u32 cc_cv_threshold_mv;
- unsigned int batt_profile_len;
- unsigned int batt_max_voltage_uv;
- const char *batt_type;
- const char *batt_psy_name;
- unsigned long last_sram_update_time;
- unsigned long last_temp_update_time;
- int64_t ocv_coeffs[12];
- int64_t cutoff_voltage;
- int evaluation_current;
- int ocv_junction_p1p2;
- int ocv_junction_p2p3;
- int nom_cap_uah;
- int actual_cap_uah;
- int status;
- int prev_status;
- int health;
- enum fg_batt_aging_mode batt_aging_mode;
- /* capacity learning */
- struct fg_learning_data learning_data;
- struct alarm fg_cap_learning_alarm;
- struct work_struct fg_cap_learning_work;
- struct fg_cc_soc_data sw_cc_soc_data;
- /* rslow compensation */
- struct fg_rslow_data rslow_comp;
- /* cycle counter */
- struct fg_cyc_ctr_data cyc_ctr;
- /* iadc compensation */
- struct fg_iadc_comp_data iadc_comp_data;
- /* interleaved memory access */
- u16 *offset;
- bool ima_supported;
- bool init_done;
- /* jeita hysteresis */
- bool jeita_hysteresis_support;
- bool batt_hot;
- bool batt_cold;
- int cold_hysteresis;
- int hot_hysteresis;
- /* ESR pulse tuning */
- struct fg_wakeup_source esr_extract_wakeup_source;
- struct work_struct esr_extract_config_work;
- bool esr_extract_disabled;
- bool imptr_pulse_slow_en;
- bool esr_pulse_tune_en;
-};
-
-/* FG_MEMIF DEBUGFS structures */
-#define ADDR_LEN 4 /* 3 byte address + 1 space character */
-#define CHARS_PER_ITEM 3 /* Format is 'XX ' */
-#define ITEMS_PER_LINE 4 /* 4 data items per line */
-#define MAX_LINE_LENGTH (ADDR_LEN + (ITEMS_PER_LINE * CHARS_PER_ITEM) + 1)
-#define MAX_REG_PER_TRANSACTION (8)
-
-static const char *DFS_ROOT_NAME = "fg_memif";
-static const mode_t DFS_MODE = S_IRUSR | S_IWUSR;
-static const char *default_batt_type = "Unknown Battery";
-static const char *loading_batt_type = "Loading Battery Data";
-static const char *missing_batt_type = "Disconnected Battery";
-
-/* Log buffer */
-struct fg_log_buffer {
- size_t rpos; /* Current 'read' position in buffer */
- size_t wpos; /* Current 'write' position in buffer */
- size_t len; /* Length of the buffer */
- char data[0]; /* Log buffer */
-};
-
-/* transaction parameters */
-struct fg_trans {
- u32 cnt; /* Number of bytes to read */
- u16 addr; /* 12-bit address in SRAM */
- u32 offset; /* Offset of last read data + byte offset */
- struct fg_chip *chip;
- struct fg_log_buffer *log; /* log buffer */
- u8 *data; /* fg data that is read */
- struct mutex memif_dfs_lock; /* Prevent thread concurrency */
-};
-
-struct fg_dbgfs {
- u32 cnt;
- u32 addr;
- struct fg_chip *chip;
- struct dentry *root;
- struct mutex lock;
- struct debugfs_blob_wrapper help_msg;
-};
-
-static struct fg_dbgfs dbgfs_data = {
- .lock = __MUTEX_INITIALIZER(dbgfs_data.lock),
- .help_msg = {
- .data =
-"FG Debug-FS support\n"
-"\n"
-"Hierarchy schema:\n"
-"/sys/kernel/debug/fg_memif\n"
-" /help -- Static help text\n"
-" /address -- Starting register address for reads or writes\n"
-" /count -- Number of registers to read (only used for reads)\n"
-" /data -- Initiates the SRAM read (formatted output)\n"
-"\n",
- },
-};
-
-static const struct of_device_id fg_match_table[] = {
- { .compatible = QPNP_FG_DEV_NAME, },
- {}
-};
-
-static char *fg_supplicants[] = {
- "battery",
- "bcl",
- "fg_adc"
-};
-
-#define DEBUG_PRINT_BUFFER_SIZE 64
-static void fill_string(char *str, size_t str_len, u8 *buf, int buf_len)
-{
- int pos = 0;
- int i;
-
- for (i = 0; i < buf_len; i++) {
- pos += scnprintf(str + pos, str_len - pos, "%02X", buf[i]);
- if (i < buf_len - 1)
- pos += scnprintf(str + pos, str_len - pos, " ");
- }
-}
-
-static int fg_write(struct fg_chip *chip, u8 *val, u16 addr, int len)
-{
- int rc = 0;
- struct platform_device *pdev = chip->pdev;
- char str[DEBUG_PRINT_BUFFER_SIZE];
-
- if ((addr & 0xff00) == 0) {
- pr_err("addr cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
- addr, to_spmi_device(pdev->dev.parent)->usid, rc);
- return -EINVAL;
- }
-
- rc = regmap_bulk_write(chip->regmap, addr, val, len);
- if (rc) {
- pr_err("write failed addr=0x%02x sid=0x%02x rc=%d\n",
- addr, to_spmi_device(pdev->dev.parent)->usid, rc);
- return rc;
- }
-
- if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_WRITES)) {
- str[0] = '\0';
- fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len);
- pr_info("write(0x%04X), sid=%d, len=%d; %s\n",
- addr, to_spmi_device(pdev->dev.parent)->usid, len,
- str);
- }
-
- return rc;
-}
-
-static int fg_read(struct fg_chip *chip, u8 *val, u16 addr, int len)
-{
- int rc = 0;
- struct platform_device *pdev = chip->pdev;
- char str[DEBUG_PRINT_BUFFER_SIZE];
-
- if ((addr & 0xff00) == 0) {
- pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
- addr, to_spmi_device(pdev->dev.parent)->usid, rc);
- return -EINVAL;
- }
-
- rc = regmap_bulk_read(chip->regmap, addr, val, len);
- if (rc) {
- pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", addr,
- to_spmi_device(pdev->dev.parent)->usid, rc);
- return rc;
- }
-
- if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_READS)) {
- str[0] = '\0';
- fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len);
- pr_info("read(0x%04x), sid=%d, len=%d; %s\n",
- addr, to_spmi_device(pdev->dev.parent)->usid, len,
- str);
- }
-
- return rc;
-}
-
-static int fg_masked_write(struct fg_chip *chip, u16 addr,
- u8 mask, u8 val, int len)
-{
- int rc;
-
- rc = regmap_update_bits(chip->regmap, addr, mask, val);
- if (rc) {
- pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc);
- return rc;
- }
-
- return rc;
-}
-
-#define RIF_MEM_ACCESS_REQ BIT(7)
-static int fg_check_rif_mem_access(struct fg_chip *chip, bool *status)
-{
- int rc;
- u8 mem_if_sts;
-
- rc = fg_read(chip, &mem_if_sts, MEM_INTF_CFG(chip), 1);
- if (rc) {
- pr_err("failed to read rif_mem status rc=%d\n", rc);
- return rc;
- }
-
- *status = mem_if_sts & RIF_MEM_ACCESS_REQ;
- return 0;
-}
-
-static bool fg_check_sram_access(struct fg_chip *chip)
-{
- int rc;
- u8 mem_if_sts;
- bool rif_mem_sts = false;
-
- rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1);
- if (rc) {
- pr_err("failed to read mem status rc=%d\n", rc);
- return false;
- }
-
- if ((mem_if_sts & BIT(FG_MEM_AVAIL)) == 0)
- return false;
-
- rc = fg_check_rif_mem_access(chip, &rif_mem_sts);
- if (rc)
- return false;
-
- return rif_mem_sts;
-}
-
-static inline int fg_assert_sram_access(struct fg_chip *chip)
-{
- int rc;
- u8 mem_if_sts;
-
- rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1);
- if (rc) {
- pr_err("failed to read mem status rc=%d\n", rc);
- return rc;
- }
-
- if ((mem_if_sts & BIT(FG_MEM_AVAIL)) == 0) {
- pr_err("mem_avail not high: %02x\n", mem_if_sts);
- return -EINVAL;
- }
-
- rc = fg_read(chip, &mem_if_sts, MEM_INTF_CFG(chip), 1);
- if (rc) {
- pr_err("failed to read mem status rc=%d\n", rc);
- return rc;
- }
-
- if ((mem_if_sts & RIF_MEM_ACCESS_REQ) == 0) {
- pr_err("mem_avail not high: %02x\n", mem_if_sts);
- return -EINVAL;
- }
-
- return 0;
-}
-
-#define INTF_CTL_BURST BIT(7)
-#define INTF_CTL_WR_EN BIT(6)
-static int fg_config_access(struct fg_chip *chip, bool write,
- bool burst)
-{
- int rc;
- u8 intf_ctl = 0;
-
- intf_ctl = (write ? INTF_CTL_WR_EN : 0) | (burst ? INTF_CTL_BURST : 0);
-
- rc = fg_write(chip, &intf_ctl, MEM_INTF_CTL(chip), 1);
- if (rc) {
- pr_err("failed to set mem access bit\n");
- return -EIO;
- }
-
- return rc;
-}
-
-static int fg_req_and_wait_access(struct fg_chip *chip, int timeout)
-{
- int rc = 0, ret = 0;
- bool tried_again = false;
-
- if (!fg_check_sram_access(chip)) {
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
- RIF_MEM_ACCESS_REQ, RIF_MEM_ACCESS_REQ, 1);
- if (rc) {
- pr_err("failed to set mem access bit\n");
- return -EIO;
- }
- fg_stay_awake(&chip->memif_wakeup_source);
- }
-
-wait:
- /* Wait for MEM_AVAIL IRQ. */
- ret = wait_for_completion_interruptible_timeout(
- &chip->sram_access_granted,
- msecs_to_jiffies(timeout));
- /* If we were interrupted wait again one more time. */
- if (ret == -ERESTARTSYS && !tried_again) {
- tried_again = true;
- goto wait;
- } else if (ret <= 0) {
- rc = -ETIMEDOUT;
- pr_err("transaction timed out rc=%d\n", rc);
- return rc;
- }
-
- return rc;
-}
-
-static int fg_release_access(struct fg_chip *chip)
-{
- int rc;
-
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
- RIF_MEM_ACCESS_REQ, 0, 1);
- fg_relax(&chip->memif_wakeup_source);
- reinit_completion(&chip->sram_access_granted);
-
- return rc;
-}
-
-static void fg_release_access_if_necessary(struct fg_chip *chip)
-{
- mutex_lock(&chip->rw_lock);
- if (atomic_sub_return(1, &chip->memif_user_cnt) <= 0) {
- fg_release_access(chip);
- }
- mutex_unlock(&chip->rw_lock);
-}
-
-/*
- * fg_mem_lock disallows the fuel gauge to release access until it has been
- * released.
- *
- * an equal number of calls must be made to fg_mem_release for the fuel gauge
- * driver to release the sram access.
- */
-static void fg_mem_lock(struct fg_chip *chip)
-{
- mutex_lock(&chip->rw_lock);
- atomic_add_return(1, &chip->memif_user_cnt);
- mutex_unlock(&chip->rw_lock);
-}
-
-static void fg_mem_release(struct fg_chip *chip)
-{
- fg_release_access_if_necessary(chip);
-}
-
-static int fg_set_ram_addr(struct fg_chip *chip, u16 *address)
-{
- int rc;
-
- rc = fg_write(chip, (u8 *) address,
- chip->mem_base + chip->offset[MEM_INTF_ADDR_LSB], 2);
- if (rc) {
- pr_err("spmi write failed: addr=%03X, rc=%d\n",
- chip->mem_base + chip->offset[MEM_INTF_ADDR_LSB], rc);
- return rc;
- }
-
- return rc;
-}
-
-#define BUF_LEN 4
-static int fg_sub_mem_read(struct fg_chip *chip, u8 *val, u16 address, int len,
- int offset)
-{
- int rc, total_len;
- u8 *rd_data = val;
- char str[DEBUG_PRINT_BUFFER_SIZE];
-
- rc = fg_config_access(chip, 0, (len > 4));
- if (rc)
- return rc;
-
- rc = fg_set_ram_addr(chip, &address);
- if (rc)
- return rc;
-
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("length %d addr=%02X\n", len, address);
-
- total_len = len;
- while (len > 0) {
- if (!offset) {
- rc = fg_read(chip, rd_data, MEM_INTF_RD_DATA0(chip),
- min(len, BUF_LEN));
- } else {
- rc = fg_read(chip, rd_data,
- MEM_INTF_RD_DATA0(chip) + offset,
- min(len, BUF_LEN - offset));
-
- /* manually set address to allow continous reads */
- address += BUF_LEN;
-
- rc = fg_set_ram_addr(chip, &address);
- if (rc)
- return rc;
- }
- if (rc) {
- pr_err("spmi read failed: addr=%03x, rc=%d\n",
- MEM_INTF_RD_DATA0(chip) + offset, rc);
- return rc;
- }
- rd_data += (BUF_LEN - offset);
- len -= (BUF_LEN - offset);
- offset = 0;
- }
-
- if (fg_debug_mask & FG_MEM_DEBUG_READS) {
- fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len);
- pr_info("data: %s\n", str);
- }
- return rc;
-}
-
-static int fg_conventional_mem_read(struct fg_chip *chip, u8 *val, u16 address,
- int len, int offset, bool keep_access)
-{
- int rc = 0, user_cnt = 0, orig_address = address;
-
- if (offset > 3) {
- pr_err("offset too large %d\n", offset);
- return -EINVAL;
- }
-
- address = ((orig_address + offset) / 4) * 4;
- offset = (orig_address + offset) % 4;
-
- user_cnt = atomic_add_return(1, &chip->memif_user_cnt);
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("user_cnt %d\n", user_cnt);
- mutex_lock(&chip->rw_lock);
- if (!fg_check_sram_access(chip)) {
- rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS);
- if (rc)
- goto out;
- }
-
- rc = fg_sub_mem_read(chip, val, address, len, offset);
-
-out:
- user_cnt = atomic_sub_return(1, &chip->memif_user_cnt);
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("user_cnt %d\n", user_cnt);
-
- fg_assert_sram_access(chip);
-
- if (!keep_access && (user_cnt == 0) && !rc) {
- rc = fg_release_access(chip);
- if (rc) {
- pr_err("failed to set mem access bit\n");
- rc = -EIO;
- }
- }
-
- mutex_unlock(&chip->rw_lock);
- return rc;
-}
-
-static int fg_conventional_mem_write(struct fg_chip *chip, u8 *val, u16 address,
- int len, int offset, bool keep_access)
-{
- int rc = 0, user_cnt = 0, sublen;
- bool access_configured = false;
- u8 *wr_data = val, word[4];
- char str[DEBUG_PRINT_BUFFER_SIZE];
-
- if (address < RAM_OFFSET)
- return -EINVAL;
-
- if (offset > 3)
- return -EINVAL;
-
- address = ((address + offset) / 4) * 4;
- offset = (address + offset) % 4;
-
- user_cnt = atomic_add_return(1, &chip->memif_user_cnt);
- if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
- pr_info("user_cnt %d\n", user_cnt);
- mutex_lock(&chip->rw_lock);
- if (!fg_check_sram_access(chip)) {
- rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS);
- if (rc)
- goto out;
- }
-
- if (fg_debug_mask & FG_MEM_DEBUG_WRITES) {
- pr_info("length %d addr=%02X offset=%d\n",
- len, address, offset);
- fill_string(str, DEBUG_PRINT_BUFFER_SIZE, wr_data, len);
- pr_info("writing: %s\n", str);
- }
-
- while (len > 0) {
- if (offset != 0) {
- sublen = min(4 - offset, len);
- rc = fg_sub_mem_read(chip, word, address, 4, 0);
- if (rc)
- goto out;
- memcpy(word + offset, wr_data, sublen);
- /* configure access as burst if more to write */
- rc = fg_config_access(chip, 1, (len - sublen) > 0);
- if (rc)
- goto out;
- rc = fg_set_ram_addr(chip, &address);
- if (rc)
- goto out;
- offset = 0;
- access_configured = true;
- } else if (len >= 4) {
- if (!access_configured) {
- rc = fg_config_access(chip, 1, len > 4);
- if (rc)
- goto out;
- rc = fg_set_ram_addr(chip, &address);
- if (rc)
- goto out;
- access_configured = true;
- }
- sublen = 4;
- memcpy(word, wr_data, 4);
- } else if (len > 0 && len < 4) {
- sublen = len;
- rc = fg_sub_mem_read(chip, word, address, 4, 0);
- if (rc)
- goto out;
- memcpy(word, wr_data, sublen);
- rc = fg_config_access(chip, 1, 0);
- if (rc)
- goto out;
- rc = fg_set_ram_addr(chip, &address);
- if (rc)
- goto out;
- access_configured = true;
- } else {
- pr_err("Invalid length: %d\n", len);
- break;
- }
- rc = fg_write(chip, word, MEM_INTF_WR_DATA0(chip), 4);
- if (rc) {
- pr_err("spmi write failed: addr=%03x, rc=%d\n",
- MEM_INTF_WR_DATA0(chip), rc);
- goto out;
- }
- len -= sublen;
- wr_data += sublen;
- address += 4;
- }
-
-out:
- user_cnt = atomic_sub_return(1, &chip->memif_user_cnt);
- if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
- pr_info("user_cnt %d\n", user_cnt);
-
- fg_assert_sram_access(chip);
-
- if (!keep_access && (user_cnt == 0) && !rc) {
- rc = fg_release_access(chip);
- if (rc) {
- pr_err("failed to set mem access bit\n");
- rc = -EIO;
- }
- }
-
- mutex_unlock(&chip->rw_lock);
- return rc;
-}
-
-#define MEM_INTF_IMA_CFG 0x52
-#define MEM_INTF_IMA_OPR_STS 0x54
-#define MEM_INTF_IMA_ERR_STS 0x5F
-#define MEM_INTF_IMA_EXP_STS 0x55
-#define MEM_INTF_IMA_HW_STS 0x56
-#define MEM_INTF_IMA_BYTE_EN 0x60
-#define IMA_ADDR_STBL_ERR BIT(7)
-#define IMA_WR_ACS_ERR BIT(6)
-#define IMA_RD_ACS_ERR BIT(5)
-#define IMA_IACS_CLR BIT(2)
-#define IMA_IACS_RDY BIT(1)
-static int fg_check_ima_exception(struct fg_chip *chip)
-{
- int rc = 0, ret = 0;
- u8 err_sts, exp_sts = 0, hw_sts = 0;
-
- rc = fg_read(chip, &err_sts,
- chip->mem_base + MEM_INTF_IMA_ERR_STS, 1);
- if (rc) {
- pr_err("failed to read beat count rc=%d\n", rc);
- return rc;
- }
-
- if (err_sts & (IMA_ADDR_STBL_ERR | IMA_WR_ACS_ERR | IMA_RD_ACS_ERR)) {
- u8 temp;
-
- fg_read(chip, &exp_sts,
- chip->mem_base + MEM_INTF_IMA_EXP_STS, 1);
- fg_read(chip, &hw_sts,
- chip->mem_base + MEM_INTF_IMA_HW_STS, 1);
- pr_err("IMA access failed ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n",
- err_sts, exp_sts, hw_sts);
- rc = err_sts;
-
- /* clear the error */
- ret |= fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
- IMA_IACS_CLR, IMA_IACS_CLR, 1);
- temp = 0x4;
- ret |= fg_write(chip, &temp, MEM_INTF_ADDR_LSB(chip) + 1, 1);
- temp = 0x0;
- ret |= fg_write(chip, &temp, MEM_INTF_WR_DATA0(chip) + 3, 1);
- ret |= fg_read(chip, &temp, MEM_INTF_RD_DATA0(chip) + 3, 1);
- ret |= fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
- IMA_IACS_CLR, 0, 1);
- if (!ret)
- return -EAGAIN;
- else
- pr_err("Error clearing IMA exception ret=%d\n", ret);
- }
-
- return rc;
-}
-
-static int fg_check_iacs_ready(struct fg_chip *chip)
-{
- int rc = 0, timeout = 250;
- u8 ima_opr_sts = 0;
-
- /*
- * Additional delay to make sure IACS ready bit is set after
- * Read/Write operation.
- */
-
- usleep_range(30, 35);
- while (1) {
- rc = fg_read(chip, &ima_opr_sts,
- chip->mem_base + MEM_INTF_IMA_OPR_STS, 1);
- if (!rc && (ima_opr_sts & IMA_IACS_RDY)) {
- break;
- } else {
- if (!(--timeout) || rc)
- break;
- /* delay for iacs_ready to be asserted */
- usleep_range(5000, 7000);
- }
- }
-
- if (!timeout || rc) {
- pr_err("IACS_RDY not set\n");
- /* perform IACS_CLR sequence */
- fg_check_ima_exception(chip);
- return -EBUSY;
- }
-
- return 0;
-}
-
-#define IACS_SLCT BIT(5)
-static int __fg_interleaved_mem_write(struct fg_chip *chip, u8 *val,
- u16 address, int offset, int len)
-{
- int rc = 0, i;
- u8 *word = val, byte_enable = 0, num_bytes = 0;
-
- if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
- pr_info("length %d addr=%02X offset=%d\n",
- len, address, offset);
-
- while (len > 0) {
- num_bytes = (offset + len) > BUF_LEN ?
- (BUF_LEN - offset) : len;
- /* write to byte_enable */
- for (i = offset; i < (offset + num_bytes); i++)
- byte_enable |= BIT(i);
-
- rc = fg_write(chip, &byte_enable,
- chip->mem_base + MEM_INTF_IMA_BYTE_EN, 1);
- if (rc) {
- pr_err("Unable to write to byte_en_reg rc=%d\n",
- rc);
- return rc;
- }
- /* write data */
- rc = fg_write(chip, word, MEM_INTF_WR_DATA0(chip) + offset,
- num_bytes);
- if (rc) {
- pr_err("spmi write failed: addr=%03x, rc=%d\n",
- MEM_INTF_WR_DATA0(chip) + offset, rc);
- return rc;
- }
- /*
- * The last-byte WR_DATA3 starts the write transaction.
- * Write a dummy value to WR_DATA3 if it does not have
- * valid data. This dummy data is not written to the
- * SRAM as byte_en for WR_DATA3 is not set.
- */
- if (!(byte_enable & BIT(3))) {
- u8 dummy_byte = 0x0;
- rc = fg_write(chip, &dummy_byte,
- MEM_INTF_WR_DATA0(chip) + 3, 1);
- if (rc) {
- pr_err("Unable to write dummy-data to WR_DATA3 rc=%d\n",
- rc);
- return rc;
- }
- }
-
- rc = fg_check_iacs_ready(chip);
- if (rc) {
- pr_debug("IACS_RDY failed rc=%d\n", rc);
- return rc;
- }
-
- /* check for error condition */
- rc = fg_check_ima_exception(chip);
- if (rc) {
- pr_err("IMA transaction failed rc=%d", rc);
- return rc;
- }
-
- word += num_bytes;
- len -= num_bytes;
- offset = byte_enable = 0;
- }
-
- return rc;
-}
-
-static int __fg_interleaved_mem_read(struct fg_chip *chip, u8 *val, u16 address,
- int offset, int len)
-{
- int rc = 0, total_len;
- u8 *rd_data = val, num_bytes;
- char str[DEBUG_PRINT_BUFFER_SIZE];
-
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("length %d addr=%02X\n", len, address);
-
- total_len = len;
- while (len > 0) {
- num_bytes = (offset + len) > BUF_LEN ? (BUF_LEN - offset) : len;
- rc = fg_read(chip, rd_data, MEM_INTF_RD_DATA0(chip) + offset,
- num_bytes);
- if (rc) {
- pr_err("spmi read failed: addr=%03x, rc=%d\n",
- MEM_INTF_RD_DATA0(chip) + offset, rc);
- return rc;
- }
-
- rd_data += num_bytes;
- len -= num_bytes;
- offset = 0;
-
- rc = fg_check_iacs_ready(chip);
- if (rc) {
- pr_debug("IACS_RDY failed rc=%d\n", rc);
- return rc;
- }
-
- /* check for error condition */
- rc = fg_check_ima_exception(chip);
- if (rc) {
- pr_err("IMA transaction failed rc=%d", rc);
- return rc;
- }
-
- if (len && (len + offset) < BUF_LEN) {
- /* move to single mode */
- u8 intr_ctl = 0;
-
- rc = fg_write(chip, &intr_ctl, MEM_INTF_CTL(chip), 1);
- if (rc) {
- pr_err("failed to move to single mode rc=%d\n",
- rc);
- return -EIO;
- }
- }
- }
-
- if (fg_debug_mask & FG_MEM_DEBUG_READS) {
- fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len);
- pr_info("data: %s\n", str);
- }
-
- return rc;
-}
-
-#define IMA_REQ_ACCESS (IACS_SLCT | RIF_MEM_ACCESS_REQ)
-static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val,
- u16 address, int len, int offset, int op)
-{
- int rc = 0;
- bool rif_mem_sts = true;
- int time_count = 0;
-
- while (1) {
- rc = fg_check_rif_mem_access(chip, &rif_mem_sts);
- if (rc)
- return rc;
-
- if (!rif_mem_sts)
- break;
-
- if (fg_debug_mask & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
- pr_info("RIF_MEM_ACCESS_REQ is not clear yet for IMA_%s\n",
- op ? "write" : "read");
-
- /*
- * Try this no more than 4 times. If RIF_MEM_ACCESS_REQ is not
- * clear, then return an error instead of waiting for it again.
- */
- if (time_count > 4) {
- pr_err("Waited for 1.5 seconds polling RIF_MEM_ACCESS_REQ\n");
- return -ETIMEDOUT;
- }
-
- /* Wait for 4ms before reading RIF_MEM_ACCESS_REQ again */
- usleep_range(4000, 4100);
- time_count++;
- }
-
- /* configure for IMA access */
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
- IMA_REQ_ACCESS, IMA_REQ_ACCESS, 1);
- if (rc) {
- pr_err("failed to set mem access bit rc = %d\n", rc);
- return rc;
- }
-
- /* configure for the read/write single/burst mode */
- rc = fg_config_access(chip, op, (offset + len) > 4);
- if (rc) {
- pr_err("failed to set configure memory access rc = %d\n", rc);
- return rc;
- }
-
- rc = fg_check_iacs_ready(chip);
- if (rc) {
- pr_debug("IACS_RDY failed rc=%d\n", rc);
- return rc;
- }
-
- /* write addresses to the register */
- rc = fg_set_ram_addr(chip, &address);
- if (rc) {
- pr_err("failed to set SRAM address rc = %d\n", rc);
- return rc;
- }
-
- rc = fg_check_iacs_ready(chip);
- if (rc)
- pr_debug("IACS_RDY failed rc=%d\n", rc);
-
- return rc;
-}
-
-#define MEM_INTF_FG_BEAT_COUNT 0x57
-#define BEAT_COUNT_MASK 0x0F
-#define RETRY_COUNT 3
-static int fg_interleaved_mem_read(struct fg_chip *chip, u8 *val, u16 address,
- int len, int offset)
-{
- int rc = 0, orig_address = address;
- u8 start_beat_count, end_beat_count, count = 0;
- bool retry = false;
-
- if (offset > 3) {
- pr_err("offset too large %d\n", offset);
- return -EINVAL;
- }
-
- fg_stay_awake(&chip->memif_wakeup_source);
- address = ((orig_address + offset) / 4) * 4;
- offset = (orig_address + offset) % 4;
-
- if (address < RAM_OFFSET) {
- /*
- * OTP memory reads need a conventional memory access, do a
- * conventional read when SRAM offset < RAM_OFFSET.
- */
- rc = fg_conventional_mem_read(chip, val, address, len, offset,
- 0);
- if (rc)
- pr_err("Failed to read OTP memory %d\n", rc);
- goto exit;
- }
-
- mutex_lock(&chip->rw_lock);
-
-retry:
- rc = fg_interleaved_mem_config(chip, val, address, offset, len, 0);
- if (rc) {
- pr_err("failed to configure SRAM for IMA rc = %d\n", rc);
- goto out;
- }
-
- /* read the start beat count */
- rc = fg_read(chip, &start_beat_count,
- chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1);
- if (rc) {
- pr_err("failed to read beat count rc=%d\n", rc);
- goto out;
- }
-
- /* read data */
- rc = __fg_interleaved_mem_read(chip, val, address, offset, len);
- if (rc) {
- if ((rc == -EAGAIN) && (count < RETRY_COUNT)) {
- count++;
- pr_err("IMA access failed retry_count = %d\n", count);
- goto retry;
- } else {
- pr_err("failed to read SRAM address rc = %d\n", rc);
- goto out;
- }
- }
-
- /* read the end beat count */
- rc = fg_read(chip, &end_beat_count,
- chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1);
- if (rc) {
- pr_err("failed to read beat count rc=%d\n", rc);
- goto out;
- }
-
- start_beat_count &= BEAT_COUNT_MASK;
- end_beat_count &= BEAT_COUNT_MASK;
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("Start beat_count = %x End beat_count = %x\n",
- start_beat_count, end_beat_count);
- if (start_beat_count != end_beat_count) {
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("Beat count do not match - retry transaction\n");
- retry = true;
- }
-out:
- /* Release IMA access */
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1);
- if (rc)
- pr_err("failed to reset IMA access bit rc = %d\n", rc);
-
- if (retry) {
- retry = false;
- goto retry;
- }
- mutex_unlock(&chip->rw_lock);
-
-exit:
- fg_relax(&chip->memif_wakeup_source);
- return rc;
-}
-
-static int fg_interleaved_mem_write(struct fg_chip *chip, u8 *val, u16 address,
- int len, int offset)
-{
- int rc = 0, orig_address = address;
- u8 count = 0;
-
- if (address < RAM_OFFSET)
- return -EINVAL;
-
- if (offset > 3) {
- pr_err("offset too large %d\n", offset);
- return -EINVAL;
- }
-
- fg_stay_awake(&chip->memif_wakeup_source);
- address = ((orig_address + offset) / 4) * 4;
- offset = (orig_address + offset) % 4;
-
- mutex_lock(&chip->rw_lock);
-
-retry:
- rc = fg_interleaved_mem_config(chip, val, address, offset, len, 1);
- if (rc) {
- pr_err("failed to xonfigure SRAM for IMA rc = %d\n", rc);
- goto out;
- }
-
- /* write data */
- rc = __fg_interleaved_mem_write(chip, val, address, offset, len);
- if (rc) {
- if ((rc == -EAGAIN) && (count < RETRY_COUNT)) {
- count++;
- pr_err("IMA access failed retry_count = %d\n", count);
- goto retry;
- } else {
- pr_err("failed to write SRAM address rc = %d\n", rc);
- goto out;
- }
- }
-
-out:
- /* Release IMA access */
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1);
- if (rc)
- pr_err("failed to reset IMA access bit rc = %d\n", rc);
-
- mutex_unlock(&chip->rw_lock);
- fg_relax(&chip->memif_wakeup_source);
- return rc;
-}
-
-static int fg_mem_read(struct fg_chip *chip, u8 *val, u16 address,
- int len, int offset, bool keep_access)
-{
- if (chip->ima_supported)
- return fg_interleaved_mem_read(chip, val, address,
- len, offset);
- else
- return fg_conventional_mem_read(chip, val, address,
- len, offset, keep_access);
-}
-
-static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address,
- int len, int offset, bool keep_access)
-{
- if (chip->ima_supported)
- return fg_interleaved_mem_write(chip, val, address,
- len, offset);
- else
- return fg_conventional_mem_write(chip, val, address,
- len, offset, keep_access);
-}
-
-static int fg_mem_masked_write(struct fg_chip *chip, u16 addr,
- u8 mask, u8 val, u8 offset)
-{
- int rc = 0;
- u8 reg[4];
- char str[DEBUG_PRINT_BUFFER_SIZE];
-
- rc = fg_mem_read(chip, reg, addr, 4, 0, 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n", addr, rc);
- return rc;
- }
-
- reg[offset] &= ~mask;
- reg[offset] |= val & mask;
-
- str[0] = '\0';
- fill_string(str, DEBUG_PRINT_BUFFER_SIZE, reg, 4);
- pr_debug("Writing %s address %03x, offset %d\n", str, addr, offset);
-
- rc = fg_mem_write(chip, reg, addr, 4, 0, 0);
- if (rc) {
- pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc);
- return rc;
- }
-
- return rc;
-}
-
-static int soc_to_setpoint(int soc)
-{
- return DIV_ROUND_CLOSEST(soc * 255, 100);
-}
-
-static void batt_to_setpoint_adc(int vbatt_mv, u8 *data)
-{
- int val;
- /* Battery voltage is an offset from 0 V and LSB is 1/2^15. */
- val = DIV_ROUND_CLOSEST(vbatt_mv * 32768, 5000);
- data[0] = val & 0xFF;
- data[1] = val >> 8;
- return;
-}
-
-static u8 batt_to_setpoint_8b(int vbatt_mv)
-{
- int val;
- /* Battery voltage is an offset from 2.5 V and LSB is 5/2^9. */
- val = (vbatt_mv - 2500) * 512 / 1000;
- return DIV_ROUND_CLOSEST(val, 5);
-}
-
-static u8 therm_delay_to_setpoint(u32 delay_us)
-{
- u8 val;
-
- if (delay_us < 2560)
- val = 0;
- else if (delay_us > 163840)
- val = 7;
- else
- val = ilog2(delay_us / 10) - 7;
- return val << 5;
-}
-
-static int get_current_time(unsigned long *now_tm_sec)
-{
- struct rtc_time tm;
- struct rtc_device *rtc;
- int rc;
-
- rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
- if (rtc == NULL) {
- pr_err("%s: unable to open rtc device (%s)\n",
- __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
- return -EINVAL;
- }
-
- rc = rtc_read_time(rtc, &tm);
- if (rc) {
- pr_err("Error reading rtc device (%s) : %d\n",
- CONFIG_RTC_HCTOSYS_DEVICE, rc);
- goto close_time;
- }
-
- rc = rtc_valid_tm(&tm);
- if (rc) {
- pr_err("Invalid RTC time (%s): %d\n",
- CONFIG_RTC_HCTOSYS_DEVICE, rc);
- goto close_time;
- }
- rtc_tm_to_time(&tm, now_tm_sec);
-
-close_time:
- rtc_class_close(rtc);
- return rc;
-}
-
-#define BATTERY_SOC_REG 0x56C
-#define BATTERY_SOC_OFFSET 1
-#define FULL_PERCENT_3B 0xFFFFFF
-static int get_battery_soc_raw(struct fg_chip *chip)
-{
- int rc;
- u8 buffer[3];
-
- rc = fg_mem_read(chip, buffer, BATTERY_SOC_REG, 3, 1, 0);
- if (rc) {
- pr_err("Unable to read battery soc: %d\n", rc);
- return 0;
- }
- return (int)(buffer[2] << 16 | buffer[1] << 8 | buffer[0]);
-}
-
-#define COUNTER_IMPTR_REG 0X558
-#define COUNTER_PULSE_REG 0X55C
-#define SOC_FULL_REG 0x564
-#define COUNTER_IMPTR_OFFSET 2
-#define COUNTER_PULSE_OFFSET 0
-#define SOC_FULL_OFFSET 3
-#define ESR_PULSE_RECONFIG_SOC 0xFFF971
-static int fg_configure_soc(struct fg_chip *chip)
-{
- u32 batt_soc;
- u8 cntr[2] = {0, 0};
- int rc = 0;
-
- mutex_lock(&chip->rw_lock);
- atomic_add_return(1, &chip->memif_user_cnt);
- mutex_unlock(&chip->rw_lock);
-
- /* Read Battery SOC */
- batt_soc = get_battery_soc_raw(chip);
-
- if (batt_soc > ESR_PULSE_RECONFIG_SOC) {
- if (fg_debug_mask & FG_POWER_SUPPLY)
- pr_info("Configuring soc registers batt_soc: %x\n",
- batt_soc);
- batt_soc = ESR_PULSE_RECONFIG_SOC;
- rc = fg_mem_write(chip, (u8 *)&batt_soc, BATTERY_SOC_REG, 3,
- BATTERY_SOC_OFFSET, 1);
- if (rc) {
- pr_err("failed to write BATT_SOC rc=%d\n", rc);
- goto out;
- }
-
- rc = fg_mem_write(chip, (u8 *)&batt_soc, SOC_FULL_REG, 3,
- SOC_FULL_OFFSET, 1);
- if (rc) {
- pr_err("failed to write SOC_FULL rc=%d\n", rc);
- goto out;
- }
-
- rc = fg_mem_write(chip, cntr, COUNTER_IMPTR_REG, 2,
- COUNTER_IMPTR_OFFSET, 1);
- if (rc) {
- pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
- goto out;
- }
-
- rc = fg_mem_write(chip, cntr, COUNTER_PULSE_REG, 2,
- COUNTER_PULSE_OFFSET, 0);
- if (rc)
- pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
- }
-out:
- fg_release_access_if_necessary(chip);
- return rc;
-}
-
-#define SOC_EMPTY BIT(3)
-static bool fg_is_batt_empty(struct fg_chip *chip)
-{
- u8 fg_soc_sts;
- int rc;
-
- rc = fg_read(chip, &fg_soc_sts,
- INT_RT_STS(chip->soc_base), 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->soc_base), rc);
- return false;
- }
-
- return (fg_soc_sts & SOC_EMPTY) != 0;
-}
-
-static int get_monotonic_soc_raw(struct fg_chip *chip)
-{
- u8 cap[2];
- int rc, tries = 0;
-
- while (tries < MAX_TRIES_SOC) {
- rc = fg_read(chip, cap,
- chip->soc_base + SOC_MONOTONIC_SOC, 2);
- if (rc) {
- pr_err("spmi read failed: addr=%03x, rc=%d\n",
- chip->soc_base + SOC_MONOTONIC_SOC, rc);
- return rc;
- }
-
- if (cap[0] == cap[1])
- break;
-
- tries++;
- }
-
- if (tries == MAX_TRIES_SOC) {
- pr_err("shadow registers do not match\n");
- return -EINVAL;
- }
-
- if (fg_debug_mask & FG_POWER_SUPPLY)
- pr_info_ratelimited("raw: 0x%02x\n", cap[0]);
- return cap[0];
-}
-
-#define EMPTY_CAPACITY 0
-#define DEFAULT_CAPACITY 50
-#define MISSING_CAPACITY 100
-#define FULL_CAPACITY 100
-#define FULL_SOC_RAW 0xFF
-static int get_prop_capacity(struct fg_chip *chip)
-{
- int msoc;
-
- if (chip->battery_missing)
- return MISSING_CAPACITY;
- if (!chip->profile_loaded && !chip->use_otp_profile)
- return DEFAULT_CAPACITY;
- if (chip->charge_full)
- return FULL_CAPACITY;
- if (chip->soc_empty) {
- if (fg_debug_mask & FG_POWER_SUPPLY)
- pr_info_ratelimited("capacity: %d, EMPTY\n",
- EMPTY_CAPACITY);
- return EMPTY_CAPACITY;
- }
- msoc = get_monotonic_soc_raw(chip);
- if (msoc == 0)
- return EMPTY_CAPACITY;
- else if (msoc == FULL_SOC_RAW)
- return FULL_CAPACITY;
- return DIV_ROUND_CLOSEST((msoc - 1) * (FULL_CAPACITY - 2),
- FULL_SOC_RAW - 2) + 1;
-}
-
-#define HIGH_BIAS 3
-#define MED_BIAS BIT(1)
-#define LOW_BIAS BIT(0)
-static u8 bias_ua[] = {
- [HIGH_BIAS] = 150,
- [MED_BIAS] = 15,
- [LOW_BIAS] = 5,
-};
-
-static int64_t get_batt_id(unsigned int battery_id_uv, u8 bid_info)
-{
- u64 battery_id_ohm;
-
- if ((bid_info & 0x3) == 0) {
- pr_err("can't determine battery id 0x%02x\n", bid_info);
- return -EINVAL;
- }
-
- battery_id_ohm = div_u64(battery_id_uv, bias_ua[bid_info & 0x3]);
-
- return battery_id_ohm;
-}
-
-#define DEFAULT_TEMP_DEGC 250
-static int get_sram_prop_now(struct fg_chip *chip, unsigned int type)
-{
- if (fg_debug_mask & FG_POWER_SUPPLY)
- pr_info("addr 0x%02X, offset %d value %d\n",
- fg_data[type].address, fg_data[type].offset,
- fg_data[type].value);
-
- if (type == FG_DATA_BATT_ID)
- return get_batt_id(fg_data[type].value,
- fg_data[FG_DATA_BATT_ID_INFO].value);
-
- return fg_data[type].value;
-}
-
-#define MIN_TEMP_DEGC -300
-#define MAX_TEMP_DEGC 970
-static int get_prop_jeita_temp(struct fg_chip *chip, unsigned int type)
-{
- if (fg_debug_mask & FG_POWER_SUPPLY)
- pr_info("addr 0x%02X, offset %d\n", settings[type].address,
- settings[type].offset);
-
- return settings[type].value;
-}
-
-static int set_prop_jeita_temp(struct fg_chip *chip,
- unsigned int type, int decidegc)
-{
- int rc = 0;
-
- if (fg_debug_mask & FG_POWER_SUPPLY)
- pr_info("addr 0x%02X, offset %d temp%d\n",
- settings[type].address,
- settings[type].offset, decidegc);
-
- settings[type].value = decidegc;
-
- cancel_delayed_work_sync(
- &chip->update_jeita_setting);
- schedule_delayed_work(
- &chip->update_jeita_setting, 0);
-
- return rc;
-}
-
-#define EXTERNAL_SENSE_SELECT 0x4AC
-#define EXTERNAL_SENSE_OFFSET 0x2
-#define EXTERNAL_SENSE_BIT BIT(2)
-static int set_prop_sense_type(struct fg_chip *chip, int ext_sense_type)
-{
- int rc;
-
- rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
- EXTERNAL_SENSE_BIT,
- ext_sense_type ? EXTERNAL_SENSE_BIT : 0,
- EXTERNAL_SENSE_OFFSET);
- if (rc) {
- pr_err("failed to write profile rc=%d\n", rc);
- return rc;
- }
-
- return 0;
-}
-
-#define EXPONENT_MASK 0xF800
-#define MANTISSA_MASK 0x3FF
-#define SIGN BIT(10)
-#define EXPONENT_SHIFT 11
-#define MICRO_UNIT 1000000ULL
-static int64_t float_decode(u16 reg)
-{
- int64_t final_val, exponent_val, mantissa_val;
- int exponent, mantissa, n;
- bool sign;
-
- exponent = (reg & EXPONENT_MASK) >> EXPONENT_SHIFT;
- mantissa = (reg & MANTISSA_MASK);
- sign = !!(reg & SIGN);
-
- pr_debug("exponent=%d mantissa=%d sign=%d\n", exponent, mantissa, sign);
-
- mantissa_val = mantissa * MICRO_UNIT;
-
- n = exponent - 15;
- if (n < 0)
- exponent_val = MICRO_UNIT >> -n;
- else
- exponent_val = MICRO_UNIT << n;
-
- n = n - 10;
- if (n < 0)
- mantissa_val >>= -n;
- else
- mantissa_val <<= n;
-
- final_val = exponent_val + mantissa_val;
-
- if (sign)
- final_val *= -1;
-
- return final_val;
-}
-
-#define MIN_HALFFLOAT_EXP_N -15
-#define MAX_HALFFLOAT_EXP_N 16
-static int log2_floor(int64_t uval)
-{
- int n = 0;
- int64_t i = MICRO_UNIT;
-
- if (uval > i) {
- while (uval > i && n > MIN_HALFFLOAT_EXP_N) {
- i <<= 1;
- n += 1;
- }
- if (uval < i)
- n -= 1;
- } else if (uval < i) {
- while (uval < i && n < MAX_HALFFLOAT_EXP_N) {
- i >>= 1;
- n -= 1;
- }
- }
-
- return n;
-}
-
-static int64_t exp2_int(int64_t n)
-{
- int p = n - 1;
-
- if (p > 0)
- return (2 * MICRO_UNIT) << p;
- else
- return (2 * MICRO_UNIT) >> abs(p);
-}
-
-static u16 float_encode(int64_t uval)
-{
- int sign = 0, n, exp, mantissa;
- u16 half = 0;
-
- if (uval < 0) {
- sign = 1;
- uval = abs(uval);
- }
- n = log2_floor(uval);
- exp = n + 15;
- mantissa = div_s64(div_s64((uval - exp2_int(n)) * exp2_int(10 - n),
- MICRO_UNIT) + MICRO_UNIT / 2, MICRO_UNIT);
-
- half = (mantissa & MANTISSA_MASK) | ((sign << 10) & SIGN)
- | ((exp << 11) & EXPONENT_MASK);
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("uval = %lld, m = 0x%02x, sign = 0x%02x, exp = 0x%02x, half = 0x%04x\n",
- uval, mantissa, sign, exp, half);
- return half;
-}
-
-#define BATT_IDED BIT(3)
-static int fg_is_batt_id_valid(struct fg_chip *chip)
-{
- u8 fg_batt_sts;
- int rc;
-
- rc = fg_read(chip, &fg_batt_sts,
- INT_RT_STS(chip->batt_base), 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->batt_base), rc);
- return rc;
- }
-
- if (fg_debug_mask & FG_IRQS)
- pr_info("fg batt sts 0x%x\n", fg_batt_sts);
-
- return (fg_batt_sts & BATT_IDED) ? 1 : 0;
-}
-
-static int64_t twos_compliment_extend(int64_t val, int nbytes)
-{
- int i;
- int64_t mask;
-
- mask = 0x80LL << ((nbytes - 1) * 8);
- if (val & mask) {
- for (i = 8; i > nbytes; i--) {
- mask = 0xFFLL << ((i - 1) * 8);
- val |= mask;
- }
- }
-
- return val;
-}
-
-#define LSB_24B_NUMRTR 596046
-#define LSB_24B_DENMTR 1000000
-#define LSB_16B_NUMRTR 152587
-#define LSB_16B_DENMTR 1000
-#define LSB_8B 9800
-#define TEMP_LSB_16B 625
-#define DECIKELVIN 2730
-#define SRAM_PERIOD_NO_ID_UPDATE_MS 100
-#define FULL_PERCENT_28BIT 0xFFFFFFF
-static void update_sram_data(struct fg_chip *chip, int *resched_ms)
-{
- int i, j, rc = 0;
- u8 reg[4];
- int64_t temp;
- int battid_valid = fg_is_batt_id_valid(chip);
-
- fg_stay_awake(&chip->update_sram_wakeup_source);
- if (chip->fg_restarting)
- goto resched;
-
- fg_mem_lock(chip);
- for (i = 1; i < FG_DATA_MAX; i++) {
- if (chip->profile_loaded && i >= FG_DATA_BATT_ID)
- continue;
- rc = fg_mem_read(chip, reg, fg_data[i].address,
- fg_data[i].len, fg_data[i].offset, 0);
- if (rc) {
- pr_err("Failed to update sram data\n");
- break;
- }
-
- temp = 0;
- for (j = 0; j < fg_data[i].len; j++)
- temp |= reg[j] << (8 * j);
-
- switch (i) {
- case FG_DATA_OCV:
- case FG_DATA_VOLTAGE:
- case FG_DATA_CPRED_VOLTAGE:
- fg_data[i].value = div_u64(
- (u64)(u16)temp * LSB_16B_NUMRTR,
- LSB_16B_DENMTR);
- break;
- case FG_DATA_CURRENT:
- temp = twos_compliment_extend(temp, fg_data[i].len);
- fg_data[i].value = div_s64(
- (s64)temp * LSB_16B_NUMRTR,
- LSB_16B_DENMTR);
- break;
- case FG_DATA_BATT_ESR:
- fg_data[i].value = float_decode((u16) temp);
- break;
- case FG_DATA_BATT_ESR_COUNT:
- fg_data[i].value = (u16)temp;
- break;
- case FG_DATA_BATT_ID:
- if (battid_valid)
- fg_data[i].value = reg[0] * LSB_8B;
- break;
- case FG_DATA_BATT_ID_INFO:
- if (battid_valid)
- fg_data[i].value = reg[0];
- break;
- case FG_DATA_BATT_SOC:
- fg_data[i].value = div64_s64((temp * 10000),
- FULL_PERCENT_3B);
- break;
- case FG_DATA_CC_CHARGE:
- temp = twos_compliment_extend(temp, fg_data[i].len);
- fg_data[i].value = div64_s64(
- temp * (int64_t)chip->nom_cap_uah,
- FULL_PERCENT_28BIT);
- break;
- case FG_DATA_VINT_ERR:
- temp = twos_compliment_extend(temp, fg_data[i].len);
- fg_data[i].value = div64_s64(temp * chip->nom_cap_uah,
- FULL_PERCENT_3B);
- break;
- };
-
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("%d %lld %d\n", i, temp, fg_data[i].value);
- }
- fg_mem_release(chip);
-
- if (!rc)
- get_current_time(&chip->last_sram_update_time);
-
-resched:
- if (battid_valid) {
- complete_all(&chip->batt_id_avail);
- *resched_ms = fg_sram_update_period_ms;
- } else {
- *resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS;
- }
- fg_relax(&chip->update_sram_wakeup_source);
-}
-
-#define SRAM_TIMEOUT_MS 3000
-static void update_sram_data_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- update_sram_data.work);
- int resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS, ret;
- bool tried_again = false;
-
-wait:
- /* Wait for MEMIF access revoked */
- ret = wait_for_completion_interruptible_timeout(
- &chip->sram_access_revoked,
- msecs_to_jiffies(SRAM_TIMEOUT_MS));
-
- /* If we were interrupted wait again one more time. */
- if (ret == -ERESTARTSYS && !tried_again) {
- tried_again = true;
- goto wait;
- } else if (ret <= 0) {
- pr_err("transaction timed out ret=%d\n", ret);
- goto out;
- }
- update_sram_data(chip, &resched_ms);
-
-out:
- schedule_delayed_work(
- &chip->update_sram_data,
- msecs_to_jiffies(resched_ms));
-}
-
-#define BATT_TEMP_OFFSET 3
-#define BATT_TEMP_CNTRL_MASK 0x17
-#define DISABLE_THERM_BIT BIT(0)
-#define TEMP_SENSE_ALWAYS_BIT BIT(1)
-#define TEMP_SENSE_CHARGE_BIT BIT(2)
-#define FORCE_RBIAS_ON_BIT BIT(4)
-#define BATT_TEMP_OFF DISABLE_THERM_BIT
-#define BATT_TEMP_ON (FORCE_RBIAS_ON_BIT | TEMP_SENSE_ALWAYS_BIT | \
- TEMP_SENSE_CHARGE_BIT)
-#define TEMP_PERIOD_UPDATE_MS 10000
-#define TEMP_PERIOD_TIMEOUT_MS 3000
-static void update_temp_data(struct work_struct *work)
-{
- s16 temp;
- u8 reg[2];
- bool tried_again = false;
- int rc, ret, timeout = TEMP_PERIOD_TIMEOUT_MS;
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- update_temp_work.work);
-
- if (chip->fg_restarting)
- goto resched;
-
- fg_stay_awake(&chip->update_temp_wakeup_source);
- if (chip->sw_rbias_ctrl) {
- rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
- BATT_TEMP_CNTRL_MASK,
- BATT_TEMP_ON,
- BATT_TEMP_OFFSET);
- if (rc) {
- pr_err("failed to write BATT_TEMP_ON rc=%d\n", rc);
- goto out;
- }
-
-wait:
- /* Wait for MEMIF access revoked */
- ret = wait_for_completion_interruptible_timeout(
- &chip->sram_access_revoked,
- msecs_to_jiffies(timeout));
-
- /* If we were interrupted wait again one more time. */
- if (ret == -ERESTARTSYS && !tried_again) {
- tried_again = true;
- goto wait;
- } else if (ret <= 0) {
- rc = -ETIMEDOUT;
- pr_err("transaction timed out ret=%d\n", ret);
- goto out;
- }
- }
-
- /* Read FG_DATA_BATT_TEMP now */
- rc = fg_mem_read(chip, reg, fg_data[0].address,
- fg_data[0].len, fg_data[0].offset,
- chip->sw_rbias_ctrl ? 1 : 0);
- if (rc) {
- pr_err("Failed to update temp data\n");
- goto out;
- }
-
- temp = reg[0] | (reg[1] << 8);
- fg_data[0].value = (temp * TEMP_LSB_16B / 1000)
- - DECIKELVIN;
-
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("BATT_TEMP %d %d\n", temp, fg_data[0].value);
-
- get_current_time(&chip->last_temp_update_time);
-
-out:
- if (chip->sw_rbias_ctrl) {
- rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
- BATT_TEMP_CNTRL_MASK,
- BATT_TEMP_OFF,
- BATT_TEMP_OFFSET);
- if (rc)
- pr_err("failed to write BATT_TEMP_OFF rc=%d\n", rc);
- }
- fg_relax(&chip->update_temp_wakeup_source);
-
-resched:
- schedule_delayed_work(
- &chip->update_temp_work,
- msecs_to_jiffies(TEMP_PERIOD_UPDATE_MS));
-}
-
-static void update_jeita_setting(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- update_jeita_setting.work);
- u8 reg[4];
- int i, rc;
-
- for (i = 0; i < 4; i++)
- reg[i] = (settings[FG_MEM_SOFT_COLD + i].value / 10) + 30;
-
- rc = fg_mem_write(chip, reg, settings[FG_MEM_SOFT_COLD].address,
- 4, settings[FG_MEM_SOFT_COLD].offset, 0);
- if (rc)
- pr_err("failed to update JEITA setting rc=%d\n", rc);
-}
-
-static int fg_set_resume_soc(struct fg_chip *chip, u8 threshold)
-{
- u16 address;
- int offset, rc;
-
- address = settings[FG_MEM_RESUME_SOC].address;
- offset = settings[FG_MEM_RESUME_SOC].offset;
-
- rc = fg_mem_masked_write(chip, address, 0xFF, threshold, offset);
-
- if (rc)
- pr_err("write failed rc=%d\n", rc);
- else
- pr_debug("setting resume-soc to %x\n", threshold);
-
- return rc;
-}
-
-#define VBATT_LOW_STS_BIT BIT(2)
-static int fg_get_vbatt_status(struct fg_chip *chip, bool *vbatt_low_sts)
-{
- int rc = 0;
- u8 fg_batt_sts;
-
- rc = fg_read(chip, &fg_batt_sts, INT_RT_STS(chip->batt_base), 1);
- if (!rc)
- *vbatt_low_sts = !!(fg_batt_sts & VBATT_LOW_STS_BIT);
- return rc;
-}
-
-#define BATT_CYCLE_NUMBER_REG 0x5E8
-#define BATT_CYCLE_OFFSET 0
-static void restore_cycle_counter(struct fg_chip *chip)
-{
- int rc = 0, i, address;
- u8 data[2];
-
- fg_mem_lock(chip);
- for (i = 0; i < BUCKET_COUNT; i++) {
- address = BATT_CYCLE_NUMBER_REG + i * 2;
- rc = fg_mem_read(chip, (u8 *)&data, address, 2,
- BATT_CYCLE_OFFSET, 0);
- if (rc)
- pr_err("Failed to read BATT_CYCLE_NUMBER[%d] rc: %d\n",
- i, rc);
- else
- chip->cyc_ctr.count[i] = data[0] | data[1] << 8;
- }
- fg_mem_release(chip);
-}
-
-static void clear_cycle_counter(struct fg_chip *chip)
-{
- int rc = 0, len, i;
-
- if (!chip->cyc_ctr.en)
- return;
-
- len = sizeof(chip->cyc_ctr.count);
- memset(chip->cyc_ctr.count, 0, len);
- for (i = 0; i < BUCKET_COUNT; i++) {
- chip->cyc_ctr.started[i] = false;
- chip->cyc_ctr.last_soc[i] = 0;
- }
- rc = fg_mem_write(chip, (u8 *)&chip->cyc_ctr.count,
- BATT_CYCLE_NUMBER_REG, len,
- BATT_CYCLE_OFFSET, 0);
- if (rc)
- pr_err("failed to write BATT_CYCLE_NUMBER rc=%d\n", rc);
-}
-
-static int fg_inc_store_cycle_ctr(struct fg_chip *chip, int bucket)
-{
- int rc = 0, address;
- u16 cyc_count;
- u8 data[2];
-
- if (bucket < 0 || (bucket > BUCKET_COUNT - 1))
- return 0;
-
- cyc_count = chip->cyc_ctr.count[bucket];
- cyc_count++;
- data[0] = cyc_count & 0xFF;
- data[1] = cyc_count >> 8;
-
- address = BATT_CYCLE_NUMBER_REG + bucket * 2;
-
- rc = fg_mem_write(chip, data, address, 2, BATT_CYCLE_OFFSET, 0);
- if (rc)
- pr_err("failed to write BATT_CYCLE_NUMBER[%d] rc=%d\n",
- bucket, rc);
- else
- chip->cyc_ctr.count[bucket] = cyc_count;
- return rc;
-}
-
-static void update_cycle_count(struct work_struct *work)
-{
- int rc = 0, bucket, i;
- u8 reg[3], batt_soc;
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- cycle_count_work);
-
- mutex_lock(&chip->cyc_ctr.lock);
- rc = fg_mem_read(chip, reg, BATTERY_SOC_REG, 3,
- BATTERY_SOC_OFFSET, 0);
- if (rc) {
- pr_err("Failed to read battery soc rc: %d\n", rc);
- goto out;
- }
- batt_soc = reg[2];
-
- if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
- /* Find out which bucket the SOC falls in */
- bucket = batt_soc / BUCKET_SOC_PCT;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("batt_soc: %x bucket: %d\n", reg[2], bucket);
-
- /*
- * If we've started counting for the previous bucket,
- * then store the counter for that bucket if the
- * counter for current bucket is getting started.
- */
- if (bucket > 0 && chip->cyc_ctr.started[bucket - 1] &&
- !chip->cyc_ctr.started[bucket]) {
- rc = fg_inc_store_cycle_ctr(chip, bucket - 1);
- if (rc) {
- pr_err("Error in storing cycle_ctr rc: %d\n",
- rc);
- goto out;
- } else {
- chip->cyc_ctr.started[bucket - 1] = false;
- chip->cyc_ctr.last_soc[bucket - 1] = 0;
- }
- }
- if (!chip->cyc_ctr.started[bucket]) {
- chip->cyc_ctr.started[bucket] = true;
- chip->cyc_ctr.last_soc[bucket] = batt_soc;
- }
- } else {
- for (i = 0; i < BUCKET_COUNT; i++) {
- if (chip->cyc_ctr.started[i] &&
- batt_soc > chip->cyc_ctr.last_soc[i]) {
- rc = fg_inc_store_cycle_ctr(chip, i);
- if (rc)
- pr_err("Error in storing cycle_ctr rc: %d\n",
- rc);
- chip->cyc_ctr.last_soc[i] = 0;
- }
- chip->cyc_ctr.started[i] = false;
- }
- }
-out:
- mutex_unlock(&chip->cyc_ctr.lock);
-}
-
-static int fg_get_cycle_count(struct fg_chip *chip)
-{
- int count;
-
- if (!chip->cyc_ctr.en)
- return 0;
-
- if ((chip->cyc_ctr.id <= 0) || (chip->cyc_ctr.id > BUCKET_COUNT))
- return -EINVAL;
-
- mutex_lock(&chip->cyc_ctr.lock);
- count = chip->cyc_ctr.count[chip->cyc_ctr.id - 1];
- mutex_unlock(&chip->cyc_ctr.lock);
- return count;
-}
-
-static void half_float_to_buffer(int64_t uval, u8 *buffer)
-{
- u16 raw;
-
- raw = float_encode(uval);
- buffer[0] = (u8)(raw & 0xFF);
- buffer[1] = (u8)((raw >> 8) & 0xFF);
-}
-
-static int64_t half_float(u8 *buffer)
-{
- u16 val;
-
- val = buffer[1] << 8 | buffer[0];
- return float_decode(val);
-}
-
-static int voltage_2b(u8 *buffer)
-{
- u16 val;
-
- val = buffer[1] << 8 | buffer[0];
- /* the range of voltage 2b is [-5V, 5V], so it will fit in an int */
- return (int)div_u64(((u64)val) * LSB_16B_NUMRTR, LSB_16B_DENMTR);
-}
-
-static int bcap_uah_2b(u8 *buffer)
-{
- u16 val;
-
- val = buffer[1] << 8 | buffer[0];
- return ((int)val) * 1000;
-}
-
-static int lookup_ocv_for_soc(struct fg_chip *chip, int soc)
-{
- int64_t *coeffs;
-
- if (soc > chip->ocv_junction_p1p2 * 10)
- coeffs = chip->ocv_coeffs;
- else if (soc > chip->ocv_junction_p2p3 * 10)
- coeffs = chip->ocv_coeffs + 4;
- else
- coeffs = chip->ocv_coeffs + 8;
- /* the range of ocv will fit in a 32 bit int */
- return (int)(coeffs[0]
- + div_s64(coeffs[1] * soc, 1000LL)
- + div_s64(coeffs[2] * soc * soc, 1000000LL)
- + div_s64(coeffs[3] * soc * soc * soc, 1000000000LL));
-}
-
-static int lookup_soc_for_ocv(struct fg_chip *chip, int ocv)
-{
- int64_t val;
- int soc = -EINVAL;
- /*
- * binary search variables representing the valid start and end
- * percentages to search
- */
- int start = 0, end = 1000, mid;
-
- if (fg_debug_mask & FG_AGING)
- pr_info("target_ocv = %d\n", ocv);
- /* do a binary search for the closest soc to match the ocv */
- while (end - start > 1) {
- mid = (start + end) / 2;
- val = lookup_ocv_for_soc(chip, mid);
- if (fg_debug_mask & FG_AGING)
- pr_info("start = %d, mid = %d, end = %d, ocv = %lld\n",
- start, mid, end, val);
- if (ocv < val) {
- end = mid;
- } else if (ocv > val) {
- start = mid;
- } else {
- soc = mid;
- break;
- }
- }
- /*
- * if the exact soc was not found and there are two or less values
- * remaining, just compare them and see which one is closest to the ocv
- */
- if (soc == -EINVAL) {
- if (abs(ocv - lookup_ocv_for_soc(chip, start))
- > abs(ocv - lookup_ocv_for_soc(chip, end)))
- soc = end;
- else
- soc = start;
- }
- if (fg_debug_mask & FG_AGING)
- pr_info("closest = %d, target_ocv = %d, ocv_found = %d\n",
- soc, ocv, lookup_ocv_for_soc(chip, soc));
- return soc;
-}
-
-#define ESR_ACTUAL_REG 0x554
-#define BATTERY_ESR_REG 0x4F4
-#define TEMP_RS_TO_RSLOW_REG 0x514
-static int estimate_battery_age(struct fg_chip *chip, int *actual_capacity)
-{
- int64_t ocv_cutoff_new, ocv_cutoff_aged, temp_rs_to_rslow;
- int64_t esr_actual, battery_esr, val;
- int soc_cutoff_aged, soc_cutoff_new, rc;
- int battery_soc, unusable_soc, batt_temp;
- u8 buffer[3];
-
- if (chip->batt_aging_mode != FG_AGING_ESR)
- return 0;
-
- if (chip->nom_cap_uah == 0) {
- if (fg_debug_mask & FG_AGING)
- pr_info("ocv coefficients not loaded, aborting\n");
- return 0;
- }
- fg_mem_lock(chip);
-
- batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
- if (batt_temp < 150 || batt_temp > 400) {
- if (fg_debug_mask & FG_AGING)
- pr_info("Battery temp (%d) out of range, aborting\n",
- (int)batt_temp);
- rc = 0;
- goto done;
- }
-
- battery_soc = get_battery_soc_raw(chip) * 100 / FULL_PERCENT_3B;
- if (battery_soc < 25 || battery_soc > 75) {
- if (fg_debug_mask & FG_AGING)
- pr_info("Battery SoC (%d) out of range, aborting\n",
- (int)battery_soc);
- rc = 0;
- goto done;
- }
-
- rc = fg_mem_read(chip, buffer, ESR_ACTUAL_REG, 2, 2, 0);
- esr_actual = half_float(buffer);
- rc |= fg_mem_read(chip, buffer, BATTERY_ESR_REG, 2, 2, 0);
- battery_esr = half_float(buffer);
-
- if (rc) {
- goto error_done;
- } else if (esr_actual < battery_esr) {
- if (fg_debug_mask & FG_AGING)
- pr_info("Batt ESR lower than ESR actual, aborting\n");
- rc = 0;
- goto done;
- }
- rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2, 0, 0);
- temp_rs_to_rslow = half_float(buffer);
-
- if (rc)
- goto error_done;
-
- fg_mem_release(chip);
-
- if (fg_debug_mask & FG_AGING) {
- pr_info("batt_soc = %d, cutoff_voltage = %lld, eval current = %d\n",
- battery_soc, chip->cutoff_voltage,
- chip->evaluation_current);
- pr_info("temp_rs_to_rslow = %lld, batt_esr = %lld, esr_actual = %lld\n",
- temp_rs_to_rslow, battery_esr, esr_actual);
- }
-
- /* calculate soc_cutoff_new */
- val = (1000000LL + temp_rs_to_rslow) * battery_esr;
- do_div(val, 1000000);
- ocv_cutoff_new = div64_s64(chip->evaluation_current * val, 1000)
- + chip->cutoff_voltage;
-
- /* calculate soc_cutoff_aged */
- val = (1000000LL + temp_rs_to_rslow) * esr_actual;
- do_div(val, 1000000);
- ocv_cutoff_aged = div64_s64(chip->evaluation_current * val, 1000)
- + chip->cutoff_voltage;
-
- if (fg_debug_mask & FG_AGING)
- pr_info("ocv_cutoff_new = %lld, ocv_cutoff_aged = %lld\n",
- ocv_cutoff_new, ocv_cutoff_aged);
-
- soc_cutoff_new = lookup_soc_for_ocv(chip, ocv_cutoff_new);
- soc_cutoff_aged = lookup_soc_for_ocv(chip, ocv_cutoff_aged);
-
- if (fg_debug_mask & FG_AGING)
- pr_info("aged soc = %d, new soc = %d\n",
- soc_cutoff_aged, soc_cutoff_new);
- unusable_soc = soc_cutoff_aged - soc_cutoff_new;
-
- *actual_capacity = div64_s64(((int64_t)chip->nom_cap_uah)
- * (1000 - unusable_soc), 1000);
- if (fg_debug_mask & FG_AGING)
- pr_info("nom cap = %d, actual cap = %d\n",
- chip->nom_cap_uah, *actual_capacity);
-
- return rc;
-
-error_done:
- pr_err("some register reads failed: %d\n", rc);
-done:
- fg_mem_release(chip);
- return rc;
-}
-
-static void battery_age_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- battery_age_work);
-
- estimate_battery_age(chip, &chip->actual_cap_uah);
-}
-
-static enum power_supply_property fg_power_props[] = {
- POWER_SUPPLY_PROP_CAPACITY,
- POWER_SUPPLY_PROP_CAPACITY_RAW,
- POWER_SUPPLY_PROP_CURRENT_NOW,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- POWER_SUPPLY_PROP_VOLTAGE_OCV,
- POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
- POWER_SUPPLY_PROP_CHARGE_NOW,
- POWER_SUPPLY_PROP_CHARGE_NOW_RAW,
- POWER_SUPPLY_PROP_CHARGE_NOW_ERROR,
- POWER_SUPPLY_PROP_CHARGE_FULL,
- POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
- POWER_SUPPLY_PROP_TEMP,
- POWER_SUPPLY_PROP_COOL_TEMP,
- POWER_SUPPLY_PROP_WARM_TEMP,
- POWER_SUPPLY_PROP_RESISTANCE,
- POWER_SUPPLY_PROP_RESISTANCE_ID,
- POWER_SUPPLY_PROP_BATTERY_TYPE,
- POWER_SUPPLY_PROP_UPDATE_NOW,
- POWER_SUPPLY_PROP_ESR_COUNT,
- POWER_SUPPLY_PROP_VOLTAGE_MIN,
- POWER_SUPPLY_PROP_CYCLE_COUNT,
- POWER_SUPPLY_PROP_CYCLE_COUNT_ID,
- POWER_SUPPLY_PROP_HI_POWER,
-};
-
-static int fg_power_get_property(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- struct fg_chip *chip = power_supply_get_drvdata(psy);
- bool vbatt_low_sts;
-
- switch (psp) {
- case POWER_SUPPLY_PROP_BATTERY_TYPE:
- if (chip->battery_missing)
- val->strval = missing_batt_type;
- else if (chip->fg_restarting)
- val->strval = loading_batt_type;
- else
- val->strval = chip->batt_type;
- break;
- case POWER_SUPPLY_PROP_CAPACITY:
- val->intval = get_prop_capacity(chip);
- break;
- case POWER_SUPPLY_PROP_CAPACITY_RAW:
- val->intval = get_sram_prop_now(chip, FG_DATA_BATT_SOC);
- break;
- case POWER_SUPPLY_PROP_CHARGE_NOW_ERROR:
- val->intval = get_sram_prop_now(chip, FG_DATA_VINT_ERR);
- break;
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- val->intval = get_sram_prop_now(chip, FG_DATA_CURRENT);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = get_sram_prop_now(chip, FG_DATA_VOLTAGE);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_OCV:
- val->intval = get_sram_prop_now(chip, FG_DATA_OCV);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
- val->intval = chip->batt_max_voltage_uv;
- break;
- case POWER_SUPPLY_PROP_TEMP:
- val->intval = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
- break;
- case POWER_SUPPLY_PROP_COOL_TEMP:
- val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_COLD);
- break;
- case POWER_SUPPLY_PROP_WARM_TEMP:
- val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_HOT);
- break;
- case POWER_SUPPLY_PROP_RESISTANCE:
- val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR);
- break;
- case POWER_SUPPLY_PROP_ESR_COUNT:
- val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR_COUNT);
- break;
- case POWER_SUPPLY_PROP_CYCLE_COUNT:
- val->intval = fg_get_cycle_count(chip);
- break;
- case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
- val->intval = chip->cyc_ctr.id;
- break;
- case POWER_SUPPLY_PROP_RESISTANCE_ID:
- val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ID);
- break;
- case POWER_SUPPLY_PROP_UPDATE_NOW:
- val->intval = 0;
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MIN:
- if (!fg_get_vbatt_status(chip, &vbatt_low_sts))
- val->intval = (int)vbatt_low_sts;
- else
- val->intval = 1;
- break;
- case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
- val->intval = chip->nom_cap_uah;
- break;
- case POWER_SUPPLY_PROP_CHARGE_FULL:
- val->intval = chip->learning_data.learned_cc_uah;
- break;
- case POWER_SUPPLY_PROP_CHARGE_NOW:
- val->intval = chip->learning_data.cc_uah;
- break;
- case POWER_SUPPLY_PROP_CHARGE_NOW_RAW:
- val->intval = get_sram_prop_now(chip, FG_DATA_CC_CHARGE);
- break;
- case POWER_SUPPLY_PROP_HI_POWER:
- val->intval = !!chip->bcl_lpm_disabled;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int correction_times[] = {
- 1470,
- 2940,
- 4410,
- 5880,
- 7350,
- 8820,
- 10290,
- 11760,
- 13230,
- 14700,
- 16170,
- 17640,
- 19110,
- 20580,
- 22050,
- 23520,
- 24990,
- 26460,
- 27930,
- 29400,
- 30870,
- 32340,
- 33810,
- 35280,
- 36750,
- 38220,
- 39690,
- 41160,
- 42630,
- 44100,
- 45570,
- 47040,
-};
-
-static int correction_factors[] = {
- 1000000,
- 1007874,
- 1015789,
- 1023745,
- 1031742,
- 1039780,
- 1047859,
- 1055979,
- 1064140,
- 1072342,
- 1080584,
- 1088868,
- 1097193,
- 1105558,
- 1113964,
- 1122411,
- 1130899,
- 1139427,
- 1147996,
- 1156606,
- 1165256,
- 1173947,
- 1182678,
- 1191450,
- 1200263,
- 1209115,
- 1218008,
- 1226942,
- 1235915,
- 1244929,
- 1253983,
- 1263076,
-};
-
-#define FG_CONVERSION_FACTOR (64198531LL)
-static int iavg_3b_to_uah(u8 *buffer, int delta_ms)
-{
- int64_t val, i_filtered;
- int i, correction_factor;
-
- for (i = 0; i < ARRAY_SIZE(correction_times); i++) {
- if (correction_times[i] > delta_ms)
- break;
- }
- if (i >= ARRAY_SIZE(correction_times)) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("fuel gauge took more than 32 cycles\n");
- i = ARRAY_SIZE(correction_times) - 1;
- }
- correction_factor = correction_factors[i];
- if (fg_debug_mask & FG_STATUS)
- pr_info("delta_ms = %d, cycles = %d, correction = %d\n",
- delta_ms, i, correction_factor);
- val = buffer[2] << 16 | buffer[1] << 8 | buffer[0];
- /* convert val from signed 24b to signed 64b */
- i_filtered = (val << 40) >> 40;
- val = i_filtered * correction_factor;
- val = div64_s64(val + FG_CONVERSION_FACTOR / 2, FG_CONVERSION_FACTOR);
- if (fg_debug_mask & FG_STATUS)
- pr_info("i_filtered = 0x%llx/%lld, cc_uah = %lld\n",
- i_filtered, i_filtered, val);
-
- return val;
-}
-
-static bool fg_is_temperature_ok_for_learning(struct fg_chip *chip)
-{
- int batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
-
- if (batt_temp > chip->learning_data.max_temp
- || batt_temp < chip->learning_data.min_temp) {
- if (fg_debug_mask & FG_AGING)
- pr_info("temp (%d) out of range [%d, %d], aborting\n",
- batt_temp,
- chip->learning_data.min_temp,
- chip->learning_data.max_temp);
- return false;
- }
- return true;
-}
-
-static void fg_cap_learning_stop(struct fg_chip *chip)
-{
- chip->learning_data.cc_uah = 0;
- chip->learning_data.active = false;
-}
-
-#define I_FILTERED_REG 0x584
-static void fg_cap_learning_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- fg_cap_learning_work);
- u8 i_filtered[3], data[3];
- int rc, cc_uah, delta_ms;
- ktime_t now_kt, delta_kt;
-
- mutex_lock(&chip->learning_data.learning_lock);
- if (!chip->learning_data.active)
- goto fail;
- if (!fg_is_temperature_ok_for_learning(chip)) {
- fg_cap_learning_stop(chip);
- goto fail;
- }
-
- if (chip->wa_flag & USE_CC_SOC_REG) {
- mutex_unlock(&chip->learning_data.learning_lock);
- fg_relax(&chip->capacity_learning_wakeup_source);
- return;
- }
-
- fg_mem_lock(chip);
-
- rc = fg_mem_read(chip, i_filtered, I_FILTERED_REG, 3, 0, 0);
- if (rc) {
- pr_err("Failed to read i_filtered: %d\n", rc);
- fg_mem_release(chip);
- goto fail;
- }
- memset(data, 0, 3);
- rc = fg_mem_write(chip, data, I_FILTERED_REG, 3, 0, 0);
- if (rc) {
- pr_err("Failed to clear i_filtered: %d\n", rc);
- fg_mem_release(chip);
- goto fail;
- }
- fg_mem_release(chip);
-
- now_kt = ktime_get_boottime();
- delta_kt = ktime_sub(now_kt, chip->learning_data.time_stamp);
- chip->learning_data.time_stamp = now_kt;
-
- delta_ms = (int)div64_s64(ktime_to_ns(delta_kt), 1000000);
-
- cc_uah = iavg_3b_to_uah(i_filtered, delta_ms);
- chip->learning_data.cc_uah -= cc_uah;
- if (fg_debug_mask & FG_AGING)
- pr_info("total_cc_uah = %lld\n", chip->learning_data.cc_uah);
-
-fail:
- mutex_unlock(&chip->learning_data.learning_lock);
- return;
-
-}
-
-#define CC_SOC_BASE_REG 0x5BC
-#define CC_SOC_OFFSET 3
-#define CC_SOC_MAGNITUDE_MASK 0x1FFFFFFF
-#define CC_SOC_NEGATIVE_BIT BIT(29)
-static int fg_get_cc_soc(struct fg_chip *chip, int *cc_soc)
-{
- int rc;
- u8 reg[4];
- unsigned int temp, magnitude;
-
- rc = fg_mem_read(chip, reg, CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0);
- if (rc) {
- pr_err("Failed to read CC_SOC_REG rc=%d\n", rc);
- return rc;
- }
-
- temp = reg[3] << 24 | reg[2] << 16 | reg[1] << 8 | reg[0];
- magnitude = temp & CC_SOC_MAGNITUDE_MASK;
- if (temp & CC_SOC_NEGATIVE_BIT)
- *cc_soc = -1 * (~magnitude + 1);
- else
- *cc_soc = magnitude;
-
- return 0;
-}
-
-static int fg_cap_learning_process_full_data(struct fg_chip *chip)
-{
- int cc_pc_val, rc = -EINVAL;
- unsigned int cc_soc_delta_pc;
- int64_t delta_cc_uah;
-
- if (!chip->learning_data.active)
- goto fail;
-
- if (!fg_is_temperature_ok_for_learning(chip)) {
- fg_cap_learning_stop(chip);
- goto fail;
- }
-
- rc = fg_get_cc_soc(chip, &cc_pc_val);
- if (rc) {
- pr_err("failed to get CC_SOC, stopping capacity learning\n");
- fg_cap_learning_stop(chip);
- goto fail;
- }
-
- cc_soc_delta_pc = DIV_ROUND_CLOSEST(
- abs(cc_pc_val - chip->learning_data.init_cc_pc_val)
- * 100, FULL_PERCENT_28BIT);
-
- delta_cc_uah = div64_s64(
- chip->learning_data.learned_cc_uah * cc_soc_delta_pc,
- 100);
- chip->learning_data.cc_uah = delta_cc_uah + chip->learning_data.cc_uah;
-
- if (fg_debug_mask & FG_AGING)
- pr_info("current cc_soc=%d cc_soc_pc=%d total_cc_uah = %lld\n",
- cc_pc_val, cc_soc_delta_pc,
- chip->learning_data.cc_uah);
-
- return 0;
-
-fail:
- return rc;
-}
-
-#define FG_CAP_LEARNING_INTERVAL_NS 30000000000
-static enum alarmtimer_restart fg_cap_learning_alarm_cb(struct alarm *alarm,
- ktime_t now)
-{
- struct fg_chip *chip = container_of(alarm, struct fg_chip,
- fg_cap_learning_alarm);
-
- if (chip->learning_data.active) {
- if (fg_debug_mask & FG_AGING)
- pr_info("alarm fired\n");
- schedule_work(&chip->fg_cap_learning_work);
- alarm_forward_now(alarm,
- ns_to_ktime(FG_CAP_LEARNING_INTERVAL_NS));
- return ALARMTIMER_RESTART;
- }
- if (fg_debug_mask & FG_AGING)
- pr_info("alarm misfired\n");
- return ALARMTIMER_NORESTART;
-}
-
-#define FG_AGING_STORAGE_REG 0x5E4
-#define ACTUAL_CAPACITY_REG 0x578
-#define MAH_TO_SOC_CONV_REG 0x4A0
-#define CC_SOC_COEFF_OFFSET 0
-#define ACTUAL_CAPACITY_OFFSET 2
-#define MAH_TO_SOC_CONV_CS_OFFSET 0
-static int fg_calc_and_store_cc_soc_coeff(struct fg_chip *chip, int16_t cc_mah)
-{
- int rc;
- int64_t cc_to_soc_coeff, mah_to_soc;
- u8 data[2];
-
- rc = fg_mem_write(chip, (u8 *)&cc_mah, ACTUAL_CAPACITY_REG, 2,
- ACTUAL_CAPACITY_OFFSET, 0);
- if (rc) {
- pr_err("Failed to store actual capacity: %d\n", rc);
- return rc;
- }
-
- rc = fg_mem_read(chip, (u8 *)&data, MAH_TO_SOC_CONV_REG, 2,
- MAH_TO_SOC_CONV_CS_OFFSET, 0);
- if (rc) {
- pr_err("Failed to read mah_to_soc_conv_cs: %d\n", rc);
- } else {
- mah_to_soc = data[1] << 8 | data[0];
- mah_to_soc *= MICRO_UNIT;
- cc_to_soc_coeff = div64_s64(mah_to_soc, cc_mah);
- half_float_to_buffer(cc_to_soc_coeff, data);
- rc = fg_mem_write(chip, (u8 *)data,
- ACTUAL_CAPACITY_REG, 2,
- CC_SOC_COEFF_OFFSET, 0);
- if (rc)
- pr_err("Failed to write cc_soc_coeff_offset: %d\n",
- rc);
- else if (fg_debug_mask & FG_AGING)
- pr_info("new cc_soc_coeff %lld [%x %x] saved to sram\n",
- cc_to_soc_coeff, data[0], data[1]);
- }
- return rc;
-}
-
-static void fg_cap_learning_load_data(struct fg_chip *chip)
-{
- int16_t cc_mah;
- int64_t old_cap = chip->learning_data.learned_cc_uah;
- int rc;
-
- rc = fg_mem_read(chip, (u8 *)&cc_mah, FG_AGING_STORAGE_REG, 2, 0, 0);
- if (rc) {
- pr_err("Failed to load aged capacity: %d\n", rc);
- } else {
- chip->learning_data.learned_cc_uah = cc_mah * 1000;
- if (fg_debug_mask & FG_AGING)
- pr_info("learned capacity %lld-> %lld/%x uah\n",
- old_cap,
- chip->learning_data.learned_cc_uah,
- cc_mah);
- }
-}
-
-static void fg_cap_learning_save_data(struct fg_chip *chip)
-{
- int16_t cc_mah;
- int rc;
-
- cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000);
-
- rc = fg_mem_write(chip, (u8 *)&cc_mah, FG_AGING_STORAGE_REG, 2, 0, 0);
- if (rc)
- pr_err("Failed to store aged capacity: %d\n", rc);
- else if (fg_debug_mask & FG_AGING)
- pr_info("learned capacity %lld uah (%d/0x%x uah) saved to sram\n",
- chip->learning_data.learned_cc_uah,
- cc_mah, cc_mah);
-
- if (chip->learning_data.feedback_on) {
- rc = fg_calc_and_store_cc_soc_coeff(chip, cc_mah);
- if (rc)
- pr_err("Error in storing cc_soc_coeff, rc:%d\n", rc);
- }
-}
-
-static void fg_cap_learning_post_process(struct fg_chip *chip)
-{
- int64_t max_inc_val, min_dec_val, old_cap;
-
- max_inc_val = chip->learning_data.learned_cc_uah
- * (1000 + chip->learning_data.max_increment);
- do_div(max_inc_val, 1000);
-
- min_dec_val = chip->learning_data.learned_cc_uah
- * (1000 - chip->learning_data.max_decrement);
- do_div(min_dec_val, 1000);
-
- old_cap = chip->learning_data.learned_cc_uah;
- if (chip->learning_data.cc_uah > max_inc_val)
- chip->learning_data.learned_cc_uah = max_inc_val;
- else if (chip->learning_data.cc_uah < min_dec_val)
- chip->learning_data.learned_cc_uah = min_dec_val;
- else
- chip->learning_data.learned_cc_uah =
- chip->learning_data.cc_uah;
-
- fg_cap_learning_save_data(chip);
- if (fg_debug_mask & FG_AGING)
- pr_info("final cc_uah = %lld, learned capacity %lld -> %lld uah\n",
- chip->learning_data.cc_uah,
- old_cap, chip->learning_data.learned_cc_uah);
-}
-
-static int get_vbat_est_diff(struct fg_chip *chip)
-{
- return abs(fg_data[FG_DATA_VOLTAGE].value
- - fg_data[FG_DATA_CPRED_VOLTAGE].value);
-}
-
-#define CBITS_INPUT_FILTER_REG 0x4B4
-#define IBATTF_TAU_MASK 0x38
-#define IBATTF_TAU_99_S 0x30
-static int fg_cap_learning_check(struct fg_chip *chip)
-{
- u8 data[4];
- int rc = 0, battery_soc, cc_pc_val;
- int vbat_est_diff, vbat_est_thr_uv;
- unsigned int cc_pc_100 = FULL_PERCENT_28BIT;
-
- mutex_lock(&chip->learning_data.learning_lock);
- if (chip->status == POWER_SUPPLY_STATUS_CHARGING
- && !chip->learning_data.active
- && chip->batt_aging_mode == FG_AGING_CC) {
- if (chip->learning_data.learned_cc_uah == 0) {
- if (fg_debug_mask & FG_AGING)
- pr_info("no capacity, aborting\n");
- goto fail;
- }
-
- if (!fg_is_temperature_ok_for_learning(chip))
- goto fail;
-
- fg_mem_lock(chip);
- if (!chip->learning_data.feedback_on) {
- vbat_est_diff = get_vbat_est_diff(chip);
- vbat_est_thr_uv = chip->learning_data.vbat_est_thr_uv;
- if (vbat_est_diff >= vbat_est_thr_uv &&
- vbat_est_thr_uv > 0) {
- if (fg_debug_mask & FG_AGING)
- pr_info("vbat_est_diff (%d) < threshold (%d)\n",
- vbat_est_diff, vbat_est_thr_uv);
- fg_mem_release(chip);
- fg_cap_learning_stop(chip);
- goto fail;
- }
- }
- battery_soc = get_battery_soc_raw(chip);
- if (fg_debug_mask & FG_AGING)
- pr_info("checking battery soc (%d vs %d)\n",
- battery_soc * 100 / FULL_PERCENT_3B,
- chip->learning_data.max_start_soc);
- /* check if the battery is low enough to start soc learning */
- if (battery_soc * 100 / FULL_PERCENT_3B
- > chip->learning_data.max_start_soc) {
- if (fg_debug_mask & FG_AGING)
- pr_info("battery soc too low (%d < %d), aborting\n",
- battery_soc * 100 / FULL_PERCENT_3B,
- chip->learning_data.max_start_soc);
- fg_mem_release(chip);
- fg_cap_learning_stop(chip);
- goto fail;
- }
-
- /* set the coulomb counter to a percentage of the capacity */
- chip->learning_data.cc_uah = div64_s64(
- (chip->learning_data.learned_cc_uah * battery_soc),
- FULL_PERCENT_3B);
-
- /* Use CC_SOC_REG based capacity learning */
- if (chip->wa_flag & USE_CC_SOC_REG) {
- fg_mem_release(chip);
- /* SW_CC_SOC based capacity learning */
- if (fg_get_cc_soc(chip, &cc_pc_val)) {
- pr_err("failed to get CC_SOC, stop capacity learning\n");
- fg_cap_learning_stop(chip);
- goto fail;
- }
-
- chip->learning_data.init_cc_pc_val = cc_pc_val;
- chip->learning_data.active = true;
- if (fg_debug_mask & FG_AGING)
- pr_info("SW_CC_SOC based learning init_CC_SOC=%d\n",
- chip->learning_data.init_cc_pc_val);
- } else {
- rc = fg_mem_masked_write(chip, CBITS_INPUT_FILTER_REG,
- IBATTF_TAU_MASK, IBATTF_TAU_99_S, 0);
- if (rc) {
- pr_err("Failed to write IF IBAT Tau: %d\n",
- rc);
- fg_mem_release(chip);
- fg_cap_learning_stop(chip);
- goto fail;
- }
-
- /* clear the i_filtered register */
- memset(data, 0, 4);
- rc = fg_mem_write(chip, data, I_FILTERED_REG, 3, 0, 0);
- if (rc) {
- pr_err("Failed to clear i_filtered: %d\n", rc);
- fg_mem_release(chip);
- fg_cap_learning_stop(chip);
- goto fail;
- }
- fg_mem_release(chip);
- chip->learning_data.time_stamp = ktime_get_boottime();
- chip->learning_data.active = true;
-
- if (fg_debug_mask & FG_AGING)
- pr_info("cap learning started, soc = %d cc_uah = %lld\n",
- battery_soc * 100 / FULL_PERCENT_3B,
- chip->learning_data.cc_uah);
- alarm_start_relative(&chip->fg_cap_learning_alarm,
- ns_to_ktime(FG_CAP_LEARNING_INTERVAL_NS));
- }
- } else if ((chip->status != POWER_SUPPLY_STATUS_CHARGING)
- && chip->learning_data.active) {
- if (fg_debug_mask & FG_AGING)
- pr_info("capacity learning stopped\n");
- if (!(chip->wa_flag & USE_CC_SOC_REG))
- alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
-
- if (chip->status == POWER_SUPPLY_STATUS_FULL) {
- if (chip->wa_flag & USE_CC_SOC_REG) {
- rc = fg_cap_learning_process_full_data(chip);
- if (rc) {
- fg_cap_learning_stop(chip);
- goto fail;
- }
- /* reset SW_CC_SOC register to 100% */
- rc = fg_mem_write(chip, (u8 *)&cc_pc_100,
- CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0);
- if (rc)
- pr_err("Failed to reset CC_SOC_REG rc=%d\n",
- rc);
- }
- fg_cap_learning_post_process(chip);
- }
-
- fg_cap_learning_stop(chip);
- }
-
-fail:
- mutex_unlock(&chip->learning_data.learning_lock);
- return rc;
-}
-
-static bool is_usb_present(struct fg_chip *chip)
-{
- union power_supply_propval prop = {0,};
- if (!chip->usb_psy)
- chip->usb_psy = power_supply_get_by_name("usb");
-
- if (chip->usb_psy)
- power_supply_get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_PRESENT, &prop);
- return prop.intval != 0;
-}
-
-static bool is_dc_present(struct fg_chip *chip)
-{
- union power_supply_propval prop = {0,};
- if (!chip->dc_psy)
- chip->dc_psy = power_supply_get_by_name("dc");
-
- if (chip->dc_psy)
- power_supply_get_property(chip->dc_psy,
- POWER_SUPPLY_PROP_PRESENT, &prop);
- return prop.intval != 0;
-}
-
-static bool is_input_present(struct fg_chip *chip)
-{
- return is_usb_present(chip) || is_dc_present(chip);
-}
-
-static bool is_otg_present(struct fg_chip *chip)
-{
- union power_supply_propval prop = {0,};
-
- if (!chip->usb_psy)
- chip->usb_psy = power_supply_get_by_name("usb");
-
- if (chip->usb_psy)
- power_supply_get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_USB_OTG, &prop);
- return prop.intval != 0;
-}
-
-static bool is_charger_available(struct fg_chip *chip)
-{
- if (!chip->batt_psy_name)
- return false;
-
- if (!chip->batt_psy)
- chip->batt_psy = power_supply_get_by_name(chip->batt_psy_name);
-
- if (!chip->batt_psy)
- return false;
-
- return true;
-}
-
-static int set_prop_enable_charging(struct fg_chip *chip, bool enable)
-{
- int rc = 0;
- union power_supply_propval ret = {enable, };
-
- if (!is_charger_available(chip)) {
- pr_err("Charger not available yet!\n");
- return -EINVAL;
- }
-
- rc = power_supply_set_property(chip->batt_psy,
- POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
- &ret);
- if (rc) {
- pr_err("couldn't configure batt chg %d\n", rc);
- return rc;
- }
-
- chip->charging_disabled = !enable;
- if (fg_debug_mask & FG_STATUS)
- pr_info("%sabling charging\n", enable ? "en" : "dis");
-
- return rc;
-}
-
-#define MAX_BATTERY_CC_SOC_CAPACITY 150
-static void status_change_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- status_change_work);
- unsigned long current_time = 0;
- int cc_soc, rc, capacity = get_prop_capacity(chip);
-
- if (chip->esr_pulse_tune_en) {
- fg_stay_awake(&chip->esr_extract_wakeup_source);
- schedule_work(&chip->esr_extract_config_work);
- }
-
- if (chip->status == POWER_SUPPLY_STATUS_FULL) {
- if (capacity >= 99 && chip->hold_soc_while_full
- && chip->health == POWER_SUPPLY_HEALTH_GOOD) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("holding soc at 100\n");
- chip->charge_full = true;
- } else if (fg_debug_mask & FG_STATUS) {
- pr_info("terminated charging at %d/0x%02x\n",
- capacity, get_monotonic_soc_raw(chip));
- }
- }
- if (chip->status == POWER_SUPPLY_STATUS_FULL ||
- chip->status == POWER_SUPPLY_STATUS_CHARGING) {
- if (!chip->vbat_low_irq_enabled) {
- enable_irq(chip->batt_irq[VBATT_LOW].irq);
- enable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
- chip->vbat_low_irq_enabled = true;
- }
- if (!!(chip->wa_flag & PULSE_REQUEST_WA) && capacity == 100)
- fg_configure_soc(chip);
- } else if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) {
- if (chip->vbat_low_irq_enabled) {
- disable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
- disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
- chip->vbat_low_irq_enabled = false;
- }
- }
- fg_cap_learning_check(chip);
- schedule_work(&chip->update_esr_work);
-
- if (chip->wa_flag & USE_CC_SOC_REG) {
- if (fg_get_cc_soc(chip, &cc_soc)) {
- pr_err("failed to get CC_SOC\n");
- return;
- }
- }
-
- if (chip->prev_status != chip->status && chip->last_sram_update_time) {
- get_current_time(&current_time);
- /*
- * When charging status changes, update SRAM parameters if it
- * was not updated before 5 seconds from now.
- */
- if (chip->last_sram_update_time + 5 < current_time) {
- cancel_delayed_work(&chip->update_sram_data);
- schedule_delayed_work(&chip->update_sram_data,
- msecs_to_jiffies(0));
- }
- if (chip->cyc_ctr.en)
- schedule_work(&chip->cycle_count_work);
- if ((chip->wa_flag & USE_CC_SOC_REG) &&
- chip->bad_batt_detection_en &&
- chip->status == POWER_SUPPLY_STATUS_CHARGING) {
- chip->sw_cc_soc_data.init_sys_soc = capacity;
- chip->sw_cc_soc_data.init_cc_soc = cc_soc;
- if (fg_debug_mask & FG_STATUS)
- pr_info(" Init_sys_soc %d init_cc_soc %d\n",
- chip->sw_cc_soc_data.init_sys_soc,
- chip->sw_cc_soc_data.init_cc_soc);
- }
- }
- if ((chip->wa_flag & USE_CC_SOC_REG) && chip->bad_batt_detection_en
- && chip->safety_timer_expired) {
- chip->sw_cc_soc_data.delta_soc =
- DIV_ROUND_CLOSEST(abs(cc_soc -
- chip->sw_cc_soc_data.init_cc_soc)
- * 100, FULL_PERCENT_28BIT);
- chip->sw_cc_soc_data.full_capacity =
- chip->sw_cc_soc_data.delta_soc +
- chip->sw_cc_soc_data.init_sys_soc;
- pr_info("Init_sys_soc %d init_cc_soc %d cc_soc %d delta_soc %d full_capacity %d\n",
- chip->sw_cc_soc_data.init_sys_soc,
- chip->sw_cc_soc_data.init_cc_soc, cc_soc,
- chip->sw_cc_soc_data.delta_soc,
- chip->sw_cc_soc_data.full_capacity);
- /*
- * If sw_cc_soc capacity greater than 150, then it's a bad
- * battery. else, reset timer and restart charging.
- */
- if (chip->sw_cc_soc_data.full_capacity >
- MAX_BATTERY_CC_SOC_CAPACITY) {
- pr_info("Battery possibly damaged, do not restart charging\n");
- } else {
- pr_info("Reset safety-timer and restart charging\n");
- rc = set_prop_enable_charging(chip, false);
- if (rc) {
- pr_err("failed to disable charging %d\n", rc);
- return;
- }
-
- chip->safety_timer_expired = false;
- msleep(200);
-
- rc = set_prop_enable_charging(chip, true);
- if (rc) {
- pr_err("failed to enable charging %d\n", rc);
- return;
- }
- }
- }
-}
-
-/*
- * Check for change in the status of input or OTG and schedule
- * IADC gain compensation work.
- */
-static void check_gain_compensation(struct fg_chip *chip)
-{
- bool input_present = is_input_present(chip);
- bool otg_present = is_otg_present(chip);
-
- if ((chip->wa_flag & IADC_GAIN_COMP_WA)
- && ((chip->input_present ^ input_present)
- || (chip->otg_present ^ otg_present))) {
- fg_stay_awake(&chip->gain_comp_wakeup_source);
- chip->input_present = input_present;
- chip->otg_present = otg_present;
- cancel_work_sync(&chip->gain_comp_work);
- schedule_work(&chip->gain_comp_work);
- }
-}
-
-static void fg_hysteresis_config(struct fg_chip *chip)
-{
- int hard_hot = 0, hard_cold = 0;
-
- hard_hot = get_prop_jeita_temp(chip, FG_MEM_HARD_HOT);
- hard_cold = get_prop_jeita_temp(chip, FG_MEM_HARD_COLD);
- if (chip->health == POWER_SUPPLY_HEALTH_OVERHEAT && !chip->batt_hot) {
- /* turn down the hard hot threshold */
- chip->batt_hot = true;
- set_prop_jeita_temp(chip, FG_MEM_HARD_HOT,
- hard_hot - chip->hot_hysteresis);
- if (fg_debug_mask & FG_STATUS)
- pr_info("hard hot hysteresis: old hot=%d, new hot=%d\n",
- hard_hot, hard_hot - chip->hot_hysteresis);
- } else if (chip->health == POWER_SUPPLY_HEALTH_COLD &&
- !chip->batt_cold) {
- /* turn up the hard cold threshold */
- chip->batt_cold = true;
- set_prop_jeita_temp(chip, FG_MEM_HARD_COLD,
- hard_cold + chip->cold_hysteresis);
- if (fg_debug_mask & FG_STATUS)
- pr_info("hard cold hysteresis: old cold=%d, new cold=%d\n",
- hard_cold, hard_cold + chip->hot_hysteresis);
- } else if (chip->health != POWER_SUPPLY_HEALTH_OVERHEAT &&
- chip->batt_hot) {
- /* restore the hard hot threshold */
- set_prop_jeita_temp(chip, FG_MEM_HARD_HOT,
- hard_hot + chip->hot_hysteresis);
- chip->batt_hot = !chip->batt_hot;
- if (fg_debug_mask & FG_STATUS)
- pr_info("restore hard hot threshold: old hot=%d, new hot=%d\n",
- hard_hot,
- hard_hot + chip->hot_hysteresis);
- } else if (chip->health != POWER_SUPPLY_HEALTH_COLD &&
- chip->batt_cold) {
- /* restore the hard cold threshold */
- set_prop_jeita_temp(chip, FG_MEM_HARD_COLD,
- hard_cold - chip->cold_hysteresis);
- chip->batt_cold = !chip->batt_cold;
- if (fg_debug_mask & FG_STATUS)
- pr_info("restore hard cold threshold: old cold=%d, new cold=%d\n",
- hard_cold,
- hard_cold - chip->cold_hysteresis);
- }
-}
-
-#define BATT_INFO_STS(base) (base + 0x09)
-#define JEITA_HARD_HOT_RT_STS BIT(6)
-#define JEITA_HARD_COLD_RT_STS BIT(5)
-static int fg_init_batt_temp_state(struct fg_chip *chip)
-{
- int rc = 0;
- u8 batt_info_sts;
- int hard_hot = 0, hard_cold = 0;
-
- /*
- * read the batt_info_sts register to parse battery's
- * initial status and do hysteresis config accordingly.
- */
- rc = fg_read(chip, &batt_info_sts,
- BATT_INFO_STS(chip->batt_base), 1);
- if (rc) {
- pr_err("failed to read batt info sts, rc=%d\n", rc);
- return rc;
- }
-
- hard_hot = get_prop_jeita_temp(chip, FG_MEM_HARD_HOT);
- hard_cold = get_prop_jeita_temp(chip, FG_MEM_HARD_COLD);
- chip->batt_hot =
- (batt_info_sts & JEITA_HARD_HOT_RT_STS) ? true : false;
- chip->batt_cold =
- (batt_info_sts & JEITA_HARD_COLD_RT_STS) ? true : false;
- if (chip->batt_hot || chip->batt_cold) {
- if (chip->batt_hot) {
- chip->health = POWER_SUPPLY_HEALTH_OVERHEAT;
- set_prop_jeita_temp(chip, FG_MEM_HARD_HOT,
- hard_hot - chip->hot_hysteresis);
- } else {
- chip->health = POWER_SUPPLY_HEALTH_COLD;
- set_prop_jeita_temp(chip, FG_MEM_HARD_COLD,
- hard_cold + chip->cold_hysteresis);
- }
- }
-
- return rc;
-}
-
-static int fg_power_set_property(struct power_supply *psy,
- enum power_supply_property psp,
- const union power_supply_propval *val)
-{
- struct fg_chip *chip = power_supply_get_drvdata(psy);
- int rc = 0, unused;
-
- switch (psp) {
- case POWER_SUPPLY_PROP_COOL_TEMP:
- rc = set_prop_jeita_temp(chip, FG_MEM_SOFT_COLD, val->intval);
- break;
- case POWER_SUPPLY_PROP_WARM_TEMP:
- rc = set_prop_jeita_temp(chip, FG_MEM_SOFT_HOT, val->intval);
- break;
- case POWER_SUPPLY_PROP_UPDATE_NOW:
- if (val->intval)
- update_sram_data(chip, &unused);
- break;
- case POWER_SUPPLY_PROP_STATUS:
- chip->prev_status = chip->status;
- chip->status = val->intval;
- schedule_work(&chip->status_change_work);
- check_gain_compensation(chip);
- break;
- case POWER_SUPPLY_PROP_HEALTH:
- chip->health = val->intval;
- if (chip->health == POWER_SUPPLY_HEALTH_GOOD) {
- fg_stay_awake(&chip->resume_soc_wakeup_source);
- schedule_work(&chip->set_resume_soc_work);
- }
-
- if (chip->jeita_hysteresis_support)
- fg_hysteresis_config(chip);
- break;
- case POWER_SUPPLY_PROP_CHARGE_DONE:
- chip->charge_done = val->intval;
- if (!chip->resume_soc_lowered) {
- fg_stay_awake(&chip->resume_soc_wakeup_source);
- schedule_work(&chip->set_resume_soc_work);
- }
- break;
- case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
- if ((val->intval > 0) && (val->intval <= BUCKET_COUNT)) {
- chip->cyc_ctr.id = val->intval;
- } else {
- pr_err("rejecting invalid cycle_count_id = %d\n",
- val->intval);
- rc = -EINVAL;
- }
- break;
- case POWER_SUPPLY_PROP_SAFETY_TIMER_EXPIRED:
- chip->safety_timer_expired = val->intval;
- schedule_work(&chip->status_change_work);
- break;
- case POWER_SUPPLY_PROP_HI_POWER:
- if (chip->wa_flag & BCL_HI_POWER_FOR_CHGLED_WA) {
- chip->bcl_lpm_disabled = !!val->intval;
- schedule_work(&chip->bcl_hi_power_work);
- }
- break;
- default:
- return -EINVAL;
- };
-
- return rc;
-};
-
-static int fg_property_is_writeable(struct power_supply *psy,
- enum power_supply_property psp)
-{
- switch (psp) {
- case POWER_SUPPLY_PROP_COOL_TEMP:
- case POWER_SUPPLY_PROP_WARM_TEMP:
- case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
- return 1;
- default:
- break;
- }
-
- return 0;
-}
-
-#define SRAM_DUMP_START 0x400
-#define SRAM_DUMP_LEN 0x200
-static void dump_sram(struct work_struct *work)
-{
- int i, rc;
- u8 *buffer, rt_sts;
- char str[16];
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- dump_sram);
-
- buffer = devm_kzalloc(chip->dev, SRAM_DUMP_LEN, GFP_KERNEL);
- if (buffer == NULL) {
- pr_err("Can't allocate buffer\n");
- return;
- }
-
- rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->soc_base), 1);
- if (rc)
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->soc_base), rc);
- else
- pr_info("soc rt_sts: 0x%x\n", rt_sts);
-
- rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->batt_base), 1);
- if (rc)
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->batt_base), rc);
- else
- pr_info("batt rt_sts: 0x%x\n", rt_sts);
-
- rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->mem_base), 1);
- if (rc)
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->mem_base), rc);
- else
- pr_info("memif rt_sts: 0x%x\n", rt_sts);
-
- rc = fg_mem_read(chip, buffer, SRAM_DUMP_START, SRAM_DUMP_LEN, 0, 0);
- if (rc) {
- pr_err("dump failed: rc = %d\n", rc);
- return;
- }
-
- for (i = 0; i < SRAM_DUMP_LEN; i += 4) {
- str[0] = '\0';
- fill_string(str, DEBUG_PRINT_BUFFER_SIZE, buffer + i, 4);
- pr_info("%03X %s\n", SRAM_DUMP_START + i, str);
- }
- devm_kfree(chip->dev, buffer);
-}
-
-#define MAXRSCHANGE_REG 0x434
-#define ESR_VALUE_OFFSET 1
-#define ESR_STRICT_VALUE 0x4120391F391F3019
-#define ESR_DEFAULT_VALUE 0x58CD4A6761C34A67
-static void update_esr_value(struct work_struct *work)
-{
- union power_supply_propval prop = {0, };
- u64 esr_value;
- int rc = 0;
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- update_esr_work);
-
- if (!is_charger_available(chip))
- return;
-
- power_supply_get_property(chip->batt_psy,
- POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
-
- if (!chip->esr_strict_filter) {
- if ((prop.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER &&
- chip->status == POWER_SUPPLY_STATUS_CHARGING) ||
- (chip->status == POWER_SUPPLY_STATUS_FULL)) {
- esr_value = ESR_STRICT_VALUE;
- rc = fg_mem_write(chip, (u8 *)&esr_value,
- MAXRSCHANGE_REG, 8,
- ESR_VALUE_OFFSET, 0);
- if (rc)
- pr_err("failed to write strict ESR value rc=%d\n",
- rc);
- else
- chip->esr_strict_filter = true;
- }
- } else if ((prop.intval != POWER_SUPPLY_CHARGE_TYPE_TAPER &&
- chip->status == POWER_SUPPLY_STATUS_CHARGING) ||
- (chip->status == POWER_SUPPLY_STATUS_DISCHARGING)) {
- esr_value = ESR_DEFAULT_VALUE;
- rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8,
- ESR_VALUE_OFFSET, 0);
- if (rc)
- pr_err("failed to write default ESR value rc=%d\n", rc);
- else
- chip->esr_strict_filter = false;
- }
-}
-
-#define TEMP_COUNTER_REG 0x580
-#define VBAT_FILTERED_OFFSET 1
-#define GAIN_REG 0x424
-#define GAIN_OFFSET 1
-#define K_VCOR_REG 0x484
-#define DEF_GAIN_OFFSET 2
-#define PICO_UNIT 0xE8D4A51000LL
-#define ATTO_UNIT 0xDE0B6B3A7640000LL
-#define VBAT_REF 3800000
-
-/*
- * IADC Gain compensation steps:
- * If Input/OTG absent:
- * - read VBAT_FILTERED, KVCOR, GAIN
- * - calculate the gain compensation using following formula:
- * gain = (1 + gain) * (1 + kvcor * (vbat_filtered - 3800000)) - 1;
- * else
- * - reset to the default gain compensation
- */
-static void iadc_gain_comp_work(struct work_struct *work)
-{
- u8 reg[4];
- int rc;
- uint64_t vbat_filtered;
- int64_t gain, kvcor, temp, numerator;
- struct fg_chip *chip = container_of(work, struct fg_chip,
- gain_comp_work);
- bool input_present = is_input_present(chip);
- bool otg_present = is_otg_present(chip);
-
- if (!chip->init_done)
- goto done;
-
- if (!input_present && !otg_present) {
- /* read VBAT_FILTERED */
- rc = fg_mem_read(chip, reg, TEMP_COUNTER_REG, 3,
- VBAT_FILTERED_OFFSET, 0);
- if (rc) {
- pr_err("Failed to read VBAT: rc=%d\n", rc);
- goto done;
- }
- temp = (reg[2] << 16) | (reg[1] << 8) | reg[0];
- vbat_filtered = div_u64((u64)temp * LSB_24B_NUMRTR,
- LSB_24B_DENMTR);
-
- /* read K_VCOR */
- rc = fg_mem_read(chip, reg, K_VCOR_REG, 2, 0, 0);
- if (rc) {
- pr_err("Failed to KVCOR rc=%d\n", rc);
- goto done;
- }
- kvcor = half_float(reg);
-
- /* calculate gain */
- numerator = (MICRO_UNIT + chip->iadc_comp_data.dfl_gain)
- * (PICO_UNIT + kvcor * (vbat_filtered - VBAT_REF))
- - ATTO_UNIT;
- gain = div64_s64(numerator, PICO_UNIT);
-
- /* write back gain */
- half_float_to_buffer(gain, reg);
- rc = fg_mem_write(chip, reg, GAIN_REG, 2, GAIN_OFFSET, 0);
- if (rc) {
- pr_err("Failed to write gain reg rc=%d\n", rc);
- goto done;
- }
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("IADC gain update [%x %x]\n", reg[1], reg[0]);
- chip->iadc_comp_data.gain_active = true;
- } else {
- /* reset gain register */
- rc = fg_mem_write(chip, chip->iadc_comp_data.dfl_gain_reg,
- GAIN_REG, 2, GAIN_OFFSET, 0);
- if (rc) {
- pr_err("unable to write gain comp: %d\n", rc);
- goto done;
- }
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("IADC gain reset [%x %x]\n",
- chip->iadc_comp_data.dfl_gain_reg[1],
- chip->iadc_comp_data.dfl_gain_reg[0]);
- chip->iadc_comp_data.gain_active = false;
- }
-
-done:
- fg_relax(&chip->gain_comp_wakeup_source);
-}
-
-#define BATT_MISSING_STS BIT(6)
-static bool is_battery_missing(struct fg_chip *chip)
-{
- int rc;
- u8 fg_batt_sts;
-
- rc = fg_read(chip, &fg_batt_sts,
- INT_RT_STS(chip->batt_base), 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->batt_base), rc);
- return false;
- }
-
- return (fg_batt_sts & BATT_MISSING_STS) ? true : false;
-}
-
-#define SOC_FIRST_EST_DONE BIT(5)
-static bool is_first_est_done(struct fg_chip *chip)
-{
- int rc;
- u8 fg_soc_sts;
-
- rc = fg_read(chip, &fg_soc_sts,
- INT_RT_STS(chip->soc_base), 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->soc_base), rc);
- return false;
- }
-
- return (fg_soc_sts & SOC_FIRST_EST_DONE) ? true : false;
-}
-
-static irqreturn_t fg_vbatt_low_handler(int irq, void *_chip)
-{
- struct fg_chip *chip = _chip;
- int rc;
- bool vbatt_low_sts;
-
- if (fg_debug_mask & FG_IRQS)
- pr_info("vbatt-low triggered\n");
-
- if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
- rc = fg_get_vbatt_status(chip, &vbatt_low_sts);
- if (rc) {
- pr_err("error in reading vbatt_status, rc:%d\n", rc);
- goto out;
- }
- if (!vbatt_low_sts && chip->vbat_low_irq_enabled) {
- if (fg_debug_mask & FG_IRQS)
- pr_info("disabling vbatt_low irq\n");
- disable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
- disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
- chip->vbat_low_irq_enabled = false;
- }
- }
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
-out:
- return IRQ_HANDLED;
-}
-
-static irqreturn_t fg_batt_missing_irq_handler(int irq, void *_chip)
-{
- struct fg_chip *chip = _chip;
- bool batt_missing = is_battery_missing(chip);
-
- if (batt_missing) {
- chip->battery_missing = true;
- chip->profile_loaded = false;
- chip->batt_type = default_batt_type;
- mutex_lock(&chip->cyc_ctr.lock);
- if (fg_debug_mask & FG_IRQS)
- pr_info("battery missing, clearing cycle counters\n");
- clear_cycle_counter(chip);
- mutex_unlock(&chip->cyc_ctr.lock);
- } else {
- if (!chip->use_otp_profile) {
- reinit_completion(&chip->batt_id_avail);
- reinit_completion(&chip->first_soc_done);
- schedule_delayed_work(&chip->batt_profile_init, 0);
- cancel_delayed_work(&chip->update_sram_data);
- schedule_delayed_work(
- &chip->update_sram_data,
- msecs_to_jiffies(0));
- } else {
- chip->battery_missing = false;
- }
- }
-
- if (fg_debug_mask & FG_IRQS)
- pr_info("batt-missing triggered: %s\n",
- batt_missing ? "missing" : "present");
-
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip)
-{
- struct fg_chip *chip = _chip;
- u8 mem_if_sts;
- int rc;
-
- rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1);
- if (rc) {
- pr_err("failed to read mem status rc=%d\n", rc);
- return IRQ_HANDLED;
- }
-
- if (fg_check_sram_access(chip)) {
- if ((fg_debug_mask & FG_IRQS)
- & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
- pr_info("sram access granted\n");
- reinit_completion(&chip->sram_access_revoked);
- complete_all(&chip->sram_access_granted);
- } else {
- if ((fg_debug_mask & FG_IRQS)
- & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
- pr_info("sram access revoked\n");
- complete_all(&chip->sram_access_revoked);
- }
-
- if (!rc && (fg_debug_mask & FG_IRQS)
- & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
- pr_info("mem_if sts 0x%02x\n", mem_if_sts);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t fg_soc_irq_handler(int irq, void *_chip)
-{
- struct fg_chip *chip = _chip;
- u8 soc_rt_sts;
- int rc;
-
- rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->soc_base), rc);
- }
-
- if (fg_debug_mask & FG_IRQS)
- pr_info("triggered 0x%x\n", soc_rt_sts);
-
- schedule_work(&chip->battery_age_work);
-
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
-
- if (chip->rslow_comp.chg_rs_to_rslow > 0 &&
- chip->rslow_comp.chg_rslow_comp_c1 > 0 &&
- chip->rslow_comp.chg_rslow_comp_c2 > 0)
- schedule_work(&chip->rslow_comp_work);
- if (chip->cyc_ctr.en)
- schedule_work(&chip->cycle_count_work);
- schedule_work(&chip->update_esr_work);
- if (chip->charge_full)
- schedule_work(&chip->charge_full_work);
- if (chip->wa_flag & IADC_GAIN_COMP_WA
- && chip->iadc_comp_data.gain_active) {
- fg_stay_awake(&chip->gain_comp_wakeup_source);
- schedule_work(&chip->gain_comp_work);
- }
-
- if (chip->wa_flag & USE_CC_SOC_REG
- && chip->learning_data.active) {
- fg_stay_awake(&chip->capacity_learning_wakeup_source);
- schedule_work(&chip->fg_cap_learning_work);
- }
-
- if (chip->esr_pulse_tune_en) {
- fg_stay_awake(&chip->esr_extract_wakeup_source);
- schedule_work(&chip->esr_extract_config_work);
- }
-
- return IRQ_HANDLED;
-}
-
-#define FG_EMPTY_DEBOUNCE_MS 1500
-static irqreturn_t fg_empty_soc_irq_handler(int irq, void *_chip)
-{
- struct fg_chip *chip = _chip;
- u8 soc_rt_sts;
- int rc;
-
- rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->soc_base), rc);
- goto done;
- }
-
- if (fg_debug_mask & FG_IRQS)
- pr_info("triggered 0x%x\n", soc_rt_sts);
- if (fg_is_batt_empty(chip)) {
- fg_stay_awake(&chip->empty_check_wakeup_source);
- schedule_delayed_work(&chip->check_empty_work,
- msecs_to_jiffies(FG_EMPTY_DEBOUNCE_MS));
- } else {
- chip->soc_empty = false;
- }
-
-done:
- return IRQ_HANDLED;
-}
-
-static irqreturn_t fg_first_soc_irq_handler(int irq, void *_chip)
-{
- struct fg_chip *chip = _chip;
-
- if (fg_debug_mask & FG_IRQS)
- pr_info("triggered\n");
-
- if (fg_est_dump)
- schedule_work(&chip->dump_sram);
-
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
-
- complete_all(&chip->first_soc_done);
-
- return IRQ_HANDLED;
-}
-
-static void fg_external_power_changed(struct power_supply *psy)
-{
- struct fg_chip *chip = power_supply_get_drvdata(psy);
-
- if (is_input_present(chip) && chip->rslow_comp.active &&
- chip->rslow_comp.chg_rs_to_rslow > 0 &&
- chip->rslow_comp.chg_rslow_comp_c1 > 0 &&
- chip->rslow_comp.chg_rslow_comp_c2 > 0)
- schedule_work(&chip->rslow_comp_work);
- if (!is_input_present(chip) && chip->resume_soc_lowered) {
- fg_stay_awake(&chip->resume_soc_wakeup_source);
- schedule_work(&chip->set_resume_soc_work);
- }
- if (!is_input_present(chip) && chip->charge_full)
- schedule_work(&chip->charge_full_work);
-}
-
-static void set_resume_soc_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- set_resume_soc_work);
- int rc, resume_soc_raw;
-
- if (is_input_present(chip) && !chip->resume_soc_lowered) {
- if (!chip->charge_done)
- goto done;
- resume_soc_raw = get_monotonic_soc_raw(chip)
- - (0xFF - settings[FG_MEM_RESUME_SOC].value);
- if (resume_soc_raw > 0 && resume_soc_raw < FULL_SOC_RAW) {
- rc = fg_set_resume_soc(chip, resume_soc_raw);
- if (rc) {
- pr_err("Couldn't set resume SOC for FG\n");
- goto done;
- }
- if (fg_debug_mask & FG_STATUS) {
- pr_info("resume soc lowered to 0x%02x\n",
- resume_soc_raw);
- }
- } else if (settings[FG_MEM_RESUME_SOC].value > 0) {
- pr_err("bad resume soc 0x%02x\n", resume_soc_raw);
- }
- chip->charge_done = false;
- chip->resume_soc_lowered = true;
- } else if (chip->resume_soc_lowered && (!is_input_present(chip)
- || chip->health == POWER_SUPPLY_HEALTH_GOOD)) {
- resume_soc_raw = settings[FG_MEM_RESUME_SOC].value;
- if (resume_soc_raw > 0 && resume_soc_raw < FULL_SOC_RAW) {
- rc = fg_set_resume_soc(chip, resume_soc_raw);
- if (rc) {
- pr_err("Couldn't set resume SOC for FG\n");
- goto done;
- }
- if (fg_debug_mask & FG_STATUS) {
- pr_info("resume soc set to 0x%02x\n",
- resume_soc_raw);
- }
- } else if (settings[FG_MEM_RESUME_SOC].value > 0) {
- pr_err("bad resume soc 0x%02x\n", resume_soc_raw);
- }
- chip->resume_soc_lowered = false;
- }
-done:
- fg_relax(&chip->resume_soc_wakeup_source);
-}
-
-
-#define OCV_COEFFS_START_REG 0x4C0
-#define OCV_JUNCTION_REG 0x4D8
-#define NOM_CAP_REG 0x4F4
-#define CUTOFF_VOLTAGE_REG 0x40C
-#define RSLOW_CFG_REG 0x538
-#define RSLOW_CFG_OFFSET 2
-#define RSLOW_THRESH_REG 0x52C
-#define RSLOW_THRESH_OFFSET 0
-#define TEMP_RS_TO_RSLOW_OFFSET 2
-#define RSLOW_COMP_REG 0x528
-#define RSLOW_COMP_C1_OFFSET 0
-#define RSLOW_COMP_C2_OFFSET 2
-static int populate_system_data(struct fg_chip *chip)
-{
- u8 buffer[24];
- int rc, i;
- int16_t cc_mah;
-
- fg_mem_lock(chip);
- rc = fg_mem_read(chip, buffer, OCV_COEFFS_START_REG, 24, 0, 0);
- if (rc) {
- pr_err("Failed to read ocv coefficients: %d\n", rc);
- goto done;
- }
- for (i = 0; i < 12; i += 1)
- chip->ocv_coeffs[i] = half_float(buffer + (i * 2));
- if (fg_debug_mask & FG_AGING) {
- pr_info("coeffs1 = %lld %lld %lld %lld\n",
- chip->ocv_coeffs[0], chip->ocv_coeffs[1],
- chip->ocv_coeffs[2], chip->ocv_coeffs[3]);
- pr_info("coeffs2 = %lld %lld %lld %lld\n",
- chip->ocv_coeffs[4], chip->ocv_coeffs[5],
- chip->ocv_coeffs[6], chip->ocv_coeffs[7]);
- pr_info("coeffs3 = %lld %lld %lld %lld\n",
- chip->ocv_coeffs[8], chip->ocv_coeffs[9],
- chip->ocv_coeffs[10], chip->ocv_coeffs[11]);
- }
- rc = fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 1, 0, 0);
- chip->ocv_junction_p1p2 = buffer[0] * 100 / 255;
- rc |= fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 1, 1, 0);
- chip->ocv_junction_p2p3 = buffer[0] * 100 / 255;
- if (rc) {
- pr_err("Failed to read ocv junctions: %d\n", rc);
- goto done;
- }
- rc = fg_mem_read(chip, buffer, NOM_CAP_REG, 2, 0, 0);
- if (rc) {
- pr_err("Failed to read nominal capacitance: %d\n", rc);
- goto done;
- }
- chip->nom_cap_uah = bcap_uah_2b(buffer);
- chip->actual_cap_uah = chip->nom_cap_uah;
- if (chip->learning_data.learned_cc_uah == 0) {
- chip->learning_data.learned_cc_uah = chip->nom_cap_uah;
- fg_cap_learning_save_data(chip);
- } else if (chip->learning_data.feedback_on) {
- cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000);
- rc = fg_calc_and_store_cc_soc_coeff(chip, cc_mah);
- if (rc)
- pr_err("Error in restoring cc_soc_coeff, rc:%d\n", rc);
- }
- rc = fg_mem_read(chip, buffer, CUTOFF_VOLTAGE_REG, 2, 0, 0);
- if (rc) {
- pr_err("Failed to read cutoff voltage: %d\n", rc);
- goto done;
- }
- chip->cutoff_voltage = voltage_2b(buffer);
- if (fg_debug_mask & FG_AGING)
- pr_info("cutoff_voltage = %lld, nom_cap_uah = %d p1p2 = %d, p2p3 = %d\n",
- chip->cutoff_voltage, chip->nom_cap_uah,
- chip->ocv_junction_p1p2,
- chip->ocv_junction_p2p3);
-
- rc = fg_mem_read(chip, buffer, RSLOW_CFG_REG, 1, RSLOW_CFG_OFFSET, 0);
- if (rc) {
- pr_err("unable to read rslow cfg: %d\n", rc);
- goto done;
- }
- chip->rslow_comp.rslow_cfg = buffer[0];
- rc = fg_mem_read(chip, buffer, RSLOW_THRESH_REG, 1,
- RSLOW_THRESH_OFFSET, 0);
- if (rc) {
- pr_err("unable to read rslow thresh: %d\n", rc);
- goto done;
- }
- chip->rslow_comp.rslow_thr = buffer[0];
- rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2,
- RSLOW_THRESH_OFFSET, 0);
- if (rc) {
- pr_err("unable to read rs to rslow: %d\n", rc);
- goto done;
- }
- memcpy(chip->rslow_comp.rs_to_rslow, buffer, 2);
- rc = fg_mem_read(chip, buffer, RSLOW_COMP_REG, 4,
- RSLOW_COMP_C1_OFFSET, 0);
- if (rc) {
- pr_err("unable to read rslow comp: %d\n", rc);
- goto done;
- }
- memcpy(chip->rslow_comp.rslow_comp, buffer, 4);
-
-done:
- fg_mem_release(chip);
- return rc;
-}
-
-#define RSLOW_CFG_MASK (BIT(2) | BIT(3) | BIT(4) | BIT(5))
-#define RSLOW_CFG_ON_VAL (BIT(2) | BIT(3))
-#define RSLOW_THRESH_FULL_VAL 0xFF
-static int fg_rslow_charge_comp_set(struct fg_chip *chip)
-{
- int rc;
- u8 buffer[2];
-
- mutex_lock(&chip->rslow_comp.lock);
- fg_mem_lock(chip);
-
- rc = fg_mem_masked_write(chip, RSLOW_CFG_REG,
- RSLOW_CFG_MASK, RSLOW_CFG_ON_VAL, RSLOW_CFG_OFFSET);
- if (rc) {
- pr_err("unable to write rslow cfg: %d\n", rc);
- goto done;
- }
- rc = fg_mem_masked_write(chip, RSLOW_THRESH_REG,
- 0xFF, RSLOW_THRESH_FULL_VAL, RSLOW_THRESH_OFFSET);
- if (rc) {
- pr_err("unable to write rslow thresh: %d\n", rc);
- goto done;
- }
-
- half_float_to_buffer(chip->rslow_comp.chg_rs_to_rslow, buffer);
- rc = fg_mem_write(chip, buffer,
- TEMP_RS_TO_RSLOW_REG, 2, TEMP_RS_TO_RSLOW_OFFSET, 0);
- if (rc) {
- pr_err("unable to write rs to rslow: %d\n", rc);
- goto done;
- }
- half_float_to_buffer(chip->rslow_comp.chg_rslow_comp_c1, buffer);
- rc = fg_mem_write(chip, buffer,
- RSLOW_COMP_REG, 2, RSLOW_COMP_C1_OFFSET, 0);
- if (rc) {
- pr_err("unable to write rslow comp: %d\n", rc);
- goto done;
- }
- half_float_to_buffer(chip->rslow_comp.chg_rslow_comp_c2, buffer);
- rc = fg_mem_write(chip, buffer,
- RSLOW_COMP_REG, 2, RSLOW_COMP_C2_OFFSET, 0);
- if (rc) {
- pr_err("unable to write rslow comp: %d\n", rc);
- goto done;
- }
- chip->rslow_comp.active = true;
- if (fg_debug_mask & FG_STATUS)
- pr_info("Activated rslow charge comp values\n");
-
-done:
- fg_mem_release(chip);
- mutex_unlock(&chip->rslow_comp.lock);
- return rc;
-}
-
-#define RSLOW_CFG_ORIG_MASK (BIT(4) | BIT(5))
-static int fg_rslow_charge_comp_clear(struct fg_chip *chip)
-{
- u8 reg;
- int rc;
-
- mutex_lock(&chip->rslow_comp.lock);
- fg_mem_lock(chip);
-
- reg = chip->rslow_comp.rslow_cfg & RSLOW_CFG_ORIG_MASK;
- rc = fg_mem_masked_write(chip, RSLOW_CFG_REG,
- RSLOW_CFG_MASK, reg, RSLOW_CFG_OFFSET);
- if (rc) {
- pr_err("unable to write rslow cfg: %d\n", rc);
- goto done;
- }
- rc = fg_mem_masked_write(chip, RSLOW_THRESH_REG,
- 0xFF, chip->rslow_comp.rslow_thr, RSLOW_THRESH_OFFSET);
- if (rc) {
- pr_err("unable to write rslow thresh: %d\n", rc);
- goto done;
- }
-
- rc = fg_mem_write(chip, chip->rslow_comp.rs_to_rslow,
- TEMP_RS_TO_RSLOW_REG, 2, TEMP_RS_TO_RSLOW_OFFSET, 0);
- if (rc) {
- pr_err("unable to write rs to rslow: %d\n", rc);
- goto done;
- }
- rc = fg_mem_write(chip, chip->rslow_comp.rslow_comp,
- RSLOW_COMP_REG, 4, RSLOW_COMP_C1_OFFSET, 0);
- if (rc) {
- pr_err("unable to write rslow comp: %d\n", rc);
- goto done;
- }
- chip->rslow_comp.active = false;
- if (fg_debug_mask & FG_STATUS)
- pr_info("Cleared rslow charge comp values\n");
-
-done:
- fg_mem_release(chip);
- mutex_unlock(&chip->rslow_comp.lock);
- return rc;
-}
-
-static void rslow_comp_work(struct work_struct *work)
-{
- int battery_soc_1b;
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- rslow_comp_work);
-
- battery_soc_1b = get_battery_soc_raw(chip) >> 16;
- if (battery_soc_1b > chip->rslow_comp.chg_rslow_comp_thr
- && chip->status == POWER_SUPPLY_STATUS_CHARGING) {
- if (!chip->rslow_comp.active)
- fg_rslow_charge_comp_set(chip);
- } else {
- if (chip->rslow_comp.active)
- fg_rslow_charge_comp_clear(chip);
- }
-}
-
-#define MICROUNITS_TO_ADC_RAW(units) \
- div64_s64(units * LSB_16B_DENMTR, LSB_16B_NUMRTR)
-static int update_chg_iterm(struct fg_chip *chip)
-{
- u8 data[2];
- u16 converted_current_raw;
- s64 current_ma = -settings[FG_MEM_CHG_TERM_CURRENT].value;
-
- converted_current_raw = (s16)MICROUNITS_TO_ADC_RAW(current_ma * 1000);
- data[0] = cpu_to_le16(converted_current_raw) & 0xFF;
- data[1] = cpu_to_le16(converted_current_raw) >> 8;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("current = %lld, converted_raw = %04x, data = %02x %02x\n",
- current_ma, converted_current_raw, data[0], data[1]);
- return fg_mem_write(chip, data,
- settings[FG_MEM_CHG_TERM_CURRENT].address,
- 2, settings[FG_MEM_CHG_TERM_CURRENT].offset, 0);
-}
-
-#define CC_CV_SETPOINT_REG 0x4F8
-#define CC_CV_SETPOINT_OFFSET 0
-static void update_cc_cv_setpoint(struct fg_chip *chip)
-{
- int rc;
- u8 tmp[2];
-
- if (!chip->cc_cv_threshold_mv)
- return;
- batt_to_setpoint_adc(chip->cc_cv_threshold_mv, tmp);
- rc = fg_mem_write(chip, tmp, CC_CV_SETPOINT_REG, 2,
- CC_CV_SETPOINT_OFFSET, 0);
- if (rc) {
- pr_err("failed to write CC_CV_VOLT rc=%d\n", rc);
- return;
- }
- if (fg_debug_mask & FG_STATUS)
- pr_info("Wrote %x %x to address %x for CC_CV setpoint\n",
- tmp[0], tmp[1], CC_CV_SETPOINT_REG);
-}
-
-#define CBITS_INPUT_FILTER_REG 0x4B4
-#define CBITS_RMEAS1_OFFSET 1
-#define CBITS_RMEAS2_OFFSET 2
-#define CBITS_RMEAS1_DEFAULT_VAL 0x65
-#define CBITS_RMEAS2_DEFAULT_VAL 0x65
-#define IMPTR_FAST_TIME_SHIFT 1
-#define IMPTR_LONG_TIME_SHIFT (1 << 4)
-#define IMPTR_PULSE_CTR_CHG 1
-#define IMPTR_PULSE_CTR_DISCHG (1 << 4)
-static int fg_config_imptr_pulse(struct fg_chip *chip, bool slow)
-{
- int rc;
- u8 cntr[2] = {0, 0};
- u8 val;
-
- if (slow == chip->imptr_pulse_slow_en) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("imptr_pulse_slow is %sabled already\n",
- slow ? "en" : "dis");
- return 0;
- }
-
- fg_mem_lock(chip);
-
- val = slow ? (IMPTR_FAST_TIME_SHIFT | IMPTR_LONG_TIME_SHIFT) :
- CBITS_RMEAS1_DEFAULT_VAL;
- rc = fg_mem_write(chip, &val, CBITS_INPUT_FILTER_REG, 1,
- CBITS_RMEAS1_OFFSET, 0);
- if (rc) {
- pr_err("unable to write cbits_rmeas1_offset rc=%d\n", rc);
- goto done;
- }
-
- val = slow ? (IMPTR_PULSE_CTR_CHG | IMPTR_PULSE_CTR_DISCHG) :
- CBITS_RMEAS2_DEFAULT_VAL;
- rc = fg_mem_write(chip, &val, CBITS_INPUT_FILTER_REG, 1,
- CBITS_RMEAS2_OFFSET, 0);
- if (rc) {
- pr_err("unable to write cbits_rmeas2_offset rc=%d\n", rc);
- goto done;
- }
-
- if (slow) {
- rc = fg_mem_write(chip, cntr, COUNTER_IMPTR_REG, 4,
- COUNTER_IMPTR_OFFSET, 0);
- if (rc) {
- pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
- goto done;
- }
-
- rc = fg_mem_write(chip, cntr, COUNTER_PULSE_REG, 2,
- COUNTER_PULSE_OFFSET, 0);
- if (rc) {
- pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
- goto done;
- }
- }
-
- chip->imptr_pulse_slow_en = slow;
- if (fg_debug_mask & FG_STATUS)
- pr_info("imptr_pulse_slow is %sabled\n", slow ? "en" : "dis");
-done:
- fg_mem_release(chip);
- return rc;
-}
-
-#define CURRENT_DELTA_MIN_REG 0x42C
-#define CURRENT_DELTA_MIN_OFFSET 1
-#define SYS_CFG_1_REG 0x4AC
-#define SYS_CFG_1_OFFSET 0
-#define CURRENT_DELTA_MIN_DEFAULT 0x16
-#define CURRENT_DELTA_MIN_500MA 0xCD
-#define RSLOW_CFG_USE_FIX_RSER_VAL BIT(7)
-#define ENABLE_ESR_PULSE_VAL BIT(3)
-static int fg_config_esr_extract(struct fg_chip *chip, bool disable)
-{
- int rc;
- u8 val;
-
- if (disable == chip->esr_extract_disabled) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("ESR extract already %sabled\n",
- disable ? "dis" : "en");
- return 0;
- }
-
- fg_mem_lock(chip);
-
- val = disable ? CURRENT_DELTA_MIN_500MA :
- CURRENT_DELTA_MIN_DEFAULT;
- rc = fg_mem_write(chip, &val, CURRENT_DELTA_MIN_REG, 1,
- CURRENT_DELTA_MIN_OFFSET, 0);
- if (rc) {
- pr_err("unable to write curr_delta_min rc=%d\n", rc);
- goto done;
- }
-
- val = disable ? RSLOW_CFG_USE_FIX_RSER_VAL : 0;
- rc = fg_mem_masked_write(chip, RSLOW_CFG_REG,
- RSLOW_CFG_USE_FIX_RSER_VAL, val, RSLOW_CFG_OFFSET);
- if (rc) {
- pr_err("unable to write rslow cfg rc= %d\n", rc);
- goto done;
- }
-
- val = disable ? 0 : ENABLE_ESR_PULSE_VAL;
- rc = fg_mem_masked_write(chip, SYS_CFG_1_REG,
- ENABLE_ESR_PULSE_VAL, val, SYS_CFG_1_OFFSET);
- if (rc) {
- pr_err("unable to write sys_cfg_1 rc= %d\n", rc);
- goto done;
- }
-
- chip->esr_extract_disabled = disable;
- if (fg_debug_mask & FG_STATUS)
- pr_info("ESR extract is %sabled\n", disable ? "dis" : "en");
-done:
- fg_mem_release(chip);
- return rc;
-}
-
-#define ESR_EXTRACT_STOP_SOC 2
-#define IMPTR_PULSE_CONFIG_SOC 5
-static void esr_extract_config_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work, struct fg_chip,
- esr_extract_config_work);
- bool input_present = is_input_present(chip);
- int capacity = get_prop_capacity(chip);
-
- if (input_present && capacity <= ESR_EXTRACT_STOP_SOC) {
- fg_config_esr_extract(chip, true);
- } else if (capacity > ESR_EXTRACT_STOP_SOC) {
- fg_config_esr_extract(chip, false);
-
- if (capacity <= IMPTR_PULSE_CONFIG_SOC)
- fg_config_imptr_pulse(chip, true);
- else
- fg_config_imptr_pulse(chip, false);
- }
-
- fg_relax(&chip->esr_extract_wakeup_source);
-}
-
-#define LOW_LATENCY BIT(6)
-#define BATT_PROFILE_OFFSET 0x4C0
-#define PROFILE_INTEGRITY_REG 0x53C
-#define PROFILE_INTEGRITY_BIT BIT(0)
-#define FIRST_EST_DONE_BIT BIT(5)
-#define MAX_TRIES_FIRST_EST 3
-#define FIRST_EST_WAIT_MS 2000
-#define PROFILE_LOAD_TIMEOUT_MS 5000
-static int fg_do_restart(struct fg_chip *chip, bool write_profile)
-{
- int rc, ibat_ua;
- u8 reg = 0;
- u8 buf[2];
- bool tried_once = false;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("restarting fuel gauge...\n");
-
-try_again:
- if (write_profile) {
- if (!chip->charging_disabled) {
- pr_err("Charging not yet disabled!\n");
- return -EINVAL;
- }
-
- ibat_ua = get_sram_prop_now(chip, FG_DATA_CURRENT);
- if (ibat_ua == -EINVAL) {
- pr_err("SRAM not updated yet!\n");
- return ibat_ua;
- }
-
- if (ibat_ua < 0) {
- pr_warn("Charging enabled?, ibat_ua: %d\n", ibat_ua);
-
- if (!tried_once) {
- cancel_delayed_work(&chip->update_sram_data);
- schedule_delayed_work(&chip->update_sram_data,
- msecs_to_jiffies(0));
- msleep(1000);
- tried_once = true;
- goto try_again;
- }
- }
- }
-
- chip->fg_restarting = true;
- /*
- * save the temperature if the sw rbias control is active so that there
- * is no gap of time when there is no valid temperature read after the
- * restart
- */
- if (chip->sw_rbias_ctrl) {
- rc = fg_mem_read(chip, buf,
- fg_data[FG_DATA_BATT_TEMP].address,
- fg_data[FG_DATA_BATT_TEMP].len,
- fg_data[FG_DATA_BATT_TEMP].offset, 0);
- if (rc) {
- pr_err("failed to read batt temp rc=%d\n", rc);
- goto sub_and_fail;
- }
- }
- /*
- * release the sram access and configure the correct settings
- * before re-requesting access.
- */
- mutex_lock(&chip->rw_lock);
- fg_release_access(chip);
-
- rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD,
- NO_OTP_PROF_RELOAD, 0, 1);
- if (rc) {
- pr_err("failed to set no otp reload bit\n");
- goto unlock_and_fail;
- }
-
- /* unset the restart bits so the fg doesn't continuously restart */
- reg = REDO_FIRST_ESTIMATE | RESTART_GO;
- rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART,
- reg, 0, 1);
- if (rc) {
- pr_err("failed to unset fg restart: %d\n", rc);
- goto unlock_and_fail;
- }
-
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
- LOW_LATENCY, LOW_LATENCY, 1);
- if (rc) {
- pr_err("failed to set low latency access bit\n");
- goto unlock_and_fail;
- }
- mutex_unlock(&chip->rw_lock);
-
- /* read once to get a fg cycle in */
- rc = fg_mem_read(chip, &reg, PROFILE_INTEGRITY_REG, 1, 0, 0);
- if (rc) {
- pr_err("failed to read profile integrity rc=%d\n", rc);
- goto fail;
- }
-
- /*
- * If this is not the first time a profile has been loaded, sleep for
- * 3 seconds to make sure the NO_OTP_RELOAD is cleared in memory
- */
- if (chip->first_profile_loaded)
- msleep(3000);
-
- mutex_lock(&chip->rw_lock);
- fg_release_access(chip);
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip), LOW_LATENCY, 0, 1);
- if (rc) {
- pr_err("failed to set low latency access bit\n");
- goto unlock_and_fail;
- }
-
- atomic_add_return(1, &chip->memif_user_cnt);
- mutex_unlock(&chip->rw_lock);
-
- if (write_profile) {
- /* write the battery profile */
- rc = fg_mem_write(chip, chip->batt_profile, BATT_PROFILE_OFFSET,
- chip->batt_profile_len, 0, 1);
- if (rc) {
- pr_err("failed to write profile rc=%d\n", rc);
- goto sub_and_fail;
- }
- /* write the integrity bits and release access */
- rc = fg_mem_masked_write(chip, PROFILE_INTEGRITY_REG,
- PROFILE_INTEGRITY_BIT,
- PROFILE_INTEGRITY_BIT, 0);
- if (rc) {
- pr_err("failed to write profile rc=%d\n", rc);
- goto sub_and_fail;
- }
- }
-
- /* decrement the user count so that memory access can be released */
- fg_release_access_if_necessary(chip);
-
- /*
- * make sure that the first estimate has completed
- * in case of a hotswap
- */
- rc = wait_for_completion_interruptible_timeout(&chip->first_soc_done,
- msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS));
- if (rc <= 0) {
- pr_err("transaction timed out rc=%d\n", rc);
- rc = -ETIMEDOUT;
- goto fail;
- }
-
- /*
- * reinitialize the completion so that the driver knows when the restart
- * finishes
- */
- reinit_completion(&chip->first_soc_done);
-
- if (chip->esr_pulse_tune_en) {
- fg_stay_awake(&chip->esr_extract_wakeup_source);
- schedule_work(&chip->esr_extract_config_work);
- }
-
- /*
- * set the restart bits so that the next fg cycle will not reload
- * the profile
- */
- rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD,
- NO_OTP_PROF_RELOAD, NO_OTP_PROF_RELOAD, 1);
- if (rc) {
- pr_err("failed to set no otp reload bit\n");
- goto fail;
- }
-
- reg = REDO_FIRST_ESTIMATE | RESTART_GO;
- rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART,
- reg, reg, 1);
- if (rc) {
- pr_err("failed to set fg restart: %d\n", rc);
- goto fail;
- }
-
- /* wait for the first estimate to complete */
- rc = wait_for_completion_interruptible_timeout(&chip->first_soc_done,
- msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS));
- if (rc <= 0) {
- pr_err("transaction timed out rc=%d\n", rc);
- rc = -ETIMEDOUT;
- goto fail;
- }
- rc = fg_read(chip, &reg, INT_RT_STS(chip->soc_base), 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->soc_base), rc);
- goto fail;
- }
- if ((reg & FIRST_EST_DONE_BIT) == 0)
- pr_err("Battery profile reloading failed, no first estimate\n");
-
- rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD,
- NO_OTP_PROF_RELOAD, 0, 1);
- if (rc) {
- pr_err("failed to set no otp reload bit\n");
- goto fail;
- }
- /* unset the restart bits so the fg doesn't continuously restart */
- reg = REDO_FIRST_ESTIMATE | RESTART_GO;
- rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART,
- reg, 0, 1);
- if (rc) {
- pr_err("failed to unset fg restart: %d\n", rc);
- goto fail;
- }
-
- /* restore the battery temperature reading here */
- if (chip->sw_rbias_ctrl) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("reloaded 0x%02x%02x into batt temp",
- buf[0], buf[1]);
- rc = fg_mem_write(chip, buf,
- fg_data[FG_DATA_BATT_TEMP].address,
- fg_data[FG_DATA_BATT_TEMP].len,
- fg_data[FG_DATA_BATT_TEMP].offset, 0);
- if (rc) {
- pr_err("failed to write batt temp rc=%d\n", rc);
- goto fail;
- }
- }
-
- /* Enable charging now as the first estimate is done now */
- if (chip->charging_disabled) {
- rc = set_prop_enable_charging(chip, true);
- if (rc)
- pr_err("Failed to enable charging, rc=%d\n", rc);
- else
- chip->charging_disabled = false;
- }
-
- chip->fg_restarting = false;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("done!\n");
- return 0;
-
-unlock_and_fail:
- mutex_unlock(&chip->rw_lock);
- goto fail;
-sub_and_fail:
- fg_release_access_if_necessary(chip);
- goto fail;
-fail:
- chip->fg_restarting = false;
- return -EINVAL;
-}
-
-#define FG_PROFILE_LEN 128
-#define PROFILE_COMPARE_LEN 32
-#define THERMAL_COEFF_ADDR 0x444
-#define THERMAL_COEFF_OFFSET 0x2
-#define BATTERY_PSY_WAIT_MS 2000
-static int fg_batt_profile_init(struct fg_chip *chip)
-{
- int rc = 0, ret, len, batt_id;
- struct device_node *node = chip->pdev->dev.of_node;
- struct device_node *batt_node, *profile_node;
- const char *data, *batt_type_str;
- bool tried_again = false, vbat_in_range, profiles_same;
- u8 reg = 0;
-
-wait:
- fg_stay_awake(&chip->profile_wakeup_source);
- ret = wait_for_completion_interruptible_timeout(&chip->batt_id_avail,
- msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS));
- /* If we were interrupted wait again one more time. */
- if (ret == -ERESTARTSYS && !tried_again) {
- tried_again = true;
- pr_debug("interrupted, waiting again\n");
- goto wait;
- } else if (ret <= 0) {
- rc = -ETIMEDOUT;
- pr_err("profile loading timed out rc=%d\n", rc);
- goto no_profile;
- }
-
- batt_node = of_find_node_by_name(node, "qcom,battery-data");
- if (!batt_node) {
- pr_warn("No available batterydata, using OTP defaults\n");
- rc = 0;
- goto no_profile;
- }
-
- batt_id = get_sram_prop_now(chip, FG_DATA_BATT_ID);
- batt_id /= 1000;
- if (fg_debug_mask & FG_STATUS)
- pr_info("battery id = %dKOhms\n", batt_id);
-
- profile_node = of_batterydata_get_best_profile(batt_node, batt_id,
- fg_batt_type);
- if (IS_ERR_OR_NULL(profile_node)) {
- rc = PTR_ERR(profile_node);
- pr_err("couldn't find profile handle %d\n", rc);
- goto no_profile;
- }
-
- /* read rslow compensation values if they're available */
- rc = of_property_read_u32(profile_node, "qcom,chg-rs-to-rslow",
- &chip->rslow_comp.chg_rs_to_rslow);
- if (rc) {
- chip->rslow_comp.chg_rs_to_rslow = -EINVAL;
- if (rc != -EINVAL)
- pr_err("Could not read rs to rslow: %d\n", rc);
- }
- rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-c1",
- &chip->rslow_comp.chg_rslow_comp_c1);
- if (rc) {
- chip->rslow_comp.chg_rslow_comp_c1 = -EINVAL;
- if (rc != -EINVAL)
- pr_err("Could not read rslow comp c1: %d\n", rc);
- }
- rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-c2",
- &chip->rslow_comp.chg_rslow_comp_c2);
- if (rc) {
- chip->rslow_comp.chg_rslow_comp_c2 = -EINVAL;
- if (rc != -EINVAL)
- pr_err("Could not read rslow comp c2: %d\n", rc);
- }
- rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-thr",
- &chip->rslow_comp.chg_rslow_comp_thr);
- if (rc) {
- chip->rslow_comp.chg_rslow_comp_thr = -EINVAL;
- if (rc != -EINVAL)
- pr_err("Could not read rslow comp thr: %d\n", rc);
- }
-
- rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
- &chip->batt_max_voltage_uv);
-
- if (rc)
- pr_warn("couldn't find battery max voltage\n");
-
- /*
- * Only configure from profile if fg-cc-cv-threshold-mv is not
- * defined in the charger device node.
- */
- if (!of_find_property(chip->pdev->dev.of_node,
- "qcom,fg-cc-cv-threshold-mv", NULL)) {
- of_property_read_u32(profile_node,
- "qcom,fg-cc-cv-threshold-mv",
- &chip->cc_cv_threshold_mv);
- }
-
- data = of_get_property(profile_node, "qcom,fg-profile-data", &len);
- if (!data) {
- pr_err("no battery profile loaded\n");
- rc = 0;
- goto no_profile;
- }
-
- if (len != FG_PROFILE_LEN) {
- pr_err("battery profile incorrect size: %d\n", len);
- rc = -EINVAL;
- goto no_profile;
- }
-
- rc = of_property_read_string(profile_node, "qcom,battery-type",
- &batt_type_str);
- if (rc) {
- pr_err("Could not find battery data type: %d\n", rc);
- rc = 0;
- goto no_profile;
- }
-
- if (!chip->batt_profile)
- chip->batt_profile = devm_kzalloc(chip->dev,
- sizeof(char) * len, GFP_KERNEL);
-
- if (!chip->batt_profile) {
- pr_err("out of memory\n");
- rc = -ENOMEM;
- goto no_profile;
- }
-
- rc = fg_mem_read(chip, &reg, PROFILE_INTEGRITY_REG, 1, 0, 1);
- if (rc) {
- pr_err("failed to read profile integrity rc=%d\n", rc);
- goto no_profile;
- }
-
- rc = fg_mem_read(chip, chip->batt_profile, BATT_PROFILE_OFFSET,
- len, 0, 1);
- if (rc) {
- pr_err("failed to read profile rc=%d\n", rc);
- goto no_profile;
- }
-
- /* Check whether the charger is ready */
- if (!is_charger_available(chip))
- goto reschedule;
-
- /* Disable charging for a FG cycle before calculating vbat_in_range */
- if (!chip->charging_disabled) {
- rc = set_prop_enable_charging(chip, false);
- if (rc)
- pr_err("Failed to disable charging, rc=%d\n", rc);
-
- goto reschedule;
- }
-
- vbat_in_range = get_vbat_est_diff(chip)
- < settings[FG_MEM_VBAT_EST_DIFF].value * 1000;
- profiles_same = memcmp(chip->batt_profile, data,
- PROFILE_COMPARE_LEN) == 0;
- if (reg & PROFILE_INTEGRITY_BIT) {
- fg_cap_learning_load_data(chip);
- if (vbat_in_range && !fg_is_batt_empty(chip) && profiles_same) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("Battery profiles same, using default\n");
- if (fg_est_dump)
- schedule_work(&chip->dump_sram);
- goto done;
- }
- } else {
- pr_info("Battery profile not same, clearing data\n");
- clear_cycle_counter(chip);
- chip->learning_data.learned_cc_uah = 0;
- }
-
- if (fg_est_dump)
- dump_sram(&chip->dump_sram);
-
- if ((fg_debug_mask & FG_STATUS) && !vbat_in_range)
- pr_info("Vbat out of range: v_current_pred: %d, v:%d\n",
- fg_data[FG_DATA_CPRED_VOLTAGE].value,
- fg_data[FG_DATA_VOLTAGE].value);
-
- if ((fg_debug_mask & FG_STATUS) && fg_is_batt_empty(chip))
- pr_info("battery empty\n");
-
- if ((fg_debug_mask & FG_STATUS) && !profiles_same)
- pr_info("profiles differ\n");
-
- if (fg_debug_mask & FG_STATUS) {
- pr_info("Using new profile\n");
- print_hex_dump(KERN_INFO, "FG: loaded profile: ",
- DUMP_PREFIX_NONE, 16, 1,
- chip->batt_profile, len, false);
- }
-
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
-
- memcpy(chip->batt_profile, data, len);
-
- chip->batt_profile_len = len;
-
- if (fg_debug_mask & FG_STATUS)
- print_hex_dump(KERN_INFO, "FG: new profile: ",
- DUMP_PREFIX_NONE, 16, 1, chip->batt_profile,
- chip->batt_profile_len, false);
-
- rc = fg_do_restart(chip, true);
- if (rc) {
- pr_err("restart failed: %d\n", rc);
- goto no_profile;
- }
-
- /*
- * Only configure from profile if thermal-coefficients is not
- * defined in the FG device node.
- */
- if (!of_find_property(chip->pdev->dev.of_node,
- "qcom,thermal-coefficients", NULL)) {
- data = of_get_property(profile_node,
- "qcom,thermal-coefficients", &len);
- if (data && len == THERMAL_COEFF_N_BYTES) {
- memcpy(chip->thermal_coefficients, data, len);
- rc = fg_mem_write(chip, chip->thermal_coefficients,
- THERMAL_COEFF_ADDR, THERMAL_COEFF_N_BYTES,
- THERMAL_COEFF_OFFSET, 0);
- if (rc)
- pr_err("spmi write failed addr:%03x, ret:%d\n",
- THERMAL_COEFF_ADDR, rc);
- else if (fg_debug_mask & FG_STATUS)
- pr_info("Battery thermal coefficients changed\n");
- }
- }
-
-done:
- if (chip->charging_disabled) {
- rc = set_prop_enable_charging(chip, true);
- if (rc)
- pr_err("Failed to enable charging, rc=%d\n", rc);
- else
- chip->charging_disabled = false;
- }
-
- if (fg_batt_type)
- chip->batt_type = fg_batt_type;
- else
- chip->batt_type = batt_type_str;
- chip->first_profile_loaded = true;
- chip->profile_loaded = true;
- chip->battery_missing = is_battery_missing(chip);
- update_chg_iterm(chip);
- update_cc_cv_setpoint(chip);
- rc = populate_system_data(chip);
- if (rc) {
- pr_err("failed to read ocv properties=%d\n", rc);
- return rc;
- }
- estimate_battery_age(chip, &chip->actual_cap_uah);
- schedule_work(&chip->status_change_work);
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
- fg_relax(&chip->profile_wakeup_source);
- pr_info("Battery SOC: %d, V: %duV\n", get_prop_capacity(chip),
- fg_data[FG_DATA_VOLTAGE].value);
- return rc;
-no_profile:
- if (chip->charging_disabled) {
- rc = set_prop_enable_charging(chip, true);
- if (rc)
- pr_err("Failed to enable charging, rc=%d\n", rc);
- else
- chip->charging_disabled = false;
- }
-
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
- fg_relax(&chip->profile_wakeup_source);
- return rc;
-reschedule:
- schedule_delayed_work(
- &chip->batt_profile_init,
- msecs_to_jiffies(BATTERY_PSY_WAIT_MS));
- cancel_delayed_work(&chip->update_sram_data);
- schedule_delayed_work(
- &chip->update_sram_data,
- msecs_to_jiffies(0));
- fg_relax(&chip->profile_wakeup_source);
- return 0;
-}
-
-static void check_empty_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- check_empty_work.work);
-
- if (fg_is_batt_empty(chip)) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("EMPTY SOC high\n");
- chip->soc_empty = true;
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
- }
- fg_relax(&chip->empty_check_wakeup_source);
-}
-
-static void batt_profile_init(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- batt_profile_init.work);
-
- if (fg_batt_profile_init(chip))
- pr_err("failed to initialize profile\n");
-}
-
-static void sysfs_restart_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- sysfs_restart_work);
- int rc;
-
- rc = fg_do_restart(chip, false);
- if (rc)
- pr_err("fg restart failed: %d\n", rc);
- mutex_lock(&chip->sysfs_restart_lock);
- fg_restart = 0;
- mutex_unlock(&chip->sysfs_restart_lock);
-}
-
-#define SRAM_MONOTONIC_SOC_REG 0x574
-#define SRAM_MONOTONIC_SOC_OFFSET 2
-#define SRAM_RELEASE_TIMEOUT_MS 500
-static void charge_full_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- charge_full_work);
- int rc;
- u8 buffer[3];
- int bsoc;
- int resume_soc_raw = FULL_SOC_RAW - settings[FG_MEM_RESUME_SOC].value;
- bool disable = false;
- u8 reg;
-
- if (chip->status != POWER_SUPPLY_STATUS_FULL) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("battery not full: %d\n", chip->status);
- disable = true;
- }
-
- fg_mem_lock(chip);
- rc = fg_mem_read(chip, buffer, BATTERY_SOC_REG, 3, 1, 0);
- if (rc) {
- pr_err("Unable to read battery soc: %d\n", rc);
- goto out;
- }
- if (buffer[2] <= resume_soc_raw) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("bsoc = 0x%02x <= resume = 0x%02x\n",
- buffer[2], resume_soc_raw);
- disable = true;
- }
- if (!disable)
- goto out;
-
- rc = fg_mem_write(chip, buffer, SOC_FULL_REG, 3,
- SOC_FULL_OFFSET, 0);
- if (rc) {
- pr_err("failed to write SOC_FULL rc=%d\n", rc);
- goto out;
- }
- /* force a full soc value into the monotonic in order to display 100 */
- buffer[0] = 0xFF;
- buffer[1] = 0xFF;
- rc = fg_mem_write(chip, buffer, SRAM_MONOTONIC_SOC_REG, 2,
- SRAM_MONOTONIC_SOC_OFFSET, 0);
- if (rc) {
- pr_err("failed to write SOC_FULL rc=%d\n", rc);
- goto out;
- }
- if (fg_debug_mask & FG_STATUS) {
- bsoc = buffer[0] | buffer[1] << 8 | buffer[2] << 16;
- pr_info("wrote %06x into soc full\n", bsoc);
- }
- fg_mem_release(chip);
- /*
- * wait one cycle to make sure the soc is updated before clearing
- * the soc mask bit
- */
- fg_mem_lock(chip);
- fg_mem_read(chip, &reg, PROFILE_INTEGRITY_REG, 1, 0, 0);
-out:
- fg_mem_release(chip);
- if (disable)
- chip->charge_full = false;
-}
-
-static void update_bcl_thresholds(struct fg_chip *chip)
-{
- u8 data[4];
- u8 mh_offset = 0, lm_offset = 0;
- u16 address = 0;
- int ret = 0;
-
- address = settings[FG_MEM_BCL_MH_THRESHOLD].address;
- mh_offset = settings[FG_MEM_BCL_MH_THRESHOLD].offset;
- lm_offset = settings[FG_MEM_BCL_LM_THRESHOLD].offset;
- ret = fg_mem_read(chip, data, address, 4, 0, 1);
- if (ret)
- pr_err("Error reading BCL LM & MH threshold rc:%d\n", ret);
- else
- pr_debug("Old BCL LM threshold:%x MH threshold:%x\n",
- data[lm_offset], data[mh_offset]);
- BCL_MA_TO_ADC(settings[FG_MEM_BCL_MH_THRESHOLD].value, data[mh_offset]);
- BCL_MA_TO_ADC(settings[FG_MEM_BCL_LM_THRESHOLD].value, data[lm_offset]);
-
- ret = fg_mem_write(chip, data, address, 4, 0, 0);
- if (ret)
- pr_err("spmi write failed. addr:%03x, ret:%d\n",
- address, ret);
- else
- pr_debug("New BCL LM threshold:%x MH threshold:%x\n",
- data[lm_offset], data[mh_offset]);
-}
-
-static int disable_bcl_lpm(struct fg_chip *chip)
-{
- u8 data[4];
- u8 lm_offset = 0;
- u16 address = 0;
- int rc = 0;
-
- address = settings[FG_MEM_BCL_LM_THRESHOLD].address;
- lm_offset = settings[FG_MEM_BCL_LM_THRESHOLD].offset;
- rc = fg_mem_read(chip, data, address, 4, 0, 1);
- if (rc) {
- pr_err("Error reading BCL LM & MH threshold rc:%d\n", rc);
- return rc;
- }
- pr_debug("Old BCL LM threshold:%x\n", data[lm_offset]);
-
- /* Put BCL always above LPM */
- BCL_MA_TO_ADC(0, data[lm_offset]);
-
- rc = fg_mem_write(chip, data, address, 4, 0, 0);
- if (rc)
- pr_err("spmi write failed. addr:%03x, rc:%d\n",
- address, rc);
- else
- pr_debug("New BCL LM threshold:%x\n", data[lm_offset]);
-
- return rc;
-}
-
-static void bcl_hi_power_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- bcl_hi_power_work);
- int rc;
-
- if (chip->bcl_lpm_disabled) {
- rc = disable_bcl_lpm(chip);
- if (rc)
- pr_err("failed to disable bcl low mode %d\n",
- rc);
- } else {
- update_bcl_thresholds(chip);
- }
-}
-
-#define VOLT_UV_TO_VOLTCMP8(volt_uv) \
- ((volt_uv - 2500000) / 9766)
-static int update_irq_volt_empty(struct fg_chip *chip)
-{
- u8 data;
- int volt_mv = settings[FG_MEM_IRQ_VOLT_EMPTY].value;
-
- data = (u8)VOLT_UV_TO_VOLTCMP8(volt_mv * 1000);
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("voltage = %d, converted_raw = %04x\n", volt_mv, data);
- return fg_mem_write(chip, &data,
- settings[FG_MEM_IRQ_VOLT_EMPTY].address, 1,
- settings[FG_MEM_IRQ_VOLT_EMPTY].offset, 0);
-}
-
-static int update_cutoff_voltage(struct fg_chip *chip)
-{
- u8 data[2];
- u16 converted_voltage_raw;
- s64 voltage_mv = settings[FG_MEM_CUTOFF_VOLTAGE].value;
-
- converted_voltage_raw = (s16)MICROUNITS_TO_ADC_RAW(voltage_mv * 1000);
- data[0] = cpu_to_le16(converted_voltage_raw) & 0xFF;
- data[1] = cpu_to_le16(converted_voltage_raw) >> 8;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("voltage = %lld, converted_raw = %04x, data = %02x %02x\n",
- voltage_mv, converted_voltage_raw, data[0], data[1]);
- return fg_mem_write(chip, data, settings[FG_MEM_CUTOFF_VOLTAGE].address,
- 2, settings[FG_MEM_CUTOFF_VOLTAGE].offset, 0);
-}
-
-static int update_iterm(struct fg_chip *chip)
-{
- u8 data[2];
- u16 converted_current_raw;
- s64 current_ma = -settings[FG_MEM_TERM_CURRENT].value;
-
- converted_current_raw = (s16)MICROUNITS_TO_ADC_RAW(current_ma * 1000);
- data[0] = cpu_to_le16(converted_current_raw) & 0xFF;
- data[1] = cpu_to_le16(converted_current_raw) >> 8;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("current = %lld, converted_raw = %04x, data = %02x %02x\n",
- current_ma, converted_current_raw, data[0], data[1]);
- return fg_mem_write(chip, data, settings[FG_MEM_TERM_CURRENT].address,
- 2, settings[FG_MEM_TERM_CURRENT].offset, 0);
-}
-
-#define OF_READ_SETTING(type, qpnp_dt_property, retval, optional) \
-do { \
- if (retval) \
- break; \
- \
- retval = of_property_read_u32(chip->pdev->dev.of_node, \
- "qcom," qpnp_dt_property, \
- &settings[type].value); \
- \
- if ((retval == -EINVAL) && optional) \
- retval = 0; \
- else if (retval) \
- pr_err("Error reading " #qpnp_dt_property \
- " property rc = %d\n", rc); \
-} while (0)
-
-#define OF_READ_PROPERTY(store, qpnp_dt_property, retval, default_val) \
-do { \
- if (retval) \
- break; \
- \
- retval = of_property_read_u32(chip->pdev->dev.of_node, \
- "qcom," qpnp_dt_property, \
- &store); \
- \
- if (retval == -EINVAL) { \
- retval = 0; \
- store = default_val; \
- } else if (retval) { \
- pr_err("Error reading " #qpnp_dt_property \
- " property rc = %d\n", rc); \
- } \
-} while (0)
-
-#define DEFAULT_EVALUATION_CURRENT_MA 1000
-static int fg_of_init(struct fg_chip *chip)
-{
- int rc = 0, sense_type, len = 0;
- const char *data;
- struct device_node *node = chip->pdev->dev.of_node;
- u32 temp[2] = {0};
-
- OF_READ_SETTING(FG_MEM_SOFT_HOT, "warm-bat-decidegc", rc, 1);
- OF_READ_SETTING(FG_MEM_SOFT_COLD, "cool-bat-decidegc", rc, 1);
- OF_READ_SETTING(FG_MEM_HARD_HOT, "hot-bat-decidegc", rc, 1);
- OF_READ_SETTING(FG_MEM_HARD_COLD, "cold-bat-decidegc", rc, 1);
-
- if (of_find_property(node, "qcom,cold-hot-jeita-hysteresis", NULL)) {
- int hard_hot = 0, soft_hot = 0, hard_cold = 0, soft_cold = 0;
-
- rc = of_property_read_u32_array(node,
- "qcom,cold-hot-jeita-hysteresis", temp, 2);
- if (rc) {
- pr_err("Error reading cold-hot-jeita-hysteresis rc=%d\n",
- rc);
- return rc;
- }
-
- chip->jeita_hysteresis_support = true;
- chip->cold_hysteresis = temp[0];
- chip->hot_hysteresis = temp[1];
- hard_hot = settings[FG_MEM_HARD_HOT].value;
- soft_hot = settings[FG_MEM_SOFT_HOT].value;
- hard_cold = settings[FG_MEM_HARD_COLD].value;
- soft_cold = settings[FG_MEM_SOFT_COLD].value;
- if (((hard_hot - chip->hot_hysteresis) < soft_hot) ||
- ((hard_cold + chip->cold_hysteresis) > soft_cold)) {
- chip->jeita_hysteresis_support = false;
- pr_err("invalid hysteresis: hot_hysterresis = %d cold_hysteresis = %d\n",
- chip->hot_hysteresis, chip->cold_hysteresis);
- } else {
- pr_debug("cold_hysteresis = %d, hot_hysteresis = %d\n",
- chip->cold_hysteresis, chip->hot_hysteresis);
- }
- }
-
- OF_READ_SETTING(FG_MEM_BCL_LM_THRESHOLD, "bcl-lm-threshold-ma",
- rc, 1);
- OF_READ_SETTING(FG_MEM_BCL_MH_THRESHOLD, "bcl-mh-threshold-ma",
- rc, 1);
- OF_READ_SETTING(FG_MEM_TERM_CURRENT, "fg-iterm-ma", rc, 1);
- OF_READ_SETTING(FG_MEM_CHG_TERM_CURRENT, "fg-chg-iterm-ma", rc, 1);
- OF_READ_SETTING(FG_MEM_CUTOFF_VOLTAGE, "fg-cutoff-voltage-mv", rc, 1);
- data = of_get_property(chip->pdev->dev.of_node,
- "qcom,thermal-coefficients", &len);
- if (data && len == THERMAL_COEFF_N_BYTES) {
- memcpy(chip->thermal_coefficients, data, len);
- chip->use_thermal_coefficients = true;
- }
- OF_READ_SETTING(FG_MEM_RESUME_SOC, "resume-soc", rc, 1);
- settings[FG_MEM_RESUME_SOC].value =
- DIV_ROUND_CLOSEST(settings[FG_MEM_RESUME_SOC].value
- * FULL_SOC_RAW, FULL_CAPACITY);
- OF_READ_SETTING(FG_MEM_RESUME_SOC, "resume-soc-raw", rc, 1);
- OF_READ_SETTING(FG_MEM_IRQ_VOLT_EMPTY, "irq-volt-empty-mv", rc, 1);
- OF_READ_SETTING(FG_MEM_VBAT_EST_DIFF, "vbat-estimate-diff-mv", rc, 1);
- OF_READ_SETTING(FG_MEM_DELTA_SOC, "fg-delta-soc", rc, 1);
- OF_READ_SETTING(FG_MEM_BATT_LOW, "fg-vbatt-low-threshold", rc, 1);
- OF_READ_SETTING(FG_MEM_THERM_DELAY, "fg-therm-delay-us", rc, 1);
- OF_READ_PROPERTY(chip->learning_data.max_increment,
- "cl-max-increment-deciperc", rc, 5);
- OF_READ_PROPERTY(chip->learning_data.max_decrement,
- "cl-max-decrement-deciperc", rc, 100);
- OF_READ_PROPERTY(chip->learning_data.max_temp,
- "cl-max-temp-decidegc", rc, 450);
- OF_READ_PROPERTY(chip->learning_data.min_temp,
- "cl-min-temp-decidegc", rc, 150);
- OF_READ_PROPERTY(chip->learning_data.max_start_soc,
- "cl-max-start-capacity", rc, 15);
- OF_READ_PROPERTY(chip->learning_data.vbat_est_thr_uv,
- "cl-vbat-est-thr-uv", rc, 40000);
- OF_READ_PROPERTY(chip->evaluation_current,
- "aging-eval-current-ma", rc,
- DEFAULT_EVALUATION_CURRENT_MA);
- OF_READ_PROPERTY(chip->cc_cv_threshold_mv,
- "fg-cc-cv-threshold-mv", rc, 0);
- if (of_property_read_bool(chip->pdev->dev.of_node,
- "qcom,capacity-learning-on"))
- chip->batt_aging_mode = FG_AGING_CC;
- else if (of_property_read_bool(chip->pdev->dev.of_node,
- "qcom,capacity-estimation-on"))
- chip->batt_aging_mode = FG_AGING_ESR;
- else
- chip->batt_aging_mode = FG_AGING_NONE;
- if (chip->batt_aging_mode == FG_AGING_CC) {
- chip->learning_data.feedback_on
- = of_property_read_bool(chip->pdev->dev.of_node,
- "qcom,capacity-learning-feedback");
- }
- if (fg_debug_mask & FG_AGING)
- pr_info("battery aging mode: %d\n", chip->batt_aging_mode);
-
- /* Get the use-otp-profile property */
- chip->use_otp_profile = of_property_read_bool(chip->pdev->dev.of_node,
- "qcom,use-otp-profile");
- chip->hold_soc_while_full
- = of_property_read_bool(chip->pdev->dev.of_node,
- "qcom,hold-soc-while-full");
-
- sense_type = of_property_read_bool(chip->pdev->dev.of_node,
- "qcom,ext-sense-type");
- if (rc == 0) {
- if (fg_sense_type < 0)
- fg_sense_type = sense_type;
-
- if (fg_debug_mask & FG_STATUS) {
- if (fg_sense_type == INTERNAL_CURRENT_SENSE)
- pr_info("Using internal sense\n");
- else if (fg_sense_type == EXTERNAL_CURRENT_SENSE)
- pr_info("Using external sense\n");
- else
- pr_info("Using default sense\n");
- }
- } else {
- rc = 0;
- }
-
- chip->bad_batt_detection_en = of_property_read_bool(node,
- "qcom,bad-battery-detection-enable");
-
- chip->sw_rbias_ctrl = of_property_read_bool(node,
- "qcom,sw-rbias-control");
-
- chip->cyc_ctr.en = of_property_read_bool(node,
- "qcom,cycle-counter-en");
- if (chip->cyc_ctr.en)
- chip->cyc_ctr.id = 1;
-
- chip->esr_pulse_tune_en = of_property_read_bool(node,
- "qcom,esr-pulse-tuning-en");
-
- return rc;
-}
-
-static int fg_init_irqs(struct fg_chip *chip)
-{
- int rc = 0;
- unsigned int base;
- struct device_node *child;
- u8 subtype;
- struct platform_device *pdev = chip->pdev;
-
- if (of_get_available_child_count(pdev->dev.of_node) == 0) {
- pr_err("no child nodes\n");
- return -ENXIO;
- }
-
- for_each_available_child_of_node(pdev->dev.of_node, child) {
- rc = of_property_read_u32(child, "reg", &base);
- if (rc < 0) {
- dev_err(&pdev->dev,
- "Couldn't find reg in node = %s rc = %d\n",
- child->full_name, rc);
- return rc;
- }
-
- if ((base == chip->vbat_adc_addr) ||
- (base == chip->ibat_adc_addr) ||
- (base == chip->tp_rev_addr))
- continue;
-
- rc = fg_read(chip, &subtype,
- base + REG_OFFSET_PERP_SUBTYPE, 1);
- if (rc) {
- pr_err("Peripheral subtype read failed rc=%d\n", rc);
- return rc;
- }
-
- switch (subtype) {
- case FG_SOC:
- chip->soc_irq[FULL_SOC].irq = of_irq_get_byname(child,
- "full-soc");
- if (chip->soc_irq[FULL_SOC].irq < 0) {
- pr_err("Unable to get full-soc irq\n");
- return rc;
- }
- chip->soc_irq[EMPTY_SOC].irq = of_irq_get_byname(child,
- "empty-soc");
- if (chip->soc_irq[EMPTY_SOC].irq < 0) {
- pr_err("Unable to get low-soc irq\n");
- return rc;
- }
- chip->soc_irq[DELTA_SOC].irq = of_irq_get_byname(child,
- "delta-soc");
- if (chip->soc_irq[DELTA_SOC].irq < 0) {
- pr_err("Unable to get delta-soc irq\n");
- return rc;
- }
- chip->soc_irq[FIRST_EST_DONE].irq
- = of_irq_get_byname(child, "first-est-done");
- if (chip->soc_irq[FIRST_EST_DONE].irq < 0) {
- pr_err("Unable to get first-est-done irq\n");
- return rc;
- }
-
- rc = devm_request_irq(chip->dev,
- chip->soc_irq[FULL_SOC].irq,
- fg_soc_irq_handler, IRQF_TRIGGER_RISING,
- "full-soc", chip);
- if (rc < 0) {
- pr_err("Can't request %d full-soc: %d\n",
- chip->soc_irq[FULL_SOC].irq, rc);
- return rc;
- }
- rc = devm_request_irq(chip->dev,
- chip->soc_irq[EMPTY_SOC].irq,
- fg_empty_soc_irq_handler,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "empty-soc", chip);
- if (rc < 0) {
- pr_err("Can't request %d empty-soc: %d\n",
- chip->soc_irq[EMPTY_SOC].irq, rc);
- return rc;
- }
- rc = devm_request_irq(chip->dev,
- chip->soc_irq[DELTA_SOC].irq,
- fg_soc_irq_handler, IRQF_TRIGGER_RISING,
- "delta-soc", chip);
- if (rc < 0) {
- pr_err("Can't request %d delta-soc: %d\n",
- chip->soc_irq[DELTA_SOC].irq, rc);
- return rc;
- }
- rc = devm_request_irq(chip->dev,
- chip->soc_irq[FIRST_EST_DONE].irq,
- fg_first_soc_irq_handler, IRQF_TRIGGER_RISING,
- "first-est-done", chip);
- if (rc < 0) {
- pr_err("Can't request %d delta-soc: %d\n",
- chip->soc_irq[FIRST_EST_DONE].irq, rc);
- return rc;
- }
-
- enable_irq_wake(chip->soc_irq[DELTA_SOC].irq);
- enable_irq_wake(chip->soc_irq[FULL_SOC].irq);
- enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq);
- break;
- case FG_MEMIF:
- chip->mem_irq[FG_MEM_AVAIL].irq
- = of_irq_get_byname(child, "mem-avail");
- if (chip->mem_irq[FG_MEM_AVAIL].irq < 0) {
- pr_err("Unable to get mem-avail irq\n");
- return rc;
- }
- rc = devm_request_irq(chip->dev,
- chip->mem_irq[FG_MEM_AVAIL].irq,
- fg_mem_avail_irq_handler,
- IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING,
- "mem-avail", chip);
- if (rc < 0) {
- pr_err("Can't request %d mem-avail: %d\n",
- chip->mem_irq[FG_MEM_AVAIL].irq, rc);
- return rc;
- }
- break;
- case FG_BATT:
- chip->batt_irq[BATT_MISSING].irq
- = of_irq_get_byname(child, "batt-missing");
- if (chip->batt_irq[BATT_MISSING].irq < 0) {
- pr_err("Unable to get batt-missing irq\n");
- rc = -EINVAL;
- return rc;
- }
- rc = devm_request_threaded_irq(chip->dev,
- chip->batt_irq[BATT_MISSING].irq,
- NULL,
- fg_batt_missing_irq_handler,
- IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING |
- IRQF_ONESHOT,
- "batt-missing", chip);
- if (rc < 0) {
- pr_err("Can't request %d batt-missing: %d\n",
- chip->batt_irq[BATT_MISSING].irq, rc);
- return rc;
- }
- chip->batt_irq[VBATT_LOW].irq
- = of_irq_get_byname(child, "vbatt-low");
- if (chip->batt_irq[VBATT_LOW].irq < 0) {
- pr_err("Unable to get vbatt-low irq\n");
- rc = -EINVAL;
- return rc;
- }
- rc = devm_request_irq(chip->dev,
- chip->batt_irq[VBATT_LOW].irq,
- fg_vbatt_low_handler,
- IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING,
- "vbatt-low", chip);
- if (rc < 0) {
- pr_err("Can't request %d vbatt-low: %d\n",
- chip->batt_irq[VBATT_LOW].irq, rc);
- return rc;
- }
- disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
- chip->vbat_low_irq_enabled = false;
- break;
- case FG_ADC:
- break;
- default:
- pr_err("subtype %d\n", subtype);
- return -EINVAL;
- }
- }
-
- return rc;
-}
-
-static void fg_cleanup(struct fg_chip *chip)
-{
- cancel_delayed_work_sync(&chip->update_sram_data);
- cancel_delayed_work_sync(&chip->update_temp_work);
- cancel_delayed_work_sync(&chip->update_jeita_setting);
- cancel_delayed_work_sync(&chip->check_empty_work);
- cancel_delayed_work_sync(&chip->batt_profile_init);
- alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
- cancel_work_sync(&chip->rslow_comp_work);
- cancel_work_sync(&chip->set_resume_soc_work);
- cancel_work_sync(&chip->fg_cap_learning_work);
- cancel_work_sync(&chip->dump_sram);
- cancel_work_sync(&chip->status_change_work);
- cancel_work_sync(&chip->cycle_count_work);
- cancel_work_sync(&chip->update_esr_work);
- cancel_work_sync(&chip->sysfs_restart_work);
- cancel_work_sync(&chip->gain_comp_work);
- cancel_work_sync(&chip->init_work);
- cancel_work_sync(&chip->charge_full_work);
- cancel_work_sync(&chip->esr_extract_config_work);
- mutex_destroy(&chip->rslow_comp.lock);
- mutex_destroy(&chip->rw_lock);
- mutex_destroy(&chip->cyc_ctr.lock);
- mutex_destroy(&chip->learning_data.learning_lock);
- mutex_destroy(&chip->sysfs_restart_lock);
- wakeup_source_trash(&chip->resume_soc_wakeup_source.source);
- wakeup_source_trash(&chip->empty_check_wakeup_source.source);
- wakeup_source_trash(&chip->memif_wakeup_source.source);
- wakeup_source_trash(&chip->profile_wakeup_source.source);
- wakeup_source_trash(&chip->update_temp_wakeup_source.source);
- wakeup_source_trash(&chip->update_sram_wakeup_source.source);
- wakeup_source_trash(&chip->gain_comp_wakeup_source.source);
- wakeup_source_trash(&chip->capacity_learning_wakeup_source.source);
- wakeup_source_trash(&chip->esr_extract_wakeup_source.source);
-}
-
-static int fg_remove(struct platform_device *pdev)
-{
- struct fg_chip *chip = dev_get_drvdata(&pdev->dev);
-
- fg_cleanup(chip);
- dev_set_drvdata(&pdev->dev, NULL);
- return 0;
-}
-
-static int fg_memif_data_open(struct inode *inode, struct file *file)
-{
- struct fg_log_buffer *log;
- struct fg_trans *trans;
- u8 *data_buf;
-
- size_t logbufsize = SZ_4K;
- size_t databufsize = SZ_4K;
-
- if (!dbgfs_data.chip) {
- pr_err("Not initialized data\n");
- return -EINVAL;
- }
-
- /* Per file "transaction" data */
- trans = kzalloc(sizeof(*trans), GFP_KERNEL);
- if (!trans) {
- pr_err("Unable to allocate memory for transaction data\n");
- return -ENOMEM;
- }
-
- /* Allocate log buffer */
- log = kzalloc(logbufsize, GFP_KERNEL);
-
- if (!log) {
- kfree(trans);
- pr_err("Unable to allocate memory for log buffer\n");
- return -ENOMEM;
- }
-
- log->rpos = 0;
- log->wpos = 0;
- log->len = logbufsize - sizeof(*log);
-
- /* Allocate data buffer */
- data_buf = kzalloc(databufsize, GFP_KERNEL);
-
- if (!data_buf) {
- kfree(trans);
- kfree(log);
- pr_err("Unable to allocate memory for data buffer\n");
- return -ENOMEM;
- }
-
- trans->log = log;
- trans->data = data_buf;
- trans->cnt = dbgfs_data.cnt;
- trans->addr = dbgfs_data.addr;
- trans->chip = dbgfs_data.chip;
- trans->offset = trans->addr;
- mutex_init(&trans->memif_dfs_lock);
-
- file->private_data = trans;
- return 0;
-}
-
-static int fg_memif_dfs_close(struct inode *inode, struct file *file)
-{
- struct fg_trans *trans = file->private_data;
-
- if (trans && trans->log && trans->data) {
- file->private_data = NULL;
- mutex_destroy(&trans->memif_dfs_lock);
- kfree(trans->log);
- kfree(trans->data);
- kfree(trans);
- }
-
- return 0;
-}
-
-/**
- * print_to_log: format a string and place into the log buffer
- * @log: The log buffer to place the result into.
- * @fmt: The format string to use.
- * @...: The arguments for the format string.
- *
- * The return value is the number of characters written to @log buffer
- * not including the trailing '\0'.
- */
-static int print_to_log(struct fg_log_buffer *log, const char *fmt, ...)
-{
- va_list args;
- int cnt;
- char *buf = &log->data[log->wpos];
- size_t size = log->len - log->wpos;
-
- va_start(args, fmt);
- cnt = vscnprintf(buf, size, fmt, args);
- va_end(args);
-
- log->wpos += cnt;
- return cnt;
-}
-
-/**
- * write_next_line_to_log: Writes a single "line" of data into the log buffer
- * @trans: Pointer to SRAM transaction data.
- * @offset: SRAM address offset to start reading from.
- * @pcnt: Pointer to 'cnt' variable. Indicates the number of bytes to read.
- *
- * The 'offset' is a 12-bit SRAM address.
- *
- * On a successful read, the pcnt is decremented by the number of data
- * bytes read from the SRAM. When the cnt reaches 0, all requested bytes have
- * been read.
- */
-static int
-write_next_line_to_log(struct fg_trans *trans, int offset, size_t *pcnt)
-{
- int i, j;
- u8 data[ITEMS_PER_LINE];
- struct fg_log_buffer *log = trans->log;
-
- int cnt = 0;
- int padding = offset % ITEMS_PER_LINE;
- int items_to_read = min(ARRAY_SIZE(data) - padding, *pcnt);
- int items_to_log = min(ITEMS_PER_LINE, padding + items_to_read);
-
- /* Buffer needs enough space for an entire line */
- if ((log->len - log->wpos) < MAX_LINE_LENGTH)
- goto done;
-
- memcpy(data, trans->data + (offset - trans->addr), items_to_read);
-
- *pcnt -= items_to_read;
-
- /* Each line starts with the aligned offset (12-bit address) */
- cnt = print_to_log(log, "%3.3X ", offset & 0xfff);
- if (cnt == 0)
- goto done;
-
- /* If the offset is unaligned, add padding to right justify items */
- for (i = 0; i < padding; ++i) {
- cnt = print_to_log(log, "-- ");
- if (cnt == 0)
- goto done;
- }
-
- /* Log the data items */
- for (j = 0; i < items_to_log; ++i, ++j) {
- cnt = print_to_log(log, "%2.2X ", data[j]);
- if (cnt == 0)
- goto done;
- }
-
- /* If the last character was a space, then replace it with a newline */
- if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
- log->data[log->wpos - 1] = '\n';
-
-done:
- return cnt;
-}
-
-/**
- * get_log_data - reads data from SRAM and saves to the log buffer
- * @trans: Pointer to SRAM transaction data.
- *
- * Returns the number of "items" read or SPMI error code for read failures.
- */
-static int get_log_data(struct fg_trans *trans)
-{
- int cnt, rc;
- int last_cnt;
- int items_read;
- int total_items_read = 0;
- u32 offset = trans->offset;
- size_t item_cnt = trans->cnt;
- struct fg_log_buffer *log = trans->log;
-
- if (item_cnt == 0)
- return 0;
-
- if (item_cnt > SZ_4K) {
- pr_err("Reading too many bytes\n");
- return -EINVAL;
- }
-
- rc = fg_mem_read(trans->chip, trans->data,
- trans->addr, trans->cnt, 0, 0);
- if (rc) {
- pr_err("dump failed: rc = %d\n", rc);
- return rc;
- }
- /* Reset the log buffer 'pointers' */
- log->wpos = log->rpos = 0;
-
- /* Keep reading data until the log is full */
- do {
- last_cnt = item_cnt;
- cnt = write_next_line_to_log(trans, offset, &item_cnt);
- items_read = last_cnt - item_cnt;
- offset += items_read;
- total_items_read += items_read;
- } while (cnt && item_cnt > 0);
-
- /* Adjust the transaction offset and count */
- trans->cnt = item_cnt;
- trans->offset += total_items_read;
-
- return total_items_read;
-}
-
-/**
- * fg_memif_dfs_reg_read: reads value(s) from SRAM and fills user's buffer a
- * byte array (coded as string)
- * @file: file pointer
- * @buf: where to put the result
- * @count: maximum space available in @buf
- * @ppos: starting position
- * @return number of user bytes read, or negative error value
- */
-static ssize_t fg_memif_dfs_reg_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct fg_trans *trans = file->private_data;
- struct fg_log_buffer *log = trans->log;
- size_t ret;
- size_t len;
-
- mutex_lock(&trans->memif_dfs_lock);
- /* Is the the log buffer empty */
- if (log->rpos >= log->wpos) {
- if (get_log_data(trans) <= 0) {
- len = 0;
- goto unlock_mutex;
- }
- }
-
- len = min(count, log->wpos - log->rpos);
-
- ret = copy_to_user(buf, &log->data[log->rpos], len);
- if (ret == len) {
- pr_err("error copy sram register values to user\n");
- len = -EFAULT;
- goto unlock_mutex;
- }
-
- /* 'ret' is the number of bytes not copied */
- len -= ret;
-
- *ppos += len;
- log->rpos += len;
-
-unlock_mutex:
- mutex_unlock(&trans->memif_dfs_lock);
- return len;
-}
-
-/**
- * fg_memif_dfs_reg_write: write user's byte array (coded as string) to SRAM.
- * @file: file pointer
- * @buf: user data to be written.
- * @count: maximum space available in @buf
- * @ppos: starting position
- * @return number of user byte written, or negative error value
- */
-static ssize_t fg_memif_dfs_reg_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- int bytes_read;
- int data;
- int pos = 0;
- int cnt = 0;
- u8 *values;
- size_t ret = 0;
- char *kbuf;
- u32 offset;
-
- struct fg_trans *trans = file->private_data;
-
- mutex_lock(&trans->memif_dfs_lock);
- offset = trans->offset;
-
- /* Make a copy of the user data */
- kbuf = kmalloc(count + 1, GFP_KERNEL);
- if (!kbuf) {
- ret = -ENOMEM;
- goto unlock_mutex;
- }
-
- ret = copy_from_user(kbuf, buf, count);
- if (ret == count) {
- pr_err("failed to copy data from user\n");
- ret = -EFAULT;
- goto free_buf;
- }
-
- count -= ret;
- *ppos += count;
- kbuf[count] = '\0';
-
- /* Override the text buffer with the raw data */
- values = kbuf;
-
- /* Parse the data in the buffer. It should be a string of numbers */
- while ((pos < count) &&
- sscanf(kbuf + pos, "%i%n", &data, &bytes_read) == 1) {
- /*
- * We shouldn't be receiving a string of characters that
- * exceeds a size of 5 to keep this functionally correct.
- * Also, we should make sure that pos never gets overflowed
- * beyond the limit.
- */
- if (bytes_read > 5 || bytes_read > INT_MAX - pos) {
- cnt = 0;
- ret = -EINVAL;
- break;
- }
- pos += bytes_read;
- values[cnt++] = data & 0xff;
- }
-
- if (!cnt)
- goto free_buf;
-
- pr_info("address %x, count %d\n", offset, cnt);
- /* Perform the write(s) */
-
- ret = fg_mem_write(trans->chip, values, offset,
- cnt, 0, 0);
- if (ret) {
- pr_err("SPMI write failed, err = %zu\n", ret);
- } else {
- ret = count;
- trans->offset += cnt > 4 ? 4 : cnt;
- }
-
-free_buf:
- kfree(kbuf);
-unlock_mutex:
- mutex_unlock(&trans->memif_dfs_lock);
- return ret;
-}
-
-static const struct file_operations fg_memif_dfs_reg_fops = {
- .open = fg_memif_data_open,
- .release = fg_memif_dfs_close,
- .read = fg_memif_dfs_reg_read,
- .write = fg_memif_dfs_reg_write,
-};
-
-/**
- * fg_dfs_create_fs: create debugfs file system.
- * @return pointer to root directory or NULL if failed to create fs
- */
-static struct dentry *fg_dfs_create_fs(void)
-{
- struct dentry *root, *file;
-
- pr_debug("Creating FG_MEM debugfs file-system\n");
- root = debugfs_create_dir(DFS_ROOT_NAME, NULL);
- if (IS_ERR_OR_NULL(root)) {
- pr_err("Error creating top level directory err:%ld",
- (long)root);
- if (PTR_ERR(root) == -ENODEV)
- pr_err("debugfs is not enabled in the kernel");
- return NULL;
- }
-
- dbgfs_data.help_msg.size = strlen(dbgfs_data.help_msg.data);
-
- file = debugfs_create_blob("help", S_IRUGO, root, &dbgfs_data.help_msg);
- if (!file) {
- pr_err("error creating help entry\n");
- goto err_remove_fs;
- }
- return root;
-
-err_remove_fs:
- debugfs_remove_recursive(root);
- return NULL;
-}
-
-/**
- * fg_dfs_get_root: return a pointer to FG debugfs root directory.
- * @return a pointer to the existing directory, or if no root
- * directory exists then create one. Directory is created with file that
- * configures SRAM transaction, namely: address, and count.
- * @returns valid pointer on success or NULL
- */
-struct dentry *fg_dfs_get_root(void)
-{
- if (dbgfs_data.root)
- return dbgfs_data.root;
-
- if (mutex_lock_interruptible(&dbgfs_data.lock) < 0)
- return NULL;
- /* critical section */
- if (!dbgfs_data.root) { /* double checking idiom */
- dbgfs_data.root = fg_dfs_create_fs();
- }
- mutex_unlock(&dbgfs_data.lock);
- return dbgfs_data.root;
-}
-
-/*
- * fg_dfs_create: adds new fg_mem if debugfs entry
- * @return zero on success
- */
-int fg_dfs_create(struct fg_chip *chip)
-{
- struct dentry *root;
- struct dentry *file;
-
- root = fg_dfs_get_root();
- if (!root)
- return -ENOENT;
-
- dbgfs_data.chip = chip;
-
- file = debugfs_create_u32("count", DFS_MODE, root, &(dbgfs_data.cnt));
- if (!file) {
- pr_err("error creating 'count' entry\n");
- goto err_remove_fs;
- }
-
- file = debugfs_create_x32("address", DFS_MODE,
- root, &(dbgfs_data.addr));
- if (!file) {
- pr_err("error creating 'address' entry\n");
- goto err_remove_fs;
- }
-
- file = debugfs_create_file("data", DFS_MODE, root, &dbgfs_data,
- &fg_memif_dfs_reg_fops);
- if (!file) {
- pr_err("error creating 'data' entry\n");
- goto err_remove_fs;
- }
-
- return 0;
-
-err_remove_fs:
- debugfs_remove_recursive(root);
- return -ENOMEM;
-}
-
-#define EXTERNAL_SENSE_OFFSET_REG 0x41C
-#define EXT_OFFSET_TRIM_REG 0xF8
-#define SEC_ACCESS_REG 0xD0
-#define SEC_ACCESS_UNLOCK 0xA5
-#define BCL_TRIM_REV_FIXED 12
-static int bcl_trim_workaround(struct fg_chip *chip)
-{
- u8 reg, rc;
-
- if (chip->tp_rev_addr == 0)
- return 0;
-
- rc = fg_read(chip, &reg, chip->tp_rev_addr, 1);
- if (rc) {
- pr_err("Failed to read tp reg, rc = %d\n", rc);
- return rc;
- }
- if (reg >= BCL_TRIM_REV_FIXED) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("workaround not applied, tp_rev = %d\n", reg);
- return 0;
- }
-
- rc = fg_mem_read(chip, &reg, EXTERNAL_SENSE_OFFSET_REG, 1, 2, 0);
- if (rc) {
- pr_err("Failed to read ext sense offset trim, rc = %d\n", rc);
- return rc;
- }
- rc = fg_masked_write(chip, chip->soc_base + SEC_ACCESS_REG,
- SEC_ACCESS_UNLOCK, SEC_ACCESS_UNLOCK, 1);
-
- rc |= fg_masked_write(chip, chip->soc_base + EXT_OFFSET_TRIM_REG,
- 0xFF, reg, 1);
- if (rc) {
- pr_err("Failed to write ext sense offset trim, rc = %d\n", rc);
- return rc;
- }
-
- return 0;
-}
-
-#define FG_ALG_SYSCTL_1 0x4B0
-#define SOC_CNFG 0x450
-#define SOC_DELTA_OFFSET 3
-#define DELTA_SOC_PERCENT 1
-#define I_TERM_QUAL_BIT BIT(1)
-#define PATCH_NEG_CURRENT_BIT BIT(3)
-#define KI_COEFF_PRED_FULL_ADDR 0x408
-#define KI_COEFF_PRED_FULL_4_0_MSB 0x88
-#define KI_COEFF_PRED_FULL_4_0_LSB 0x00
-#define TEMP_FRAC_SHIFT_REG 0x4A4
-#define FG_ADC_CONFIG_REG 0x4B8
-#define FG_BCL_CONFIG_OFFSET 0x3
-#define BCL_FORCED_HPM_IN_CHARGE BIT(2)
-static int fg_common_hw_init(struct fg_chip *chip)
-{
- int rc;
- int resume_soc_raw;
- u8 val;
-
- update_iterm(chip);
- update_cutoff_voltage(chip);
- update_irq_volt_empty(chip);
- update_bcl_thresholds(chip);
-
- resume_soc_raw = settings[FG_MEM_RESUME_SOC].value;
- if (resume_soc_raw > 0) {
- rc = fg_set_resume_soc(chip, resume_soc_raw);
- if (rc) {
- pr_err("Couldn't set resume SOC for FG\n");
- return rc;
- }
- } else {
- pr_info("FG auto recharge threshold not specified in DT\n");
- }
-
- if (fg_sense_type >= 0) {
- rc = set_prop_sense_type(chip, fg_sense_type);
- if (rc) {
- pr_err("failed to config sense type %d rc=%d\n",
- fg_sense_type, rc);
- return rc;
- }
- }
-
- rc = fg_mem_masked_write(chip, settings[FG_MEM_DELTA_SOC].address, 0xFF,
- soc_to_setpoint(settings[FG_MEM_DELTA_SOC].value),
- settings[FG_MEM_DELTA_SOC].offset);
- if (rc) {
- pr_err("failed to write delta soc rc=%d\n", rc);
- return rc;
- }
-
- rc = fg_mem_masked_write(chip, settings[FG_MEM_BATT_LOW].address, 0xFF,
- batt_to_setpoint_8b(settings[FG_MEM_BATT_LOW].value),
- settings[FG_MEM_BATT_LOW].offset);
- if (rc) {
- pr_err("failed to write Vbatt_low rc=%d\n", rc);
- return rc;
- }
-
- rc = fg_mem_masked_write(chip, settings[FG_MEM_THERM_DELAY].address,
- THERM_DELAY_MASK,
- therm_delay_to_setpoint(settings[FG_MEM_THERM_DELAY].value),
- settings[FG_MEM_THERM_DELAY].offset);
- if (rc) {
- pr_err("failed to write therm_delay rc=%d\n", rc);
- return rc;
- }
-
- if (chip->use_thermal_coefficients) {
- fg_mem_write(chip, chip->thermal_coefficients,
- THERMAL_COEFF_ADDR, THERMAL_COEFF_N_BYTES,
- THERMAL_COEFF_OFFSET, 0);
- }
-
- if (!chip->sw_rbias_ctrl) {
- rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
- BATT_TEMP_CNTRL_MASK,
- TEMP_SENSE_ALWAYS_BIT,
- BATT_TEMP_OFFSET);
- if (rc) {
- pr_err("failed to write BATT_TEMP_OFFSET rc=%d\n", rc);
- return rc;
- }
- }
-
- /* Read the cycle counter back from FG SRAM */
- if (chip->cyc_ctr.en)
- restore_cycle_counter(chip);
-
- if (chip->esr_pulse_tune_en) {
- rc = fg_mem_read(chip, &val, SYS_CFG_1_REG, 1, SYS_CFG_1_OFFSET,
- 0);
- if (rc) {
- pr_err("unable to read sys_cfg_1: %d\n", rc);
- return rc;
- }
-
- if (!(val & ENABLE_ESR_PULSE_VAL))
- chip->esr_extract_disabled = true;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("ESR extract is %sabled\n",
- chip->esr_extract_disabled ? "dis" : "en");
-
- rc = fg_mem_read(chip, &val, CBITS_INPUT_FILTER_REG, 1,
- CBITS_RMEAS1_OFFSET, 0);
- if (rc) {
- pr_err("unable to read cbits_input_filter_reg: %d\n",
- rc);
- return rc;
- }
-
- if (val & (IMPTR_FAST_TIME_SHIFT | IMPTR_LONG_TIME_SHIFT))
- chip->imptr_pulse_slow_en = true;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("imptr_pulse_slow is %sabled\n",
- chip->imptr_pulse_slow_en ? "en" : "dis");
-
- rc = fg_mem_read(chip, &val, RSLOW_CFG_REG, 1, RSLOW_CFG_OFFSET,
- 0);
- if (rc) {
- pr_err("unable to read rslow cfg: %d\n", rc);
- return rc;
- }
-
- if (val & RSLOW_CFG_ON_VAL)
- chip->rslow_comp.active = true;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("rslow_comp active is %sabled\n",
- chip->rslow_comp.active ? "en" : "dis");
- }
-
- return 0;
-}
-
-static int fg_8994_hw_init(struct fg_chip *chip)
-{
- int rc = 0;
- u8 data[4];
- u64 esr_value;
-
- rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
- PATCH_NEG_CURRENT_BIT,
- PATCH_NEG_CURRENT_BIT,
- EXTERNAL_SENSE_OFFSET);
- if (rc) {
- pr_err("failed to write patch current bit rc=%d\n", rc);
- return rc;
- }
-
- rc = bcl_trim_workaround(chip);
- if (rc) {
- pr_err("failed to redo bcl trim rc=%d\n", rc);
- return rc;
- }
-
- rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
- BCL_FORCED_HPM_IN_CHARGE,
- BCL_FORCED_HPM_IN_CHARGE,
- FG_BCL_CONFIG_OFFSET);
- if (rc) {
- pr_err("failed to force hpm in charge rc=%d\n", rc);
- return rc;
- }
-
- fg_mem_masked_write(chip, FG_ALG_SYSCTL_1, I_TERM_QUAL_BIT, 0, 0);
-
- data[0] = 0xA2;
- data[1] = 0x12;
-
- rc = fg_mem_write(chip, data, TEMP_FRAC_SHIFT_REG, 2, 2, 0);
- if (rc) {
- pr_err("failed to write temp ocv constants rc=%d\n", rc);
- return rc;
- }
-
- data[0] = KI_COEFF_PRED_FULL_4_0_LSB;
- data[1] = KI_COEFF_PRED_FULL_4_0_MSB;
- fg_mem_write(chip, data, KI_COEFF_PRED_FULL_ADDR, 2, 2, 0);
-
- esr_value = ESR_DEFAULT_VALUE;
- rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8,
- ESR_VALUE_OFFSET, 0);
- if (rc)
- pr_err("failed to write default ESR value rc=%d\n", rc);
- else
- pr_debug("set default value to esr filter\n");
-
- return 0;
-}
-
-#define FG_USBID_CONFIG_OFFSET 0x2
-#define DISABLE_USBID_DETECT_BIT BIT(0)
-static int fg_8996_hw_init(struct fg_chip *chip)
-{
- int rc;
-
- rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
- BCL_FORCED_HPM_IN_CHARGE,
- BCL_FORCED_HPM_IN_CHARGE,
- FG_BCL_CONFIG_OFFSET);
- if (rc) {
- pr_err("failed to force hpm in charge rc=%d\n", rc);
- return rc;
- }
-
- /* enable usbid conversions for PMi8996 V1.0 */
- if (chip->pmic_revision[REVID_DIG_MAJOR] == 1
- && chip->pmic_revision[REVID_ANA_MAJOR] == 0) {
- rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
- DISABLE_USBID_DETECT_BIT,
- 0, FG_USBID_CONFIG_OFFSET);
- if (rc) {
- pr_err("failed to enable usbid conversions: %d\n", rc);
- return rc;
- }
- }
-
- return rc;
-}
-
-static int fg_8950_hw_init(struct fg_chip *chip)
-{
- int rc;
-
- rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
- BCL_FORCED_HPM_IN_CHARGE,
- BCL_FORCED_HPM_IN_CHARGE,
- FG_BCL_CONFIG_OFFSET);
- if (rc)
- pr_err("failed to force hpm in charge rc=%d\n", rc);
-
- return rc;
-}
-
-static int fg_hw_init(struct fg_chip *chip)
-{
- int rc = 0;
-
- rc = fg_common_hw_init(chip);
- if (rc) {
- pr_err("Unable to initialize FG HW rc=%d\n", rc);
- return rc;
- }
-
- /* add PMIC specific hw init */
- switch (chip->pmic_subtype) {
- case PMI8994:
- rc = fg_8994_hw_init(chip);
- chip->wa_flag |= PULSE_REQUEST_WA;
- break;
- case PMI8996:
- rc = fg_8996_hw_init(chip);
- /* Setup workaround flag based on PMIC type */
- if (fg_sense_type == INTERNAL_CURRENT_SENSE)
- chip->wa_flag |= IADC_GAIN_COMP_WA;
- if (chip->pmic_revision[REVID_DIG_MAJOR] > 1)
- chip->wa_flag |= USE_CC_SOC_REG;
-
- break;
- case PMI8950:
- case PMI8937:
- rc = fg_8950_hw_init(chip);
- /* Setup workaround flag based on PMIC type */
- chip->wa_flag |= BCL_HI_POWER_FOR_CHGLED_WA;
- if (fg_sense_type == INTERNAL_CURRENT_SENSE)
- chip->wa_flag |= IADC_GAIN_COMP_WA;
- if (chip->pmic_revision[REVID_DIG_MAJOR] > 1)
- chip->wa_flag |= USE_CC_SOC_REG;
-
- break;
- }
- if (rc)
- pr_err("Unable to initialize PMIC specific FG HW rc=%d\n", rc);
-
- pr_debug("wa_flag=0x%x\n", chip->wa_flag);
-
- return rc;
-}
-
-#define DIG_MINOR 0x0
-#define DIG_MAJOR 0x1
-#define ANA_MINOR 0x2
-#define ANA_MAJOR 0x3
-#define IACS_INTR_SRC_SLCT BIT(3)
-static int fg_setup_memif_offset(struct fg_chip *chip)
-{
- int rc;
-
- rc = fg_read(chip, chip->revision, chip->mem_base + DIG_MINOR, 4);
- if (rc) {
- pr_err("Unable to read FG revision rc=%d\n", rc);
- return rc;
- }
-
- switch (chip->revision[DIG_MAJOR]) {
- case DIG_REV_1:
- case DIG_REV_2:
- chip->offset = offset[0].address;
- break;
- case DIG_REV_3:
- chip->offset = offset[1].address;
- chip->ima_supported = true;
- break;
- default:
- pr_err("Digital Major rev=%d not supported\n",
- chip->revision[DIG_MAJOR]);
- return -EINVAL;
- }
-
- if (chip->ima_supported) {
- /*
- * Change the FG_MEM_INT interrupt to track IACS_READY
- * condition instead of end-of-transaction. This makes sure
- * that the next transaction starts only after the hw is ready.
- */
- rc = fg_masked_write(chip,
- chip->mem_base + MEM_INTF_IMA_CFG, IACS_INTR_SRC_SLCT,
- IACS_INTR_SRC_SLCT, 1);
- if (rc) {
- pr_err("failed to configure interrupt source %d\n", rc);
- return rc;
- }
- }
-
- return 0;
-}
-
-static int fg_detect_pmic_type(struct fg_chip *chip)
-{
- struct pmic_revid_data *pmic_rev_id;
- struct device_node *revid_dev_node;
-
- revid_dev_node = of_parse_phandle(chip->pdev->dev.of_node,
- "qcom,pmic-revid", 0);
- if (!revid_dev_node) {
- pr_err("Missing qcom,pmic-revid property - driver failed\n");
- return -EINVAL;
- }
-
- pmic_rev_id = get_revid_data(revid_dev_node);
- if (IS_ERR_OR_NULL(pmic_rev_id)) {
- pr_err("Unable to get pmic_revid rc=%ld\n",
- PTR_ERR(pmic_rev_id));
- /*
- * the revid peripheral must be registered, any failure
- * here only indicates that the rev-id module has not
- * probed yet.
- */
- return -EPROBE_DEFER;
- }
-
- switch (pmic_rev_id->pmic_subtype) {
- case PMI8994:
- case PMI8950:
- case PMI8937:
- case PMI8996:
- chip->pmic_subtype = pmic_rev_id->pmic_subtype;
- chip->pmic_revision[REVID_RESERVED] = pmic_rev_id->rev1;
- chip->pmic_revision[REVID_VARIANT] = pmic_rev_id->rev2;
- chip->pmic_revision[REVID_ANA_MAJOR] = pmic_rev_id->rev3;
- chip->pmic_revision[REVID_DIG_MAJOR] = pmic_rev_id->rev4;
- break;
- default:
- pr_err("PMIC subtype %d not supported\n",
- pmic_rev_id->pmic_subtype);
- return -EINVAL;
- }
-
- return 0;
-}
-
-#define INIT_JEITA_DELAY_MS 1000
-
-static void delayed_init_work(struct work_struct *work)
-{
- u8 reg[2];
- int rc;
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- init_work);
-
- /* hold memory access until initialization finishes */
- fg_mem_lock(chip);
-
- rc = fg_hw_init(chip);
- if (rc) {
- pr_err("failed to hw init rc = %d\n", rc);
- fg_mem_release(chip);
- fg_cleanup(chip);
- return;
- }
- /* release memory access before update_sram_data is called */
- fg_mem_release(chip);
-
- schedule_delayed_work(
- &chip->update_jeita_setting,
- msecs_to_jiffies(INIT_JEITA_DELAY_MS));
-
- if (chip->last_sram_update_time == 0)
- update_sram_data_work(&chip->update_sram_data.work);
-
- if (chip->last_temp_update_time == 0)
- update_temp_data(&chip->update_temp_work.work);
-
- if (!chip->use_otp_profile)
- schedule_delayed_work(&chip->batt_profile_init, 0);
-
- if (chip->wa_flag & IADC_GAIN_COMP_WA) {
- /* read default gain config */
- rc = fg_mem_read(chip, reg, K_VCOR_REG, 2, DEF_GAIN_OFFSET, 0);
- if (rc) {
- pr_err("Failed to read default gain rc=%d\n", rc);
- goto done;
- }
-
- if (reg[1] || reg[0]) {
- /*
- * Default gain register has valid value:
- * - write to gain register.
- */
- rc = fg_mem_write(chip, reg, GAIN_REG, 2,
- GAIN_OFFSET, 0);
- if (rc) {
- pr_err("Failed to write gain rc=%d\n", rc);
- goto done;
- }
- } else {
- /*
- * Default gain register is invalid:
- * - read gain register for default gain value
- * - write to default gain register.
- */
- rc = fg_mem_read(chip, reg, GAIN_REG, 2,
- GAIN_OFFSET, 0);
- if (rc) {
- pr_err("Failed to read gain rc=%d\n", rc);
- goto done;
- }
- rc = fg_mem_write(chip, reg, K_VCOR_REG, 2,
- DEF_GAIN_OFFSET, 0);
- if (rc) {
- pr_err("Failed to write default gain rc=%d\n",
- rc);
- goto done;
- }
- }
-
- chip->iadc_comp_data.dfl_gain_reg[0] = reg[0];
- chip->iadc_comp_data.dfl_gain_reg[1] = reg[1];
- chip->iadc_comp_data.dfl_gain = half_float(reg);
- chip->input_present = is_input_present(chip);
- chip->otg_present = is_otg_present(chip);
- chip->init_done = true;
-
- pr_debug("IADC gain initial config reg_val 0x%x%x gain %lld\n",
- reg[1], reg[0], chip->iadc_comp_data.dfl_gain);
- }
-
- pr_debug("FG: HW_init success\n");
-
- return;
-done:
- fg_cleanup(chip);
-}
-
-static int fg_probe(struct platform_device *pdev)
-{
- struct device *dev = &(pdev->dev);
- struct fg_chip *chip;
- struct device_node *child;
- unsigned int base;
- u8 subtype, reg;
- int rc = 0;
- struct power_supply_config bms_psy_cfg;
-
- if (!pdev) {
- pr_err("no valid spmi pointer\n");
- return -ENODEV;
- }
-
- if (!pdev->dev.of_node) {
- pr_err("device node missing\n");
- return -ENODEV;
- }
-
- chip = devm_kzalloc(dev, sizeof(struct fg_chip), GFP_KERNEL);
- if (chip == NULL) {
- pr_err("Can't allocate fg_chip\n");
- return -ENOMEM;
- }
- chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!chip->regmap) {
- dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
- return -EINVAL;
- }
-
- chip->pdev = pdev;
- chip->dev = &(pdev->dev);
-
- wakeup_source_init(&chip->empty_check_wakeup_source.source,
- "qpnp_fg_empty_check");
- wakeup_source_init(&chip->memif_wakeup_source.source,
- "qpnp_fg_memaccess");
- wakeup_source_init(&chip->profile_wakeup_source.source,
- "qpnp_fg_profile");
- wakeup_source_init(&chip->update_temp_wakeup_source.source,
- "qpnp_fg_update_temp");
- wakeup_source_init(&chip->update_sram_wakeup_source.source,
- "qpnp_fg_update_sram");
- wakeup_source_init(&chip->resume_soc_wakeup_source.source,
- "qpnp_fg_set_resume_soc");
- wakeup_source_init(&chip->gain_comp_wakeup_source.source,
- "qpnp_fg_gain_comp");
- wakeup_source_init(&chip->capacity_learning_wakeup_source.source,
- "qpnp_fg_cap_learning");
- wakeup_source_init(&chip->esr_extract_wakeup_source.source,
- "qpnp_fg_esr_extract");
- mutex_init(&chip->rw_lock);
- mutex_init(&chip->cyc_ctr.lock);
- mutex_init(&chip->learning_data.learning_lock);
- mutex_init(&chip->rslow_comp.lock);
- mutex_init(&chip->sysfs_restart_lock);
- INIT_DELAYED_WORK(&chip->update_jeita_setting, update_jeita_setting);
- INIT_DELAYED_WORK(&chip->update_sram_data, update_sram_data_work);
- INIT_DELAYED_WORK(&chip->update_temp_work, update_temp_data);
- INIT_DELAYED_WORK(&chip->check_empty_work, check_empty_work);
- INIT_DELAYED_WORK(&chip->batt_profile_init, batt_profile_init);
- INIT_WORK(&chip->rslow_comp_work, rslow_comp_work);
- INIT_WORK(&chip->fg_cap_learning_work, fg_cap_learning_work);
- INIT_WORK(&chip->dump_sram, dump_sram);
- INIT_WORK(&chip->status_change_work, status_change_work);
- INIT_WORK(&chip->cycle_count_work, update_cycle_count);
- INIT_WORK(&chip->battery_age_work, battery_age_work);
- INIT_WORK(&chip->update_esr_work, update_esr_value);
- INIT_WORK(&chip->set_resume_soc_work, set_resume_soc_work);
- INIT_WORK(&chip->sysfs_restart_work, sysfs_restart_work);
- INIT_WORK(&chip->init_work, delayed_init_work);
- INIT_WORK(&chip->charge_full_work, charge_full_work);
- INIT_WORK(&chip->gain_comp_work, iadc_gain_comp_work);
- INIT_WORK(&chip->bcl_hi_power_work, bcl_hi_power_work);
- INIT_WORK(&chip->esr_extract_config_work, esr_extract_config_work);
- alarm_init(&chip->fg_cap_learning_alarm, ALARM_BOOTTIME,
- fg_cap_learning_alarm_cb);
- init_completion(&chip->sram_access_granted);
- init_completion(&chip->sram_access_revoked);
- complete_all(&chip->sram_access_revoked);
- init_completion(&chip->batt_id_avail);
- init_completion(&chip->first_soc_done);
- dev_set_drvdata(&pdev->dev, chip);
-
- if (of_get_available_child_count(pdev->dev.of_node) == 0) {
- pr_err("no child nodes\n");
- rc = -ENXIO;
- goto of_init_fail;
- }
-
- for_each_available_child_of_node(pdev->dev.of_node, child) {
- rc = of_property_read_u32(child, "reg", &base);
- if (rc < 0) {
- dev_err(&pdev->dev,
- "Couldn't find reg in node = %s rc = %d\n",
- child->full_name, rc);
- goto of_init_fail;
- }
-
- if (strcmp("qcom,fg-adc-vbat", child->name) == 0) {
- chip->vbat_adc_addr = base;
- continue;
- } else if (strcmp("qcom,fg-adc-ibat", child->name) == 0) {
- chip->ibat_adc_addr = base;
- continue;
- } else if (strcmp("qcom,revid-tp-rev", child->name) == 0) {
- chip->tp_rev_addr = base;
- continue;
- }
-
- rc = fg_read(chip, &subtype,
- base + REG_OFFSET_PERP_SUBTYPE, 1);
- if (rc) {
- pr_err("Peripheral subtype read failed rc=%d\n", rc);
- goto of_init_fail;
- }
-
- switch (subtype) {
- case FG_SOC:
- chip->soc_base = base;
- break;
- case FG_MEMIF:
- chip->mem_base = base;
- break;
- case FG_BATT:
- chip->batt_base = base;
- break;
- default:
- pr_err("Invalid peripheral subtype=0x%x\n", subtype);
- rc = -EINVAL;
- }
- }
-
- rc = fg_detect_pmic_type(chip);
- if (rc) {
- pr_err("Unable to detect PMIC type rc=%d\n", rc);
- return rc;
- }
-
- rc = fg_setup_memif_offset(chip);
- if (rc) {
- pr_err("Unable to setup mem_if offsets rc=%d\n", rc);
- goto of_init_fail;
- }
-
- rc = fg_of_init(chip);
- if (rc) {
- pr_err("failed to parse devicetree rc%d\n", rc);
- goto of_init_fail;
- }
-
- if (chip->jeita_hysteresis_support) {
- rc = fg_init_batt_temp_state(chip);
- if (rc) {
- pr_err("failed to get battery status rc%d\n", rc);
- goto of_init_fail;
- }
- }
-
- /* check if the first estimate is already finished at this time */
- if (is_first_est_done(chip))
- complete_all(&chip->first_soc_done);
-
- reg = 0xFF;
- rc = fg_write(chip, &reg, INT_EN_CLR(chip->mem_base), 1);
- if (rc) {
- pr_err("failed to clear interrupts %d\n", rc);
- goto of_init_fail;
- }
-
- rc = fg_init_irqs(chip);
- if (rc) {
- pr_err("failed to request interrupts %d\n", rc);
- goto cancel_work;
- }
-
- chip->batt_type = default_batt_type;
-
- chip->bms_psy_d.name = "bms";
- chip->bms_psy_d.type = POWER_SUPPLY_TYPE_BMS;
- chip->bms_psy_d.properties = fg_power_props;
- chip->bms_psy_d.num_properties = ARRAY_SIZE(fg_power_props);
- chip->bms_psy_d.get_property = fg_power_get_property;
- chip->bms_psy_d.set_property = fg_power_set_property;
- chip->bms_psy_d.external_power_changed = fg_external_power_changed;
- chip->bms_psy_d.property_is_writeable = fg_property_is_writeable;
-
- bms_psy_cfg.drv_data = chip;
- bms_psy_cfg.supplied_to = fg_supplicants;
- bms_psy_cfg.num_supplicants = ARRAY_SIZE(fg_supplicants);
- bms_psy_cfg.of_node = NULL;
- chip->bms_psy = devm_power_supply_register(chip->dev,
- &chip->bms_psy_d,
- &bms_psy_cfg);
- if (IS_ERR(chip->bms_psy)) {
- pr_err("batt failed to register rc = %ld\n",
- PTR_ERR(chip->bms_psy));
- goto of_init_fail;
- }
- chip->power_supply_registered = true;
- /*
- * Just initialize the batt_psy_name here. Power supply
- * will be obtained later.
- */
- chip->batt_psy_name = "battery";
-
- if (chip->mem_base) {
- rc = fg_dfs_create(chip);
- if (rc < 0) {
- pr_err("failed to create debugfs rc = %d\n", rc);
- goto cancel_work;
- }
- }
-
- schedule_work(&chip->init_work);
-
- pr_info("FG Probe success - FG Revision DIG:%d.%d ANA:%d.%d PMIC subtype=%d\n",
- chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
- chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR],
- chip->pmic_subtype);
-
- return rc;
-
-cancel_work:
- cancel_delayed_work_sync(&chip->update_jeita_setting);
- cancel_delayed_work_sync(&chip->update_sram_data);
- cancel_delayed_work_sync(&chip->update_temp_work);
- cancel_delayed_work_sync(&chip->check_empty_work);
- cancel_delayed_work_sync(&chip->batt_profile_init);
- alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
- cancel_work_sync(&chip->set_resume_soc_work);
- cancel_work_sync(&chip->fg_cap_learning_work);
- cancel_work_sync(&chip->dump_sram);
- cancel_work_sync(&chip->status_change_work);
- cancel_work_sync(&chip->cycle_count_work);
- cancel_work_sync(&chip->update_esr_work);
- cancel_work_sync(&chip->rslow_comp_work);
- cancel_work_sync(&chip->sysfs_restart_work);
- cancel_work_sync(&chip->gain_comp_work);
- cancel_work_sync(&chip->init_work);
- cancel_work_sync(&chip->charge_full_work);
- cancel_work_sync(&chip->bcl_hi_power_work);
- cancel_work_sync(&chip->esr_extract_config_work);
-of_init_fail:
- mutex_destroy(&chip->rslow_comp.lock);
- mutex_destroy(&chip->rw_lock);
- mutex_destroy(&chip->cyc_ctr.lock);
- mutex_destroy(&chip->learning_data.learning_lock);
- mutex_destroy(&chip->sysfs_restart_lock);
- wakeup_source_trash(&chip->resume_soc_wakeup_source.source);
- wakeup_source_trash(&chip->empty_check_wakeup_source.source);
- wakeup_source_trash(&chip->memif_wakeup_source.source);
- wakeup_source_trash(&chip->profile_wakeup_source.source);
- wakeup_source_trash(&chip->update_temp_wakeup_source.source);
- wakeup_source_trash(&chip->update_sram_wakeup_source.source);
- wakeup_source_trash(&chip->gain_comp_wakeup_source.source);
- wakeup_source_trash(&chip->capacity_learning_wakeup_source.source);
- wakeup_source_trash(&chip->esr_extract_wakeup_source.source);
- return rc;
-}
-
-static void check_and_update_sram_data(struct fg_chip *chip)
-{
- unsigned long current_time = 0, next_update_time, time_left;
-
- get_current_time(&current_time);
-
- next_update_time = chip->last_temp_update_time
- + (TEMP_PERIOD_UPDATE_MS / 1000);
-
- if (next_update_time > current_time)
- time_left = next_update_time - current_time;
- else
- time_left = 0;
-
- schedule_delayed_work(
- &chip->update_temp_work, msecs_to_jiffies(time_left * 1000));
-
- next_update_time = chip->last_sram_update_time
- + (fg_sram_update_period_ms / 1000);
-
- if (next_update_time > current_time)
- time_left = next_update_time - current_time;
- else
- time_left = 0;
-
- schedule_delayed_work(
- &chip->update_sram_data, msecs_to_jiffies(time_left * 1000));
-}
-
-static int fg_suspend(struct device *dev)
-{
- struct fg_chip *chip = dev_get_drvdata(dev);
-
- if (!chip->sw_rbias_ctrl)
- return 0;
-
- cancel_delayed_work(&chip->update_temp_work);
- cancel_delayed_work(&chip->update_sram_data);
-
- return 0;
-}
-
-static int fg_resume(struct device *dev)
-{
- struct fg_chip *chip = dev_get_drvdata(dev);
-
- if (!chip->sw_rbias_ctrl)
- return 0;
-
- check_and_update_sram_data(chip);
- return 0;
-}
-
-static const struct dev_pm_ops qpnp_fg_pm_ops = {
- .suspend = fg_suspend,
- .resume = fg_resume,
-};
-
-static int fg_sense_type_set(const char *val, const struct kernel_param *kp)
-{
- int rc;
- struct power_supply *bms_psy;
- struct fg_chip *chip;
- int old_fg_sense_type = fg_sense_type;
-
- rc = param_set_int(val, kp);
- if (rc) {
- pr_err("Unable to set fg_sense_type: %d\n", rc);
- return rc;
- }
-
- if (fg_sense_type != 0 && fg_sense_type != 1) {
- pr_err("Bad value %d\n", fg_sense_type);
- fg_sense_type = old_fg_sense_type;
- return -EINVAL;
- }
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("fg_sense_type set to %d\n", fg_sense_type);
-
- bms_psy = power_supply_get_by_name("bms");
- if (!bms_psy) {
- pr_err("bms psy not found\n");
- return 0;
- }
-
- chip = power_supply_get_drvdata(bms_psy);
- rc = set_prop_sense_type(chip, fg_sense_type);
- return rc;
-}
-
-static struct kernel_param_ops fg_sense_type_ops = {
- .set = fg_sense_type_set,
- .get = param_get_int,
-};
-
-module_param_cb(sense_type, &fg_sense_type_ops, &fg_sense_type, 0644);
-
-static int fg_restart_set(const char *val, const struct kernel_param *kp)
-{
- struct power_supply *bms_psy;
- struct fg_chip *chip;
-
- bms_psy = power_supply_get_by_name("bms");
- if (!bms_psy) {
- pr_err("bms psy not found\n");
- return 0;
- }
- chip = power_supply_get_drvdata(bms_psy);
-
- mutex_lock(&chip->sysfs_restart_lock);
- if (fg_restart != 0) {
- mutex_unlock(&chip->sysfs_restart_lock);
- return 0;
- }
- fg_restart = 1;
- mutex_unlock(&chip->sysfs_restart_lock);
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("fuel gauge restart initiated from sysfs...\n");
-
- schedule_work(&chip->sysfs_restart_work);
- return 0;
-}
-
-static struct kernel_param_ops fg_restart_ops = {
- .set = fg_restart_set,
- .get = param_get_int,
-};
-
-module_param_cb(restart, &fg_restart_ops, &fg_restart, 0644);
-
-static struct platform_driver fg_driver = {
- .driver = {
- .name = QPNP_FG_DEV_NAME,
- .of_match_table = fg_match_table,
- .pm = &qpnp_fg_pm_ops,
- },
- .probe = fg_probe,
- .remove = fg_remove,
-};
-
-static int __init fg_init(void)
-{
- return platform_driver_register(&fg_driver);
-}
-
-static void __exit fg_exit(void)
-{
- return platform_driver_unregister(&fg_driver);
-}
-
-module_init(fg_init);
-module_exit(fg_exit);
-
-MODULE_DESCRIPTION("QPNP Fuel Gauge Driver");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" QPNP_FG_DEV_NAME);
diff --git a/drivers/power/supply/qcom/qpnp-smbcharger.c b/drivers/power/supply/qcom/qpnp-smbcharger.c
deleted file mode 100644
index 6c1e58d046e8..000000000000
--- a/drivers/power/supply/qcom/qpnp-smbcharger.c
+++ /dev/null
@@ -1,8472 +0,0 @@
-/* Copyright (c) 2014-2016 The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#define pr_fmt(fmt) "SMBCHG: %s: " fmt, __func__
-
-#include <linux/regmap.h>
-#include <linux/spinlock.h>
-#include <linux/gpio.h>
-#include <linux/errno.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/power_supply.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
-#include <linux/of_irq.h>
-#include <linux/bitops.h>
-#include <linux/regulator/consumer.h>
-#include <linux/regulator/driver.h>
-#include <linux/regulator/of_regulator.h>
-#include <linux/regulator/machine.h>
-#include <linux/spmi.h>
-#include <linux/platform_device.h>
-#include <linux/printk.h>
-#include <linux/ratelimit.h>
-#include <linux/debugfs.h>
-#include <linux/leds.h>
-#include <linux/rtc.h>
-#include <linux/qpnp/qpnp-adc.h>
-#include <linux/batterydata-lib.h>
-#include <linux/of_batterydata.h>
-#include <linux/msm_bcl.h>
-#include <linux/ktime.h>
-#include <linux/extcon.h>
-#include "pmic-voter.h"
-
-/* Mask/Bit helpers */
-#define _SMB_MASK(BITS, POS) \
- ((unsigned char)(((1 << (BITS)) - 1) << (POS)))
-#define SMB_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \
- _SMB_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \
- (RIGHT_BIT_POS))
-/* Config registers */
-struct smbchg_regulator {
- struct regulator_desc rdesc;
- struct regulator_dev *rdev;
-};
-
-struct parallel_usb_cfg {
- struct power_supply *psy;
- int min_current_thr_ma;
- int min_9v_current_thr_ma;
- int allowed_lowering_ma;
- int current_max_ma;
- bool avail;
- struct mutex lock;
- int initial_aicl_ma;
- ktime_t last_disabled;
- bool enabled_once;
-};
-
-struct ilim_entry {
- int vmin_uv;
- int vmax_uv;
- int icl_pt_ma;
- int icl_lv_ma;
- int icl_hv_ma;
-};
-
-struct ilim_map {
- int num;
- struct ilim_entry *entries;
-};
-
-struct smbchg_version_tables {
- const int *dc_ilim_ma_table;
- int dc_ilim_ma_len;
- const int *usb_ilim_ma_table;
- int usb_ilim_ma_len;
- const int *iterm_ma_table;
- int iterm_ma_len;
- const int *fcc_comp_table;
- int fcc_comp_len;
- const int *aicl_rerun_period_table;
- int aicl_rerun_period_len;
- int rchg_thr_mv;
-};
-
-struct smbchg_chip {
- struct device *dev;
- struct platform_device *pdev;
- struct regmap *regmap;
- int schg_version;
-
- /* peripheral register address bases */
- u16 chgr_base;
- u16 bat_if_base;
- u16 usb_chgpth_base;
- u16 dc_chgpth_base;
- u16 otg_base;
- u16 misc_base;
-
- int fake_battery_soc;
- u8 revision[4];
-
- /* configuration parameters */
- int iterm_ma;
- int usb_max_current_ma;
- int typec_current_ma;
- int dc_max_current_ma;
- int dc_target_current_ma;
- int cfg_fastchg_current_ma;
- int fastchg_current_ma;
- int vfloat_mv;
- int fastchg_current_comp;
- int float_voltage_comp;
- int resume_delta_mv;
- int safety_time;
- int prechg_safety_time;
- int bmd_pin_src;
- int jeita_temp_hard_limit;
- int aicl_rerun_period_s;
- bool use_vfloat_adjustments;
- bool iterm_disabled;
- bool bmd_algo_disabled;
- bool soft_vfloat_comp_disabled;
- bool chg_enabled;
- bool charge_unknown_battery;
- bool chg_inhibit_en;
- bool chg_inhibit_source_fg;
- bool low_volt_dcin;
- bool cfg_chg_led_support;
- bool cfg_chg_led_sw_ctrl;
- bool vbat_above_headroom;
- bool force_aicl_rerun;
- bool hvdcp3_supported;
- bool restricted_charging;
- bool skip_usb_suspend_for_fake_battery;
- bool hvdcp_not_supported;
- bool otg_pinctrl;
- u8 original_usbin_allowance;
- struct parallel_usb_cfg parallel;
- struct delayed_work parallel_en_work;
- struct dentry *debug_root;
- struct smbchg_version_tables tables;
-
- /* wipower params */
- struct ilim_map wipower_default;
- struct ilim_map wipower_pt;
- struct ilim_map wipower_div2;
- struct qpnp_vadc_chip *vadc_dev;
- bool wipower_dyn_icl_avail;
- struct ilim_entry current_ilim;
- struct mutex wipower_config;
- bool wipower_configured;
- struct qpnp_adc_tm_btm_param param;
-
- /* flash current prediction */
- int rpara_uohm;
- int rslow_uohm;
- int vled_max_uv;
-
- /* vfloat adjustment */
- int max_vbat_sample;
- int n_vbat_samples;
-
- /* status variables */
- int wake_reasons;
- int previous_soc;
- int usb_online;
- bool dc_present;
- bool usb_present;
- bool batt_present;
- int otg_retries;
- ktime_t otg_enable_time;
- bool aicl_deglitch_short;
- bool safety_timer_en;
- bool aicl_complete;
- bool usb_ov_det;
- bool otg_pulse_skip_dis;
- const char *battery_type;
- enum power_supply_type usb_supply_type;
- bool very_weak_charger;
- bool parallel_charger_detected;
- bool chg_otg_enabled;
- bool flash_triggered;
- bool flash_active;
- bool icl_disabled;
- u32 wa_flags;
- int usb_icl_delta;
- bool typec_dfp;
- unsigned int usb_current_max;
- unsigned int usb_health;
-
- /* jeita and temperature */
- bool batt_hot;
- bool batt_cold;
- bool batt_warm;
- bool batt_cool;
- unsigned int thermal_levels;
- unsigned int therm_lvl_sel;
- unsigned int *thermal_mitigation;
-
- /* irqs */
- int batt_hot_irq;
- int batt_warm_irq;
- int batt_cool_irq;
- int batt_cold_irq;
- int batt_missing_irq;
- int vbat_low_irq;
- int chg_hot_irq;
- int chg_term_irq;
- int taper_irq;
- bool taper_irq_enabled;
- struct mutex taper_irq_lock;
- int recharge_irq;
- int fastchg_irq;
- int wdog_timeout_irq;
- int power_ok_irq;
- int dcin_uv_irq;
- int usbin_uv_irq;
- int usbin_ov_irq;
- int src_detect_irq;
- int otg_fail_irq;
- int otg_oc_irq;
- int aicl_done_irq;
- int usbid_change_irq;
- int chg_error_irq;
- bool enable_aicl_wake;
-
- /* psy */
- struct power_supply_desc usb_psy_d;
- struct power_supply *usb_psy;
- struct power_supply_desc batt_psy_d;
- struct power_supply *batt_psy;
- struct power_supply_desc dc_psy_d;
- struct power_supply *dc_psy;
- struct power_supply *bms_psy;
- struct power_supply *typec_psy;
- int dc_psy_type;
- const char *bms_psy_name;
- const char *battery_psy_name;
-
- struct regulator *dpdm_reg;
- struct smbchg_regulator otg_vreg;
- struct smbchg_regulator ext_otg_vreg;
- struct work_struct usb_set_online_work;
- struct delayed_work vfloat_adjust_work;
- struct delayed_work hvdcp_det_work;
- spinlock_t sec_access_lock;
- struct mutex therm_lvl_lock;
- struct mutex usb_set_online_lock;
- struct mutex pm_lock;
- /* aicl deglitch workaround */
- unsigned long first_aicl_seconds;
- int aicl_irq_count;
- struct mutex usb_status_lock;
- bool hvdcp_3_det_ignore_uv;
- struct completion src_det_lowered;
- struct completion src_det_raised;
- struct completion usbin_uv_lowered;
- struct completion usbin_uv_raised;
- int pulse_cnt;
- struct led_classdev led_cdev;
- bool skip_usb_notification;
- u32 vchg_adc_channel;
- struct qpnp_vadc_chip *vchg_vadc_dev;
-
- /* voters */
- struct votable *fcc_votable;
- struct votable *usb_icl_votable;
- struct votable *dc_icl_votable;
- struct votable *usb_suspend_votable;
- struct votable *dc_suspend_votable;
- struct votable *battchg_suspend_votable;
- struct votable *hw_aicl_rerun_disable_votable;
- struct votable *hw_aicl_rerun_enable_indirect_votable;
- struct votable *aicl_deglitch_short_votable;
-
- /* extcon for VBUS / ID notification to USB */
- struct extcon_dev *extcon;
-};
-
-enum qpnp_schg {
- QPNP_SCHG,
- QPNP_SCHG_LITE,
-};
-
-static char *version_str[] = {
- [QPNP_SCHG] = "SCHG",
- [QPNP_SCHG_LITE] = "SCHG_LITE",
-};
-
-enum pmic_subtype {
- PMI8994 = 10,
- PMI8950 = 17,
- PMI8996 = 19,
- PMI8937 = 55,
-};
-
-enum smbchg_wa {
- SMBCHG_AICL_DEGLITCH_WA = BIT(0),
- SMBCHG_HVDCP_9V_EN_WA = BIT(1),
- SMBCHG_USB100_WA = BIT(2),
- SMBCHG_BATT_OV_WA = BIT(3),
- SMBCHG_CC_ESR_WA = BIT(4),
- SMBCHG_FLASH_ICL_DISABLE_WA = BIT(5),
- SMBCHG_RESTART_WA = BIT(6),
- SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA = BIT(7),
-};
-
-enum print_reason {
- PR_REGISTER = BIT(0),
- PR_INTERRUPT = BIT(1),
- PR_STATUS = BIT(2),
- PR_DUMP = BIT(3),
- PR_PM = BIT(4),
- PR_MISC = BIT(5),
- PR_WIPOWER = BIT(6),
- PR_TYPEC = BIT(7),
-};
-
-enum wake_reason {
- PM_PARALLEL_CHECK = BIT(0),
- PM_REASON_VFLOAT_ADJUST = BIT(1),
- PM_ESR_PULSE = BIT(2),
- PM_PARALLEL_TAPER = BIT(3),
- PM_DETECT_HVDCP = BIT(4),
-};
-
-/* fcc_voters */
-#define ESR_PULSE_FCC_VOTER "ESR_PULSE_FCC_VOTER"
-#define BATT_TYPE_FCC_VOTER "BATT_TYPE_FCC_VOTER"
-#define RESTRICTED_CHG_FCC_VOTER "RESTRICTED_CHG_FCC_VOTER"
-
-/* ICL VOTERS */
-#define PSY_ICL_VOTER "PSY_ICL_VOTER"
-#define THERMAL_ICL_VOTER "THERMAL_ICL_VOTER"
-#define HVDCP_ICL_VOTER "HVDCP_ICL_VOTER"
-#define USER_ICL_VOTER "USER_ICL_VOTER"
-#define WEAK_CHARGER_ICL_VOTER "WEAK_CHARGER_ICL_VOTER"
-#define SW_AICL_ICL_VOTER "SW_AICL_ICL_VOTER"
-#define CHG_SUSPEND_WORKAROUND_ICL_VOTER "CHG_SUSPEND_WORKAROUND_ICL_VOTER"
-
-/* USB SUSPEND VOTERS */
-/* userspace has suspended charging altogether */
-#define USER_EN_VOTER "USER_EN_VOTER"
-/*
- * this specific path has been suspended through the power supply
- * framework
- */
-#define POWER_SUPPLY_EN_VOTER "POWER_SUPPLY_EN_VOTER"
-/*
- * the usb driver has suspended this path by setting a current limit
- * of < 2MA
- */
-#define USB_EN_VOTER "USB_EN_VOTER"
-/*
- * the thermal daemon can suspend a charge path when the system
- * temperature levels rise
- */
-#define THERMAL_EN_VOTER "THERMAL_EN_VOTER"
-/*
- * an external OTG supply is being used, suspend charge path so the
- * charger does not accidentally try to charge from the external supply.
- */
-#define OTG_EN_VOTER "OTG_EN_VOTER"
-/*
- * the charger is very weak, do not draw any current from it
- */
-#define WEAK_CHARGER_EN_VOTER "WEAK_CHARGER_EN_VOTER"
-/*
- * fake battery voter, if battery id-resistance around 7.5 Kohm
- */
-#define FAKE_BATTERY_EN_VOTER "FAKE_BATTERY_EN_VOTER"
-
-/* battchg_enable_voters */
- /* userspace has disabled battery charging */
-#define BATTCHG_USER_EN_VOTER "BATTCHG_USER_EN_VOTER"
- /* battery charging disabled while loading battery profiles */
-#define BATTCHG_UNKNOWN_BATTERY_EN_VOTER "BATTCHG_UNKNOWN_BATTERY_EN_VOTER"
-
-/* hw_aicl_rerun_enable_indirect_voters */
-/* enabled via device tree */
-#define DEFAULT_CONFIG_HW_AICL_VOTER "DEFAULT_CONFIG_HW_AICL_VOTER"
-/* Varb workaround voter */
-#define VARB_WORKAROUND_VOTER "VARB_WORKAROUND_VOTER"
-/* SHUTDOWN workaround voter */
-#define SHUTDOWN_WORKAROUND_VOTER "SHUTDOWN_WORKAROUND_VOTER"
-
-/* hw_aicl_rerun_disable_voters */
-/* the results from enabling clients */
-#define HW_AICL_RERUN_ENABLE_INDIRECT_VOTER \
- "HW_AICL_RERUN_ENABLE_INDIRECT_VOTER"
-/* Weak charger voter */
-#define WEAK_CHARGER_HW_AICL_VOTER "WEAK_CHARGER_HW_AICL_VOTER"
-
-/* aicl_short_deglitch_voters */
-/* Varb workaround voter */
-#define VARB_WORKAROUND_SHORT_DEGLITCH_VOTER \
- "VARB_WRKARND_SHORT_DEGLITCH_VOTER"
-/* QC 2.0 */
-#define HVDCP_SHORT_DEGLITCH_VOTER "HVDCP_SHORT_DEGLITCH_VOTER"
-
-static const unsigned int smbchg_extcon_cable[] = {
- EXTCON_USB,
- EXTCON_USB_HOST,
- EXTCON_NONE,
-};
-
-static int smbchg_debug_mask;
-module_param_named(
- debug_mask, smbchg_debug_mask, int, S_IRUSR | S_IWUSR
-);
-
-static int smbchg_parallel_en = 1;
-module_param_named(
- parallel_en, smbchg_parallel_en, int, S_IRUSR | S_IWUSR
-);
-
-static int smbchg_main_chg_fcc_percent = 50;
-module_param_named(
- main_chg_fcc_percent, smbchg_main_chg_fcc_percent,
- int, S_IRUSR | S_IWUSR
-);
-
-static int smbchg_main_chg_icl_percent = 60;
-module_param_named(
- main_chg_icl_percent, smbchg_main_chg_icl_percent,
- int, S_IRUSR | S_IWUSR
-);
-
-static int smbchg_default_hvdcp_icl_ma = 1800;
-module_param_named(
- default_hvdcp_icl_ma, smbchg_default_hvdcp_icl_ma,
- int, S_IRUSR | S_IWUSR
-);
-
-static int smbchg_default_hvdcp3_icl_ma = 3000;
-module_param_named(
- default_hvdcp3_icl_ma, smbchg_default_hvdcp3_icl_ma,
- int, S_IRUSR | S_IWUSR
-);
-
-static int smbchg_default_dcp_icl_ma = 1800;
-module_param_named(
- default_dcp_icl_ma, smbchg_default_dcp_icl_ma,
- int, S_IRUSR | S_IWUSR
-);
-
-static int wipower_dyn_icl_en;
-module_param_named(
- dynamic_icl_wipower_en, wipower_dyn_icl_en,
- int, S_IRUSR | S_IWUSR
-);
-
-static int wipower_dcin_interval = ADC_MEAS1_INTERVAL_2P0MS;
-module_param_named(
- wipower_dcin_interval, wipower_dcin_interval,
- int, S_IRUSR | S_IWUSR
-);
-
-#define WIPOWER_DEFAULT_HYSTERISIS_UV 250000
-static int wipower_dcin_hyst_uv = WIPOWER_DEFAULT_HYSTERISIS_UV;
-module_param_named(
- wipower_dcin_hyst_uv, wipower_dcin_hyst_uv,
- int, S_IRUSR | S_IWUSR
-);
-
-#define pr_smb(reason, fmt, ...) \
- do { \
- if (smbchg_debug_mask & (reason)) \
- pr_info(fmt, ##__VA_ARGS__); \
- else \
- pr_debug(fmt, ##__VA_ARGS__); \
- } while (0)
-
-#define pr_smb_rt(reason, fmt, ...) \
- do { \
- if (smbchg_debug_mask & (reason)) \
- pr_info_ratelimited(fmt, ##__VA_ARGS__); \
- else \
- pr_debug(fmt, ##__VA_ARGS__); \
- } while (0)
-
-static int smbchg_read(struct smbchg_chip *chip, u8 *val,
- u16 addr, int count)
-{
- int rc = 0;
- struct platform_device *pdev = chip->pdev;
-
- if (addr == 0) {
- dev_err(chip->dev, "addr cannot be zero addr=0x%02x sid=0x%02x rc=%d\n",
- addr, to_spmi_device(pdev->dev.parent)->usid, rc);
- return -EINVAL;
- }
-
- rc = regmap_bulk_read(chip->regmap, addr, val, count);
- if (rc) {
- dev_err(chip->dev, "spmi read failed addr=0x%02x sid=0x%02x rc=%d\n",
- addr, to_spmi_device(pdev->dev.parent)->usid,
- rc);
- return rc;
- }
- return 0;
-}
-
-/*
- * Writes a register to the specified by the base and limited by the bit mask
- *
- * Do not use this function for register writes if possible. Instead use the
- * smbchg_masked_write function.
- *
- * The sec_access_lock must be held for all register writes and this function
- * does not do that. If this function is used, please hold the spinlock or
- * random secure access writes may fail.
- */
-static int smbchg_masked_write_raw(struct smbchg_chip *chip, u16 base, u8 mask,
- u8 val)
-{
- int rc;
-
- rc = regmap_update_bits(chip->regmap, base, mask, val);
- if (rc) {
- dev_err(chip->dev, "spmi write failed: addr=%03X, rc=%d\n",
- base, rc);
- return rc;
- }
-
- return 0;
-}
-
-/*
- * Writes a register to the specified by the base and limited by the bit mask
- *
- * This function holds a spin lock to ensure secure access register writes goes
- * through. If the secure access unlock register is armed, any old register
- * write can unarm the secure access unlock, causing the next write to fail.
- *
- * Note: do not use this for sec_access registers. Instead use the function
- * below: smbchg_sec_masked_write
- */
-static int smbchg_masked_write(struct smbchg_chip *chip, u16 base, u8 mask,
- u8 val)
-{
- unsigned long flags;
- int rc;
-
- spin_lock_irqsave(&chip->sec_access_lock, flags);
- rc = smbchg_masked_write_raw(chip, base, mask, val);
- spin_unlock_irqrestore(&chip->sec_access_lock, flags);
-
- return rc;
-}
-
-/*
- * Unlocks sec access and writes to the register specified.
- *
- * This function holds a spin lock to exclude other register writes while
- * the two writes are taking place.
- */
-#define SEC_ACCESS_OFFSET 0xD0
-#define SEC_ACCESS_VALUE 0xA5
-#define PERIPHERAL_MASK 0xFF
-static int smbchg_sec_masked_write(struct smbchg_chip *chip, u16 base, u8 mask,
- u8 val)
-{
- unsigned long flags;
- int rc;
- u16 peripheral_base = base & (~PERIPHERAL_MASK);
-
- spin_lock_irqsave(&chip->sec_access_lock, flags);
-
- rc = smbchg_masked_write_raw(chip, peripheral_base + SEC_ACCESS_OFFSET,
- SEC_ACCESS_VALUE, SEC_ACCESS_VALUE);
- if (rc) {
- dev_err(chip->dev, "Unable to unlock sec_access: %d", rc);
- goto out;
- }
-
- rc = smbchg_masked_write_raw(chip, base, mask, val);
-
-out:
- spin_unlock_irqrestore(&chip->sec_access_lock, flags);
- return rc;
-}
-
-static void smbchg_stay_awake(struct smbchg_chip *chip, int reason)
-{
- int reasons;
-
- mutex_lock(&chip->pm_lock);
- reasons = chip->wake_reasons | reason;
- if (reasons != 0 && chip->wake_reasons == 0) {
- pr_smb(PR_PM, "staying awake: 0x%02x (bit %d)\n",
- reasons, reason);
- pm_stay_awake(chip->dev);
- }
- chip->wake_reasons = reasons;
- mutex_unlock(&chip->pm_lock);
-}
-
-static void smbchg_relax(struct smbchg_chip *chip, int reason)
-{
- int reasons;
-
- mutex_lock(&chip->pm_lock);
- reasons = chip->wake_reasons & (~reason);
- if (reasons == 0 && chip->wake_reasons != 0) {
- pr_smb(PR_PM, "relaxing: 0x%02x (bit %d)\n",
- reasons, reason);
- pm_relax(chip->dev);
- }
- chip->wake_reasons = reasons;
- mutex_unlock(&chip->pm_lock);
-};
-
-enum pwr_path_type {
- UNKNOWN = 0,
- PWR_PATH_BATTERY = 1,
- PWR_PATH_USB = 2,
- PWR_PATH_DC = 3,
-};
-
-#define PWR_PATH 0x08
-#define PWR_PATH_MASK 0x03
-static enum pwr_path_type smbchg_get_pwr_path(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + PWR_PATH, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read PWR_PATH rc = %d\n", rc);
- return PWR_PATH_BATTERY;
- }
-
- return reg & PWR_PATH_MASK;
-}
-
-#define RID_STS 0xB
-#define RID_MASK 0xF
-#define IDEV_STS 0x8
-#define RT_STS 0x10
-#define USBID_MSB 0xE
-#define USBIN_UV_BIT BIT(0)
-#define USBIN_OV_BIT BIT(1)
-#define USBIN_SRC_DET_BIT BIT(2)
-#define FMB_STS_MASK SMB_MASK(3, 0)
-#define USBID_GND_THRESHOLD 0x495
-static bool is_otg_present_schg(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
- u8 usbid_reg[2];
- u16 usbid_val;
- /*
- * After the falling edge of the usbid change interrupt occurs,
- * there may still be some time before the ADC conversion for USB RID
- * finishes in the fuel gauge. In the worst case, this could be up to
- * 15 ms.
- *
- * Sleep for 20 ms (minimum msleep time) to wait for the conversion to
- * finish and the USB RID status register to be updated before trying
- * to detect OTG insertions.
- */
-
- msleep(20);
-
- /*
- * There is a problem with USBID conversions on PMI8994 revisions
- * 2.0.0. As a workaround, check that the cable is not
- * detected as factory test before enabling OTG.
- */
- rc = smbchg_read(chip, &reg, chip->misc_base + IDEV_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read IDEV_STS rc = %d\n", rc);
- return false;
- }
-
- if ((reg & FMB_STS_MASK) != 0) {
- pr_smb(PR_STATUS, "IDEV_STS = %02x, not ground\n", reg);
- return false;
- }
-
- rc = smbchg_read(chip, usbid_reg, chip->usb_chgpth_base + USBID_MSB, 2);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read USBID rc = %d\n", rc);
- return false;
- }
- usbid_val = (usbid_reg[0] << 8) | usbid_reg[1];
-
- if (usbid_val > USBID_GND_THRESHOLD) {
- pr_smb(PR_STATUS, "USBID = 0x%04x, too high to be ground\n",
- usbid_val);
- return false;
- }
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RID_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't read usb rid status rc = %d\n", rc);
- return false;
- }
-
- pr_smb(PR_STATUS, "RID_STS = %02x\n", reg);
-
- return (reg & RID_MASK) == 0;
-}
-
-#define RID_GND_DET_STS BIT(2)
-static bool is_otg_present_schg_lite(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->otg_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't read otg RT status rc = %d\n", rc);
- return false;
- }
-
- return !!(reg & RID_GND_DET_STS);
-}
-
-static bool is_otg_present(struct smbchg_chip *chip)
-{
- if (chip->schg_version == QPNP_SCHG_LITE)
- return is_otg_present_schg_lite(chip);
-
- return is_otg_present_schg(chip);
-}
-
-#define USBIN_9V BIT(5)
-#define USBIN_UNREG BIT(4)
-#define USBIN_LV BIT(3)
-#define DCIN_9V BIT(2)
-#define DCIN_UNREG BIT(1)
-#define DCIN_LV BIT(0)
-#define INPUT_STS 0x0D
-#define DCIN_UV_BIT BIT(0)
-#define DCIN_OV_BIT BIT(1)
-static bool is_dc_present(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->dc_chgpth_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read dc status rc = %d\n", rc);
- return false;
- }
-
- if ((reg & DCIN_UV_BIT) || (reg & DCIN_OV_BIT))
- return false;
-
- return true;
-}
-
-static bool is_usb_present(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
- return false;
- }
- if (!(reg & USBIN_SRC_DET_BIT) || (reg & USBIN_OV_BIT))
- return false;
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + INPUT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read usb status rc = %d\n", rc);
- return false;
- }
-
- return !!(reg & (USBIN_9V | USBIN_UNREG | USBIN_LV));
-}
-
-static char *usb_type_str[] = {
- "SDP", /* bit 0 */
- "OTHER", /* bit 1 */
- "DCP", /* bit 2 */
- "CDP", /* bit 3 */
- "NONE", /* bit 4 error case */
-};
-
-#define N_TYPE_BITS 4
-#define TYPE_BITS_OFFSET 4
-
-static int get_type(u8 type_reg)
-{
- unsigned long type = type_reg;
- type >>= TYPE_BITS_OFFSET;
- return find_first_bit(&type, N_TYPE_BITS);
-}
-
-/* helper to return the string of USB type */
-static inline char *get_usb_type_name(int type)
-{
- return usb_type_str[type];
-}
-
-static enum power_supply_type usb_type_enum[] = {
- POWER_SUPPLY_TYPE_USB, /* bit 0 */
- POWER_SUPPLY_TYPE_USB_DCP, /* bit 1 */
- POWER_SUPPLY_TYPE_USB_DCP, /* bit 2 */
- POWER_SUPPLY_TYPE_USB_CDP, /* bit 3 */
- POWER_SUPPLY_TYPE_USB_DCP, /* bit 4 error case, report DCP */
-};
-
-/* helper to return enum power_supply_type of USB type */
-static inline enum power_supply_type get_usb_supply_type(int type)
-{
- return usb_type_enum[type];
-}
-
-static bool is_src_detect_high(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
- return false;
- }
- return reg &= USBIN_SRC_DET_BIT;
-}
-
-static void read_usb_type(struct smbchg_chip *chip, char **usb_type_name,
- enum power_supply_type *usb_supply_type)
-{
- int rc, type;
- u8 reg;
-
- if (!is_src_detect_high(chip)) {
- pr_smb(PR_MISC, "src det low\n");
- *usb_type_name = "Absent";
- *usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
- return;
- }
-
- rc = smbchg_read(chip, &reg, chip->misc_base + IDEV_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc);
- *usb_type_name = "Other";
- *usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
- return;
- }
- type = get_type(reg);
- *usb_type_name = get_usb_type_name(type);
- *usb_supply_type = get_usb_supply_type(type);
-}
-
-#define CHGR_STS 0x0E
-#define BATT_LESS_THAN_2V BIT(4)
-#define CHG_HOLD_OFF_BIT BIT(3)
-#define CHG_TYPE_MASK SMB_MASK(2, 1)
-#define CHG_TYPE_SHIFT 1
-#define BATT_NOT_CHG_VAL 0x0
-#define BATT_PRE_CHG_VAL 0x1
-#define BATT_FAST_CHG_VAL 0x2
-#define BATT_TAPER_CHG_VAL 0x3
-#define CHG_INHIBIT_BIT BIT(1)
-#define BAT_TCC_REACHED_BIT BIT(7)
-static int get_prop_batt_status(struct smbchg_chip *chip)
-{
- int rc, status = POWER_SUPPLY_STATUS_DISCHARGING;
- u8 reg = 0, chg_type;
- bool charger_present, chg_inhibit;
-
- charger_present = is_usb_present(chip) | is_dc_present(chip) |
- chip->hvdcp_3_det_ignore_uv;
- if (!charger_present)
- return POWER_SUPPLY_STATUS_DISCHARGING;
-
- rc = smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Unable to read RT_STS rc = %d\n", rc);
- return POWER_SUPPLY_STATUS_UNKNOWN;
- }
-
- if (reg & BAT_TCC_REACHED_BIT)
- return POWER_SUPPLY_STATUS_FULL;
-
- chg_inhibit = reg & CHG_INHIBIT_BIT;
- if (chg_inhibit)
- return POWER_SUPPLY_STATUS_FULL;
-
- rc = smbchg_read(chip, &reg, chip->chgr_base + CHGR_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc);
- return POWER_SUPPLY_STATUS_UNKNOWN;
- }
-
- if (reg & CHG_HOLD_OFF_BIT) {
- /*
- * when chg hold off happens the battery is
- * not charging
- */
- status = POWER_SUPPLY_STATUS_NOT_CHARGING;
- goto out;
- }
-
- chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT;
-
- if (chg_type == BATT_NOT_CHG_VAL && !chip->hvdcp_3_det_ignore_uv)
- status = POWER_SUPPLY_STATUS_DISCHARGING;
- else
- status = POWER_SUPPLY_STATUS_CHARGING;
-out:
- pr_smb_rt(PR_MISC, "CHGR_STS = 0x%02x\n", reg);
- return status;
-}
-
-#define BAT_PRES_STATUS 0x08
-#define BAT_PRES_BIT BIT(7)
-static int get_prop_batt_present(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->bat_if_base + BAT_PRES_STATUS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc);
- return 0;
- }
-
- return !!(reg & BAT_PRES_BIT);
-}
-
-static int get_prop_charge_type(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg, chg_type;
-
- rc = smbchg_read(chip, &reg, chip->chgr_base + CHGR_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc);
- return 0;
- }
-
- chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT;
- if (chg_type == BATT_NOT_CHG_VAL)
- return POWER_SUPPLY_CHARGE_TYPE_NONE;
- else if (chg_type == BATT_TAPER_CHG_VAL)
- return POWER_SUPPLY_CHARGE_TYPE_TAPER;
- else if (chg_type == BATT_FAST_CHG_VAL)
- return POWER_SUPPLY_CHARGE_TYPE_FAST;
- else if (chg_type == BATT_PRE_CHG_VAL)
- return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
-
- return POWER_SUPPLY_CHARGE_TYPE_NONE;
-}
-
-static int set_property_on_fg(struct smbchg_chip *chip,
- enum power_supply_property prop, int val)
-{
- int rc;
- union power_supply_propval ret = {0, };
-
- if (!chip->bms_psy && chip->bms_psy_name)
- chip->bms_psy =
- power_supply_get_by_name((char *)chip->bms_psy_name);
- if (!chip->bms_psy) {
- pr_smb(PR_STATUS, "no bms psy found\n");
- return -EINVAL;
- }
-
- ret.intval = val;
- rc = power_supply_set_property(chip->bms_psy, prop, &ret);
- if (rc)
- pr_smb(PR_STATUS,
- "bms psy does not allow updating prop %d rc = %d\n",
- prop, rc);
-
- return rc;
-}
-
-static int get_property_from_fg(struct smbchg_chip *chip,
- enum power_supply_property prop, int *val)
-{
- int rc;
- union power_supply_propval ret = {0, };
-
- if (!chip->bms_psy && chip->bms_psy_name)
- chip->bms_psy =
- power_supply_get_by_name((char *)chip->bms_psy_name);
- if (!chip->bms_psy) {
- pr_smb(PR_STATUS, "no bms psy found\n");
- return -EINVAL;
- }
-
- rc = power_supply_get_property(chip->bms_psy, prop, &ret);
- if (rc) {
- pr_smb(PR_STATUS,
- "bms psy doesn't support reading prop %d rc = %d\n",
- prop, rc);
- return rc;
- }
-
- *val = ret.intval;
- return rc;
-}
-
-#define DEFAULT_BATT_CAPACITY 50
-static int get_prop_batt_capacity(struct smbchg_chip *chip)
-{
- int capacity, rc;
-
- if (chip->fake_battery_soc >= 0)
- return chip->fake_battery_soc;
-
- rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CAPACITY, &capacity);
- if (rc) {
- pr_smb(PR_STATUS, "Couldn't get capacity rc = %d\n", rc);
- capacity = DEFAULT_BATT_CAPACITY;
- }
- return capacity;
-}
-
-#define DEFAULT_BATT_TEMP 200
-static int get_prop_batt_temp(struct smbchg_chip *chip)
-{
- int temp, rc;
-
- rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_TEMP, &temp);
- if (rc) {
- pr_smb(PR_STATUS, "Couldn't get temperature rc = %d\n", rc);
- temp = DEFAULT_BATT_TEMP;
- }
- return temp;
-}
-
-#define DEFAULT_BATT_CURRENT_NOW 0
-static int get_prop_batt_current_now(struct smbchg_chip *chip)
-{
- int ua, rc;
-
- rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CURRENT_NOW, &ua);
- if (rc) {
- pr_smb(PR_STATUS, "Couldn't get current rc = %d\n", rc);
- ua = DEFAULT_BATT_CURRENT_NOW;
- }
- return ua;
-}
-
-#define DEFAULT_BATT_VOLTAGE_NOW 0
-static int get_prop_batt_voltage_now(struct smbchg_chip *chip)
-{
- int uv, rc;
-
- rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_VOLTAGE_NOW, &uv);
- if (rc) {
- pr_smb(PR_STATUS, "Couldn't get voltage rc = %d\n", rc);
- uv = DEFAULT_BATT_VOLTAGE_NOW;
- }
- return uv;
-}
-
-#define DEFAULT_BATT_VOLTAGE_MAX_DESIGN 4200000
-static int get_prop_batt_voltage_max_design(struct smbchg_chip *chip)
-{
- int uv, rc;
-
- rc = get_property_from_fg(chip,
- POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, &uv);
- if (rc) {
- pr_smb(PR_STATUS, "Couldn't get voltage rc = %d\n", rc);
- uv = DEFAULT_BATT_VOLTAGE_MAX_DESIGN;
- }
- return uv;
-}
-
-static int get_prop_batt_health(struct smbchg_chip *chip)
-{
- if (chip->batt_hot)
- return POWER_SUPPLY_HEALTH_OVERHEAT;
- else if (chip->batt_cold)
- return POWER_SUPPLY_HEALTH_COLD;
- else if (chip->batt_warm)
- return POWER_SUPPLY_HEALTH_WARM;
- else if (chip->batt_cool)
- return POWER_SUPPLY_HEALTH_COOL;
- else
- return POWER_SUPPLY_HEALTH_GOOD;
-}
-
-static void get_property_from_typec(struct smbchg_chip *chip,
- enum power_supply_property property,
- union power_supply_propval *prop)
-{
- int rc;
-
- rc = power_supply_get_property(chip->typec_psy,
- property, prop);
- if (rc)
- pr_smb(PR_TYPEC,
- "typec psy doesn't support reading prop %d rc = %d\n",
- property, rc);
-}
-
-static void update_typec_status(struct smbchg_chip *chip)
-{
- union power_supply_propval type = {0, };
- union power_supply_propval capability = {0, };
-
- get_property_from_typec(chip, POWER_SUPPLY_PROP_TYPE, &type);
- if (type.intval != POWER_SUPPLY_TYPE_UNKNOWN) {
- get_property_from_typec(chip,
- POWER_SUPPLY_PROP_CURRENT_CAPABILITY,
- &capability);
- chip->typec_current_ma = capability.intval;
- pr_smb(PR_TYPEC, "SMB Type-C mode = %d, current=%d\n",
- type.intval, capability.intval);
- } else {
- pr_smb(PR_TYPEC,
- "typec detection not completed continuing with USB update\n");
- }
-}
-
-/*
- * finds the index of the closest value in the array. If there are two that
- * are equally close, the lower index will be returned
- */
-static int find_closest_in_array(const int *arr, int len, int val)
-{
- int i, closest = 0;
-
- if (len == 0)
- return closest;
- for (i = 0; i < len; i++)
- if (abs(val - arr[i]) < abs(val - arr[closest]))
- closest = i;
-
- return closest;
-}
-
-/* finds the index of the closest smaller value in the array. */
-static int find_smaller_in_array(const int *table, int val, int len)
-{
- int i;
-
- for (i = len - 1; i >= 0; i--) {
- if (val >= table[i])
- break;
- }
-
- return i;
-}
-
-static const int iterm_ma_table_8994[] = {
- 300,
- 50,
- 100,
- 150,
- 200,
- 250,
- 500,
- 600
-};
-
-static const int iterm_ma_table_8996[] = {
- 300,
- 50,
- 100,
- 150,
- 200,
- 250,
- 400,
- 500
-};
-
-static const int usb_ilim_ma_table_8994[] = {
- 300,
- 400,
- 450,
- 475,
- 500,
- 550,
- 600,
- 650,
- 700,
- 900,
- 950,
- 1000,
- 1100,
- 1200,
- 1400,
- 1450,
- 1500,
- 1600,
- 1800,
- 1850,
- 1880,
- 1910,
- 1930,
- 1950,
- 1970,
- 2000,
- 2050,
- 2100,
- 2300,
- 2400,
- 2500,
- 3000
-};
-
-static const int usb_ilim_ma_table_8996[] = {
- 300,
- 400,
- 500,
- 600,
- 700,
- 800,
- 900,
- 1000,
- 1100,
- 1200,
- 1300,
- 1400,
- 1450,
- 1500,
- 1550,
- 1600,
- 1700,
- 1800,
- 1900,
- 1950,
- 2000,
- 2050,
- 2100,
- 2200,
- 2300,
- 2400,
- 2500,
- 2600,
- 2700,
- 2800,
- 2900,
- 3000
-};
-
-static int dc_ilim_ma_table_8994[] = {
- 300,
- 400,
- 450,
- 475,
- 500,
- 550,
- 600,
- 650,
- 700,
- 900,
- 950,
- 1000,
- 1100,
- 1200,
- 1400,
- 1450,
- 1500,
- 1600,
- 1800,
- 1850,
- 1880,
- 1910,
- 1930,
- 1950,
- 1970,
- 2000,
-};
-
-static int dc_ilim_ma_table_8996[] = {
- 300,
- 400,
- 500,
- 600,
- 700,
- 800,
- 900,
- 1000,
- 1100,
- 1200,
- 1300,
- 1400,
- 1450,
- 1500,
- 1550,
- 1600,
- 1700,
- 1800,
- 1900,
- 1950,
- 2000,
- 2050,
- 2100,
- 2200,
- 2300,
- 2400,
-};
-
-static const int fcc_comp_table_8994[] = {
- 250,
- 700,
- 900,
- 1200,
-};
-
-static const int fcc_comp_table_8996[] = {
- 250,
- 1100,
- 1200,
- 1500,
-};
-
-static const int aicl_rerun_period[] = {
- 45,
- 90,
- 180,
- 360,
-};
-
-static const int aicl_rerun_period_schg_lite[] = {
- 3, /* 2.8s */
- 6, /* 5.6s */
- 11, /* 11.3s */
- 23, /* 22.5s */
- 45,
- 90,
- 180,
- 360,
-};
-
-static void use_pmi8994_tables(struct smbchg_chip *chip)
-{
- chip->tables.usb_ilim_ma_table = usb_ilim_ma_table_8994;
- chip->tables.usb_ilim_ma_len = ARRAY_SIZE(usb_ilim_ma_table_8994);
- chip->tables.dc_ilim_ma_table = dc_ilim_ma_table_8994;
- chip->tables.dc_ilim_ma_len = ARRAY_SIZE(dc_ilim_ma_table_8994);
- chip->tables.iterm_ma_table = iterm_ma_table_8994;
- chip->tables.iterm_ma_len = ARRAY_SIZE(iterm_ma_table_8994);
- chip->tables.fcc_comp_table = fcc_comp_table_8994;
- chip->tables.fcc_comp_len = ARRAY_SIZE(fcc_comp_table_8994);
- chip->tables.rchg_thr_mv = 200;
- chip->tables.aicl_rerun_period_table = aicl_rerun_period;
- chip->tables.aicl_rerun_period_len = ARRAY_SIZE(aicl_rerun_period);
-}
-
-static void use_pmi8996_tables(struct smbchg_chip *chip)
-{
- chip->tables.usb_ilim_ma_table = usb_ilim_ma_table_8996;
- chip->tables.usb_ilim_ma_len = ARRAY_SIZE(usb_ilim_ma_table_8996);
- chip->tables.dc_ilim_ma_table = dc_ilim_ma_table_8996;
- chip->tables.dc_ilim_ma_len = ARRAY_SIZE(dc_ilim_ma_table_8996);
- chip->tables.iterm_ma_table = iterm_ma_table_8996;
- chip->tables.iterm_ma_len = ARRAY_SIZE(iterm_ma_table_8996);
- chip->tables.fcc_comp_table = fcc_comp_table_8996;
- chip->tables.fcc_comp_len = ARRAY_SIZE(fcc_comp_table_8996);
- chip->tables.rchg_thr_mv = 150;
- chip->tables.aicl_rerun_period_table = aicl_rerun_period;
- chip->tables.aicl_rerun_period_len = ARRAY_SIZE(aicl_rerun_period);
-}
-
-#define CMD_CHG_REG 0x42
-#define EN_BAT_CHG_BIT BIT(1)
-static int smbchg_charging_en(struct smbchg_chip *chip, bool en)
-{
- /* The en bit is configured active low */
- return smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
- EN_BAT_CHG_BIT, en ? 0 : EN_BAT_CHG_BIT);
-}
-
-#define CMD_IL 0x40
-#define USBIN_SUSPEND_BIT BIT(4)
-#define CURRENT_100_MA 100
-#define CURRENT_150_MA 150
-#define CURRENT_500_MA 500
-#define CURRENT_900_MA 900
-#define CURRENT_1500_MA 1500
-#define SUSPEND_CURRENT_MA 2
-#define ICL_OVERRIDE_BIT BIT(2)
-static int smbchg_usb_suspend(struct smbchg_chip *chip, bool suspend)
-{
- int rc;
-
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
- USBIN_SUSPEND_BIT, suspend ? USBIN_SUSPEND_BIT : 0);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't set usb suspend rc = %d\n", rc);
- return rc;
-}
-
-#define DCIN_SUSPEND_BIT BIT(3)
-static int smbchg_dc_suspend(struct smbchg_chip *chip, bool suspend)
-{
- int rc = 0;
-
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
- DCIN_SUSPEND_BIT, suspend ? DCIN_SUSPEND_BIT : 0);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't set dc suspend rc = %d\n", rc);
- return rc;
-}
-
-#define IL_CFG 0xF2
-#define DCIN_INPUT_MASK SMB_MASK(4, 0)
-static int smbchg_set_dc_current_max(struct smbchg_chip *chip, int current_ma)
-{
- int i;
- u8 dc_cur_val;
-
- i = find_smaller_in_array(chip->tables.dc_ilim_ma_table,
- current_ma, chip->tables.dc_ilim_ma_len);
-
- if (i < 0) {
- dev_err(chip->dev, "Cannot find %dma current_table\n",
- current_ma);
- return -EINVAL;
- }
-
- chip->dc_max_current_ma = chip->tables.dc_ilim_ma_table[i];
- dc_cur_val = i & DCIN_INPUT_MASK;
-
- pr_smb(PR_STATUS, "dc current set to %d mA\n",
- chip->dc_max_current_ma);
- return smbchg_sec_masked_write(chip, chip->dc_chgpth_base + IL_CFG,
- DCIN_INPUT_MASK, dc_cur_val);
-}
-
-#define AICL_WL_SEL_CFG 0xF5
-#define AICL_WL_SEL_MASK SMB_MASK(1, 0)
-#define AICL_WL_SEL_SCHG_LITE_MASK SMB_MASK(2, 0)
-static int smbchg_set_aicl_rerun_period_s(struct smbchg_chip *chip,
- int period_s)
-{
- int i;
- u8 reg, mask;
-
- i = find_smaller_in_array(chip->tables.aicl_rerun_period_table,
- period_s, chip->tables.aicl_rerun_period_len);
-
- if (i < 0) {
- dev_err(chip->dev, "Cannot find %ds in aicl rerun period\n",
- period_s);
- return -EINVAL;
- }
-
- if (chip->schg_version == QPNP_SCHG_LITE)
- mask = AICL_WL_SEL_SCHG_LITE_MASK;
- else
- mask = AICL_WL_SEL_MASK;
-
- reg = i & mask;
-
- pr_smb(PR_STATUS, "aicl rerun period set to %ds\n",
- chip->tables.aicl_rerun_period_table[i]);
- return smbchg_sec_masked_write(chip,
- chip->dc_chgpth_base + AICL_WL_SEL_CFG,
- mask, reg);
-}
-
-static struct power_supply *get_parallel_psy(struct smbchg_chip *chip)
-{
- if (!chip->parallel.avail)
- return NULL;
- if (chip->parallel.psy)
- return chip->parallel.psy;
- chip->parallel.psy = power_supply_get_by_name("usb-parallel");
- if (!chip->parallel.psy)
- pr_smb(PR_STATUS, "parallel charger not found\n");
- return chip->parallel.psy;
-}
-
-static void smbchg_usb_update_online_work(struct work_struct *work)
-{
- struct smbchg_chip *chip = container_of(work,
- struct smbchg_chip,
- usb_set_online_work);
- bool user_enabled = !get_client_vote(chip->usb_suspend_votable,
- USER_EN_VOTER);
- int online;
-
- online = user_enabled && chip->usb_present && !chip->very_weak_charger;
-
- mutex_lock(&chip->usb_set_online_lock);
- if (chip->usb_online != online) {
- pr_smb(PR_MISC, "setting usb psy online = %d\n", online);
- chip->usb_online = online;
- power_supply_changed(chip->usb_psy);
- }
- mutex_unlock(&chip->usb_set_online_lock);
-}
-
-#define CHGPTH_CFG 0xF4
-#define CFG_USB_2_3_SEL_BIT BIT(7)
-#define CFG_USB_2 0
-#define CFG_USB_3 BIT(7)
-#define USBIN_INPUT_MASK SMB_MASK(4, 0)
-#define USBIN_MODE_CHG_BIT BIT(0)
-#define USBIN_LIMITED_MODE 0
-#define USBIN_HC_MODE BIT(0)
-#define USB51_MODE_BIT BIT(1)
-#define USB51_100MA 0
-#define USB51_500MA BIT(1)
-static int smbchg_set_high_usb_chg_current(struct smbchg_chip *chip,
- int current_ma)
-{
- int i, rc;
- u8 usb_cur_val;
-
- if (current_ma == CURRENT_100_MA) {
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- CFG_USB_2_3_SEL_BIT, CFG_USB_2);
- if (rc < 0) {
- pr_err("Couldn't set CFG_USB_2 rc=%d\n", rc);
- return rc;
- }
-
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
- USBIN_MODE_CHG_BIT | USB51_MODE_BIT | ICL_OVERRIDE_BIT,
- USBIN_LIMITED_MODE | USB51_100MA | ICL_OVERRIDE_BIT);
- if (rc < 0) {
- pr_err("Couldn't set ICL_OVERRIDE rc=%d\n", rc);
- return rc;
- }
-
- pr_smb(PR_STATUS,
- "Forcing 100mA current limit\n");
- chip->usb_max_current_ma = CURRENT_100_MA;
- return rc;
- }
-
- i = find_smaller_in_array(chip->tables.usb_ilim_ma_table,
- current_ma, chip->tables.usb_ilim_ma_len);
- if (i < 0) {
- dev_err(chip->dev,
- "Cannot find %dma current_table using %d\n",
- current_ma, CURRENT_150_MA);
-
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- CFG_USB_2_3_SEL_BIT, CFG_USB_3);
- rc |= smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
- USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
- USBIN_LIMITED_MODE | USB51_100MA);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't set %dmA rc=%d\n",
- CURRENT_150_MA, rc);
- else
- chip->usb_max_current_ma = 150;
- return rc;
- }
-
- usb_cur_val = i & USBIN_INPUT_MASK;
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + IL_CFG,
- USBIN_INPUT_MASK, usb_cur_val);
- if (rc < 0) {
- dev_err(chip->dev, "cannot write to config c rc = %d\n", rc);
- return rc;
- }
-
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
- USBIN_MODE_CHG_BIT, USBIN_HC_MODE);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't write cfg 5 rc = %d\n", rc);
- chip->usb_max_current_ma = chip->tables.usb_ilim_ma_table[i];
- return rc;
-}
-
-/* if APSD results are used
- * if SDP is detected it will look at 500mA setting
- * if set it will draw 500mA
- * if unset it will draw 100mA
- * if CDP/DCP it will look at 0x0C setting
- * i.e. values in 0x41[1, 0] does not matter
- */
-static int smbchg_set_usb_current_max(struct smbchg_chip *chip,
- int current_ma)
-{
- int rc = 0;
-
- /*
- * if the battery is not present, do not allow the usb ICL to lower in
- * order to avoid browning out the device during a hotswap.
- */
- if (!chip->batt_present && current_ma < chip->usb_max_current_ma) {
- pr_info_ratelimited("Ignoring usb current->%d, battery is absent\n",
- current_ma);
- return 0;
- }
- pr_smb(PR_STATUS, "USB current_ma = %d\n", current_ma);
-
- if (current_ma <= SUSPEND_CURRENT_MA) {
- /* suspend the usb if current <= 2mA */
- rc = vote(chip->usb_suspend_votable, USB_EN_VOTER, true, 0);
- chip->usb_max_current_ma = 0;
- goto out;
- } else {
- rc = vote(chip->usb_suspend_votable, USB_EN_VOTER, false, 0);
- }
-
- switch (chip->usb_supply_type) {
- case POWER_SUPPLY_TYPE_USB:
- if ((current_ma < CURRENT_150_MA) &&
- (chip->wa_flags & SMBCHG_USB100_WA))
- current_ma = CURRENT_150_MA;
-
- if (current_ma < CURRENT_150_MA) {
- /* force 100mA */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- CFG_USB_2_3_SEL_BIT, CFG_USB_2);
- if (rc < 0) {
- pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
- goto out;
- }
- rc = smbchg_masked_write(chip,
- chip->usb_chgpth_base + CMD_IL,
- USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
- USBIN_LIMITED_MODE | USB51_100MA);
- if (rc < 0) {
- pr_err("Couldn't set CMD_IL rc = %d\n", rc);
- goto out;
- }
- chip->usb_max_current_ma = 100;
- }
- /* specific current values */
- if (current_ma == CURRENT_150_MA) {
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- CFG_USB_2_3_SEL_BIT, CFG_USB_3);
- if (rc < 0) {
- pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
- goto out;
- }
- rc = smbchg_masked_write(chip,
- chip->usb_chgpth_base + CMD_IL,
- USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
- USBIN_LIMITED_MODE | USB51_100MA);
- if (rc < 0) {
- pr_err("Couldn't set CMD_IL rc = %d\n", rc);
- goto out;
- }
- chip->usb_max_current_ma = 150;
- }
- if (current_ma == CURRENT_500_MA) {
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- CFG_USB_2_3_SEL_BIT, CFG_USB_2);
- if (rc < 0) {
- pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
- goto out;
- }
- rc = smbchg_masked_write(chip,
- chip->usb_chgpth_base + CMD_IL,
- USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
- USBIN_LIMITED_MODE | USB51_500MA);
- if (rc < 0) {
- pr_err("Couldn't set CMD_IL rc = %d\n", rc);
- goto out;
- }
- chip->usb_max_current_ma = 500;
- }
- if (current_ma == CURRENT_900_MA) {
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- CFG_USB_2_3_SEL_BIT, CFG_USB_3);
- if (rc < 0) {
- pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
- goto out;
- }
- rc = smbchg_masked_write(chip,
- chip->usb_chgpth_base + CMD_IL,
- USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
- USBIN_LIMITED_MODE | USB51_500MA);
- if (rc < 0) {
- pr_err("Couldn't set CMD_IL rc = %d\n", rc);
- goto out;
- }
- chip->usb_max_current_ma = 900;
- }
- break;
- case POWER_SUPPLY_TYPE_USB_CDP:
- if (current_ma < CURRENT_1500_MA) {
- /* use override for CDP */
- rc = smbchg_masked_write(chip,
- chip->usb_chgpth_base + CMD_IL,
- ICL_OVERRIDE_BIT, ICL_OVERRIDE_BIT);
- if (rc < 0)
- pr_err("Couldn't set override rc = %d\n", rc);
- }
- /* fall through */
- default:
- rc = smbchg_set_high_usb_chg_current(chip, current_ma);
- if (rc < 0)
- pr_err("Couldn't set %dmA rc = %d\n", current_ma, rc);
- break;
- }
-
-out:
- pr_smb(PR_STATUS, "usb type = %d current set to %d mA\n",
- chip->usb_supply_type, chip->usb_max_current_ma);
- return rc;
-}
-
-#define USBIN_HVDCP_STS 0x0C
-#define USBIN_HVDCP_SEL_BIT BIT(4)
-#define USBIN_HVDCP_SEL_9V_BIT BIT(1)
-#define SCHG_LITE_USBIN_HVDCP_SEL_9V_BIT BIT(2)
-#define SCHG_LITE_USBIN_HVDCP_SEL_BIT BIT(0)
-static int smbchg_get_min_parallel_current_ma(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg, hvdcp_sel, hvdcp_sel_9v;
-
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + USBIN_HVDCP_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read usb status rc = %d\n", rc);
- return 0;
- }
- if (chip->schg_version == QPNP_SCHG_LITE) {
- hvdcp_sel = SCHG_LITE_USBIN_HVDCP_SEL_BIT;
- hvdcp_sel_9v = SCHG_LITE_USBIN_HVDCP_SEL_9V_BIT;
- } else {
- hvdcp_sel = USBIN_HVDCP_SEL_BIT;
- hvdcp_sel_9v = USBIN_HVDCP_SEL_9V_BIT;
- }
-
- if ((reg & hvdcp_sel) && (reg & hvdcp_sel_9v))
- return chip->parallel.min_9v_current_thr_ma;
- return chip->parallel.min_current_thr_ma;
-}
-
-static bool is_hvdcp_present(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg, hvdcp_sel;
-
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + USBIN_HVDCP_STS, 1);
- if (rc < 0) {
- pr_err("Couldn't read hvdcp status rc = %d\n", rc);
- return false;
- }
-
- pr_smb(PR_STATUS, "HVDCP_STS = 0x%02x\n", reg);
- /*
- * If a valid HVDCP is detected, notify it to the usb_psy only
- * if USB is still present.
- */
- if (chip->schg_version == QPNP_SCHG_LITE)
- hvdcp_sel = SCHG_LITE_USBIN_HVDCP_SEL_BIT;
- else
- hvdcp_sel = USBIN_HVDCP_SEL_BIT;
-
- if ((reg & hvdcp_sel) && is_usb_present(chip))
- return true;
-
- return false;
-}
-
-#define FCC_CFG 0xF2
-#define FCC_500MA_VAL 0x4
-#define FCC_MASK SMB_MASK(4, 0)
-static int smbchg_set_fastchg_current_raw(struct smbchg_chip *chip,
- int current_ma)
-{
- int i, rc;
- u8 cur_val;
-
- /* the fcc enumerations are the same as the usb currents */
- i = find_smaller_in_array(chip->tables.usb_ilim_ma_table,
- current_ma, chip->tables.usb_ilim_ma_len);
- if (i < 0) {
- dev_err(chip->dev,
- "Cannot find %dma current_table using %d\n",
- current_ma, CURRENT_500_MA);
-
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CFG,
- FCC_MASK,
- FCC_500MA_VAL);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't set %dmA rc=%d\n",
- CURRENT_500_MA, rc);
- else
- chip->fastchg_current_ma = 500;
- return rc;
- }
-
- if (chip->tables.usb_ilim_ma_table[i] == chip->fastchg_current_ma) {
- pr_smb(PR_STATUS, "skipping fastchg current request: %d\n",
- chip->fastchg_current_ma);
- return 0;
- }
-
- cur_val = i & FCC_MASK;
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CFG,
- FCC_MASK, cur_val);
- if (rc < 0) {
- dev_err(chip->dev, "cannot write to fcc cfg rc = %d\n", rc);
- return rc;
- }
- pr_smb(PR_STATUS, "fastcharge current requested %d, set to %d\n",
- current_ma, chip->tables.usb_ilim_ma_table[cur_val]);
-
- chip->fastchg_current_ma = chip->tables.usb_ilim_ma_table[cur_val];
- return rc;
-}
-
-#define ICL_STS_1_REG 0x7
-#define ICL_STS_2_REG 0x9
-#define ICL_STS_MASK 0x1F
-#define AICL_SUSP_BIT BIT(6)
-#define AICL_STS_BIT BIT(5)
-#define USBIN_SUSPEND_STS_BIT BIT(3)
-#define USBIN_ACTIVE_PWR_SRC_BIT BIT(1)
-#define DCIN_ACTIVE_PWR_SRC_BIT BIT(0)
-#define PARALLEL_REENABLE_TIMER_MS 1000
-#define PARALLEL_CHG_THRESHOLD_CURRENT 1800
-static bool smbchg_is_usbin_active_pwr_src(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + ICL_STS_2_REG, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Could not read usb icl sts 2: %d\n", rc);
- return false;
- }
-
- return !(reg & USBIN_SUSPEND_STS_BIT)
- && (reg & USBIN_ACTIVE_PWR_SRC_BIT);
-}
-
-static int smbchg_parallel_usb_charging_en(struct smbchg_chip *chip, bool en)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval pval = {0, };
-
- if (!parallel_psy || !chip->parallel_charger_detected)
- return 0;
-
- pval.intval = en;
- return power_supply_set_property(parallel_psy,
- POWER_SUPPLY_PROP_CHARGING_ENABLED, &pval);
-}
-
-#define ESR_PULSE_CURRENT_DELTA_MA 200
-static int smbchg_sw_esr_pulse_en(struct smbchg_chip *chip, bool en)
-{
- int rc, fg_current_now, icl_ma;
-
- rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CURRENT_NOW,
- &fg_current_now);
- if (rc) {
- pr_smb(PR_STATUS, "bms psy does not support OCV\n");
- return 0;
- }
-
- icl_ma = max(chip->iterm_ma + ESR_PULSE_CURRENT_DELTA_MA,
- fg_current_now - ESR_PULSE_CURRENT_DELTA_MA);
- rc = vote(chip->fcc_votable, ESR_PULSE_FCC_VOTER, en, icl_ma);
- if (rc < 0) {
- pr_err("Couldn't Vote FCC en = %d rc = %d\n", en, rc);
- return rc;
- }
- rc = smbchg_parallel_usb_charging_en(chip, !en);
- return rc;
-}
-
-#define USB_AICL_CFG 0xF3
-#define AICL_EN_BIT BIT(2)
-static void smbchg_rerun_aicl(struct smbchg_chip *chip)
-{
- pr_smb(PR_STATUS, "Rerunning AICL...\n");
- smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, 0);
- /* Add a delay so that AICL successfully clears */
- msleep(50);
- smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, AICL_EN_BIT);
-}
-
-static void taper_irq_en(struct smbchg_chip *chip, bool en)
-{
- mutex_lock(&chip->taper_irq_lock);
- if (en != chip->taper_irq_enabled) {
- if (en) {
- enable_irq(chip->taper_irq);
- enable_irq_wake(chip->taper_irq);
- } else {
- disable_irq_wake(chip->taper_irq);
- disable_irq_nosync(chip->taper_irq);
- }
- chip->taper_irq_enabled = en;
- }
- mutex_unlock(&chip->taper_irq_lock);
-}
-
-static int smbchg_get_aicl_level_ma(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + ICL_STS_1_REG, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Could not read usb icl sts 1: %d\n", rc);
- return 0;
- }
- if (reg & AICL_SUSP_BIT) {
- pr_warn("AICL suspended: %02x\n", reg);
- return 0;
- }
- reg &= ICL_STS_MASK;
- if (reg >= chip->tables.usb_ilim_ma_len) {
- pr_warn("invalid AICL value: %02x\n", reg);
- return 0;
- }
- return chip->tables.usb_ilim_ma_table[reg];
-}
-
-static void smbchg_parallel_usb_disable(struct smbchg_chip *chip)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval pval = {0, };
- int fcc_ma, usb_icl_ma;
-
- if (!parallel_psy || !chip->parallel_charger_detected)
- return;
- pr_smb(PR_STATUS, "disabling parallel charger\n");
- chip->parallel.last_disabled = ktime_get_boottime();
- taper_irq_en(chip, false);
- chip->parallel.initial_aicl_ma = 0;
- chip->parallel.current_max_ma = 0;
- pval.intval = SUSPEND_CURRENT_MA * 1000;
- power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_CURRENT_MAX,
- &pval);
-
- pval.intval = false;
- power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_PRESENT,
- &pval);
-
- fcc_ma = get_effective_result_locked(chip->fcc_votable);
- usb_icl_ma = get_effective_result_locked(chip->usb_icl_votable);
- if (fcc_ma < 0)
- pr_err("no voters for fcc, skip it\n");
- else
- smbchg_set_fastchg_current_raw(chip, fcc_ma);
-
- if (usb_icl_ma < 0)
- pr_err("no voters for usb_icl, skip it\n");
- else
- smbchg_set_usb_current_max(chip, usb_icl_ma);
-
- smbchg_rerun_aicl(chip);
-}
-
-#define PARALLEL_TAPER_MAX_TRIES 3
-#define PARALLEL_FCC_PERCENT_REDUCTION 75
-#define MINIMUM_PARALLEL_FCC_MA 500
-#define CHG_ERROR_BIT BIT(0)
-#define BAT_TAPER_MODE_BIT BIT(6)
-static void smbchg_parallel_usb_taper(struct smbchg_chip *chip)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval pval = {0, };
- int parallel_fcc_ma, tries = 0;
- u8 reg = 0;
-
- if (!parallel_psy || !chip->parallel_charger_detected)
- return;
-
- smbchg_stay_awake(chip, PM_PARALLEL_TAPER);
-try_again:
- mutex_lock(&chip->parallel.lock);
- if (chip->parallel.current_max_ma == 0) {
- pr_smb(PR_STATUS, "Not parallel charging, skipping\n");
- goto done;
- }
- power_supply_get_property(parallel_psy,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
- tries += 1;
- parallel_fcc_ma = pval.intval / 1000;
- pr_smb(PR_STATUS, "try #%d parallel charger fcc = %d\n",
- tries, parallel_fcc_ma);
- if (parallel_fcc_ma < MINIMUM_PARALLEL_FCC_MA
- || tries > PARALLEL_TAPER_MAX_TRIES) {
- smbchg_parallel_usb_disable(chip);
- goto done;
- }
- pval.intval = ((parallel_fcc_ma
- * PARALLEL_FCC_PERCENT_REDUCTION) / 100);
- pr_smb(PR_STATUS, "reducing FCC of parallel charger to %d\n",
- pval.intval);
- /* Change it to uA */
- pval.intval *= 1000;
- power_supply_set_property(parallel_psy,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
- /*
- * sleep here for 100 ms in order to make sure the charger has a chance
- * to go back into constant current charging
- */
- mutex_unlock(&chip->parallel.lock);
- msleep(100);
-
- mutex_lock(&chip->parallel.lock);
- if (chip->parallel.current_max_ma == 0) {
- pr_smb(PR_STATUS, "Not parallel charging, skipping\n");
- goto done;
- }
- smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
- if (reg & BAT_TAPER_MODE_BIT) {
- mutex_unlock(&chip->parallel.lock);
- goto try_again;
- }
- taper_irq_en(chip, true);
-done:
- mutex_unlock(&chip->parallel.lock);
- smbchg_relax(chip, PM_PARALLEL_TAPER);
-}
-
-static void smbchg_parallel_usb_enable(struct smbchg_chip *chip,
- int total_current_ma)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval pval = {0, };
- int new_parallel_cl_ma, set_parallel_cl_ma, new_pmi_cl_ma, rc;
- int current_table_index, target_icl_ma;
- int fcc_ma, main_fastchg_current_ma;
- int target_parallel_fcc_ma, supplied_parallel_fcc_ma;
- int parallel_chg_fcc_percent;
-
- if (!parallel_psy || !chip->parallel_charger_detected)
- return;
-
- pr_smb(PR_STATUS, "Attempting to enable parallel charger\n");
- pval.intval = chip->vfloat_mv + 50;
- rc = power_supply_set_property(parallel_psy,
- POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set Vflt on parallel psy rc: %d\n", rc);
- return;
- }
- /* Set USB ICL */
- target_icl_ma = get_effective_result_locked(chip->usb_icl_votable);
- if (target_icl_ma < 0) {
- pr_err("no voters for usb_icl, skip it\n");
- return;
- }
- new_parallel_cl_ma = total_current_ma
- * (100 - smbchg_main_chg_icl_percent) / 100;
- taper_irq_en(chip, true);
-
- pval.intval = true;
- power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_PRESENT,
- &pval);
-
- pval.intval = new_parallel_cl_ma * 1000;
- power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_CURRENT_MAX,
- &pval);
-
- /* read back the real amount of current we are getting */
- power_supply_get_property(parallel_psy,
- POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
- set_parallel_cl_ma = pval.intval / 1000;
- chip->parallel.current_max_ma = new_parallel_cl_ma;
- pr_smb(PR_MISC, "Requested ICL = %d from parallel, got %d\n",
- new_parallel_cl_ma, set_parallel_cl_ma);
- new_pmi_cl_ma = max(0, target_icl_ma - set_parallel_cl_ma);
- pr_smb(PR_STATUS, "New Total USB current = %d[%d, %d]\n",
- total_current_ma, new_pmi_cl_ma,
- set_parallel_cl_ma);
- smbchg_set_usb_current_max(chip, new_pmi_cl_ma);
-
- /* begin splitting the fast charge current */
- fcc_ma = get_effective_result_locked(chip->fcc_votable);
- if (fcc_ma < 0) {
- pr_err("no voters for fcc, skip it\n");
- return;
- }
- parallel_chg_fcc_percent = 100 - smbchg_main_chg_fcc_percent;
- target_parallel_fcc_ma = (fcc_ma * parallel_chg_fcc_percent) / 100;
- pval.intval = target_parallel_fcc_ma * 1000;
- power_supply_set_property(parallel_psy,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
- /* check how much actual current is supplied by the parallel charger */
- power_supply_get_property(parallel_psy,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
- supplied_parallel_fcc_ma = pval.intval / 1000;
- pr_smb(PR_MISC, "Requested FCC = %d from parallel, got %d\n",
- target_parallel_fcc_ma, supplied_parallel_fcc_ma);
-
- /* then for the main charger, use the left over FCC */
- current_table_index = find_smaller_in_array(
- chip->tables.usb_ilim_ma_table,
- fcc_ma - supplied_parallel_fcc_ma,
- chip->tables.usb_ilim_ma_len);
- main_fastchg_current_ma =
- chip->tables.usb_ilim_ma_table[current_table_index];
- smbchg_set_fastchg_current_raw(chip, main_fastchg_current_ma);
- pr_smb(PR_STATUS, "FCC = %d[%d, %d]\n", fcc_ma, main_fastchg_current_ma,
- supplied_parallel_fcc_ma);
-
- chip->parallel.enabled_once = true;
-
- return;
-}
-
-static bool smbchg_is_parallel_usb_ok(struct smbchg_chip *chip,
- int *ret_total_current_ma)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval pval = {0, };
- int min_current_thr_ma, rc, type;
- int total_current_ma, current_limit_ma, parallel_cl_ma;
- ktime_t kt_since_last_disable;
- u8 reg;
- int fcc_ma = get_effective_result_locked(chip->fcc_votable);
- const char *fcc_voter
- = get_effective_client_locked(chip->fcc_votable);
- int usb_icl_ma = get_effective_result_locked(chip->usb_icl_votable);
-
- if (!parallel_psy || !smbchg_parallel_en
- || !chip->parallel_charger_detected) {
- pr_smb(PR_STATUS, "Parallel charging not enabled\n");
- return false;
- }
-
- if (fcc_ma < 0) {
- pr_err("no voters for fcc! Can't enable parallel\n");
- return false;
- }
- if (usb_icl_ma < 0) {
- pr_err("no voters for usb_icl, Can't enable parallel\n");
- return false;
- }
-
- kt_since_last_disable = ktime_sub(ktime_get_boottime(),
- chip->parallel.last_disabled);
- if (chip->parallel.current_max_ma == 0
- && chip->parallel.enabled_once
- && ktime_to_ms(kt_since_last_disable)
- < PARALLEL_REENABLE_TIMER_MS) {
- pr_smb(PR_STATUS, "Only been %lld since disable, skipping\n",
- ktime_to_ms(kt_since_last_disable));
- return false;
- }
-
- /*
- * If the battery is not present, try not to change parallel charging
- * from OFF to ON or from ON to OFF, as it could cause the device to
- * brown out in the instant that the USB settings are changed.
- *
- * Only allow parallel charging check to report false (thereby turnin
- * off parallel charging) if the battery is still there, or if parallel
- * charging is disabled in the first place.
- */
- if (get_prop_charge_type(chip) != POWER_SUPPLY_CHARGE_TYPE_FAST
- && (get_prop_batt_present(chip)
- || chip->parallel.current_max_ma == 0)) {
- pr_smb(PR_STATUS, "Not in fast charge, skipping\n");
- return false;
- }
-
- if (get_prop_batt_health(chip) != POWER_SUPPLY_HEALTH_GOOD) {
- pr_smb(PR_STATUS, "JEITA active, skipping\n");
- return false;
- }
-
- rc = smbchg_read(chip, &reg, chip->misc_base + IDEV_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc);
- return false;
- }
-
- type = get_type(reg);
- if (get_usb_supply_type(type) == POWER_SUPPLY_TYPE_USB_CDP) {
- pr_smb(PR_STATUS, "CDP adapter, skipping\n");
- return false;
- }
-
- if (get_usb_supply_type(type) == POWER_SUPPLY_TYPE_USB) {
- pr_smb(PR_STATUS, "SDP adapter, skipping\n");
- return false;
- }
-
- /*
- * If USBIN is suspended or not the active power source, do not enable
- * parallel charging. The device may be charging off of DCIN.
- */
- if (!smbchg_is_usbin_active_pwr_src(chip)) {
- pr_smb(PR_STATUS, "USB not active power source: %02x\n", reg);
- return false;
- }
-
- min_current_thr_ma = smbchg_get_min_parallel_current_ma(chip);
- if (min_current_thr_ma <= 0) {
- pr_smb(PR_STATUS, "parallel charger unavailable for thr: %d\n",
- min_current_thr_ma);
- return false;
- }
-
- if (usb_icl_ma < min_current_thr_ma) {
- pr_smb(PR_STATUS, "Weak USB chg skip enable: %d < %d\n",
- usb_icl_ma, min_current_thr_ma);
- return false;
- }
-
- if (!fcc_voter)
- return false;
- /*
- * Suspend the parallel charger if the charging current is < 1800 mA
- * and is not because of an ESR pulse.
- */
- if ((strcmp(fcc_voter, ESR_PULSE_FCC_VOTER) == 0)
- && fcc_ma < PARALLEL_CHG_THRESHOLD_CURRENT) {
- pr_smb(PR_STATUS, "FCC %d lower than %d\n",
- fcc_ma,
- PARALLEL_CHG_THRESHOLD_CURRENT);
- return false;
- }
-
- current_limit_ma = smbchg_get_aicl_level_ma(chip);
- if (current_limit_ma <= 0)
- return false;
-
- if (chip->parallel.initial_aicl_ma == 0) {
- if (current_limit_ma < min_current_thr_ma) {
- pr_smb(PR_STATUS, "Initial AICL very low: %d < %d\n",
- current_limit_ma, min_current_thr_ma);
- return false;
- }
- chip->parallel.initial_aicl_ma = current_limit_ma;
- }
-
- power_supply_get_property(parallel_psy,
- POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
- parallel_cl_ma = pval.intval / 1000;
- /*
- * Read back the real amount of current we are getting
- * Treat 2mA as 0 because that is the suspend current setting
- */
- if (parallel_cl_ma <= SUSPEND_CURRENT_MA)
- parallel_cl_ma = 0;
-
- /*
- * Set the parallel charge path's input current limit (ICL)
- * to the total current / 2
- */
- total_current_ma = min(current_limit_ma + parallel_cl_ma, usb_icl_ma);
-
- if (total_current_ma < chip->parallel.initial_aicl_ma
- - chip->parallel.allowed_lowering_ma) {
- pr_smb(PR_STATUS,
- "Total current reduced a lot: %d (%d + %d) < %d - %d\n",
- total_current_ma,
- current_limit_ma, parallel_cl_ma,
- chip->parallel.initial_aicl_ma,
- chip->parallel.allowed_lowering_ma);
- return false;
- }
-
- *ret_total_current_ma = total_current_ma;
- return true;
-}
-
-#define PARALLEL_CHARGER_EN_DELAY_MS 500
-static void smbchg_parallel_usb_en_work(struct work_struct *work)
-{
- struct smbchg_chip *chip = container_of(work,
- struct smbchg_chip,
- parallel_en_work.work);
- int previous_aicl_ma, total_current_ma, aicl_ma;
- bool in_progress;
-
- /* do a check to see if the aicl is stable */
- previous_aicl_ma = smbchg_get_aicl_level_ma(chip);
- msleep(PARALLEL_CHARGER_EN_DELAY_MS);
- aicl_ma = smbchg_get_aicl_level_ma(chip);
- if (previous_aicl_ma == aicl_ma) {
- pr_smb(PR_STATUS, "AICL at %d\n", aicl_ma);
- } else {
- pr_smb(PR_STATUS,
- "AICL changed [%d -> %d], recheck %d ms\n",
- previous_aicl_ma, aicl_ma,
- PARALLEL_CHARGER_EN_DELAY_MS);
- goto recheck;
- }
-
- mutex_lock(&chip->parallel.lock);
- in_progress = (chip->parallel.current_max_ma != 0);
- if (smbchg_is_parallel_usb_ok(chip, &total_current_ma)) {
- smbchg_parallel_usb_enable(chip, total_current_ma);
- } else {
- if (in_progress) {
- pr_smb(PR_STATUS, "parallel charging unavailable\n");
- smbchg_parallel_usb_disable(chip);
- }
- }
- mutex_unlock(&chip->parallel.lock);
- smbchg_relax(chip, PM_PARALLEL_CHECK);
- return;
-
-recheck:
- schedule_delayed_work(&chip->parallel_en_work, 0);
-}
-
-static void smbchg_parallel_usb_check_ok(struct smbchg_chip *chip)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
-
- if (!parallel_psy || !chip->parallel_charger_detected)
- return;
-
- smbchg_stay_awake(chip, PM_PARALLEL_CHECK);
- schedule_delayed_work(&chip->parallel_en_work, 0);
-}
-
-static int charging_suspend_vote_cb(struct votable *votable, void *data,
- int suspend,
- const char *client)
-{
- int rc;
- struct smbchg_chip *chip = data;
-
- if (suspend < 0) {
- pr_err("No voters\n");
- suspend = false;
- }
-
- rc = smbchg_charging_en(chip, !suspend);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't configure batt chg: 0x%x rc = %d\n",
- !suspend, rc);
- }
-
- return rc;
-}
-
-static int usb_suspend_vote_cb(struct votable *votable,
- void *data,
- int suspend,
- const char *client)
-{
- int rc;
- struct smbchg_chip *chip = data;
-
- if (suspend < 0) {
- pr_err("No voters\n");
- suspend = false;
- }
-
- rc = smbchg_usb_suspend(chip, suspend);
- if (rc < 0)
- return rc;
-
- if ((strcmp(client, THERMAL_EN_VOTER) == 0)
- || (strcmp(client, POWER_SUPPLY_EN_VOTER) == 0)
- || (strcmp(client, USER_EN_VOTER) == 0)
- || (strcmp(client, FAKE_BATTERY_EN_VOTER) == 0))
- smbchg_parallel_usb_check_ok(chip);
-
- return rc;
-}
-
-static int dc_suspend_vote_cb(struct votable *votable,
- void *data,
- int suspend,
- const char *client)
-{
- int rc;
- struct smbchg_chip *chip = data;
-
- if (suspend < 0) {
- pr_err("No voters\n");
- suspend = false;
- }
-
- rc = smbchg_dc_suspend(chip, suspend);
- if (rc < 0)
- return rc;
-
- if (chip->dc_psy_type != -EINVAL && chip->dc_psy)
- power_supply_changed(chip->dc_psy);
-
- return rc;
-}
-
-static int set_fastchg_current_vote_cb(struct votable *votable,
- void *data,
- int fcc_ma,
- const char *client)
-{
- struct smbchg_chip *chip = data;
- int rc;
-
- if (fcc_ma < 0) {
- pr_err("No voters\n");
- return 0;
- }
-
- if (chip->parallel.current_max_ma == 0) {
- rc = smbchg_set_fastchg_current_raw(chip, fcc_ma);
- if (rc < 0) {
- pr_err("Can't set FCC fcc_ma=%d rc=%d\n", fcc_ma, rc);
- return rc;
- }
- }
- /*
- * check if parallel charging can be enabled, and if enabled,
- * distribute the fcc
- */
- smbchg_parallel_usb_check_ok(chip);
- return 0;
-}
-
-static int smbchg_set_fastchg_current_user(struct smbchg_chip *chip,
- int current_ma)
-{
- int rc = 0;
-
- pr_smb(PR_STATUS, "User setting FCC to %d\n", current_ma);
-
- rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true, current_ma);
- if (rc < 0)
- pr_err("Couldn't vote en rc %d\n", rc);
- return rc;
-}
-
-static struct ilim_entry *smbchg_wipower_find_entry(struct smbchg_chip *chip,
- struct ilim_map *map, int uv)
-{
- int i;
- struct ilim_entry *ret = &(chip->wipower_default.entries[0]);
-
- for (i = 0; i < map->num; i++) {
- if (is_between(map->entries[i].vmin_uv, map->entries[i].vmax_uv,
- uv))
- ret = &map->entries[i];
- }
- return ret;
-}
-
-#define ZIN_ICL_PT 0xFC
-#define ZIN_ICL_LV 0xFD
-#define ZIN_ICL_HV 0xFE
-#define ZIN_ICL_MASK SMB_MASK(4, 0)
-static int smbchg_dcin_ilim_config(struct smbchg_chip *chip, int offset, int ma)
-{
- int i, rc;
-
- i = find_smaller_in_array(chip->tables.dc_ilim_ma_table,
- ma, chip->tables.dc_ilim_ma_len);
-
- if (i < 0)
- i = 0;
-
- rc = smbchg_sec_masked_write(chip, chip->bat_if_base + offset,
- ZIN_ICL_MASK, i);
- if (rc)
- dev_err(chip->dev, "Couldn't write bat if offset %d value = %d rc = %d\n",
- offset, i, rc);
- return rc;
-}
-
-static int smbchg_wipower_ilim_config(struct smbchg_chip *chip,
- struct ilim_entry *ilim)
-{
- int rc = 0;
-
- if (chip->current_ilim.icl_pt_ma != ilim->icl_pt_ma) {
- rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_PT, ilim->icl_pt_ma);
- if (rc)
- dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n",
- ZIN_ICL_PT, ilim->icl_pt_ma, rc);
- else
- chip->current_ilim.icl_pt_ma = ilim->icl_pt_ma;
- }
-
- if (chip->current_ilim.icl_lv_ma != ilim->icl_lv_ma) {
- rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_LV, ilim->icl_lv_ma);
- if (rc)
- dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n",
- ZIN_ICL_LV, ilim->icl_lv_ma, rc);
- else
- chip->current_ilim.icl_lv_ma = ilim->icl_lv_ma;
- }
-
- if (chip->current_ilim.icl_hv_ma != ilim->icl_hv_ma) {
- rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_HV, ilim->icl_hv_ma);
- if (rc)
- dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n",
- ZIN_ICL_HV, ilim->icl_hv_ma, rc);
- else
- chip->current_ilim.icl_hv_ma = ilim->icl_hv_ma;
- }
- return rc;
-}
-
-static void btm_notify_dcin(enum qpnp_tm_state state, void *ctx);
-static int smbchg_wipower_dcin_btm_configure(struct smbchg_chip *chip,
- struct ilim_entry *ilim)
-{
- int rc;
-
- if (ilim->vmin_uv == chip->current_ilim.vmin_uv
- && ilim->vmax_uv == chip->current_ilim.vmax_uv)
- return 0;
-
- chip->param.channel = DCIN;
- chip->param.btm_ctx = chip;
- if (wipower_dcin_interval < ADC_MEAS1_INTERVAL_0MS)
- wipower_dcin_interval = ADC_MEAS1_INTERVAL_0MS;
-
- if (wipower_dcin_interval > ADC_MEAS1_INTERVAL_16S)
- wipower_dcin_interval = ADC_MEAS1_INTERVAL_16S;
-
- chip->param.timer_interval = wipower_dcin_interval;
- chip->param.threshold_notification = &btm_notify_dcin;
- chip->param.high_thr = ilim->vmax_uv + wipower_dcin_hyst_uv;
- chip->param.low_thr = ilim->vmin_uv - wipower_dcin_hyst_uv;
- chip->param.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
- rc = qpnp_vadc_channel_monitor(chip->vadc_dev, &chip->param);
- if (rc) {
- dev_err(chip->dev, "Couldn't configure btm for dcin rc = %d\n",
- rc);
- } else {
- chip->current_ilim.vmin_uv = ilim->vmin_uv;
- chip->current_ilim.vmax_uv = ilim->vmax_uv;
- pr_smb(PR_STATUS, "btm ilim = (%duV %duV %dmA %dmA %dmA)\n",
- ilim->vmin_uv, ilim->vmax_uv,
- ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma);
- }
- return rc;
-}
-
-static int smbchg_wipower_icl_configure(struct smbchg_chip *chip,
- int dcin_uv, bool div2)
-{
- int rc = 0;
- struct ilim_map *map = div2 ? &chip->wipower_div2 : &chip->wipower_pt;
- struct ilim_entry *ilim = smbchg_wipower_find_entry(chip, map, dcin_uv);
-
- rc = smbchg_wipower_ilim_config(chip, ilim);
- if (rc) {
- dev_err(chip->dev, "failed to config ilim rc = %d, dcin_uv = %d , div2 = %d, ilim = (%duV %duV %dmA %dmA %dmA)\n",
- rc, dcin_uv, div2,
- ilim->vmin_uv, ilim->vmax_uv,
- ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma);
- return rc;
- }
-
- rc = smbchg_wipower_dcin_btm_configure(chip, ilim);
- if (rc) {
- dev_err(chip->dev, "failed to config btm rc = %d, dcin_uv = %d , div2 = %d, ilim = (%duV %duV %dmA %dmA %dmA)\n",
- rc, dcin_uv, div2,
- ilim->vmin_uv, ilim->vmax_uv,
- ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma);
- return rc;
- }
- chip->wipower_configured = true;
- return 0;
-}
-
-static void smbchg_wipower_icl_deconfigure(struct smbchg_chip *chip)
-{
- int rc;
- struct ilim_entry *ilim = &(chip->wipower_default.entries[0]);
-
- if (!chip->wipower_configured)
- return;
-
- rc = smbchg_wipower_ilim_config(chip, ilim);
- if (rc)
- dev_err(chip->dev, "Couldn't config default ilim rc = %d\n",
- rc);
-
- rc = qpnp_vadc_end_channel_monitor(chip->vadc_dev);
- if (rc)
- dev_err(chip->dev, "Couldn't de configure btm for dcin rc = %d\n",
- rc);
-
- chip->wipower_configured = false;
- chip->current_ilim.vmin_uv = 0;
- chip->current_ilim.vmax_uv = 0;
- chip->current_ilim.icl_pt_ma = ilim->icl_pt_ma;
- chip->current_ilim.icl_lv_ma = ilim->icl_lv_ma;
- chip->current_ilim.icl_hv_ma = ilim->icl_hv_ma;
- pr_smb(PR_WIPOWER, "De config btm\n");
-}
-
-#define FV_STS 0x0C
-#define DIV2_ACTIVE BIT(7)
-static void __smbchg_wipower_check(struct smbchg_chip *chip)
-{
- int chg_type;
- bool usb_present, dc_present;
- int rc;
- int dcin_uv;
- bool div2;
- struct qpnp_vadc_result adc_result;
- u8 reg;
-
- if (!wipower_dyn_icl_en) {
- smbchg_wipower_icl_deconfigure(chip);
- return;
- }
-
- chg_type = get_prop_charge_type(chip);
- usb_present = is_usb_present(chip);
- dc_present = is_dc_present(chip);
- if (chg_type != POWER_SUPPLY_CHARGE_TYPE_NONE
- && !usb_present
- && dc_present
- && chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER) {
- rc = qpnp_vadc_read(chip->vadc_dev, DCIN, &adc_result);
- if (rc) {
- pr_smb(PR_STATUS, "error DCIN read rc = %d\n", rc);
- return;
- }
- dcin_uv = adc_result.physical;
-
- /* check div_by_2 */
- rc = smbchg_read(chip, &reg, chip->chgr_base + FV_STS, 1);
- if (rc) {
- pr_smb(PR_STATUS, "error DCIN read rc = %d\n", rc);
- return;
- }
- div2 = !!(reg & DIV2_ACTIVE);
-
- pr_smb(PR_WIPOWER,
- "config ICL chg_type = %d usb = %d dc = %d dcin_uv(adc_code) = %d (0x%x) div2 = %d\n",
- chg_type, usb_present, dc_present, dcin_uv,
- adc_result.adc_code, div2);
- smbchg_wipower_icl_configure(chip, dcin_uv, div2);
- } else {
- pr_smb(PR_WIPOWER,
- "deconfig ICL chg_type = %d usb = %d dc = %d\n",
- chg_type, usb_present, dc_present);
- smbchg_wipower_icl_deconfigure(chip);
- }
-}
-
-static void smbchg_wipower_check(struct smbchg_chip *chip)
-{
- if (!chip->wipower_dyn_icl_avail)
- return;
-
- mutex_lock(&chip->wipower_config);
- __smbchg_wipower_check(chip);
- mutex_unlock(&chip->wipower_config);
-}
-
-static void btm_notify_dcin(enum qpnp_tm_state state, void *ctx)
-{
- struct smbchg_chip *chip = ctx;
-
- mutex_lock(&chip->wipower_config);
- pr_smb(PR_WIPOWER, "%s state\n",
- state == ADC_TM_LOW_STATE ? "low" : "high");
- chip->current_ilim.vmin_uv = 0;
- chip->current_ilim.vmax_uv = 0;
- __smbchg_wipower_check(chip);
- mutex_unlock(&chip->wipower_config);
-}
-
-static int force_dcin_icl_write(void *data, u64 val)
-{
- struct smbchg_chip *chip = data;
-
- smbchg_wipower_check(chip);
- return 0;
-}
-DEFINE_SIMPLE_ATTRIBUTE(force_dcin_icl_ops, NULL,
- force_dcin_icl_write, "0x%02llx\n");
-
-/*
- * set the dc charge path's maximum allowed current draw
- * that may be limited by the system's thermal level
- */
-static int set_dc_current_limit_vote_cb(struct votable *votable,
- void *data,
- int icl_ma,
- const char *client)
-{
- struct smbchg_chip *chip = data;
-
- if (icl_ma < 0) {
- pr_err("No voters\n");
- return 0;
- }
-
- return smbchg_set_dc_current_max(chip, icl_ma);
-}
-
-/*
- * set the usb charge path's maximum allowed current draw
- * that may be limited by the system's thermal level
- */
-static int set_usb_current_limit_vote_cb(struct votable *votable,
- void *data,
- int icl_ma,
- const char *client)
-{
- struct smbchg_chip *chip = data;
- int rc, aicl_ma;
- const char *effective_id;
-
- if (icl_ma < 0) {
- pr_err("No voters\n");
- return 0;
- }
- effective_id = get_effective_client_locked(chip->usb_icl_votable);
-
- if (!effective_id)
- return 0;
-
- /* disable parallel charging if HVDCP is voting for 300mA */
- if (strcmp(effective_id, HVDCP_ICL_VOTER) == 0)
- smbchg_parallel_usb_disable(chip);
-
- if (chip->parallel.current_max_ma == 0) {
- rc = smbchg_set_usb_current_max(chip, icl_ma);
- if (rc) {
- pr_err("Failed to set usb current max: %d\n", rc);
- return rc;
- }
- }
-
- /* skip the aicl rerun if hvdcp icl voter is active */
- if (strcmp(effective_id, HVDCP_ICL_VOTER) == 0)
- return 0;
-
- aicl_ma = smbchg_get_aicl_level_ma(chip);
- if (icl_ma > aicl_ma)
- smbchg_rerun_aicl(chip);
- smbchg_parallel_usb_check_ok(chip);
- return 0;
-}
-
-static int smbchg_system_temp_level_set(struct smbchg_chip *chip,
- int lvl_sel)
-{
- int rc = 0;
- int prev_therm_lvl;
- int thermal_icl_ma;
-
- if (!chip->thermal_mitigation) {
- dev_err(chip->dev, "Thermal mitigation not supported\n");
- return -EINVAL;
- }
-
- if (lvl_sel < 0) {
- dev_err(chip->dev, "Unsupported level selected %d\n", lvl_sel);
- return -EINVAL;
- }
-
- if (lvl_sel >= chip->thermal_levels) {
- dev_err(chip->dev, "Unsupported level selected %d forcing %d\n",
- lvl_sel, chip->thermal_levels - 1);
- lvl_sel = chip->thermal_levels - 1;
- }
-
- if (lvl_sel == chip->therm_lvl_sel)
- return 0;
-
- mutex_lock(&chip->therm_lvl_lock);
- prev_therm_lvl = chip->therm_lvl_sel;
- chip->therm_lvl_sel = lvl_sel;
- if (chip->therm_lvl_sel == (chip->thermal_levels - 1)) {
- /*
- * Disable charging if highest value selected by
- * setting the DC and USB path in suspend
- */
- rc = vote(chip->dc_suspend_votable, THERMAL_EN_VOTER, true, 0);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set dc suspend rc %d\n", rc);
- goto out;
- }
- rc = vote(chip->usb_suspend_votable, THERMAL_EN_VOTER, true, 0);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set usb suspend rc %d\n", rc);
- goto out;
- }
- goto out;
- }
-
- if (chip->therm_lvl_sel == 0) {
- rc = vote(chip->usb_icl_votable, THERMAL_ICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't disable USB thermal ICL vote rc=%d\n",
- rc);
-
- rc = vote(chip->dc_icl_votable, THERMAL_ICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't disable DC thermal ICL vote rc=%d\n",
- rc);
- } else {
- thermal_icl_ma =
- (int)chip->thermal_mitigation[chip->therm_lvl_sel];
- rc = vote(chip->usb_icl_votable, THERMAL_ICL_VOTER, true,
- thermal_icl_ma);
- if (rc < 0)
- pr_err("Couldn't vote for USB thermal ICL rc=%d\n", rc);
-
- rc = vote(chip->dc_icl_votable, THERMAL_ICL_VOTER, true,
- thermal_icl_ma);
- if (rc < 0)
- pr_err("Couldn't vote for DC thermal ICL rc=%d\n", rc);
- }
-
- if (prev_therm_lvl == chip->thermal_levels - 1) {
- /*
- * If previously highest value was selected charging must have
- * been disabed. Enable charging by taking the DC and USB path
- * out of suspend.
- */
- rc = vote(chip->dc_suspend_votable, THERMAL_EN_VOTER, false, 0);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set dc suspend rc %d\n", rc);
- goto out;
- }
- rc = vote(chip->usb_suspend_votable, THERMAL_EN_VOTER,
- false, 0);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set usb suspend rc %d\n", rc);
- goto out;
- }
- }
-out:
- mutex_unlock(&chip->therm_lvl_lock);
- return rc;
-}
-
-static int smbchg_ibat_ocp_threshold_ua = 4500000;
-module_param(smbchg_ibat_ocp_threshold_ua, int, 0644);
-
-#define UCONV 1000000LL
-#define MCONV 1000LL
-#define FLASH_V_THRESHOLD 3000000
-#define FLASH_VDIP_MARGIN 100000
-#define VPH_FLASH_VDIP (FLASH_V_THRESHOLD + FLASH_VDIP_MARGIN)
-#define BUCK_EFFICIENCY 800LL
-static int smbchg_calc_max_flash_current(struct smbchg_chip *chip)
-{
- int ocv_uv, esr_uohm, rbatt_uohm, ibat_now, rc;
- int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw;
- int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv;
-
- rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_VOLTAGE_OCV, &ocv_uv);
- if (rc) {
- pr_smb(PR_STATUS, "bms psy does not support OCV\n");
- return 0;
- }
-
- rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_RESISTANCE,
- &esr_uohm);
- if (rc) {
- pr_smb(PR_STATUS, "bms psy does not support resistance\n");
- return 0;
- }
-
- rc = msm_bcl_read(BCL_PARAM_CURRENT, &ibat_now);
- if (rc) {
- pr_smb(PR_STATUS, "BCL current read failed: %d\n", rc);
- return 0;
- }
-
- rbatt_uohm = esr_uohm + chip->rpara_uohm + chip->rslow_uohm;
- /*
- * Calculate the maximum current that can pulled out of the battery
- * before the battery voltage dips below a safe threshold.
- */
- ibat_safe_ua = div_s64((ocv_uv - VPH_FLASH_VDIP) * UCONV,
- rbatt_uohm);
-
- if (ibat_safe_ua <= smbchg_ibat_ocp_threshold_ua) {
- /*
- * If the calculated current is below the OCP threshold, then
- * use it as the possible flash current.
- */
- ibat_flash_ua = ibat_safe_ua - ibat_now;
- vph_flash_uv = VPH_FLASH_VDIP;
- } else {
- /*
- * If the calculated current is above the OCP threshold, then
- * use the ocp threshold instead.
- *
- * Any higher current will be tripping the battery OCP.
- */
- ibat_flash_ua = smbchg_ibat_ocp_threshold_ua - ibat_now;
- vph_flash_uv = ocv_uv - div64_s64((int64_t)rbatt_uohm
- * smbchg_ibat_ocp_threshold_ua, UCONV);
- }
- /* Calculate the input voltage of the flash module. */
- vin_flash_uv = max((chip->vled_max_uv + 500000LL),
- div64_s64((vph_flash_uv * 1200), 1000));
- /* Calculate the available power for the flash module. */
- avail_flash_power_fw = BUCK_EFFICIENCY * vph_flash_uv * ibat_flash_ua;
- /*
- * Calculate the available amount of current the flash module can draw
- * before collapsing the battery. (available power/ flash input voltage)
- */
- avail_flash_ua = div64_s64(avail_flash_power_fw, vin_flash_uv * MCONV);
- pr_smb(PR_MISC,
- "avail_iflash=%lld, ocv=%d, ibat=%d, rbatt=%d\n",
- avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm);
- return (int)avail_flash_ua;
-}
-
-#define FCC_CMP_CFG 0xF3
-#define FCC_COMP_MASK SMB_MASK(1, 0)
-static int smbchg_fastchg_current_comp_set(struct smbchg_chip *chip,
- int comp_current)
-{
- int rc;
- u8 i;
-
- for (i = 0; i < chip->tables.fcc_comp_len; i++)
- if (comp_current == chip->tables.fcc_comp_table[i])
- break;
-
- if (i >= chip->tables.fcc_comp_len)
- return -EINVAL;
-
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CMP_CFG,
- FCC_COMP_MASK, i);
-
- if (rc)
- dev_err(chip->dev, "Couldn't set fastchg current comp rc = %d\n",
- rc);
-
- return rc;
-}
-
-#define CFG_TCC_REG 0xF9
-#define CHG_ITERM_MASK SMB_MASK(2, 0)
-static int smbchg_iterm_set(struct smbchg_chip *chip, int iterm_ma)
-{
- int rc;
- u8 reg;
-
- reg = find_closest_in_array(
- chip->tables.iterm_ma_table,
- chip->tables.iterm_ma_len,
- iterm_ma);
-
- rc = smbchg_sec_masked_write(chip,
- chip->chgr_base + CFG_TCC_REG,
- CHG_ITERM_MASK, reg);
- if (rc) {
- dev_err(chip->dev,
- "Couldn't set iterm rc = %d\n", rc);
- return rc;
- }
- pr_smb(PR_STATUS, "set tcc (%d) to 0x%02x\n",
- iterm_ma, reg);
- chip->iterm_ma = iterm_ma;
-
- return 0;
-}
-
-#define FV_CMP_CFG 0xF5
-#define FV_COMP_MASK SMB_MASK(5, 0)
-static int smbchg_float_voltage_comp_set(struct smbchg_chip *chip, int code)
-{
- int rc;
- u8 val;
-
- val = code & FV_COMP_MASK;
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + FV_CMP_CFG,
- FV_COMP_MASK, val);
-
- if (rc)
- dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n",
- rc);
-
- return rc;
-}
-
-#define VFLOAT_CFG_REG 0xF4
-#define MIN_FLOAT_MV 3600
-#define MAX_FLOAT_MV 4500
-#define VFLOAT_MASK SMB_MASK(5, 0)
-
-#define MID_RANGE_FLOAT_MV_MIN 3600
-#define MID_RANGE_FLOAT_MIN_VAL 0x05
-#define MID_RANGE_FLOAT_STEP_MV 20
-
-#define HIGH_RANGE_FLOAT_MIN_MV 4340
-#define HIGH_RANGE_FLOAT_MIN_VAL 0x2A
-#define HIGH_RANGE_FLOAT_STEP_MV 10
-
-#define VHIGH_RANGE_FLOAT_MIN_MV 4360
-#define VHIGH_RANGE_FLOAT_MIN_VAL 0x2C
-#define VHIGH_RANGE_FLOAT_STEP_MV 20
-static int smbchg_float_voltage_set(struct smbchg_chip *chip, int vfloat_mv)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval prop;
- int rc, delta;
- u8 temp;
-
- if ((vfloat_mv < MIN_FLOAT_MV) || (vfloat_mv > MAX_FLOAT_MV)) {
- dev_err(chip->dev, "bad float voltage mv =%d asked to set\n",
- vfloat_mv);
- return -EINVAL;
- }
-
- if (vfloat_mv <= HIGH_RANGE_FLOAT_MIN_MV) {
- /* mid range */
- delta = vfloat_mv - MID_RANGE_FLOAT_MV_MIN;
- temp = MID_RANGE_FLOAT_MIN_VAL + delta
- / MID_RANGE_FLOAT_STEP_MV;
- vfloat_mv -= delta % MID_RANGE_FLOAT_STEP_MV;
- } else if (vfloat_mv <= VHIGH_RANGE_FLOAT_MIN_MV) {
- /* high range */
- delta = vfloat_mv - HIGH_RANGE_FLOAT_MIN_MV;
- temp = HIGH_RANGE_FLOAT_MIN_VAL + delta
- / HIGH_RANGE_FLOAT_STEP_MV;
- vfloat_mv -= delta % HIGH_RANGE_FLOAT_STEP_MV;
- } else {
- /* very high range */
- delta = vfloat_mv - VHIGH_RANGE_FLOAT_MIN_MV;
- temp = VHIGH_RANGE_FLOAT_MIN_VAL + delta
- / VHIGH_RANGE_FLOAT_STEP_MV;
- vfloat_mv -= delta % VHIGH_RANGE_FLOAT_STEP_MV;
- }
-
- if (parallel_psy) {
- prop.intval = vfloat_mv + 50;
- rc = power_supply_set_property(parallel_psy,
- POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop);
- if (rc)
- dev_err(chip->dev, "Couldn't set float voltage on parallel psy rc: %d\n",
- rc);
- }
-
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + VFLOAT_CFG_REG,
- VFLOAT_MASK, temp);
-
- if (rc)
- dev_err(chip->dev, "Couldn't set float voltage rc = %d\n", rc);
- else
- chip->vfloat_mv = vfloat_mv;
-
- return rc;
-}
-
-static int smbchg_float_voltage_get(struct smbchg_chip *chip)
-{
- return chip->vfloat_mv;
-}
-
-#define SFT_CFG 0xFD
-#define SFT_EN_MASK SMB_MASK(5, 4)
-#define SFT_TO_MASK SMB_MASK(3, 2)
-#define PRECHG_SFT_TO_MASK SMB_MASK(1, 0)
-#define SFT_TIMER_DISABLE_BIT BIT(5)
-#define PRECHG_SFT_TIMER_DISABLE_BIT BIT(4)
-#define SAFETY_TIME_MINUTES_SHIFT 2
-static int smbchg_safety_timer_enable(struct smbchg_chip *chip, bool enable)
-{
- int rc;
- u8 reg;
-
- if (enable == chip->safety_timer_en)
- return 0;
-
- if (enable)
- reg = 0;
- else
- reg = SFT_TIMER_DISABLE_BIT | PRECHG_SFT_TIMER_DISABLE_BIT;
-
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + SFT_CFG,
- SFT_EN_MASK, reg);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't %s safety timer rc = %d\n",
- enable ? "enable" : "disable", rc);
- return rc;
- }
- chip->safety_timer_en = enable;
- return 0;
-}
-
-enum skip_reason {
- REASON_OTG_ENABLED = BIT(0),
- REASON_FLASH_ENABLED = BIT(1)
-};
-
-#define BAT_IF_TRIM7_REG 0xF7
-#define CFG_750KHZ_BIT BIT(1)
-#define MISC_CFG_NTC_VOUT_REG 0xF3
-#define CFG_NTC_VOUT_FSW_BIT BIT(0)
-static int smbchg_switch_buck_frequency(struct smbchg_chip *chip,
- bool flash_active)
-{
- int rc;
-
- if (!(chip->wa_flags & SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA))
- return 0;
-
- if (chip->flash_active == flash_active) {
- pr_smb(PR_STATUS, "Fsw not changed, flash_active: %d\n",
- flash_active);
- return 0;
- }
-
- /*
- * As per the systems team recommendation, before the flash fires,
- * buck switching frequency(Fsw) needs to be increased to 1MHz. Once the
- * flash is disabled, Fsw needs to be set back to 750KHz.
- */
- rc = smbchg_sec_masked_write(chip, chip->misc_base +
- MISC_CFG_NTC_VOUT_REG, CFG_NTC_VOUT_FSW_BIT,
- flash_active ? CFG_NTC_VOUT_FSW_BIT : 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set switching frequency multiplier rc=%d\n",
- rc);
- return rc;
- }
-
- rc = smbchg_sec_masked_write(chip, chip->bat_if_base + BAT_IF_TRIM7_REG,
- CFG_750KHZ_BIT, flash_active ? 0 : CFG_750KHZ_BIT);
- if (rc < 0) {
- dev_err(chip->dev, "Cannot set switching freq: %d\n", rc);
- return rc;
- }
-
- pr_smb(PR_STATUS, "Fsw @ %sHz\n", flash_active ? "1M" : "750K");
- chip->flash_active = flash_active;
- return 0;
-}
-
-#define OTG_TRIM6 0xF6
-#define TR_ENB_SKIP_BIT BIT(2)
-#define OTG_EN_BIT BIT(0)
-static int smbchg_otg_pulse_skip_disable(struct smbchg_chip *chip,
- enum skip_reason reason, bool disable)
-{
- int rc;
- bool disabled;
-
- disabled = !!chip->otg_pulse_skip_dis;
- pr_smb(PR_STATUS, "%s pulse skip, reason %d\n",
- disable ? "disabling" : "enabling", reason);
- if (disable)
- chip->otg_pulse_skip_dis |= reason;
- else
- chip->otg_pulse_skip_dis &= ~reason;
- if (disabled == !!chip->otg_pulse_skip_dis)
- return 0;
- disabled = !!chip->otg_pulse_skip_dis;
-
- rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_TRIM6,
- TR_ENB_SKIP_BIT, disabled ? TR_ENB_SKIP_BIT : 0);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't %s otg pulse skip rc = %d\n",
- disabled ? "disable" : "enable", rc);
- return rc;
- }
- pr_smb(PR_STATUS, "%s pulse skip\n", disabled ? "disabled" : "enabled");
- return 0;
-}
-
-#define LOW_PWR_OPTIONS_REG 0xFF
-#define FORCE_TLIM_BIT BIT(4)
-static int smbchg_force_tlim_en(struct smbchg_chip *chip, bool enable)
-{
- int rc;
-
- rc = smbchg_sec_masked_write(chip, chip->otg_base + LOW_PWR_OPTIONS_REG,
- FORCE_TLIM_BIT, enable ? FORCE_TLIM_BIT : 0);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't %s otg force tlim rc = %d\n",
- enable ? "enable" : "disable", rc);
- return rc;
- }
- return rc;
-}
-
-static void smbchg_vfloat_adjust_check(struct smbchg_chip *chip)
-{
- if (!chip->use_vfloat_adjustments)
- return;
-
- smbchg_stay_awake(chip, PM_REASON_VFLOAT_ADJUST);
- pr_smb(PR_STATUS, "Starting vfloat adjustments\n");
- schedule_delayed_work(&chip->vfloat_adjust_work, 0);
-}
-
-#define FV_STS_REG 0xC
-#define AICL_INPUT_STS_BIT BIT(6)
-static bool smbchg_is_input_current_limited(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->chgr_base + FV_STS_REG, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read FV_STS rc=%d\n", rc);
- return false;
- }
-
- return !!(reg & AICL_INPUT_STS_BIT);
-}
-
-#define SW_ESR_PULSE_MS 1500
-static void smbchg_cc_esr_wa_check(struct smbchg_chip *chip)
-{
- int rc, esr_count;
-
- if (!(chip->wa_flags & SMBCHG_CC_ESR_WA))
- return;
-
- if (!is_usb_present(chip) && !is_dc_present(chip)) {
- pr_smb(PR_STATUS, "No inputs present, skipping\n");
- return;
- }
-
- if (get_prop_charge_type(chip) != POWER_SUPPLY_CHARGE_TYPE_FAST) {
- pr_smb(PR_STATUS, "Not in fast charge, skipping\n");
- return;
- }
-
- if (!smbchg_is_input_current_limited(chip)) {
- pr_smb(PR_STATUS, "Not input current limited, skipping\n");
- return;
- }
-
- set_property_on_fg(chip, POWER_SUPPLY_PROP_UPDATE_NOW, 1);
- rc = get_property_from_fg(chip,
- POWER_SUPPLY_PROP_ESR_COUNT, &esr_count);
- if (rc) {
- pr_smb(PR_STATUS,
- "could not read ESR counter rc = %d\n", rc);
- return;
- }
-
- /*
- * The esr_count is counting down the number of fuel gauge cycles
- * before a ESR pulse is needed.
- *
- * After a successful ESR pulse, this count is reset to some
- * high number like 28. If this reaches 0, then the fuel gauge
- * hardware should force a ESR pulse.
- *
- * However, if the device is in constant current charge mode while
- * being input current limited, the ESR pulse will not affect the
- * battery current, so the measurement will fail.
- *
- * As a failsafe, force a manual ESR pulse if this value is read as
- * 0.
- */
- if (esr_count != 0) {
- pr_smb(PR_STATUS, "ESR count is not zero, skipping\n");
- return;
- }
-
- pr_smb(PR_STATUS, "Lowering charge current for ESR pulse\n");
- smbchg_stay_awake(chip, PM_ESR_PULSE);
- smbchg_sw_esr_pulse_en(chip, true);
- msleep(SW_ESR_PULSE_MS);
- pr_smb(PR_STATUS, "Raising charge current for ESR pulse\n");
- smbchg_relax(chip, PM_ESR_PULSE);
- smbchg_sw_esr_pulse_en(chip, false);
-}
-
-static void smbchg_soc_changed(struct smbchg_chip *chip)
-{
- smbchg_cc_esr_wa_check(chip);
-}
-
-#define DC_AICL_CFG 0xF3
-#define MISC_TRIM_OPT_15_8 0xF5
-#define USB_AICL_DEGLITCH_MASK (BIT(5) | BIT(4) | BIT(3))
-#define USB_AICL_DEGLITCH_SHORT (BIT(5) | BIT(4) | BIT(3))
-#define USB_AICL_DEGLITCH_LONG 0
-#define DC_AICL_DEGLITCH_MASK (BIT(5) | BIT(4) | BIT(3))
-#define DC_AICL_DEGLITCH_SHORT (BIT(5) | BIT(4) | BIT(3))
-#define DC_AICL_DEGLITCH_LONG 0
-#define AICL_RERUN_MASK (BIT(5) | BIT(4))
-#define AICL_RERUN_ON (BIT(5) | BIT(4))
-#define AICL_RERUN_OFF 0
-
-static int smbchg_hw_aicl_rerun_enable_indirect_cb(struct votable *votable,
- void *data,
- int enable,
- const char *client)
-{
- int rc = 0;
- struct smbchg_chip *chip = data;
-
- if (enable < 0) {
- pr_err("No voters\n");
- enable = 0;
- }
- /*
- * If the indirect voting result of all the clients is to enable hw aicl
- * rerun, then remove our vote to disable hw aicl rerun
- */
- rc = vote(chip->hw_aicl_rerun_disable_votable,
- HW_AICL_RERUN_ENABLE_INDIRECT_VOTER, !enable, 0);
- if (rc < 0) {
- pr_err("Couldn't vote for hw rerun rc= %d\n", rc);
- return rc;
- }
-
- return rc;
-}
-
-static int smbchg_hw_aicl_rerun_disable_cb(struct votable *votable, void *data,
- int disable,
- const char *client)
-{
- int rc = 0;
- struct smbchg_chip *chip = data;
-
- if (disable < 0) {
- pr_err("No voters\n");
- disable = 0;
- }
-
- rc = smbchg_sec_masked_write(chip,
- chip->misc_base + MISC_TRIM_OPT_15_8,
- AICL_RERUN_MASK, disable ? AICL_RERUN_OFF : AICL_RERUN_ON);
- if (rc < 0)
- pr_err("Couldn't write to MISC_TRIM_OPTIONS_15_8 rc=%d\n", rc);
-
- return rc;
-}
-
-static int smbchg_aicl_deglitch_config_cb(struct votable *votable, void *data,
- int shorter,
- const char *client)
-{
- int rc = 0;
- struct smbchg_chip *chip = data;
-
- if (shorter < 0) {
- pr_err("No voters\n");
- shorter = 0;
- }
-
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + USB_AICL_CFG,
- USB_AICL_DEGLITCH_MASK,
- shorter ? USB_AICL_DEGLITCH_SHORT : USB_AICL_DEGLITCH_LONG);
- if (rc < 0) {
- pr_err("Couldn't write to USB_AICL_CFG rc=%d\n", rc);
- return rc;
- }
- rc = smbchg_sec_masked_write(chip,
- chip->dc_chgpth_base + DC_AICL_CFG,
- DC_AICL_DEGLITCH_MASK,
- shorter ? DC_AICL_DEGLITCH_SHORT : DC_AICL_DEGLITCH_LONG);
- if (rc < 0) {
- pr_err("Couldn't write to DC_AICL_CFG rc=%d\n", rc);
- return rc;
- }
- return rc;
-}
-
-static void smbchg_aicl_deglitch_wa_en(struct smbchg_chip *chip, bool en)
-{
- int rc;
-
- rc = vote(chip->aicl_deglitch_short_votable,
- VARB_WORKAROUND_VOTER, en, 0);
- if (rc < 0) {
- pr_err("Couldn't vote %s deglitch rc=%d\n",
- en ? "short" : "long", rc);
- return;
- }
- pr_smb(PR_STATUS, "AICL deglitch set to %s\n", en ? "short" : "long");
-
- rc = vote(chip->hw_aicl_rerun_enable_indirect_votable,
- VARB_WORKAROUND_VOTER, en, 0);
- if (rc < 0) {
- pr_err("Couldn't vote hw aicl rerun rc= %d\n", rc);
- return;
- }
- chip->aicl_deglitch_short = en;
-}
-
-static void smbchg_aicl_deglitch_wa_check(struct smbchg_chip *chip)
-{
- union power_supply_propval prop = {0,};
- int rc;
- bool low_volt_chgr = true;
-
- if (!(chip->wa_flags & SMBCHG_AICL_DEGLITCH_WA))
- return;
-
- if (!is_usb_present(chip) && !is_dc_present(chip)) {
- pr_smb(PR_STATUS, "Charger removed\n");
- smbchg_aicl_deglitch_wa_en(chip, false);
- return;
- }
-
- if (!chip->bms_psy)
- return;
-
- if (is_usb_present(chip)) {
- if (is_hvdcp_present(chip))
- low_volt_chgr = false;
- } else if (is_dc_present(chip)) {
- if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER)
- low_volt_chgr = false;
- else
- low_volt_chgr = chip->low_volt_dcin;
- }
-
- if (!low_volt_chgr) {
- pr_smb(PR_STATUS, "High volt charger! Don't set deglitch\n");
- smbchg_aicl_deglitch_wa_en(chip, false);
- return;
- }
-
- /* It is possible that battery voltage went high above threshold
- * when the charger is inserted and can go low because of system
- * load. We shouldn't be reconfiguring AICL deglitch when this
- * happens as it will lead to oscillation again which is being
- * fixed here. Do it once when the battery voltage crosses the
- * threshold (e.g. 4.2 V) and clear it only when the charger
- * is removed.
- */
- if (!chip->vbat_above_headroom) {
- rc = power_supply_get_property(chip->bms_psy,
- POWER_SUPPLY_PROP_VOLTAGE_MIN, &prop);
- if (rc < 0) {
- pr_err("could not read voltage_min, rc=%d\n", rc);
- return;
- }
- chip->vbat_above_headroom = !prop.intval;
- }
- smbchg_aicl_deglitch_wa_en(chip, chip->vbat_above_headroom);
-}
-
-#define MISC_TEST_REG 0xE2
-#define BB_LOOP_DISABLE_ICL BIT(2)
-static int smbchg_icl_loop_disable_check(struct smbchg_chip *chip)
-{
- bool icl_disabled = !chip->chg_otg_enabled && chip->flash_triggered;
- int rc = 0;
-
- if ((chip->wa_flags & SMBCHG_FLASH_ICL_DISABLE_WA)
- && icl_disabled != chip->icl_disabled) {
- rc = smbchg_sec_masked_write(chip,
- chip->misc_base + MISC_TEST_REG,
- BB_LOOP_DISABLE_ICL,
- icl_disabled ? BB_LOOP_DISABLE_ICL : 0);
- chip->icl_disabled = icl_disabled;
- }
-
- return rc;
-}
-
-#define UNKNOWN_BATT_TYPE "Unknown Battery"
-#define LOADING_BATT_TYPE "Loading Battery Data"
-static int smbchg_config_chg_battery_type(struct smbchg_chip *chip)
-{
- int rc = 0, max_voltage_uv = 0, fastchg_ma = 0, ret = 0, iterm_ua = 0;
- struct device_node *batt_node, *profile_node;
- struct device_node *node = chip->pdev->dev.of_node;
- union power_supply_propval prop = {0,};
-
- rc = power_supply_get_property(chip->bms_psy,
- POWER_SUPPLY_PROP_BATTERY_TYPE, &prop);
- if (rc) {
- pr_smb(PR_STATUS, "Unable to read battery-type rc=%d\n", rc);
- return 0;
- }
- if (!strcmp(prop.strval, UNKNOWN_BATT_TYPE) ||
- !strcmp(prop.strval, LOADING_BATT_TYPE)) {
- pr_smb(PR_MISC, "Battery-type not identified\n");
- return 0;
- }
- /* quit if there is no change in the battery-type from previous */
- if (chip->battery_type && !strcmp(prop.strval, chip->battery_type))
- return 0;
-
- chip->battery_type = prop.strval;
- batt_node = of_parse_phandle(node, "qcom,battery-data", 0);
- if (!batt_node) {
- pr_smb(PR_MISC, "No batterydata available\n");
- return 0;
- }
-
- rc = power_supply_get_property(chip->bms_psy,
- POWER_SUPPLY_PROP_RESISTANCE_ID, &prop);
- if (rc < 0) {
- pr_smb(PR_STATUS, "Unable to read battery-id rc=%d\n", rc);
- return 0;
- }
-
- profile_node = of_batterydata_get_best_profile(batt_node,
- prop.intval / 1000, NULL);
- if (IS_ERR_OR_NULL(profile_node)) {
- rc = PTR_ERR(profile_node);
- pr_err("couldn't find profile handle %d\n", rc);
- return rc;
- }
-
- /* change vfloat */
- rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
- &max_voltage_uv);
- if (rc) {
- pr_warn("couldn't find battery max voltage rc=%d\n", rc);
- ret = rc;
- } else {
- if (chip->vfloat_mv != (max_voltage_uv / 1000)) {
- pr_info("Vfloat changed from %dmV to %dmV for battery-type %s\n",
- chip->vfloat_mv, (max_voltage_uv / 1000),
- chip->battery_type);
- rc = smbchg_float_voltage_set(chip,
- (max_voltage_uv / 1000));
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set float voltage rc = %d\n", rc);
- return rc;
- }
- }
- }
-
- /* change chg term */
- rc = of_property_read_u32(profile_node, "qcom,chg-term-ua",
- &iterm_ua);
- if (rc && rc != -EINVAL) {
- pr_warn("couldn't read battery term current=%d\n", rc);
- ret = rc;
- } else if (!rc) {
- if (chip->iterm_ma != (iterm_ua / 1000)
- && !chip->iterm_disabled) {
- pr_info("Term current changed from %dmA to %dmA for battery-type %s\n",
- chip->iterm_ma, (iterm_ua / 1000),
- chip->battery_type);
- rc = smbchg_iterm_set(chip,
- (iterm_ua / 1000));
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set iterm rc = %d\n", rc);
- return rc;
- }
- }
- chip->iterm_ma = iterm_ua / 1000;
- }
-
- /*
- * Only configure from profile if fastchg-ma is not defined in the
- * charger device node.
- */
- if (!of_find_property(chip->pdev->dev.of_node,
- "qcom,fastchg-current-ma", NULL)) {
- rc = of_property_read_u32(profile_node,
- "qcom,fastchg-current-ma", &fastchg_ma);
- if (rc) {
- ret = rc;
- } else {
- pr_smb(PR_MISC,
- "fastchg-ma changed from to %dma for battery-type %s\n",
- fastchg_ma, chip->battery_type);
- rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true,
- fastchg_ma);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't vote for fastchg current rc=%d\n",
- rc);
- return rc;
- }
- }
- }
-
- return ret;
-}
-
-#define MAX_INV_BATT_ID 7700
-#define MIN_INV_BATT_ID 7300
-static void check_battery_type(struct smbchg_chip *chip)
-{
- union power_supply_propval prop = {0,};
- bool en;
-
- if (!chip->bms_psy && chip->bms_psy_name)
- chip->bms_psy =
- power_supply_get_by_name((char *)chip->bms_psy_name);
- if (chip->bms_psy) {
- power_supply_get_property(chip->bms_psy,
- POWER_SUPPLY_PROP_BATTERY_TYPE, &prop);
- en = (strcmp(prop.strval, UNKNOWN_BATT_TYPE) != 0
- || chip->charge_unknown_battery)
- && (strcmp(prop.strval, LOADING_BATT_TYPE) != 0);
- vote(chip->battchg_suspend_votable,
- BATTCHG_UNKNOWN_BATTERY_EN_VOTER, !en, 0);
-
- if (!chip->skip_usb_suspend_for_fake_battery) {
- power_supply_get_property(chip->bms_psy,
- POWER_SUPPLY_PROP_RESISTANCE_ID, &prop);
- /* suspend USB path for invalid battery-id */
- en = (prop.intval <= MAX_INV_BATT_ID &&
- prop.intval >= MIN_INV_BATT_ID) ? 1 : 0;
- vote(chip->usb_suspend_votable, FAKE_BATTERY_EN_VOTER,
- en, 0);
- }
- }
-}
-
-static void smbchg_external_power_changed(struct power_supply *psy)
-{
- struct smbchg_chip *chip = power_supply_get_drvdata(psy);
- union power_supply_propval prop = {0,};
- int rc, current_limit = 0, soc;
- enum power_supply_type usb_supply_type;
- char *usb_type_name = "null";
-
- if (chip->bms_psy_name)
- chip->bms_psy =
- power_supply_get_by_name((char *)chip->bms_psy_name);
-
- smbchg_aicl_deglitch_wa_check(chip);
- if (chip->bms_psy) {
- check_battery_type(chip);
- soc = get_prop_batt_capacity(chip);
- if (chip->previous_soc != soc) {
- chip->previous_soc = soc;
- smbchg_soc_changed(chip);
- }
-
- rc = smbchg_config_chg_battery_type(chip);
- if (rc)
- pr_smb(PR_MISC,
- "Couldn't update charger configuration rc=%d\n",
- rc);
- }
-
- rc = power_supply_get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop);
- if (rc == 0)
- vote(chip->usb_suspend_votable, POWER_SUPPLY_EN_VOTER,
- !prop.intval, 0);
-
- current_limit = chip->usb_current_max / 1000;
-
- /* Override if type-c charger used */
- if (chip->typec_current_ma > 500 &&
- current_limit < chip->typec_current_ma)
- current_limit = chip->typec_current_ma;
-
- read_usb_type(chip, &usb_type_name, &usb_supply_type);
-
- if (usb_supply_type != POWER_SUPPLY_TYPE_USB)
- goto skip_current_for_non_sdp;
-
- pr_smb(PR_MISC, "usb type = %s current_limit = %d\n",
- usb_type_name, current_limit);
-
- rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true,
- current_limit);
- if (rc < 0)
- pr_err("Couldn't update USB PSY ICL vote rc=%d\n", rc);
-
-skip_current_for_non_sdp:
- smbchg_vfloat_adjust_check(chip);
-
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
-}
-
-static int smbchg_otg_regulator_enable(struct regulator_dev *rdev)
-{
- int rc = 0;
- struct smbchg_chip *chip = rdev_get_drvdata(rdev);
-
- chip->otg_retries = 0;
- chip->chg_otg_enabled = true;
- smbchg_icl_loop_disable_check(chip);
- smbchg_otg_pulse_skip_disable(chip, REASON_OTG_ENABLED, true);
-
- /* If pin control mode then return from here */
- if (chip->otg_pinctrl)
- return rc;
-
- /* sleep to make sure the pulse skip is actually disabled */
- msleep(20);
- rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
- OTG_EN_BIT, OTG_EN_BIT);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't enable OTG mode rc=%d\n", rc);
- else
- chip->otg_enable_time = ktime_get();
- pr_smb(PR_STATUS, "Enabling OTG Boost\n");
- return rc;
-}
-
-static int smbchg_otg_regulator_disable(struct regulator_dev *rdev)
-{
- int rc = 0;
- struct smbchg_chip *chip = rdev_get_drvdata(rdev);
-
- if (!chip->otg_pinctrl) {
- rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
- OTG_EN_BIT, 0);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't disable OTG mode rc=%d\n",
- rc);
- }
-
- chip->chg_otg_enabled = false;
- smbchg_otg_pulse_skip_disable(chip, REASON_OTG_ENABLED, false);
- smbchg_icl_loop_disable_check(chip);
- pr_smb(PR_STATUS, "Disabling OTG Boost\n");
- return rc;
-}
-
-static int smbchg_otg_regulator_is_enable(struct regulator_dev *rdev)
-{
- int rc = 0;
- u8 reg = 0;
- struct smbchg_chip *chip = rdev_get_drvdata(rdev);
-
- rc = smbchg_read(chip, &reg, chip->bat_if_base + CMD_CHG_REG, 1);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't read OTG enable bit rc=%d\n", rc);
- return rc;
- }
-
- return (reg & OTG_EN_BIT) ? 1 : 0;
-}
-
-struct regulator_ops smbchg_otg_reg_ops = {
- .enable = smbchg_otg_regulator_enable,
- .disable = smbchg_otg_regulator_disable,
- .is_enabled = smbchg_otg_regulator_is_enable,
-};
-
-#define USBIN_CHGR_CFG 0xF1
-#define ADAPTER_ALLOWANCE_MASK 0x7
-#define USBIN_ADAPTER_9V 0x3
-#define USBIN_ADAPTER_5V_9V_CONT 0x2
-#define USBIN_ADAPTER_5V_UNREGULATED_9V 0x5
-#define HVDCP_EN_BIT BIT(3)
-static int smbchg_external_otg_regulator_enable(struct regulator_dev *rdev)
-{
- int rc = 0;
- struct smbchg_chip *chip = rdev_get_drvdata(rdev);
-
- rc = vote(chip->usb_suspend_votable, OTG_EN_VOTER, true, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't suspend charger rc=%d\n", rc);
- return rc;
- }
-
- rc = smbchg_read(chip, &chip->original_usbin_allowance,
- chip->usb_chgpth_base + USBIN_CHGR_CFG, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read usb allowance rc=%d\n", rc);
- return rc;
- }
-
- /*
- * To disallow source detect and usbin_uv interrupts, set the adapter
- * allowance to 9V, so that the audio boost operating in reverse never
- * gets detected as a valid input
- */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't disable HVDCP rc=%d\n", rc);
- return rc;
- }
-
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + USBIN_CHGR_CFG,
- 0xFF, USBIN_ADAPTER_9V);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't write usb allowance rc=%d\n", rc);
- return rc;
- }
-
- pr_smb(PR_STATUS, "Enabling OTG Boost\n");
- return rc;
-}
-
-static int smbchg_external_otg_regulator_disable(struct regulator_dev *rdev)
-{
- int rc = 0;
- struct smbchg_chip *chip = rdev_get_drvdata(rdev);
-
- rc = vote(chip->usb_suspend_votable, OTG_EN_VOTER, false, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't unsuspend charger rc=%d\n", rc);
- return rc;
- }
-
- /*
- * Reenable HVDCP and set the adapter allowance back to the original
- * value in order to allow normal USBs to be recognized as a valid
- * input.
- */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, HVDCP_EN_BIT);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't enable HVDCP rc=%d\n", rc);
- return rc;
- }
-
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + USBIN_CHGR_CFG,
- 0xFF, chip->original_usbin_allowance);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't write usb allowance rc=%d\n", rc);
- return rc;
- }
-
- pr_smb(PR_STATUS, "Disabling OTG Boost\n");
- return rc;
-}
-
-static int smbchg_external_otg_regulator_is_enable(struct regulator_dev *rdev)
-{
- struct smbchg_chip *chip = rdev_get_drvdata(rdev);
-
- return get_client_vote(chip->usb_suspend_votable, OTG_EN_VOTER);
-}
-
-struct regulator_ops smbchg_external_otg_reg_ops = {
- .enable = smbchg_external_otg_regulator_enable,
- .disable = smbchg_external_otg_regulator_disable,
- .is_enabled = smbchg_external_otg_regulator_is_enable,
-};
-
-static int smbchg_regulator_init(struct smbchg_chip *chip)
-{
- int rc = 0;
- struct regulator_config cfg = {};
- struct device_node *regulator_node;
-
- cfg.dev = chip->dev;
- cfg.driver_data = chip;
-
- chip->otg_vreg.rdesc.owner = THIS_MODULE;
- chip->otg_vreg.rdesc.type = REGULATOR_VOLTAGE;
- chip->otg_vreg.rdesc.ops = &smbchg_otg_reg_ops;
- chip->otg_vreg.rdesc.of_match = "qcom,smbcharger-boost-otg";
- chip->otg_vreg.rdesc.name = "qcom,smbcharger-boost-otg";
-
- chip->otg_vreg.rdev = devm_regulator_register(chip->dev,
- &chip->otg_vreg.rdesc, &cfg);
- if (IS_ERR(chip->otg_vreg.rdev)) {
- rc = PTR_ERR(chip->otg_vreg.rdev);
- chip->otg_vreg.rdev = NULL;
- if (rc != -EPROBE_DEFER)
- dev_err(chip->dev,
- "OTG reg failed, rc=%d\n", rc);
- }
- if (rc)
- return rc;
-
- regulator_node = of_get_child_by_name(chip->dev->of_node,
- "qcom,smbcharger-external-otg");
- if (!regulator_node) {
- dev_dbg(chip->dev, "external-otg node absent\n");
- return 0;
- }
-
- chip->ext_otg_vreg.rdesc.owner = THIS_MODULE;
- chip->ext_otg_vreg.rdesc.type = REGULATOR_VOLTAGE;
- chip->ext_otg_vreg.rdesc.ops = &smbchg_external_otg_reg_ops;
- chip->ext_otg_vreg.rdesc.of_match = "qcom,smbcharger-external-otg";
- chip->ext_otg_vreg.rdesc.name = "qcom,smbcharger-external-otg";
- if (of_get_property(chip->dev->of_node, "otg-parent-supply", NULL))
- chip->ext_otg_vreg.rdesc.supply_name = "otg-parent";
- cfg.dev = chip->dev;
- cfg.driver_data = chip;
-
- chip->ext_otg_vreg.rdev = devm_regulator_register(chip->dev,
- &chip->ext_otg_vreg.rdesc,
- &cfg);
- if (IS_ERR(chip->ext_otg_vreg.rdev)) {
- rc = PTR_ERR(chip->ext_otg_vreg.rdev);
- chip->ext_otg_vreg.rdev = NULL;
- if (rc != -EPROBE_DEFER)
- dev_err(chip->dev,
- "external OTG reg failed, rc=%d\n", rc);
- }
-
- return rc;
-}
-
-#define CMD_CHG_LED_REG 0x43
-#define CHG_LED_CTRL_BIT BIT(0)
-#define LED_SW_CTRL_BIT 0x1
-#define LED_CHG_CTRL_BIT 0x0
-#define CHG_LED_ON 0x03
-#define CHG_LED_OFF 0x00
-#define LED_BLINKING_PATTERN1 0x01
-#define LED_BLINKING_PATTERN2 0x02
-#define LED_BLINKING_CFG_MASK SMB_MASK(2, 1)
-#define CHG_LED_SHIFT 1
-static int smbchg_chg_led_controls(struct smbchg_chip *chip)
-{
- u8 reg, mask;
- int rc;
-
- if (chip->cfg_chg_led_sw_ctrl) {
- /* turn-off LED by default for software control */
- mask = CHG_LED_CTRL_BIT | LED_BLINKING_CFG_MASK;
- reg = LED_SW_CTRL_BIT;
- } else {
- mask = CHG_LED_CTRL_BIT;
- reg = LED_CHG_CTRL_BIT;
- }
-
- rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_LED_REG,
- mask, reg);
- if (rc < 0)
- dev_err(chip->dev,
- "Couldn't write LED_CTRL_BIT rc=%d\n", rc);
- return rc;
-}
-
-static void smbchg_chg_led_brightness_set(struct led_classdev *cdev,
- enum led_brightness value)
-{
- struct smbchg_chip *chip = container_of(cdev,
- struct smbchg_chip, led_cdev);
- union power_supply_propval pval = {0, };
- u8 reg;
- int rc;
-
- reg = (value > LED_OFF) ? CHG_LED_ON << CHG_LED_SHIFT :
- CHG_LED_OFF << CHG_LED_SHIFT;
- pval.intval = value > LED_OFF ? 1 : 0;
- power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_HI_POWER,
- &pval);
- pr_smb(PR_STATUS,
- "set the charger led brightness to value=%d\n",
- value);
- rc = smbchg_sec_masked_write(chip,
- chip->bat_if_base + CMD_CHG_LED_REG,
- LED_BLINKING_CFG_MASK, reg);
- if (rc)
- dev_err(chip->dev, "Couldn't write CHG_LED rc=%d\n",
- rc);
-}
-
-static enum
-led_brightness smbchg_chg_led_brightness_get(struct led_classdev *cdev)
-{
- struct smbchg_chip *chip = container_of(cdev,
- struct smbchg_chip, led_cdev);
- u8 reg_val, chg_led_sts;
- int rc;
-
- rc = smbchg_read(chip, &reg_val, chip->bat_if_base + CMD_CHG_LED_REG,
- 1);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't read CHG_LED_REG sts rc=%d\n",
- rc);
- return rc;
- }
-
- chg_led_sts = (reg_val & LED_BLINKING_CFG_MASK) >> CHG_LED_SHIFT;
-
- pr_smb(PR_STATUS, "chg_led_sts = %02x\n", chg_led_sts);
-
- return (chg_led_sts == CHG_LED_OFF) ? LED_OFF : LED_FULL;
-}
-
-static void smbchg_chg_led_blink_set(struct smbchg_chip *chip,
- unsigned long blinking)
-{
- union power_supply_propval pval = {0, };
- u8 reg;
- int rc;
-
- pval.intval = (blinking == 0) ? 0 : 1;
- power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_HI_POWER,
- &pval);
-
- if (blinking == 0) {
- reg = CHG_LED_OFF << CHG_LED_SHIFT;
- } else {
- if (blinking == 1)
- reg = LED_BLINKING_PATTERN1 << CHG_LED_SHIFT;
- else if (blinking == 2)
- reg = LED_BLINKING_PATTERN2 << CHG_LED_SHIFT;
- else
- reg = LED_BLINKING_PATTERN1 << CHG_LED_SHIFT;
- }
-
- rc = smbchg_sec_masked_write(chip,
- chip->bat_if_base + CMD_CHG_LED_REG,
- LED_BLINKING_CFG_MASK, reg);
- if (rc)
- dev_err(chip->dev, "Couldn't write CHG_LED rc=%d\n",
- rc);
-}
-
-static ssize_t smbchg_chg_led_blink_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t len)
-{
- struct led_classdev *cdev = dev_get_drvdata(dev);
- struct smbchg_chip *chip = container_of(cdev, struct smbchg_chip,
- led_cdev);
- unsigned long blinking;
- ssize_t rc = -EINVAL;
-
- rc = kstrtoul(buf, 10, &blinking);
- if (rc)
- return rc;
-
- smbchg_chg_led_blink_set(chip, blinking);
-
- return len;
-}
-
-static DEVICE_ATTR(blink, 0664, NULL, smbchg_chg_led_blink_store);
-
-static struct attribute *led_blink_attributes[] = {
- &dev_attr_blink.attr,
- NULL,
-};
-
-static struct attribute_group smbchg_led_attr_group = {
- .attrs = led_blink_attributes
-};
-
-static int smbchg_register_chg_led(struct smbchg_chip *chip)
-{
- int rc;
-
- chip->led_cdev.name = "red";
- chip->led_cdev.brightness_set = smbchg_chg_led_brightness_set;
- chip->led_cdev.brightness_get = smbchg_chg_led_brightness_get;
-
- rc = led_classdev_register(chip->dev, &chip->led_cdev);
- if (rc) {
- dev_err(chip->dev, "unable to register charger led, rc=%d\n",
- rc);
- return rc;
- }
-
- rc = sysfs_create_group(&chip->led_cdev.dev->kobj,
- &smbchg_led_attr_group);
- if (rc) {
- dev_err(chip->dev, "led sysfs rc: %d\n", rc);
- return rc;
- }
-
- return rc;
-}
-
-static int vf_adjust_low_threshold = 5;
-module_param(vf_adjust_low_threshold, int, 0644);
-
-static int vf_adjust_high_threshold = 7;
-module_param(vf_adjust_high_threshold, int, 0644);
-
-static int vf_adjust_n_samples = 10;
-module_param(vf_adjust_n_samples, int, 0644);
-
-static int vf_adjust_max_delta_mv = 40;
-module_param(vf_adjust_max_delta_mv, int, 0644);
-
-static int vf_adjust_trim_steps_per_adjust = 1;
-module_param(vf_adjust_trim_steps_per_adjust, int, 0644);
-
-#define CENTER_TRIM_CODE 7
-#define MAX_LIN_CODE 14
-#define MAX_TRIM_CODE 15
-#define SCALE_SHIFT 4
-#define VF_TRIM_OFFSET_MASK SMB_MASK(3, 0)
-#define VF_STEP_SIZE_MV 10
-#define SCALE_LSB_MV 17
-static int smbchg_trim_add_steps(int prev_trim, int delta_steps)
-{
- int scale_steps;
- int linear_offset, linear_scale;
- int offset_code = prev_trim & VF_TRIM_OFFSET_MASK;
- int scale_code = (prev_trim & ~VF_TRIM_OFFSET_MASK) >> SCALE_SHIFT;
-
- if (abs(delta_steps) > 1) {
- pr_smb(PR_STATUS,
- "Cant trim multiple steps delta_steps = %d\n",
- delta_steps);
- return prev_trim;
- }
- if (offset_code <= CENTER_TRIM_CODE)
- linear_offset = offset_code + CENTER_TRIM_CODE;
- else if (offset_code > CENTER_TRIM_CODE)
- linear_offset = MAX_TRIM_CODE - offset_code;
-
- if (scale_code <= CENTER_TRIM_CODE)
- linear_scale = scale_code + CENTER_TRIM_CODE;
- else if (scale_code > CENTER_TRIM_CODE)
- linear_scale = scale_code - (CENTER_TRIM_CODE + 1);
-
- /* check if we can accomodate delta steps with just the offset */
- if (linear_offset + delta_steps >= 0
- && linear_offset + delta_steps <= MAX_LIN_CODE) {
- linear_offset += delta_steps;
-
- if (linear_offset > CENTER_TRIM_CODE)
- offset_code = linear_offset - CENTER_TRIM_CODE;
- else
- offset_code = MAX_TRIM_CODE - linear_offset;
-
- return (prev_trim & ~VF_TRIM_OFFSET_MASK) | offset_code;
- }
-
- /* changing offset cannot satisfy delta steps, change the scale bits */
- scale_steps = delta_steps > 0 ? 1 : -1;
-
- if (linear_scale + scale_steps < 0
- || linear_scale + scale_steps > MAX_LIN_CODE) {
- pr_smb(PR_STATUS,
- "Cant trim scale_steps = %d delta_steps = %d\n",
- scale_steps, delta_steps);
- return prev_trim;
- }
-
- linear_scale += scale_steps;
-
- if (linear_scale > CENTER_TRIM_CODE)
- scale_code = linear_scale - CENTER_TRIM_CODE;
- else
- scale_code = linear_scale + (CENTER_TRIM_CODE + 1);
- prev_trim = (prev_trim & VF_TRIM_OFFSET_MASK)
- | scale_code << SCALE_SHIFT;
-
- /*
- * now that we have changed scale which is a 17mV jump, change the
- * offset bits (10mV) too so the effective change is just 7mV
- */
- delta_steps = -1 * delta_steps;
-
- linear_offset = clamp(linear_offset + delta_steps, 0, MAX_LIN_CODE);
- if (linear_offset > CENTER_TRIM_CODE)
- offset_code = linear_offset - CENTER_TRIM_CODE;
- else
- offset_code = MAX_TRIM_CODE - linear_offset;
-
- return (prev_trim & ~VF_TRIM_OFFSET_MASK) | offset_code;
-}
-
-#define TRIM_14 0xFE
-#define VF_TRIM_MASK 0xFF
-static int smbchg_adjust_vfloat_mv_trim(struct smbchg_chip *chip,
- int delta_mv)
-{
- int sign, delta_steps, rc = 0;
- u8 prev_trim, new_trim;
- int i;
-
- sign = delta_mv > 0 ? 1 : -1;
- delta_steps = (delta_mv + sign * VF_STEP_SIZE_MV / 2)
- / VF_STEP_SIZE_MV;
-
- rc = smbchg_read(chip, &prev_trim, chip->misc_base + TRIM_14, 1);
- if (rc) {
- dev_err(chip->dev, "Unable to read trim 14: %d\n", rc);
- return rc;
- }
-
- for (i = 1; i <= abs(delta_steps)
- && i <= vf_adjust_trim_steps_per_adjust; i++) {
- new_trim = (u8)smbchg_trim_add_steps(prev_trim,
- delta_steps > 0 ? 1 : -1);
- if (new_trim == prev_trim) {
- pr_smb(PR_STATUS,
- "VFloat trim unchanged from %02x\n", prev_trim);
- /* treat no trim change as an error */
- return -EINVAL;
- }
-
- rc = smbchg_sec_masked_write(chip, chip->misc_base + TRIM_14,
- VF_TRIM_MASK, new_trim);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't change vfloat trim rc=%d\n", rc);
- }
- pr_smb(PR_STATUS,
- "VFlt trim %02x to %02x, delta steps: %d\n",
- prev_trim, new_trim, delta_steps);
- prev_trim = new_trim;
- }
-
- return rc;
-}
-
-#define VFLOAT_RESAMPLE_DELAY_MS 10000
-static void smbchg_vfloat_adjust_work(struct work_struct *work)
-{
- struct smbchg_chip *chip = container_of(work,
- struct smbchg_chip,
- vfloat_adjust_work.work);
- int vbat_uv, vbat_mv, ibat_ua, rc, delta_vfloat_mv;
- bool taper, enable;
-
- smbchg_stay_awake(chip, PM_REASON_VFLOAT_ADJUST);
- taper = (get_prop_charge_type(chip)
- == POWER_SUPPLY_CHARGE_TYPE_TAPER);
- enable = taper && (chip->parallel.current_max_ma == 0);
-
- if (!enable) {
- pr_smb(PR_MISC,
- "Stopping vfloat adj taper=%d parallel_ma = %d\n",
- taper, chip->parallel.current_max_ma);
- goto stop;
- }
-
- if (get_prop_batt_health(chip) != POWER_SUPPLY_HEALTH_GOOD) {
- pr_smb(PR_STATUS, "JEITA active, skipping\n");
- goto stop;
- }
-
- set_property_on_fg(chip, POWER_SUPPLY_PROP_UPDATE_NOW, 1);
- rc = get_property_from_fg(chip,
- POWER_SUPPLY_PROP_VOLTAGE_NOW, &vbat_uv);
- if (rc) {
- pr_smb(PR_STATUS,
- "bms psy does not support voltage rc = %d\n", rc);
- goto stop;
- }
- vbat_mv = vbat_uv / 1000;
-
- if ((vbat_mv - chip->vfloat_mv) < -1 * vf_adjust_max_delta_mv) {
- pr_smb(PR_STATUS, "Skip vbat out of range: %d\n", vbat_mv);
- goto reschedule;
- }
-
- rc = get_property_from_fg(chip,
- POWER_SUPPLY_PROP_CURRENT_NOW, &ibat_ua);
- if (rc) {
- pr_smb(PR_STATUS,
- "bms psy does not support current_now rc = %d\n", rc);
- goto stop;
- }
-
- if (ibat_ua / 1000 > -chip->iterm_ma) {
- pr_smb(PR_STATUS, "Skip ibat too high: %d\n", ibat_ua);
- goto reschedule;
- }
-
- pr_smb(PR_STATUS, "sample number = %d vbat_mv = %d ibat_ua = %d\n",
- chip->n_vbat_samples,
- vbat_mv,
- ibat_ua);
-
- chip->max_vbat_sample = max(chip->max_vbat_sample, vbat_mv);
- chip->n_vbat_samples += 1;
- if (chip->n_vbat_samples < vf_adjust_n_samples) {
- pr_smb(PR_STATUS, "Skip %d samples; max = %d\n",
- chip->n_vbat_samples, chip->max_vbat_sample);
- goto reschedule;
- }
- /* if max vbat > target vfloat, delta_vfloat_mv could be negative */
- delta_vfloat_mv = chip->vfloat_mv - chip->max_vbat_sample;
- pr_smb(PR_STATUS, "delta_vfloat_mv = %d, samples = %d, mvbat = %d\n",
- delta_vfloat_mv, chip->n_vbat_samples, chip->max_vbat_sample);
- /*
- * enough valid samples has been collected, adjust trim codes
- * based on maximum of collected vbat samples if necessary
- */
- if (delta_vfloat_mv > vf_adjust_high_threshold
- || delta_vfloat_mv < -1 * vf_adjust_low_threshold) {
- rc = smbchg_adjust_vfloat_mv_trim(chip, delta_vfloat_mv);
- if (rc) {
- pr_smb(PR_STATUS,
- "Stopping vfloat adj after trim adj rc = %d\n",
- rc);
- goto stop;
- }
- chip->max_vbat_sample = 0;
- chip->n_vbat_samples = 0;
- goto reschedule;
- }
-
-stop:
- chip->max_vbat_sample = 0;
- chip->n_vbat_samples = 0;
- smbchg_relax(chip, PM_REASON_VFLOAT_ADJUST);
- return;
-
-reschedule:
- schedule_delayed_work(&chip->vfloat_adjust_work,
- msecs_to_jiffies(VFLOAT_RESAMPLE_DELAY_MS));
- return;
-}
-
-static int smbchg_charging_status_change(struct smbchg_chip *chip)
-{
- smbchg_vfloat_adjust_check(chip);
- set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
- get_prop_batt_status(chip));
- return 0;
-}
-
-#define BB_CLMP_SEL 0xF8
-#define BB_CLMP_MASK SMB_MASK(1, 0)
-#define BB_CLMP_VFIX_3338MV 0x1
-#define BB_CLMP_VFIX_3512MV 0x2
-static int smbchg_set_optimal_charging_mode(struct smbchg_chip *chip, int type)
-{
- int rc;
- bool hvdcp2 = (type == POWER_SUPPLY_TYPE_USB_HVDCP
- && smbchg_is_usbin_active_pwr_src(chip));
-
- /*
- * Set the charger switching freq to 1MHZ if HVDCP 2.0,
- * or 750KHZ otherwise
- */
- rc = smbchg_sec_masked_write(chip,
- chip->bat_if_base + BAT_IF_TRIM7_REG,
- CFG_750KHZ_BIT, hvdcp2 ? 0 : CFG_750KHZ_BIT);
- if (rc) {
- dev_err(chip->dev, "Cannot set switching freq: %d\n", rc);
- return rc;
- }
-
- /*
- * Set the charger switch frequency clamp voltage threshold to 3.338V
- * if HVDCP 2.0, or 3.512V otherwise.
- */
- rc = smbchg_sec_masked_write(chip, chip->bat_if_base + BB_CLMP_SEL,
- BB_CLMP_MASK,
- hvdcp2 ? BB_CLMP_VFIX_3338MV : BB_CLMP_VFIX_3512MV);
- if (rc) {
- dev_err(chip->dev, "Cannot set switching freq: %d\n", rc);
- return rc;
- }
-
- return 0;
-}
-
-#define DEFAULT_SDP_MA 100
-#define DEFAULT_CDP_MA 1500
-static int smbchg_change_usb_supply_type(struct smbchg_chip *chip,
- enum power_supply_type type)
-{
- int rc, current_limit_ma;
-
- /*
- * if the type is not unknown, set the type before changing ICL vote
- * in order to ensure that the correct current limit registers are
- * used
- */
- if (type != POWER_SUPPLY_TYPE_UNKNOWN)
- chip->usb_supply_type = type;
-
- /*
- * Type-C only supports STD(900), MEDIUM(1500) and HIGH(3000) current
- * modes, skip all BC 1.2 current if external typec is supported.
- * Note: for SDP supporting current based on USB notifications.
- */
- if (chip->typec_psy && (type != POWER_SUPPLY_TYPE_USB))
- current_limit_ma = chip->typec_current_ma;
- else if (type == POWER_SUPPLY_TYPE_USB)
- current_limit_ma = DEFAULT_SDP_MA;
- else if (type == POWER_SUPPLY_TYPE_USB_CDP)
- current_limit_ma = DEFAULT_CDP_MA;
- else if (type == POWER_SUPPLY_TYPE_USB_HVDCP)
- current_limit_ma = smbchg_default_hvdcp_icl_ma;
- else if (type == POWER_SUPPLY_TYPE_USB_HVDCP_3)
- current_limit_ma = smbchg_default_hvdcp3_icl_ma;
- else
- current_limit_ma = smbchg_default_dcp_icl_ma;
-
- pr_smb(PR_STATUS, "Type %d: setting mA = %d\n",
- type, current_limit_ma);
- rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true,
- current_limit_ma);
- if (rc < 0) {
- pr_err("Couldn't vote for new USB ICL rc=%d\n", rc);
- goto out;
- }
-
- /* otherwise if it is unknown, set type after the vote */
- if (type == POWER_SUPPLY_TYPE_UNKNOWN)
- chip->usb_supply_type = type;
-
- if (!chip->skip_usb_notification)
- power_supply_changed(chip->usb_psy);
-
- /* set the correct buck switching frequency */
- rc = smbchg_set_optimal_charging_mode(chip, type);
- if (rc < 0)
- pr_err("Couldn't set charger optimal mode rc=%d\n", rc);
-
-out:
- return rc;
-}
-
-#define HVDCP_ADAPTER_SEL_MASK SMB_MASK(5, 4)
-#define HVDCP_5V 0x00
-#define HVDCP_9V 0x10
-#define USB_CMD_HVDCP_1 0x42
-#define FORCE_HVDCP_2p0 BIT(3)
-
-static int force_9v_hvdcp(struct smbchg_chip *chip)
-{
- int rc;
-
- /* Force 5V HVDCP */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
- if (rc) {
- pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n", rc);
- return rc;
- }
-
- /* Force QC2.0 */
- rc = smbchg_masked_write(chip,
- chip->usb_chgpth_base + USB_CMD_HVDCP_1,
- FORCE_HVDCP_2p0, FORCE_HVDCP_2p0);
- rc |= smbchg_masked_write(chip,
- chip->usb_chgpth_base + USB_CMD_HVDCP_1,
- FORCE_HVDCP_2p0, 0);
- if (rc < 0) {
- pr_err("Couldn't force QC2.0 rc=%d\n", rc);
- return rc;
- }
-
- /* Delay to switch into HVDCP 2.0 and avoid UV */
- msleep(500);
-
- /* Force 9V HVDCP */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
- if (rc)
- pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n", rc);
-
- return rc;
-}
-
-static void smbchg_hvdcp_det_work(struct work_struct *work)
-{
- struct smbchg_chip *chip = container_of(work,
- struct smbchg_chip,
- hvdcp_det_work.work);
- int rc;
-
- if (is_hvdcp_present(chip)) {
- if (!chip->hvdcp3_supported &&
- (chip->wa_flags & SMBCHG_HVDCP_9V_EN_WA)) {
- /* force HVDCP 2.0 */
- rc = force_9v_hvdcp(chip);
- if (rc)
- pr_err("could not force 9V HVDCP continuing rc=%d\n",
- rc);
- }
- smbchg_change_usb_supply_type(chip,
- POWER_SUPPLY_TYPE_USB_HVDCP);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_aicl_deglitch_wa_check(chip);
- }
- smbchg_relax(chip, PM_DETECT_HVDCP);
-}
-
-static int set_usb_psy_dp_dm(struct smbchg_chip *chip, int state)
-{
- int rc;
- u8 reg;
- union power_supply_propval pval = {0, };
-
- /*
- * ensure that we are not in the middle of an insertion where usbin_uv
- * is low and src_detect hasnt gone high. If so force dp=F dm=F
- * which guarantees proper type detection
- */
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
- if (!rc && !(reg & USBIN_UV_BIT) && !(reg & USBIN_SRC_DET_BIT)) {
- pr_smb(PR_MISC, "overwriting state = %d with %d\n",
- state, POWER_SUPPLY_DP_DM_DPF_DMF);
- if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
- return regulator_enable(chip->dpdm_reg);
- }
- pr_smb(PR_MISC, "setting usb psy dp dm = %d\n", state);
- pval.intval = state;
- return power_supply_set_property(chip->usb_psy,
- POWER_SUPPLY_PROP_DP_DM, &pval);
-}
-
-#define APSD_CFG 0xF5
-#define AUTO_SRC_DETECT_EN_BIT BIT(0)
-#define APSD_TIMEOUT_MS 1500
-static void restore_from_hvdcp_detection(struct smbchg_chip *chip)
-{
- int rc;
-
- pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
- rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
-
- /* switch to 9V HVDCP */
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
- if (rc < 0)
- pr_err("Couldn't configure HVDCP 9V rc=%d\n", rc);
-
- /* enable HVDCP */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, HVDCP_EN_BIT);
- if (rc < 0)
- pr_err("Couldn't enable HVDCP rc=%d\n", rc);
-
- /* enable APSD */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + APSD_CFG,
- AUTO_SRC_DETECT_EN_BIT, AUTO_SRC_DETECT_EN_BIT);
- if (rc < 0)
- pr_err("Couldn't enable APSD rc=%d\n", rc);
-
- /* Reset back to 5V unregulated */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + USBIN_CHGR_CFG,
- ADAPTER_ALLOWANCE_MASK, USBIN_ADAPTER_5V_UNREGULATED_9V);
- if (rc < 0)
- pr_err("Couldn't write usb allowance rc=%d\n", rc);
-
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, AICL_EN_BIT);
- if (rc < 0)
- pr_err("Couldn't enable AICL rc=%d\n", rc);
-
- chip->hvdcp_3_det_ignore_uv = false;
- chip->pulse_cnt = 0;
-}
-
-#define RESTRICTED_CHG_FCC_PERCENT 50
-static int smbchg_restricted_charging(struct smbchg_chip *chip, bool enable)
-{
- int current_table_index, fastchg_current;
- int rc = 0;
-
- /* If enable, set the fcc to the set point closest
- * to 50% of the configured fcc while remaining below it
- */
- current_table_index = find_smaller_in_array(
- chip->tables.usb_ilim_ma_table,
- chip->cfg_fastchg_current_ma
- * RESTRICTED_CHG_FCC_PERCENT / 100,
- chip->tables.usb_ilim_ma_len);
- fastchg_current =
- chip->tables.usb_ilim_ma_table[current_table_index];
- rc = vote(chip->fcc_votable, RESTRICTED_CHG_FCC_VOTER, enable,
- fastchg_current);
-
- pr_smb(PR_STATUS, "restricted_charging set to %d\n", enable);
- chip->restricted_charging = enable;
-
- return rc;
-}
-
-static void handle_usb_removal(struct smbchg_chip *chip)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval pval = {0, };
- int rc;
-
- pr_smb(PR_STATUS, "triggered\n");
- smbchg_aicl_deglitch_wa_check(chip);
- /* Clear the OV detected status set before */
- if (chip->usb_ov_det)
- chip->usb_ov_det = false;
- /* Clear typec current status */
- if (chip->typec_psy)
- chip->typec_current_ma = 0;
- smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_UNKNOWN);
- extcon_set_cable_state_(chip->extcon, EXTCON_USB, chip->usb_present);
- if (chip->dpdm_reg)
- regulator_disable(chip->dpdm_reg);
- schedule_work(&chip->usb_set_online_work);
-
- pr_smb(PR_MISC, "setting usb psy health UNKNOWN\n");
- chip->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
- power_supply_changed(chip->usb_psy);
-
- if (parallel_psy && chip->parallel_charger_detected) {
- pval.intval = false;
- power_supply_set_property(parallel_psy,
- POWER_SUPPLY_PROP_PRESENT, &pval);
- }
- if (chip->parallel.avail && chip->aicl_done_irq
- && chip->enable_aicl_wake) {
- disable_irq_wake(chip->aicl_done_irq);
- chip->enable_aicl_wake = false;
- }
- chip->parallel.enabled_once = false;
- chip->vbat_above_headroom = false;
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
- ICL_OVERRIDE_BIT, 0);
- if (rc < 0)
- pr_err("Couldn't set override rc = %d\n", rc);
-
- vote(chip->usb_icl_votable, WEAK_CHARGER_ICL_VOTER, false, 0);
- chip->usb_icl_delta = 0;
- vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, false, 0);
- vote(chip->aicl_deglitch_short_votable,
- HVDCP_SHORT_DEGLITCH_VOTER, false, 0);
- if (!chip->hvdcp_not_supported)
- restore_from_hvdcp_detection(chip);
-}
-
-static bool is_usbin_uv_high(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
- return false;
- }
- return reg &= USBIN_UV_BIT;
-}
-
-#define HVDCP_NOTIFY_MS 2500
-static void handle_usb_insertion(struct smbchg_chip *chip)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval pval = {0, };
- enum power_supply_type usb_supply_type;
- int rc;
- char *usb_type_name = "null";
-
- pr_smb(PR_STATUS, "triggered\n");
- /* usb inserted */
- read_usb_type(chip, &usb_type_name, &usb_supply_type);
- pr_smb(PR_STATUS,
- "inserted type = %d (%s)", usb_supply_type, usb_type_name);
-
- smbchg_aicl_deglitch_wa_check(chip);
- if (chip->typec_psy)
- update_typec_status(chip);
- smbchg_change_usb_supply_type(chip, usb_supply_type);
-
- /* Only notify USB if it's not a charger */
- if (usb_supply_type == POWER_SUPPLY_TYPE_USB ||
- usb_supply_type == POWER_SUPPLY_TYPE_USB_CDP)
- extcon_set_cable_state_(chip->extcon, EXTCON_USB,
- chip->usb_present);
-
- /* Notify the USB psy if OV condition is not present */
- if (!chip->usb_ov_det) {
- /*
- * Note that this could still be a very weak charger
- * if the handle_usb_insertion was triggered from
- * the falling edge of an USBIN_OV interrupt
- */
- pr_smb(PR_MISC, "setting usb psy health %s\n",
- chip->very_weak_charger
- ? "UNSPEC_FAILURE" : "GOOD");
- chip->usb_health = chip->very_weak_charger
- ? POWER_SUPPLY_HEALTH_UNSPEC_FAILURE
- : POWER_SUPPLY_HEALTH_GOOD;
- power_supply_changed(chip->usb_psy);
- }
- schedule_work(&chip->usb_set_online_work);
-
- if (!chip->hvdcp_not_supported &&
- (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP)) {
- cancel_delayed_work_sync(&chip->hvdcp_det_work);
- smbchg_stay_awake(chip, PM_DETECT_HVDCP);
- schedule_delayed_work(&chip->hvdcp_det_work,
- msecs_to_jiffies(HVDCP_NOTIFY_MS));
- }
-
- if (parallel_psy) {
- pval.intval = true;
- rc = power_supply_set_property(parallel_psy,
- POWER_SUPPLY_PROP_PRESENT, &pval);
- chip->parallel_charger_detected = rc ? false : true;
- if (rc)
- pr_debug("parallel-charger absent rc=%d\n", rc);
- }
-
- if (chip->parallel.avail && chip->aicl_done_irq
- && !chip->enable_aicl_wake) {
- rc = enable_irq_wake(chip->aicl_done_irq);
- chip->enable_aicl_wake = true;
- }
-}
-
-void update_usb_status(struct smbchg_chip *chip, bool usb_present, bool force)
-{
- mutex_lock(&chip->usb_status_lock);
- if (force) {
- chip->usb_present = usb_present;
- chip->usb_present ? handle_usb_insertion(chip)
- : handle_usb_removal(chip);
- goto unlock;
- }
- if (!chip->usb_present && usb_present) {
- chip->usb_present = usb_present;
- handle_usb_insertion(chip);
- } else if (chip->usb_present && !usb_present) {
- chip->usb_present = usb_present;
- handle_usb_removal(chip);
- }
-
- /* update FG */
- set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
- get_prop_batt_status(chip));
-unlock:
- mutex_unlock(&chip->usb_status_lock);
-}
-
-static int otg_oc_reset(struct smbchg_chip *chip)
-{
- int rc;
-
- rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
- OTG_EN_BIT, 0);
- if (rc)
- pr_err("Failed to disable OTG rc=%d\n", rc);
-
- msleep(20);
-
- /*
- * There is a possibility that an USBID interrupt might have
- * occurred notifying USB power supply to disable OTG. We
- * should not enable OTG in such cases.
- */
- if (!is_otg_present(chip)) {
- pr_smb(PR_STATUS,
- "OTG is not present, not enabling OTG_EN_BIT\n");
- goto out;
- }
-
- rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
- OTG_EN_BIT, OTG_EN_BIT);
- if (rc)
- pr_err("Failed to re-enable OTG rc=%d\n", rc);
-
-out:
- return rc;
-}
-
-static int get_current_time(unsigned long *now_tm_sec)
-{
- struct rtc_time tm;
- struct rtc_device *rtc;
- int rc;
-
- rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
- if (rtc == NULL) {
- pr_err("%s: unable to open rtc device (%s)\n",
- __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
- return -EINVAL;
- }
-
- rc = rtc_read_time(rtc, &tm);
- if (rc) {
- pr_err("Error reading rtc device (%s) : %d\n",
- CONFIG_RTC_HCTOSYS_DEVICE, rc);
- goto close_time;
- }
-
- rc = rtc_valid_tm(&tm);
- if (rc) {
- pr_err("Invalid RTC time (%s): %d\n",
- CONFIG_RTC_HCTOSYS_DEVICE, rc);
- goto close_time;
- }
- rtc_tm_to_time(&tm, now_tm_sec);
-
-close_time:
- rtc_class_close(rtc);
- return rc;
-}
-
-#define AICL_IRQ_LIMIT_SECONDS 60
-#define AICL_IRQ_LIMIT_COUNT 25
-static void increment_aicl_count(struct smbchg_chip *chip)
-{
- bool bad_charger = false;
- int max_aicl_count, rc;
- u8 reg;
- long elapsed_seconds;
- unsigned long now_seconds;
-
- pr_smb(PR_INTERRUPT, "aicl count c:%d dgltch:%d first:%ld\n",
- chip->aicl_irq_count, chip->aicl_deglitch_short,
- chip->first_aicl_seconds);
-
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + ICL_STS_1_REG, 1);
- if (!rc)
- chip->aicl_complete = reg & AICL_STS_BIT;
- else
- chip->aicl_complete = false;
-
- if (chip->aicl_deglitch_short || chip->force_aicl_rerun) {
- if (!chip->aicl_irq_count)
- get_current_time(&chip->first_aicl_seconds);
- get_current_time(&now_seconds);
- elapsed_seconds = now_seconds
- - chip->first_aicl_seconds;
-
- if (elapsed_seconds > AICL_IRQ_LIMIT_SECONDS) {
- pr_smb(PR_INTERRUPT,
- "resetting: elp:%ld first:%ld now:%ld c=%d\n",
- elapsed_seconds, chip->first_aicl_seconds,
- now_seconds, chip->aicl_irq_count);
- chip->aicl_irq_count = 1;
- get_current_time(&chip->first_aicl_seconds);
- return;
- }
- /*
- * Double the amount of AICLs allowed if parallel charging is
- * enabled.
- */
- max_aicl_count = AICL_IRQ_LIMIT_COUNT
- * (chip->parallel.avail ? 2 : 1);
- chip->aicl_irq_count++;
-
- if (chip->aicl_irq_count > max_aicl_count) {
- pr_smb(PR_INTERRUPT, "elp:%ld first:%ld now:%ld c=%d\n",
- elapsed_seconds, chip->first_aicl_seconds,
- now_seconds, chip->aicl_irq_count);
- pr_smb(PR_INTERRUPT, "Disable AICL rerun\n");
- chip->very_weak_charger = true;
- bad_charger = true;
-
- /*
- * Disable AICL rerun since many interrupts were
- * triggered in a short time
- */
- /* disable hw aicl */
- rc = vote(chip->hw_aicl_rerun_disable_votable,
- WEAK_CHARGER_HW_AICL_VOTER, true, 0);
- if (rc < 0) {
- pr_err("Couldn't disable hw aicl rerun rc=%d\n",
- rc);
- return;
- }
-
- /* Vote 100mA current limit */
- rc = vote(chip->usb_icl_votable, WEAK_CHARGER_ICL_VOTER,
- true, CURRENT_100_MA);
- if (rc < 0) {
- pr_err("Can't vote %d current limit rc=%d\n",
- CURRENT_100_MA, rc);
- }
-
- chip->aicl_irq_count = 0;
- } else if ((get_prop_charge_type(chip) ==
- POWER_SUPPLY_CHARGE_TYPE_FAST) &&
- (reg & AICL_SUSP_BIT)) {
- /*
- * If the AICL_SUSP_BIT is on, then AICL reruns have
- * already been disabled. Set the very weak charger
- * flag so that the driver reports a bad charger
- * and does not reenable AICL reruns.
- */
- chip->very_weak_charger = true;
- bad_charger = true;
- }
- if (bad_charger) {
- pr_smb(PR_MISC,
- "setting usb psy health UNSPEC_FAILURE\n");
- chip->usb_health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
- power_supply_changed(chip->usb_psy);
- schedule_work(&chip->usb_set_online_work);
- }
- }
-}
-
-static int wait_for_usbin_uv(struct smbchg_chip *chip, bool high)
-{
- int rc;
- int tries = 3;
- struct completion *completion = &chip->usbin_uv_lowered;
- bool usbin_uv;
-
- if (high)
- completion = &chip->usbin_uv_raised;
-
- while (tries--) {
- rc = wait_for_completion_interruptible_timeout(
- completion,
- msecs_to_jiffies(APSD_TIMEOUT_MS));
- if (rc >= 0)
- break;
- }
-
- usbin_uv = is_usbin_uv_high(chip);
-
- if (high == usbin_uv)
- return 0;
-
- pr_err("usbin uv didnt go to a %s state, still at %s, tries = %d, rc = %d\n",
- high ? "risen" : "lowered",
- usbin_uv ? "high" : "low",
- tries, rc);
- return -EINVAL;
-}
-
-static int wait_for_src_detect(struct smbchg_chip *chip, bool high)
-{
- int rc;
- int tries = 3;
- struct completion *completion = &chip->src_det_lowered;
- bool src_detect;
-
- if (high)
- completion = &chip->src_det_raised;
-
- while (tries--) {
- rc = wait_for_completion_interruptible_timeout(
- completion,
- msecs_to_jiffies(APSD_TIMEOUT_MS));
- if (rc >= 0)
- break;
- }
-
- src_detect = is_src_detect_high(chip);
-
- if (high == src_detect)
- return 0;
-
- pr_err("src detect didnt go to a %s state, still at %s, tries = %d, rc = %d\n",
- high ? "risen" : "lowered",
- src_detect ? "high" : "low",
- tries, rc);
- return -EINVAL;
-}
-
-static int fake_insertion_removal(struct smbchg_chip *chip, bool insertion)
-{
- int rc;
- bool src_detect;
- bool usbin_uv;
-
- if (insertion) {
- reinit_completion(&chip->src_det_raised);
- reinit_completion(&chip->usbin_uv_lowered);
- } else {
- reinit_completion(&chip->src_det_lowered);
- reinit_completion(&chip->usbin_uv_raised);
- }
-
- /* ensure that usbin uv real time status is in the right state */
- usbin_uv = is_usbin_uv_high(chip);
- if (usbin_uv != insertion) {
- pr_err("Skip faking, usbin uv is already %d\n", usbin_uv);
- return -EINVAL;
- }
-
- /* ensure that src_detect real time status is in the right state */
- src_detect = is_src_detect_high(chip);
- if (src_detect == insertion) {
- pr_err("Skip faking, src detect is already %d\n", src_detect);
- return -EINVAL;
- }
-
- pr_smb(PR_MISC, "Allow only %s charger\n",
- insertion ? "5-9V" : "9V only");
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + USBIN_CHGR_CFG,
- ADAPTER_ALLOWANCE_MASK,
- insertion ?
- USBIN_ADAPTER_5V_9V_CONT : USBIN_ADAPTER_9V);
- if (rc < 0) {
- pr_err("Couldn't write usb allowance rc=%d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Waiting on %s usbin uv\n",
- insertion ? "falling" : "rising");
- rc = wait_for_usbin_uv(chip, !insertion);
- if (rc < 0) {
- pr_err("wait for usbin uv failed rc = %d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Waiting on %s src det\n",
- insertion ? "rising" : "falling");
- rc = wait_for_src_detect(chip, insertion);
- if (rc < 0) {
- pr_err("wait for src detect failed rc = %d\n", rc);
- return rc;
- }
-
- return 0;
-}
-
-static int smbchg_prepare_for_pulsing(struct smbchg_chip *chip)
-{
- int rc = 0;
- u8 reg;
-
- /* switch to 5V HVDCP */
- pr_smb(PR_MISC, "Switch to 5V HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
- if (rc < 0) {
- pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc);
- goto out;
- }
-
- /* wait for HVDCP to lower to 5V */
- msleep(500);
- /*
- * Check if the same hvdcp session is in progress. src_det should be
- * high and that we are still in 5V hvdcp
- */
- if (!is_src_detect_high(chip)) {
- pr_smb(PR_MISC, "src det low after 500mS sleep\n");
- goto out;
- }
-
- /* disable HVDCP */
- pr_smb(PR_MISC, "Disable HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, 0);
- if (rc < 0) {
- pr_err("Couldn't disable HVDCP rc=%d\n", rc);
- goto out;
- }
-
- pr_smb(PR_MISC, "HVDCP voting for 300mA ICL\n");
- rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, true, 300);
- if (rc < 0) {
- pr_err("Couldn't vote for 300mA HVDCP ICL rc=%d\n", rc);
- goto out;
- }
-
- pr_smb(PR_MISC, "Disable AICL\n");
- smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, 0);
-
- chip->hvdcp_3_det_ignore_uv = true;
- /* fake a removal */
- pr_smb(PR_MISC, "Faking Removal\n");
- rc = fake_insertion_removal(chip, false);
- if (rc < 0) {
- pr_err("Couldn't fake removal HVDCP Removed rc=%d\n", rc);
- goto handle_removal;
- }
-
- /* disable APSD */
- pr_smb(PR_MISC, "Disabling APSD\n");
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + APSD_CFG,
- AUTO_SRC_DETECT_EN_BIT, 0);
- if (rc < 0) {
- pr_err("Couldn't disable APSD rc=%d\n", rc);
- goto out;
- }
-
- /* fake an insertion */
- pr_smb(PR_MISC, "Faking Insertion\n");
- rc = fake_insertion_removal(chip, true);
- if (rc < 0) {
- pr_err("Couldn't fake insertion rc=%d\n", rc);
- goto handle_removal;
- }
- chip->hvdcp_3_det_ignore_uv = false;
-
- pr_smb(PR_MISC, "Enable AICL\n");
- smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, AICL_EN_BIT);
-
- set_usb_psy_dp_dm(chip, POWER_SUPPLY_DP_DM_DP0P6_DMF);
- /*
- * DCP will switch to HVDCP in this time by removing the short
- * between DP DM
- */
- msleep(HVDCP_NOTIFY_MS);
- /*
- * Check if the same hvdcp session is in progress. src_det should be
- * high and the usb type should be none since APSD was disabled
- */
- if (!is_src_detect_high(chip)) {
- pr_smb(PR_MISC, "src det low after 2s sleep\n");
- rc = -EINVAL;
- goto out;
- }
-
- smbchg_read(chip, &reg, chip->misc_base + IDEV_STS, 1);
- if ((reg >> TYPE_BITS_OFFSET) != 0) {
- pr_smb(PR_MISC, "type bits set after 2s sleep - abort\n");
- rc = -EINVAL;
- goto out;
- }
-
- set_usb_psy_dp_dm(chip, POWER_SUPPLY_DP_DM_DP0P6_DM3P3);
- /* Wait 60mS after entering continuous mode */
- msleep(60);
-
- return 0;
-out:
- chip->hvdcp_3_det_ignore_uv = false;
- restore_from_hvdcp_detection(chip);
- return rc;
-handle_removal:
- chip->hvdcp_3_det_ignore_uv = false;
- update_usb_status(chip, 0, 0);
- return rc;
-}
-
-static int smbchg_unprepare_for_pulsing(struct smbchg_chip *chip)
-{
- int rc = 0;
-
- if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
- rc = regulator_enable(chip->dpdm_reg);
- if (rc < 0) {
- pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc);
- return rc;
- }
-
- /* switch to 9V HVDCP */
- pr_smb(PR_MISC, "Switch to 9V HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
- if (rc < 0) {
- pr_err("Couldn't configure HVDCP 9V rc=%d\n", rc);
- return rc;
- }
-
- /* enable HVDCP */
- pr_smb(PR_MISC, "Enable HVDCP\n");
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, HVDCP_EN_BIT);
- if (rc < 0) {
- pr_err("Couldn't enable HVDCP rc=%d\n", rc);
- return rc;
- }
-
- /* enable APSD */
- pr_smb(PR_MISC, "Enabling APSD\n");
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + APSD_CFG,
- AUTO_SRC_DETECT_EN_BIT, AUTO_SRC_DETECT_EN_BIT);
- if (rc < 0) {
- pr_err("Couldn't enable APSD rc=%d\n", rc);
- return rc;
- }
-
- /* Disable AICL */
- pr_smb(PR_MISC, "Disable AICL\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, 0);
- if (rc < 0) {
- pr_err("Couldn't disable AICL rc=%d\n", rc);
- return rc;
- }
-
- /* fake a removal */
- chip->hvdcp_3_det_ignore_uv = true;
- pr_smb(PR_MISC, "Faking Removal\n");
- rc = fake_insertion_removal(chip, false);
- if (rc < 0) {
- pr_err("Couldn't fake removal rc=%d\n", rc);
- goto out;
- }
-
- /*
- * reset the enabled once flag for parallel charging so
- * parallel charging can immediately restart after the HVDCP pulsing
- * is complete
- */
- chip->parallel.enabled_once = false;
-
- /* fake an insertion */
- pr_smb(PR_MISC, "Faking Insertion\n");
- rc = fake_insertion_removal(chip, true);
- if (rc < 0) {
- pr_err("Couldn't fake insertion rc=%d\n", rc);
- goto out;
- }
- chip->hvdcp_3_det_ignore_uv = false;
-
- /* Enable AICL */
- pr_smb(PR_MISC, "Enable AICL\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, 0);
- if (rc < 0) {
- pr_err("Couldn't enable AICL rc=%d\n", rc);
- return rc;
- }
-
-out:
- /*
- * There are many QC 2.0 chargers that collapse before the aicl deglitch
- * timer can mitigate. Hence set the aicl deglitch time to a shorter
- * period.
- */
-
- rc = vote(chip->aicl_deglitch_short_votable,
- HVDCP_SHORT_DEGLITCH_VOTER, true, 0);
- if (rc < 0)
- pr_err("Couldn't reduce aicl deglitch rc=%d\n", rc);
-
- pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
- rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
-
- chip->hvdcp_3_det_ignore_uv = false;
- if (!is_src_detect_high(chip)) {
- pr_smb(PR_MISC, "HVDCP removed\n");
- update_usb_status(chip, 0, 0);
- }
- return rc;
-}
-
-#define USB_CMD_APSD 0x41
-#define APSD_RERUN BIT(0)
-static int rerun_apsd(struct smbchg_chip *chip)
-{
- int rc;
-
- reinit_completion(&chip->src_det_raised);
- reinit_completion(&chip->usbin_uv_lowered);
- reinit_completion(&chip->src_det_lowered);
- reinit_completion(&chip->usbin_uv_raised);
-
- /* re-run APSD */
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + USB_CMD_APSD,
- APSD_RERUN, APSD_RERUN);
- if (rc) {
- pr_err("Couldn't re-run APSD rc=%d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Waiting on rising usbin uv\n");
- rc = wait_for_usbin_uv(chip, true);
- if (rc < 0) {
- pr_err("wait for usbin uv failed rc = %d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Waiting on falling src det\n");
- rc = wait_for_src_detect(chip, false);
- if (rc < 0) {
- pr_err("wait for src detect failed rc = %d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Waiting on falling usbin uv\n");
- rc = wait_for_usbin_uv(chip, false);
- if (rc < 0) {
- pr_err("wait for usbin uv failed rc = %d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Waiting on rising src det\n");
- rc = wait_for_src_detect(chip, true);
- if (rc < 0) {
- pr_err("wait for src detect failed rc = %d\n", rc);
- return rc;
- }
-
- return rc;
-}
-
-#define SCHG_LITE_USBIN_HVDCP_5_9V 0x8
-#define SCHG_LITE_USBIN_HVDCP_5_9V_SEL_MASK 0x38
-#define SCHG_LITE_USBIN_HVDCP_SEL_IDLE BIT(3)
-static bool is_hvdcp_5v_cont_mode(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg = 0;
-
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + USBIN_HVDCP_STS, 1);
- if (rc) {
- pr_err("Unable to read HVDCP status rc=%d\n", rc);
- return false;
- }
-
- pr_smb(PR_STATUS, "HVDCP status = %x\n", reg);
-
- if (reg & SCHG_LITE_USBIN_HVDCP_SEL_IDLE) {
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + INPUT_STS, 1);
- if (rc) {
- pr_err("Unable to read INPUT status rc=%d\n", rc);
- return false;
- }
- pr_smb(PR_STATUS, "INPUT status = %x\n", reg);
- if ((reg & SCHG_LITE_USBIN_HVDCP_5_9V_SEL_MASK) ==
- SCHG_LITE_USBIN_HVDCP_5_9V)
- return true;
- }
- return false;
-}
-
-static int smbchg_prepare_for_pulsing_lite(struct smbchg_chip *chip)
-{
- int rc = 0;
-
- /* check if HVDCP is already in 5V continuous mode */
- if (is_hvdcp_5v_cont_mode(chip)) {
- pr_smb(PR_MISC, "HVDCP by default is in 5V continuous mode\n");
- return 0;
- }
-
- /* switch to 5V HVDCP */
- pr_smb(PR_MISC, "Switch to 5V HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
- if (rc < 0) {
- pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc);
- goto out;
- }
-
- /* wait for HVDCP to lower to 5V */
- msleep(500);
- /*
- * Check if the same hvdcp session is in progress. src_det should be
- * high and that we are still in 5V hvdcp
- */
- if (!is_src_detect_high(chip)) {
- pr_smb(PR_MISC, "src det low after 500mS sleep\n");
- goto out;
- }
-
- pr_smb(PR_MISC, "HVDCP voting for 300mA ICL\n");
- rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, true, 300);
- if (rc < 0) {
- pr_err("Couldn't vote for 300mA HVDCP ICL rc=%d\n", rc);
- goto out;
- }
-
- pr_smb(PR_MISC, "Disable AICL\n");
- smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, 0);
-
- chip->hvdcp_3_det_ignore_uv = true;
-
- /* re-run APSD */
- rc = rerun_apsd(chip);
- if (rc) {
- pr_err("APSD rerun failed\n");
- goto out;
- }
-
- chip->hvdcp_3_det_ignore_uv = false;
-
- pr_smb(PR_MISC, "Enable AICL\n");
- smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, AICL_EN_BIT);
- /*
- * DCP will switch to HVDCP in this time by removing the short
- * between DP DM
- */
- msleep(HVDCP_NOTIFY_MS);
- /*
- * Check if the same hvdcp session is in progress. src_det should be
- * high and the usb type should be none since APSD was disabled
- */
- if (!is_src_detect_high(chip)) {
- pr_smb(PR_MISC, "src det low after 2s sleep\n");
- rc = -EINVAL;
- goto out;
- }
-
- /* We are set if HVDCP in 5V continuous mode */
- if (!is_hvdcp_5v_cont_mode(chip)) {
- pr_err("HVDCP could not be set in 5V continuous mode\n");
- goto out;
- }
-
- return 0;
-out:
- chip->hvdcp_3_det_ignore_uv = false;
- restore_from_hvdcp_detection(chip);
- return rc;
-}
-
-static int smbchg_unprepare_for_pulsing_lite(struct smbchg_chip *chip)
-{
- int rc = 0;
-
- pr_smb(PR_MISC, "Forcing 9V HVDCP 2.0\n");
- rc = force_9v_hvdcp(chip);
- if (rc) {
- pr_err("Failed to force 9V HVDCP=%d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
- rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
-
- return rc;
-}
-
-#define CMD_HVDCP_2 0x43
-#define SINGLE_INCREMENT BIT(0)
-#define SINGLE_DECREMENT BIT(1)
-static int smbchg_dp_pulse_lite(struct smbchg_chip *chip)
-{
- int rc = 0;
-
- pr_smb(PR_MISC, "Increment DP\n");
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_HVDCP_2,
- SINGLE_INCREMENT, SINGLE_INCREMENT);
- if (rc)
- pr_err("Single-increment failed rc=%d\n", rc);
-
- return rc;
-}
-
-static int smbchg_dm_pulse_lite(struct smbchg_chip *chip)
-{
- int rc = 0;
-
- pr_smb(PR_MISC, "Decrement DM\n");
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_HVDCP_2,
- SINGLE_DECREMENT, SINGLE_DECREMENT);
- if (rc)
- pr_err("Single-decrement failed rc=%d\n", rc);
-
- return rc;
-}
-
-static int smbchg_hvdcp3_confirmed(struct smbchg_chip *chip)
-{
- int rc = 0;
-
- /*
- * reset the enabled once flag for parallel charging because this is
- * effectively a new insertion.
- */
- chip->parallel.enabled_once = false;
-
- pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
- rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
-
- smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_USB_HVDCP_3);
-
- return rc;
-}
-
-static int smbchg_dp_dm(struct smbchg_chip *chip, int val)
-{
- int rc = 0;
- int target_icl_vote_ma;
-
- switch (val) {
- case POWER_SUPPLY_DP_DM_PREPARE:
- if (!is_hvdcp_present(chip)) {
- pr_err("No pulsing unless HVDCP\n");
- return -ENODEV;
- }
- if (chip->schg_version == QPNP_SCHG_LITE)
- rc = smbchg_prepare_for_pulsing_lite(chip);
- else
- rc = smbchg_prepare_for_pulsing(chip);
- break;
- case POWER_SUPPLY_DP_DM_UNPREPARE:
- if (chip->schg_version == QPNP_SCHG_LITE)
- rc = smbchg_unprepare_for_pulsing_lite(chip);
- else
- rc = smbchg_unprepare_for_pulsing(chip);
- break;
- case POWER_SUPPLY_DP_DM_CONFIRMED_HVDCP3:
- rc = smbchg_hvdcp3_confirmed(chip);
- break;
- case POWER_SUPPLY_DP_DM_DP_PULSE:
- if (chip->schg_version == QPNP_SCHG)
- rc = set_usb_psy_dp_dm(chip,
- POWER_SUPPLY_DP_DM_DP_PULSE);
- else
- rc = smbchg_dp_pulse_lite(chip);
- if (!rc)
- chip->pulse_cnt++;
- pr_smb(PR_MISC, "pulse_cnt = %d\n", chip->pulse_cnt);
- break;
- case POWER_SUPPLY_DP_DM_DM_PULSE:
- if (chip->schg_version == QPNP_SCHG)
- rc = set_usb_psy_dp_dm(chip,
- POWER_SUPPLY_DP_DM_DM_PULSE);
- else
- rc = smbchg_dm_pulse_lite(chip);
- if (!rc && chip->pulse_cnt)
- chip->pulse_cnt--;
- pr_smb(PR_MISC, "pulse_cnt = %d\n", chip->pulse_cnt);
- break;
- case POWER_SUPPLY_DP_DM_HVDCP3_SUPPORTED:
- chip->hvdcp3_supported = true;
- pr_smb(PR_MISC, "HVDCP3 supported\n");
- break;
- case POWER_SUPPLY_DP_DM_ICL_DOWN:
- chip->usb_icl_delta -= 100;
- target_icl_vote_ma = get_client_vote(chip->usb_icl_votable,
- PSY_ICL_VOTER);
- vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, true,
- target_icl_vote_ma + chip->usb_icl_delta);
- break;
- case POWER_SUPPLY_DP_DM_ICL_UP:
- chip->usb_icl_delta += 100;
- target_icl_vote_ma = get_client_vote(chip->usb_icl_votable,
- PSY_ICL_VOTER);
- vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, true,
- target_icl_vote_ma + chip->usb_icl_delta);
- break;
- default:
- break;
- }
-
- return rc;
-}
-
-static void update_typec_capability_status(struct smbchg_chip *chip,
- const union power_supply_propval *val)
-{
- pr_smb(PR_TYPEC, "typec capability = %dma\n", val->intval);
-
- pr_debug("changing ICL from %dma to %dma\n", chip->typec_current_ma,
- val->intval);
- chip->typec_current_ma = val->intval;
- smbchg_change_usb_supply_type(chip, chip->usb_supply_type);
-}
-
-static void update_typec_otg_status(struct smbchg_chip *chip, int mode,
- bool force)
-{
- union power_supply_propval pval = {0, };
- pr_smb(PR_TYPEC, "typec mode = %d\n", mode);
-
- if (mode == POWER_SUPPLY_TYPE_DFP) {
- chip->typec_dfp = true;
- pval.intval = 1;
- extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST,
- chip->typec_dfp);
- /* update FG */
- set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
- get_prop_batt_status(chip));
- } else if (force || chip->typec_dfp) {
- chip->typec_dfp = false;
- pval.intval = 0;
- extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST,
- chip->typec_dfp);
- /* update FG */
- set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
- get_prop_batt_status(chip));
- }
-}
-
-static int smbchg_usb_get_property(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- struct smbchg_chip *chip = power_supply_get_drvdata(psy);
-
- switch (psp) {
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- val->intval = chip->usb_current_max;
- break;
- case POWER_SUPPLY_PROP_PRESENT:
- val->intval = chip->usb_present;
- break;
- case POWER_SUPPLY_PROP_ONLINE:
- val->intval = chip->usb_online;
- break;
- case POWER_SUPPLY_PROP_TYPE:
- val->intval = chip->usb_supply_type;
- break;
- case POWER_SUPPLY_PROP_HEALTH:
- val->intval = chip->usb_health;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int smbchg_usb_set_property(struct power_supply *psy,
- enum power_supply_property psp,
- const union power_supply_propval *val)
-{
- struct smbchg_chip *chip = power_supply_get_drvdata(psy);
-
- switch (psp) {
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- chip->usb_current_max = val->intval;
- break;
- case POWER_SUPPLY_PROP_ONLINE:
- chip->usb_online = val->intval;
- break;
- default:
- return -EINVAL;
- }
-
- power_supply_changed(psy);
- return 0;
-}
-
-static int
-smbchg_usb_is_writeable(struct power_supply *psy, enum power_supply_property psp)
-{
- switch (psp) {
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- return 1;
- default:
- break;
- }
-
- return 0;
-}
-
-
-static char *smbchg_usb_supplicants[] = {
- "battery",
- "bms",
-};
-
-static enum power_supply_property smbchg_usb_properties[] = {
- POWER_SUPPLY_PROP_PRESENT,
- POWER_SUPPLY_PROP_ONLINE,
- POWER_SUPPLY_PROP_CURRENT_MAX,
- POWER_SUPPLY_PROP_TYPE,
- POWER_SUPPLY_PROP_HEALTH,
-};
-
-#define CHARGE_OUTPUT_VTG_RATIO 840
-static int smbchg_get_iusb(struct smbchg_chip *chip)
-{
- int rc, iusb_ua = -EINVAL;
- struct qpnp_vadc_result adc_result;
-
- if (!is_usb_present(chip) && !is_dc_present(chip))
- return 0;
-
- if (chip->vchg_vadc_dev && chip->vchg_adc_channel != -EINVAL) {
- rc = qpnp_vadc_read(chip->vchg_vadc_dev,
- chip->vchg_adc_channel, &adc_result);
- if (rc) {
- pr_smb(PR_STATUS,
- "error in VCHG (channel-%d) read rc = %d\n",
- chip->vchg_adc_channel, rc);
- return 0;
- }
- iusb_ua = div_s64(adc_result.physical * 1000,
- CHARGE_OUTPUT_VTG_RATIO);
- }
-
- return iusb_ua;
-}
-
-static enum power_supply_property smbchg_battery_properties[] = {
- POWER_SUPPLY_PROP_STATUS,
- POWER_SUPPLY_PROP_PRESENT,
- POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
- POWER_SUPPLY_PROP_CHARGING_ENABLED,
- POWER_SUPPLY_PROP_CHARGE_TYPE,
- POWER_SUPPLY_PROP_CAPACITY,
- POWER_SUPPLY_PROP_HEALTH,
- POWER_SUPPLY_PROP_TECHNOLOGY,
- POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
- POWER_SUPPLY_PROP_FLASH_CURRENT_MAX,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
- POWER_SUPPLY_PROP_VOLTAGE_MAX,
- POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
- POWER_SUPPLY_PROP_CURRENT_NOW,
- POWER_SUPPLY_PROP_TEMP,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE,
- POWER_SUPPLY_PROP_INPUT_CURRENT_MAX,
- POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
- POWER_SUPPLY_PROP_INPUT_CURRENT_NOW,
- POWER_SUPPLY_PROP_FLASH_ACTIVE,
- POWER_SUPPLY_PROP_FLASH_TRIGGER,
- POWER_SUPPLY_PROP_DP_DM,
- POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
- POWER_SUPPLY_PROP_RERUN_AICL,
- POWER_SUPPLY_PROP_RESTRICTED_CHARGING,
-};
-
-static int smbchg_battery_set_property(struct power_supply *psy,
- enum power_supply_property prop,
- const union power_supply_propval *val)
-{
- int rc = 0;
- struct smbchg_chip *chip = power_supply_get_drvdata(psy);
-
- switch (prop) {
- case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
- vote(chip->battchg_suspend_votable, BATTCHG_USER_EN_VOTER,
- !val->intval, 0);
- break;
- case POWER_SUPPLY_PROP_CHARGING_ENABLED:
- rc = vote(chip->usb_suspend_votable, USER_EN_VOTER,
- !val->intval, 0);
- rc = vote(chip->dc_suspend_votable, USER_EN_VOTER,
- !val->intval, 0);
- chip->chg_enabled = val->intval;
- schedule_work(&chip->usb_set_online_work);
- break;
- case POWER_SUPPLY_PROP_CAPACITY:
- chip->fake_battery_soc = val->intval;
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- break;
- case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
- smbchg_system_temp_level_set(chip, val->intval);
- break;
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
- rc = smbchg_set_fastchg_current_user(chip, val->intval / 1000);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MAX:
- rc = smbchg_float_voltage_set(chip, val->intval);
- break;
- case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE:
- rc = smbchg_safety_timer_enable(chip, val->intval);
- break;
- case POWER_SUPPLY_PROP_FLASH_ACTIVE:
- rc = smbchg_switch_buck_frequency(chip, val->intval);
- if (rc) {
- pr_err("Couldn't switch buck frequency, rc=%d\n", rc);
- /*
- * Trigger a panic if there is an error while switching
- * buck frequency. This will prevent LS FET damage.
- */
- BUG_ON(1);
- }
-
- rc = smbchg_otg_pulse_skip_disable(chip,
- REASON_FLASH_ENABLED, val->intval);
- break;
- case POWER_SUPPLY_PROP_FLASH_TRIGGER:
- chip->flash_triggered = !!val->intval;
- smbchg_icl_loop_disable_check(chip);
- break;
- case POWER_SUPPLY_PROP_FORCE_TLIM:
- rc = smbchg_force_tlim_en(chip, val->intval);
- break;
- case POWER_SUPPLY_PROP_DP_DM:
- rc = smbchg_dp_dm(chip, val->intval);
- break;
- case POWER_SUPPLY_PROP_RERUN_AICL:
- smbchg_rerun_aicl(chip);
- break;
- case POWER_SUPPLY_PROP_RESTRICTED_CHARGING:
- rc = smbchg_restricted_charging(chip, val->intval);
- break;
- case POWER_SUPPLY_PROP_CURRENT_CAPABILITY:
- if (chip->typec_psy)
- update_typec_capability_status(chip, val);
- break;
- case POWER_SUPPLY_PROP_TYPEC_MODE:
- if (chip->typec_psy)
- update_typec_otg_status(chip, val->intval, false);
- break;
- default:
- return -EINVAL;
- }
-
- return rc;
-}
-
-static int smbchg_battery_is_writeable(struct power_supply *psy,
- enum power_supply_property prop)
-{
- int rc;
-
- switch (prop) {
- case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
- case POWER_SUPPLY_PROP_CHARGING_ENABLED:
- case POWER_SUPPLY_PROP_CAPACITY:
- case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
- case POWER_SUPPLY_PROP_VOLTAGE_MAX:
- case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE:
- case POWER_SUPPLY_PROP_DP_DM:
- case POWER_SUPPLY_PROP_RERUN_AICL:
- case POWER_SUPPLY_PROP_RESTRICTED_CHARGING:
- rc = 1;
- break;
- default:
- rc = 0;
- break;
- }
- return rc;
-}
-
-static int smbchg_battery_get_property(struct power_supply *psy,
- enum power_supply_property prop,
- union power_supply_propval *val)
-{
- struct smbchg_chip *chip = power_supply_get_drvdata(psy);
-
- switch (prop) {
- case POWER_SUPPLY_PROP_STATUS:
- val->intval = get_prop_batt_status(chip);
- break;
- case POWER_SUPPLY_PROP_PRESENT:
- val->intval = get_prop_batt_present(chip);
- break;
- case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
- val->intval
- = get_effective_result(chip->battchg_suspend_votable);
- if (val->intval < 0) /* no votes */
- val->intval = 1;
- else
- val->intval = !val->intval;
- break;
- case POWER_SUPPLY_PROP_CHARGING_ENABLED:
- val->intval = chip->chg_enabled;
- break;
- case POWER_SUPPLY_PROP_CHARGE_TYPE:
- val->intval = get_prop_charge_type(chip);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MAX:
- val->intval = smbchg_float_voltage_get(chip);
- break;
- case POWER_SUPPLY_PROP_HEALTH:
- val->intval = get_prop_batt_health(chip);
- break;
- case POWER_SUPPLY_PROP_TECHNOLOGY:
- val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
- break;
- case POWER_SUPPLY_PROP_FLASH_CURRENT_MAX:
- val->intval = smbchg_calc_max_flash_current(chip);
- break;
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
- val->intval = chip->fastchg_current_ma * 1000;
- break;
- case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
- val->intval = chip->therm_lvl_sel;
- break;
- case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX:
- val->intval = smbchg_get_aicl_level_ma(chip) * 1000;
- break;
- case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED:
- val->intval = (int)chip->aicl_complete;
- break;
- case POWER_SUPPLY_PROP_RESTRICTED_CHARGING:
- val->intval = (int)chip->restricted_charging;
- break;
- /* properties from fg */
- case POWER_SUPPLY_PROP_CAPACITY:
- val->intval = get_prop_batt_capacity(chip);
- break;
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- val->intval = get_prop_batt_current_now(chip);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = get_prop_batt_voltage_now(chip);
- break;
- case POWER_SUPPLY_PROP_TEMP:
- val->intval = get_prop_batt_temp(chip);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
- val->intval = get_prop_batt_voltage_max_design(chip);
- break;
- case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE:
- val->intval = chip->safety_timer_en;
- break;
- case POWER_SUPPLY_PROP_FLASH_ACTIVE:
- val->intval = chip->otg_pulse_skip_dis;
- break;
- case POWER_SUPPLY_PROP_FLASH_TRIGGER:
- val->intval = chip->flash_triggered;
- break;
- case POWER_SUPPLY_PROP_DP_DM:
- val->intval = chip->pulse_cnt;
- break;
- case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
- val->intval = smbchg_is_input_current_limited(chip);
- break;
- case POWER_SUPPLY_PROP_RERUN_AICL:
- val->intval = 0;
- break;
- case POWER_SUPPLY_PROP_INPUT_CURRENT_NOW:
- val->intval = smbchg_get_iusb(chip);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static char *smbchg_dc_supplicants[] = {
- "bms",
-};
-
-static enum power_supply_property smbchg_dc_properties[] = {
- POWER_SUPPLY_PROP_PRESENT,
- POWER_SUPPLY_PROP_ONLINE,
- POWER_SUPPLY_PROP_CHARGING_ENABLED,
- POWER_SUPPLY_PROP_CURRENT_MAX,
-};
-
-static int smbchg_dc_set_property(struct power_supply *psy,
- enum power_supply_property prop,
- const union power_supply_propval *val)
-{
- int rc = 0;
- struct smbchg_chip *chip = power_supply_get_drvdata(psy);
-
- switch (prop) {
- case POWER_SUPPLY_PROP_CHARGING_ENABLED:
- rc = vote(chip->dc_suspend_votable, POWER_SUPPLY_EN_VOTER,
- !val->intval, 0);
- break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- rc = vote(chip->dc_icl_votable, USER_ICL_VOTER, true,
- val->intval / 1000);
- break;
- default:
- return -EINVAL;
- }
-
- return rc;
-}
-
-static int smbchg_dc_get_property(struct power_supply *psy,
- enum power_supply_property prop,
- union power_supply_propval *val)
-{
- struct smbchg_chip *chip = power_supply_get_drvdata(psy);
-
- switch (prop) {
- case POWER_SUPPLY_PROP_PRESENT:
- val->intval = is_dc_present(chip);
- break;
- case POWER_SUPPLY_PROP_CHARGING_ENABLED:
- val->intval = get_effective_result(chip->dc_suspend_votable);
- if (val->intval < 0) /* no votes */
- val->intval = 1;
- else
- val->intval = !val->intval;
- break;
- case POWER_SUPPLY_PROP_ONLINE:
- /* return if dc is charging the battery */
- val->intval = (smbchg_get_pwr_path(chip) == PWR_PATH_DC)
- && (get_prop_batt_status(chip)
- == POWER_SUPPLY_STATUS_CHARGING);
- break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- val->intval = chip->dc_max_current_ma * 1000;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int smbchg_dc_is_writeable(struct power_supply *psy,
- enum power_supply_property prop)
-{
- int rc;
-
- switch (prop) {
- case POWER_SUPPLY_PROP_CHARGING_ENABLED:
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- rc = 1;
- break;
- default:
- rc = 0;
- break;
- }
- return rc;
-}
-
-#define HOT_BAT_HARD_BIT BIT(0)
-#define HOT_BAT_SOFT_BIT BIT(1)
-#define COLD_BAT_HARD_BIT BIT(2)
-#define COLD_BAT_SOFT_BIT BIT(3)
-#define BAT_OV_BIT BIT(4)
-#define BAT_LOW_BIT BIT(5)
-#define BAT_MISSING_BIT BIT(6)
-#define BAT_TERM_MISSING_BIT BIT(7)
-static irqreturn_t batt_hot_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
- chip->batt_hot = !!(reg & HOT_BAT_HARD_BIT);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- smbchg_wipower_check(chip);
- set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
- get_prop_batt_health(chip));
- return IRQ_HANDLED;
-}
-
-static irqreturn_t batt_cold_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
- chip->batt_cold = !!(reg & COLD_BAT_HARD_BIT);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- smbchg_wipower_check(chip);
- set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
- get_prop_batt_health(chip));
- return IRQ_HANDLED;
-}
-
-static irqreturn_t batt_warm_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
- chip->batt_warm = !!(reg & HOT_BAT_SOFT_BIT);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
- get_prop_batt_health(chip));
- return IRQ_HANDLED;
-}
-
-static irqreturn_t batt_cool_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
- chip->batt_cool = !!(reg & COLD_BAT_SOFT_BIT);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
- get_prop_batt_health(chip));
- return IRQ_HANDLED;
-}
-
-static irqreturn_t batt_pres_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
- chip->batt_present = !(reg & BAT_MISSING_BIT);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
- get_prop_batt_health(chip));
- return IRQ_HANDLED;
-}
-
-static irqreturn_t vbat_low_handler(int irq, void *_chip)
-{
- pr_warn_ratelimited("vbat low\n");
- return IRQ_HANDLED;
-}
-
-#define CHG_COMP_SFT_BIT BIT(3)
-static irqreturn_t chg_error_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- int rc = 0;
- u8 reg;
-
- pr_smb(PR_INTERRUPT, "chg-error triggered\n");
-
- rc = smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Unable to read RT_STS rc = %d\n", rc);
- } else {
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- if (reg & CHG_COMP_SFT_BIT)
- set_property_on_fg(chip,
- POWER_SUPPLY_PROP_SAFETY_TIMER_EXPIRED,
- 1);
- }
-
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- smbchg_wipower_check(chip);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t fastchg_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
-
- pr_smb(PR_INTERRUPT, "p2f triggered\n");
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- smbchg_wipower_check(chip);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t chg_hot_handler(int irq, void *_chip)
-{
- pr_warn_ratelimited("chg hot\n");
- smbchg_wipower_check(_chip);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t chg_term_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
-
- pr_smb(PR_INTERRUPT, "tcc triggered\n");
- /*
- * Charge termination is a pulse and not level triggered. That means,
- * TCC bit in RT_STS can get cleared by the time this interrupt is
- * handled. Instead of relying on that to determine whether the
- * charge termination had happened, we've to simply notify the FG
- * about this as long as the interrupt is handled.
- */
- set_property_on_fg(chip, POWER_SUPPLY_PROP_CHARGE_DONE, 1);
-
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t taper_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- taper_irq_en(chip, false);
- smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- smbchg_parallel_usb_taper(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- smbchg_wipower_check(chip);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t recharge_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t wdog_timeout_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->misc_base + RT_STS, 1);
- pr_warn_ratelimited("wdog timeout rt_stat = 0x%02x\n", reg);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- return IRQ_HANDLED;
-}
-
-/**
- * power_ok_handler() - called when the switcher turns on or turns off
- * @chip: pointer to smbchg_chip
- * @rt_stat: the status bit indicating switcher turning on or off
- */
-static irqreturn_t power_ok_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->misc_base + RT_STS, 1);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- return IRQ_HANDLED;
-}
-
-/**
- * dcin_uv_handler() - called when the dc voltage crosses the uv threshold
- * @chip: pointer to smbchg_chip
- * @rt_stat: the status bit indicating whether dc voltage is uv
- */
-#define DCIN_UNSUSPEND_DELAY_MS 1000
-static irqreturn_t dcin_uv_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- bool dc_present = is_dc_present(chip);
-
- pr_smb(PR_STATUS, "chip->dc_present = %d dc_present = %d\n",
- chip->dc_present, dc_present);
-
- if (chip->dc_present != dc_present) {
- /* dc changed */
- chip->dc_present = dc_present;
- if (chip->dc_psy_type != -EINVAL && chip->batt_psy)
- power_supply_changed(chip->dc_psy);
- smbchg_charging_status_change(chip);
- smbchg_aicl_deglitch_wa_check(chip);
- chip->vbat_above_headroom = false;
- }
-
- smbchg_wipower_check(chip);
- return IRQ_HANDLED;
-}
-
-/**
- * usbin_ov_handler() - this is called when an overvoltage condition occurs
- * @chip: pointer to smbchg_chip chip
- */
-static irqreturn_t usbin_ov_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- int rc;
- u8 reg;
- bool usb_present;
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
- goto out;
- }
-
- /* OV condition is detected. Notify it to USB psy */
- if (reg & USBIN_OV_BIT) {
- chip->usb_ov_det = true;
- pr_smb(PR_MISC, "setting usb psy health OV\n");
- chip->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
- power_supply_changed(chip->usb_psy);
- } else {
- chip->usb_ov_det = false;
- /* If USB is present, then handle the USB insertion */
- usb_present = is_usb_present(chip);
- if (usb_present)
- update_usb_status(chip, usb_present, false);
- }
-out:
- return IRQ_HANDLED;
-}
-
-/**
- * usbin_uv_handler() - this is called when USB charger is removed
- * @chip: pointer to smbchg_chip chip
- * @rt_stat: the status bit indicating chg insertion/removal
- */
-#define ICL_MODE_MASK SMB_MASK(5, 4)
-#define ICL_MODE_HIGH_CURRENT 0
-static irqreturn_t usbin_uv_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- int aicl_level = smbchg_get_aicl_level_ma(chip);
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
- if (rc) {
- pr_err("could not read rt sts: %d", rc);
- goto out;
- }
-
- pr_smb(PR_STATUS,
- "%s chip->usb_present = %d rt_sts = 0x%02x hvdcp_3_det_ignore_uv = %d aicl = %d\n",
- chip->hvdcp_3_det_ignore_uv ? "Ignoring":"",
- chip->usb_present, reg, chip->hvdcp_3_det_ignore_uv,
- aicl_level);
-
- /*
- * set usb_psy's dp=f dm=f if this is a new insertion, i.e. it is
- * not already src_detected and usbin_uv is seen falling
- */
- if (!(reg & USBIN_UV_BIT) && !(reg & USBIN_SRC_DET_BIT)) {
- pr_smb(PR_MISC, "setting usb dp=f dm=f\n");
- if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
- rc = regulator_enable(chip->dpdm_reg);
- if (rc < 0) {
- pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc);
- return rc;
- }
- }
-
- if (reg & USBIN_UV_BIT)
- complete_all(&chip->usbin_uv_raised);
- else
- complete_all(&chip->usbin_uv_lowered);
-
- if (chip->hvdcp_3_det_ignore_uv)
- goto out;
-
- if ((reg & USBIN_UV_BIT) && (reg & USBIN_SRC_DET_BIT)) {
- pr_smb(PR_STATUS, "Very weak charger detected\n");
- chip->very_weak_charger = true;
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + ICL_STS_2_REG, 1);
- if (rc) {
- dev_err(chip->dev, "Could not read usb icl sts 2: %d\n",
- rc);
- goto out;
- }
- if ((reg & ICL_MODE_MASK) != ICL_MODE_HIGH_CURRENT) {
- /*
- * If AICL is not even enabled, this is either an
- * SDP or a grossly out of spec charger. Do not
- * draw any current from it.
- */
- rc = vote(chip->usb_suspend_votable,
- WEAK_CHARGER_EN_VOTER, true, 0);
- if (rc < 0)
- pr_err("could not disable charger: %d", rc);
- } else if (aicl_level == chip->tables.usb_ilim_ma_table[0]) {
- /*
- * we are in a situation where the adapter is not able
- * to supply even 300mA. Disable hw aicl reruns else it
- * is only a matter of time when we get back here again
- */
- rc = vote(chip->hw_aicl_rerun_disable_votable,
- WEAK_CHARGER_HW_AICL_VOTER, true, 0);
- if (rc < 0)
- pr_err("Couldn't disable hw aicl rerun rc=%d\n",
- rc);
- }
- pr_smb(PR_MISC, "setting usb psy health UNSPEC_FAILURE\n");
- chip->usb_health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
- power_supply_changed(chip->usb_psy);
- schedule_work(&chip->usb_set_online_work);
- }
-
- smbchg_wipower_check(chip);
-out:
- return IRQ_HANDLED;
-}
-
-/**
- * src_detect_handler() - this is called on rising edge when USB charger type
- * is detected and on falling edge when USB voltage falls
- * below the coarse detect voltage(1V), use it for
- * handling USB charger insertion and removal.
- * @chip: pointer to smbchg_chip
- * @rt_stat: the status bit indicating chg insertion/removal
- */
-static irqreturn_t src_detect_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- bool usb_present = is_usb_present(chip);
- bool src_detect = is_src_detect_high(chip);
- int rc;
-
- pr_smb(PR_STATUS,
- "%s chip->usb_present = %d usb_present = %d src_detect = %d hvdcp_3_det_ignore_uv=%d\n",
- chip->hvdcp_3_det_ignore_uv ? "Ignoring":"",
- chip->usb_present, usb_present, src_detect,
- chip->hvdcp_3_det_ignore_uv);
-
- if (src_detect)
- complete_all(&chip->src_det_raised);
- else
- complete_all(&chip->src_det_lowered);
-
- if (chip->hvdcp_3_det_ignore_uv)
- goto out;
-
- /*
- * When VBAT is above the AICL threshold (4.25V) - 180mV (4.07V),
- * an input collapse due to AICL will actually cause an USBIN_UV
- * interrupt to fire as well.
- *
- * Handle USB insertions and removals in the source detect handler
- * instead of the USBIN_UV handler since the latter is untrustworthy
- * when the battery voltage is high.
- */
- chip->very_weak_charger = false;
- /*
- * a src detect marks a new insertion or a real removal,
- * vote for enable aicl hw reruns
- */
- rc = vote(chip->hw_aicl_rerun_disable_votable,
- WEAK_CHARGER_HW_AICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't enable hw aicl rerun rc=%d\n", rc);
-
- rc = vote(chip->usb_suspend_votable, WEAK_CHARGER_EN_VOTER, false, 0);
- if (rc < 0)
- pr_err("could not enable charger: %d\n", rc);
-
- if (src_detect) {
- update_usb_status(chip, usb_present, 0);
- } else {
- update_usb_status(chip, 0, false);
- chip->aicl_irq_count = 0;
- }
-out:
- return IRQ_HANDLED;
-}
-
-/**
- * otg_oc_handler() - called when the usb otg goes over current
- */
-#define NUM_OTG_RETRIES 5
-#define OTG_OC_RETRY_DELAY_US 50000
-static irqreturn_t otg_oc_handler(int irq, void *_chip)
-{
- int rc;
- struct smbchg_chip *chip = _chip;
- s64 elapsed_us = ktime_us_delta(ktime_get(), chip->otg_enable_time);
-
- pr_smb(PR_INTERRUPT, "triggered\n");
-
- if (chip->schg_version == QPNP_SCHG_LITE) {
- pr_warn("OTG OC triggered - OTG disabled\n");
- return IRQ_HANDLED;
- }
-
- if (elapsed_us > OTG_OC_RETRY_DELAY_US)
- chip->otg_retries = 0;
-
- /*
- * Due to a HW bug in the PMI8994 charger, the current inrush that
- * occurs when connecting certain OTG devices can cause the OTG
- * overcurrent protection to trip.
- *
- * The work around is to try reenabling the OTG when getting an
- * overcurrent interrupt once.
- */
- if (chip->otg_retries < NUM_OTG_RETRIES) {
- chip->otg_retries += 1;
- pr_smb(PR_STATUS,
- "Retrying OTG enable. Try #%d, elapsed_us %lld\n",
- chip->otg_retries, elapsed_us);
- rc = otg_oc_reset(chip);
- if (rc)
- pr_err("Failed to reset OTG OC state rc=%d\n", rc);
- chip->otg_enable_time = ktime_get();
- }
- return IRQ_HANDLED;
-}
-
-/**
- * otg_fail_handler() - called when the usb otg fails
- * (when vbat < OTG UVLO threshold)
- */
-static irqreturn_t otg_fail_handler(int irq, void *_chip)
-{
- pr_smb(PR_INTERRUPT, "triggered\n");
- return IRQ_HANDLED;
-}
-
-/**
- * aicl_done_handler() - called when the usb AICL algorithm is finished
- * and a current is set.
- */
-static irqreturn_t aicl_done_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- bool usb_present = is_usb_present(chip);
- int aicl_level = smbchg_get_aicl_level_ma(chip);
-
- pr_smb(PR_INTERRUPT, "triggered, aicl: %d\n", aicl_level);
-
- increment_aicl_count(chip);
-
- if (usb_present)
- smbchg_parallel_usb_check_ok(chip);
-
- if (chip->aicl_complete && chip->batt_psy)
- power_supply_changed(chip->batt_psy);
-
- return IRQ_HANDLED;
-}
-
-/**
- * usbid_change_handler() - called when the usb RID changes.
- * This is used mostly for detecting OTG
- */
-static irqreturn_t usbid_change_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- bool otg_present;
-
- pr_smb(PR_INTERRUPT, "triggered\n");
-
- otg_present = is_otg_present(chip);
- pr_smb(PR_MISC, "setting usb psy OTG = %d\n",
- otg_present ? 1 : 0);
-
- extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST, otg_present);
-
- if (otg_present)
- pr_smb(PR_STATUS, "OTG detected\n");
-
- /* update FG */
- set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
- get_prop_batt_status(chip));
-
- return IRQ_HANDLED;
-}
-
-static int determine_initial_status(struct smbchg_chip *chip)
-{
- union power_supply_propval type = {0, };
-
- /*
- * It is okay to read the interrupt status here since
- * interrupts aren't requested. reading interrupt status
- * clears the interrupt so be careful to read interrupt
- * status only in interrupt handling code
- */
-
- batt_pres_handler(0, chip);
- batt_hot_handler(0, chip);
- batt_warm_handler(0, chip);
- batt_cool_handler(0, chip);
- batt_cold_handler(0, chip);
- if (chip->typec_psy) {
- get_property_from_typec(chip, POWER_SUPPLY_PROP_TYPE, &type);
- update_typec_otg_status(chip, type.intval, true);
- } else {
- usbid_change_handler(0, chip);
- }
- src_detect_handler(0, chip);
-
- chip->usb_present = is_usb_present(chip);
- chip->dc_present = is_dc_present(chip);
-
- if (chip->usb_present) {
- int rc = 0;
- pr_smb(PR_MISC, "setting usb dp=f dm=f\n");
- if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
- rc = regulator_enable(chip->dpdm_reg);
- if (rc < 0) {
- pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc);
- return rc;
- }
- handle_usb_insertion(chip);
- } else {
- handle_usb_removal(chip);
- }
-
- return 0;
-}
-
-static int prechg_time[] = {
- 24,
- 48,
- 96,
- 192,
-};
-static int chg_time[] = {
- 192,
- 384,
- 768,
- 1536,
-};
-
-enum bpd_type {
- BPD_TYPE_BAT_NONE,
- BPD_TYPE_BAT_ID,
- BPD_TYPE_BAT_THM,
- BPD_TYPE_BAT_THM_BAT_ID,
- BPD_TYPE_DEFAULT,
-};
-
-static const char * const bpd_label[] = {
- [BPD_TYPE_BAT_NONE] = "bpd_none",
- [BPD_TYPE_BAT_ID] = "bpd_id",
- [BPD_TYPE_BAT_THM] = "bpd_thm",
- [BPD_TYPE_BAT_THM_BAT_ID] = "bpd_thm_id",
-};
-
-static inline int get_bpd(const char *name)
-{
- int i = 0;
- for (i = 0; i < ARRAY_SIZE(bpd_label); i++) {
- if (strcmp(bpd_label[i], name) == 0)
- return i;
- }
- return -EINVAL;
-}
-
-#define REVISION1_REG 0x0
-#define DIG_MINOR 0
-#define DIG_MAJOR 1
-#define ANA_MINOR 2
-#define ANA_MAJOR 3
-#define CHGR_CFG1 0xFB
-#define RECHG_THRESHOLD_SRC_BIT BIT(1)
-#define TERM_I_SRC_BIT BIT(2)
-#define TERM_SRC_FG BIT(2)
-#define CHG_INHIB_CFG_REG 0xF7
-#define CHG_INHIBIT_50MV_VAL 0x00
-#define CHG_INHIBIT_100MV_VAL 0x01
-#define CHG_INHIBIT_200MV_VAL 0x02
-#define CHG_INHIBIT_300MV_VAL 0x03
-#define CHG_INHIBIT_MASK 0x03
-#define USE_REGISTER_FOR_CURRENT BIT(2)
-#define CHGR_CFG2 0xFC
-#define CHG_EN_SRC_BIT BIT(7)
-#define CHG_EN_POLARITY_BIT BIT(6)
-#define P2F_CHG_TRAN BIT(5)
-#define CHG_BAT_OV_ECC BIT(4)
-#define I_TERM_BIT BIT(3)
-#define AUTO_RECHG_BIT BIT(2)
-#define CHARGER_INHIBIT_BIT BIT(0)
-#define USB51_COMMAND_POL BIT(2)
-#define USB51AC_CTRL BIT(1)
-#define TR_8OR32B 0xFE
-#define BUCK_8_16_FREQ_BIT BIT(0)
-#define BM_CFG 0xF3
-#define BATT_MISSING_ALGO_BIT BIT(2)
-#define BMD_PIN_SRC_MASK SMB_MASK(1, 0)
-#define PIN_SRC_SHIFT 0
-#define CHGR_CFG 0xFF
-#define RCHG_LVL_BIT BIT(0)
-#define VCHG_EN_BIT BIT(1)
-#define VCHG_INPUT_CURRENT_BIT BIT(3)
-#define CFG_AFVC 0xF6
-#define VFLOAT_COMP_ENABLE_MASK SMB_MASK(2, 0)
-#define TR_RID_REG 0xFA
-#define FG_INPUT_FET_DELAY_BIT BIT(3)
-#define TRIM_OPTIONS_7_0 0xF6
-#define INPUT_MISSING_POLLER_EN_BIT BIT(3)
-#define CHGR_CCMP_CFG 0xFA
-#define JEITA_TEMP_HARD_LIMIT_BIT BIT(5)
-#define HVDCP_ADAPTER_SEL_MASK SMB_MASK(5, 4)
-#define HVDCP_ADAPTER_SEL_9V_BIT BIT(4)
-#define HVDCP_AUTH_ALG_EN_BIT BIT(6)
-#define CMD_APSD 0x41
-#define APSD_RERUN_BIT BIT(0)
-#define OTG_CFG 0xF1
-#define HICCUP_ENABLED_BIT BIT(6)
-#define OTG_PIN_POLARITY_BIT BIT(4)
-#define OTG_PIN_ACTIVE_LOW BIT(4)
-#define OTG_EN_CTRL_MASK SMB_MASK(3, 2)
-#define OTG_PIN_CTRL_RID_DIS 0x04
-#define OTG_CMD_CTRL_RID_EN 0x08
-#define AICL_ADC_BIT BIT(6)
-static void batt_ov_wa_check(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- /* disable-'battery OV disables charging' feature */
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG2,
- CHG_BAT_OV_ECC, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc);
- return;
- }
-
- /*
- * if battery OV is set:
- * restart charging by disable/enable charging
- */
- rc = smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't read Battery RT status rc = %d\n", rc);
- return;
- }
-
- if (reg & BAT_OV_BIT) {
- rc = smbchg_charging_en(chip, false);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't disable charging: rc = %d\n", rc);
- return;
- }
-
- /* delay for charging-disable to take affect */
- msleep(200);
-
- rc = smbchg_charging_en(chip, true);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't enable charging: rc = %d\n", rc);
- return;
- }
- }
-}
-
-static int smbchg_hw_init(struct smbchg_chip *chip)
-{
- int rc, i;
- u8 reg, mask;
-
- rc = smbchg_read(chip, chip->revision,
- chip->misc_base + REVISION1_REG, 4);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read revision rc=%d\n",
- rc);
- return rc;
- }
- pr_smb(PR_STATUS, "Charger Revision DIG: %d.%d; ANA: %d.%d\n",
- chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
- chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]);
-
- /* Setup 9V HVDCP */
- if (!chip->hvdcp_not_supported) {
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
- if (rc < 0) {
- pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n",
- rc);
- return rc;
- }
- }
-
- if (chip->aicl_rerun_period_s > 0) {
- rc = smbchg_set_aicl_rerun_period_s(chip,
- chip->aicl_rerun_period_s);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set AICL rerun timer rc=%d\n",
- rc);
- return rc;
- }
- }
-
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + TR_RID_REG,
- FG_INPUT_FET_DELAY_BIT, FG_INPUT_FET_DELAY_BIT);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't disable fg input fet delay rc=%d\n",
- rc);
- return rc;
- }
-
- rc = smbchg_sec_masked_write(chip, chip->misc_base + TRIM_OPTIONS_7_0,
- INPUT_MISSING_POLLER_EN_BIT,
- INPUT_MISSING_POLLER_EN_BIT);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't enable input missing poller rc=%d\n",
- rc);
- return rc;
- }
-
- /*
- * Do not force using current from the register i.e. use auto
- * power source detect (APSD) mA ratings for the initial current values.
- *
- * If this is set, AICL will not rerun at 9V for HVDCPs
- */
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
- USE_REGISTER_FOR_CURRENT, 0);
-
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set input limit cmd rc=%d\n", rc);
- return rc;
- }
-
- /*
- * set chg en by cmd register, set chg en by writing bit 1,
- * enable auto pre to fast, enable auto recharge by default.
- * enable current termination and charge inhibition based on
- * the device tree configuration.
- */
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG2,
- CHG_EN_SRC_BIT | CHG_EN_POLARITY_BIT | P2F_CHG_TRAN
- | I_TERM_BIT | AUTO_RECHG_BIT | CHARGER_INHIBIT_BIT,
- CHG_EN_POLARITY_BIT
- | (chip->chg_inhibit_en ? CHARGER_INHIBIT_BIT : 0)
- | (chip->iterm_disabled ? I_TERM_BIT : 0));
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc);
- return rc;
- }
-
- /*
- * enable battery charging to make sure it hasn't been changed earlier
- * by the bootloader.
- */
- rc = smbchg_charging_en(chip, true);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't enable battery charging=%d\n", rc);
- return rc;
- }
-
- /*
- * Based on the configuration, use the analog sensors or the fuelgauge
- * adc for recharge threshold source.
- */
-
- if (chip->chg_inhibit_source_fg)
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG1,
- TERM_I_SRC_BIT | RECHG_THRESHOLD_SRC_BIT,
- TERM_SRC_FG | RECHG_THRESHOLD_SRC_BIT);
- else
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG1,
- TERM_I_SRC_BIT | RECHG_THRESHOLD_SRC_BIT, 0);
-
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc);
- return rc;
- }
-
- /*
- * control USB suspend via command bits and set correct 100/500mA
- * polarity on the usb current
- */
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- USB51_COMMAND_POL | USB51AC_CTRL, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set usb_chgpth cfg rc=%d\n", rc);
- return rc;
- }
-
- check_battery_type(chip);
-
- /* set the float voltage */
- if (chip->vfloat_mv != -EINVAL) {
- rc = smbchg_float_voltage_set(chip, chip->vfloat_mv);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set float voltage rc = %d\n", rc);
- return rc;
- }
- pr_smb(PR_STATUS, "set vfloat to %d\n", chip->vfloat_mv);
- }
-
- /* set the fast charge current compensation */
- if (chip->fastchg_current_comp != -EINVAL) {
- rc = smbchg_fastchg_current_comp_set(chip,
- chip->fastchg_current_comp);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set fastchg current comp rc = %d\n",
- rc);
- return rc;
- }
- pr_smb(PR_STATUS, "set fastchg current comp to %d\n",
- chip->fastchg_current_comp);
- }
-
- /* set the float voltage compensation */
- if (chip->float_voltage_comp != -EINVAL) {
- rc = smbchg_float_voltage_comp_set(chip,
- chip->float_voltage_comp);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n",
- rc);
- return rc;
- }
- pr_smb(PR_STATUS, "set float voltage comp to %d\n",
- chip->float_voltage_comp);
- }
-
- /* set iterm */
- if (chip->iterm_ma != -EINVAL) {
- if (chip->iterm_disabled) {
- dev_err(chip->dev, "Error: Both iterm_disabled and iterm_ma set\n");
- return -EINVAL;
- } else {
- smbchg_iterm_set(chip, chip->iterm_ma);
- }
- }
-
- /* set the safety time voltage */
- if (chip->safety_time != -EINVAL) {
- reg = (chip->safety_time > 0 ? 0 : SFT_TIMER_DISABLE_BIT) |
- (chip->prechg_safety_time > 0
- ? 0 : PRECHG_SFT_TIMER_DISABLE_BIT);
-
- for (i = 0; i < ARRAY_SIZE(chg_time); i++) {
- if (chip->safety_time <= chg_time[i]) {
- reg |= i << SAFETY_TIME_MINUTES_SHIFT;
- break;
- }
- }
- for (i = 0; i < ARRAY_SIZE(prechg_time); i++) {
- if (chip->prechg_safety_time <= prechg_time[i]) {
- reg |= i;
- break;
- }
- }
-
- rc = smbchg_sec_masked_write(chip,
- chip->chgr_base + SFT_CFG,
- SFT_EN_MASK | SFT_TO_MASK |
- (chip->prechg_safety_time > 0
- ? PRECHG_SFT_TO_MASK : 0), reg);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set safety timer rc = %d\n",
- rc);
- return rc;
- }
- chip->safety_timer_en = true;
- } else {
- rc = smbchg_read(chip, &reg, chip->chgr_base + SFT_CFG, 1);
- if (rc < 0)
- dev_err(chip->dev, "Unable to read SFT_CFG rc = %d\n",
- rc);
- else if (!(reg & SFT_EN_MASK))
- chip->safety_timer_en = true;
- }
-
- /* configure jeita temperature hard limit */
- if (chip->jeita_temp_hard_limit >= 0) {
- rc = smbchg_sec_masked_write(chip,
- chip->chgr_base + CHGR_CCMP_CFG,
- JEITA_TEMP_HARD_LIMIT_BIT,
- chip->jeita_temp_hard_limit
- ? 0 : JEITA_TEMP_HARD_LIMIT_BIT);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set jeita temp hard limit rc = %d\n",
- rc);
- return rc;
- }
- }
-
- /* make the buck switch faster to prevent some vbus oscillation */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + TR_8OR32B,
- BUCK_8_16_FREQ_BIT, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set buck frequency rc = %d\n", rc);
- return rc;
- }
-
- /* battery missing detection */
- mask = BATT_MISSING_ALGO_BIT;
- reg = chip->bmd_algo_disabled ? 0 : BATT_MISSING_ALGO_BIT;
- if (chip->bmd_pin_src < BPD_TYPE_DEFAULT) {
- mask |= BMD_PIN_SRC_MASK;
- reg |= chip->bmd_pin_src << PIN_SRC_SHIFT;
- }
- rc = smbchg_sec_masked_write(chip,
- chip->bat_if_base + BM_CFG, mask, reg);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set batt_missing config = %d\n",
- rc);
- return rc;
- }
-
- if (chip->vchg_adc_channel != -EINVAL) {
- /* configure and enable VCHG */
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG,
- VCHG_INPUT_CURRENT_BIT | VCHG_EN_BIT,
- VCHG_INPUT_CURRENT_BIT | VCHG_EN_BIT);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set recharge rc = %d\n",
- rc);
- return rc;
- }
- }
-
- smbchg_charging_status_change(chip);
-
- vote(chip->usb_suspend_votable, USER_EN_VOTER, !chip->chg_enabled, 0);
- vote(chip->dc_suspend_votable, USER_EN_VOTER, !chip->chg_enabled, 0);
- /* resume threshold */
- if (chip->resume_delta_mv != -EINVAL) {
-
- /*
- * Configure only if the recharge threshold source is not
- * fuel gauge ADC.
- */
- if (!chip->chg_inhibit_source_fg) {
- if (chip->resume_delta_mv < 100)
- reg = CHG_INHIBIT_50MV_VAL;
- else if (chip->resume_delta_mv < 200)
- reg = CHG_INHIBIT_100MV_VAL;
- else if (chip->resume_delta_mv < 300)
- reg = CHG_INHIBIT_200MV_VAL;
- else
- reg = CHG_INHIBIT_300MV_VAL;
-
- rc = smbchg_sec_masked_write(chip,
- chip->chgr_base + CHG_INHIB_CFG_REG,
- CHG_INHIBIT_MASK, reg);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set inhibit val rc = %d\n",
- rc);
- return rc;
- }
- }
-
- rc = smbchg_sec_masked_write(chip,
- chip->chgr_base + CHGR_CFG,
- RCHG_LVL_BIT,
- (chip->resume_delta_mv
- < chip->tables.rchg_thr_mv)
- ? 0 : RCHG_LVL_BIT);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set recharge rc = %d\n",
- rc);
- return rc;
- }
- }
-
- /* DC path current settings */
- if (chip->dc_psy_type != -EINVAL) {
- rc = vote(chip->dc_icl_votable, PSY_ICL_VOTER, true,
- chip->dc_target_current_ma);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't vote for initial DC ICL rc=%d\n", rc);
- return rc;
- }
- }
-
-
- /*
- * on some devices the battery is powered via external sources which
- * could raise its voltage above the float voltage. smbchargers go
- * in to reverse boost in such a situation and the workaround is to
- * disable float voltage compensation (note that the battery will appear
- * hot/cold when powered via external source).
- */
- if (chip->soft_vfloat_comp_disabled) {
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + CFG_AFVC,
- VFLOAT_COMP_ENABLE_MASK, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't disable soft vfloat rc = %d\n",
- rc);
- return rc;
- }
- }
-
- rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true,
- chip->cfg_fastchg_current_ma);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't vote fastchg ma rc = %d\n", rc);
- return rc;
- }
-
- rc = smbchg_read(chip, &chip->original_usbin_allowance,
- chip->usb_chgpth_base + USBIN_CHGR_CFG, 1);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't read usb allowance rc=%d\n", rc);
-
- if (chip->wipower_dyn_icl_avail) {
- rc = smbchg_wipower_ilim_config(chip,
- &(chip->wipower_default.entries[0]));
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set default wipower ilim = %d\n",
- rc);
- return rc;
- }
- }
- /* unsuspend dc path, it could be suspended by the bootloader */
- rc = smbchg_dc_suspend(chip, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't unsuspend dc path= %d\n", rc);
- return rc;
- }
-
- if (chip->force_aicl_rerun) {
- /* vote to enable hw aicl */
- rc = vote(chip->hw_aicl_rerun_enable_indirect_votable,
- DEFAULT_CONFIG_HW_AICL_VOTER, true, 0);
- if (rc < 0) {
- pr_err("Couldn't vote enable hw aicl rerun rc=%d\n",
- rc);
- return rc;
- }
- }
-
- if (chip->schg_version == QPNP_SCHG_LITE) {
- /* enable OTG hiccup mode */
- rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_CFG,
- HICCUP_ENABLED_BIT, HICCUP_ENABLED_BIT);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't set OTG OC config rc = %d\n",
- rc);
- }
-
- if (chip->otg_pinctrl) {
- /* configure OTG enable to pin control active low */
- rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_CFG,
- OTG_PIN_POLARITY_BIT | OTG_EN_CTRL_MASK,
- OTG_PIN_ACTIVE_LOW | OTG_PIN_CTRL_RID_DIS);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set OTG EN config rc = %d\n",
- rc);
- return rc;
- }
- }
-
- if (chip->wa_flags & SMBCHG_BATT_OV_WA)
- batt_ov_wa_check(chip);
-
- /* turn off AICL adc for improved accuracy */
- rc = smbchg_sec_masked_write(chip,
- chip->misc_base + MISC_TRIM_OPT_15_8, AICL_ADC_BIT, 0);
- if (rc)
- pr_err("Couldn't write to MISC_TRIM_OPTIONS_15_8 rc=%d\n",
- rc);
-
- return rc;
-}
-
-static struct of_device_id smbchg_match_table[] = {
- {
- .compatible = "qcom,qpnp-smbcharger",
- },
- { },
-};
-
-#define DC_MA_MIN 300
-#define DC_MA_MAX 2000
-#define OF_PROP_READ(chip, prop, dt_property, retval, optional) \
-do { \
- if (retval) \
- break; \
- if (optional) \
- prop = -EINVAL; \
- \
- retval = of_property_read_u32(chip->pdev->dev.of_node, \
- "qcom," dt_property , \
- &prop); \
- \
- if ((retval == -EINVAL) && optional) \
- retval = 0; \
- else if (retval) \
- dev_err(chip->dev, "Error reading " #dt_property \
- " property rc = %d\n", rc); \
-} while (0)
-
-#define ILIM_ENTRIES 3
-#define VOLTAGE_RANGE_ENTRIES 2
-#define RANGE_ENTRY (ILIM_ENTRIES + VOLTAGE_RANGE_ENTRIES)
-static int smb_parse_wipower_map_dt(struct smbchg_chip *chip,
- struct ilim_map *map, char *property)
-{
- struct device_node *node = chip->dev->of_node;
- int total_elements, size;
- struct property *prop;
- const __be32 *data;
- int num, i;
-
- prop = of_find_property(node, property, &size);
- if (!prop) {
- dev_err(chip->dev, "%s missing\n", property);
- return -EINVAL;
- }
-
- total_elements = size / sizeof(int);
- if (total_elements % RANGE_ENTRY) {
- dev_err(chip->dev, "%s table not in multiple of %d, total elements = %d\n",
- property, RANGE_ENTRY, total_elements);
- return -EINVAL;
- }
-
- data = prop->value;
- num = total_elements / RANGE_ENTRY;
- map->entries = devm_kzalloc(chip->dev,
- num * sizeof(struct ilim_entry), GFP_KERNEL);
- if (!map->entries) {
- dev_err(chip->dev, "kzalloc failed for default ilim\n");
- return -ENOMEM;
- }
- for (i = 0; i < num; i++) {
- map->entries[i].vmin_uv = be32_to_cpup(data++);
- map->entries[i].vmax_uv = be32_to_cpup(data++);
- map->entries[i].icl_pt_ma = be32_to_cpup(data++);
- map->entries[i].icl_lv_ma = be32_to_cpup(data++);
- map->entries[i].icl_hv_ma = be32_to_cpup(data++);
- }
- map->num = num;
- return 0;
-}
-
-static int smb_parse_wipower_dt(struct smbchg_chip *chip)
-{
- int rc = 0;
-
- chip->wipower_dyn_icl_avail = false;
-
- if (!chip->vadc_dev)
- goto err;
-
- rc = smb_parse_wipower_map_dt(chip, &chip->wipower_default,
- "qcom,wipower-default-ilim-map");
- if (rc) {
- dev_err(chip->dev, "failed to parse wipower-pt-ilim-map rc = %d\n",
- rc);
- goto err;
- }
-
- rc = smb_parse_wipower_map_dt(chip, &chip->wipower_pt,
- "qcom,wipower-pt-ilim-map");
- if (rc) {
- dev_err(chip->dev, "failed to parse wipower-pt-ilim-map rc = %d\n",
- rc);
- goto err;
- }
-
- rc = smb_parse_wipower_map_dt(chip, &chip->wipower_div2,
- "qcom,wipower-div2-ilim-map");
- if (rc) {
- dev_err(chip->dev, "failed to parse wipower-div2-ilim-map rc = %d\n",
- rc);
- goto err;
- }
- chip->wipower_dyn_icl_avail = true;
- return 0;
-err:
- chip->wipower_default.num = 0;
- chip->wipower_pt.num = 0;
- chip->wipower_default.num = 0;
- if (chip->wipower_default.entries)
- devm_kfree(chip->dev, chip->wipower_default.entries);
- if (chip->wipower_pt.entries)
- devm_kfree(chip->dev, chip->wipower_pt.entries);
- if (chip->wipower_div2.entries)
- devm_kfree(chip->dev, chip->wipower_div2.entries);
- chip->wipower_default.entries = NULL;
- chip->wipower_pt.entries = NULL;
- chip->wipower_div2.entries = NULL;
- chip->vadc_dev = NULL;
- return rc;
-}
-
-#define DEFAULT_VLED_MAX_UV 3500000
-#define DEFAULT_FCC_MA 2000
-static int smb_parse_dt(struct smbchg_chip *chip)
-{
- int rc = 0, ocp_thresh = -EINVAL;
- struct device_node *node = chip->dev->of_node;
- const char *dc_psy_type, *bpd;
-
- if (!node) {
- dev_err(chip->dev, "device tree info. missing\n");
- return -EINVAL;
- }
-
- /* read optional u32 properties */
- OF_PROP_READ(chip, ocp_thresh,
- "ibat-ocp-threshold-ua", rc, 1);
- if (ocp_thresh >= 0)
- smbchg_ibat_ocp_threshold_ua = ocp_thresh;
- OF_PROP_READ(chip, chip->iterm_ma, "iterm-ma", rc, 1);
- OF_PROP_READ(chip, chip->cfg_fastchg_current_ma,
- "fastchg-current-ma", rc, 1);
- if (chip->cfg_fastchg_current_ma == -EINVAL)
- chip->cfg_fastchg_current_ma = DEFAULT_FCC_MA;
- OF_PROP_READ(chip, chip->vfloat_mv, "float-voltage-mv", rc, 1);
- OF_PROP_READ(chip, chip->safety_time, "charging-timeout-mins", rc, 1);
- OF_PROP_READ(chip, chip->vled_max_uv, "vled-max-uv", rc, 1);
- if (chip->vled_max_uv < 0)
- chip->vled_max_uv = DEFAULT_VLED_MAX_UV;
- OF_PROP_READ(chip, chip->rpara_uohm, "rparasitic-uohm", rc, 1);
- if (chip->rpara_uohm < 0)
- chip->rpara_uohm = 0;
- OF_PROP_READ(chip, chip->prechg_safety_time, "precharging-timeout-mins",
- rc, 1);
- OF_PROP_READ(chip, chip->fastchg_current_comp, "fastchg-current-comp",
- rc, 1);
- OF_PROP_READ(chip, chip->float_voltage_comp, "float-voltage-comp",
- rc, 1);
- if (chip->safety_time != -EINVAL &&
- (chip->safety_time > chg_time[ARRAY_SIZE(chg_time) - 1])) {
- dev_err(chip->dev, "Bad charging-timeout-mins %d\n",
- chip->safety_time);
- return -EINVAL;
- }
- if (chip->prechg_safety_time != -EINVAL &&
- (chip->prechg_safety_time >
- prechg_time[ARRAY_SIZE(prechg_time) - 1])) {
- dev_err(chip->dev, "Bad precharging-timeout-mins %d\n",
- chip->prechg_safety_time);
- return -EINVAL;
- }
- OF_PROP_READ(chip, chip->resume_delta_mv, "resume-delta-mv", rc, 1);
- OF_PROP_READ(chip, chip->parallel.min_current_thr_ma,
- "parallel-usb-min-current-ma", rc, 1);
- OF_PROP_READ(chip, chip->parallel.min_9v_current_thr_ma,
- "parallel-usb-9v-min-current-ma", rc, 1);
- OF_PROP_READ(chip, chip->parallel.allowed_lowering_ma,
- "parallel-allowed-lowering-ma", rc, 1);
- if (chip->parallel.min_current_thr_ma != -EINVAL
- && chip->parallel.min_9v_current_thr_ma != -EINVAL)
- chip->parallel.avail = true;
- /*
- * use the dt values if they exist, otherwise do not touch the params
- */
- of_property_read_u32(node, "qcom,parallel-main-chg-fcc-percent",
- &smbchg_main_chg_fcc_percent);
- of_property_read_u32(node, "qcom,parallel-main-chg-icl-percent",
- &smbchg_main_chg_icl_percent);
- pr_smb(PR_STATUS, "parallel usb thr: %d, 9v thr: %d\n",
- chip->parallel.min_current_thr_ma,
- chip->parallel.min_9v_current_thr_ma);
- OF_PROP_READ(chip, chip->jeita_temp_hard_limit,
- "jeita-temp-hard-limit", rc, 1);
- OF_PROP_READ(chip, chip->aicl_rerun_period_s,
- "aicl-rerun-period-s", rc, 1);
- OF_PROP_READ(chip, chip->vchg_adc_channel,
- "vchg-adc-channel-id", rc, 1);
-
- /* read boolean configuration properties */
- chip->use_vfloat_adjustments = of_property_read_bool(node,
- "qcom,autoadjust-vfloat");
- chip->bmd_algo_disabled = of_property_read_bool(node,
- "qcom,bmd-algo-disabled");
- chip->iterm_disabled = of_property_read_bool(node,
- "qcom,iterm-disabled");
- chip->soft_vfloat_comp_disabled = of_property_read_bool(node,
- "qcom,soft-vfloat-comp-disabled");
- chip->chg_enabled = !(of_property_read_bool(node,
- "qcom,charging-disabled"));
- chip->charge_unknown_battery = of_property_read_bool(node,
- "qcom,charge-unknown-battery");
- chip->chg_inhibit_en = of_property_read_bool(node,
- "qcom,chg-inhibit-en");
- chip->chg_inhibit_source_fg = of_property_read_bool(node,
- "qcom,chg-inhibit-fg");
- chip->low_volt_dcin = of_property_read_bool(node,
- "qcom,low-volt-dcin");
- chip->force_aicl_rerun = of_property_read_bool(node,
- "qcom,force-aicl-rerun");
- chip->skip_usb_suspend_for_fake_battery = of_property_read_bool(node,
- "qcom,skip-usb-suspend-for-fake-battery");
-
- /* parse the battery missing detection pin source */
- rc = of_property_read_string(chip->pdev->dev.of_node,
- "qcom,bmd-pin-src", &bpd);
- if (rc) {
- /* Select BAT_THM as default BPD scheme */
- chip->bmd_pin_src = BPD_TYPE_DEFAULT;
- rc = 0;
- } else {
- chip->bmd_pin_src = get_bpd(bpd);
- if (chip->bmd_pin_src < 0) {
- dev_err(chip->dev,
- "failed to determine bpd schema %d\n", rc);
- return rc;
- }
- }
-
- /* parse the dc power supply configuration */
- rc = of_property_read_string(node, "qcom,dc-psy-type", &dc_psy_type);
- if (rc) {
- chip->dc_psy_type = -EINVAL;
- rc = 0;
- } else {
- if (strcmp(dc_psy_type, "Mains") == 0)
- chip->dc_psy_type = POWER_SUPPLY_TYPE_MAINS;
- else if (strcmp(dc_psy_type, "Wireless") == 0)
- chip->dc_psy_type = POWER_SUPPLY_TYPE_WIRELESS;
- else if (strcmp(dc_psy_type, "Wipower") == 0)
- chip->dc_psy_type = POWER_SUPPLY_TYPE_WIPOWER;
- }
- if (chip->dc_psy_type != -EINVAL) {
- OF_PROP_READ(chip, chip->dc_target_current_ma,
- "dc-psy-ma", rc, 0);
- if (rc)
- return rc;
- if (chip->dc_target_current_ma < DC_MA_MIN
- || chip->dc_target_current_ma > DC_MA_MAX) {
- dev_err(chip->dev, "Bad dc mA %d\n",
- chip->dc_target_current_ma);
- return -EINVAL;
- }
- }
-
- if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER)
- smb_parse_wipower_dt(chip);
-
- /* read the bms power supply name */
- rc = of_property_read_string(node, "qcom,bms-psy-name",
- &chip->bms_psy_name);
- if (rc)
- chip->bms_psy_name = NULL;
-
- /* read the battery power supply name */
- rc = of_property_read_string(node, "qcom,battery-psy-name",
- &chip->battery_psy_name);
- if (rc)
- chip->battery_psy_name = "battery";
-
- /* Get the charger led support property */
- chip->cfg_chg_led_sw_ctrl =
- of_property_read_bool(node, "qcom,chg-led-sw-controls");
- chip->cfg_chg_led_support =
- of_property_read_bool(node, "qcom,chg-led-support");
-
- if (of_find_property(node, "qcom,thermal-mitigation",
- &chip->thermal_levels)) {
- chip->thermal_mitigation = devm_kzalloc(chip->dev,
- chip->thermal_levels,
- GFP_KERNEL);
-
- if (chip->thermal_mitigation == NULL) {
- dev_err(chip->dev, "thermal mitigation kzalloc() failed.\n");
- return -ENOMEM;
- }
-
- chip->thermal_levels /= sizeof(int);
- rc = of_property_read_u32_array(node,
- "qcom,thermal-mitigation",
- chip->thermal_mitigation, chip->thermal_levels);
- if (rc) {
- dev_err(chip->dev,
- "Couldn't read threm limits rc = %d\n", rc);
- return rc;
- }
- }
-
- chip->skip_usb_notification
- = of_property_read_bool(node,
- "qcom,skip-usb-notification");
-
- chip->otg_pinctrl = of_property_read_bool(node, "qcom,otg-pinctrl");
-
- return 0;
-}
-
-#define SUBTYPE_REG 0x5
-#define SMBCHG_CHGR_SUBTYPE 0x1
-#define SMBCHG_OTG_SUBTYPE 0x8
-#define SMBCHG_BAT_IF_SUBTYPE 0x3
-#define SMBCHG_USB_CHGPTH_SUBTYPE 0x4
-#define SMBCHG_DC_CHGPTH_SUBTYPE 0x5
-#define SMBCHG_MISC_SUBTYPE 0x7
-#define SMBCHG_LITE_CHGR_SUBTYPE 0x51
-#define SMBCHG_LITE_OTG_SUBTYPE 0x58
-#define SMBCHG_LITE_BAT_IF_SUBTYPE 0x53
-#define SMBCHG_LITE_USB_CHGPTH_SUBTYPE 0x54
-#define SMBCHG_LITE_DC_CHGPTH_SUBTYPE 0x55
-#define SMBCHG_LITE_MISC_SUBTYPE 0x57
-static int smbchg_request_irq(struct smbchg_chip *chip,
- struct device_node *child,
- int irq_num, char *irq_name,
- irqreturn_t (irq_handler)(int irq, void *_chip),
- int flags)
-{
- int rc;
-
- irq_num = of_irq_get_byname(child, irq_name);
- if (irq_num < 0) {
- dev_err(chip->dev, "Unable to get %s irqn", irq_name);
- rc = -ENXIO;
- }
- rc = devm_request_threaded_irq(chip->dev,
- irq_num, NULL, irq_handler, flags, irq_name,
- chip);
- if (rc < 0) {
- dev_err(chip->dev, "Unable to request %s irq: %dn",
- irq_name, rc);
- rc = -ENXIO;
- }
- return 0;
-}
-
-static int smbchg_request_irqs(struct smbchg_chip *chip)
-{
- int rc = 0;
- unsigned int base;
- struct device_node *child;
- u8 subtype;
- unsigned long flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
- | IRQF_ONESHOT;
-
- if (of_get_available_child_count(chip->pdev->dev.of_node) == 0) {
- pr_err("no child nodes\n");
- return -ENXIO;
- }
-
- for_each_available_child_of_node(chip->pdev->dev.of_node, child) {
- rc = of_property_read_u32(child, "reg", &base);
- if (rc < 0) {
- rc = 0;
- continue;
- }
-
- rc = smbchg_read(chip, &subtype, base + SUBTYPE_REG, 1);
- if (rc) {
- dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n",
- rc);
- return rc;
- }
-
- switch (subtype) {
- case SMBCHG_CHGR_SUBTYPE:
- case SMBCHG_LITE_CHGR_SUBTYPE:
- rc = smbchg_request_irq(chip, child,
- chip->chg_error_irq, "chg-error",
- chg_error_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child, chip->taper_irq,
- "chg-taper-thr", taper_handler,
- (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
- if (rc < 0)
- return rc;
- disable_irq_nosync(chip->taper_irq);
- rc = smbchg_request_irq(chip, child, chip->chg_term_irq,
- "chg-tcc-thr", chg_term_handler,
- (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child, chip->recharge_irq,
- "chg-rechg-thr", recharge_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child, chip->fastchg_irq,
- "chg-p2f-thr", fastchg_handler, flags);
- if (rc < 0)
- return rc;
- enable_irq_wake(chip->chg_term_irq);
- enable_irq_wake(chip->chg_error_irq);
- enable_irq_wake(chip->fastchg_irq);
- break;
- case SMBCHG_BAT_IF_SUBTYPE:
- case SMBCHG_LITE_BAT_IF_SUBTYPE:
- rc = smbchg_request_irq(chip, child, chip->batt_hot_irq,
- "batt-hot", batt_hot_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->batt_warm_irq,
- "batt-warm", batt_warm_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->batt_cool_irq,
- "batt-cool", batt_cool_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->batt_cold_irq,
- "batt-cold", batt_cold_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->batt_missing_irq,
- "batt-missing", batt_pres_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->vbat_low_irq,
- "batt-low", vbat_low_handler, flags);
- if (rc < 0)
- return rc;
-
- enable_irq_wake(chip->batt_hot_irq);
- enable_irq_wake(chip->batt_warm_irq);
- enable_irq_wake(chip->batt_cool_irq);
- enable_irq_wake(chip->batt_cold_irq);
- enable_irq_wake(chip->batt_missing_irq);
- enable_irq_wake(chip->vbat_low_irq);
- break;
- case SMBCHG_USB_CHGPTH_SUBTYPE:
- case SMBCHG_LITE_USB_CHGPTH_SUBTYPE:
- rc = smbchg_request_irq(chip, child,
- chip->usbin_uv_irq,
- "usbin-uv", usbin_uv_handler,
- flags | IRQF_EARLY_RESUME);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->usbin_ov_irq,
- "usbin-ov", usbin_ov_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->src_detect_irq,
- "usbin-src-det",
- src_detect_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->aicl_done_irq,
- "aicl-done",
- aicl_done_handler,
- (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
- if (rc < 0)
- return rc;
-
- if (chip->schg_version != QPNP_SCHG_LITE) {
- rc = smbchg_request_irq(chip, child,
- chip->otg_fail_irq, "otg-fail",
- otg_fail_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->otg_oc_irq, "otg-oc",
- otg_oc_handler,
- (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->usbid_change_irq, "usbid-change",
- usbid_change_handler,
- (IRQF_TRIGGER_FALLING | IRQF_ONESHOT));
- if (rc < 0)
- return rc;
- enable_irq_wake(chip->otg_oc_irq);
- enable_irq_wake(chip->usbid_change_irq);
- enable_irq_wake(chip->otg_fail_irq);
- }
- enable_irq_wake(chip->usbin_uv_irq);
- enable_irq_wake(chip->usbin_ov_irq);
- enable_irq_wake(chip->src_detect_irq);
- if (chip->parallel.avail && chip->usb_present) {
- rc = enable_irq_wake(chip->aicl_done_irq);
- chip->enable_aicl_wake = true;
- }
- break;
- case SMBCHG_DC_CHGPTH_SUBTYPE:
- case SMBCHG_LITE_DC_CHGPTH_SUBTYPE:
- rc = smbchg_request_irq(chip, child, chip->dcin_uv_irq,
- "dcin-uv", dcin_uv_handler, flags);
- if (rc < 0)
- return rc;
- enable_irq_wake(chip->dcin_uv_irq);
- break;
- case SMBCHG_MISC_SUBTYPE:
- case SMBCHG_LITE_MISC_SUBTYPE:
- rc = smbchg_request_irq(chip, child, chip->power_ok_irq,
- "power-ok", power_ok_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child, chip->chg_hot_irq,
- "temp-shutdown", chg_hot_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child, chip->wdog_timeout_irq,
- "wdog-timeout",
- wdog_timeout_handler, flags);
- if (rc < 0)
- return rc;
- enable_irq_wake(chip->chg_hot_irq);
- enable_irq_wake(chip->wdog_timeout_irq);
- break;
- case SMBCHG_OTG_SUBTYPE:
- break;
- case SMBCHG_LITE_OTG_SUBTYPE:
- rc = smbchg_request_irq(chip, child,
- chip->usbid_change_irq, "usbid-change",
- usbid_change_handler,
- (IRQF_TRIGGER_FALLING | IRQF_ONESHOT));
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->otg_oc_irq, "otg-oc",
- otg_oc_handler,
- (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->otg_fail_irq, "otg-fail",
- otg_fail_handler, flags);
- if (rc < 0)
- return rc;
- enable_irq_wake(chip->usbid_change_irq);
- enable_irq_wake(chip->otg_oc_irq);
- enable_irq_wake(chip->otg_fail_irq);
- break;
- }
- }
-
- return rc;
-}
-
-#define REQUIRE_BASE(chip, base, rc) \
-do { \
- if (!rc && !chip->base) { \
- dev_err(chip->dev, "Missing " #base "\n"); \
- rc = -EINVAL; \
- } \
-} while (0)
-
-static int smbchg_parse_peripherals(struct smbchg_chip *chip)
-{
- int rc = 0;
- unsigned int base;
- struct device_node *child;
- u8 subtype;
-
- if (of_get_available_child_count(chip->pdev->dev.of_node) == 0) {
- pr_err("no child nodes\n");
- return -ENXIO;
- }
-
- for_each_available_child_of_node(chip->pdev->dev.of_node, child) {
- rc = of_property_read_u32(child, "reg", &base);
- if (rc < 0) {
- rc = 0;
- continue;
- }
-
- rc = smbchg_read(chip, &subtype, base + SUBTYPE_REG, 1);
- if (rc) {
- dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n",
- rc);
- return rc;
- }
-
- switch (subtype) {
- case SMBCHG_CHGR_SUBTYPE:
- case SMBCHG_LITE_CHGR_SUBTYPE:
- chip->chgr_base = base;
- break;
- case SMBCHG_BAT_IF_SUBTYPE:
- case SMBCHG_LITE_BAT_IF_SUBTYPE:
- chip->bat_if_base = base;
- break;
- case SMBCHG_USB_CHGPTH_SUBTYPE:
- case SMBCHG_LITE_USB_CHGPTH_SUBTYPE:
- chip->usb_chgpth_base = base;
- break;
- case SMBCHG_DC_CHGPTH_SUBTYPE:
- case SMBCHG_LITE_DC_CHGPTH_SUBTYPE:
- chip->dc_chgpth_base = base;
- break;
- case SMBCHG_MISC_SUBTYPE:
- case SMBCHG_LITE_MISC_SUBTYPE:
- chip->misc_base = base;
- break;
- case SMBCHG_OTG_SUBTYPE:
- case SMBCHG_LITE_OTG_SUBTYPE:
- chip->otg_base = base;
- break;
- }
- }
-
- REQUIRE_BASE(chip, chgr_base, rc);
- REQUIRE_BASE(chip, bat_if_base, rc);
- REQUIRE_BASE(chip, usb_chgpth_base, rc);
- REQUIRE_BASE(chip, dc_chgpth_base, rc);
- REQUIRE_BASE(chip, misc_base, rc);
-
- return rc;
-}
-
-static inline void dump_reg(struct smbchg_chip *chip, u16 addr,
- const char *name)
-{
- u8 reg;
-
- smbchg_read(chip, &reg, addr, 1);
- pr_smb(PR_DUMP, "%s - %04X = %02X\n", name, addr, reg);
-}
-
-/* dumps useful registers for debug */
-static void dump_regs(struct smbchg_chip *chip)
-{
- u16 addr;
-
- /* charger peripheral */
- for (addr = 0xB; addr <= 0x10; addr++)
- dump_reg(chip, chip->chgr_base + addr, "CHGR Status");
- for (addr = 0xF0; addr <= 0xFF; addr++)
- dump_reg(chip, chip->chgr_base + addr, "CHGR Config");
- /* battery interface peripheral */
- dump_reg(chip, chip->bat_if_base + RT_STS, "BAT_IF Status");
- dump_reg(chip, chip->bat_if_base + CMD_CHG_REG, "BAT_IF Command");
- for (addr = 0xF0; addr <= 0xFB; addr++)
- dump_reg(chip, chip->bat_if_base + addr, "BAT_IF Config");
- /* usb charge path peripheral */
- for (addr = 0x7; addr <= 0x10; addr++)
- dump_reg(chip, chip->usb_chgpth_base + addr, "USB Status");
- dump_reg(chip, chip->usb_chgpth_base + CMD_IL, "USB Command");
- for (addr = 0xF0; addr <= 0xF5; addr++)
- dump_reg(chip, chip->usb_chgpth_base + addr, "USB Config");
- /* dc charge path peripheral */
- dump_reg(chip, chip->dc_chgpth_base + RT_STS, "DC Status");
- for (addr = 0xF0; addr <= 0xF6; addr++)
- dump_reg(chip, chip->dc_chgpth_base + addr, "DC Config");
- /* misc peripheral */
- dump_reg(chip, chip->misc_base + IDEV_STS, "MISC Status");
- dump_reg(chip, chip->misc_base + RT_STS, "MISC Status");
- for (addr = 0xF0; addr <= 0xF3; addr++)
- dump_reg(chip, chip->misc_base + addr, "MISC CFG");
-}
-
-static int create_debugfs_entries(struct smbchg_chip *chip)
-{
- struct dentry *ent;
-
- chip->debug_root = debugfs_create_dir("qpnp-smbcharger", NULL);
- if (!chip->debug_root) {
- dev_err(chip->dev, "Couldn't create debug dir\n");
- return -EINVAL;
- }
-
- ent = debugfs_create_file("force_dcin_icl_check",
- S_IFREG | S_IWUSR | S_IRUGO,
- chip->debug_root, chip,
- &force_dcin_icl_ops);
- if (!ent) {
- dev_err(chip->dev,
- "Couldn't create force dcin icl check file\n");
- return -EINVAL;
- }
- return 0;
-}
-
-static int smbchg_check_chg_version(struct smbchg_chip *chip)
-{
- struct pmic_revid_data *pmic_rev_id;
- struct device_node *revid_dev_node;
- int rc;
-
- revid_dev_node = of_parse_phandle(chip->pdev->dev.of_node,
- "qcom,pmic-revid", 0);
- if (!revid_dev_node) {
- pr_err("Missing qcom,pmic-revid property - driver failed\n");
- return -EINVAL;
- }
-
- pmic_rev_id = get_revid_data(revid_dev_node);
- if (IS_ERR(pmic_rev_id)) {
- rc = PTR_ERR(revid_dev_node);
- if (rc != -EPROBE_DEFER)
- pr_err("Unable to get pmic_revid rc=%d\n", rc);
- return rc;
- }
-
- switch (pmic_rev_id->pmic_subtype) {
- case PMI8994:
- chip->wa_flags |= SMBCHG_AICL_DEGLITCH_WA
- | SMBCHG_BATT_OV_WA
- | SMBCHG_CC_ESR_WA
- | SMBCHG_RESTART_WA;
- use_pmi8994_tables(chip);
- chip->schg_version = QPNP_SCHG;
- break;
- case PMI8950:
- case PMI8937:
- chip->wa_flags |= SMBCHG_BATT_OV_WA;
- if (pmic_rev_id->rev4 < 2) /* PMI8950 1.0 */ {
- chip->wa_flags |= SMBCHG_AICL_DEGLITCH_WA;
- } else { /* rev > PMI8950 v1.0 */
- chip->wa_flags |= SMBCHG_HVDCP_9V_EN_WA
- | SMBCHG_USB100_WA;
- }
- use_pmi8994_tables(chip);
- chip->tables.aicl_rerun_period_table =
- aicl_rerun_period_schg_lite;
- chip->tables.aicl_rerun_period_len =
- ARRAY_SIZE(aicl_rerun_period_schg_lite);
-
- chip->schg_version = QPNP_SCHG_LITE;
- if (pmic_rev_id->pmic_subtype == PMI8937)
- chip->hvdcp_not_supported = true;
- break;
- case PMI8996:
- chip->wa_flags |= SMBCHG_CC_ESR_WA
- | SMBCHG_FLASH_ICL_DISABLE_WA
- | SMBCHG_RESTART_WA
- | SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA;
- use_pmi8996_tables(chip);
- chip->schg_version = QPNP_SCHG;
- break;
- default:
- pr_err("PMIC subtype %d not supported, WA flags not set\n",
- pmic_rev_id->pmic_subtype);
- }
-
- pr_smb(PR_STATUS, "pmic=%s, wa_flags=0x%x, hvdcp_supported=%s\n",
- pmic_rev_id->pmic_name, chip->wa_flags,
- chip->hvdcp_not_supported ? "false" : "true");
-
- return 0;
-}
-
-static void rerun_hvdcp_det_if_necessary(struct smbchg_chip *chip)
-{
- enum power_supply_type usb_supply_type;
- char *usb_type_name;
- int rc;
-
- if (!(chip->wa_flags & SMBCHG_RESTART_WA))
- return;
-
- read_usb_type(chip, &usb_type_name, &usb_supply_type);
- if (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP
- && !is_hvdcp_present(chip)) {
- pr_smb(PR_STATUS, "DCP found rerunning APSD\n");
- rc = vote(chip->usb_icl_votable,
- CHG_SUSPEND_WORKAROUND_ICL_VOTER, true, 300);
- if (rc < 0)
- pr_err("Couldn't vote for 300mA for suspend wa, going ahead rc=%d\n",
- rc);
-
- pr_smb(PR_STATUS, "Faking Removal\n");
- fake_insertion_removal(chip, false);
- msleep(500);
- pr_smb(PR_STATUS, "Faking Insertion\n");
- fake_insertion_removal(chip, true);
-
- read_usb_type(chip, &usb_type_name, &usb_supply_type);
- if (usb_supply_type != POWER_SUPPLY_TYPE_USB_DCP) {
- msleep(500);
- pr_smb(PR_STATUS, "Fake Removal again as type!=DCP\n");
- fake_insertion_removal(chip, false);
- msleep(500);
- pr_smb(PR_STATUS, "Fake Insert again as type!=DCP\n");
- fake_insertion_removal(chip, true);
- }
-
- rc = vote(chip->usb_icl_votable,
- CHG_SUSPEND_WORKAROUND_ICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't vote for 0 for suspend wa, going ahead rc=%d\n",
- rc);
- }
-}
-
-static int smbchg_probe(struct platform_device *pdev)
-{
- int rc;
- struct smbchg_chip *chip;
- struct power_supply *typec_psy = NULL;
- struct qpnp_vadc_chip *vadc_dev, *vchg_vadc_dev;
- const char *typec_psy_name;
- struct power_supply_config usb_psy_cfg = {};
- struct power_supply_config batt_psy_cfg = {};
- struct power_supply_config dc_psy_cfg = {};
-
- if (of_property_read_bool(pdev->dev.of_node, "qcom,external-typec")) {
- /* read the type power supply name */
- rc = of_property_read_string(pdev->dev.of_node,
- "qcom,typec-psy-name", &typec_psy_name);
- if (rc) {
- pr_err("failed to get prop typec-psy-name rc=%d\n",
- rc);
- return rc;
- }
-
- typec_psy = power_supply_get_by_name(typec_psy_name);
- if (!typec_psy) {
- pr_smb(PR_STATUS,
- "Type-C supply not found, deferring probe\n");
- return -EPROBE_DEFER;
- }
- }
-
- vadc_dev = NULL;
- if (of_find_property(pdev->dev.of_node, "qcom,dcin-vadc", NULL)) {
- vadc_dev = qpnp_get_vadc(&pdev->dev, "dcin");
- if (IS_ERR(vadc_dev)) {
- rc = PTR_ERR(vadc_dev);
- if (rc != -EPROBE_DEFER)
- dev_err(&pdev->dev,
- "Couldn't get vadc rc=%d\n",
- rc);
- return rc;
- }
- }
-
- vchg_vadc_dev = NULL;
- if (of_find_property(pdev->dev.of_node, "qcom,vchg_sns-vadc", NULL)) {
- vchg_vadc_dev = qpnp_get_vadc(&pdev->dev, "vchg_sns");
- if (IS_ERR(vchg_vadc_dev)) {
- rc = PTR_ERR(vchg_vadc_dev);
- if (rc != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Couldn't get vadc 'vchg' rc=%d\n",
- rc);
- return rc;
- }
- }
-
-
- chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
-
- chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!chip->regmap) {
- dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
- return -EINVAL;
- }
-
- chip->fcc_votable = create_votable("BATT_FCC",
- VOTE_MIN,
- set_fastchg_current_vote_cb, chip);
- if (IS_ERR(chip->fcc_votable)) {
- rc = PTR_ERR(chip->fcc_votable);
- goto votables_cleanup;
- }
-
- chip->usb_icl_votable = create_votable("USB_ICL",
- VOTE_MIN,
- set_usb_current_limit_vote_cb, chip);
- if (IS_ERR(chip->usb_icl_votable)) {
- rc = PTR_ERR(chip->usb_icl_votable);
- goto votables_cleanup;
- }
-
- chip->dc_icl_votable = create_votable("DCIN_ICL",
- VOTE_MIN,
- set_dc_current_limit_vote_cb, chip);
- if (IS_ERR(chip->dc_icl_votable)) {
- rc = PTR_ERR(chip->dc_icl_votable);
- goto votables_cleanup;
- }
-
- chip->usb_suspend_votable = create_votable("USB_SUSPEND",
- VOTE_SET_ANY,
- usb_suspend_vote_cb, chip);
- if (IS_ERR(chip->usb_suspend_votable)) {
- rc = PTR_ERR(chip->usb_suspend_votable);
- goto votables_cleanup;
- }
-
- chip->dc_suspend_votable = create_votable("DC_SUSPEND",
- VOTE_SET_ANY,
- dc_suspend_vote_cb, chip);
- if (IS_ERR(chip->dc_suspend_votable)) {
- rc = PTR_ERR(chip->dc_suspend_votable);
- goto votables_cleanup;
- }
-
- chip->battchg_suspend_votable = create_votable("BATTCHG_SUSPEND",
- VOTE_SET_ANY,
- charging_suspend_vote_cb, chip);
- if (IS_ERR(chip->battchg_suspend_votable)) {
- rc = PTR_ERR(chip->battchg_suspend_votable);
- goto votables_cleanup;
- }
-
- chip->hw_aicl_rerun_disable_votable = create_votable("HWAICL_DISABLE",
- VOTE_SET_ANY,
- smbchg_hw_aicl_rerun_disable_cb, chip);
- if (IS_ERR(chip->hw_aicl_rerun_disable_votable)) {
- rc = PTR_ERR(chip->hw_aicl_rerun_disable_votable);
- goto votables_cleanup;
- }
-
- chip->hw_aicl_rerun_enable_indirect_votable = create_votable(
- "HWAICL_ENABLE_INDIRECT",
- VOTE_SET_ANY,
- smbchg_hw_aicl_rerun_enable_indirect_cb, chip);
- if (IS_ERR(chip->hw_aicl_rerun_enable_indirect_votable)) {
- rc = PTR_ERR(chip->hw_aicl_rerun_enable_indirect_votable);
- goto votables_cleanup;
- }
-
- chip->aicl_deglitch_short_votable = create_votable(
- "HWAICL_SHORT_DEGLITCH",
- VOTE_SET_ANY,
- smbchg_aicl_deglitch_config_cb, chip);
- if (IS_ERR(chip->aicl_deglitch_short_votable)) {
- rc = PTR_ERR(chip->aicl_deglitch_short_votable);
- goto votables_cleanup;
- }
-
- INIT_WORK(&chip->usb_set_online_work, smbchg_usb_update_online_work);
- INIT_DELAYED_WORK(&chip->parallel_en_work,
- smbchg_parallel_usb_en_work);
- INIT_DELAYED_WORK(&chip->vfloat_adjust_work, smbchg_vfloat_adjust_work);
- INIT_DELAYED_WORK(&chip->hvdcp_det_work, smbchg_hvdcp_det_work);
- init_completion(&chip->src_det_lowered);
- init_completion(&chip->src_det_raised);
- init_completion(&chip->usbin_uv_lowered);
- init_completion(&chip->usbin_uv_raised);
- chip->vadc_dev = vadc_dev;
- chip->vchg_vadc_dev = vchg_vadc_dev;
- chip->pdev = pdev;
- chip->dev = &pdev->dev;
-
- chip->typec_psy = typec_psy;
- chip->fake_battery_soc = -EINVAL;
- chip->usb_online = -EINVAL;
- dev_set_drvdata(&pdev->dev, chip);
-
- spin_lock_init(&chip->sec_access_lock);
- mutex_init(&chip->therm_lvl_lock);
- mutex_init(&chip->usb_set_online_lock);
- mutex_init(&chip->parallel.lock);
- mutex_init(&chip->taper_irq_lock);
- mutex_init(&chip->pm_lock);
- mutex_init(&chip->wipower_config);
- mutex_init(&chip->usb_status_lock);
- device_init_wakeup(chip->dev, true);
-
- rc = smbchg_parse_peripherals(chip);
- if (rc) {
- dev_err(chip->dev, "Error parsing DT peripherals: %d\n", rc);
- goto votables_cleanup;
- }
-
- rc = smbchg_check_chg_version(chip);
- if (rc) {
- pr_err("Unable to check schg version rc=%d\n", rc);
- goto votables_cleanup;
- }
-
- rc = smb_parse_dt(chip);
- if (rc < 0) {
- dev_err(&pdev->dev, "Unable to parse DT nodes: %d\n", rc);
- goto votables_cleanup;
- }
-
- rc = smbchg_regulator_init(chip);
- if (rc) {
- dev_err(&pdev->dev,
- "Couldn't initialize regulator rc=%d\n", rc);
- goto votables_cleanup;
- }
-
- chip->extcon = devm_extcon_dev_allocate(chip->dev, smbchg_extcon_cable);
- if (IS_ERR(chip->extcon)) {
- dev_err(chip->dev, "failed to allocate extcon device\n");
- rc = PTR_ERR(chip->extcon);
- goto votables_cleanup;
- }
-
- rc = devm_extcon_dev_register(chip->dev, chip->extcon);
- if (rc) {
- dev_err(chip->dev, "failed to register extcon device\n");
- goto votables_cleanup;
- }
-
- chip->usb_psy_d.name = "usb";
- chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB;
- chip->usb_psy_d.get_property = smbchg_usb_get_property;
- chip->usb_psy_d.set_property = smbchg_usb_set_property;
- chip->usb_psy_d.properties = smbchg_usb_properties;
- chip->usb_psy_d.num_properties = ARRAY_SIZE(smbchg_usb_properties);
- chip->usb_psy_d.property_is_writeable = smbchg_usb_is_writeable;
-
- usb_psy_cfg.drv_data = chip;
- usb_psy_cfg.supplied_to = smbchg_usb_supplicants;
- usb_psy_cfg.num_supplicants = ARRAY_SIZE(smbchg_usb_supplicants);
-
- chip->usb_psy = devm_power_supply_register(chip->dev,
- &chip->usb_psy_d, &usb_psy_cfg);
- if (IS_ERR(chip->usb_psy)) {
- dev_err(&pdev->dev, "Unable to register usb_psy rc = %ld\n",
- PTR_ERR(chip->usb_psy));
- rc = PTR_ERR(chip->usb_psy);
- goto votables_cleanup;
- }
-
- if (of_find_property(chip->dev->of_node, "dpdm-supply", NULL)) {
- chip->dpdm_reg = devm_regulator_get(chip->dev, "dpdm");
- if (IS_ERR(chip->dpdm_reg)) {
- rc = PTR_ERR(chip->dpdm_reg);
- goto votables_cleanup;
- }
- }
-
- rc = smbchg_hw_init(chip);
- if (rc < 0) {
- dev_err(&pdev->dev,
- "Unable to intialize hardware rc = %d\n", rc);
- goto out;
- }
-
- rc = determine_initial_status(chip);
- if (rc < 0) {
- dev_err(&pdev->dev,
- "Unable to determine init status rc = %d\n", rc);
- goto out;
- }
-
- chip->previous_soc = -EINVAL;
- chip->batt_psy_d.name = chip->battery_psy_name;
- chip->batt_psy_d.type = POWER_SUPPLY_TYPE_BATTERY;
- chip->batt_psy_d.get_property = smbchg_battery_get_property;
- chip->batt_psy_d.set_property = smbchg_battery_set_property;
- chip->batt_psy_d.properties = smbchg_battery_properties;
- chip->batt_psy_d.num_properties = ARRAY_SIZE(smbchg_battery_properties);
- chip->batt_psy_d.external_power_changed = smbchg_external_power_changed;
- chip->batt_psy_d.property_is_writeable = smbchg_battery_is_writeable;
-
- batt_psy_cfg.drv_data = chip;
- batt_psy_cfg.num_supplicants = 0;
- chip->batt_psy = devm_power_supply_register(chip->dev,
- &chip->batt_psy_d,
- &batt_psy_cfg);
- if (IS_ERR(chip->batt_psy)) {
- dev_err(&pdev->dev,
- "Unable to register batt_psy rc = %ld\n",
- PTR_ERR(chip->batt_psy));
- goto out;
- }
-
- if (chip->dc_psy_type != -EINVAL) {
- chip->dc_psy_d.name = "dc";
- chip->dc_psy_d.type = chip->dc_psy_type;
- chip->dc_psy_d.get_property = smbchg_dc_get_property;
- chip->dc_psy_d.set_property = smbchg_dc_set_property;
- chip->dc_psy_d.property_is_writeable = smbchg_dc_is_writeable;
- chip->dc_psy_d.properties = smbchg_dc_properties;
- chip->dc_psy_d.num_properties
- = ARRAY_SIZE(smbchg_dc_properties);
-
- dc_psy_cfg.drv_data = chip;
- dc_psy_cfg.num_supplicants
- = ARRAY_SIZE(smbchg_dc_supplicants);
- dc_psy_cfg.supplied_to = smbchg_dc_supplicants;
-
- chip->dc_psy = devm_power_supply_register(chip->dev,
- &chip->dc_psy_d,
- &dc_psy_cfg);
- if (IS_ERR(chip->dc_psy)) {
- dev_err(&pdev->dev,
- "Unable to register dc_psy rc = %ld\n",
- PTR_ERR(chip->dc_psy));
- goto out;
- }
- }
-
- if (chip->cfg_chg_led_support &&
- chip->schg_version == QPNP_SCHG_LITE) {
- rc = smbchg_register_chg_led(chip);
- if (rc) {
- dev_err(chip->dev,
- "Unable to register charger led: %d\n",
- rc);
- goto out;
- }
-
- rc = smbchg_chg_led_controls(chip);
- if (rc) {
- dev_err(chip->dev,
- "Failed to set charger led controld bit: %d\n",
- rc);
- goto unregister_led_class;
- }
- }
-
- rc = smbchg_request_irqs(chip);
- if (rc < 0) {
- dev_err(&pdev->dev, "Unable to request irqs rc = %d\n", rc);
- goto unregister_led_class;
- }
-
- rerun_hvdcp_det_if_necessary(chip);
-
- dump_regs(chip);
- create_debugfs_entries(chip);
- dev_info(chip->dev,
- "SMBCHG successfully probe Charger version=%s Revision DIG:%d.%d ANA:%d.%d batt=%d dc=%d usb=%d\n",
- version_str[chip->schg_version],
- chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
- chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR],
- get_prop_batt_present(chip),
- chip->dc_present, chip->usb_present);
- return 0;
-
-unregister_led_class:
- if (chip->cfg_chg_led_support && chip->schg_version == QPNP_SCHG_LITE)
- led_classdev_unregister(&chip->led_cdev);
-out:
- handle_usb_removal(chip);
-votables_cleanup:
- if (chip->aicl_deglitch_short_votable)
- destroy_votable(chip->aicl_deglitch_short_votable);
- if (chip->hw_aicl_rerun_enable_indirect_votable)
- destroy_votable(chip->hw_aicl_rerun_enable_indirect_votable);
- if (chip->hw_aicl_rerun_disable_votable)
- destroy_votable(chip->hw_aicl_rerun_disable_votable);
- if (chip->battchg_suspend_votable)
- destroy_votable(chip->battchg_suspend_votable);
- if (chip->dc_suspend_votable)
- destroy_votable(chip->dc_suspend_votable);
- if (chip->usb_suspend_votable)
- destroy_votable(chip->usb_suspend_votable);
- if (chip->dc_icl_votable)
- destroy_votable(chip->dc_icl_votable);
- if (chip->usb_icl_votable)
- destroy_votable(chip->usb_icl_votable);
- if (chip->fcc_votable)
- destroy_votable(chip->fcc_votable);
- return rc;
-}
-
-static int smbchg_remove(struct platform_device *pdev)
-{
- struct smbchg_chip *chip = dev_get_drvdata(&pdev->dev);
-
- debugfs_remove_recursive(chip->debug_root);
-
- destroy_votable(chip->aicl_deglitch_short_votable);
- destroy_votable(chip->hw_aicl_rerun_enable_indirect_votable);
- destroy_votable(chip->hw_aicl_rerun_disable_votable);
- destroy_votable(chip->battchg_suspend_votable);
- destroy_votable(chip->dc_suspend_votable);
- destroy_votable(chip->usb_suspend_votable);
- destroy_votable(chip->dc_icl_votable);
- destroy_votable(chip->usb_icl_votable);
- destroy_votable(chip->fcc_votable);
-
- return 0;
-}
-
-static void smbchg_shutdown(struct platform_device *pdev)
-{
- struct smbchg_chip *chip = dev_get_drvdata(&pdev->dev);
- int rc;
-
- if (!(chip->wa_flags & SMBCHG_RESTART_WA))
- return;
-
- if (!is_hvdcp_present(chip))
- return;
-
- pr_smb(PR_MISC, "Disable Parallel\n");
- mutex_lock(&chip->parallel.lock);
- smbchg_parallel_en = 0;
- smbchg_parallel_usb_disable(chip);
- mutex_unlock(&chip->parallel.lock);
-
- pr_smb(PR_MISC, "Disable all interrupts\n");
- disable_irq(chip->aicl_done_irq);
- disable_irq(chip->batt_cold_irq);
- disable_irq(chip->batt_cool_irq);
- disable_irq(chip->batt_hot_irq);
- disable_irq(chip->batt_missing_irq);
- disable_irq(chip->batt_warm_irq);
- disable_irq(chip->chg_error_irq);
- disable_irq(chip->chg_hot_irq);
- disable_irq(chip->chg_term_irq);
- disable_irq(chip->dcin_uv_irq);
- disable_irq(chip->fastchg_irq);
- disable_irq(chip->otg_fail_irq);
- disable_irq(chip->otg_oc_irq);
- disable_irq(chip->power_ok_irq);
- disable_irq(chip->recharge_irq);
- disable_irq(chip->src_detect_irq);
- disable_irq(chip->taper_irq);
- disable_irq(chip->usbid_change_irq);
- disable_irq(chip->usbin_ov_irq);
- disable_irq(chip->usbin_uv_irq);
- disable_irq(chip->vbat_low_irq);
- disable_irq(chip->wdog_timeout_irq);
-
- /* remove all votes for short deglitch */
- vote(chip->aicl_deglitch_short_votable,
- VARB_WORKAROUND_SHORT_DEGLITCH_VOTER, false, 0);
- vote(chip->aicl_deglitch_short_votable,
- HVDCP_SHORT_DEGLITCH_VOTER, false, 0);
-
- /* vote to ensure AICL rerun is enabled */
- rc = vote(chip->hw_aicl_rerun_enable_indirect_votable,
- SHUTDOWN_WORKAROUND_VOTER, true, 0);
- if (rc < 0)
- pr_err("Couldn't vote to enable indirect AICL rerun\n");
- rc = vote(chip->hw_aicl_rerun_disable_votable,
- WEAK_CHARGER_HW_AICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't vote to enable AICL rerun\n");
-
- /* switch to 5V HVDCP */
- pr_smb(PR_MISC, "Switch to 5V HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
- if (rc < 0) {
- pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc);
- return;
- }
-
- pr_smb(PR_MISC, "Wait 500mS to lower to 5V\n");
- /* wait for HVDCP to lower to 5V */
- msleep(500);
- /*
- * Check if the same hvdcp session is in progress. src_det should be
- * high and that we are still in 5V hvdcp
- */
- if (!is_src_detect_high(chip)) {
- pr_smb(PR_MISC, "src det low after 500mS sleep\n");
- return;
- }
-
- /* disable HVDCP */
- pr_smb(PR_MISC, "Disable HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, 0);
- if (rc < 0)
- pr_err("Couldn't disable HVDCP rc=%d\n", rc);
-
- chip->hvdcp_3_det_ignore_uv = true;
- /* fake a removal */
- pr_smb(PR_MISC, "Faking Removal\n");
- rc = fake_insertion_removal(chip, false);
- if (rc < 0)
- pr_err("Couldn't fake removal HVDCP Removed rc=%d\n", rc);
-
- /* fake an insertion */
- pr_smb(PR_MISC, "Faking Insertion\n");
- rc = fake_insertion_removal(chip, true);
- if (rc < 0)
- pr_err("Couldn't fake insertion rc=%d\n", rc);
-
- pr_smb(PR_MISC, "Wait 1S to settle\n");
- msleep(1000);
- chip->hvdcp_3_det_ignore_uv = false;
-
- pr_smb(PR_STATUS, "wrote power off configurations\n");
-}
-
-static const struct dev_pm_ops smbchg_pm_ops = {
-};
-
-MODULE_DEVICE_TABLE(spmi, smbchg_id);
-
-static struct platform_driver smbchg_driver = {
- .driver = {
- .name = "qpnp-smbcharger",
- .owner = THIS_MODULE,
- .of_match_table = smbchg_match_table,
- .pm = &smbchg_pm_ops,
- },
- .probe = smbchg_probe,
- .remove = smbchg_remove,
- .shutdown = smbchg_shutdown,
-};
-
-static int __init smbchg_init(void)
-{
- return platform_driver_register(&smbchg_driver);
-}
-
-static void __exit smbchg_exit(void)
-{
- return platform_driver_unregister(&smbchg_driver);
-}
-
-module_init(smbchg_init);
-module_exit(smbchg_exit);
-
-MODULE_DESCRIPTION("QPNP SMB Charger");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:qpnp-smbcharger");
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index f9784630c327..a8427c5e3107 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -381,13 +381,13 @@ static int smblib_set_opt_freq_buck(struct smb_charger *chg, int fsw_khz)
if (chg->mode == PARALLEL_MASTER && chg->pl.psy) {
pval.intval = fsw_khz;
- rc = power_supply_set_property(chg->pl.psy,
+ /*
+ * Some parallel charging implementations may not have
+ * PROP_BUCK_FREQ property - they could be running
+ * with a fixed frequency
+ */
+ power_supply_set_property(chg->pl.psy,
POWER_SUPPLY_PROP_BUCK_FREQ, &pval);
- if (rc < 0) {
- dev_err(chg->dev,
- "Could not set parallel buck_freq rc=%d\n", rc);
- return rc;
- }
}
return rc;
@@ -474,6 +474,36 @@ int smblib_set_dc_suspend(struct smb_charger *chg, bool suspend)
return rc;
}
+static int smblib_set_adapter_allowance(struct smb_charger *chg,
+ u8 allowed_voltage)
+{
+ int rc = 0;
+
+ switch (allowed_voltage) {
+ case USBIN_ADAPTER_ALLOW_12V:
+ case USBIN_ADAPTER_ALLOW_5V_OR_12V:
+ case USBIN_ADAPTER_ALLOW_9V_TO_12V:
+ case USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V:
+ case USBIN_ADAPTER_ALLOW_5V_TO_12V:
+ /* PM660 only support max. 9V */
+ if (chg->smb_version == PM660_SUBTYPE) {
+ smblib_dbg(chg, PR_MISC, "voltage not supported=%d\n",
+ allowed_voltage);
+ allowed_voltage = USBIN_ADAPTER_ALLOW_5V_TO_9V;
+ }
+ break;
+ }
+
+ rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, allowed_voltage);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't write 0x%02x to USBIN_ADAPTER_ALLOW_CFG rc=%d\n",
+ allowed_voltage, rc);
+ return rc;
+ }
+
+ return rc;
+}
+
#define MICRO_5V 5000000
#define MICRO_9V 9000000
#define MICRO_12V 12000000
@@ -504,10 +534,10 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg,
return -EINVAL;
}
- rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, allowed_voltage);
+ rc = smblib_set_adapter_allowance(chg, allowed_voltage);
if (rc < 0) {
- smblib_err(chg, "Couldn't write 0x%02x to USBIN_ADAPTER_ALLOW_CFG rc=%d\n",
- allowed_voltage, rc);
+ smblib_err(chg, "Couldn't configure adapter allowance rc=%d\n",
+ rc);
return rc;
}
@@ -650,8 +680,8 @@ static void smblib_uusb_removal(struct smb_charger *chg)
}
/* reconfigure allowed voltage for HVDCP */
- rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG,
- USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
+ rc = smblib_set_adapter_allowance(chg,
+ USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
if (rc < 0)
smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n",
rc);
@@ -3405,8 +3435,8 @@ static void typec_source_removal(struct smb_charger *chg)
}
/* reconfigure allowed voltage for HVDCP */
- rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG,
- USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
+ rc = smblib_set_adapter_allowance(chg,
+ USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
if (rc < 0)
smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n",
rc);
diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c
index 37dc15494761..1c7c1e78699f 100644
--- a/drivers/power/supply/qcom/smb138x-charger.c
+++ b/drivers/power/supply/qcom/smb138x-charger.c
@@ -44,6 +44,8 @@
#define SMB2CHG_DC_TM_SREFGEN (DCIN_BASE + 0xE2)
#define STACKED_DIODE_EN_BIT BIT(2)
+#define TDIE_AVG_COUNT 10
+
enum {
OOB_COMP_WA_BIT = BIT(0),
};
@@ -118,6 +120,27 @@ irqreturn_t smb138x_handle_slave_chg_state_change(int irq, void *data)
return IRQ_HANDLED;
}
+static int smb138x_get_prop_charger_temp(struct smb138x *chip,
+ union power_supply_propval *val)
+{
+ union power_supply_propval pval;
+ int rc = 0, avg = 0, i;
+ struct smb_charger *chg = &chip->chg;
+
+ for (i = 0; i < TDIE_AVG_COUNT; i++) {
+ pval.intval = 0;
+ rc = smblib_get_prop_charger_temp(chg, &pval);
+ if (rc < 0) {
+ pr_err("Couldnt read chg temp at %dth iteration rc = %d\n",
+ i + 1, rc);
+ return rc;
+ }
+ avg += pval.intval;
+ }
+ val->intval = avg / TDIE_AVG_COUNT;
+ return rc;
+}
+
static int smb138x_parse_dt(struct smb138x *chip)
{
struct smb_charger *chg = &chip->chg;
@@ -343,7 +366,7 @@ static int smb138x_batt_get_prop(struct power_supply *psy,
rc = smblib_get_prop_batt_capacity(chg, val);
break;
case POWER_SUPPLY_PROP_CHARGER_TEMP:
- rc = smblib_get_prop_charger_temp(chg, val);
+ rc = smb138x_get_prop_charger_temp(chip, val);
break;
case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX:
rc = smblib_get_prop_charger_temp_max(chg, val);
@@ -547,7 +570,7 @@ static int smb138x_parallel_get_prop(struct power_supply *psy,
rc = smblib_get_prop_slave_current_now(chg, val);
break;
case POWER_SUPPLY_PROP_CHARGER_TEMP:
- rc = smblib_get_prop_charger_temp(chg, val);
+ rc = smb138x_get_prop_charger_temp(chip, val);
break;
case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX:
rc = smblib_get_prop_charger_temp_max(chg, val);
@@ -621,10 +644,6 @@ static int smb138x_parallel_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
rc = smblib_set_charge_param(chg, &chg->param.fcc, val->intval);
break;
- case POWER_SUPPLY_PROP_BUCK_FREQ:
- rc = smblib_set_charge_param(chg, &chg->param.freq_buck,
- val->intval);
- break;
case POWER_SUPPLY_PROP_SET_SHIP_MODE:
/* Not in ship mode as long as the device is active */
if (!val->intval)
@@ -632,7 +651,7 @@ static int smb138x_parallel_set_prop(struct power_supply *psy,
rc = smblib_set_prop_ship_mode(chg, val);
break;
default:
- pr_err("parallel power supply set prop %d not supported\n",
+ pr_debug("parallel power supply set prop %d not supported\n",
prop);
return -EINVAL;
}
@@ -911,6 +930,13 @@ static int smb138x_init_hw(struct smb138x *chip)
chg->dcp_icl_ua = chip->dt.usb_icl_ua;
+ /* configure to a fixed 700khz freq to avoid tdie errors */
+ rc = smblib_set_charge_param(chg, &chg->param.freq_buck, 700);
+ if (rc < 0) {
+ pr_err("Couldn't configure 700Khz switch freq rc=%d\n", rc);
+ return rc;
+ }
+
/* configure charge enable for software control; active high */
rc = smblib_masked_write(chg, CHGR_CFG2_REG,
CHG_EN_POLARITY_BIT | CHG_EN_SRC_BIT, 0);
diff --git a/drivers/power/tps65217_charger.c b/drivers/power/tps65217_charger.c
index d9f56730c735..040a40b4b173 100644
--- a/drivers/power/tps65217_charger.c
+++ b/drivers/power/tps65217_charger.c
@@ -205,6 +205,7 @@ static int tps65217_charger_probe(struct platform_device *pdev)
if (!charger)
return -ENOMEM;
+ platform_set_drvdata(pdev, charger);
charger->tps = tps;
charger->dev = &pdev->dev;
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index d24ca5f281b4..ec84ff8ad1b4 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -321,6 +321,8 @@ int pwmchip_remove(struct pwm_chip *chip)
unsigned int i;
int ret = 0;
+ pwmchip_sysfs_unexport_children(chip);
+
mutex_lock(&pwm_lock);
for (i = 0; i < chip->npwm; i++) {
@@ -889,7 +891,7 @@ EXPORT_SYMBOL_GPL(devm_pwm_put);
*/
bool pwm_can_sleep(struct pwm_device *pwm)
{
- return pwm->chip->can_sleep;
+ return true;
}
EXPORT_SYMBOL_GPL(pwm_can_sleep);
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index 9c90886f4123..375008e2be20 100644
--- a/drivers/pwm/sysfs.c
+++ b/drivers/pwm/sysfs.c
@@ -350,6 +350,26 @@ void pwmchip_sysfs_unexport(struct pwm_chip *chip)
}
}
+void pwmchip_sysfs_unexport_children(struct pwm_chip *chip)
+{
+ struct device *parent;
+ unsigned int i;
+
+ parent = class_find_device(&pwm_class, NULL, chip,
+ pwmchip_sysfs_match);
+ if (!parent)
+ return;
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+
+ if (test_bit(PWMF_EXPORTED, &pwm->flags))
+ pwm_unexport_child(parent, pwm);
+ }
+
+ put_device(parent);
+}
+
static int __init pwm_sysfs_init(void)
{
return class_register(&pwm_class);
diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c
index 6fa0c7d13290..4bda998afdef 100644
--- a/drivers/regulator/qcom_smd-regulator.c
+++ b/drivers/regulator/qcom_smd-regulator.c
@@ -166,29 +166,30 @@ static const struct regulator_desc pm8x41_hfsmps = {
static const struct regulator_desc pm8841_ftsmps = {
.linear_ranges = (struct regulator_linear_range[]) {
REGULATOR_LINEAR_RANGE(350000, 0, 184, 5000),
- REGULATOR_LINEAR_RANGE(700000, 185, 339, 10000),
+ REGULATOR_LINEAR_RANGE(1280000, 185, 261, 10000),
},
.n_linear_ranges = 2,
- .n_voltages = 340,
+ .n_voltages = 262,
.ops = &rpm_smps_ldo_ops,
};
static const struct regulator_desc pm8941_boost = {
.linear_ranges = (struct regulator_linear_range[]) {
- REGULATOR_LINEAR_RANGE(4000000, 0, 15, 100000),
+ REGULATOR_LINEAR_RANGE(4000000, 0, 30, 50000),
},
.n_linear_ranges = 1,
- .n_voltages = 16,
+ .n_voltages = 31,
.ops = &rpm_smps_ldo_ops,
};
static const struct regulator_desc pm8941_pldo = {
.linear_ranges = (struct regulator_linear_range[]) {
- REGULATOR_LINEAR_RANGE( 750000, 0, 30, 25000),
- REGULATOR_LINEAR_RANGE(1500000, 31, 99, 50000),
+ REGULATOR_LINEAR_RANGE( 750000, 0, 63, 12500),
+ REGULATOR_LINEAR_RANGE(1550000, 64, 126, 25000),
+ REGULATOR_LINEAR_RANGE(3100000, 127, 163, 50000),
},
- .n_linear_ranges = 2,
- .n_voltages = 100,
+ .n_linear_ranges = 3,
+ .n_voltages = 164,
.ops = &rpm_smps_ldo_ops,
};
diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c
index 88a5dc88badc..fee6457e3111 100644
--- a/drivers/regulator/qcom_spmi-regulator.c
+++ b/drivers/regulator/qcom_spmi-regulator.c
@@ -1050,6 +1050,8 @@ static struct regulator_ops spmi_vs_ops = {
.set_pull_down = spmi_regulator_common_set_pull_down,
.set_soft_start = spmi_regulator_common_set_soft_start,
.set_over_current_protection = spmi_regulator_vs_ocp,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
};
static struct regulator_ops spmi_boost_ops = {
@@ -1440,6 +1442,7 @@ static const struct spmi_regulator_data pm8941_regulators[] = {
{ "s1", 0x1400, "vdd_s1", },
{ "s2", 0x1700, "vdd_s2", },
{ "s3", 0x1a00, "vdd_s3", },
+ { "s4", 0xa000, },
{ "l1", 0x4000, "vdd_l1_l3", },
{ "l2", 0x4100, "vdd_l2_lvs_1_2_3", },
{ "l3", 0x4200, "vdd_l1_l3", },
@@ -1467,8 +1470,8 @@ static const struct spmi_regulator_data pm8941_regulators[] = {
{ "lvs1", 0x8000, "vdd_l2_lvs_1_2_3", },
{ "lvs2", 0x8100, "vdd_l2_lvs_1_2_3", },
{ "lvs3", 0x8200, "vdd_l2_lvs_1_2_3", },
- { "mvs1", 0x8300, "vin_5vs", },
- { "mvs2", 0x8400, "vin_5vs", },
+ { "5vs1", 0x8300, "vin_5vs", "ocp-5vs1", },
+ { "5vs2", 0x8400, "vin_5vs", "ocp-5vs2", },
{ }
};
diff --git a/drivers/regulator/qpnp-oledb-regulator.c b/drivers/regulator/qpnp-oledb-regulator.c
index 8d017fb55a3f..c012f373e80e 100644
--- a/drivers/regulator/qpnp-oledb-regulator.c
+++ b/drivers/regulator/qpnp-oledb-regulator.c
@@ -17,6 +17,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/spmi.h>
@@ -24,6 +25,8 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/qpnp-labibb-regulator.h>
+#include <linux/qpnp/qpnp-pbs.h>
#define QPNP_OLEDB_REGULATOR_DRIVER_NAME "qcom,qpnp-oledb-regulator"
#define OLEDB_VOUT_STEP_MV 100
@@ -91,6 +94,12 @@
#define OLEDB_ENABLE_NLIMIT_BIT_SHIFT 7
#define OLEDB_NLIMIT_PGM_MASK GENMASK(1, 0)
+#define OLEDB_SPARE_CTL 0xE9
+#define OLEDB_FORCE_PD_CTL_SPARE_BIT BIT(7)
+
+#define OLEDB_PD_PBS_TRIGGER_BIT BIT(0)
+
+#define OLEDB_SEC_UNLOCK_CODE 0xA5
#define OLEDB_PSM_HYS_CTRL_MIN 13
#define OLEDB_PSM_HYS_CTRL_MAX 26
@@ -150,6 +159,9 @@ struct qpnp_oledb {
struct qpnp_oledb_psm_ctl psm_ctl;
struct qpnp_oledb_pfm_ctl pfm_ctl;
struct qpnp_oledb_fast_precharge_ctl fast_prechg_ctl;
+ struct notifier_block oledb_nb;
+ struct mutex bus_lock;
+ struct device_node *pbs_dev_node;
u32 base;
u8 mod_enable;
@@ -168,6 +180,7 @@ struct qpnp_oledb {
bool ext_pin_control;
bool dynamic_ext_pinctl_config;
bool pbs_control;
+ bool force_pd_control;
};
static const u16 oledb_warmup_dly_ns[] = {6700, 13300, 26700, 53400};
@@ -184,11 +197,13 @@ static int qpnp_oledb_read(struct qpnp_oledb *oledb, u32 address,
int rc = 0;
struct platform_device *pdev = oledb->pdev;
+ mutex_lock(&oledb->bus_lock);
rc = regmap_bulk_read(oledb->regmap, address, val, count);
if (rc)
pr_err("Failed to read address=0x%02x sid=0x%02x rc=%d\n",
address, to_spmi_device(pdev->dev.parent)->usid, rc);
+ mutex_unlock(&oledb->bus_lock);
return rc;
}
@@ -197,6 +212,7 @@ static int qpnp_oledb_masked_write(struct qpnp_oledb *oledb,
{
int rc;
+ mutex_lock(&oledb->bus_lock);
rc = regmap_update_bits(oledb->regmap, address, mask, val);
if (rc < 0)
pr_err("Failed to write address 0x%04X, rc = %d\n",
@@ -205,6 +221,31 @@ static int qpnp_oledb_masked_write(struct qpnp_oledb *oledb,
pr_debug("Wrote 0x%02X to addr 0x%04X\n",
val, address);
+ mutex_unlock(&oledb->bus_lock);
+ return rc;
+}
+
+#define OLEDB_SEC_ACCESS 0xD0
+static int qpnp_oledb_sec_masked_write(struct qpnp_oledb *oledb, u16 address,
+ u8 mask, u8 val)
+{
+ int rc = 0;
+ u8 sec_val = OLEDB_SEC_UNLOCK_CODE;
+ u16 sec_reg_addr = (address & 0xFF00) | OLEDB_SEC_ACCESS;
+
+ mutex_lock(&oledb->bus_lock);
+ rc = regmap_write(oledb->regmap, sec_reg_addr, sec_val);
+ if (rc < 0) {
+ pr_err("register %x failed rc = %d\n", sec_reg_addr, rc);
+ goto error;
+ }
+
+ rc = regmap_update_bits(oledb->regmap, address, mask, val);
+ if (rc < 0)
+ pr_err("spmi write failed: addr=%03X, rc=%d\n", address, rc);
+
+error:
+ mutex_unlock(&oledb->bus_lock);
return rc;
}
@@ -214,6 +255,7 @@ static int qpnp_oledb_write(struct qpnp_oledb *oledb, u16 address, u8 *val,
int rc = 0;
struct platform_device *pdev = oledb->pdev;
+ mutex_lock(&oledb->bus_lock);
rc = regmap_bulk_write(oledb->regmap, address, val, count);
if (rc)
pr_err("Failed to write address=0x%02x sid=0x%02x rc=%d\n",
@@ -222,7 +264,8 @@ static int qpnp_oledb_write(struct qpnp_oledb *oledb, u16 address, u8 *val,
pr_debug("Wrote 0x%02X to addr 0x%04X\n",
*val, address);
- return 0;
+ mutex_unlock(&oledb->bus_lock);
+ return rc;
}
static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev)
@@ -285,6 +328,8 @@ static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev)
static int qpnp_oledb_regulator_disable(struct regulator_dev *rdev)
{
int rc = 0;
+ u8 trigger_bitmap = OLEDB_PD_PBS_TRIGGER_BIT;
+ u8 val;
struct qpnp_oledb *oledb = rdev_get_drvdata(rdev);
@@ -314,6 +359,27 @@ static int qpnp_oledb_regulator_disable(struct regulator_dev *rdev)
pr_debug("Register-control mode, module disabled\n");
}
+ if (oledb->force_pd_control) {
+ rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_SPARE_CTL,
+ &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to read OLEDB_SPARE_CTL rc=%d\n", rc);
+ return rc;
+ }
+
+ if (val & OLEDB_FORCE_PD_CTL_SPARE_BIT) {
+ rc = qpnp_pbs_trigger_event(oledb->pbs_dev_node,
+ trigger_bitmap);
+ if (rc < 0) {
+ pr_err("Failed to trigger the PBS sequence\n");
+ return rc;
+ }
+ pr_debug("PBS event triggered\n");
+ } else {
+ pr_debug("OLEDB_SPARE_CTL register bit not set\n");
+ }
+ }
+
oledb->mod_enable = false;
return rc;
@@ -1034,6 +1100,18 @@ static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb)
oledb->pbs_control =
of_property_read_bool(of_node, "qcom,pbs-control");
+ oledb->force_pd_control =
+ of_property_read_bool(of_node, "qcom,force-pd-control");
+
+ if (oledb->force_pd_control) {
+ oledb->pbs_dev_node = of_parse_phandle(of_node,
+ "qcom,pbs-client", 0);
+ if (!oledb->pbs_dev_node) {
+ pr_err("Missing qcom,pbs-client property\n");
+ return -EINVAL;
+ }
+ }
+
oledb->current_voltage = -EINVAL;
rc = of_property_read_u32(of_node, "qcom,oledb-init-voltage-mv",
&oledb->current_voltage);
@@ -1116,6 +1194,52 @@ static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb)
return rc;
}
+static int qpnp_oledb_force_pulldown_config(struct qpnp_oledb *oledb)
+{
+ int rc = 0;
+ u8 val;
+
+ rc = qpnp_oledb_sec_masked_write(oledb, oledb->base +
+ OLEDB_SPARE_CTL, OLEDB_FORCE_PD_CTL_SPARE_BIT, 0);
+ if (rc < 0) {
+ pr_err("Failed to write SPARE_CTL rc=%d\n", rc);
+ return rc;
+ }
+
+ val = 1;
+ rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_PD_CTL,
+ &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to write PD_CTL rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_oledb_masked_write(oledb, oledb->base +
+ OLEDB_SWIRE_CONTROL, OLEDB_EN_SWIRE_PD_UPD_BIT, 0);
+ if (rc < 0)
+ pr_err("Failed to write SWIRE_CTL for pbs mode rc=%d\n",
+ rc);
+
+ return rc;
+}
+
+static int qpnp_labibb_notifier_cb(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ int rc = 0;
+ struct qpnp_oledb *oledb = container_of(nb, struct qpnp_oledb,
+ oledb_nb);
+
+ if (action == LAB_VREG_OK) {
+ /* Disable SWIRE pull down control and enable via spmi mode */
+ rc = qpnp_oledb_force_pulldown_config(oledb);
+ if (rc < 0)
+ return NOTIFY_STOP;
+ }
+
+ return NOTIFY_OK;
+}
+
static int qpnp_oledb_regulator_probe(struct platform_device *pdev)
{
int rc = 0;
@@ -1143,6 +1267,7 @@ static int qpnp_oledb_regulator_probe(struct platform_device *pdev)
return rc;
}
+ mutex_init(&(oledb->bus_lock));
oledb->base = val;
rc = qpnp_oledb_parse_dt(oledb);
if (rc < 0) {
@@ -1156,18 +1281,47 @@ static int qpnp_oledb_regulator_probe(struct platform_device *pdev)
return rc;
}
+ if (oledb->force_pd_control) {
+ oledb->oledb_nb.notifier_call = qpnp_labibb_notifier_cb;
+ rc = qpnp_labibb_notifier_register(&oledb->oledb_nb);
+ if (rc < 0) {
+ pr_err("Failed to register qpnp_labibb_notifier_cb\n");
+ return rc;
+ }
+ }
+
rc = qpnp_oledb_register_regulator(oledb);
- if (!rc)
- pr_info("OLEDB registered successfully, ext_pin_en=%d mod_en=%d cuurent_voltage=%d mV\n",
+ if (rc < 0) {
+ pr_err("Failed to register regulator rc=%d\n", rc);
+ goto out;
+ }
+ pr_info("OLEDB registered successfully, ext_pin_en=%d mod_en=%d current_voltage=%d mV\n",
oledb->ext_pin_control, oledb->mod_enable,
oledb->current_voltage);
+ return 0;
+
+out:
+ if (oledb->force_pd_control) {
+ rc = qpnp_labibb_notifier_unregister(&oledb->oledb_nb);
+ if (rc < 0)
+ pr_err("Failed to unregister lab_vreg_ok notifier\n");
+ }
return rc;
}
static int qpnp_oledb_regulator_remove(struct platform_device *pdev)
{
- return 0;
+ int rc = 0;
+ struct qpnp_oledb *oledb = platform_get_drvdata(pdev);
+
+ if (oledb->force_pd_control) {
+ rc = qpnp_labibb_notifier_unregister(&oledb->oledb_nb);
+ if (rc < 0)
+ pr_err("Failed to unregister lab_vreg_ok notifier\n");
+ }
+
+ return rc;
}
const struct of_device_id qpnp_oledb_regulator_match_table[] = {
diff --git a/drivers/regulator/spm-regulator.c b/drivers/regulator/spm-regulator.c
index 6893e52f6bdd..c75beec59a79 100644
--- a/drivers/regulator/spm-regulator.c
+++ b/drivers/regulator/spm-regulator.c
@@ -29,9 +29,9 @@
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/spm-regulator.h>
#include <soc/qcom/spm.h>
+#include <linux/arm-smccc.h>
#if defined(CONFIG_ARM64) || (defined(CONFIG_ARM) && defined(CONFIG_ARM_PSCI))
- asmlinkage int __invoke_psci_fn_smc(u64, u64, u64, u64);
#else
#define __invoke_psci_fn_smc(a, b, c, d) 0
#endif
diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c
index fb991ec76423..696116ebdf50 100644
--- a/drivers/regulator/tps65910-regulator.c
+++ b/drivers/regulator/tps65910-regulator.c
@@ -1111,6 +1111,12 @@ static int tps65910_probe(struct platform_device *pdev)
pmic->num_regulators = ARRAY_SIZE(tps65910_regs);
pmic->ext_sleep_control = tps65910_ext_sleep_control;
info = tps65910_regs;
+ /* Work around silicon erratum SWCZ010: output programmed
+ * voltage level can go higher than expected or crash
+ * Workaround: use no synchronization of DCDC clocks
+ */
+ tps65910_reg_clear_bits(pmic->mfd, TPS65910_DCDCCTRL,
+ DCDCCTRL_DCDCCKSYNC_MASK);
break;
case TPS65911:
pmic->get_ctrl_reg = &tps65911_get_ctrl_register;
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index ec2e9c5fb993..22394fe30579 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -109,6 +109,7 @@
/* OMAP_RTC_OSC_REG bit fields: */
#define OMAP_RTC_OSC_32KCLK_EN BIT(6)
#define OMAP_RTC_OSC_SEL_32KCLK_SRC BIT(3)
+#define OMAP_RTC_OSC_OSC32K_GZ_DISABLE BIT(4)
/* OMAP_RTC_IRQWAKEEN bit fields: */
#define OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN BIT(1)
@@ -646,8 +647,9 @@ static int omap_rtc_probe(struct platform_device *pdev)
*/
if (rtc->has_ext_clk) {
reg = rtc_read(rtc, OMAP_RTC_OSC_REG);
- rtc_write(rtc, OMAP_RTC_OSC_REG,
- reg | OMAP_RTC_OSC_SEL_32KCLK_SRC);
+ reg &= ~OMAP_RTC_OSC_OSC32K_GZ_DISABLE;
+ reg |= OMAP_RTC_OSC_32KCLK_EN | OMAP_RTC_OSC_SEL_32KCLK_SRC;
+ rtc_writel(rtc, OMAP_RTC_OSC_REG, reg);
}
rtc->type->lock(rtc);
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index 7c511add5aa7..bae98521c808 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -124,7 +124,12 @@ con3270_create_status(struct con3270 *cp)
static void
con3270_update_string(struct con3270 *cp, struct string *s, int nr)
{
- if (s->len >= cp->view.cols - 5)
+ if (s->len < 4) {
+ /* This indicates a bug, but printing a warning would
+ * cause a deadlock. */
+ return;
+ }
+ if (s->string[s->len - 4] != TO_RA)
return;
raw3270_buffer_address(cp->view.dev, s->string + s->len - 3,
cp->view.cols * (nr + 1));
@@ -461,11 +466,11 @@ con3270_cline_end(struct con3270 *cp)
cp->cline->len + 4 : cp->view.cols;
s = con3270_alloc_string(cp, size);
memcpy(s->string, cp->cline->string, cp->cline->len);
- if (s->len < cp->view.cols - 5) {
+ if (cp->cline->len < cp->view.cols - 5) {
s->string[s->len - 4] = TO_RA;
s->string[s->len - 1] = 0;
} else {
- while (--size > cp->cline->len)
+ while (--size >= cp->cline->len)
s->string[size] = cp->view.ascebc[' '];
}
/* Replace cline with allocated line s and reset cline. */
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index c424c0c7367e..1e16331891a9 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -95,12 +95,13 @@ struct chsc_ssd_area {
int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
{
struct chsc_ssd_area *ssd_area;
+ unsigned long flags;
int ccode;
int ret;
int i;
int mask;
- spin_lock_irq(&chsc_page_lock);
+ spin_lock_irqsave(&chsc_page_lock, flags);
memset(chsc_page, 0, PAGE_SIZE);
ssd_area = chsc_page;
ssd_area->request.length = 0x0010;
@@ -144,7 +145,7 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
ssd->fla[i] = ssd_area->fla[i];
}
out:
- spin_unlock_irq(&chsc_page_lock);
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
}
@@ -832,9 +833,10 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable)
u32 fmt : 4;
u32 : 16;
} __attribute__ ((packed)) *secm_area;
+ unsigned long flags;
int ret, ccode;
- spin_lock_irq(&chsc_page_lock);
+ spin_lock_irqsave(&chsc_page_lock, flags);
memset(chsc_page, 0, PAGE_SIZE);
secm_area = chsc_page;
secm_area->request.length = 0x0050;
@@ -864,7 +866,7 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable)
CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n",
secm_area->response.code);
out:
- spin_unlock_irq(&chsc_page_lock);
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
}
@@ -993,6 +995,7 @@ chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
int chsc_get_channel_measurement_chars(struct channel_path *chp)
{
+ unsigned long flags;
int ccode, ret;
struct {
@@ -1022,7 +1025,7 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)
if (!css_chsc_characteristics.scmc || !css_chsc_characteristics.secm)
return 0;
- spin_lock_irq(&chsc_page_lock);
+ spin_lock_irqsave(&chsc_page_lock, flags);
memset(chsc_page, 0, PAGE_SIZE);
scmc_area = chsc_page;
scmc_area->request.length = 0x0010;
@@ -1054,7 +1057,7 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)
chsc_initialize_cmg_chars(chp, scmc_area->cmcv,
(struct cmg_chars *) &scmc_area->data);
out:
- spin_unlock_irq(&chsc_page_lock);
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
}
@@ -1135,6 +1138,7 @@ struct css_chsc_char css_chsc_characteristics;
int __init
chsc_determine_css_characteristics(void)
{
+ unsigned long flags;
int result;
struct {
struct chsc_header request;
@@ -1147,7 +1151,7 @@ chsc_determine_css_characteristics(void)
u32 chsc_char[508];
} __attribute__ ((packed)) *scsc_area;
- spin_lock_irq(&chsc_page_lock);
+ spin_lock_irqsave(&chsc_page_lock, flags);
memset(chsc_page, 0, PAGE_SIZE);
scsc_area = chsc_page;
scsc_area->request.length = 0x0010;
@@ -1169,7 +1173,7 @@ chsc_determine_css_characteristics(void)
CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n",
scsc_area->response.code);
exit:
- spin_unlock_irq(&chsc_page_lock);
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return result;
}
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 5d7fbe4e907e..581001989937 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -3,7 +3,7 @@
*
* Debug traces for zfcp.
*
- * Copyright IBM Corp. 2002, 2013
+ * Copyright IBM Corp. 2002, 2016
*/
#define KMSG_COMPONENT "zfcp"
@@ -65,7 +65,7 @@ void zfcp_dbf_pl_write(struct zfcp_dbf *dbf, void *data, u16 length, char *area,
* @tag: tag indicating which kind of unsolicited status has been received
* @req: request for which a response was received
*/
-void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req)
+void zfcp_dbf_hba_fsf_res(char *tag, int level, struct zfcp_fsf_req *req)
{
struct zfcp_dbf *dbf = req->adapter->dbf;
struct fsf_qtcb_prefix *q_pref = &req->qtcb->prefix;
@@ -85,6 +85,8 @@ void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req)
rec->u.res.req_issued = req->issued;
rec->u.res.prot_status = q_pref->prot_status;
rec->u.res.fsf_status = q_head->fsf_status;
+ rec->u.res.port_handle = q_head->port_handle;
+ rec->u.res.lun_handle = q_head->lun_handle;
memcpy(rec->u.res.prot_status_qual, &q_pref->prot_status_qual,
FSF_PROT_STATUS_QUAL_SIZE);
@@ -97,7 +99,7 @@ void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req)
rec->pl_len, "fsf_res", req->req_id);
}
- debug_event(dbf->hba, 1, rec, sizeof(*rec));
+ debug_event(dbf->hba, level, rec, sizeof(*rec));
spin_unlock_irqrestore(&dbf->hba_lock, flags);
}
@@ -241,7 +243,8 @@ static void zfcp_dbf_set_common(struct zfcp_dbf_rec *rec,
if (sdev) {
rec->lun_status = atomic_read(&sdev_to_zfcp(sdev)->status);
rec->lun = zfcp_scsi_dev_lun(sdev);
- }
+ } else
+ rec->lun = ZFCP_DBF_INVALID_LUN;
}
/**
@@ -320,13 +323,48 @@ void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp)
spin_unlock_irqrestore(&dbf->rec_lock, flags);
}
+/**
+ * zfcp_dbf_rec_run_wka - trace wka port event with info like running recovery
+ * @tag: identifier for event
+ * @wka_port: well known address port
+ * @req_id: request ID to correlate with potential HBA trace record
+ */
+void zfcp_dbf_rec_run_wka(char *tag, struct zfcp_fc_wka_port *wka_port,
+ u64 req_id)
+{
+ struct zfcp_dbf *dbf = wka_port->adapter->dbf;
+ struct zfcp_dbf_rec *rec = &dbf->rec_buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dbf->rec_lock, flags);
+ memset(rec, 0, sizeof(*rec));
+
+ rec->id = ZFCP_DBF_REC_RUN;
+ memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN);
+ rec->port_status = wka_port->status;
+ rec->d_id = wka_port->d_id;
+ rec->lun = ZFCP_DBF_INVALID_LUN;
+
+ rec->u.run.fsf_req_id = req_id;
+ rec->u.run.rec_status = ~0;
+ rec->u.run.rec_step = ~0;
+ rec->u.run.rec_action = ~0;
+ rec->u.run.rec_count = ~0;
+
+ debug_event(dbf->rec, 1, rec, sizeof(*rec));
+ spin_unlock_irqrestore(&dbf->rec_lock, flags);
+}
+
static inline
-void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, void *data, u8 id, u16 len,
- u64 req_id, u32 d_id)
+void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf,
+ char *paytag, struct scatterlist *sg, u8 id, u16 len,
+ u64 req_id, u32 d_id, u16 cap_len)
{
struct zfcp_dbf_san *rec = &dbf->san_buf;
u16 rec_len;
unsigned long flags;
+ struct zfcp_dbf_pay *payload = &dbf->pay_buf;
+ u16 pay_sum = 0;
spin_lock_irqsave(&dbf->san_lock, flags);
memset(rec, 0, sizeof(*rec));
@@ -334,10 +372,41 @@ void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, void *data, u8 id, u16 len,
rec->id = id;
rec->fsf_req_id = req_id;
rec->d_id = d_id;
- rec_len = min(len, (u16)ZFCP_DBF_SAN_MAX_PAYLOAD);
- memcpy(rec->payload, data, rec_len);
memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN);
+ rec->pl_len = len; /* full length even if we cap pay below */
+ if (!sg)
+ goto out;
+ rec_len = min_t(unsigned int, sg->length, ZFCP_DBF_SAN_MAX_PAYLOAD);
+ memcpy(rec->payload, sg_virt(sg), rec_len); /* part of 1st sg entry */
+ if (len <= rec_len)
+ goto out; /* skip pay record if full content in rec->payload */
+
+ /* if (len > rec_len):
+ * dump data up to cap_len ignoring small duplicate in rec->payload
+ */
+ spin_lock(&dbf->pay_lock);
+ memset(payload, 0, sizeof(*payload));
+ memcpy(payload->area, paytag, ZFCP_DBF_TAG_LEN);
+ payload->fsf_req_id = req_id;
+ payload->counter = 0;
+ for (; sg && pay_sum < cap_len; sg = sg_next(sg)) {
+ u16 pay_len, offset = 0;
+
+ while (offset < sg->length && pay_sum < cap_len) {
+ pay_len = min((u16)ZFCP_DBF_PAY_MAX_REC,
+ (u16)(sg->length - offset));
+ /* cap_len <= pay_sum < cap_len+ZFCP_DBF_PAY_MAX_REC */
+ memcpy(payload->data, sg_virt(sg) + offset, pay_len);
+ debug_event(dbf->pay, 1, payload,
+ zfcp_dbf_plen(pay_len));
+ payload->counter++;
+ offset += pay_len;
+ pay_sum += pay_len;
+ }
+ }
+ spin_unlock(&dbf->pay_lock);
+out:
debug_event(dbf->san, 1, rec, sizeof(*rec));
spin_unlock_irqrestore(&dbf->san_lock, flags);
}
@@ -354,9 +423,62 @@ void zfcp_dbf_san_req(char *tag, struct zfcp_fsf_req *fsf, u32 d_id)
struct zfcp_fsf_ct_els *ct_els = fsf->data;
u16 length;
- length = (u16)(ct_els->req->length + FC_CT_HDR_LEN);
- zfcp_dbf_san(tag, dbf, sg_virt(ct_els->req), ZFCP_DBF_SAN_REQ, length,
- fsf->req_id, d_id);
+ length = (u16)zfcp_qdio_real_bytes(ct_els->req);
+ zfcp_dbf_san(tag, dbf, "san_req", ct_els->req, ZFCP_DBF_SAN_REQ,
+ length, fsf->req_id, d_id, length);
+}
+
+static u16 zfcp_dbf_san_res_cap_len_if_gpn_ft(char *tag,
+ struct zfcp_fsf_req *fsf,
+ u16 len)
+{
+ struct zfcp_fsf_ct_els *ct_els = fsf->data;
+ struct fc_ct_hdr *reqh = sg_virt(ct_els->req);
+ struct fc_ns_gid_ft *reqn = (struct fc_ns_gid_ft *)(reqh + 1);
+ struct scatterlist *resp_entry = ct_els->resp;
+ struct fc_gpn_ft_resp *acc;
+ int max_entries, x, last = 0;
+
+ if (!(memcmp(tag, "fsscth2", 7) == 0
+ && ct_els->d_id == FC_FID_DIR_SERV
+ && reqh->ct_rev == FC_CT_REV
+ && reqh->ct_in_id[0] == 0
+ && reqh->ct_in_id[1] == 0
+ && reqh->ct_in_id[2] == 0
+ && reqh->ct_fs_type == FC_FST_DIR
+ && reqh->ct_fs_subtype == FC_NS_SUBTYPE
+ && reqh->ct_options == 0
+ && reqh->_ct_resvd1 == 0
+ && reqh->ct_cmd == FC_NS_GPN_FT
+ /* reqh->ct_mr_size can vary so do not match but read below */
+ && reqh->_ct_resvd2 == 0
+ && reqh->ct_reason == 0
+ && reqh->ct_explan == 0
+ && reqh->ct_vendor == 0
+ && reqn->fn_resvd == 0
+ && reqn->fn_domain_id_scope == 0
+ && reqn->fn_area_id_scope == 0
+ && reqn->fn_fc4_type == FC_TYPE_FCP))
+ return len; /* not GPN_FT response so do not cap */
+
+ acc = sg_virt(resp_entry);
+ max_entries = (reqh->ct_mr_size * 4 / sizeof(struct fc_gpn_ft_resp))
+ + 1 /* zfcp_fc_scan_ports: bytes correct, entries off-by-one
+ * to account for header as 1st pseudo "entry" */;
+
+ /* the basic CT_IU preamble is the same size as one entry in the GPN_FT
+ * response, allowing us to skip special handling for it - just skip it
+ */
+ for (x = 1; x < max_entries && !last; x++) {
+ if (x % (ZFCP_FC_GPN_FT_ENT_PAGE + 1))
+ acc++;
+ else
+ acc = sg_virt(++resp_entry);
+
+ last = acc->fp_flags & FC_NS_FID_LAST;
+ }
+ len = min(len, (u16)(x * sizeof(struct fc_gpn_ft_resp)));
+ return len; /* cap after last entry */
}
/**
@@ -370,9 +492,10 @@ void zfcp_dbf_san_res(char *tag, struct zfcp_fsf_req *fsf)
struct zfcp_fsf_ct_els *ct_els = fsf->data;
u16 length;
- length = (u16)(ct_els->resp->length + FC_CT_HDR_LEN);
- zfcp_dbf_san(tag, dbf, sg_virt(ct_els->resp), ZFCP_DBF_SAN_RES, length,
- fsf->req_id, 0);
+ length = (u16)zfcp_qdio_real_bytes(ct_els->resp);
+ zfcp_dbf_san(tag, dbf, "san_res", ct_els->resp, ZFCP_DBF_SAN_RES,
+ length, fsf->req_id, ct_els->d_id,
+ zfcp_dbf_san_res_cap_len_if_gpn_ft(tag, fsf, length));
}
/**
@@ -386,11 +509,13 @@ void zfcp_dbf_san_in_els(char *tag, struct zfcp_fsf_req *fsf)
struct fsf_status_read_buffer *srb =
(struct fsf_status_read_buffer *) fsf->data;
u16 length;
+ struct scatterlist sg;
length = (u16)(srb->length -
offsetof(struct fsf_status_read_buffer, payload));
- zfcp_dbf_san(tag, dbf, srb->payload.data, ZFCP_DBF_SAN_ELS, length,
- fsf->req_id, ntoh24(srb->d_id));
+ sg_init_one(&sg, srb->payload.data, length);
+ zfcp_dbf_san(tag, dbf, "san_els", &sg, ZFCP_DBF_SAN_ELS, length,
+ fsf->req_id, ntoh24(srb->d_id), length);
}
/**
@@ -399,7 +524,8 @@ void zfcp_dbf_san_in_els(char *tag, struct zfcp_fsf_req *fsf)
* @sc: pointer to struct scsi_cmnd
* @fsf: pointer to struct zfcp_fsf_req
*/
-void zfcp_dbf_scsi(char *tag, struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf)
+void zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *sc,
+ struct zfcp_fsf_req *fsf)
{
struct zfcp_adapter *adapter =
(struct zfcp_adapter *) sc->device->host->hostdata[0];
@@ -442,7 +568,7 @@ void zfcp_dbf_scsi(char *tag, struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf)
}
}
- debug_event(dbf->scsi, 1, rec, sizeof(*rec));
+ debug_event(dbf->scsi, level, rec, sizeof(*rec));
spin_unlock_irqrestore(&dbf->scsi_lock, flags);
}
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index 0be3d48681ae..36d07584271d 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -2,7 +2,7 @@
* zfcp device driver
* debug feature declarations
*
- * Copyright IBM Corp. 2008, 2010
+ * Copyright IBM Corp. 2008, 2015
*/
#ifndef ZFCP_DBF_H
@@ -17,6 +17,11 @@
#define ZFCP_DBF_INVALID_LUN 0xFFFFFFFFFFFFFFFFull
+enum zfcp_dbf_pseudo_erp_act_type {
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD = 0xff,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL = 0xfe,
+};
+
/**
* struct zfcp_dbf_rec_trigger - trace record for triggered recovery action
* @ready: number of ready recovery actions
@@ -110,6 +115,7 @@ struct zfcp_dbf_san {
u32 d_id;
#define ZFCP_DBF_SAN_MAX_PAYLOAD (FC_CT_HDR_LEN + 32)
char payload[ZFCP_DBF_SAN_MAX_PAYLOAD];
+ u16 pl_len;
} __packed;
/**
@@ -126,6 +132,8 @@ struct zfcp_dbf_hba_res {
u8 prot_status_qual[FSF_PROT_STATUS_QUAL_SIZE];
u32 fsf_status;
u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
+ u32 port_handle;
+ u32 lun_handle;
} __packed;
/**
@@ -279,7 +287,7 @@ static inline
void zfcp_dbf_hba_fsf_resp(char *tag, int level, struct zfcp_fsf_req *req)
{
if (debug_level_enabled(req->adapter->dbf->hba, level))
- zfcp_dbf_hba_fsf_res(tag, req);
+ zfcp_dbf_hba_fsf_res(tag, level, req);
}
/**
@@ -318,7 +326,7 @@ void _zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *scmd,
scmd->device->host->hostdata[0];
if (debug_level_enabled(adapter->dbf->scsi, level))
- zfcp_dbf_scsi(tag, scmd, req);
+ zfcp_dbf_scsi(tag, level, scmd, req);
}
/**
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 3fb410977014..a59d678125bd 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -3,7 +3,7 @@
*
* Error Recovery Procedures (ERP).
*
- * Copyright IBM Corp. 2002, 2010
+ * Copyright IBM Corp. 2002, 2015
*/
#define KMSG_COMPONENT "zfcp"
@@ -1217,8 +1217,14 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
break;
case ZFCP_ERP_ACTION_REOPEN_PORT:
- if (result == ZFCP_ERP_SUCCEEDED)
- zfcp_scsi_schedule_rport_register(port);
+ /* This switch case might also happen after a forced reopen
+ * was successfully done and thus overwritten with a new
+ * non-forced reopen at `ersfs_2'. In this case, we must not
+ * do the clean-up of the non-forced version.
+ */
+ if (act->step != ZFCP_ERP_STEP_UNINITIALIZED)
+ if (result == ZFCP_ERP_SUCCEEDED)
+ zfcp_scsi_schedule_rport_register(port);
/* fall through */
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
put_device(&port->dev);
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 5b500652572b..c8fed9fa1cca 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -3,7 +3,7 @@
*
* External function declarations.
*
- * Copyright IBM Corp. 2002, 2010
+ * Copyright IBM Corp. 2002, 2015
*/
#ifndef ZFCP_EXT_H
@@ -35,8 +35,9 @@ 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_wka(char *, struct zfcp_fc_wka_port *, u64);
extern void zfcp_dbf_hba_fsf_uss(char *, struct zfcp_fsf_req *);
-extern void zfcp_dbf_hba_fsf_res(char *, struct zfcp_fsf_req *);
+extern void zfcp_dbf_hba_fsf_res(char *, int, struct zfcp_fsf_req *);
extern void zfcp_dbf_hba_bit_err(char *, struct zfcp_fsf_req *);
extern void zfcp_dbf_hba_berr(struct zfcp_dbf *, struct zfcp_fsf_req *);
extern void zfcp_dbf_hba_def_err(struct zfcp_adapter *, u64, u16, void **);
@@ -44,7 +45,8 @@ extern void zfcp_dbf_hba_basic(char *, struct zfcp_adapter *);
extern void zfcp_dbf_san_req(char *, struct zfcp_fsf_req *, u32);
extern void zfcp_dbf_san_res(char *, struct zfcp_fsf_req *);
extern void zfcp_dbf_san_in_els(char *, struct zfcp_fsf_req *);
-extern void zfcp_dbf_scsi(char *, struct scsi_cmnd *, struct zfcp_fsf_req *);
+extern void zfcp_dbf_scsi(char *, int, struct scsi_cmnd *,
+ struct zfcp_fsf_req *);
/* zfcp_erp.c */
extern void zfcp_erp_set_adapter_status(struct zfcp_adapter *, u32);
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index 522a633c866a..75f820ca17b7 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -3,7 +3,7 @@
*
* Implementation of FSF commands.
*
- * Copyright IBM Corp. 2002, 2013
+ * Copyright IBM Corp. 2002, 2015
*/
#define KMSG_COMPONENT "zfcp"
@@ -508,7 +508,10 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
fc_host_port_type(shost) = FC_PORTTYPE_PTP;
break;
case FSF_TOPO_FABRIC:
- fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
+ if (bottom->connection_features & FSF_FEATURE_NPIV_MODE)
+ fc_host_port_type(shost) = FC_PORTTYPE_NPIV;
+ else
+ fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
break;
case FSF_TOPO_AL:
fc_host_port_type(shost) = FC_PORTTYPE_NLPORT;
@@ -613,7 +616,6 @@ static void zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *req)
if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) {
fc_host_permanent_port_name(shost) = bottom->wwpn;
- fc_host_port_type(shost) = FC_PORTTYPE_NPIV;
} else
fc_host_permanent_port_name(shost) = fc_host_port_name(shost);
fc_host_maxframe_size(shost) = bottom->maximum_frame_size;
@@ -982,8 +984,12 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req,
if (zfcp_adapter_multi_buffer_active(adapter)) {
if (zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sg_req))
return -EIO;
+ qtcb->bottom.support.req_buf_length =
+ zfcp_qdio_real_bytes(sg_req);
if (zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sg_resp))
return -EIO;
+ qtcb->bottom.support.resp_buf_length =
+ zfcp_qdio_real_bytes(sg_resp);
zfcp_qdio_set_data_div(qdio, &req->qdio_req,
zfcp_qdio_sbale_count(sg_req));
@@ -1073,6 +1079,7 @@ int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *wka_port,
req->handler = zfcp_fsf_send_ct_handler;
req->qtcb->header.port_handle = wka_port->handle;
+ ct->d_id = wka_port->d_id;
req->data = ct;
zfcp_dbf_san_req("fssct_1", req, wka_port->d_id);
@@ -1169,6 +1176,7 @@ int zfcp_fsf_send_els(struct zfcp_adapter *adapter, u32 d_id,
hton24(req->qtcb->bottom.support.d_id, d_id);
req->handler = zfcp_fsf_send_els_handler;
+ els->d_id = d_id;
req->data = els;
zfcp_dbf_san_req("fssels1", req, d_id);
@@ -1575,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;
+ struct zfcp_fsf_req *req = NULL;
int retval = -EIO;
spin_lock_irq(&qdio->req_q_lock);
@@ -1604,6 +1612,8 @@ int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port)
zfcp_fsf_req_free(req);
out:
spin_unlock_irq(&qdio->req_q_lock);
+ if (req && !IS_ERR(req))
+ zfcp_dbf_rec_run_wka("fsowp_1", wka_port, req->req_id);
return retval;
}
@@ -1628,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;
+ struct zfcp_fsf_req *req = NULL;
int retval = -EIO;
spin_lock_irq(&qdio->req_q_lock);
@@ -1657,6 +1667,8 @@ int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port)
zfcp_fsf_req_free(req);
out:
spin_unlock_irq(&qdio->req_q_lock);
+ if (req && !IS_ERR(req))
+ zfcp_dbf_rec_run_wka("fscwp_1", wka_port, req->req_id);
return retval;
}
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
index 57ae3ae1046d..be1c04b334c5 100644
--- a/drivers/s390/scsi/zfcp_fsf.h
+++ b/drivers/s390/scsi/zfcp_fsf.h
@@ -3,7 +3,7 @@
*
* Interface to the FSF support functions.
*
- * Copyright IBM Corp. 2002, 2010
+ * Copyright IBM Corp. 2002, 2015
*/
#ifndef FSF_H
@@ -436,6 +436,7 @@ struct zfcp_blk_drv_data {
* @handler_data: data passed to handler function
* @port: Optional pointer to port for zfcp internal ELS (only test link ADISC)
* @status: used to pass error status to calling function
+ * @d_id: Destination ID of either open WKA port for CT or of D_ID for ELS
*/
struct zfcp_fsf_ct_els {
struct scatterlist *req;
@@ -444,6 +445,7 @@ struct zfcp_fsf_ct_els {
void *handler_data;
struct zfcp_port *port;
int status;
+ u32 d_id;
};
#endif /* FSF_H */
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index b3c6ff49103b..9069f98a1817 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -3,7 +3,7 @@
*
* Interface to Linux SCSI midlayer.
*
- * Copyright IBM Corp. 2002, 2013
+ * Copyright IBM Corp. 2002, 2015
*/
#define KMSG_COMPONENT "zfcp"
@@ -556,6 +556,9 @@ static void zfcp_scsi_rport_register(struct zfcp_port *port)
ids.port_id = port->d_id;
ids.roles = FC_RPORT_ROLE_FCP_TARGET;
+ zfcp_dbf_rec_trig("scpaddy", port->adapter, port, NULL,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD);
rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids);
if (!rport) {
dev_err(&port->adapter->ccw_device->dev,
@@ -577,6 +580,9 @@ static void zfcp_scsi_rport_block(struct zfcp_port *port)
struct fc_rport *rport = port->rport;
if (rport) {
+ zfcp_dbf_rec_trig("scpdely", port->adapter, port, NULL,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL);
fc_remote_port_delete(rport);
port->rport = NULL;
}
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index 41f9a00e4f74..7aa01c1960ea 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -2297,15 +2297,23 @@ static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb,
}
case ARCMSR_MESSAGE_WRITE_WQBUFFER: {
unsigned char *ver_addr;
- int32_t user_len, cnt2end;
+ uint32_t user_len;
+ int32_t cnt2end;
uint8_t *pQbuffer, *ptmpuserbuffer;
+
+ user_len = pcmdmessagefld->cmdmessage.Length;
+ if (user_len > ARCMSR_API_DATA_BUFLEN) {
+ retvalue = ARCMSR_MESSAGE_FAIL;
+ goto message_out;
+ }
+
ver_addr = kmalloc(ARCMSR_API_DATA_BUFLEN, GFP_ATOMIC);
if (!ver_addr) {
retvalue = ARCMSR_MESSAGE_FAIL;
goto message_out;
}
ptmpuserbuffer = ver_addr;
- user_len = pcmdmessagefld->cmdmessage.Length;
+
memcpy(ptmpuserbuffer,
pcmdmessagefld->messagedatabuffer, user_len);
spin_lock_irqsave(&acb->wqbuffer_lock, flags);
@@ -2537,18 +2545,9 @@ static int arcmsr_queue_command_lck(struct scsi_cmnd *cmd,
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
struct CommandControlBlock *ccb;
int target = cmd->device->id;
- int lun = cmd->device->lun;
- uint8_t scsicmd = cmd->cmnd[0];
cmd->scsi_done = done;
cmd->host_scribble = NULL;
cmd->result = 0;
- if ((scsicmd == SYNCHRONIZE_CACHE) ||(scsicmd == SEND_DIAGNOSTIC)){
- if(acb->devstate[target][lun] == ARECA_RAID_GONE) {
- cmd->result = (DID_NO_CONNECT << 16);
- }
- cmd->scsi_done(cmd);
- return 0;
- }
if (target == 16) {
/* virtual device for iop message transfer */
arcmsr_handle_virtual_command(acb, cmd);
diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c
index 67669a9e73c1..f3a33312a9a6 100644
--- a/drivers/scsi/fnic/fnic_fcs.c
+++ b/drivers/scsi/fnic/fnic_fcs.c
@@ -954,8 +954,8 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq)
skb_put(skb, len);
pa = pci_map_single(fnic->pdev, skb->data, len, PCI_DMA_FROMDEVICE);
- r = pci_dma_mapping_error(fnic->pdev, pa);
- if (r) {
+ if (pci_dma_mapping_error(fnic->pdev, pa)) {
+ r = -ENOMEM;
printk(KERN_ERR "PCI mapping failed with error %d\n", r);
goto free_skb;
}
@@ -1093,8 +1093,8 @@ static int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
pa = pci_map_single(fnic->pdev, eth_hdr, tot_len, PCI_DMA_TODEVICE);
- ret = pci_dma_mapping_error(fnic->pdev, pa);
- if (ret) {
+ if (pci_dma_mapping_error(fnic->pdev, pa)) {
+ ret = -ENOMEM;
printk(KERN_ERR "DMA map failed with error %d\n", ret);
goto free_skb_on_err;
}
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index a3860367b568..e9ce74afd13f 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -3930,6 +3930,70 @@ static int hpsa_set_local_logical_count(struct ctlr_info *h,
return rc;
}
+static bool hpsa_is_disk_spare(struct ctlr_info *h, u8 *lunaddrbytes)
+{
+ struct bmic_identify_physical_device *id_phys;
+ bool is_spare = false;
+ int rc;
+
+ id_phys = kzalloc(sizeof(*id_phys), GFP_KERNEL);
+ if (!id_phys)
+ return false;
+
+ rc = hpsa_bmic_id_physical_device(h,
+ lunaddrbytes,
+ GET_BMIC_DRIVE_NUMBER(lunaddrbytes),
+ id_phys, sizeof(*id_phys));
+ if (rc == 0)
+ is_spare = (id_phys->more_flags >> 6) & 0x01;
+
+ kfree(id_phys);
+ return is_spare;
+}
+
+#define RPL_DEV_FLAG_NON_DISK 0x1
+#define RPL_DEV_FLAG_UNCONFIG_DISK_REPORTING_SUPPORTED 0x2
+#define RPL_DEV_FLAG_UNCONFIG_DISK 0x4
+
+#define BMIC_DEVICE_TYPE_ENCLOSURE 6
+
+static bool hpsa_skip_device(struct ctlr_info *h, u8 *lunaddrbytes,
+ struct ext_report_lun_entry *rle)
+{
+ u8 device_flags;
+ u8 device_type;
+
+ if (!MASKED_DEVICE(lunaddrbytes))
+ return false;
+
+ device_flags = rle->device_flags;
+ device_type = rle->device_type;
+
+ if (device_flags & RPL_DEV_FLAG_NON_DISK) {
+ if (device_type == BMIC_DEVICE_TYPE_ENCLOSURE)
+ return false;
+ return true;
+ }
+
+ if (!(device_flags & RPL_DEV_FLAG_UNCONFIG_DISK_REPORTING_SUPPORTED))
+ return false;
+
+ if (device_flags & RPL_DEV_FLAG_UNCONFIG_DISK)
+ return false;
+
+ /*
+ * Spares may be spun down, we do not want to
+ * do an Inquiry to a RAID set spare drive as
+ * that would have them spun up, that is a
+ * performance hit because I/O to the RAID device
+ * stops while the spin up occurs which can take
+ * over 50 seconds.
+ */
+ if (hpsa_is_disk_spare(h, lunaddrbytes))
+ return true;
+
+ return false;
+}
static void hpsa_update_scsi_devices(struct ctlr_info *h)
{
@@ -4023,6 +4087,7 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h)
u8 *lunaddrbytes, is_OBDR = 0;
int rc = 0;
int phys_dev_index = i - (raid_ctlr_position == 0);
+ bool skip_device = false;
physical_device = i < nphysicals + (raid_ctlr_position == 0);
@@ -4030,10 +4095,15 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h)
lunaddrbytes = figure_lunaddrbytes(h, raid_ctlr_position,
i, nphysicals, nlogicals, physdev_list, logdev_list);
- /* skip masked non-disk devices */
- if (MASKED_DEVICE(lunaddrbytes) && physical_device &&
- (physdev_list->LUN[phys_dev_index].device_flags & 0x01))
- continue;
+ /*
+ * Skip over some devices such as a spare.
+ */
+ if (!tmpdevice->external && physical_device) {
+ skip_device = hpsa_skip_device(h, lunaddrbytes,
+ &physdev_list->LUN[phys_dev_index]);
+ if (skip_device)
+ continue;
+ }
/* Get device type, vendor, model, device id */
rc = hpsa_update_device_info(h, lunaddrbytes, tmpdevice,
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 6aa317c303e2..1f9f9e5af207 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -717,7 +717,6 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
spin_lock_irqsave(vhost->host->host_lock, flags);
vhost->state = IBMVFC_NO_CRQ;
vhost->logged_in = 0;
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
/* Clean out the queue */
memset(crq->msgs, 0, PAGE_SIZE);
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
index ef4ff03242ea..aaf7da07a358 100644
--- a/drivers/scsi/megaraid/megaraid_sas.h
+++ b/drivers/scsi/megaraid/megaraid_sas.h
@@ -1923,7 +1923,7 @@ struct megasas_instance_template {
};
#define MEGASAS_IS_LOGICAL(scp) \
- (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1
+ ((scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1)
#define MEGASAS_DEV_INDEX(scp) \
(((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index 3f8d357b1bac..17c440b9d086 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -1688,16 +1688,13 @@ megasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd)
goto out_done;
}
- switch (scmd->cmnd[0]) {
- case SYNCHRONIZE_CACHE:
- /*
- * FW takes care of flush cache on its own
- * No need to send it down
- */
+ /*
+ * FW takes care of flush cache on its own for Virtual Disk.
+ * No need to send it down for VD. For JBOD send SYNCHRONIZE_CACHE to FW.
+ */
+ if ((scmd->cmnd[0] == SYNCHRONIZE_CACHE) && MEGASAS_IS_LOGICAL(scmd)) {
scmd->result = DID_OK << 16;
goto out_done;
- default:
- break;
}
if (instance->instancet->build_and_issue_cmd(instance, scmd)) {
@@ -5941,11 +5938,11 @@ static void megasas_detach_one(struct pci_dev *pdev)
if (fusion->ld_drv_map[i])
free_pages((ulong)fusion->ld_drv_map[i],
fusion->drv_map_pages);
- if (fusion->pd_seq_sync)
- dma_free_coherent(&instance->pdev->dev,
- pd_seq_map_sz,
- fusion->pd_seq_sync[i],
- fusion->pd_seq_phys[i]);
+ if (fusion->pd_seq_sync[i])
+ dma_free_coherent(&instance->pdev->dev,
+ pd_seq_map_sz,
+ fusion->pd_seq_sync[i],
+ fusion->pd_seq_phys[i]);
}
free_pages((ulong)instance->ctrl_context,
instance->ctrl_context_pages);
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index 6180f7970bbf..8cead04f26d6 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -1275,9 +1275,9 @@ scsih_target_alloc(struct scsi_target *starget)
sas_target_priv_data->handle = raid_device->handle;
sas_target_priv_data->sas_address = raid_device->wwid;
sas_target_priv_data->flags |= MPT_TARGET_FLAGS_VOLUME;
- sas_target_priv_data->raid_device = raid_device;
if (ioc->is_warpdrive)
- raid_device->starget = starget;
+ sas_target_priv_data->raid_device = raid_device;
+ raid_device->starget = starget;
}
spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
return 0;
@@ -3706,6 +3706,11 @@ _scsih_temp_threshold_events(struct MPT3SAS_ADAPTER *ioc,
}
}
+static inline bool ata_12_16_cmd(struct scsi_cmnd *scmd)
+{
+ return (scmd->cmnd[0] == ATA_12 || scmd->cmnd[0] == ATA_16);
+}
+
/**
* _scsih_flush_running_cmds - completing outstanding commands.
* @ioc: per adapter object
@@ -3727,6 +3732,9 @@ _scsih_flush_running_cmds(struct MPT3SAS_ADAPTER *ioc)
if (!scmd)
continue;
count++;
+ if (ata_12_16_cmd(scmd))
+ scsi_internal_device_unblock(scmd->device,
+ SDEV_RUNNING);
mpt3sas_base_free_smid(ioc, smid);
scsi_dma_unmap(scmd);
if (ioc->pci_error_recovery)
@@ -3831,8 +3839,6 @@ _scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status)
SAM_STAT_CHECK_CONDITION;
}
-
-
/**
* scsih_qcmd - main scsi request entry point
* @scmd: pointer to scsi command object
@@ -3859,6 +3865,13 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd)
if (ioc->logging_level & MPT_DEBUG_SCSI)
scsi_print_command(scmd);
+ /*
+ * Lock the device for any subsequent command until command is
+ * done.
+ */
+ if (ata_12_16_cmd(scmd))
+ scsi_internal_device_block(scmd->device);
+
sas_device_priv_data = scmd->device->hostdata;
if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
scmd->result = DID_NO_CONNECT << 16;
@@ -4431,6 +4444,9 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
if (scmd == NULL)
return 1;
+ if (ata_12_16_cmd(scmd))
+ scsi_internal_device_unblock(scmd->device, SDEV_RUNNING);
+
mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
if (mpi_reply == NULL) {
@@ -4510,7 +4526,7 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
le16_to_cpu(mpi_reply->DevHandle));
mpt3sas_trigger_scsi(ioc, data.skey, data.asc, data.ascq);
- if (!(ioc->logging_level & MPT_DEBUG_REPLY) &&
+ if ((ioc->logging_level & MPT_DEBUG_REPLY) &&
((scmd->sense_buffer[2] == UNIT_ATTENTION) ||
(scmd->sense_buffer[2] == MEDIUM_ERROR) ||
(scmd->sense_buffer[2] == HARDWARE_ERROR)))
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index fc6674db4f2d..c44cbf46221c 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -2257,6 +2257,8 @@ qla2xxx_scan_finished(struct Scsi_Host *shost, unsigned long time)
{
scsi_qla_host_t *vha = shost_priv(shost);
+ if (test_bit(UNLOADING, &vha->dpc_flags))
+ return 1;
if (!vha->host)
return 1;
if (time > vha->hw->loop_reset_delay * HZ)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index d09d60293c27..e357a393d56e 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -4981,6 +4981,7 @@ static void __exit scsi_debug_exit(void)
bus_unregister(&pseudo_lld_bus);
root_device_unregister(pseudo_primary);
+ vfree(map_storep);
vfree(dif_storep);
vfree(fake_storep);
}
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 87b35b78d879..95fa74203b99 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -1458,12 +1458,12 @@ retry:
out_err:
kfree(lun_data);
out:
- scsi_device_put(sdev);
if (scsi_device_created(sdev))
/*
* the sdev we used didn't appear in the report luns scan
*/
__scsi_remove_device(sdev);
+ scsi_device_put(sdev);
return ret;
}
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 5d81bcc1dc75..d5c00951cf93 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -2806,10 +2806,10 @@ static int sd_revalidate_disk(struct gendisk *disk)
if (sdkp->opt_xfer_blocks &&
sdkp->opt_xfer_blocks <= dev_max &&
sdkp->opt_xfer_blocks <= SD_DEF_XFER_BLOCKS &&
- sdkp->opt_xfer_blocks * sdp->sector_size >= PAGE_CACHE_SIZE)
- rw_max = q->limits.io_opt =
- sdkp->opt_xfer_blocks * sdp->sector_size;
- else
+ logical_to_bytes(sdp, sdkp->opt_xfer_blocks) >= PAGE_CACHE_SIZE) {
+ q->limits.io_opt = logical_to_bytes(sdp, sdkp->opt_xfer_blocks);
+ rw_max = logical_to_sectors(sdp, sdkp->opt_xfer_blocks);
+ } else
rw_max = BLK_DEF_MAX_SECTORS;
/* Combine with controller limits */
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
index 654630bb7d0e..765a6f1ac1b7 100644
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -151,6 +151,11 @@ static inline sector_t logical_to_sectors(struct scsi_device *sdev, sector_t blo
return blocks << (ilog2(sdev->sector_size) - 9);
}
+static inline unsigned int logical_to_bytes(struct scsi_device *sdev, sector_t blocks)
+{
+ return blocks * sdev->sector_size;
+}
+
/*
* A DIF-capable target device can be formatted with different
* protection schemes. Currently 0 through 3 are defined:
diff --git a/drivers/sensors/sensors_ssc.c b/drivers/sensors/sensors_ssc.c
index 0910ef34e777..0e299efece94 100644
--- a/drivers/sensors/sensors_ssc.c
+++ b/drivers/sensors/sensors_ssc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -26,6 +26,8 @@
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+
#include <soc/qcom/subsystem_restart.h>
#define IMAGE_LOAD_CMD 1
@@ -64,10 +66,11 @@ static struct attribute *attrs[] = {
};
static struct platform_device *slpi_private;
+static struct work_struct slpi_ldr_work;
-static void slpi_loader_do(struct platform_device *pdev)
+static void slpi_load_fw(struct work_struct *slpi_ldr_work)
{
-
+ struct platform_device *pdev = slpi_private;
struct slpi_loader_private *priv = NULL;
int ret;
const char *firmware_name = NULL;
@@ -111,6 +114,12 @@ fail:
dev_err(&pdev->dev, "%s: SLPI image loading failed\n", __func__);
}
+static void slpi_loader_do(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: scheduling work to load SLPI fw\n", __func__);
+ schedule_work(&slpi_ldr_work);
+}
+
static void slpi_loader_unload(struct platform_device *pdev)
{
struct slpi_loader_private *priv = NULL;
@@ -336,6 +345,8 @@ static int sensors_ssc_probe(struct platform_device *pdev)
goto cdev_add_err;
}
+ INIT_WORK(&slpi_ldr_work, slpi_load_fw);
+
return 0;
cdev_add_err:
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index d3f1050499f3..75bebf66376d 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -46,6 +46,15 @@ config QPNP_HAPTIC
module provides haptic feedback for user actions such as a long press
on the touch screen. It uses the Android timed-output framework.
+config QPNP_PBS
+ tristate "PBS trigger support for QPNP PMIC"
+ depends on SPMI
+ help
+ This driver supports configuring software PBS trigger event through PBS
+ RAM on Qualcomm Technologies, Inc. QPNP PMICs. This module provides
+ the APIs to the client drivers that wants to send the PBS trigger
+ event to the PBS RAM.
+
config MSM_SMD
depends on MSM_SMEM
bool "MSM Shared Memory Driver (SMD)"
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 2605107e2dbd..fa350d122384 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -52,6 +52,7 @@ endif
obj-$(CONFIG_QPNP_HAPTIC) += qpnp-haptic.o
obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
obj-$(CONFIG_QCOM_CPUSS_DUMP) += cpuss_dump.o
+obj-$(CONFIG_QPNP_PBS) += qpnp-pbs.o
obj-$(CONFIG_QCOM_PM) += spm.o
obj-$(CONFIG_QCOM_SMD) += smd.o
obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 768871ffa9a7..4e541773c805 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -195,6 +195,38 @@ struct ce_irq_list {
irqreturn_t (*handler)(int, void *);
};
+struct icnss_vreg_info {
+ struct regulator *reg;
+ const char *name;
+ u32 min_v;
+ u32 max_v;
+ u32 load_ua;
+ unsigned long settle_delay;
+ bool required;
+};
+
+struct icnss_clk_info {
+ struct clk *handle;
+ const char *name;
+ u32 freq;
+ bool required;
+};
+
+static struct icnss_vreg_info icnss_vreg_info[] = {
+ {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, true},
+ {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false},
+ {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false},
+ {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false},
+};
+
+#define ICNSS_VREG_INFO_SIZE ARRAY_SIZE(icnss_vreg_info)
+
+static struct icnss_clk_info icnss_clk_info[] = {
+ {NULL, "cxo_ref_clk_pin", 0, false},
+};
+
+#define ICNSS_CLK_INFO_SIZE ARRAY_SIZE(icnss_clk_info)
+
struct icnss_stats {
struct {
uint32_t posted;
@@ -248,6 +280,7 @@ struct icnss_stats {
uint32_t rejuvenate_ack_req;
uint32_t rejuvenate_ack_resp;
uint32_t rejuvenate_ack_err;
+ uint32_t trigger_recovery;
};
#define MAX_NO_OF_MAC_ADDR 4
@@ -267,6 +300,8 @@ static struct icnss_priv {
struct platform_device *pdev;
struct icnss_driver_ops *ops;
struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
+ struct icnss_vreg_info vreg_info[ICNSS_VREG_INFO_SIZE];
+ struct icnss_clk_info clk_info[ICNSS_CLK_INFO_SIZE];
u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
phys_addr_t mem_base_pa;
void __iomem *mem_base_va;
@@ -664,41 +699,220 @@ out:
return ret;
}
+static int icnss_vreg_on(struct icnss_priv *priv)
+{
+ int ret = 0;
+ struct icnss_vreg_info *vreg_info;
+ int i;
+
+ for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
+ vreg_info = &priv->vreg_info[i];
+
+ if (!vreg_info->reg)
+ continue;
+
+ icnss_pr_dbg("Regulator %s being enabled\n", vreg_info->name);
+
+ ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
+ vreg_info->max_v);
+ if (ret) {
+ icnss_pr_err("Regulator %s, can't set voltage: min_v: %u, max_v: %u, ret: %d\n",
+ vreg_info->name, vreg_info->min_v,
+ vreg_info->max_v, ret);
+ break;
+ }
+
+ if (vreg_info->load_ua) {
+ ret = regulator_set_load(vreg_info->reg,
+ vreg_info->load_ua);
+ if (ret < 0) {
+ icnss_pr_err("Regulator %s, can't set load: %u, ret: %d\n",
+ vreg_info->name,
+ vreg_info->load_ua, ret);
+ break;
+ }
+ }
+
+ ret = regulator_enable(vreg_info->reg);
+ if (ret) {
+ icnss_pr_err("Regulator %s, can't enable: %d\n",
+ vreg_info->name, ret);
+ break;
+ }
+
+ if (vreg_info->settle_delay)
+ udelay(vreg_info->settle_delay);
+ }
+
+ if (!ret)
+ return 0;
+
+ for (; i >= 0; i--) {
+ vreg_info = &priv->vreg_info[i];
+
+ if (!vreg_info->reg)
+ continue;
+
+ regulator_disable(vreg_info->reg);
+ regulator_set_load(vreg_info->reg, 0);
+ regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
+ }
+
+ return ret;
+}
+
+static int icnss_vreg_off(struct icnss_priv *priv)
+{
+ int ret = 0;
+ struct icnss_vreg_info *vreg_info;
+ int i;
+
+ for (i = ICNSS_VREG_INFO_SIZE - 1; i >= 0; i--) {
+ vreg_info = &priv->vreg_info[i];
+
+ if (!vreg_info->reg)
+ continue;
+
+ icnss_pr_dbg("Regulator %s being disabled\n", vreg_info->name);
+
+ ret = regulator_disable(vreg_info->reg);
+ if (ret)
+ icnss_pr_err("Regulator %s, can't disable: %d\n",
+ vreg_info->name, ret);
+
+ ret = regulator_set_load(vreg_info->reg, 0);
+ if (ret < 0)
+ icnss_pr_err("Regulator %s, can't set load: %d\n",
+ vreg_info->name, ret);
+
+ ret = regulator_set_voltage(vreg_info->reg, 0,
+ vreg_info->max_v);
+ if (ret)
+ icnss_pr_err("Regulator %s, can't set voltage: %d\n",
+ vreg_info->name, ret);
+ }
+
+ return ret;
+}
+
+static int icnss_clk_init(struct icnss_priv *priv)
+{
+ struct icnss_clk_info *clk_info;
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
+ clk_info = &priv->clk_info[i];
+
+ if (!clk_info->handle)
+ continue;
+
+ icnss_pr_dbg("Clock %s being enabled\n", clk_info->name);
+
+ if (clk_info->freq) {
+ ret = clk_set_rate(clk_info->handle, clk_info->freq);
+
+ if (ret) {
+ icnss_pr_err("Clock %s, can't set frequency: %u, ret: %d\n",
+ clk_info->name, clk_info->freq,
+ ret);
+ break;
+ }
+ }
+
+ ret = clk_prepare_enable(clk_info->handle);
+ if (ret) {
+ icnss_pr_err("Clock %s, can't enable: %d\n",
+ clk_info->name, ret);
+ break;
+ }
+ }
+
+ if (ret == 0)
+ return 0;
+
+ for (; i >= 0; i--) {
+ clk_info = &priv->clk_info[i];
+
+ if (!clk_info->handle)
+ continue;
+
+ clk_disable_unprepare(clk_info->handle);
+ }
+
+ return ret;
+}
+
+static int icnss_clk_deinit(struct icnss_priv *priv)
+{
+ struct icnss_clk_info *clk_info;
+ int i;
+
+ for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
+ clk_info = &priv->clk_info[i];
+
+ if (!clk_info->handle)
+ continue;
+
+ icnss_pr_dbg("Clock %s being disabled\n", clk_info->name);
+
+ clk_disable_unprepare(clk_info->handle);
+ }
+
+ return 0;
+}
+
static int icnss_hw_power_on(struct icnss_priv *priv)
{
int ret = 0;
- unsigned long flags;
icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state);
- spin_lock_irqsave(&priv->on_off_lock, flags);
+ spin_lock(&priv->on_off_lock);
if (test_bit(ICNSS_POWER_ON, &priv->state)) {
- spin_unlock_irqrestore(&priv->on_off_lock, flags);
+ spin_unlock(&priv->on_off_lock);
return ret;
}
set_bit(ICNSS_POWER_ON, &priv->state);
- spin_unlock_irqrestore(&priv->on_off_lock, flags);
+ spin_unlock(&priv->on_off_lock);
+
+ ret = icnss_vreg_on(priv);
+ if (ret)
+ goto out;
+
+ ret = icnss_clk_init(priv);
+ if (ret)
+ goto vreg_off;
+
+ return ret;
+vreg_off:
+ icnss_vreg_off(priv);
+out:
+ clear_bit(ICNSS_POWER_ON, &priv->state);
return ret;
}
static int icnss_hw_power_off(struct icnss_priv *priv)
{
int ret = 0;
- unsigned long flags;
if (test_bit(HW_ALWAYS_ON, &quirks))
return 0;
icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
- spin_lock_irqsave(&priv->on_off_lock, flags);
+ spin_lock(&priv->on_off_lock);
if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
- spin_unlock_irqrestore(&priv->on_off_lock, flags);
+ spin_unlock(&priv->on_off_lock);
return ret;
}
clear_bit(ICNSS_POWER_ON, &priv->state);
- spin_unlock_irqrestore(&priv->on_off_lock, flags);
+ spin_unlock(&priv->on_off_lock);
+
+ icnss_clk_deinit(priv);
+
+ ret = icnss_vreg_off(priv);
return ret;
}
@@ -1895,6 +2109,8 @@ static int icnss_call_driver_remove(struct icnss_priv *priv)
clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
+ icnss_hw_power_off(penv);
+
return 0;
}
@@ -1947,8 +2163,6 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
icnss_call_driver_remove(priv);
out:
- ret = icnss_hw_power_off(priv);
-
kfree(data);
return ret;
@@ -2922,12 +3136,24 @@ 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",
+ priv->state);
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
if (!priv->service_notifier[0].handle) {
- icnss_pr_err("Invalid handle during recovery\n");
+ 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);
+ priv->stats.trigger_recovery++;
+
/*
* Initiate PDR, required only for the first instance
*/
@@ -3005,6 +3231,114 @@ static void icnss_smmu_deinit(struct icnss_priv *priv)
priv->smmu_mapping = NULL;
}
+static int icnss_get_vreg_info(struct device *dev,
+ struct icnss_vreg_info *vreg_info)
+{
+ int ret = 0;
+ char prop_name[MAX_PROP_SIZE];
+ struct regulator *reg;
+ const __be32 *prop;
+ int len = 0;
+ int i;
+
+ reg = devm_regulator_get_optional(dev, vreg_info->name);
+ if (PTR_ERR(reg) == -EPROBE_DEFER) {
+ icnss_pr_err("EPROBE_DEFER for regulator: %s\n",
+ vreg_info->name);
+ ret = PTR_ERR(reg);
+ goto out;
+ }
+
+ if (IS_ERR(reg)) {
+ ret = PTR_ERR(reg);
+
+ if (vreg_info->required) {
+ icnss_pr_err("Regulator %s doesn't exist: %d\n",
+ vreg_info->name, ret);
+ goto out;
+ } else {
+ icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
+ vreg_info->name, ret);
+ goto done;
+ }
+ }
+
+ vreg_info->reg = reg;
+
+ snprintf(prop_name, MAX_PROP_SIZE,
+ "qcom,%s-config", vreg_info->name);
+
+ prop = of_get_property(dev->of_node, prop_name, &len);
+
+ icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
+ prop_name, len);
+
+ if (!prop || len < (2 * sizeof(__be32))) {
+ icnss_pr_dbg("Property %s %s\n", prop_name,
+ prop ? "invalid format" : "doesn't exist");
+ goto done;
+ }
+
+ for (i = 0; (i * sizeof(__be32)) < len; i++) {
+ switch (i) {
+ case 0:
+ vreg_info->min_v = be32_to_cpup(&prop[0]);
+ break;
+ case 1:
+ vreg_info->max_v = be32_to_cpup(&prop[1]);
+ break;
+ case 2:
+ vreg_info->load_ua = be32_to_cpup(&prop[2]);
+ break;
+ case 3:
+ vreg_info->settle_delay = be32_to_cpup(&prop[3]);
+ break;
+ default:
+ icnss_pr_dbg("Property %s, ignoring value at %d\n",
+ prop_name, i);
+ break;
+ }
+ }
+
+done:
+ icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n",
+ vreg_info->name, vreg_info->min_v, vreg_info->max_v,
+ vreg_info->load_ua, vreg_info->settle_delay);
+
+ return 0;
+
+out:
+ return ret;
+}
+
+static int icnss_get_clk_info(struct device *dev,
+ struct icnss_clk_info *clk_info)
+{
+ struct clk *handle;
+ int ret = 0;
+
+ handle = devm_clk_get(dev, clk_info->name);
+ if (IS_ERR(handle)) {
+ ret = PTR_ERR(handle);
+ if (clk_info->required) {
+ icnss_pr_err("Clock %s isn't available: %d\n",
+ clk_info->name, ret);
+ goto out;
+ } else {
+ icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name,
+ ret);
+ ret = 0;
+ goto out;
+ }
+ }
+
+ icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq);
+
+ clk_info->handle = handle;
+out:
+ return ret;
+}
+
static int icnss_fw_debug_show(struct seq_file *s, void *data)
{
struct icnss_priv *priv = s->private;
@@ -3370,6 +3704,7 @@ static int icnss_stats_show(struct seq_file *s, void *data)
ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
+ ICNSS_STATS_DUMP(s, priv, trigger_recovery);
seq_puts(s, "\n<------------------ PM stats ------------------->\n");
ICNSS_STATS_DUMP(s, priv, pm_suspend);
@@ -3715,6 +4050,21 @@ static int icnss_probe(struct platform_device *pdev)
if (ret == -EPROBE_DEFER)
goto out;
+ memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info));
+ for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
+ ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]);
+
+ if (ret)
+ goto out;
+ }
+
+ memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info));
+ for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
+ ret = icnss_get_clk_info(dev, &priv->clk_info[i]);
+ if (ret)
+ goto out;
+ }
+
if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass"))
priv->bypass_s1_smmu = true;
diff --git a/drivers/soc/qcom/qdsp6v2/adsp-loader.c b/drivers/soc/qcom/qdsp6v2/adsp-loader.c
index 51539a36a74f..b45eac88ed12 100644
--- a/drivers/soc/qcom/qdsp6v2/adsp-loader.c
+++ b/drivers/soc/qcom/qdsp6v2/adsp-loader.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -20,6 +20,8 @@
#include <linux/qdsp6v2/apr.h>
#include <linux/of_device.h>
#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+
#include <soc/qcom/subsystem_restart.h>
#define Q6_PIL_GET_DELAY_MS 100
@@ -44,12 +46,13 @@ static struct attribute *attrs[] = {
NULL,
};
+static struct work_struct adsp_ldr_work;
static struct platform_device *adsp_private;
static void adsp_loader_unload(struct platform_device *pdev);
-static void adsp_loader_do(struct platform_device *pdev)
+static void adsp_load_fw(struct work_struct *adsp_ldr_work)
{
-
+ struct platform_device *pdev = adsp_private;
struct adsp_loader_private *priv = NULL;
const char *adsp_dt = "qcom,adsp-state";
@@ -146,6 +149,11 @@ fail:
return;
}
+static void adsp_loader_do(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: scheduling work to load ADSP fw\n", __func__);
+ schedule_work(&adsp_ldr_work);
+}
static ssize_t adsp_boot_store(struct kobject *kobj,
struct kobj_attribute *attr,
@@ -270,6 +278,8 @@ static int adsp_loader_probe(struct platform_device *pdev)
return ret;
}
+ INIT_WORK(&adsp_ldr_work, adsp_load_fw);
+
return 0;
}
diff --git a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c
index 19974b61ec1c..d11ffdde23be 100644
--- a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c
+++ b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c
@@ -218,6 +218,7 @@ static void apr_tal_notify_remote_rx_intent(void *handle, const void *priv,
*/
pr_debug("%s: remote queued an intent\n", __func__);
apr_ch->if_remote_intent_ready = true;
+ wake_up(&apr_ch->wait);
}
void apr_tal_notify_state(void *handle, const void *priv, unsigned event)
diff --git a/drivers/soc/qcom/qdsp6v2/cdsp-loader.c b/drivers/soc/qcom/qdsp6v2/cdsp-loader.c
index 0b801c5cd7dd..cae26e3913d6 100644
--- a/drivers/soc/qcom/qdsp6v2/cdsp-loader.c
+++ b/drivers/soc/qcom/qdsp6v2/cdsp-loader.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014,2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,6 +19,8 @@
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+
#include <soc/qcom/subsystem_restart.h>
#define BOOT_CMD 1
@@ -47,11 +49,12 @@ static struct attribute *attrs[] = {
static u32 cdsp_state = CDSP_SUBSYS_DOWN;
static struct platform_device *cdsp_private;
+static struct work_struct cdsp_ldr_work;
static void cdsp_loader_unload(struct platform_device *pdev);
-static int cdsp_loader_do(struct platform_device *pdev)
+static void cdsp_load_fw(struct work_struct *cdsp_ldr_work)
{
-
+ struct platform_device *pdev = cdsp_private;
struct cdsp_loader_private *priv = NULL;
int rc = 0;
@@ -101,14 +104,19 @@ static int cdsp_loader_do(struct platform_device *pdev)
}
dev_dbg(&pdev->dev, "%s: CDSP image is loaded\n", __func__);
- return rc;
+ return;
}
fail:
dev_err(&pdev->dev, "%s: CDSP image loading failed\n", __func__);
- return rc;
+ return;
}
+static void cdsp_loader_do(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: scheduling work to load CDSP fw\n", __func__);
+ schedule_work(&cdsp_ldr_work);
+}
static ssize_t cdsp_boot_store(struct kobject *kobj,
struct kobj_attribute *attr,
@@ -126,7 +134,7 @@ static ssize_t cdsp_boot_store(struct kobject *kobj,
pr_debug("%s: going to call cdsp_loader_do\n", __func__);
cdsp_loader_do(cdsp_private);
} else if (boot == IMAGE_UNLOAD_CMD) {
- pr_debug("%s: going to call adsp_unloader\n", __func__);
+ pr_debug("%s: going to call cdsp_unloader\n", __func__);
cdsp_loader_unload(cdsp_private);
}
return count;
@@ -238,6 +246,8 @@ static int cdsp_loader_probe(struct platform_device *pdev)
return ret;
}
+ INIT_WORK(&cdsp_ldr_work, cdsp_load_fw);
+
return 0;
}
diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c
index 39070561d7e4..cf0b7ff25201 100644
--- a/drivers/soc/qcom/qpnp-haptic.c
+++ b/drivers/soc/qcom/qpnp-haptic.c
@@ -141,9 +141,7 @@
#define QPNP_HAP_CYCLS 5
#define QPNP_TEST_TIMER_MS 5
-#define AUTO_RES_ENABLE_TIMEOUT 20000
-#define AUTO_RES_ERR_CAPTURE_RES 5
-#define AUTO_RES_ERR_MAX 15
+#define QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN 20000
#define MISC_TRIM_ERROR_RC19P2_CLK 0x09F5
#define MISC_SEC_ACCESS 0x09D0
@@ -152,8 +150,22 @@
#define POLL_TIME_AUTO_RES_ERR_NS (5 * NSEC_PER_MSEC)
-#define LRA_POS_FREQ_COUNT 6
-int lra_play_rate_code[LRA_POS_FREQ_COUNT];
+#define MAX_POSITIVE_VARIATION_LRA_FREQ 30
+#define MAX_NEGATIVE_VARIATION_LRA_FREQ -30
+#define FREQ_VARIATION_STEP 5
+#define AUTO_RES_ERROR_CAPTURE_RES 5
+#define AUTO_RES_ERROR_MAX 30
+#define ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE \
+ ((MAX_POSITIVE_VARIATION_LRA_FREQ - MAX_NEGATIVE_VARIATION_LRA_FREQ) \
+ / FREQ_VARIATION_STEP)
+#define LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent) \
+ (hap->init_drive_period_code = (hap->init_drive_period_code * \
+ (1000 + rc_clk_err_percent_x10)) / 1000)
+#define LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent) \
+ (hap->init_drive_period_code = (hap->init_drive_period_code * \
+ (1000 - rc_clk_err_percent_x10)) / 1000)
+
+u32 adjusted_lra_play_rate_code[ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE];
/* haptic debug register set */
static u8 qpnp_hap_dbg_regs[] = {
@@ -246,10 +258,21 @@ struct qpnp_pwm_info {
* @ pwm_info - pwm info
* @ lock - mutex lock
* @ wf_lock - mutex lock for waveform
+ * @ init_drive_period_code - the initial lra drive period code
+ * @ drive_period_code_max_limit_percent_variation - maximum limit of
+ percentage variation of drive period code
+ * @ drive_period_code_min_limit_percent_variation - minimum limit og
+ percentage variation of drive period code
+ * @ drive_period_code_max_limit - calculated drive period code with
+ percentage variation on the higher side.
+ * @ drive_period_code_min_limit - calculated drive period code with
+ percentage variation on the lower side
* @ play_mode - play mode
* @ auto_res_mode - auto resonace mode
* @ lra_high_z - high z option line
* @ timeout_ms - max timeout in ms
+ * @ time_required_to_generate_back_emf_us - the time required for sufficient
+ back-emf to be generated for auto resonance to be successful
* @ vmax_mv - max voltage in mv
* @ ilim_ma - limiting current in ma
* @ sc_deb_cycles - short circuit debounce cycles
@@ -280,6 +303,8 @@ struct qpnp_pwm_info {
* @ 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
+ * @ perform_lra_auto_resonance_search - whether lra auto resonance search
+ * algorithm should be performed or not.
*/
struct qpnp_hap {
struct platform_device *pdev;
@@ -300,7 +325,9 @@ struct qpnp_hap {
enum qpnp_hap_mode play_mode;
enum qpnp_hap_auto_res_mode auto_res_mode;
enum qpnp_hap_high_z lra_high_z;
+ u32 init_drive_period_code;
u32 timeout_ms;
+ u32 time_required_to_generate_back_emf_us;
u32 vmax_mv;
u32 ilim_ma;
u32 sc_deb_cycles;
@@ -312,16 +339,21 @@ struct qpnp_hap {
u32 play_irq;
u32 sc_irq;
u16 base;
+ u16 drive_period_code_max_limit;
+ u16 drive_period_code_min_limit;
+ u8 drive_period_code_max_limit_percent_variation;
+ u8 drive_period_code_min_limit_percent_variation;
u8 act_type;
u8 wave_shape;
- u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN];
- u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN];
- u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN];
+ u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN];
+ u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN];
+ u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN];
u8 reg_en_ctl;
u8 reg_play;
u8 lra_res_cal_period;
u8 sc_duration;
u8 ext_pwm_dtest_line;
+ bool vcc_pon_enabled;
bool state;
bool use_play_irq;
bool use_sc_irq;
@@ -333,6 +365,7 @@ struct qpnp_hap {
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;
@@ -1314,29 +1347,62 @@ static struct device_attribute qpnp_hap_attrs[] = {
qpnp_hap_min_max_test_data_store),
};
-static void calculate_lra_code(struct qpnp_hap *hap)
+static int calculate_lra_code(struct qpnp_hap *hap)
{
- u8 play_rate_code_lo, play_rate_code_hi;
- int play_rate_code, neg_idx = 0, pos_idx = LRA_POS_FREQ_COUNT-1;
- int lra_init_freq, freq_variation, start_variation = AUTO_RES_ERR_MAX;
+ u8 lra_drive_period_code_lo = 0, lra_drive_period_code_hi = 0;
+ u32 lra_drive_period_code, lra_drive_frequency_hz, freq_variation;
+ u8 start_variation = AUTO_RES_ERROR_MAX, i;
+ u8 neg_idx = 0, pos_idx = ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE - 1;
+ int rc = 0;
- qpnp_hap_read_reg(hap, &play_rate_code_lo,
- QPNP_HAP_RATE_CFG1_REG(hap->base));
- qpnp_hap_read_reg(hap, &play_rate_code_hi,
- QPNP_HAP_RATE_CFG2_REG(hap->base));
+ rc = qpnp_hap_read_reg(hap, &lra_drive_period_code_lo,
+ QPNP_HAP_RATE_CFG1_REG(hap->base));
+ if (rc) {
+ dev_err(&hap->pdev->dev,
+ "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));
+ if (rc) {
+ dev_err(&hap->pdev->dev,
+ "Error while reading RATE_CFG2 register\n");
+ return rc;
+ }
- play_rate_code = (play_rate_code_hi << 8) | (play_rate_code_lo & 0xff);
+ 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");
+ return -EINVAL;
+ }
- lra_init_freq = 200000 / play_rate_code;
+ lra_drive_period_code =
+ (lra_drive_period_code_hi << 8) | (lra_drive_period_code_lo & 0xff);
+ lra_drive_frequency_hz = 200000 / lra_drive_period_code;
- while (start_variation >= AUTO_RES_ERR_CAPTURE_RES) {
- freq_variation = (lra_init_freq * start_variation) / 100;
- lra_play_rate_code[neg_idx++] = 200000 / (lra_init_freq -
- freq_variation);
- lra_play_rate_code[pos_idx--] = 200000 / (lra_init_freq +
- freq_variation);
- start_variation -= AUTO_RES_ERR_CAPTURE_RES;
+ while (start_variation >= AUTO_RES_ERROR_CAPTURE_RES) {
+ freq_variation =
+ (lra_drive_frequency_hz * start_variation) / 100;
+ adjusted_lra_play_rate_code[neg_idx++] =
+ 200000 / (lra_drive_frequency_hz - freq_variation);
+ adjusted_lra_play_rate_code[pos_idx--] =
+ 200000 / (lra_drive_frequency_hz + freq_variation);
+ start_variation -= AUTO_RES_ERROR_CAPTURE_RES;
}
+
+ dev_dbg(&hap->pdev->dev,
+ "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]);
+
+ return 0;
}
static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
@@ -1369,20 +1435,37 @@ static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
static void update_lra_frequency(struct qpnp_hap *hap)
{
u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0;
+ u32 play_rate_code;
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));
- if (lra_auto_res_lo && lra_auto_res_hi) {
- qpnp_hap_write_reg(hap, &lra_auto_res_lo,
- QPNP_HAP_RATE_CFG1_REG(hap->base));
+ play_rate_code =
+ (lra_auto_res_hi & 0xF0) << 4 | (lra_auto_res_lo & 0xFF);
- 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));
- }
+ dev_dbg(&hap->pdev->dev,
+ "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);
+
+ /*
+ * If the drive period code read from AUTO RES_LO and AUTO_RES_HI
+ * registers is more than the max limit percent variation read from
+ * DT or less than the min limit percent variation read from DT, then
+ * RATE_CFG registers are not uptdated.
+ */
+
+ if ((play_rate_code <= hap->drive_period_code_min_limit) ||
+ (play_rate_code >= hap->drive_period_code_max_limit))
+ return;
+
+ qpnp_hap_write_reg(hap, &lra_auto_res_lo,
+ QPNP_HAP_RATE_CFG1_REG(hap->base));
+
+ 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));
}
static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer)
@@ -1412,36 +1495,38 @@ static void correct_auto_res_error(struct work_struct *auto_res_err_work)
struct qpnp_hap, auto_res_err_work);
u8 lra_code_lo, lra_code_hi, disable_hap = 0x00;
- static int lra_freq_index;
- ktime_t currtime, remaining_time;
- int temp, rem = 0, index = lra_freq_index % LRA_POS_FREQ_COUNT;
+ static u8 lra_freq_index;
+ ktime_t currtime = ktime_set(0, 0), remaining_time = ktime_set(0, 0);
- if (hrtimer_active(&hap->hap_timer)) {
+ if (hrtimer_active(&hap->hap_timer))
remaining_time = hrtimer_get_remaining(&hap->hap_timer);
- rem = (int)ktime_to_us(remaining_time);
- }
qpnp_hap_play(hap, 0);
qpnp_hap_write_reg(hap, &disable_hap,
QPNP_HAP_EN_CTL_REG(hap->base));
- lra_code_lo = lra_play_rate_code[index] & QPNP_HAP_RATE_CFG1_MASK;
- qpnp_hap_write_reg(hap, &lra_code_lo,
- QPNP_HAP_RATE_CFG1_REG(hap->base));
+ if (hap->perform_lra_auto_resonance_search) {
+ lra_code_lo =
+ adjusted_lra_play_rate_code[lra_freq_index]
+ & QPNP_HAP_RATE_CFG1_MASK;
- qpnp_hap_read_reg(hap, &lra_code_hi,
- QPNP_HAP_RATE_CFG2_REG(hap->base));
+ qpnp_hap_write_reg(hap, &lra_code_lo,
+ QPNP_HAP_RATE_CFG1_REG(hap->base));
- lra_code_hi &= QPNP_HAP_RATE_CFG2_MASK;
- temp = lra_play_rate_code[index] >> QPNP_HAP_RATE_CFG2_SHFT;
- lra_code_hi |= temp;
+ lra_code_hi = adjusted_lra_play_rate_code[lra_freq_index]
+ >> QPNP_HAP_RATE_CFG2_SHFT;
- qpnp_hap_write_reg(hap, &lra_code_hi,
+ qpnp_hap_write_reg(hap, &lra_code_hi,
QPNP_HAP_RATE_CFG2_REG(hap->base));
- lra_freq_index++;
+ lra_freq_index = (lra_freq_index+1) %
+ ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE;
+ }
- if (rem > 0) {
+ dev_dbg(&hap->pdev->dev, "Remaining time is %lld\n",
+ ktime_to_us(remaining_time));
+
+ if ((ktime_to_us(remaining_time)) > 0) {
currtime = ktime_get();
hap->state = 1;
hrtimer_forward(&hap->hap_timer, currtime, remaining_time);
@@ -1455,6 +1540,7 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
int rc = 0;
u8 val = 0;
unsigned long timeout_ns = POLL_TIME_AUTO_RES_ERR_NS;
+ u32 back_emf_delay_us = hap->time_required_to_generate_back_emf_us;
if (hap->play_mode == QPNP_HAP_PWM) {
if (on)
@@ -1464,8 +1550,21 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
} else if (hap->play_mode == QPNP_HAP_BUFFER ||
hap->play_mode == QPNP_HAP_DIRECT) {
if (on) {
- if (hap->correct_lra_drive_freq ||
- hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)
+ /*
+ * For auto resonance detection to work properly,
+ * sufficient back-emf has to be generated. In general,
+ * back-emf takes some time to build up. When the auto
+ * resonance mode is chosen as QWD, high-z will be
+ * applied for every LRA cycle and hence there won't be
+ * enough back-emf at the start-up. Hence, the motor
+ * needs to vibrate for few LRA cycles after the PLAY
+ * bit is asserted. So disable the auto resonance here
+ * and enable it after the sleep of
+ * 'time_required_to_generate_back_emf_us' is completed.
+ */
+ if ((hap->act_type == QPNP_HAP_LRA) &&
+ (hap->correct_lra_drive_freq ||
+ hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD))
qpnp_hap_auto_res_enable(hap, 0);
rc = qpnp_hap_mod_enable(hap, on);
@@ -1474,17 +1573,18 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
rc = qpnp_hap_play(hap, on);
- if ((hap->act_type == QPNP_HAP_LRA &&
- hap->correct_lra_drive_freq) ||
- hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) {
- usleep_range(AUTO_RES_ENABLE_TIMEOUT,
- (AUTO_RES_ENABLE_TIMEOUT + 1));
+ if ((hap->act_type == QPNP_HAP_LRA) &&
+ (hap->correct_lra_drive_freq ||
+ hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)) {
+ usleep_range(back_emf_delay_us,
+ (back_emf_delay_us + 1));
rc = qpnp_hap_auto_res_enable(hap, 1);
if (rc < 0)
return rc;
}
- if (hap->correct_lra_drive_freq) {
+ if (hap->act_type == QPNP_HAP_LRA &&
+ hap->correct_lra_drive_freq) {
/*
* Start timer to poll Auto Resonance error bit
*/
@@ -1500,7 +1600,8 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
if (rc < 0)
return rc;
- if (hap->correct_lra_drive_freq) {
+ if (hap->act_type == QPNP_HAP_LRA &&
+ hap->correct_lra_drive_freq) {
rc = qpnp_hap_read_reg(hap, &val,
QPNP_HAP_STATUS(hap->base));
if (!(val & AUTO_RES_ERR_BIT))
@@ -1511,7 +1612,6 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
if (hap->act_type == QPNP_HAP_LRA &&
hap->correct_lra_drive_freq) {
hrtimer_cancel(&hap->auto_res_err_poll_timer);
- calculate_lra_code(hap);
}
}
}
@@ -1619,13 +1719,15 @@ static void qpnp_hap_worker(struct work_struct *work)
struct qpnp_hap *hap = container_of(work, struct qpnp_hap,
work);
u8 val = 0x00;
- int rc, reg_en = 0;
+ int rc;
- if (hap->vcc_pon) {
- reg_en = regulator_enable(hap->vcc_pon);
- if (reg_en)
- pr_err("%s: could not enable vcc_pon regulator\n",
- __func__);
+ if (hap->vcc_pon && hap->state && !hap->vcc_pon_enabled) {
+ rc = regulator_enable(hap->vcc_pon);
+ if (rc < 0)
+ pr_err("%s: could not enable vcc_pon regulator rc=%d\n",
+ __func__, rc);
+ else
+ hap->vcc_pon_enabled = true;
}
/* Disable haptics module if the duration of short circuit
@@ -1640,11 +1742,13 @@ static void qpnp_hap_worker(struct work_struct *work)
qpnp_hap_set(hap, hap->state);
}
- if (hap->vcc_pon && !reg_en) {
+ if (hap->vcc_pon && !hap->state && hap->vcc_pon_enabled) {
rc = regulator_disable(hap->vcc_pon);
if (rc)
- pr_err("%s: could not disable vcc_pon regulator\n",
- __func__);
+ pr_err("%s: could not disable vcc_pon regulator rc=%d\n",
+ __func__, rc);
+ else
+ hap->vcc_pon_enabled = false;
}
}
@@ -1706,10 +1810,16 @@ static SIMPLE_DEV_PM_OPS(qpnp_haptic_pm_ops, qpnp_haptic_suspend, NULL);
/* Configuration api for haptics registers */
static int qpnp_hap_config(struct qpnp_hap *hap)
{
- u8 reg = 0, unlock_val, error_value;
- int rc, i, temp;
+ u8 reg = 0, unlock_val;
+ 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)
@@ -1838,16 +1948,22 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
else if (hap->wave_play_rate_us > QPNP_HAP_WAV_PLAY_RATE_US_MAX)
hap->wave_play_rate_us = QPNP_HAP_WAV_PLAY_RATE_US_MAX;
- temp = hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US;
+ hap->init_drive_period_code =
+ hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US;
/*
- * The frequency of 19.2Mzhz RC clock is subject to variation.
- * In PMI8950, TRIM_ERROR_RC19P2_CLK register in MISC module
- * holds the frequency error in 19.2Mhz RC clock
+ * The frequency of 19.2Mzhz RC clock is subject to variation. Currently
+ * a few PMI modules have MISC_TRIM_ERROR_RC19P2_CLK register
+ * present in their MISC block. This register holds the frequency error
+ * in 19.2Mhz 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,
@@ -1855,36 +1971,69 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
regmap_read(hap->regmap, MISC_TRIM_ERROR_RC19P2_CLK,
&error_code);
+ dev_dbg(&hap->pdev->dev, "TRIM register = 0x%x\n", error_code);
- error_value = (error_code & 0x0F) * 7;
+ /*
+ * Extract the 4 LSBs and multiply by 7 to get
+ * the %error in RC clock multiplied by 10
+ */
+ rc_clk_err_percent_x10 = (error_code & 0x0F) * 7;
- if (error_code & 0x80)
- temp = (temp * (1000 - error_value)) / 1000;
+ /*
+ * If the TRIM register holds value less than 0x80,
+ * then there is a positive error in the RC clock.
+ * If the TRIM register holds value greater than or equal to
+ * 0x80, then there is a negative error in the RC clock.
+ *
+ * The adjusted play rate code is calculated as follows:
+ * LRA drive period code (RATE_CFG) =
+ * 200KHz * 1 / LRA drive frequency * ( 1 + %error/ 100)
+ *
+ * This can be rewritten as:
+ * LRA drive period code (RATE_CFG) =
+ * 200KHz * 1/LRA drive frequency *( 1 + %error * 10/1000)
+ *
+ * Since 200KHz * 1/LRA drive frequency is already calculated
+ * above we only do rest of the scaling here.
+ */
+ if (error_code >= 128)
+ LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent_x10);
else
- temp = (temp * (1000 + error_value)) / 1000;
+ LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent_x10);
}
- reg = temp & QPNP_HAP_RATE_CFG1_MASK;
+ dev_dbg(&hap->pdev->dev,
+ "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));
if (rc)
return rc;
- rc = qpnp_hap_read_reg(hap, &reg,
- QPNP_HAP_RATE_CFG2_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_RATE_CFG2_MASK;
- temp = temp >> QPNP_HAP_RATE_CFG2_SHFT;
- reg |= temp;
+ 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));
if (rc)
return rc;
- if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq)
+ if (hap->act_type == QPNP_HAP_LRA &&
+ hap->perform_lra_auto_resonance_search)
calculate_lra_code(hap);
+ if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) {
+ hap->drive_period_code_max_limit =
+ (hap->init_drive_period_code * 100) /
+ (100 - hap->drive_period_code_max_limit_percent_variation);
+ hap->drive_period_code_min_limit =
+ (hap->init_drive_period_code * 100) /
+ (100 + hap->drive_period_code_min_limit_percent_variation);
+ 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);
+ }
+
/* Configure BRAKE register */
rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_EN_CTL2_REG(hap->base));
if (rc < 0)
@@ -2031,13 +2180,44 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
return rc;
}
+ hap->perform_lra_auto_resonance_search =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,perform-lra-auto-resonance-search");
+
hap->correct_lra_drive_freq =
of_property_read_bool(pdev->dev.of_node,
"qcom,correct-lra-drive-freq");
+ hap->drive_period_code_max_limit_percent_variation = 25;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,drive-period-code-max-limit-percent-variation", &temp);
+ if (!rc)
+ hap->drive_period_code_max_limit_percent_variation =
+ (u8) temp;
+
+ hap->drive_period_code_min_limit_percent_variation = 25;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,drive-period-code-min-limit-percent-variation", &temp);
+ if (!rc)
+ hap->drive_period_code_min_limit_percent_variation =
+ (u8) temp;
+
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;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,time-required-to-generate-back-emf-us",
+ &temp);
+ if (!rc)
+ hap->time_required_to_generate_back_emf_us =
+ temp;
+ } else {
+ hap->time_required_to_generate_back_emf_us = 0;
+ }
}
rc = of_property_read_string(pdev->dev.of_node,
diff --git a/drivers/soc/qcom/qpnp-pbs.c b/drivers/soc/qcom/qpnp-pbs.c
new file mode 100644
index 000000000000..287c8a25b391
--- /dev/null
+++ b/drivers/soc/qcom/qpnp-pbs.c
@@ -0,0 +1,361 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "PBS: %s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/qpnp/qpnp-pbs.h>
+
+#define QPNP_PBS_DEV_NAME "qcom,qpnp-pbs"
+
+#define PBS_CLIENT_TRIG_CTL 0x42
+#define PBS_CLIENT_SW_TRIG_BIT BIT(7)
+#define PBS_CLIENT_SCRATCH1 0x50
+#define PBS_CLIENT_SCRATCH2 0x51
+
+static LIST_HEAD(pbs_dev_list);
+static DEFINE_MUTEX(pbs_list_lock);
+
+struct qpnp_pbs {
+ struct platform_device *pdev;
+ struct device *dev;
+ struct device_node *dev_node;
+ struct regmap *regmap;
+ struct mutex pbs_lock;
+ struct list_head link;
+
+ u32 base;
+};
+
+static int qpnp_pbs_read(struct qpnp_pbs *pbs, u32 address,
+ u8 *val, int count)
+{
+ int rc = 0;
+ struct platform_device *pdev = pbs->pdev;
+
+ rc = regmap_bulk_read(pbs->regmap, address, val, count);
+ if (rc)
+ pr_err("Failed to read address=0x%02x sid=0x%02x rc=%d\n",
+ address, to_spmi_device(pdev->dev.parent)->usid, rc);
+
+ return rc;
+}
+
+static int qpnp_pbs_write(struct qpnp_pbs *pbs, u16 address,
+ u8 *val, int count)
+{
+ int rc = 0;
+ struct platform_device *pdev = pbs->pdev;
+
+ rc = regmap_bulk_write(pbs->regmap, address, val, count);
+ if (rc < 0)
+ pr_err("Failed to write address =0x%02x sid=0x%02x rc=%d\n",
+ address, to_spmi_device(pdev->dev.parent)->usid, rc);
+ else
+ pr_debug("Wrote 0x%02X to addr 0x%04x\n", *val, address);
+
+ return rc;
+}
+
+static int qpnp_pbs_masked_write(struct qpnp_pbs *pbs, u16 address,
+ u8 mask, u8 val)
+{
+ int rc;
+
+ rc = regmap_update_bits(pbs->regmap, address, mask, val);
+ if (rc < 0)
+ pr_err("Failed to write address 0x%04X, rc = %d\n",
+ address, rc);
+ else
+ pr_debug("Wrote 0x%02X to addr 0x%04X\n",
+ val, address);
+
+ return rc;
+}
+
+static struct qpnp_pbs *get_pbs_client_node(struct device_node *dev_node)
+{
+ struct qpnp_pbs *pbs;
+
+ mutex_lock(&pbs_list_lock);
+ list_for_each_entry(pbs, &pbs_dev_list, link) {
+ if (dev_node == pbs->dev_node) {
+ mutex_unlock(&pbs_list_lock);
+ return pbs;
+ }
+ }
+
+ mutex_unlock(&pbs_list_lock);
+ return ERR_PTR(-EINVAL);
+}
+
+static int qpnp_pbs_wait_for_ack(struct qpnp_pbs *pbs, u8 bit_pos)
+{
+ int rc = 0;
+ u16 retries = 2000, dly = 1000;
+ u8 val;
+
+ while (retries--) {
+ rc = qpnp_pbs_read(pbs, pbs->base +
+ PBS_CLIENT_SCRATCH2, &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to read register %x rc = %d\n",
+ PBS_CLIENT_SCRATCH2, rc);
+ return rc;
+ }
+
+ if (val == 0xFF) {
+ /* PBS error - clear SCRATCH2 register */
+ rc = qpnp_pbs_write(pbs, pbs->base +
+ PBS_CLIENT_SCRATCH2, 0, 1);
+ if (rc < 0) {
+ pr_err("Failed to clear register %x rc=%d\n",
+ PBS_CLIENT_SCRATCH2, rc);
+ return rc;
+ }
+
+ pr_err("NACK from PBS for bit %d\n", bit_pos);
+ return -EINVAL;
+ }
+
+ if (val & BIT(bit_pos)) {
+ pr_debug("PBS sequence for bit %d executed!\n",
+ bit_pos);
+ break;
+ }
+
+ usleep_range(dly, dly + 100);
+ }
+
+ if (!retries) {
+ pr_err("Timeout for PBS ACK/NACK for bit %d\n", bit_pos);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+/**
+ * qpnp_pbs_trigger_event - Trigger the PBS RAM sequence
+ *
+ * Returns = 0 If the PBS RAM sequence executed successfully.
+ *
+ * Returns < 0 for errors.
+ *
+ * This function is used to trigger the PBS RAM sequence to be
+ * executed by the client driver.
+ *
+ * The PBS trigger sequence involves
+ * 1. setting the PBS sequence bit in PBS_CLIENT_SCRATCH1
+ * 2. Initiating the SW PBS trigger
+ * 3. Checking the equivalent bit in PBS_CLIENT_SCRATCH2 for the
+ * completion of the sequence.
+ * 4. If PBS_CLIENT_SCRATCH2 == 0xFF, the PBS sequence failed to execute
+ */
+int qpnp_pbs_trigger_event(struct device_node *dev_node, u8 bitmap)
+{
+ struct qpnp_pbs *pbs;
+ int rc = 0;
+ u16 bit_pos = 0;
+ u8 val, mask = 0;
+
+ if (!dev_node)
+ return -EINVAL;
+
+ if (!bitmap) {
+ pr_err("Invalid bitmap passed by client\n");
+ return -EINVAL;
+ }
+
+ pbs = get_pbs_client_node(dev_node);
+ if (IS_ERR_OR_NULL(pbs)) {
+ pr_err("Unable to find the PBS dev_node\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&pbs->pbs_lock);
+ rc = qpnp_pbs_read(pbs, pbs->base + PBS_CLIENT_SCRATCH2, &val, 1);
+ if (rc < 0) {
+ pr_err("read register %x failed rc = %d\n",
+ PBS_CLIENT_SCRATCH2, rc);
+ goto out;
+ }
+
+ if (val == 0xFF) {
+ /* PBS error - clear SCRATCH2 register */
+ rc = qpnp_pbs_write(pbs, pbs->base + PBS_CLIENT_SCRATCH2, 0, 1);
+ if (rc < 0) {
+ pr_err("Failed to clear register %x rc=%d\n",
+ PBS_CLIENT_SCRATCH2, rc);
+ goto out;
+ }
+ }
+
+ for (bit_pos = 0; bit_pos < 8; bit_pos++) {
+ if (bitmap & BIT(bit_pos)) {
+ /*
+ * Clear the PBS sequence bit position in
+ * PBS_CLIENT_SCRATCH2 mask register.
+ */
+ rc = qpnp_pbs_masked_write(pbs, pbs->base +
+ PBS_CLIENT_SCRATCH2, BIT(bit_pos), 0);
+ if (rc < 0) {
+ pr_err("Failed to clear %x reg bit rc=%d\n",
+ PBS_CLIENT_SCRATCH2, rc);
+ goto error;
+ }
+
+ /*
+ * Set the PBS sequence bit position in
+ * PBS_CLIENT_SCRATCH1 register.
+ */
+ val = mask = BIT(bit_pos);
+ rc = qpnp_pbs_masked_write(pbs, pbs->base +
+ PBS_CLIENT_SCRATCH1, mask, val);
+ if (rc < 0) {
+ pr_err("Failed to set %x reg bit rc=%d\n",
+ PBS_CLIENT_SCRATCH1, rc);
+ goto error;
+ }
+
+ /* Initiate the SW trigger */
+ val = mask = PBS_CLIENT_SW_TRIG_BIT;
+ rc = qpnp_pbs_masked_write(pbs, pbs->base +
+ PBS_CLIENT_TRIG_CTL, mask, val);
+ if (rc < 0) {
+ pr_err("Failed to write register %x rc=%d\n",
+ PBS_CLIENT_TRIG_CTL, rc);
+ goto error;
+ }
+
+ rc = qpnp_pbs_wait_for_ack(pbs, bit_pos);
+ if (rc < 0) {
+ pr_err("Error during wait_for_ack\n");
+ goto error;
+ }
+
+ /*
+ * Clear the PBS sequence bit position in
+ * PBS_CLIENT_SCRATCH1 register.
+ */
+ rc = qpnp_pbs_masked_write(pbs, pbs->base +
+ PBS_CLIENT_SCRATCH1, BIT(bit_pos), 0);
+ if (rc < 0) {
+ pr_err("Failed to clear %x reg bit rc=%d\n",
+ PBS_CLIENT_SCRATCH1, rc);
+ goto error;
+ }
+
+ /*
+ * Clear the PBS sequence bit position in
+ * PBS_CLIENT_SCRATCH2 mask register.
+ */
+ rc = qpnp_pbs_masked_write(pbs, pbs->base +
+ PBS_CLIENT_SCRATCH2, BIT(bit_pos), 0);
+ if (rc < 0) {
+ pr_err("Failed to clear %x reg bit rc=%d\n",
+ PBS_CLIENT_SCRATCH2, rc);
+ goto error;
+ }
+
+ }
+ }
+
+error:
+ /* Clear all the requested bitmap */
+ rc = qpnp_pbs_masked_write(pbs, pbs->base + PBS_CLIENT_SCRATCH1,
+ bitmap, 0);
+ if (rc < 0)
+ pr_err("Failed to clear %x reg bit rc=%d\n",
+ PBS_CLIENT_SCRATCH1, rc);
+out:
+ mutex_unlock(&pbs->pbs_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(qpnp_pbs_trigger_event);
+
+static int qpnp_pbs_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ u32 val = 0;
+ struct qpnp_pbs *pbs;
+
+ pbs = devm_kzalloc(&pdev->dev, sizeof(*pbs), GFP_KERNEL);
+ if (!pbs)
+ return -ENOMEM;
+
+ pbs->pdev = pdev;
+ pbs->dev = &pdev->dev;
+ pbs->dev_node = pdev->dev.of_node;
+ pbs->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!pbs->regmap) {
+ dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node, "reg", &val);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Couldn't find reg in node = %s rc = %d\n",
+ pdev->dev.of_node->full_name, rc);
+ return rc;
+ }
+
+ pbs->base = val;
+ mutex_init(&pbs->pbs_lock);
+
+ dev_set_drvdata(&pdev->dev, pbs);
+
+ mutex_lock(&pbs_list_lock);
+ list_add(&pbs->link, &pbs_dev_list);
+ mutex_unlock(&pbs_list_lock);
+
+ return 0;
+}
+
+static const struct of_device_id qpnp_pbs_match_table[] = {
+ { .compatible = QPNP_PBS_DEV_NAME },
+ {}
+};
+
+static struct platform_driver qpnp_pbs_driver = {
+ .driver = {
+ .name = QPNP_PBS_DEV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = qpnp_pbs_match_table,
+ },
+ .probe = qpnp_pbs_probe,
+};
+
+static int __init qpnp_pbs_init(void)
+{
+ return platform_driver_register(&qpnp_pbs_driver);
+}
+arch_initcall(qpnp_pbs_init);
+
+static void __exit qpnp_pbs_exit(void)
+{
+ return platform_driver_unregister(&qpnp_pbs_driver);
+}
+module_exit(qpnp_pbs_exit);
+
+MODULE_DESCRIPTION("QPNP PBS DRIVER");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" QPNP_PBS_DEV_NAME);
diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c
index 2b708732760f..8581ed587ead 100644
--- a/drivers/soc/qcom/service-locator.c
+++ b/drivers/soc/qcom/service-locator.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -111,6 +111,7 @@ static void service_locator_svc_arrive(struct work_struct *work)
qmi_handle_create(service_locator_clnt_notify, NULL);
if (!service_locator.clnt_handle) {
service_locator.clnt_handle = NULL;
+ complete_all(&service_locator.service_available);
mutex_unlock(&service_locator.service_mutex);
pr_err("Service locator QMI client handle alloc failed!\n");
return;
@@ -123,6 +124,7 @@ static void service_locator_svc_arrive(struct work_struct *work)
if (rc) {
qmi_handle_destroy(service_locator.clnt_handle);
service_locator.clnt_handle = NULL;
+ complete_all(&service_locator.service_available);
mutex_unlock(&service_locator.service_mutex);
pr_err("Unable to connnect to service rc:%d\n", rc);
return;
@@ -138,6 +140,7 @@ static void service_locator_svc_exit(struct work_struct *work)
mutex_lock(&service_locator.service_mutex);
qmi_handle_destroy(service_locator.clnt_handle);
service_locator.clnt_handle = NULL;
+ complete_all(&service_locator.service_available);
mutex_unlock(&service_locator.service_mutex);
pr_info("Connection with service locator lost\n");
}
diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c
index 85ff81ff475c..c1c65cd25558 100644
--- a/drivers/soc/qcom/service-notifier.c
+++ b/drivers/soc/qcom/service-notifier.c
@@ -635,7 +635,13 @@ static int send_pd_restart_req(const char *service_path,
return rc;
}
- /* Check the response */
+ /* Check response if PDR is disabled */
+ if (QMI_RESP_BIT_SHIFT(resp.resp.result) == QMI_ERR_DISABLED_V01) {
+ pr_err("PD restart is disabled 0x%x\n",
+ QMI_RESP_BIT_SHIFT(resp.resp.error));
+ return -EOPNOTSUPP;
+ }
+ /* Check the response for other error case*/
if (QMI_RESP_BIT_SHIFT(resp.resp.result) != QMI_RESULT_SUCCESS_V01) {
pr_err("QMI request for PD restart failed 0x%x\n",
QMI_RESP_BIT_SHIFT(resp.resp.error));
diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c
index 6a60e3624420..07610877f140 100644
--- a/drivers/soc/qcom/spcom.c
+++ b/drivers/soc/qcom/spcom.c
@@ -886,10 +886,11 @@ static int spcom_rx(struct spcom_channel *ch,
if (timeleft == 0) {
pr_err("rx_done timeout [%d] msec expired.\n", timeout_msec);
- goto exit_err;
+ mutex_unlock(&ch->lock);
+ return -ETIMEDOUT;
} else if (ch->rx_abort) {
- pr_err("rx aborted.\n");
- goto exit_err;
+ mutex_unlock(&ch->lock);
+ return -ERESTART; /* probably SSR */
} else if (ch->actual_rx_size) {
pr_debug("actual_rx_size is [%d].\n", ch->actual_rx_size);
} else {
@@ -1976,7 +1977,8 @@ static int spcom_handle_read_req_resp(struct spcom_channel *ch,
ret = spcom_rx(ch, rx_buf, rx_buf_size, timeout_msec);
if (ret < 0) {
pr_err("rx error %d.\n", ret);
- goto exit_err;
+ kfree(rx_buf);
+ return ret;
} else {
size = ret; /* actual_rx_size */
}
@@ -2269,8 +2271,14 @@ static ssize_t spcom_device_read(struct file *filp, char __user *user_buff,
if (buf == NULL)
return -ENOMEM;
- actual_size = spcom_handle_read(ch, buf, size);
- if ((actual_size <= 0) || (actual_size > size)) {
+ ret = spcom_handle_read(ch, buf, size);
+ if (ret < 0) {
+ pr_err("read error [%d].\n", ret);
+ kfree(buf);
+ return ret;
+ }
+ actual_size = ret;
+ if ((actual_size == 0) || (actual_size > size)) {
pr_err("invalid actual_size [%d].\n", actual_size);
kfree(buf);
return -EFAULT;
diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c
index b04b05a0904e..5548a31e1a39 100644
--- a/drivers/soc/qcom/spm.c
+++ b/drivers/soc/qcom/spm.c
@@ -116,7 +116,7 @@ static const struct spm_reg_data spm_reg_8064_cpu = {
static DEFINE_PER_CPU(struct spm_driver_data *, cpu_spm_drv);
-typedef int (*idle_fn)(int);
+typedef int (*idle_fn)(void);
static DEFINE_PER_CPU(idle_fn*, qcom_idle_ops);
static inline void spm_register_write(struct spm_driver_data *drv,
@@ -179,10 +179,10 @@ static int qcom_pm_collapse(unsigned long int unused)
return -1;
}
-static int qcom_cpu_spc(int cpu)
+static int qcom_cpu_spc(void)
{
int ret;
- struct spm_driver_data *drv = per_cpu(cpu_spm_drv, cpu);
+ struct spm_driver_data *drv = __this_cpu_read(cpu_spm_drv);
spm_set_low_power_mode(drv, PM_SLEEP_MODE_SPC);
ret = cpu_suspend(0, qcom_pm_collapse);
@@ -197,9 +197,9 @@ static int qcom_cpu_spc(int cpu)
return ret;
}
-static int qcom_idle_enter(int cpu, unsigned long index)
+static int qcom_idle_enter(unsigned long index)
{
- return per_cpu(qcom_idle_ops, cpu)[index](cpu);
+ return __this_cpu_read(qcom_idle_ops)[index]();
}
static const struct of_device_id qcom_idle_state_match[] __initconst = {
@@ -288,7 +288,7 @@ static struct spm_driver_data *spm_get_drv(struct platform_device *pdev,
struct spm_driver_data *drv = NULL;
struct device_node *cpu_node, *saw_node;
int cpu;
- bool found;
+ bool found = 0;
for_each_possible_cpu(cpu) {
cpu_node = of_cpu_device_node_get(cpu);
diff --git a/drivers/soc/qcom/sysmon-qmi.c b/drivers/soc/qcom/sysmon-qmi.c
index 7ef69b527ef8..1063b96d8d83 100644
--- a/drivers/soc/qcom/sysmon-qmi.c
+++ b/drivers/soc/qcom/sysmon-qmi.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -153,10 +153,12 @@ static void sysmon_clnt_svc_arrive(struct work_struct *work)
struct sysmon_qmi_data *data = container_of(work,
struct sysmon_qmi_data, svc_arrive);
+ mutex_lock(&sysmon_lock);
/* Create a Local client port for QMI communication */
data->clnt_handle = qmi_handle_create(sysmon_clnt_notify, work);
if (!data->clnt_handle) {
pr_err("QMI client handle alloc failed for %s\n", data->name);
+ mutex_unlock(&sysmon_lock);
return;
}
@@ -167,6 +169,7 @@ static void sysmon_clnt_svc_arrive(struct work_struct *work)
data->name);
qmi_handle_destroy(data->clnt_handle);
data->clnt_handle = NULL;
+ mutex_unlock(&sysmon_lock);
return;
}
pr_info("Connection established between QMI handle and %s's SSCTL service\n"
@@ -177,6 +180,7 @@ static void sysmon_clnt_svc_arrive(struct work_struct *work)
if (rc < 0)
pr_warn("%s: Could not register the indication callback\n",
data->name);
+ mutex_unlock(&sysmon_lock);
}
static void sysmon_clnt_svc_exit(struct work_struct *work)
@@ -184,8 +188,10 @@ static void sysmon_clnt_svc_exit(struct work_struct *work)
struct sysmon_qmi_data *data = container_of(work,
struct sysmon_qmi_data, svc_exit);
+ mutex_lock(&sysmon_lock);
qmi_handle_destroy(data->clnt_handle);
data->clnt_handle = NULL;
+ mutex_unlock(&sysmon_lock);
}
static void sysmon_clnt_recv_msg(struct work_struct *work)
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index 39412c9097c6..a3965cac1b34 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -753,7 +753,6 @@ static int dspi_remove(struct platform_device *pdev)
/* Disconnect from the SPI framework */
clk_disable_unprepare(dspi->clk);
spi_unregister_master(dspi->master);
- spi_master_put(dspi->master);
return 0;
}
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index a7934ab00b96..d22de4c8c399 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -263,6 +263,9 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
for (k = 0; k < ARRAY_SIZE(sh_msiof_spi_div_table); k++) {
brps = DIV_ROUND_UP(div, sh_msiof_spi_div_table[k].div);
+ /* SCR_BRDV_DIV_1 is valid only if BRPS is x 1/1 or x 1/2 */
+ if (sh_msiof_spi_div_table[k].div == 1 && brps > 2)
+ continue;
if (brps <= 32) /* max of brdv is 32 */
break;
}
diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c b/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c
index 99c6584fcfa5..97246bcbcd62 100644
--- a/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c
+++ b/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c
@@ -197,6 +197,6 @@ void fiq_debugger_dump_stacktrace(struct fiq_debugger_output *output,
frame.sp = regs->sp;
frame.pc = regs->pc;
output->printf(output, "\n");
- walk_stackframe(&frame, report_trace, &sts);
+ walk_stackframe(current, &frame, report_trace, &sts);
}
}
diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c
index b1e45161eefc..18c2b6daf588 100644
--- a/drivers/staging/fbtft/fbtft-core.c
+++ b/drivers/staging/fbtft/fbtft-core.c
@@ -392,11 +392,11 @@ static void fbtft_update_display(struct fbtft_par *par, unsigned start_line,
if (unlikely(timeit)) {
ts_end = ktime_get();
- if (ktime_to_ns(par->update_time))
+ if (!ktime_to_ns(par->update_time))
par->update_time = ts_start;
- par->update_time = ts_start;
fps = ktime_us_delta(ts_start, par->update_time);
+ par->update_time = ts_start;
fps = fps ? 1000000 / fps : 0;
throughput = ktime_us_delta(ts_end, ts_start);
diff --git a/drivers/staging/goldfish/Kconfig b/drivers/staging/goldfish/Kconfig
index 4e094602437c..c579141a7bed 100644
--- a/drivers/staging/goldfish/Kconfig
+++ b/drivers/staging/goldfish/Kconfig
@@ -4,6 +4,12 @@ config GOLDFISH_AUDIO
---help---
Emulated audio channel for the Goldfish Android Virtual Device
+config GOLDFISH_SYNC
+ tristate "Goldfish AVD Sync Driver"
+ depends on GOLDFISH
+ ---help---
+ Emulated sync fences for the Goldfish Android Virtual Device
+
config MTD_GOLDFISH_NAND
tristate "Goldfish NAND device"
depends on GOLDFISH
diff --git a/drivers/staging/goldfish/Makefile b/drivers/staging/goldfish/Makefile
index dec34ad58162..0cf525588210 100644
--- a/drivers/staging/goldfish/Makefile
+++ b/drivers/staging/goldfish/Makefile
@@ -4,3 +4,8 @@
obj-$(CONFIG_GOLDFISH_AUDIO) += goldfish_audio.o
obj-$(CONFIG_MTD_GOLDFISH_NAND) += goldfish_nand.o
+
+# and sync
+
+ccflags-y := -Idrivers/staging/android
+obj-$(CONFIG_GOLDFISH_SYNC) += goldfish_sync.o
diff --git a/drivers/staging/goldfish/goldfish_audio.c b/drivers/staging/goldfish/goldfish_audio.c
index b0927e49d0a8..63b79c09b41b 100644
--- a/drivers/staging/goldfish/goldfish_audio.c
+++ b/drivers/staging/goldfish/goldfish_audio.c
@@ -26,7 +26,9 @@
#include <linux/sched.h>
#include <linux/dma-mapping.h>
#include <linux/uaccess.h>
+#include <linux/slab.h>
#include <linux/goldfish.h>
+#include <linux/acpi.h>
MODULE_AUTHOR("Google, Inc.");
MODULE_DESCRIPTION("Android QEMU Audio Driver");
@@ -115,6 +117,7 @@ static ssize_t goldfish_audio_read(struct file *fp, char __user *buf,
size_t count, loff_t *pos)
{
struct goldfish_audio *data = fp->private_data;
+ unsigned long irq_flags;
int length;
int result = 0;
@@ -128,6 +131,10 @@ static ssize_t goldfish_audio_read(struct file *fp, char __user *buf,
wait_event_interruptible(data->wait, data->buffer_status &
AUDIO_INT_READ_BUFFER_FULL);
+ spin_lock_irqsave(&data->lock, irq_flags);
+ data->buffer_status &= ~AUDIO_INT_READ_BUFFER_FULL;
+ spin_unlock_irqrestore(&data->lock, irq_flags);
+
length = AUDIO_READ(data, AUDIO_READ_BUFFER_AVAILABLE);
/* copy data to user space */
@@ -344,11 +351,25 @@ static int goldfish_audio_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id goldfish_audio_of_match[] = {
+ { .compatible = "google,goldfish-audio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_audio_of_match);
+
+static const struct acpi_device_id goldfish_audio_acpi_match[] = {
+ { "GFSH0005", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_audio_acpi_match);
+
static struct platform_driver goldfish_audio_driver = {
.probe = goldfish_audio_probe,
.remove = goldfish_audio_remove,
.driver = {
- .name = "goldfish_audio"
+ .name = "goldfish_audio",
+ .of_match_table = goldfish_audio_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_audio_acpi_match),
}
};
diff --git a/drivers/staging/goldfish/goldfish_sync.c b/drivers/staging/goldfish/goldfish_sync.c
new file mode 100644
index 000000000000..ba8def29901e
--- /dev/null
+++ b/drivers/staging/goldfish/goldfish_sync.c
@@ -0,0 +1,987 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/fdtable.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <linux/interrupt.h>
+#include <linux/kref.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/acpi.h>
+
+#include <linux/string.h>
+#include <linux/syscalls.h>
+
+#include "sw_sync.h"
+#include "sync.h"
+
+#define ERR(...) printk(KERN_ERR __VA_ARGS__);
+
+#define INFO(...) printk(KERN_INFO __VA_ARGS__);
+
+#define DPRINT(...) pr_debug(__VA_ARGS__);
+
+#define DTRACE() DPRINT("%s: enter", __func__)
+
+/* The Goldfish sync driver is designed to provide a interface
+ * between the underlying host's sync device and the kernel's
+ * sw_sync.
+ * The purpose of the device/driver is to enable lightweight
+ * creation and signaling of timelines and fences
+ * in order to synchronize the guest with host-side graphics events.
+ *
+ * Each time the interrupt trips, the driver
+ * may perform a sw_sync operation.
+ */
+
+/* The operations are: */
+
+/* Ready signal - used to mark when irq should lower */
+#define CMD_SYNC_READY 0
+
+/* Create a new timeline. writes timeline handle */
+#define CMD_CREATE_SYNC_TIMELINE 1
+
+/* Create a fence object. reads timeline handle and time argument.
+ * Writes fence fd to the SYNC_REG_HANDLE register. */
+#define CMD_CREATE_SYNC_FENCE 2
+
+/* Increments timeline. reads timeline handle and time argument */
+#define CMD_SYNC_TIMELINE_INC 3
+
+/* Destroys a timeline. reads timeline handle */
+#define CMD_DESTROY_SYNC_TIMELINE 4
+
+/* Starts a wait on the host with
+ * the given glsync object and sync thread handle. */
+#define CMD_TRIGGER_HOST_WAIT 5
+
+/* The register layout is: */
+
+#define SYNC_REG_BATCH_COMMAND 0x00 /* host->guest batch commands */
+#define SYNC_REG_BATCH_GUESTCOMMAND 0x04 /* guest->host batch commands */
+#define SYNC_REG_BATCH_COMMAND_ADDR 0x08 /* communicate physical address of host->guest batch commands */
+#define SYNC_REG_BATCH_COMMAND_ADDR_HIGH 0x0c /* 64-bit part */
+#define SYNC_REG_BATCH_GUESTCOMMAND_ADDR 0x10 /* communicate physical address of guest->host commands */
+#define SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH 0x14 /* 64-bit part */
+#define SYNC_REG_INIT 0x18 /* signals that the device has been probed */
+
+/* There is an ioctl associated with goldfish sync driver.
+ * Make it conflict with ioctls that are not likely to be used
+ * in the emulator.
+ *
+ * '@' 00-0F linux/radeonfb.h conflict!
+ * '@' 00-0F drivers/video/aty/aty128fb.c conflict!
+ */
+#define GOLDFISH_SYNC_IOC_MAGIC '@'
+
+#define GOLDFISH_SYNC_IOC_QUEUE_WORK _IOWR(GOLDFISH_SYNC_IOC_MAGIC, 0, struct goldfish_sync_ioctl_info)
+
+/* The above definitions (command codes, register layout, ioctl definitions)
+ * need to be in sync with the following files:
+ *
+ * Host-side (emulator):
+ * external/qemu/android/emulation/goldfish_sync.h
+ * external/qemu-android/hw/misc/goldfish_sync.c
+ *
+ * Guest-side (system image):
+ * device/generic/goldfish-opengl/system/egl/goldfish_sync.h
+ * device/generic/goldfish/ueventd.ranchu.rc
+ * platform/build/target/board/generic/sepolicy/file_contexts
+ */
+struct goldfish_sync_hostcmd {
+ /* sorted for alignment */
+ uint64_t handle;
+ uint64_t hostcmd_handle;
+ uint32_t cmd;
+ uint32_t time_arg;
+};
+
+struct goldfish_sync_guestcmd {
+ uint64_t host_command; /* uint64_t for alignment */
+ uint64_t glsync_handle;
+ uint64_t thread_handle;
+ uint64_t guest_timeline_handle;
+};
+
+#define GOLDFISH_SYNC_MAX_CMDS 64
+
+struct goldfish_sync_state {
+ char __iomem *reg_base;
+ int irq;
+
+ /* Spinlock protects |to_do| / |to_do_end|. */
+ spinlock_t lock;
+ /* |mutex_lock| protects all concurrent access
+ * to timelines for both kernel and user space. */
+ struct mutex mutex_lock;
+
+ /* Buffer holding commands issued from host. */
+ struct goldfish_sync_hostcmd to_do[GOLDFISH_SYNC_MAX_CMDS];
+ uint32_t to_do_end;
+
+ /* Addresses for the reading or writing
+ * of individual commands. The host can directly write
+ * to |batch_hostcmd| (and then this driver immediately
+ * copies contents to |to_do|). This driver either replies
+ * through |batch_hostcmd| or simply issues a
+ * guest->host command through |batch_guestcmd|.
+ */
+ struct goldfish_sync_hostcmd *batch_hostcmd;
+ struct goldfish_sync_guestcmd *batch_guestcmd;
+
+ /* Used to give this struct itself to a work queue
+ * function for executing actual sync commands. */
+ struct work_struct work_item;
+};
+
+static struct goldfish_sync_state global_sync_state[1];
+
+struct goldfish_sync_timeline_obj {
+ struct sw_sync_timeline *sw_sync_tl;
+ uint32_t current_time;
+ /* We need to be careful about when we deallocate
+ * this |goldfish_sync_timeline_obj| struct.
+ * In order to ensure proper cleanup, we need to
+ * consider the triggered host-side wait that may
+ * still be in flight when the guest close()'s a
+ * goldfish_sync device's sync context fd (and
+ * destroys the |sw_sync_tl| field above).
+ * The host-side wait may raise IRQ
+ * and tell the kernel to increment the timeline _after_
+ * the |sw_sync_tl| has already been set to null.
+ *
+ * From observations on OpenGL apps and CTS tests, this
+ * happens at some very low probability upon context
+ * destruction or process close, but it does happen
+ * and it needs to be handled properly. Otherwise,
+ * if we clean up the surrounding |goldfish_sync_timeline_obj|
+ * too early, any |handle| field of any host->guest command
+ * might not even point to a null |sw_sync_tl| field,
+ * but to garbage memory or even a reclaimed |sw_sync_tl|.
+ * If we do not count such "pending waits" and kfree the object
+ * immediately upon |goldfish_sync_timeline_destroy|,
+ * we might get mysterous RCU stalls after running a long
+ * time because the garbage memory that is being read
+ * happens to be interpretable as a |spinlock_t| struct
+ * that is currently in the locked state.
+ *
+ * To track when to free the |goldfish_sync_timeline_obj|
+ * itself, we maintain a kref.
+ * The kref essentially counts the timeline itself plus
+ * the number of waits in flight. kref_init/kref_put
+ * are issued on
+ * |goldfish_sync_timeline_create|/|goldfish_sync_timeline_destroy|
+ * and kref_get/kref_put are issued on
+ * |goldfish_sync_fence_create|/|goldfish_sync_timeline_inc|.
+ *
+ * The timeline is destroyed after reference count
+ * reaches zero, which would happen after
+ * |goldfish_sync_timeline_destroy| and all pending
+ * |goldfish_sync_timeline_inc|'s are fulfilled.
+ *
+ * NOTE (1): We assume that |fence_create| and
+ * |timeline_inc| calls are 1:1, otherwise the kref scheme
+ * will not work. This is a valid assumption as long
+ * as the host-side virtual device implementation
+ * does not insert any timeline increments
+ * that we did not trigger from here.
+ *
+ * NOTE (2): The use of kref by itself requires no locks,
+ * but this does not mean everything works without locks.
+ * Related timeline operations do require a lock of some sort,
+ * or at least are not proven to work without it.
+ * In particualr, we assume that all the operations
+ * done on the |kref| field above are done in contexts where
+ * |global_sync_state->mutex_lock| is held. Do not
+ * remove that lock until everything is proven to work
+ * without it!!! */
+ struct kref kref;
+};
+
+/* We will call |delete_timeline_obj| when the last reference count
+ * of the kref is decremented. This deletes the sw_sync
+ * timeline object along with the wrapper itself. */
+static void delete_timeline_obj(struct kref* kref) {
+ struct goldfish_sync_timeline_obj* obj =
+ container_of(kref, struct goldfish_sync_timeline_obj, kref);
+
+ sync_timeline_destroy(&obj->sw_sync_tl->obj);
+ obj->sw_sync_tl = NULL;
+ kfree(obj);
+}
+
+static uint64_t gensym_ctr;
+static void gensym(char *dst)
+{
+ sprintf(dst, "goldfish_sync:gensym:%llu", gensym_ctr);
+ gensym_ctr++;
+}
+
+/* |goldfish_sync_timeline_create| assumes that |global_sync_state->mutex_lock|
+ * is held. */
+static struct goldfish_sync_timeline_obj*
+goldfish_sync_timeline_create(void)
+{
+
+ char timeline_name[256];
+ struct sw_sync_timeline *res_sync_tl = NULL;
+ struct goldfish_sync_timeline_obj *res;
+
+ DTRACE();
+
+ gensym(timeline_name);
+
+ res_sync_tl = sw_sync_timeline_create(timeline_name);
+ if (!res_sync_tl) {
+ ERR("Failed to create sw_sync timeline.");
+ return NULL;
+ }
+
+ res = kzalloc(sizeof(struct goldfish_sync_timeline_obj), GFP_KERNEL);
+ res->sw_sync_tl = res_sync_tl;
+ res->current_time = 0;
+ kref_init(&res->kref);
+
+ DPRINT("new timeline_obj=0x%p", res);
+ return res;
+}
+
+/* |goldfish_sync_fence_create| assumes that |global_sync_state->mutex_lock|
+ * is held. */
+static int
+goldfish_sync_fence_create(struct goldfish_sync_timeline_obj *obj,
+ uint32_t val)
+{
+
+ int fd;
+ char fence_name[256];
+ struct sync_pt *syncpt = NULL;
+ struct sync_fence *sync_obj = NULL;
+ struct sw_sync_timeline *tl;
+
+ DTRACE();
+
+ if (!obj) return -1;
+
+ tl = obj->sw_sync_tl;
+
+ syncpt = sw_sync_pt_create(tl, val);
+ if (!syncpt) {
+ ERR("could not create sync point! "
+ "sync_timeline=0x%p val=%d",
+ tl, val);
+ return -1;
+ }
+
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0) {
+ ERR("could not get unused fd for sync fence. "
+ "errno=%d", fd);
+ goto err_cleanup_pt;
+ }
+
+ gensym(fence_name);
+
+ sync_obj = sync_fence_create(fence_name, syncpt);
+ if (!sync_obj) {
+ ERR("could not create sync fence! "
+ "sync_timeline=0x%p val=%d sync_pt=0x%p",
+ tl, val, syncpt);
+ goto err_cleanup_fd_pt;
+ }
+
+ DPRINT("installing sync fence into fd %d sync_obj=0x%p", fd, sync_obj);
+ sync_fence_install(sync_obj, fd);
+ kref_get(&obj->kref);
+
+ return fd;
+
+err_cleanup_fd_pt:
+ put_unused_fd(fd);
+err_cleanup_pt:
+ sync_pt_free(syncpt);
+ return -1;
+}
+
+/* |goldfish_sync_timeline_inc| assumes that |global_sync_state->mutex_lock|
+ * is held. */
+static void
+goldfish_sync_timeline_inc(struct goldfish_sync_timeline_obj *obj, uint32_t inc)
+{
+ DTRACE();
+ /* Just give up if someone else nuked the timeline.
+ * Whoever it was won't care that it doesn't get signaled. */
+ if (!obj) return;
+
+ DPRINT("timeline_obj=0x%p", obj);
+ sw_sync_timeline_inc(obj->sw_sync_tl, inc);
+ DPRINT("incremented timeline. increment max_time");
+ obj->current_time += inc;
+
+ /* Here, we will end up deleting the timeline object if it
+ * turns out that this call was a pending increment after
+ * |goldfish_sync_timeline_destroy| was called. */
+ kref_put(&obj->kref, delete_timeline_obj);
+ DPRINT("done");
+}
+
+/* |goldfish_sync_timeline_destroy| assumes
+ * that |global_sync_state->mutex_lock| is held. */
+static void
+goldfish_sync_timeline_destroy(struct goldfish_sync_timeline_obj *obj)
+{
+ DTRACE();
+ /* See description of |goldfish_sync_timeline_obj| for why we
+ * should not immediately destroy |obj| */
+ kref_put(&obj->kref, delete_timeline_obj);
+}
+
+static inline void
+goldfish_sync_cmd_queue(struct goldfish_sync_state *sync_state,
+ uint32_t cmd,
+ uint64_t handle,
+ uint32_t time_arg,
+ uint64_t hostcmd_handle)
+{
+ struct goldfish_sync_hostcmd *to_add;
+
+ DTRACE();
+
+ BUG_ON(sync_state->to_do_end == GOLDFISH_SYNC_MAX_CMDS);
+
+ to_add = &sync_state->to_do[sync_state->to_do_end];
+
+ to_add->cmd = cmd;
+ to_add->handle = handle;
+ to_add->time_arg = time_arg;
+ to_add->hostcmd_handle = hostcmd_handle;
+
+ sync_state->to_do_end += 1;
+}
+
+static inline void
+goldfish_sync_hostcmd_reply(struct goldfish_sync_state *sync_state,
+ uint32_t cmd,
+ uint64_t handle,
+ uint32_t time_arg,
+ uint64_t hostcmd_handle)
+{
+ unsigned long irq_flags;
+ struct goldfish_sync_hostcmd *batch_hostcmd =
+ sync_state->batch_hostcmd;
+
+ DTRACE();
+
+ spin_lock_irqsave(&sync_state->lock, irq_flags);
+
+ batch_hostcmd->cmd = cmd;
+ batch_hostcmd->handle = handle;
+ batch_hostcmd->time_arg = time_arg;
+ batch_hostcmd->hostcmd_handle = hostcmd_handle;
+ writel(0, sync_state->reg_base + SYNC_REG_BATCH_COMMAND);
+
+ spin_unlock_irqrestore(&sync_state->lock, irq_flags);
+}
+
+static inline void
+goldfish_sync_send_guestcmd(struct goldfish_sync_state *sync_state,
+ uint32_t cmd,
+ uint64_t glsync_handle,
+ uint64_t thread_handle,
+ uint64_t timeline_handle)
+{
+ unsigned long irq_flags;
+ struct goldfish_sync_guestcmd *batch_guestcmd =
+ sync_state->batch_guestcmd;
+
+ DTRACE();
+
+ spin_lock_irqsave(&sync_state->lock, irq_flags);
+
+ batch_guestcmd->host_command = (uint64_t)cmd;
+ batch_guestcmd->glsync_handle = (uint64_t)glsync_handle;
+ batch_guestcmd->thread_handle = (uint64_t)thread_handle;
+ batch_guestcmd->guest_timeline_handle = (uint64_t)timeline_handle;
+ writel(0, sync_state->reg_base + SYNC_REG_BATCH_GUESTCOMMAND);
+
+ spin_unlock_irqrestore(&sync_state->lock, irq_flags);
+}
+
+/* |goldfish_sync_interrupt| handles IRQ raises from the virtual device.
+ * In the context of OpenGL, this interrupt will fire whenever we need
+ * to signal a fence fd in the guest, with the command
+ * |CMD_SYNC_TIMELINE_INC|.
+ * However, because this function will be called in an interrupt context,
+ * it is necessary to do the actual work of signaling off of interrupt context.
+ * The shared work queue is used for this purpose. At the end when
+ * all pending commands are intercepted by the interrupt handler,
+ * we call |schedule_work|, which will later run the actual
+ * desired sync command in |goldfish_sync_work_item_fn|.
+ */
+static irqreturn_t goldfish_sync_interrupt(int irq, void *dev_id)
+{
+
+ struct goldfish_sync_state *sync_state = dev_id;
+
+ uint32_t nextcmd;
+ uint32_t command_r;
+ uint64_t handle_rw;
+ uint32_t time_r;
+ uint64_t hostcmd_handle_rw;
+
+ int count = 0;
+
+ DTRACE();
+
+ sync_state = dev_id;
+
+ spin_lock(&sync_state->lock);
+
+ for (;;) {
+
+ readl(sync_state->reg_base + SYNC_REG_BATCH_COMMAND);
+ nextcmd = sync_state->batch_hostcmd->cmd;
+
+ if (nextcmd == 0)
+ break;
+
+ command_r = nextcmd;
+ handle_rw = sync_state->batch_hostcmd->handle;
+ time_r = sync_state->batch_hostcmd->time_arg;
+ hostcmd_handle_rw = sync_state->batch_hostcmd->hostcmd_handle;
+
+ goldfish_sync_cmd_queue(
+ sync_state,
+ command_r,
+ handle_rw,
+ time_r,
+ hostcmd_handle_rw);
+
+ count++;
+ }
+
+ spin_unlock(&sync_state->lock);
+
+ schedule_work(&sync_state->work_item);
+
+ return (count == 0) ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/* |goldfish_sync_work_item_fn| does the actual work of servicing
+ * host->guest sync commands. This function is triggered whenever
+ * the IRQ for the goldfish sync device is raised. Once it starts
+ * running, it grabs the contents of the buffer containing the
+ * commands it needs to execute (there may be multiple, because
+ * our IRQ is active high and not edge triggered), and then
+ * runs all of them one after the other.
+ */
+static void goldfish_sync_work_item_fn(struct work_struct *input)
+{
+
+ struct goldfish_sync_state *sync_state;
+ int sync_fence_fd;
+
+ struct goldfish_sync_timeline_obj *timeline;
+ uint64_t timeline_ptr;
+
+ uint64_t hostcmd_handle;
+
+ uint32_t cmd;
+ uint64_t handle;
+ uint32_t time_arg;
+
+ struct goldfish_sync_hostcmd *todo;
+ uint32_t todo_end;
+
+ unsigned long irq_flags;
+
+ struct goldfish_sync_hostcmd to_run[GOLDFISH_SYNC_MAX_CMDS];
+ uint32_t i = 0;
+
+ sync_state = container_of(input, struct goldfish_sync_state, work_item);
+
+ mutex_lock(&sync_state->mutex_lock);
+
+ spin_lock_irqsave(&sync_state->lock, irq_flags); {
+
+ todo_end = sync_state->to_do_end;
+
+ DPRINT("num sync todos: %u", sync_state->to_do_end);
+
+ for (i = 0; i < todo_end; i++)
+ to_run[i] = sync_state->to_do[i];
+
+ /* We expect that commands will come in at a slow enough rate
+ * so that incoming items will not be more than
+ * GOLDFISH_SYNC_MAX_CMDS.
+ *
+ * This is because the way the sync device is used,
+ * it's only for managing buffer data transfers per frame,
+ * with a sequential dependency between putting things in
+ * to_do and taking them out. Once a set of commands is
+ * queued up in to_do, the user of the device waits for
+ * them to be processed before queuing additional commands,
+ * which limits the rate at which commands come in
+ * to the rate at which we take them out here.
+ *
+ * We also don't expect more than MAX_CMDS to be issued
+ * at once; there is a correspondence between
+ * which buffers need swapping to the (display / buffer queue)
+ * to particular commands, and we don't expect there to be
+ * enough display or buffer queues in operation at once
+ * to overrun GOLDFISH_SYNC_MAX_CMDS.
+ */
+ sync_state->to_do_end = 0;
+
+ } spin_unlock_irqrestore(&sync_state->lock, irq_flags);
+
+ for (i = 0; i < todo_end; i++) {
+ DPRINT("todo index: %u", i);
+
+ todo = &to_run[i];
+
+ cmd = todo->cmd;
+
+ handle = (uint64_t)todo->handle;
+ time_arg = todo->time_arg;
+ hostcmd_handle = (uint64_t)todo->hostcmd_handle;
+
+ DTRACE();
+
+ timeline = (struct goldfish_sync_timeline_obj *)(uintptr_t)handle;
+
+ switch (cmd) {
+ case CMD_SYNC_READY:
+ break;
+ case CMD_CREATE_SYNC_TIMELINE:
+ DPRINT("exec CMD_CREATE_SYNC_TIMELINE: "
+ "handle=0x%llx time_arg=%d",
+ handle, time_arg);
+ timeline = goldfish_sync_timeline_create();
+ timeline_ptr = (uintptr_t)timeline;
+ goldfish_sync_hostcmd_reply(sync_state, CMD_CREATE_SYNC_TIMELINE,
+ timeline_ptr,
+ 0,
+ hostcmd_handle);
+ DPRINT("sync timeline created: %p", timeline);
+ break;
+ case CMD_CREATE_SYNC_FENCE:
+ DPRINT("exec CMD_CREATE_SYNC_FENCE: "
+ "handle=0x%llx time_arg=%d",
+ handle, time_arg);
+ sync_fence_fd = goldfish_sync_fence_create(timeline, time_arg);
+ goldfish_sync_hostcmd_reply(sync_state, CMD_CREATE_SYNC_FENCE,
+ sync_fence_fd,
+ 0,
+ hostcmd_handle);
+ break;
+ case CMD_SYNC_TIMELINE_INC:
+ DPRINT("exec CMD_SYNC_TIMELINE_INC: "
+ "handle=0x%llx time_arg=%d",
+ handle, time_arg);
+ goldfish_sync_timeline_inc(timeline, time_arg);
+ break;
+ case CMD_DESTROY_SYNC_TIMELINE:
+ DPRINT("exec CMD_DESTROY_SYNC_TIMELINE: "
+ "handle=0x%llx time_arg=%d",
+ handle, time_arg);
+ goldfish_sync_timeline_destroy(timeline);
+ break;
+ }
+ DPRINT("Done executing sync command");
+ }
+ mutex_unlock(&sync_state->mutex_lock);
+}
+
+/* Guest-side interface: file operations */
+
+/* Goldfish sync context and ioctl info.
+ *
+ * When a sync context is created by open()-ing the goldfish sync device, we
+ * create a sync context (|goldfish_sync_context|).
+ *
+ * Currently, the only data required to track is the sync timeline itself
+ * along with the current time, which are all packed up in the
+ * |goldfish_sync_timeline_obj| field. We use a |goldfish_sync_context|
+ * as the filp->private_data.
+ *
+ * Next, when a sync context user requests that work be queued and a fence
+ * fd provided, we use the |goldfish_sync_ioctl_info| struct, which holds
+ * information about which host handles to touch for this particular
+ * queue-work operation. We need to know about the host-side sync thread
+ * and the particular host-side GLsync object. We also possibly write out
+ * a file descriptor.
+ */
+struct goldfish_sync_context {
+ struct goldfish_sync_timeline_obj *timeline;
+};
+
+struct goldfish_sync_ioctl_info {
+ uint64_t host_glsync_handle_in;
+ uint64_t host_syncthread_handle_in;
+ int fence_fd_out;
+};
+
+static int goldfish_sync_open(struct inode *inode, struct file *file)
+{
+
+ struct goldfish_sync_context *sync_context;
+
+ DTRACE();
+
+ mutex_lock(&global_sync_state->mutex_lock);
+
+ sync_context = kzalloc(sizeof(struct goldfish_sync_context), GFP_KERNEL);
+
+ if (sync_context == NULL) {
+ ERR("Creation of goldfish sync context failed!");
+ mutex_unlock(&global_sync_state->mutex_lock);
+ return -ENOMEM;
+ }
+
+ sync_context->timeline = NULL;
+
+ file->private_data = sync_context;
+
+ DPRINT("successfully create a sync context @0x%p", sync_context);
+
+ mutex_unlock(&global_sync_state->mutex_lock);
+
+ return 0;
+}
+
+static int goldfish_sync_release(struct inode *inode, struct file *file)
+{
+
+ struct goldfish_sync_context *sync_context;
+
+ DTRACE();
+
+ mutex_lock(&global_sync_state->mutex_lock);
+
+ sync_context = file->private_data;
+
+ if (sync_context->timeline)
+ goldfish_sync_timeline_destroy(sync_context->timeline);
+
+ sync_context->timeline = NULL;
+
+ kfree(sync_context);
+
+ mutex_unlock(&global_sync_state->mutex_lock);
+
+ return 0;
+}
+
+/* |goldfish_sync_ioctl| is the guest-facing interface of goldfish sync
+ * and is used in conjunction with eglCreateSyncKHR to queue up the
+ * actual work of waiting for the EGL sync command to complete,
+ * possibly returning a fence fd to the guest.
+ */
+static long goldfish_sync_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct goldfish_sync_context *sync_context_data;
+ struct goldfish_sync_timeline_obj *timeline;
+ int fd_out;
+ struct goldfish_sync_ioctl_info ioctl_data;
+
+ DTRACE();
+
+ sync_context_data = file->private_data;
+ fd_out = -1;
+
+ switch (cmd) {
+ case GOLDFISH_SYNC_IOC_QUEUE_WORK:
+
+ DPRINT("exec GOLDFISH_SYNC_IOC_QUEUE_WORK");
+
+ mutex_lock(&global_sync_state->mutex_lock);
+
+ if (copy_from_user(&ioctl_data,
+ (void __user *)arg,
+ sizeof(ioctl_data))) {
+ ERR("Failed to copy memory for ioctl_data from user.");
+ mutex_unlock(&global_sync_state->mutex_lock);
+ return -EFAULT;
+ }
+
+ if (ioctl_data.host_syncthread_handle_in == 0) {
+ DPRINT("Error: zero host syncthread handle!!!");
+ mutex_unlock(&global_sync_state->mutex_lock);
+ return -EFAULT;
+ }
+
+ if (!sync_context_data->timeline) {
+ DPRINT("no timeline yet, create one.");
+ sync_context_data->timeline = goldfish_sync_timeline_create();
+ DPRINT("timeline: 0x%p", &sync_context_data->timeline);
+ }
+
+ timeline = sync_context_data->timeline;
+ fd_out = goldfish_sync_fence_create(timeline,
+ timeline->current_time + 1);
+ DPRINT("Created fence with fd %d and current time %u (timeline: 0x%p)",
+ fd_out,
+ sync_context_data->timeline->current_time + 1,
+ sync_context_data->timeline);
+
+ ioctl_data.fence_fd_out = fd_out;
+
+ if (copy_to_user((void __user *)arg,
+ &ioctl_data,
+ sizeof(ioctl_data))) {
+ DPRINT("Error, could not copy to user!!!");
+
+ sys_close(fd_out);
+ /* We won't be doing an increment, kref_put immediately. */
+ kref_put(&timeline->kref, delete_timeline_obj);
+ mutex_unlock(&global_sync_state->mutex_lock);
+ return -EFAULT;
+ }
+
+ /* We are now about to trigger a host-side wait;
+ * accumulate on |pending_waits|. */
+ goldfish_sync_send_guestcmd(global_sync_state,
+ CMD_TRIGGER_HOST_WAIT,
+ ioctl_data.host_glsync_handle_in,
+ ioctl_data.host_syncthread_handle_in,
+ (uint64_t)(uintptr_t)(sync_context_data->timeline));
+
+ mutex_unlock(&global_sync_state->mutex_lock);
+ return 0;
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations goldfish_sync_fops = {
+ .owner = THIS_MODULE,
+ .open = goldfish_sync_open,
+ .release = goldfish_sync_release,
+ .unlocked_ioctl = goldfish_sync_ioctl,
+ .compat_ioctl = goldfish_sync_ioctl,
+};
+
+static struct miscdevice goldfish_sync_device = {
+ .name = "goldfish_sync",
+ .fops = &goldfish_sync_fops,
+};
+
+
+static bool setup_verify_batch_cmd_addr(struct goldfish_sync_state *sync_state,
+ void *batch_addr,
+ uint32_t addr_offset,
+ uint32_t addr_offset_high)
+{
+ uint64_t batch_addr_phys;
+ uint32_t batch_addr_phys_test_lo;
+ uint32_t batch_addr_phys_test_hi;
+
+ if (!batch_addr) {
+ ERR("Could not use batch command address!");
+ return false;
+ }
+
+ batch_addr_phys = virt_to_phys(batch_addr);
+ writel((uint32_t)(batch_addr_phys),
+ sync_state->reg_base + addr_offset);
+ writel((uint32_t)(batch_addr_phys >> 32),
+ sync_state->reg_base + addr_offset_high);
+
+ batch_addr_phys_test_lo =
+ readl(sync_state->reg_base + addr_offset);
+ batch_addr_phys_test_hi =
+ readl(sync_state->reg_base + addr_offset_high);
+
+ if (virt_to_phys(batch_addr) !=
+ (((uint64_t)batch_addr_phys_test_hi << 32) |
+ batch_addr_phys_test_lo)) {
+ ERR("Invalid batch command address!");
+ return false;
+ }
+
+ return true;
+}
+
+int goldfish_sync_probe(struct platform_device *pdev)
+{
+ struct resource *ioresource;
+ struct goldfish_sync_state *sync_state = global_sync_state;
+ int status;
+
+ DTRACE();
+
+ sync_state->to_do_end = 0;
+
+ spin_lock_init(&sync_state->lock);
+ mutex_init(&sync_state->mutex_lock);
+
+ platform_set_drvdata(pdev, sync_state);
+
+ ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (ioresource == NULL) {
+ ERR("platform_get_resource failed");
+ return -ENODEV;
+ }
+
+ sync_state->reg_base = devm_ioremap(&pdev->dev, ioresource->start, PAGE_SIZE);
+ if (sync_state->reg_base == NULL) {
+ ERR("Could not ioremap");
+ return -ENOMEM;
+ }
+
+ sync_state->irq = platform_get_irq(pdev, 0);
+ if (sync_state->irq < 0) {
+ ERR("Could not platform_get_irq");
+ return -ENODEV;
+ }
+
+ status = devm_request_irq(&pdev->dev,
+ sync_state->irq,
+ goldfish_sync_interrupt,
+ IRQF_SHARED,
+ pdev->name,
+ sync_state);
+ if (status) {
+ ERR("request_irq failed");
+ return -ENODEV;
+ }
+
+ INIT_WORK(&sync_state->work_item,
+ goldfish_sync_work_item_fn);
+
+ misc_register(&goldfish_sync_device);
+
+ /* Obtain addresses for batch send/recv of commands. */
+ {
+ struct goldfish_sync_hostcmd *batch_addr_hostcmd;
+ struct goldfish_sync_guestcmd *batch_addr_guestcmd;
+
+ batch_addr_hostcmd = devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_hostcmd),
+ GFP_KERNEL);
+ batch_addr_guestcmd = devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_guestcmd),
+ GFP_KERNEL);
+
+ if (!setup_verify_batch_cmd_addr(sync_state,
+ batch_addr_hostcmd,
+ SYNC_REG_BATCH_COMMAND_ADDR,
+ SYNC_REG_BATCH_COMMAND_ADDR_HIGH)) {
+ ERR("goldfish_sync: Could not setup batch command address");
+ return -ENODEV;
+ }
+
+ if (!setup_verify_batch_cmd_addr(sync_state,
+ batch_addr_guestcmd,
+ SYNC_REG_BATCH_GUESTCOMMAND_ADDR,
+ SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH)) {
+ ERR("goldfish_sync: Could not setup batch guest command address");
+ return -ENODEV;
+ }
+
+ sync_state->batch_hostcmd = batch_addr_hostcmd;
+ sync_state->batch_guestcmd = batch_addr_guestcmd;
+ }
+
+ INFO("goldfish_sync: Initialized goldfish sync device");
+
+ writel(0, sync_state->reg_base + SYNC_REG_INIT);
+
+ return 0;
+}
+
+static int goldfish_sync_remove(struct platform_device *pdev)
+{
+ struct goldfish_sync_state *sync_state = global_sync_state;
+
+ DTRACE();
+
+ misc_deregister(&goldfish_sync_device);
+ memset(sync_state, 0, sizeof(struct goldfish_sync_state));
+ return 0;
+}
+
+static const struct of_device_id goldfish_sync_of_match[] = {
+ { .compatible = "google,goldfish-sync", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_sync_of_match);
+
+static const struct acpi_device_id goldfish_sync_acpi_match[] = {
+ { "GFSH0006", 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(acpi, goldfish_sync_acpi_match);
+
+static struct platform_driver goldfish_sync = {
+ .probe = goldfish_sync_probe,
+ .remove = goldfish_sync_remove,
+ .driver = {
+ .name = "goldfish_sync",
+ .of_match_table = goldfish_sync_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_sync_acpi_match),
+ }
+};
+
+module_platform_driver(goldfish_sync);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_DESCRIPTION("Android QEMU Sync Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+/* This function is only to run a basic test of sync framework.
+ * It creates a timeline and fence object whose signal point is at 1.
+ * The timeline is incremented, and we use the sync framework's
+ * sync_fence_wait on that fence object. If everything works out,
+ * we should not hang in the wait and return immediately.
+ * There is no way to explicitly run this test yet, but it
+ * can be used by inserting it at the end of goldfish_sync_probe.
+ */
+void test_kernel_sync(void)
+{
+ struct goldfish_sync_timeline_obj *test_timeline;
+ int test_fence_fd;
+
+ DTRACE();
+
+ DPRINT("test sw_sync");
+
+ test_timeline = goldfish_sync_timeline_create();
+ DPRINT("sw_sync_timeline_create -> 0x%p", test_timeline);
+
+ test_fence_fd = goldfish_sync_fence_create(test_timeline, 1);
+ DPRINT("sync_fence_create -> %d", test_fence_fd);
+
+ DPRINT("incrementing test timeline");
+ goldfish_sync_timeline_inc(test_timeline, 1);
+
+ DPRINT("test waiting (should NOT hang)");
+ sync_fence_wait(
+ sync_fence_fdget(test_fence_fd), -1);
+
+ DPRINT("test waiting (afterward)");
+}
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
index bb40f3728742..20314ff08be0 100644
--- a/drivers/staging/iio/adc/ad7192.c
+++ b/drivers/staging/iio/adc/ad7192.c
@@ -236,7 +236,7 @@ static int ad7192_setup(struct ad7192_state *st,
st->mclk = pdata->ext_clk_hz;
else
st->mclk = AD7192_INT_FREQ_MHZ;
- break;
+ break;
default:
ret = -EINVAL;
goto out;
diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c
index 10c43dda0f5a..196da09e20a1 100644
--- a/drivers/staging/iio/impedance-analyzer/ad5933.c
+++ b/drivers/staging/iio/impedance-analyzer/ad5933.c
@@ -647,6 +647,7 @@ static void ad5933_work(struct work_struct *work)
__be16 buf[2];
int val[2];
unsigned char status;
+ int ret;
mutex_lock(&indio_dev->mlock);
if (st->state == AD5933_CTRL_INIT_START_FREQ) {
@@ -654,19 +655,22 @@ static void ad5933_work(struct work_struct *work)
ad5933_cmd(st, AD5933_CTRL_START_SWEEP);
st->state = AD5933_CTRL_START_SWEEP;
schedule_delayed_work(&st->work, st->poll_time_jiffies);
- mutex_unlock(&indio_dev->mlock);
- return;
+ goto out;
}
- ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &status);
+ ret = ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &status);
+ if (ret)
+ goto out;
if (status & AD5933_STAT_DATA_VALID) {
int scan_count = bitmap_weight(indio_dev->active_scan_mask,
indio_dev->masklength);
- ad5933_i2c_read(st->client,
+ ret = ad5933_i2c_read(st->client,
test_bit(1, indio_dev->active_scan_mask) ?
AD5933_REG_REAL_DATA : AD5933_REG_IMAG_DATA,
scan_count * 2, (u8 *)buf);
+ if (ret)
+ goto out;
if (scan_count == 2) {
val[0] = be16_to_cpu(buf[0]);
@@ -678,8 +682,7 @@ static void ad5933_work(struct work_struct *work)
} else {
/* no data available - try again later */
schedule_delayed_work(&st->work, st->poll_time_jiffies);
- mutex_unlock(&indio_dev->mlock);
- return;
+ goto out;
}
if (status & AD5933_STAT_SWEEP_DONE) {
@@ -691,7 +694,7 @@ static void ad5933_work(struct work_struct *work)
ad5933_cmd(st, AD5933_CTRL_INC_FREQ);
schedule_delayed_work(&st->work, st->poll_time_jiffies);
}
-
+out:
mutex_unlock(&indio_dev->mlock);
}
diff --git a/drivers/staging/nvec/nvec_ps2.c b/drivers/staging/nvec/nvec_ps2.c
index 0922dd3a08d3..196f6b0a288f 100644
--- a/drivers/staging/nvec/nvec_ps2.c
+++ b/drivers/staging/nvec/nvec_ps2.c
@@ -106,13 +106,12 @@ static int nvec_mouse_probe(struct platform_device *pdev)
{
struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
struct serio *ser_dev;
- char mouse_reset[] = { NVEC_PS2, SEND_COMMAND, PSMOUSE_RST, 3 };
- ser_dev = devm_kzalloc(&pdev->dev, sizeof(struct serio), GFP_KERNEL);
+ ser_dev = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!ser_dev)
return -ENOMEM;
- ser_dev->id.type = SERIO_PS_PSTHRU;
+ ser_dev->id.type = SERIO_8042;
ser_dev->write = ps2_sendcommand;
ser_dev->start = ps2_startstreaming;
ser_dev->stop = ps2_stopstreaming;
@@ -127,9 +126,6 @@ static int nvec_mouse_probe(struct platform_device *pdev)
serio_register_port(ser_dev);
- /* mouse reset */
- nvec_write_async(nvec, mouse_reset, sizeof(mouse_reset));
-
return 0;
}
diff --git a/drivers/staging/rtl8188eu/core/rtw_cmd.c b/drivers/staging/rtl8188eu/core/rtw_cmd.c
index 9b7026e7d55b..45d0a87f55d2 100644
--- a/drivers/staging/rtl8188eu/core/rtw_cmd.c
+++ b/drivers/staging/rtl8188eu/core/rtw_cmd.c
@@ -718,13 +718,13 @@ u8 rtw_addbareq_cmd(struct adapter *padapter, u8 tid, u8 *addr)
u8 res = _SUCCESS;
- ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
if (ph2c == NULL) {
res = _FAIL;
goto exit;
}
- paddbareq_parm = kzalloc(sizeof(struct addBaReq_parm), GFP_KERNEL);
+ paddbareq_parm = kzalloc(sizeof(struct addBaReq_parm), GFP_ATOMIC);
if (paddbareq_parm == NULL) {
kfree(ph2c);
res = _FAIL;
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 7bc3778a1ac9..2a67af4e2e13 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1680,6 +1680,7 @@ void transport_generic_request_failure(struct se_cmd *cmd,
case TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED:
case TCM_LOGICAL_BLOCK_APP_TAG_CHECK_FAILED:
case TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED:
+ case TCM_COPY_TARGET_DEVICE_NOT_REACHABLE:
break;
case TCM_OUT_OF_RESOURCES:
sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
@@ -2509,8 +2510,10 @@ int target_get_sess_cmd(struct se_cmd *se_cmd, bool ack_kref)
* fabric acknowledgement that requires two target_put_sess_cmd()
* invocations before se_cmd descriptor release.
*/
- if (ack_kref)
+ if (ack_kref) {
kref_get(&se_cmd->cmd_kref);
+ se_cmd->se_cmd_flags |= SCF_ACK_KREF;
+ }
spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
if (se_sess->sess_tearing_down) {
@@ -2833,6 +2836,12 @@ static const struct sense_info sense_info_table[] = {
.ascq = 0x03, /* LOGICAL BLOCK REFERENCE TAG CHECK FAILED */
.add_sector_info = true,
},
+ [TCM_COPY_TARGET_DEVICE_NOT_REACHABLE] = {
+ .key = COPY_ABORTED,
+ .asc = 0x0d,
+ .ascq = 0x02, /* COPY TARGET DEVICE NOT REACHABLE */
+
+ },
[TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE] = {
/*
* Returning ILLEGAL REQUEST would cause immediate IO errors on
diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c
index 47fe94ee10b8..153a6f255b6d 100644
--- a/drivers/target/target_core_xcopy.c
+++ b/drivers/target/target_core_xcopy.c
@@ -104,7 +104,7 @@ static int target_xcopy_locate_se_dev_e4(struct se_cmd *se_cmd, struct xcopy_op
}
mutex_unlock(&g_device_mutex);
- pr_err("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n");
+ pr_debug_ratelimited("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n");
return -EINVAL;
}
@@ -185,7 +185,7 @@ static int target_xcopy_parse_tiddesc_e4(struct se_cmd *se_cmd, struct xcopy_op
static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd,
struct xcopy_op *xop, unsigned char *p,
- unsigned short tdll)
+ unsigned short tdll, sense_reason_t *sense_ret)
{
struct se_device *local_dev = se_cmd->se_dev;
unsigned char *desc = p;
@@ -193,6 +193,8 @@ static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd,
unsigned short start = 0;
bool src = true;
+ *sense_ret = TCM_INVALID_PARAMETER_LIST;
+
if (offset != 0) {
pr_err("XCOPY target descriptor list length is not"
" multiple of %d\n", XCOPY_TARGET_DESC_LEN);
@@ -243,9 +245,16 @@ static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd,
rc = target_xcopy_locate_se_dev_e4(se_cmd, xop, true);
else
rc = target_xcopy_locate_se_dev_e4(se_cmd, xop, false);
-
- if (rc < 0)
+ /*
+ * If a matching IEEE NAA 0x83 descriptor for the requested device
+ * is not located on this node, return COPY_ABORTED with ASQ/ASQC
+ * 0x0d/0x02 - COPY_TARGET_DEVICE_NOT_REACHABLE to request the
+ * initiator to fall back to normal copy method.
+ */
+ if (rc < 0) {
+ *sense_ret = TCM_COPY_TARGET_DEVICE_NOT_REACHABLE;
goto out;
+ }
pr_debug("XCOPY TGT desc: Source dev: %p NAA IEEE WWN: 0x%16phN\n",
xop->src_dev, &xop->src_tid_wwn[0]);
@@ -653,6 +662,7 @@ static int target_xcopy_read_source(
rc = target_xcopy_setup_pt_cmd(xpt_cmd, xop, src_dev, &cdb[0],
remote_port, true);
if (rc < 0) {
+ ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status;
transport_generic_free_cmd(se_cmd, 0);
return rc;
}
@@ -664,6 +674,7 @@ static int target_xcopy_read_source(
rc = target_xcopy_issue_pt_cmd(xpt_cmd);
if (rc < 0) {
+ ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status;
transport_generic_free_cmd(se_cmd, 0);
return rc;
}
@@ -714,6 +725,7 @@ static int target_xcopy_write_destination(
remote_port, false);
if (rc < 0) {
struct se_cmd *src_cmd = &xop->src_pt_cmd->se_cmd;
+ ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status;
/*
* If the failure happened before the t_mem_list hand-off in
* target_xcopy_setup_pt_cmd(), Reset memory + clear flag so that
@@ -729,6 +741,7 @@ static int target_xcopy_write_destination(
rc = target_xcopy_issue_pt_cmd(xpt_cmd);
if (rc < 0) {
+ ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status;
se_cmd->se_cmd_flags &= ~SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC;
transport_generic_free_cmd(se_cmd, 0);
return rc;
@@ -815,9 +828,14 @@ static void target_xcopy_do_work(struct work_struct *work)
out:
xcopy_pt_undepend_remotedev(xop);
kfree(xop);
-
- pr_warn("target_xcopy_do_work: Setting X-COPY CHECK_CONDITION -> sending response\n");
- ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION;
+ /*
+ * Don't override an error scsi status if it has already been set
+ */
+ if (ec_cmd->scsi_status == SAM_STAT_GOOD) {
+ pr_warn_ratelimited("target_xcopy_do_work: rc: %d, Setting X-COPY"
+ " CHECK_CONDITION -> sending response\n", rc);
+ ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION;
+ }
target_complete_cmd(ec_cmd, SAM_STAT_CHECK_CONDITION);
}
@@ -875,7 +893,7 @@ sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
" tdll: %hu sdll: %u inline_dl: %u\n", list_id, list_id_usage,
tdll, sdll, inline_dl);
- rc = target_xcopy_parse_target_descriptors(se_cmd, xop, &p[16], tdll);
+ rc = target_xcopy_parse_target_descriptors(se_cmd, xop, &p[16], tdll, &ret);
if (rc <= 0)
goto out;
diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c
index 0f82c0b146f6..1e332855b933 100644
--- a/drivers/tty/goldfish.c
+++ b/drivers/tty/goldfish.c
@@ -68,8 +68,7 @@ static void goldfish_tty_do_write(int line, const char *buf, unsigned count)
static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id)
{
- struct platform_device *pdev = dev_id;
- struct goldfish_tty *qtty = &goldfish_ttys[pdev->id];
+ struct goldfish_tty *qtty = dev_id;
void __iomem *base = qtty->base;
unsigned long irq_flags;
unsigned char *buf;
@@ -233,6 +232,7 @@ static int goldfish_tty_probe(struct platform_device *pdev)
struct device *ttydev;
void __iomem *base;
u32 irq;
+ unsigned int line;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (r == NULL)
@@ -248,10 +248,16 @@ static int goldfish_tty_probe(struct platform_device *pdev)
irq = r->start;
- if (pdev->id >= goldfish_tty_line_count)
- goto err_unmap;
-
mutex_lock(&goldfish_tty_lock);
+
+ if (pdev->id == PLATFORM_DEVID_NONE)
+ line = goldfish_tty_current_line_count;
+ else
+ line = pdev->id;
+
+ if (line >= goldfish_tty_line_count)
+ goto err_create_driver_failed;
+
if (goldfish_tty_current_line_count == 0) {
ret = goldfish_tty_create_driver();
if (ret)
@@ -259,7 +265,7 @@ static int goldfish_tty_probe(struct platform_device *pdev)
}
goldfish_tty_current_line_count++;
- qtty = &goldfish_ttys[pdev->id];
+ qtty = &goldfish_ttys[line];
spin_lock_init(&qtty->lock);
tty_port_init(&qtty->port);
qtty->port.ops = &goldfish_port_ops;
@@ -269,13 +275,13 @@ static int goldfish_tty_probe(struct platform_device *pdev)
writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_CMD);
ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED,
- "goldfish_tty", pdev);
+ "goldfish_tty", qtty);
if (ret)
goto err_request_irq_failed;
ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver,
- pdev->id, &pdev->dev);
+ line, &pdev->dev);
if (IS_ERR(ttydev)) {
ret = PTR_ERR(ttydev);
goto err_tty_register_device_failed;
@@ -286,8 +292,9 @@ static int goldfish_tty_probe(struct platform_device *pdev)
qtty->console.device = goldfish_tty_console_device;
qtty->console.setup = goldfish_tty_console_setup;
qtty->console.flags = CON_PRINTBUFFER;
- qtty->console.index = pdev->id;
+ qtty->console.index = line;
register_console(&qtty->console);
+ platform_set_drvdata(pdev, qtty);
mutex_unlock(&goldfish_tty_lock);
return 0;
@@ -307,13 +314,12 @@ err_unmap:
static int goldfish_tty_remove(struct platform_device *pdev)
{
- struct goldfish_tty *qtty;
+ struct goldfish_tty *qtty = platform_get_drvdata(pdev);
mutex_lock(&goldfish_tty_lock);
- qtty = &goldfish_ttys[pdev->id];
unregister_console(&qtty->console);
- tty_unregister_device(goldfish_tty_driver, pdev->id);
+ tty_unregister_device(goldfish_tty_driver, qtty->console.index);
iounmap(qtty->base);
qtty->base = NULL;
free_irq(qtty->irq, pdev);
@@ -324,11 +330,19 @@ static int goldfish_tty_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id goldfish_tty_of_match[] = {
+ { .compatible = "google,goldfish-tty", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, goldfish_tty_of_match);
+
static struct platform_driver goldfish_tty_platform_driver = {
.probe = goldfish_tty_probe,
.remove = goldfish_tty_remove,
.driver = {
- .name = "goldfish_tty"
+ .name = "goldfish_tty",
+ .of_match_table = goldfish_tty_of_match,
}
};
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index a5d319e4aae6..8435c3f204c1 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -440,7 +440,7 @@ static int dw8250_probe(struct platform_device *pdev)
}
data->pclk = devm_clk_get(&pdev->dev, "apb_pclk");
- if (IS_ERR(data->clk) && PTR_ERR(data->clk) == -EPROBE_DEFER) {
+ if (IS_ERR(data->pclk) && PTR_ERR(data->pclk) == -EPROBE_DEFER) {
err = -EPROBE_DEFER;
goto err_clk;
}
diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c
index ed489880e62b..83b3988eb6b2 100644
--- a/drivers/tty/serial/8250/8250_mid.c
+++ b/drivers/tty/serial/8250/8250_mid.c
@@ -149,6 +149,9 @@ static void mid8250_set_termios(struct uart_port *p,
unsigned long w = BIT(24) - 1;
unsigned long mul, div;
+ /* Gracefully handle the B0 case: fall back to B9600 */
+ fuart = fuart ? fuart : 9600 * 16;
+
if (mid->board->freq < fuart) {
/* Find prescaler value that satisfies Fuart < Fref */
if (mid->board->freq > baud)
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index c1d4a8fa9be8..029de3f99752 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -1952,6 +1952,43 @@ pci_wch_ch38x_setup(struct serial_private *priv,
#define PCI_DEVICE_ID_PERICOM_PI7C9X7954 0x7954
#define PCI_DEVICE_ID_PERICOM_PI7C9X7958 0x7958
+#define PCI_VENDOR_ID_ACCESIO 0x494f
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SDB 0x1051
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2S 0x1053
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB 0x105C
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4S 0x105E
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_2DB 0x1091
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_2 0x1093
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB 0x1099
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_4 0x109B
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SMDB 0x10D1
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2SM 0x10D3
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB 0x10DA
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4SM 0x10DC
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_1 0x1108
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_2 0x1110
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_2 0x1111
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4 0x1118
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_4 0x1119
+#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2S 0x1152
+#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S 0x115A
+#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_2 0x1190
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_2 0x1191
+#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4 0x1198
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_4 0x1199
+#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2SM 0x11D0
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4 0x105A
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4 0x105B
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM422_8 0x106A
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM485_8 0x106B
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4 0x1098
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_8 0x10A9
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM 0x10D9
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_8SM 0x10E9
+#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM 0x11D8
+
+
+
/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1588 0x1588
@@ -5120,6 +5157,108 @@ static struct pci_device_id serial_pci_tbl[] = {
0,
0, pbn_pericom_PI7C9X7958 },
/*
+ * ACCES I/O Products quad
+ */
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SDB,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2S,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4S,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_2DB,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SMDB,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2SM,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4SM,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_1,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2S,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2SM,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM422_8,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM485_8,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_8,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_8SM,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ /*
* Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke)
*/
{ PCI_VENDOR_ID_TOPIC, PCI_DEVICE_ID_TOPIC_TP560,
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 7b5462eb8388..e0b89b961e1b 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -2075,6 +2075,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state,
static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
unsigned long flags;
unsigned int old_mode, mode, imr, quot, baud;
@@ -2178,11 +2179,29 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
mode |= ATMEL_US_USMODE_RS485;
} else if (termios->c_cflag & CRTSCTS) {
/* RS232 with hardware handshake (RTS/CTS) */
- if (atmel_use_dma_rx(port) && !atmel_use_fifo(port)) {
- dev_info(port->dev, "not enabling hardware flow control because DMA is used");
- termios->c_cflag &= ~CRTSCTS;
- } else {
+ if (atmel_use_fifo(port) &&
+ !mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS)) {
+ /*
+ * with ATMEL_US_USMODE_HWHS set, the controller will
+ * be able to drive the RTS pin high/low when the RX
+ * FIFO is above RXFTHRES/below RXFTHRES2.
+ * It will also disable the transmitter when the CTS
+ * pin is high.
+ * This mode is not activated if CTS pin is a GPIO
+ * because in this case, the transmitter is always
+ * disabled (there must be an internal pull-up
+ * responsible for this behaviour).
+ * If the RTS pin is a GPIO, the controller won't be
+ * able to drive it according to the FIFO thresholds,
+ * but it will be handled by the driver.
+ */
mode |= ATMEL_US_USMODE_HWHS;
+ } else {
+ /*
+ * For platforms without FIFO, the flow control is
+ * handled by the driver.
+ */
+ mode |= ATMEL_US_USMODE_NORMAL;
}
} else {
/* RS232 without hadware handshake */
diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c
index ca0d3802f2af..4e603d060e80 100644
--- a/drivers/tty/serial/sunhv.c
+++ b/drivers/tty/serial/sunhv.c
@@ -490,12 +490,6 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig
locked = spin_trylock_irqsave(&port->lock, flags);
else
spin_lock_irqsave(&port->lock, flags);
- if (port->sysrq) {
- locked = 0;
- } else if (oops_in_progress) {
- locked = spin_trylock(&port->lock);
- } else
- spin_lock(&port->lock);
for (i = 0; i < n; i++) {
if (*s == '\n')
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 136ebaaa9cc0..5ab54ef4f304 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -872,10 +872,15 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
if (new_cols == vc->vc_cols && new_rows == vc->vc_rows)
return 0;
+ if (new_screen_size > (4 << 20))
+ return -EINVAL;
newscreen = kmalloc(new_screen_size, GFP_USER);
if (!newscreen)
return -ENOMEM;
+ if (vc == sel_cons)
+ clear_selection();
+
old_rows = vc->vc_rows;
old_row_size = vc->vc_size_row;
@@ -1173,7 +1178,7 @@ static void csi_J(struct vc_data *vc, int vpar)
break;
case 3: /* erase scroll-back buffer (and whole display) */
scr_memsetw(vc->vc_screenbuf, vc->vc_video_erase_char,
- vc->vc_screenbuf_size >> 1);
+ vc->vc_screenbuf_size);
set_origin(vc);
if (CON_IS_VISIBLE(vc))
update_screen(vc);
diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c
index 915facbf552e..e1134a4d97f3 100644
--- a/drivers/uio/uio_dmem_genirq.c
+++ b/drivers/uio/uio_dmem_genirq.c
@@ -229,7 +229,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
++uiomem;
}
- priv->dmem_region_start = i;
+ priv->dmem_region_start = uiomem - &uioinfo->mem[0];
priv->num_dmem_regions = pdata->num_dynamic_regions;
for (i = 0; i < pdata->num_dynamic_regions; ++i) {
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 965d0e240dcb..ba4a2a1eb3ff 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -926,6 +926,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
if (!ci)
return -ENOMEM;
+ spin_lock_init(&ci->lock);
ci->dev = dev;
ci->platdata = dev_get_platdata(dev);
ci->imx28_write_fix = !!(ci->platdata->flags &
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index ca367b05e440..d8a045fc1fdb 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -939,6 +939,15 @@ static int isr_setup_status_phase(struct ci_hdrc *ci)
int retval;
struct ci_hw_ep *hwep;
+ /*
+ * Unexpected USB controller behavior, caused by bad signal integrity
+ * or ground reference problems, can lead to isr_setup_status_phase
+ * being called with ci->status equal to NULL.
+ * If this situation occurs, you should review your USB hardware design.
+ */
+ if (WARN_ON_ONCE(!ci->status))
+ return -EPIPE;
+
hwep = (ci->ep0_dir == TX) ? ci->ep0out : ci->ep0in;
ci->status->context = ci;
ci->status->complete = isr_setup_status_complete;
@@ -1875,8 +1884,6 @@ static int udc_start(struct ci_hdrc *ci)
struct usb_otg_caps *otg_caps = &ci->platdata->ci_otg_caps;
int retval = 0;
- spin_lock_init(&ci->lock);
-
ci->gadget.ops = &usb_gadget_ops;
ci->gadget.speed = USB_SPEED_UNKNOWN;
ci->gadget.max_speed = USB_SPEED_HIGH;
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 7f374369e539..4d77745f439f 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -877,8 +877,6 @@ static int wait_serial_change(struct acm *acm, unsigned long arg)
DECLARE_WAITQUEUE(wait, current);
struct async_icount old, new;
- if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD ))
- return -EINVAL;
do {
spin_lock_irq(&acm->read_lock);
old = acm->oldcount;
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 7a11a8263171..deaddb950c20 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -121,6 +121,7 @@ static void usbtmc_delete(struct kref *kref)
struct usbtmc_device_data *data = to_usbtmc_data(kref);
usb_put_dev(data->usb_dev);
+ kfree(data);
}
static int usbtmc_open(struct inode *inode, struct file *filp)
@@ -1104,7 +1105,7 @@ static int usbtmc_probe(struct usb_interface *intf,
dev_dbg(&intf->dev, "%s called\n", __func__);
- data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 80c8d90d8b75..ff44cfa26af8 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -211,8 +211,10 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
memcpy(&endpoint->desc, d, n);
INIT_LIST_HEAD(&endpoint->urb_list);
- /* Fix up bInterval values outside the legal range. Use 32 ms if no
- * proper value can be guessed. */
+ /*
+ * Fix up bInterval values outside the legal range.
+ * Use 10 or 8 ms if no proper value can be guessed.
+ */
i = 0; /* i = min, j = max, n = default */
j = 255;
if (usb_endpoint_xfer_int(d)) {
@@ -221,13 +223,15 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
case USB_SPEED_SUPER_PLUS:
case USB_SPEED_SUPER:
case USB_SPEED_HIGH:
- /* Many device manufacturers are using full-speed
+ /*
+ * Many device manufacturers are using full-speed
* bInterval values in high-speed interrupt endpoint
- * descriptors. Try to fix those and fall back to a
- * 32 ms default value otherwise. */
+ * descriptors. Try to fix those and fall back to an
+ * 8-ms default value otherwise.
+ */
n = fls(d->bInterval*8);
if (n == 0)
- n = 9; /* 32 ms = 2^(9-1) uframes */
+ n = 7; /* 8 ms = 2^(7-1) uframes */
j = 16;
/*
@@ -242,10 +246,12 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
}
break;
default: /* USB_SPEED_FULL or _LOW */
- /* For low-speed, 10 ms is the official minimum.
+ /*
+ * For low-speed, 10 ms is the official minimum.
* But some "overclocked" devices might want faster
- * polling so we'll allow it. */
- n = 32;
+ * polling so we'll allow it.
+ */
+ n = 10;
break;
}
} else if (usb_endpoint_xfer_isoc(d)) {
@@ -253,10 +259,10 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
j = 16;
switch (to_usb_device(ddev)->speed) {
case USB_SPEED_HIGH:
- n = 9; /* 32 ms = 2^(9-1) uframes */
+ n = 7; /* 8 ms = 2^(7-1) uframes */
break;
default: /* USB_SPEED_FULL */
- n = 6; /* 32 ms = 2^(6-1) frames */
+ n = 4; /* 8 ms = 2^(4-1) frames */
break;
}
}
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 068b03a35bd5..20ac60d6b6a8 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -978,22 +978,30 @@ void dbg_print_reg(const char *name, int reg)
static ssize_t dwc3_store_events(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
- unsigned tty;
+ int ret;
+ u8 tty;
if (buf == NULL) {
pr_err("[%s] EINVAL\n", __func__);
- goto done;
+ ret = -EINVAL;
+ return ret;
}
- if (sscanf(buf, "%u", &tty) != 1 || tty > 1) {
+ ret = kstrtou8_from_user(buf, count, 0, &tty);
+ if (ret < 0) {
+ pr_err("can't get enter value.\n");
+ return ret;
+ }
+
+ if (tty > 1) {
pr_err("<1|0>: enable|disable console log\n");
- goto done;
+ ret = -EINVAL;
+ return ret;
}
dbg_dwc3_data.tty = tty;
pr_info("tty = %u", dbg_dwc3_data.tty);
- done:
return count;
}
@@ -1034,21 +1042,30 @@ const struct file_operations dwc3_gadget_dbg_data_fops = {
static ssize_t dwc3_store_int_events(struct file *file,
const char __user *ubuf, size_t count, loff_t *ppos)
{
- int clear_stats, i;
+ int i, ret;
unsigned long flags;
struct seq_file *s = file->private_data;
struct dwc3 *dwc = s->private;
struct dwc3_ep *dep;
struct timespec ts;
+ u8 clear_stats;
if (ubuf == NULL) {
pr_err("[%s] EINVAL\n", __func__);
- goto done;
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = kstrtou8_from_user(ubuf, count, 0, &clear_stats);
+ if (ret < 0) {
+ pr_err("can't get enter value.\n");
+ return ret;
}
- if (sscanf(ubuf, "%u", &clear_stats) != 1 || clear_stats != 0) {
+ if (clear_stats != 0) {
pr_err("Wrong value. To clear stats, enter value as 0.\n");
- goto done;
+ ret = -EINVAL;
+ return ret;
}
spin_lock_irqsave(&dwc->lock, flags);
@@ -1065,7 +1082,6 @@ static ssize_t dwc3_store_int_events(struct file *file,
spin_unlock_irqrestore(&dwc->lock, flags);
-done:
return count;
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 88350e61f3bd..7a279db521ca 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -3582,7 +3582,7 @@ err3:
kfree(dwc->setup_buf);
err2:
- dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);
err1:
@@ -3611,7 +3611,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
kfree(dwc->setup_buf);
- dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);
dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index a53b23789d7a..9622514e3df9 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1942,7 +1942,9 @@ unknown:
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
- composite_setup_complete(gadget->ep0, req);
+ if (value != -ESHUTDOWN)
+ composite_setup_complete(gadget->ep0,
+ req);
}
return value;
}
@@ -2031,7 +2033,8 @@ try_fun_setup:
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
- composite_setup_complete(gadget->ep0, req);
+ if (value != -ESHUTDOWN)
+ composite_setup_complete(gadget->ep0, req);
}
} else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) {
WARN(cdev,
@@ -2461,6 +2464,11 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev)
spin_lock_irqsave(&cdev->lock, flags);
if (cdev->delayed_status == 0) {
+ if (!cdev->config) {
+ spin_unlock_irqrestore(&cdev->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&cdev->lock, flags);
WARN(cdev, "%s: Unexpected call\n", __func__);
} else if (--cdev->delayed_status == 0) {
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 4964bb1a24b1..ed0ff7b1fc15 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -426,11 +426,6 @@ static int config_usb_cfg_link(
}
f = usb_get_function(fi);
- if (f == NULL) {
- /* Are we trying to symlink PTP without MTP function? */
- ret = -EINVAL; /* Invalid Configuration */
- goto out;
- }
if (IS_ERR(f)) {
ret = PTR_ERR(f);
goto out;
diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c
index 61057befc136..cd096fb9078f 100644
--- a/drivers/usb/gadget/function/f_accessory.c
+++ b/drivers/usb/gadget/function/f_accessory.c
@@ -255,6 +255,7 @@ static inline struct acc_dev *func_to_dev(struct usb_function *f)
static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size)
{
struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+
if (!req)
return NULL;
@@ -1079,6 +1080,7 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f)
static void acc_start_work(struct work_struct *data)
{
char *envp[2] = { "ACCESSORY=START", NULL };
+
kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp);
}
diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c
index bcd817439dbf..db7903d19c43 100644
--- a/drivers/usb/gadget/function/f_audio_source.c
+++ b/drivers/usb/gadget/function/f_audio_source.c
@@ -310,6 +310,7 @@ static struct device_attribute *audio_source_function_attributes[] = {
static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size)
{
struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+
if (!req)
return NULL;
@@ -377,10 +378,9 @@ static void audio_send(struct audio_dev *audio)
/* compute number of frames to send */
now = ktime_get();
- msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time);
- do_div(msecs, 1000000);
- frames = msecs * SAMPLE_RATE;
- do_div(frames, 1000);
+ msecs = div_s64((ktime_to_ns(now) - ktime_to_ns(audio->start_time)),
+ 1000000);
+ frames = div_s64((msecs * SAMPLE_RATE), 1000);
/* Readjust our frames_sent if we fall too far behind.
* If we get too far behind it is better to drop some frames than
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 739cf9790cd4..ab44bd316217 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -1581,6 +1581,8 @@ static int functionfs_init(void)
pr_err("failed registering file system (%d)\n", ret);
ffs_ipc_log = ipc_log_context_create(NUM_PAGES, "f_fs", 0);
+ if (IS_ERR_OR_NULL(ffs_ipc_log))
+ ffs_ipc_log = NULL;
return ret;
}
@@ -1591,6 +1593,11 @@ static void functionfs_cleanup(void)
pr_info("unloading\n");
unregister_filesystem(&ffs_fs_type);
+
+ if (ffs_ipc_log) {
+ ipc_log_context_destroy(ffs_ipc_log);
+ ffs_ipc_log = NULL;
+ }
}
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index 2f08a6c9d476..e46edc83430c 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -40,7 +40,6 @@ MODULE_PARM_DESC(qti_packet_debug, "Print QTI Packet's Raw Data");
static struct workqueue_struct *ipa_usb_wq;
-static void gsi_rndis_ipa_reset_trigger(struct f_gsi *rndis);
static void ipa_disconnect_handler(struct gsi_data_port *d_port);
static int gsi_ctrl_send_notification(struct f_gsi *gsi);
static int gsi_alloc_trb_buffer(struct f_gsi *gsi);
@@ -48,6 +47,20 @@ static void gsi_free_trb_buffer(struct f_gsi *gsi);
static struct gsi_ctrl_pkt *gsi_ctrl_pkt_alloc(unsigned len, gfp_t flags);
static void gsi_ctrl_pkt_free(struct gsi_ctrl_pkt *pkt);
+static inline bool usb_gsi_remote_wakeup_allowed(struct usb_function *f)
+{
+ bool remote_wakeup_allowed;
+
+ if (f->config->cdev->gadget->speed == USB_SPEED_SUPER)
+ remote_wakeup_allowed = f->func_wakeup_allowed;
+ else
+ remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup;
+
+ log_event_dbg("%s: remote_wakeup_allowed:%s", __func__,
+ remote_wakeup_allowed ? "true" : "false");
+ return remote_wakeup_allowed;
+}
+
void post_event(struct gsi_data_port *port, u8 event)
{
unsigned long flags;
@@ -477,16 +490,22 @@ static void ipa_disconnect_handler(struct gsi_data_port *d_port)
log_event_dbg("%s: EP Disable for data", __func__);
- /* Block doorbell to GSI to avoid USB wrapper from
- * ringing doorbell in case IPA clocks are OFF
- */
- usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
+ if (gsi->d_port.in_ep) {
+ /*
+ * Block doorbell to GSI to avoid USB wrapper from
+ * ringing doorbell in case IPA clocks are OFF.
+ */
+ usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
GSI_EP_OP_SET_CLR_BLOCK_DBL);
+ gsi->in_ep_desc_backup = gsi->d_port.in_ep->desc;
+ usb_gsi_ep_op(gsi->d_port.in_ep, NULL, GSI_EP_OP_DISABLE);
+ }
- usb_gsi_ep_op(gsi->d_port.in_ep, NULL, GSI_EP_OP_DISABLE);
-
- if (gsi->d_port.out_ep)
+ if (gsi->d_port.out_ep) {
+ gsi->out_ep_desc_backup = gsi->d_port.out_ep->desc;
usb_gsi_ep_op(gsi->d_port.out_ep, NULL, GSI_EP_OP_DISABLE);
+ }
+
gsi->d_port.net_ready_trigger = false;
}
@@ -523,19 +542,21 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port)
int ret = 0;
bool block_db, f_suspend;
struct f_gsi *gsi = d_port_to_gsi(d_port);
+ struct usb_function *f = &gsi->function;
+
+ f_suspend = f->func_wakeup_allowed;
+ log_event_dbg("%s: f_suspend:%d", __func__, f_suspend);
- f_suspend = gsi->function.func_wakeup_allowed;
if (!usb_gsi_ep_op(gsi->d_port.in_ep, (void *) &f_suspend,
GSI_EP_OP_CHECK_FOR_SUSPEND)) {
ret = -EFAULT;
goto done;
}
- log_event_dbg("%s: Calling xdci_suspend", __func__);
+ log_event_dbg("%s: Calling xdci_suspend", __func__);
ret = ipa_usb_xdci_suspend(gsi->d_port.out_channel_handle,
gsi->d_port.in_channel_handle, gsi->prot_id,
- true);
-
+ usb_gsi_remote_wakeup_allowed(f));
if (!ret) {
d_port->sm_state = STATE_SUSPENDED;
log_event_dbg("%s: STATE SUSPENDED", __func__);
@@ -553,10 +574,8 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port)
log_event_err("%s: Error %d for %d", __func__, ret,
gsi->prot_id);
}
-
- log_event_dbg("%s: xdci_suspend ret %d", __func__, ret);
-
done:
+ log_event_dbg("%s: xdci_suspend ret %d", __func__, ret);
return ret;
}
@@ -591,7 +610,6 @@ static void ipa_work_handler(struct work_struct *w)
struct device *dev;
struct device *gad_dev;
struct f_gsi *gsi;
- bool block_db;
event = read_event(d_port);
@@ -653,6 +671,29 @@ static void ipa_work_handler(struct work_struct *w)
__func__);
break;
}
+
+ /*
+ * Update desc and reconfigure USB GSI OUT and IN
+ * endpoint for RNDIS Adaptor enable case.
+ */
+ if (d_port->out_ep && !d_port->out_ep->desc &&
+ gsi->out_ep_desc_backup) {
+ d_port->out_ep->desc = gsi->out_ep_desc_backup;
+ d_port->out_ep->ep_intr_num = 1;
+ log_event_dbg("%s: OUT ep_op_config", __func__);
+ usb_gsi_ep_op(d_port->out_ep,
+ &d_port->out_request, GSI_EP_OP_CONFIG);
+ }
+
+ if (d_port->in_ep && !d_port->in_ep->desc &&
+ gsi->in_ep_desc_backup) {
+ d_port->in_ep->desc = gsi->in_ep_desc_backup;
+ d_port->in_ep->ep_intr_num = 2;
+ log_event_dbg("%s: IN ep_op_config", __func__);
+ usb_gsi_ep_op(d_port->in_ep,
+ &d_port->in_request, GSI_EP_OP_CONFIG);
+ }
+
ipa_connect_channels(d_port);
ipa_data_path_enable(d_port);
d_port->sm_state = STATE_CONNECTED;
@@ -714,15 +755,7 @@ static void ipa_work_handler(struct work_struct *w)
if (event == EVT_HOST_NRDY) {
log_event_dbg("%s: ST_CON_HOST_NRDY\n",
__func__);
- block_db = true;
- /* stop USB ringing doorbell to GSI(OUT_EP) */
- usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
- GSI_EP_OP_SET_CLR_BLOCK_DBL);
- gsi_rndis_ipa_reset_trigger(gsi);
- usb_gsi_ep_op(d_port->in_ep, NULL,
- GSI_EP_OP_ENDXFER);
- usb_gsi_ep_op(d_port->out_ep, NULL,
- GSI_EP_OP_ENDXFER);
+ ipa_disconnect_handler(d_port);
}
ipa_disconnect_work_handler(d_port);
@@ -1086,9 +1119,9 @@ static ssize_t gsi_ctrl_dev_write(struct file *fp, const char __user *buf,
list_add_tail(&cpkt->list, &c_port->cpkt_resp_q);
spin_unlock_irqrestore(&c_port->lock, flags);
- ret = gsi_ctrl_send_notification(gsi);
+ if (!gsi_ctrl_send_notification(gsi))
+ c_port->modem_to_host++;
- c_port->modem_to_host++;
log_event_dbg("Exit %zu", count);
return ret ? ret : count;
@@ -1345,26 +1378,6 @@ static void gsi_rndis_open(struct f_gsi *rndis)
rndis_signal_connect(rndis->params);
}
-static void gsi_rndis_ipa_reset_trigger(struct f_gsi *rndis)
-{
- unsigned long flags;
-
- if (!rndis) {
- log_event_err("%s: gsi prot ctx is %pK", __func__, rndis);
- return;
- }
-
- spin_lock_irqsave(&rndis->d_port.lock, flags);
- if (!rndis) {
- log_event_err("%s: No RNDIS instance", __func__);
- spin_unlock_irqrestore(&rndis->d_port.lock, flags);
- return;
- }
-
- rndis->d_port.net_ready_trigger = false;
- spin_unlock_irqrestore(&rndis->d_port.lock, flags);
-}
-
void gsi_rndis_flow_ctrl_enable(bool enable, struct rndis_params *param)
{
struct f_gsi *rndis = param->v;
@@ -1392,33 +1405,18 @@ static int queue_notification_request(struct f_gsi *gsi)
{
int ret;
unsigned long flags;
- struct usb_cdc_notification *event;
- struct gsi_ctrl_pkt *cpkt;
ret = usb_func_ep_queue(&gsi->function, gsi->c_port.notify,
gsi->c_port.notify_req, GFP_ATOMIC);
- if (ret == -ENOTSUPP || (ret < 0 && ret != -EAGAIN)) {
+ if (ret < 0) {
spin_lock_irqsave(&gsi->c_port.lock, flags);
gsi->c_port.notify_req_queued = false;
- /* check if device disconnected while we dropped lock */
- if (atomic_read(&gsi->connected) &&
- !list_empty(&gsi->c_port.cpkt_resp_q)) {
- cpkt = list_first_entry(&gsi->c_port.cpkt_resp_q,
- struct gsi_ctrl_pkt, list);
- list_del(&cpkt->list);
- log_event_err("%s: drop ctrl pkt of len %d error %d",
- __func__, cpkt->len, ret);
- gsi_ctrl_pkt_free(cpkt);
- }
- gsi->c_port.cpkt_drop_cnt++;
spin_unlock_irqrestore(&gsi->c_port.lock, flags);
- } else {
- ret = 0;
- event = gsi->c_port.notify_req->buf;
- log_event_dbg("%s: Queued Notify type %02x", __func__,
- event->bNotificationType);
}
+ log_event_dbg("%s: ret:%d req_queued:%d",
+ __func__, ret, gsi->c_port.notify_req_queued);
+
return ret;
}
@@ -2130,7 +2128,6 @@ static void gsi_suspend(struct usb_function *f)
{
bool block_db;
struct f_gsi *gsi = func_to_gsi(f);
- bool remote_wakeup_allowed;
/* Check if function is already suspended in gsi_func_suspend() */
if (f->func_is_suspended) {
@@ -2138,49 +2135,17 @@ static void gsi_suspend(struct usb_function *f)
return;
}
- if (f->config->cdev->gadget->speed == USB_SPEED_SUPER)
- remote_wakeup_allowed = f->func_wakeup_allowed;
- else
- remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup;
-
- log_event_info("%s: remote_wakeup_allowed %d",
- __func__, remote_wakeup_allowed);
-
- if (!remote_wakeup_allowed) {
- if (gsi->prot_id == IPA_USB_RNDIS)
- rndis_flow_control(gsi->params, true);
- /*
- * When remote wakeup is disabled, IPA is disconnected
- * because it cannot send new data until the USB bus is
- * resumed. Endpoint descriptors info is saved before it
- * gets reset by the BAM disconnect API. This lets us
- * restore this info when the USB bus is resumed.
- */
- if (gsi->d_port.in_ep)
- gsi->in_ep_desc_backup = gsi->d_port.in_ep->desc;
- if (gsi->d_port.out_ep)
- gsi->out_ep_desc_backup = gsi->d_port.out_ep->desc;
-
- ipa_disconnect_handler(&gsi->d_port);
-
- post_event(&gsi->d_port, EVT_DISCONNECTED);
- queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
- log_event_dbg("%s: Disconnecting", __func__);
- } else {
- block_db = true;
- usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db,
- GSI_EP_OP_SET_CLR_BLOCK_DBL);
- post_event(&gsi->d_port, EVT_SUSPEND);
- queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
- }
-
+ block_db = true;
+ usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db,
+ GSI_EP_OP_SET_CLR_BLOCK_DBL);
+ post_event(&gsi->d_port, EVT_SUSPEND);
+ queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
log_event_dbg("gsi suspended");
}
static void gsi_resume(struct usb_function *f)
{
struct f_gsi *gsi = func_to_gsi(f);
- bool remote_wakeup_allowed;
struct usb_composite_dev *cdev = f->config->cdev;
log_event_dbg("%s", __func__);
@@ -2193,49 +2158,24 @@ static void gsi_resume(struct usb_function *f)
f->func_is_suspended)
return;
- if (f->config->cdev->gadget->speed == USB_SPEED_SUPER)
- remote_wakeup_allowed = f->func_wakeup_allowed;
- else
- remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup;
-
if (gsi->c_port.notify && !gsi->c_port.notify->desc)
config_ep_by_speed(cdev->gadget, f, gsi->c_port.notify);
/* Check any pending cpkt, and queue immediately on resume */
gsi_ctrl_send_notification(gsi);
- if (!remote_wakeup_allowed) {
-
- /* Configure EPs for GSI */
- if (gsi->d_port.out_ep) {
- gsi->d_port.out_ep->desc = gsi->out_ep_desc_backup;
- gsi->d_port.out_ep->ep_intr_num = 1;
- usb_gsi_ep_op(gsi->d_port.out_ep,
- &gsi->d_port.out_request, GSI_EP_OP_CONFIG);
- }
- gsi->d_port.in_ep->desc = gsi->in_ep_desc_backup;
- if (gsi->prot_id != IPA_USB_DIAG)
- gsi->d_port.in_ep->ep_intr_num = 2;
- else
- gsi->d_port.in_ep->ep_intr_num = 3;
-
- usb_gsi_ep_op(gsi->d_port.in_ep, &gsi->d_port.in_request,
- GSI_EP_OP_CONFIG);
- post_event(&gsi->d_port, EVT_CONNECT_IN_PROGRESS);
-
- /*
- * Linux host does not send RNDIS_MSG_INIT or non-zero
- * RNDIS_MESSAGE_PACKET_FILTER after performing bus resume.
- * Trigger state machine explicitly on resume.
- */
- if (gsi->prot_id == IPA_USB_RNDIS)
- rndis_flow_control(gsi->params, false);
- } else
- post_event(&gsi->d_port, EVT_RESUMED);
+ /*
+ * Linux host does not send RNDIS_MSG_INIT or non-zero
+ * RNDIS_MESSAGE_PACKET_FILTER after performing bus resume.
+ * Trigger state machine explicitly on resume.
+ */
+ if (gsi->prot_id == IPA_USB_RNDIS &&
+ !usb_gsi_remote_wakeup_allowed(f))
+ rndis_flow_control(gsi->params, false);
+ post_event(&gsi->d_port, EVT_RESUMED);
queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
-
log_event_dbg("%s: completed", __func__);
}
@@ -3132,7 +3072,7 @@ MODULE_DESCRIPTION("GSI function driver");
static int fgsi_init(void)
{
ipa_usb_wq = alloc_workqueue("k_ipa_usb",
- WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+ WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE, 1);
if (!ipa_usb_wq) {
log_event_err("Failed to create workqueue for IPA");
return -ENOMEM;
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 97d86b6ac69b..e309dec68a75 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -2289,8 +2289,11 @@ reset:
}
common->running = 0;
- if (!new_fsg || rc)
+ if (!new_fsg || rc) {
+ /* allow usb LPM after eps are disabled */
+ usb_gadget_autopm_put_async(common->gadget);
return rc;
+ }
common->fsg = new_fsg;
fsg = common->fsg;
@@ -2333,6 +2336,9 @@ reset:
bh->outreq->complete = bulk_out_complete;
}
+ /* prevents usb LPM until thread runs to completion */
+ usb_gadget_autopm_get_noresume(common->gadget);
+
common->running = 1;
for (i = 0; i < ARRAY_SIZE(common->luns); ++i)
if (common->luns[i])
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 8919cc26b98e..5bcff5d2cd8d 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -1160,7 +1160,7 @@ static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
card = midi->card;
midi->card = NULL;
if (card)
- snd_card_free(card);
+ snd_card_free_when_closed(card);
usb_free_all_descriptors(f);
}
diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c
index 972ea68b16e4..bf7460f25e61 100644
--- a/drivers/usb/gadget/function/f_mtp.c
+++ b/drivers/usb/gadget/function/f_mtp.c
@@ -137,6 +137,7 @@ struct mtp_dev {
unsigned dbg_read_index;
unsigned dbg_write_index;
bool is_ptp;
+ struct mutex read_mutex;
};
static struct usb_interface_descriptor mtp_interface_desc = {
@@ -412,6 +413,7 @@ static inline struct mtp_dev *func_to_mtp(struct usb_function *f)
static struct usb_request *mtp_request_new(struct usb_ep *ep, int buffer_size)
{
struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+
if (!req)
return NULL;
@@ -640,11 +642,18 @@ static ssize_t mtp_read(struct file *fp, char __user *buf,
dev->state = STATE_BUSY;
spin_unlock_irq(&dev->lock);
+ mutex_lock(&dev->read_mutex);
+ if (dev->state == STATE_OFFLINE) {
+ r = -EIO;
+ mutex_unlock(&dev->read_mutex);
+ goto done;
+ }
requeue_req:
/* queue a request */
req = dev->rx_req[0];
req->length = len;
dev->rx_done = 0;
+ mutex_unlock(&dev->read_mutex);
ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL);
if (ret < 0) {
r = -EIO;
@@ -670,6 +679,7 @@ requeue_req:
usb_ep_dequeue(dev->ep_out, req);
goto done;
}
+ mutex_lock(&dev->read_mutex);
if (dev->state == STATE_BUSY) {
/* If we got a 0-len packet, throw it back and try again. */
if (req->actual == 0)
@@ -683,6 +693,7 @@ requeue_req:
} else
r = -EIO;
+ mutex_unlock(&dev->read_mutex);
done:
spin_lock_irq(&dev->lock);
if (dev->state == STATE_CANCELED)
@@ -937,6 +948,12 @@ static void receive_file_work(struct work_struct *data)
while (count > 0 || write_req) {
if (count > 0) {
+ mutex_lock(&dev->read_mutex);
+ if (dev->state == STATE_OFFLINE) {
+ r = -EIO;
+ mutex_unlock(&dev->read_mutex);
+ break;
+ }
/* queue a request */
read_req = dev->rx_req[cur_buf];
cur_buf = (cur_buf + 1) % RX_REQ_MAX;
@@ -945,6 +962,7 @@ static void receive_file_work(struct work_struct *data)
read_req->length = mtp_rx_req_len;
dev->rx_done = 0;
+ mutex_unlock(&dev->read_mutex);
ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
if (ret < 0) {
r = -EIO;
@@ -957,15 +975,23 @@ static void receive_file_work(struct work_struct *data)
if (write_req) {
DBG(cdev, "rx %pK %d\n", write_req, write_req->actual);
start_time = ktime_get();
+ mutex_lock(&dev->read_mutex);
+ if (dev->state == STATE_OFFLINE) {
+ r = -EIO;
+ mutex_unlock(&dev->read_mutex);
+ break;
+ }
ret = vfs_write(filp, write_req->buf, write_req->actual,
&offset);
DBG(cdev, "vfs_write %d\n", ret);
if (ret != write_req->actual) {
r = -EIO;
+ mutex_unlock(&dev->read_mutex);
if (dev->state != STATE_OFFLINE)
dev->state = STATE_ERROR;
break;
}
+ mutex_unlock(&dev->read_mutex);
dev->perf[dev->dbg_write_index].vfs_wtime =
ktime_to_us(ktime_sub(ktime_get(), start_time));
dev->perf[dev->dbg_write_index].vfs_wbytes = ret;
@@ -989,6 +1015,12 @@ static void receive_file_work(struct work_struct *data)
break;
}
+ mutex_lock(&dev->read_mutex);
+ if (dev->state == STATE_OFFLINE) {
+ r = -EIO;
+ mutex_unlock(&dev->read_mutex);
+ break;
+ }
/* Check if we aligned the size due to MTU constraint */
if (count < read_req->length)
read_req->actual = (read_req->actual > count ?
@@ -1009,6 +1041,7 @@ static void receive_file_work(struct work_struct *data)
write_req = read_req;
read_req = NULL;
+ mutex_unlock(&dev->read_mutex);
}
}
@@ -1352,6 +1385,7 @@ static int mtp_ctrlrequest(struct usb_composite_dev *cdev,
} else if (ctrl->bRequest == MTP_REQ_GET_DEVICE_STATUS
&& w_index == 0 && w_value == 0) {
struct mtp_device_status *status = cdev->req->buf;
+
status->wLength =
__constant_cpu_to_le16(sizeof(*status));
@@ -1374,6 +1408,7 @@ static int mtp_ctrlrequest(struct usb_composite_dev *cdev,
/* respond with data transfer or status phase? */
if (value >= 0) {
int rc;
+
cdev->req->zero = value < w_length;
cdev->req->length = value;
rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
@@ -1469,12 +1504,14 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f)
struct usb_request *req;
int i;
+ mutex_lock(&dev->read_mutex);
while ((req = mtp_req_get(dev, &dev->tx_idle)))
mtp_request_free(req, dev->ep_in);
for (i = 0; i < RX_REQ_MAX; i++)
mtp_request_free(dev->rx_req[i], dev->ep_out);
while ((req = mtp_req_get(dev, &dev->intr_idle)))
mtp_request_free(req, dev->ep_intr);
+ mutex_unlock(&dev->read_mutex);
dev->state = STATE_OFFLINE;
dev->is_ptp = false;
kfree(f->os_desc_table);
@@ -1733,6 +1770,7 @@ static struct mtp_instance *to_mtp_instance(struct config_item *item)
static void mtp_attr_release(struct config_item *item)
{
struct mtp_instance *fi_mtp = to_mtp_instance(item);
+
usb_put_function_instance(&fi_mtp->func_inst);
}
@@ -1853,7 +1891,7 @@ struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi,
pr_err("\t2: Create MTP function\n");
pr_err("\t3: Create and symlink PTP function"
" with a gadget configuration\n");
- return NULL;
+ return ERR_PTR(-EINVAL); /* Invalid Configuration */
}
dev = fi_mtp->dev;
@@ -1877,6 +1915,7 @@ struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi,
dev->is_ptp = !mtp_config;
fi->f = &dev->function;
+ mutex_init(&dev->read_mutex);
return &dev->function;
}
EXPORT_SYMBOL_GPL(function_alloc_mtp_ptp);
diff --git a/drivers/usb/gadget/function/u_data_ipa.c b/drivers/usb/gadget/function/u_data_ipa.c
index 4975ba474f97..5718f71bcdea 100644
--- a/drivers/usb/gadget/function/u_data_ipa.c
+++ b/drivers/usb/gadget/function/u_data_ipa.c
@@ -428,6 +428,7 @@ static void ipa_data_connect_work(struct work_struct *w)
if (!gadget) {
spin_unlock_irqrestore(&port->port_lock, flags);
+ usb_gadget_autopm_put_async(port->gadget);
pr_err("%s: gport is NULL.\n", __func__);
return;
}
@@ -691,6 +692,8 @@ disconnect_usb_bam_ipa_out:
usb_bam_disconnect_ipa(port->usb_bam_type, &port->ipa_params);
is_ipa_disconnected = true;
}
+ if (port->func_type == USB_IPA_FUNC_RMNET)
+ teth_bridge_disconnect(port->ipa_params.src_client);
unconfig_msm_ep_in:
spin_lock_irqsave(&port->port_lock, flags);
/* check if USB cable is disconnected or not */
@@ -713,6 +716,7 @@ out:
spin_lock_irqsave(&port->port_lock, flags);
port->is_connected = false;
spin_unlock_irqrestore(&port->port_lock, flags);
+ usb_gadget_autopm_put_async(port->gadget);
}
/**
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 81ce22e91883..43e054666b68 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -743,7 +743,8 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
/* throttle highspeed IRQ rate back slightly */
if (gadget_is_dualspeed(dev->gadget) &&
- (dev->gadget->speed == USB_SPEED_HIGH)) {
+ (dev->gadget->speed == USB_SPEED_HIGH) &&
+ !list_empty(&dev->tx_reqs)) {
dev->tx_qlen++;
if (dev->tx_qlen == (dev->qmult/2)) {
req->no_interrupt = 0;
diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c
index 389c1f3d0fee..b13f839e7368 100644
--- a/drivers/usb/gadget/functions.c
+++ b/drivers/usb/gadget/functions.c
@@ -58,7 +58,7 @@ struct usb_function *usb_get_function(struct usb_function_instance *fi)
struct usb_function *f;
f = fi->fd->alloc_func(fi);
- if ((f == NULL) || IS_ERR(f))
+ if (IS_ERR(f))
return f;
f->fi = fi;
return f;
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index c73689b72f95..b38a33584d4a 100644
--- a/drivers/usb/gadget/udc/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -1878,11 +1878,8 @@ static int qe_get_frame(struct usb_gadget *gadget)
tmp = in_be16(&udc->usb_param->frame_n);
if (tmp & 0x8000)
- tmp = tmp & 0x07ff;
- else
- tmp = -EINVAL;
-
- return (int)tmp;
+ return tmp & 0x07ff;
+ return -EINVAL;
}
static int fsl_qe_start(struct usb_gadget *gadget,
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 9094bca1bac6..c0ce3db6a7c0 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -72,7 +72,7 @@
static const char hcd_name [] = "ohci_hcd";
#define STATECHANGE_DELAY msecs_to_jiffies(300)
-#define IO_WATCHDOG_DELAY msecs_to_jiffies(250)
+#define IO_WATCHDOG_DELAY msecs_to_jiffies(275)
#include "ohci.h"
#include "pci-quirks.h"
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 963867c2c1d5..cf147ccac7d3 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -45,6 +45,7 @@
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
+#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_XHCI 0x9cb1
#define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI 0xa12f
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f
@@ -154,7 +155,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
- pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) {
+ (pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_XHCI)) {
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
xhci->quirks |= XHCI_SPURIOUS_WAKEUP;
}
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index fe529f9f7d28..98d01acc8b11 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -846,6 +846,10 @@ 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 "
@@ -899,7 +903,7 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Calling usb_hc_died()");
- usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
+ usb_hc_died(xhci_to_hcd(xhci));
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"xHCI host controller is dead.");
}
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 7771be3ac178..4dd531ac5a7f 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -898,24 +898,6 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device
dev->interrupt_in_interval = interrupt_in_interval ? interrupt_in_interval : dev->interrupt_in_endpoint->bInterval;
dev->interrupt_out_interval = interrupt_out_interval ? interrupt_out_interval : dev->interrupt_out_endpoint->bInterval;
- /* we can register the device now, as it is ready */
- usb_set_intfdata (interface, dev);
-
- retval = usb_register_dev (interface, &tower_class);
-
- if (retval) {
- /* something prevented us from registering this driver */
- dev_err(idev, "Not able to get a minor for this device.\n");
- usb_set_intfdata (interface, NULL);
- goto error;
- }
- dev->minor = interface->minor;
-
- /* let the user know what node this device is now attached to */
- dev_info(&interface->dev, "LEGO USB Tower #%d now attached to major "
- "%d minor %d\n", (dev->minor - LEGO_USB_TOWER_MINOR_BASE),
- USB_MAJOR, dev->minor);
-
/* get the firmware version and log it */
result = usb_control_msg (udev,
usb_rcvctrlpipe(udev, 0),
@@ -936,6 +918,23 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device
get_version_reply.minor,
le16_to_cpu(get_version_reply.build_no));
+ /* we can register the device now, as it is ready */
+ usb_set_intfdata (interface, dev);
+
+ retval = usb_register_dev (interface, &tower_class);
+
+ if (retval) {
+ /* something prevented us from registering this driver */
+ dev_err(idev, "Not able to get a minor for this device.\n");
+ usb_set_intfdata (interface, NULL);
+ goto error;
+ }
+ dev->minor = interface->minor;
+
+ /* let the user know what node this device is now attached to */
+ dev_info(&interface->dev, "LEGO USB Tower #%d now attached to major "
+ "%d minor %d\n", (dev->minor - LEGO_USB_TOWER_MINOR_BASE),
+ USB_MAJOR, dev->minor);
exit:
return retval;
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 59a63a0b7985..e0a083f6ab68 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -661,7 +661,7 @@ static int musb_tx_dma_set_mode_mentor(struct dma_controller *dma,
csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE);
csr |= MUSB_TXCSR_DMAENAB; /* against programmer's guide */
}
- channel->desired_mode = mode;
+ channel->desired_mode = *mode;
musb_writew(epio, MUSB_TXCSR, csr);
return 0;
@@ -2008,10 +2008,8 @@ void musb_host_rx(struct musb *musb, u8 epnum)
qh->offset,
urb->transfer_buffer_length);
- done = musb_rx_dma_in_inventra_cppi41(c, hw_ep, qh,
- urb, xfer_len,
- iso_err);
- if (done)
+ if (musb_rx_dma_in_inventra_cppi41(c, hw_ep, qh, urb,
+ xfer_len, iso_err))
goto finish;
else
dev_err(musb->controller, "error: rx_dma failed\n");
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index ea278781440c..935bd0778bfb 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -665,7 +665,7 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type,
rx_msg->type = PD_MSG_HDR_TYPE(header);
rx_msg->len = PD_MSG_HDR_COUNT(header);
- memcpy(&rx_msg->payload, buf, len);
+ memcpy(&rx_msg->payload, buf, min(len, sizeof(rx_msg->payload)));
spin_lock_irqsave(&pd->rx_lock, flags);
list_add_tail(&rx_msg->entry, &pd->rx_q);
diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c
index d4be5d594896..28965ef4f824 100644
--- a/drivers/usb/renesas_usbhs/mod.c
+++ b/drivers/usb/renesas_usbhs/mod.c
@@ -282,9 +282,16 @@ static irqreturn_t usbhs_interrupt(int irq, void *data)
if (usbhs_mod_is_host(priv))
usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC);
- usbhs_write(priv, BRDYSTS, ~irq_state.brdysts);
+ /*
+ * The driver should not clear the xxxSTS after the line of
+ * "call irq callback functions" because each "if" statement is
+ * possible to call the callback function for avoiding any side effects.
+ */
+ if (irq_state.intsts0 & BRDY)
+ usbhs_write(priv, BRDYSTS, ~irq_state.brdysts);
usbhs_write(priv, NRDYSTS, ~irq_state.nrdysts);
- usbhs_write(priv, BEMPSTS, ~irq_state.bempsts);
+ if (irq_state.intsts0 & BEMP)
+ usbhs_write(priv, BEMPSTS, ~irq_state.bempsts);
/*
* call irq callback functions
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index a2b43a6e7fa7..fe7452f0f38a 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -117,6 +117,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x8411) }, /* Kyocera GPS Module */
{ USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */
{ USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */
+ { USB_DEVICE(0x10C4, 0x8470) }, /* Juniper Networks BX Series System Console */
{ USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */
{ USB_DEVICE(0x10C4, 0x84B6) }, /* Starizona Hyperion */
{ USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */
@@ -129,6 +130,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */
{ USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */
{ USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */
+ { USB_DEVICE(0x10C4, 0x8962) }, /* Brim Brothers charging dock */
{ USB_DEVICE(0x10C4, 0x8977) }, /* CEL MeshWorks DevKit Device */
{ USB_DEVICE(0x10C4, 0x8998) }, /* KCF Technologies PRN */
{ USB_DEVICE(0x10C4, 0x8A2A) }, /* HubZ dual ZigBee and Z-Wave dongle */
@@ -784,7 +786,7 @@ static void cp210x_set_termios(struct tty_struct *tty,
} else {
modem_ctl[0] &= ~0x7B;
modem_ctl[0] |= 0x01;
- modem_ctl[1] |= 0x40;
+ modem_ctl[1] = 0x40;
dev_dbg(dev, "%s - flow control = NONE\n", __func__);
}
@@ -844,7 +846,9 @@ static int cp210x_tiocmget(struct tty_struct *tty)
unsigned int control;
int result;
- cp210x_get_config(port, CP210X_GET_MDMSTS, &control, 1);
+ result = cp210x_get_config(port, CP210X_GET_MDMSTS, &control, 1);
+ if (result)
+ return result;
result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0)
|((control & CONTROL_RTS) ? TIOCM_RTS : 0)
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 8c48c9d83d48..d3d6ec455151 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -986,7 +986,8 @@ static const struct usb_device_id id_table_combined[] = {
/* ekey Devices */
{ USB_DEVICE(FTDI_VID, FTDI_EKEY_CONV_USB_PID) },
/* Infineon Devices */
- { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_TC1798_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_TC2X7_PID, 1) },
/* GE Healthcare devices */
{ USB_DEVICE(GE_HEALTHCARE_VID, GE_HEALTHCARE_NEMO_TRACKER_PID) },
/* Active Research (Actisense) devices */
@@ -1011,6 +1012,8 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(ICPDAS_VID, ICPDAS_I7561U_PID) },
{ USB_DEVICE(ICPDAS_VID, ICPDAS_I7563U_PID) },
{ USB_DEVICE(WICED_VID, WICED_USB20706V2_PID) },
+ { USB_DEVICE(TI_VID, TI_CC3200_LAUNCHPAD_PID),
+ .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index f87a938cf005..48ee04c94a75 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -596,6 +596,12 @@
#define STK541_PID 0x2109 /* Zigbee Controller */
/*
+ * Texas Instruments
+ */
+#define TI_VID 0x0451
+#define TI_CC3200_LAUNCHPAD_PID 0xC32A /* SimpleLink Wi-Fi CC3200 LaunchPad */
+
+/*
* Blackfin gnICE JTAG
* http://docs.blackfin.uclinux.org/doku.php?id=hw:jtag:gnice
*/
@@ -626,8 +632,9 @@
/*
* Infineon Technologies
*/
-#define INFINEON_VID 0x058b
-#define INFINEON_TRIBOARD_PID 0x0028 /* DAS JTAG TriBoard TC1798 V1.0 */
+#define INFINEON_VID 0x058b
+#define INFINEON_TRIBOARD_TC1798_PID 0x0028 /* DAS JTAG TriBoard TC1798 V1.0 */
+#define INFINEON_TRIBOARD_TC2X7_PID 0x0043 /* DAS JTAG TriBoard TC2X7 V1.0 */
/*
* Acton Research Corp.
diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c
index a204782ae530..e98b6e57b703 100644
--- a/drivers/usb/serial/usb-serial-simple.c
+++ b/drivers/usb/serial/usb-serial-simple.c
@@ -54,7 +54,8 @@ DEVICE(funsoft, FUNSOFT_IDS);
/* Infineon Flashloader driver */
#define FLASHLOADER_IDS() \
{ USB_DEVICE_INTERFACE_CLASS(0x058b, 0x0041, USB_CLASS_CDC_DATA) }, \
- { USB_DEVICE(0x8087, 0x0716) }
+ { USB_DEVICE(0x8087, 0x0716) }, \
+ { USB_DEVICE(0x8087, 0x0801) }
DEVICE(flashloader, FLASHLOADER_IDS);
/* Google Serial USB SubClass */
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index a0ca291bc07f..e7e29c797824 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -1077,7 +1077,8 @@ static int usb_serial_probe(struct usb_interface *interface,
serial->disconnected = 0;
- usb_serial_console_init(serial->port[0]->minor);
+ if (num_ports > 0)
+ usb_serial_console_init(serial->port[0]->minor);
exit:
module_put(type->driver.owner);
return 0;
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 5e67f63b2e46..02f86dd1a340 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -919,10 +919,15 @@ int usb_stor_CB_transport(struct scsi_cmnd *srb, struct us_data *us)
/* COMMAND STAGE */
/* let's send the command via the control pipe */
+ /*
+ * Command is sometime (f.e. after scsi_eh_prep_cmnd) on the stack.
+ * Stack may be vmallocated. So no DMA for us. Make a copy.
+ */
+ memcpy(us->iobuf, srb->cmnd, srb->cmd_len);
result = usb_stor_ctrl_transfer(us, us->send_ctrl_pipe,
US_CBI_ADSC,
USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
- us->ifnum, srb->cmnd, srb->cmd_len);
+ us->ifnum, us->iobuf, srb->cmd_len);
/* check the return code for the command */
usb_stor_dbg(us, "Call to usb_stor_ctrl_transfer() returned %d\n",
diff --git a/drivers/uwb/lc-rc.c b/drivers/uwb/lc-rc.c
index d059ad4d0dbd..97ee1b46db69 100644
--- a/drivers/uwb/lc-rc.c
+++ b/drivers/uwb/lc-rc.c
@@ -56,8 +56,11 @@ static struct uwb_rc *uwb_rc_find_by_index(int index)
struct uwb_rc *rc = NULL;
dev = class_find_device(&uwb_rc_class, NULL, &index, uwb_rc_index_match);
- if (dev)
+ if (dev) {
rc = dev_get_drvdata(dev);
+ put_device(dev);
+ }
+
return rc;
}
@@ -467,7 +470,9 @@ struct uwb_rc *__uwb_rc_try_get(struct uwb_rc *target_rc)
if (dev) {
rc = dev_get_drvdata(dev);
__uwb_rc_get(rc);
+ put_device(dev);
}
+
return rc;
}
EXPORT_SYMBOL_GPL(__uwb_rc_try_get);
@@ -520,8 +525,11 @@ struct uwb_rc *uwb_rc_get_by_grandpa(const struct device *grandpa_dev)
dev = class_find_device(&uwb_rc_class, NULL, grandpa_dev,
find_rc_grandpa);
- if (dev)
+ if (dev) {
rc = dev_get_drvdata(dev);
+ put_device(dev);
+ }
+
return rc;
}
EXPORT_SYMBOL_GPL(uwb_rc_get_by_grandpa);
@@ -553,8 +561,10 @@ struct uwb_rc *uwb_rc_get_by_dev(const struct uwb_dev_addr *addr)
struct uwb_rc *rc = NULL;
dev = class_find_device(&uwb_rc_class, NULL, addr, find_rc_dev);
- if (dev)
+ if (dev) {
rc = dev_get_drvdata(dev);
+ put_device(dev);
+ }
return rc;
}
diff --git a/drivers/uwb/pal.c b/drivers/uwb/pal.c
index c1304b8d4985..678e93741ae1 100644
--- a/drivers/uwb/pal.c
+++ b/drivers/uwb/pal.c
@@ -97,6 +97,8 @@ static bool uwb_rc_class_device_exists(struct uwb_rc *target_rc)
dev = class_find_device(&uwb_rc_class, NULL, target_rc, find_rc);
+ put_device(dev);
+
return (dev != NULL);
}
diff --git a/drivers/video/adf/adf_fops.c b/drivers/video/adf/adf_fops.c
index 8726617f73ab..705411bfaebb 100644
--- a/drivers/video/adf/adf_fops.c
+++ b/drivers/video/adf/adf_fops.c
@@ -132,7 +132,7 @@ static int adf_eng_get_data(struct adf_overlay_engine *eng,
eng->ops->n_supported_formats));
mutex_lock(&dev->client_lock);
- ret = adf_obj_copy_custom_data_to_user(&eng->base, arg->custom_data,
+ ret = adf_obj_copy_custom_data_to_user(&eng->base, data.custom_data,
&data.custom_data_size);
mutex_unlock(&dev->client_lock);
@@ -144,7 +144,7 @@ static int adf_eng_get_data(struct adf_overlay_engine *eng,
goto done;
}
- if (supported_formats && copy_to_user(arg->supported_formats,
+ if (supported_formats && copy_to_user(data.supported_formats,
supported_formats,
n_supported_formats * sizeof(supported_formats[0])))
ret = -EFAULT;
@@ -220,56 +220,45 @@ static int adf_device_post_config(struct adf_device *dev,
int complete_fence_fd;
struct adf_buffer *bufs = NULL;
struct adf_interface **intfs = NULL;
- size_t n_intfs, n_bufs, i;
+ struct adf_post_config data;
+ size_t i;
void *custom_data = NULL;
- size_t custom_data_size;
int ret = 0;
+ if (copy_from_user(&data, arg, sizeof(data)))
+ return -EFAULT;
+
complete_fence_fd = get_unused_fd_flags(O_CLOEXEC);
if (complete_fence_fd < 0)
return complete_fence_fd;
- if (get_user(n_intfs, &arg->n_interfaces)) {
- ret = -EFAULT;
- goto err_get_user;
- }
-
- if (n_intfs > ADF_MAX_INTERFACES) {
+ if (data.n_interfaces > ADF_MAX_INTERFACES) {
ret = -EINVAL;
goto err_get_user;
}
- if (get_user(n_bufs, &arg->n_bufs)) {
- ret = -EFAULT;
- goto err_get_user;
- }
-
- if (n_bufs > ADF_MAX_BUFFERS) {
+ if (data.n_bufs > ADF_MAX_BUFFERS) {
ret = -EINVAL;
goto err_get_user;
}
- if (get_user(custom_data_size, &arg->custom_data_size)) {
- ret = -EFAULT;
- goto err_get_user;
- }
-
- if (custom_data_size > ADF_MAX_CUSTOM_DATA_SIZE) {
+ if (data.custom_data_size > ADF_MAX_CUSTOM_DATA_SIZE) {
ret = -EINVAL;
goto err_get_user;
}
- if (n_intfs) {
- intfs = kmalloc(sizeof(intfs[0]) * n_intfs, GFP_KERNEL);
+ if (data.n_interfaces) {
+ intfs = kmalloc(sizeof(intfs[0]) * data.n_interfaces,
+ GFP_KERNEL);
if (!intfs) {
ret = -ENOMEM;
goto err_get_user;
}
}
- for (i = 0; i < n_intfs; i++) {
+ for (i = 0; i < data.n_interfaces; i++) {
u32 intf_id;
- if (get_user(intf_id, &arg->interfaces[i])) {
+ if (get_user(intf_id, &data.interfaces[i])) {
ret = -EFAULT;
goto err_get_user;
}
@@ -281,31 +270,31 @@ static int adf_device_post_config(struct adf_device *dev,
}
}
- if (n_bufs) {
- bufs = kzalloc(sizeof(bufs[0]) * n_bufs, GFP_KERNEL);
+ if (data.n_bufs) {
+ bufs = kzalloc(sizeof(bufs[0]) * data.n_bufs, GFP_KERNEL);
if (!bufs) {
ret = -ENOMEM;
goto err_get_user;
}
}
- for (i = 0; i < n_bufs; i++) {
- ret = adf_buffer_import(dev, &arg->bufs[i], &bufs[i]);
+ for (i = 0; i < data.n_bufs; i++) {
+ ret = adf_buffer_import(dev, &data.bufs[i], &bufs[i]);
if (ret < 0) {
memset(&bufs[i], 0, sizeof(bufs[i]));
goto err_import;
}
}
- if (custom_data_size) {
- custom_data = kzalloc(custom_data_size, GFP_KERNEL);
+ if (data.custom_data_size) {
+ custom_data = kzalloc(data.custom_data_size, GFP_KERNEL);
if (!custom_data) {
ret = -ENOMEM;
goto err_import;
}
- if (copy_from_user(custom_data, arg->custom_data,
- custom_data_size)) {
+ if (copy_from_user(custom_data, data.custom_data,
+ data.custom_data_size)) {
ret = -EFAULT;
goto err_import;
}
@@ -316,8 +305,8 @@ static int adf_device_post_config(struct adf_device *dev,
goto err_import;
}
- complete_fence = adf_device_post_nocopy(dev, intfs, n_intfs, bufs,
- n_bufs, custom_data, custom_data_size);
+ complete_fence = adf_device_post_nocopy(dev, intfs, data.n_interfaces,
+ bufs, data.n_bufs, custom_data, data.custom_data_size);
if (IS_ERR(complete_fence)) {
ret = PTR_ERR(complete_fence);
goto err_import;
@@ -327,7 +316,7 @@ static int adf_device_post_config(struct adf_device *dev,
return 0;
err_import:
- for (i = 0; i < n_bufs; i++)
+ for (i = 0; i < data.n_bufs; i++)
adf_buffer_cleanup(&bufs[i]);
err_get_user:
@@ -481,19 +470,19 @@ static int adf_device_get_data(struct adf_device *dev,
data.n_allowed_attachments);
mutex_lock(&dev->client_lock);
- ret = adf_obj_copy_custom_data_to_user(&dev->base, arg->custom_data,
+ ret = adf_obj_copy_custom_data_to_user(&dev->base, data.custom_data,
&data.custom_data_size);
mutex_unlock(&dev->client_lock);
if (ret < 0)
goto done;
- ret = adf_copy_attachment_list_to_user(arg->attachments,
+ ret = adf_copy_attachment_list_to_user(data.attachments,
data.n_attachments, attach, n_attach);
if (ret < 0)
goto done;
- ret = adf_copy_attachment_list_to_user(arg->allowed_attachments,
+ ret = adf_copy_attachment_list_to_user(data.allowed_attachments,
data.n_allowed_attachments, allowed_attach,
n_allowed_attach);
if (ret < 0)
@@ -592,7 +581,7 @@ static int adf_intf_get_data(struct adf_interface *intf,
data.n_available_modes = intf->n_modes;
read_unlock_irqrestore(&intf->hotplug_modelist_lock, flags);
- if (copy_to_user(arg->available_modes, modelist, modelist_size)) {
+ if (copy_to_user(data.available_modes, modelist, modelist_size)) {
ret = -EFAULT;
goto done;
}
@@ -601,7 +590,7 @@ static int adf_intf_get_data(struct adf_interface *intf,
memcpy(&data.current_mode, &intf->current_mode,
sizeof(intf->current_mode));
- ret = adf_obj_copy_custom_data_to_user(&intf->base, arg->custom_data,
+ ret = adf_obj_copy_custom_data_to_user(&intf->base, data.custom_data,
&data.custom_data_size);
done:
mutex_unlock(&dev->client_lock);
diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c
index 95d293b7445a..dc2fcda54d53 100644
--- a/drivers/video/fbdev/efifb.c
+++ b/drivers/video/fbdev/efifb.c
@@ -52,9 +52,9 @@ static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
return 1;
if (regno < 16) {
- red >>= 8;
- green >>= 8;
- blue >>= 8;
+ red >>= 16 - info->var.red.length;
+ green >>= 16 - info->var.green.length;
+ blue >>= 16 - info->var.blue.length;
((u32 *)(info->pseudo_palette))[regno] =
(red << info->var.red.offset) |
(green << info->var.green.offset) |
diff --git a/drivers/video/fbdev/goldfishfb.c b/drivers/video/fbdev/goldfishfb.c
index 7f6c9e6cfc6c..1e56b50e4082 100644
--- a/drivers/video/fbdev/goldfishfb.c
+++ b/drivers/video/fbdev/goldfishfb.c
@@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
+#include <linux/acpi.h>
enum {
FB_GET_WIDTH = 0x00,
@@ -234,7 +235,7 @@ static int goldfish_fb_probe(struct platform_device *pdev)
fb->fb.var.activate = FB_ACTIVATE_NOW;
fb->fb.var.height = readl(fb->reg_base + FB_GET_PHYS_HEIGHT);
fb->fb.var.width = readl(fb->reg_base + FB_GET_PHYS_WIDTH);
- fb->fb.var.pixclock = 10000;
+ fb->fb.var.pixclock = 0;
fb->fb.var.red.offset = 11;
fb->fb.var.red.length = 5;
@@ -304,12 +305,25 @@ static int goldfish_fb_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id goldfish_fb_of_match[] = {
+ { .compatible = "google,goldfish-fb", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_fb_of_match);
+
+static const struct acpi_device_id goldfish_fb_acpi_match[] = {
+ { "GFSH0004", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_fb_acpi_match);
static struct platform_driver goldfish_fb_driver = {
.probe = goldfish_fb_probe,
.remove = goldfish_fb_remove,
.driver = {
- .name = "goldfish_fb"
+ .name = "goldfish_fb",
+ .of_match_table = goldfish_fb_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_fb_acpi_match),
}
};
diff --git a/drivers/video/fbdev/msm/Kconfig b/drivers/video/fbdev/msm/Kconfig
index ef5c96214c19..03ee89ad0d99 100644
--- a/drivers/video/fbdev/msm/Kconfig
+++ b/drivers/video/fbdev/msm/Kconfig
@@ -63,6 +63,7 @@ config FB_MSM_MDSS_WRITEBACK
config FB_MSM_MDSS_HDMI_PANEL
depends on FB_MSM_MDSS
+ select MSM_EXT_DISPLAY
bool "MDSS HDMI Tx Panel"
default n
---help---
@@ -98,6 +99,7 @@ config FB_MSM_MDSS_DSI_CTRL_STATUS
config FB_MSM_MDSS_DP_PANEL
depends on FB_MSM_MDSS
+ select MSM_EXT_DISPLAY
bool "MDSS DP Panel"
---help---
The MDSS DP Panel provides support for DP host controller driver
diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile
index b905c0e855dd..e101b873f361 100644
--- a/drivers/video/fbdev/msm/Makefile
+++ b/drivers/video/fbdev/msm/Makefile
@@ -49,7 +49,6 @@ obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp_aux.o
obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp_hdcp2p2.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o
-obj-$(CONFIG_FB_MSM_MDSS) += msm_ext_display.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_tx.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_panel.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_hdcp2p2.o
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index bf4dc39f57ee..d9a4bd91f3eb 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -27,6 +27,7 @@
#include <linux/msm-bus.h>
#include <linux/file.h>
#include <linux/dma-direction.h>
+#include <soc/qcom/cx_ipeak.h>
#include "mdss_panel.h"
@@ -535,6 +536,7 @@ struct mdss_data_type {
u32 sec_cam_en;
u32 sec_session_cnt;
wait_queue_head_t secure_waitq;
+ struct cx_ipeak_client *mdss_cx_ipeak;
};
extern struct mdss_data_type *mdss_res;
diff --git a/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c
index 3bcacf945761..5a677dfe7484 100644
--- a/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c
+++ b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c
@@ -449,7 +449,7 @@ static struct attribute *dp_hdcp2p2_fs_attrs[] = {
};
static struct attribute_group dp_hdcp2p2_fs_attr_group = {
- .name = "dp_hdcp2p2",
+ .name = "hdcp2p2",
.attrs = dp_hdcp2p2_fs_attrs,
};
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index c66d9f3b3a65..4aa14422899f 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -629,6 +629,7 @@ struct buf_data {
char *string_buf; /* cmd buf as string, 3 bytes per number */
int sblen; /* string buffer length */
int sync_flag;
+ struct mutex dbg_mutex; /* mutex to synchronize read/write/flush */
};
struct mdss_dsi_debugfs_info {
@@ -718,6 +719,7 @@ static ssize_t mdss_dsi_cmd_read(struct file *file, char __user *buf,
char *bp;
ssize_t ret = 0;
+ mutex_lock(&pcmds->dbg_mutex);
if (*ppos == 0) {
kfree(pcmds->string_buf);
pcmds->string_buf = NULL;
@@ -736,6 +738,7 @@ static ssize_t mdss_dsi_cmd_read(struct file *file, char __user *buf,
buffer = kmalloc(bsize, GFP_KERNEL);
if (!buffer) {
pr_err("%s: Failed to allocate memory\n", __func__);
+ mutex_unlock(&pcmds->dbg_mutex);
return -ENOMEM;
}
@@ -771,10 +774,12 @@ static ssize_t mdss_dsi_cmd_read(struct file *file, char __user *buf,
kfree(pcmds->string_buf);
pcmds->string_buf = NULL;
pcmds->sblen = 0;
+ mutex_unlock(&pcmds->dbg_mutex);
return 0; /* the end */
}
ret = simple_read_from_buffer(buf, count, ppos, pcmds->string_buf,
pcmds->sblen);
+ mutex_unlock(&pcmds->dbg_mutex);
return ret;
}
@@ -786,6 +791,7 @@ static ssize_t mdss_dsi_cmd_write(struct file *file, const char __user *p,
int blen = 0;
char *string_buf;
+ mutex_lock(&pcmds->dbg_mutex);
if (*ppos == 0) {
kfree(pcmds->string_buf);
pcmds->string_buf = NULL;
@@ -797,6 +803,7 @@ static ssize_t mdss_dsi_cmd_write(struct file *file, const char __user *p,
string_buf = krealloc(pcmds->string_buf, blen + 1, GFP_KERNEL);
if (!string_buf) {
pr_err("%s: Failed to allocate memory\n", __func__);
+ mutex_unlock(&pcmds->dbg_mutex);
return -ENOMEM;
}
@@ -806,6 +813,7 @@ static ssize_t mdss_dsi_cmd_write(struct file *file, const char __user *p,
string_buf[blen] = '\0';
pcmds->string_buf = string_buf;
pcmds->sblen = blen;
+ mutex_unlock(&pcmds->dbg_mutex);
return ret;
}
@@ -816,8 +824,12 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id)
char *buf, *bufp, *bp;
struct dsi_ctrl_hdr *dchdr;
- if (!pcmds->string_buf)
+ mutex_lock(&pcmds->dbg_mutex);
+
+ if (!pcmds->string_buf) {
+ mutex_unlock(&pcmds->dbg_mutex);
return 0;
+ }
/*
* Allocate memory for command buffer
@@ -830,6 +842,7 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id)
kfree(pcmds->string_buf);
pcmds->string_buf = NULL;
pcmds->sblen = 0;
+ mutex_unlock(&pcmds->dbg_mutex);
return -ENOMEM;
}
@@ -854,6 +867,7 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id)
pr_err("%s: dtsi cmd=%x error, len=%d\n",
__func__, dchdr->dtype, dchdr->dlen);
kfree(buf);
+ mutex_unlock(&pcmds->dbg_mutex);
return -EINVAL;
}
bp += sizeof(*dchdr);
@@ -865,6 +879,7 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id)
pr_err("%s: dcs_cmd=%x len=%d error!\n", __func__,
bp[0], len);
kfree(buf);
+ mutex_unlock(&pcmds->dbg_mutex);
return -EINVAL;
}
@@ -877,6 +892,7 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id)
pcmds->buf = buf;
pcmds->blen = blen;
}
+ mutex_unlock(&pcmds->dbg_mutex);
return 0;
}
@@ -891,6 +907,7 @@ struct dentry *dsi_debugfs_create_dcs_cmd(const char *name, umode_t mode,
struct dentry *parent, struct buf_data *cmd,
struct dsi_panel_cmds ctrl_cmds)
{
+ mutex_init(&cmd->dbg_mutex);
cmd->buf = ctrl_cmds.buf;
cmd->blen = ctrl_cmds.blen;
cmd->string_buf = NULL;
diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c
index d57558a1b52d..734d3bee8fd0 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 CEIL(x, y) (((x) + ((y)-1)) / (y))
+
struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX];
struct mdss_hw mdss_dsi0_hw = {
@@ -1423,6 +1425,35 @@ void mdss_dsi_ctrl_setup(struct mdss_dsi_ctrl_pdata *ctrl)
mdss_dsi_op_mode_config(pdata->panel_info.mipi.mode, pdata);
}
+static int mdss_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ int ret = 0;
+ u32 v_total = 0, v_blank = 0, sleep_ms = 0, fps = 0;
+ struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info;
+
+ if (ctrl->panel_mode == DSI_CMD_MODE)
+ return ret;
+
+ if (ctrl->ctrl_state & CTRL_STATE_MDP_ACTIVE) {
+ mdss_dsi_wait4video_done(ctrl);
+ v_total = mdss_panel_get_vtotal(pinfo);
+ v_blank = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_front_porch +
+ pinfo->lcdc.v_pulse_width;
+ if (pinfo->dynamic_fps && pinfo->current_fps)
+ fps = pinfo->current_fps;
+ else
+ fps = pinfo->mipi.frame_rate;
+
+ sleep_ms = CEIL((v_blank * 1000), (v_total * fps)) + 1;
+ /* delay sleep_ms to skip BLLP */
+ if (sleep_ms)
+ udelay(sleep_ms * 1000);
+ ret = 1;
+ }
+
+ return ret;
+}
+
/**
* mdss_dsi_bta_status_check() - Check dsi panel status through bta check
* @ctrl_pdata: pointer to the dsi controller structure
@@ -1463,6 +1494,7 @@ 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);
+ mdss_dsi_wait4video_eng_busy(ctrl_pdata);
/* mask out overflow errors */
if (ignore_underflow)
mdss_dsi_set_reg(ctrl_pdata, 0x10c, 0x0f0000, 0x0f0000);
@@ -2348,15 +2380,20 @@ void mdss_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl)
/* DSI_INTL_CTRL */
data = MIPI_INP((ctrl->ctrl_base) + 0x0110);
data &= DSI_INTR_TOTAL_MASK;
- data |= DSI_INTR_VIDEO_DONE_MASK;
- MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data);
+ /* clear previous VIDEO_DONE interrupt */
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0110, (data | DSI_INTR_VIDEO_DONE));
+ wmb(); /* ensure interrupt is cleared */
spin_lock_irqsave(&ctrl->mdp_lock, flag);
reinit_completion(&ctrl->video_comp);
mdss_dsi_enable_irq(ctrl, DSI_VIDEO_TERM);
spin_unlock_irqrestore(&ctrl->mdp_lock, flag);
+ data |= DSI_INTR_VIDEO_DONE_MASK;
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data);
+ wmb(); /* ensure interrupt is enabled */
+
wait_for_completion_timeout(&ctrl->video_comp,
msecs_to_jiffies(VSYNC_PERIOD * 4));
@@ -2366,23 +2403,6 @@ void mdss_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl)
MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data);
}
-static int mdss_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl)
-{
- int ret = 0;
-
- if (ctrl->panel_mode == DSI_CMD_MODE)
- return ret;
-
- if (ctrl->ctrl_state & CTRL_STATE_MDP_ACTIVE) {
- mdss_dsi_wait4video_done(ctrl);
- /* delay 4 ms to skip BLLP */
- usleep_range(4000, 4000);
- ret = 1;
- }
-
- return ret;
-}
-
void mdss_dsi_cmd_mdp_start(struct mdss_dsi_ctrl_pdata *ctrl)
{
unsigned long flag;
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index f4c4c509410a..db27842eaccc 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -892,6 +892,12 @@ static ssize_t mdss_fb_get_persist_mode(struct device *dev,
return ret;
}
+static ssize_t mdss_fb_idle_pc_notify(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "idle power collapsed\n");
+}
+
static DEVICE_ATTR(msm_fb_type, S_IRUGO, mdss_fb_get_type, NULL);
static DEVICE_ATTR(msm_fb_split, S_IRUGO | S_IWUSR, mdss_fb_show_split,
mdss_fb_store_split);
@@ -912,6 +918,8 @@ static DEVICE_ATTR(measured_fps, S_IRUGO | S_IWUSR | S_IWGRP,
mdss_fb_get_fps_info, NULL);
static DEVICE_ATTR(msm_fb_persist_mode, S_IRUGO | S_IWUSR,
mdss_fb_get_persist_mode, mdss_fb_change_persist_mode);
+static DEVICE_ATTR(idle_power_collapse, S_IRUGO, mdss_fb_idle_pc_notify, NULL);
+
static struct attribute *mdss_fb_attrs[] = {
&dev_attr_msm_fb_type.attr,
&dev_attr_msm_fb_split.attr,
@@ -925,6 +933,7 @@ static struct attribute *mdss_fb_attrs[] = {
&dev_attr_msm_fb_dfps_mode.attr,
&dev_attr_measured_fps.attr,
&dev_attr_msm_fb_persist_mode.attr,
+ &dev_attr_idle_power_collapse.attr,
NULL,
};
@@ -4470,7 +4479,7 @@ err:
static int __mdss_fb_copy_destscaler_data(struct fb_info *info,
struct mdp_layer_commit *commit)
{
- int i;
+ int i = 0;
int ret = 0;
u32 data_size;
struct mdp_destination_scaler_data __user *ds_data_user;
@@ -4543,6 +4552,7 @@ static int __mdss_fb_copy_destscaler_data(struct fb_info *info,
data_size);
if (ret) {
pr_err("scale data copy from user failed\n");
+ kfree(scale_data);
goto err;
}
}
@@ -4552,7 +4562,7 @@ static int __mdss_fb_copy_destscaler_data(struct fb_info *info,
err:
if (ds_data) {
- for (i = 0; i < commit->commit_v1.dest_scaler_cnt; i++) {
+ for (i--; i >= 0; i--) {
scale_data = to_user_ptr(ds_data[i].scale);
kfree(scale_data);
}
@@ -5176,3 +5186,16 @@ void mdss_fb_calc_fps(struct msm_fb_data_type *mfd)
mfd->fps_info.frame_count = 0;
}
}
+
+void mdss_fb_idle_pc(struct msm_fb_data_type *mfd)
+{
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+
+ if (mdss_fb_is_power_off(mfd))
+ return;
+
+ if ((mfd->panel_info->type == MIPI_CMD_PANEL) && mdp5_data) {
+ pr_debug("Notify fb%d idle power collapsed\n", mfd->index);
+ sysfs_notify(&mfd->fbi->dev->kobj, NULL, "idle_power_collapse");
+ }
+}
diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h
index 111d7cfc7c9a..d64580a35775 100644
--- a/drivers/video/fbdev/msm/mdss_fb.h
+++ b/drivers/video/fbdev/msm/mdss_fb.h
@@ -468,4 +468,5 @@ void mdss_fb_report_panel_dead(struct msm_fb_data_type *mfd);
void mdss_panelinfo_to_fb_var(struct mdss_panel_info *pinfo,
struct fb_var_screeninfo *var);
void mdss_fb_calc_fps(struct msm_fb_data_type *mfd);
+void mdss_fb_idle_pc(struct msm_fb_data_type *mfd);
#endif /* MDSS_FB_H */
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index d8d11f21f3b2..3cadfa4ecef7 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -1173,6 +1173,31 @@ irqreturn_t mdss_mdp_isr(int irq, void *ptr)
return IRQ_HANDLED;
}
+static void mdss_mdp_cxipeak_vote(bool set_vote, unsigned long new_rate,
+ unsigned long prev_rate)
+{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ int ret = 0;
+
+ if (!mdata->mdss_cx_ipeak)
+ return;
+
+ /* fmax threshold for mdp in sdm660 is max MDP clk */
+ if (set_vote) {
+ if ((new_rate >= mdata->max_mdp_clk_rate) &&
+ (prev_rate < mdata->max_mdp_clk_rate))
+ ret = cx_ipeak_update(mdata->mdss_cx_ipeak, true);
+ } else {
+ if ((new_rate < mdata->max_mdp_clk_rate) &&
+ (prev_rate >= mdata->max_mdp_clk_rate))
+ ret = cx_ipeak_update(mdata->mdss_cx_ipeak, false);
+ }
+ if (ret) {
+ pr_err("cxipeak api fail ret:%d set_vote :%d new_rate:%lu prev_rate:%lu\n",
+ ret, (int)set_vote, new_rate, prev_rate);
+ }
+}
+
static int mdss_mdp_clk_update(u32 clk_idx, u32 enable)
{
int ret = -ENODEV;
@@ -1234,7 +1259,7 @@ void mdss_mdp_set_clk_rate(unsigned long rate, bool locked)
struct mdss_data_type *mdata = mdss_res;
unsigned long clk_rate;
struct clk *clk = mdss_mdp_get_clk(MDSS_CLK_MDP_CORE);
- unsigned long min_clk_rate;
+ unsigned long min_clk_rate, curr_clk_rate;
min_clk_rate = max(rate, mdata->perf_tune.min_mdp_clk);
@@ -1246,15 +1271,20 @@ void mdss_mdp_set_clk_rate(unsigned long rate, bool locked)
clk_rate = clk_round_rate(clk, min_clk_rate);
else
clk_rate = mdata->max_mdp_clk_rate;
+
+ curr_clk_rate = clk_get_rate(clk);
if (IS_ERR_VALUE(clk_rate)) {
pr_err("unable to round rate err=%ld\n", clk_rate);
- } else if (clk_rate != clk_get_rate(clk)) {
-
+ } else if (clk_rate != curr_clk_rate) {
+ mdss_mdp_cxipeak_vote(true, clk_rate, curr_clk_rate);
mdata->mdp_clk_rate = clk_rate;
- if (IS_ERR_VALUE(clk_set_rate(clk, clk_rate)))
+ if (IS_ERR_VALUE(clk_set_rate(clk, clk_rate))) {
pr_err("clk_set_rate failed\n");
- else
+ } else {
+ mdss_mdp_cxipeak_vote(false, clk_rate,
+ curr_clk_rate);
pr_debug("mdp clk rate=%lu\n", clk_rate);
+ }
}
if (!locked)
mutex_unlock(&mdp_clk_lock);
@@ -1355,6 +1385,68 @@ static inline void __mdss_mdp_reg_access_clk_enable(
}
}
+/*
+ * __mdss_mdp_clk_control - Overall MDSS clock control for power on/off
+ */
+static void __mdss_mdp_clk_control(struct mdss_data_type *mdata, bool enable)
+{
+ int rc = 0;
+ unsigned long flags;
+
+ if (enable) {
+ pm_runtime_get_sync(&mdata->pdev->dev);
+
+ mdss_update_reg_bus_vote(mdata->reg_bus_clt,
+ VOTE_INDEX_LOW);
+
+ rc = mdss_iommu_ctrl(1);
+ if (IS_ERR_VALUE(rc))
+ pr_err("IOMMU attach failed\n");
+
+ /* Active+Sleep */
+ msm_bus_scale_client_update_context(mdata->bus_hdl,
+ false, mdata->curr_bw_uc_idx);
+
+ spin_lock_irqsave(&mdp_lock, flags);
+ mdata->clk_ena = enable;
+ spin_unlock_irqrestore(&mdp_lock, flags);
+
+ mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, 1);
+ mdss_mdp_clk_update(MDSS_CLK_AHB, 1);
+ mdss_mdp_clk_update(MDSS_CLK_AXI, 1);
+ mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, 1);
+ mdss_mdp_clk_update(MDSS_CLK_MDP_LUT, 1);
+ if (mdata->vsync_ena)
+ mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, 1);
+ } else {
+ spin_lock_irqsave(&mdp_lock, flags);
+ mdata->clk_ena = enable;
+ spin_unlock_irqrestore(&mdp_lock, flags);
+
+ if (mdata->vsync_ena)
+ mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, 0);
+
+ mdss_mdp_clk_update(MDSS_CLK_MDP_LUT, 0);
+ mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, 0);
+ mdss_mdp_clk_update(MDSS_CLK_AXI, 0);
+ mdss_mdp_clk_update(MDSS_CLK_AHB, 0);
+ mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, 0);
+
+ /* release iommu control */
+ mdss_iommu_ctrl(0);
+
+ /* Active-Only */
+ msm_bus_scale_client_update_context(mdata->bus_hdl,
+ true, mdata->ao_bw_uc_idx);
+
+ mdss_update_reg_bus_vote(mdata->reg_bus_clt,
+ VOTE_INDEX_DISABLE);
+
+ pm_runtime_mark_last_busy(&mdata->pdev->dev);
+ pm_runtime_put_autosuspend(&mdata->pdev->dev);
+ }
+}
+
int __mdss_mdp_vbif_halt(struct mdss_data_type *mdata, bool is_nrt)
{
int rc = 0;
@@ -1646,9 +1738,7 @@ void mdss_mdp_clk_ctrl(int enable)
{
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
static int mdp_clk_cnt;
- unsigned long flags;
int changed = 0;
- int rc = 0;
mutex_lock(&mdp_clk_lock);
if (enable) {
@@ -1672,49 +1762,8 @@ void mdss_mdp_clk_ctrl(int enable)
__builtin_return_address(0), current->group_leader->comm,
mdata->bus_ref_cnt, changed, enable);
- if (changed) {
- if (enable) {
- pm_runtime_get_sync(&mdata->pdev->dev);
-
- mdss_update_reg_bus_vote(mdata->reg_bus_clt,
- VOTE_INDEX_LOW);
-
- rc = mdss_iommu_ctrl(1);
- if (IS_ERR_VALUE(rc))
- pr_err("IOMMU attach failed\n");
-
- /* Active+Sleep */
- msm_bus_scale_client_update_context(mdata->bus_hdl,
- false, mdata->curr_bw_uc_idx);
- }
-
- spin_lock_irqsave(&mdp_lock, flags);
- mdata->clk_ena = enable;
- spin_unlock_irqrestore(&mdp_lock, flags);
-
- mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, enable);
- mdss_mdp_clk_update(MDSS_CLK_AHB, enable);
- mdss_mdp_clk_update(MDSS_CLK_AXI, enable);
- mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, enable);
- mdss_mdp_clk_update(MDSS_CLK_MDP_LUT, enable);
- if (mdata->vsync_ena)
- mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, enable);
-
- if (!enable) {
- /* release iommu control */
- mdss_iommu_ctrl(0);
-
- /* Active-Only */
- msm_bus_scale_client_update_context(mdata->bus_hdl,
- true, mdata->ao_bw_uc_idx);
-
- mdss_update_reg_bus_vote(mdata->reg_bus_clt,
- VOTE_INDEX_DISABLE);
-
- pm_runtime_mark_last_busy(&mdata->pdev->dev);
- pm_runtime_put_autosuspend(&mdata->pdev->dev);
- }
- }
+ if (changed)
+ __mdss_mdp_clk_control(mdata, enable);
if (enable && changed)
mdss_mdp_idle_pc_restore();
@@ -4519,6 +4568,10 @@ static int mdss_mdp_parse_dt_misc(struct platform_device *pdev)
pr_debug("max pipe width not specified. Using default value\n");
mdata->max_pipe_width = DEFAULT_MDP_PIPE_WIDTH;
}
+
+ if (of_find_property(pdev->dev.of_node, "qcom,mdss-cx-ipeak", NULL))
+ mdata->mdss_cx_ipeak = cx_ipeak_register(pdev->dev.of_node,
+ "qcom,mdss-cx-ipeak");
return 0;
}
@@ -5092,6 +5145,22 @@ vreg_set_voltage_fail:
}
/**
+ * mdss_mdp_notify_idle_pc() - Notify fb driver of idle power collapse
+ * @mdata: MDP private data
+ *
+ * This function is called if there are active overlays.
+ */
+static void mdss_mdp_notify_idle_pc(struct mdss_data_type *mdata)
+{
+ int i;
+
+ for (i = 0; i < mdata->nctl; i++)
+ if ((mdata->ctl_off[i].ref_cnt) &&
+ !mdss_mdp_ctl_is_power_off(&mdata->ctl_off[i]))
+ mdss_fb_idle_pc(mdata->ctl_off[i].mfd);
+}
+
+/**
* mdss_mdp_footswitch_ctrl() - Disable/enable MDSS GDSC and CX/Batfet rails
* @mdata: MDP private data
* @on: 1 to turn on footswitch, 0 to turn off footswitch
@@ -5155,6 +5224,7 @@ static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on)
mdss_mdp_memory_retention_ctrl(MEM_RETAIN_ON,
PERIPH_RETAIN_OFF);
mdata->idle_pc = true;
+ mdss_mdp_notify_idle_pc(mdata);
pr_debug("idle pc. active overlays=%d\n",
active_cnt);
} else {
@@ -5487,6 +5557,8 @@ static int mdss_mdp_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
mdss_mdp_pp_term(&pdev->dev);
mdss_mdp_bus_scale_unregister(mdata);
+ if (mdata->mdss_cx_ipeak)
+ cx_ipeak_unregister(mdata->mdss_cx_ipeak);
mdss_debugfs_remove(mdata);
if (mdata->regulator_notif_register)
regulator_unregister_notifier(mdata->fs, &(mdata->gdsc_cb));
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index 5e98de043e55..36a866685f21 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -1842,7 +1842,7 @@ int mdss_mdp_calib_mode(struct msm_fb_data_type *mfd,
int mdss_mdp_pipe_handoff(struct mdss_mdp_pipe *pipe);
int mdss_mdp_smp_handoff(struct mdss_data_type *mdata);
struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(struct mdss_mdp_mixer *mixer,
- u32 type, struct mdss_mdp_pipe *left_blend_pipe);
+ u32 off, u32 type, struct mdss_mdp_pipe *left_blend_pipe);
struct mdss_mdp_pipe *mdss_mdp_pipe_get(u32 ndx,
enum mdss_mdp_pipe_rect rect_num);
struct mdss_mdp_pipe *mdss_mdp_pipe_search(struct mdss_data_type *mdata,
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index e258f258aeca..7b0207de101a 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -892,7 +892,7 @@ static u32 __calc_prefill_line_time_us(struct mdss_mdp_ctl *ctl)
static u32 __get_min_prefill_line_time_us(struct mdss_mdp_ctl *ctl)
{
- u32 vbp_min = 0;
+ u32 vbp_min = UINT_MAX;
int i;
struct mdss_data_type *mdata;
@@ -914,6 +914,9 @@ static u32 __get_min_prefill_line_time_us(struct mdss_mdp_ctl *ctl)
}
}
+ if (vbp_min == UINT_MAX)
+ vbp_min = 0;
+
return vbp_min;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
index 663d63092ebf..97be2fd728c8 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
@@ -449,7 +449,7 @@ static void mdss_mdp_video_avr_vtotal_setup(struct mdss_mdp_ctl *ctl,
if (sctl)
sctx = (struct mdss_mdp_video_ctx *)
sctl->intf_ctx[MASTER_CTX];
- mdss_mdp_video_timegen_flush(ctl, ctx);
+ mdss_mdp_video_timegen_flush(ctl, sctx);
MDSS_XLOG(pinfo->min_fps, pinfo->default_fps, avr_vtotal);
}
@@ -1671,7 +1671,7 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl,
struct mdss_panel_data *pdata;
int i, ret = 0, off;
u32 data, flush;
- struct mdss_mdp_video_ctx *ctx;
+ struct mdss_mdp_video_ctx *ctx, *sctx = NULL;
struct mdss_mdp_ctl *sctl;
if (!ctl) {
@@ -1695,10 +1695,13 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl,
pdata->panel_info.cont_splash_enabled = 0;
sctl = mdss_mdp_get_split_ctl(ctl);
- if (sctl)
+ if (sctl) {
sctl->panel_data->panel_info.cont_splash_enabled = 0;
- else if (ctl->panel_data->next && is_pingpong_split(ctl->mfd))
+ sctx = (struct mdss_mdp_video_ctx *) sctl->intf_ctx[MASTER_CTX];
+ } else if (ctl->panel_data->next && is_pingpong_split(ctl->mfd)) {
ctl->panel_data->next->panel_info.cont_splash_enabled = 0;
+ sctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[SLAVE_CTX];
+ }
if (!handoff) {
ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_BEGIN,
@@ -1724,6 +1727,8 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl,
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0);
+ mdss_mdp_video_timegen_flush(ctl, sctx);
+
/* wait for 1 VSYNC for the pipe to be unstaged */
msleep(20);
@@ -1896,7 +1901,6 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_format_params *fmt;
struct mdss_data_type *mdata = ctl->mdata;
struct dsc_desc *dsc = NULL;
- u32 hdmi_dp_core;
ctx->ctl = ctl;
ctx->intf_type = ctl->intf_type;
@@ -2033,10 +2037,19 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl,
mdp_video_write(ctx, MDSS_MDP_REG_INTF_PANEL_FORMAT, ctl->dst_format);
- hdmi_dp_core = (ctx->intf_type == MDSS_INTF_EDP) ? 1 : 0;
-
- writel_relaxed(hdmi_dp_core, mdata->mdp_base +
+ /* select HDMI or DP core usage */
+ switch (ctx->intf_type) {
+ case MDSS_INTF_EDP:
+ writel_relaxed(0x1, mdata->mdp_base +
+ MDSS_MDP_HDMI_DP_CORE_SELECT);
+ break;
+ case MDSS_INTF_HDMI:
+ writel_relaxed(0x0, mdata->mdp_base +
MDSS_MDP_HDMI_DP_CORE_SELECT);
+ break;
+ default:
+ break;
+ }
return 0;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index da08917d334b..9e295815da77 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -519,6 +519,102 @@ static int __mdss_mdp_validate_pxl_extn(struct mdss_mdp_pipe *pipe)
return 0;
}
+static int __mdss_mdp_validate_qseed3_cfg(struct mdss_mdp_pipe *pipe)
+{
+ int plane;
+
+ for (plane = 0; plane < MAX_PLANES; plane++) {
+ u32 hor_req_pixels, hor_fetch_pixels;
+ u32 hor_ov_fetch, vert_ov_fetch;
+ u32 vert_req_pixels, vert_fetch_pixels;
+ u32 src_w = DECIMATED_DIMENSION(pipe->src.w, pipe->horz_deci);
+ u32 src_h = DECIMATED_DIMENSION(pipe->src.h, pipe->vert_deci);
+
+ /*
+ * plane 1 and 2 are for chroma and are same. While configuring
+ * HW, programming only one of the chroma components is
+ * sufficient.
+ */
+ if (plane == 2)
+ continue;
+
+ /*
+ * For chroma plane, width is half for the following sub sampled
+ * formats. Except in case of decimation, where hardware avoids
+ * 1 line of decimation instead of downsampling.
+ */
+ if (plane == 1 && !pipe->horz_deci &&
+ ((pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_420) ||
+ (pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_H2V1))) {
+ src_w >>= 1;
+ }
+
+ if (plane == 1 && !pipe->vert_deci &&
+ ((pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_420) ||
+ (pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_H1V2)))
+ src_h >>= 1;
+
+ hor_req_pixels = pipe->scaler.num_ext_pxls_left[plane];
+
+ hor_fetch_pixels = src_w +
+ (pipe->scaler.left_ftch[plane] >> pipe->horz_deci) +
+ pipe->scaler.left_rpt[plane] +
+ (pipe->scaler.right_ftch[plane] >> pipe->horz_deci) +
+ pipe->scaler.right_rpt[plane];
+
+ hor_ov_fetch = src_w +
+ (pipe->scaler.left_ftch[plane] >> pipe->horz_deci) +
+ (pipe->scaler.right_ftch[plane] >> pipe->horz_deci);
+
+ vert_req_pixels = pipe->scaler.num_ext_pxls_top[plane];
+
+ vert_fetch_pixels = src_h +
+ (pipe->scaler.top_ftch[plane] >> pipe->vert_deci) +
+ pipe->scaler.top_rpt[plane] +
+ (pipe->scaler.btm_ftch[plane] >> pipe->vert_deci) +
+ pipe->scaler.btm_rpt[plane];
+
+ vert_ov_fetch = src_h +
+ (pipe->scaler.top_ftch[plane] >> pipe->vert_deci) +
+ (pipe->scaler.btm_ftch[plane] >> pipe->vert_deci);
+
+ if ((hor_req_pixels != hor_fetch_pixels) ||
+ (hor_ov_fetch > pipe->img_width) ||
+ (vert_req_pixels != vert_fetch_pixels) ||
+ (vert_ov_fetch > pipe->img_height)) {
+ pr_err("err: plane=%d h_req:%d h_fetch:%d v_req:%d v_fetch:%d src_img[%d %d] ov_fetch[%d %d]\n",
+
+ plane,
+ hor_req_pixels, hor_fetch_pixels,
+ vert_req_pixels, vert_fetch_pixels,
+ pipe->img_width, pipe->img_height,
+ hor_ov_fetch, vert_ov_fetch);
+ pipe->scaler.enable = 0;
+ return -EINVAL;
+ }
+ /*
+ * alpha plane can only be scaled using bilinear or pixel
+ * repeat/drop, src_width and src_height are only specified
+ * for Y and UV plane
+ */
+ if (plane != 3) {
+ if ((hor_req_pixels !=
+ pipe->scaler.src_width[plane]) ||
+ (vert_req_pixels !=
+ pipe->scaler.src_height[plane])) {
+ pr_err("roi_w[%d]=%d, scaler:[%d, %d], src_img:[%d, %d]\n",
+ plane, pipe->scaler.roi_w[plane],
+ pipe->scaler.src_width[plane],
+ pipe->scaler.src_height[plane],
+ pipe->img_width, pipe->img_height);
+ pipe->scaler.enable = 0;
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
int mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe)
{
@@ -528,8 +624,11 @@ int mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe)
mdata = mdss_mdp_get_mdata();
if (pipe->scaler.enable) {
- if (!test_bit(MDSS_CAPS_QSEED3, mdata->mdss_caps_map))
+ if (test_bit(MDSS_CAPS_QSEED3, mdata->mdss_caps_map))
+ rc = __mdss_mdp_validate_qseed3_cfg(pipe);
+ else
rc = __mdss_mdp_validate_pxl_extn(pipe);
+
return rc;
}
@@ -609,6 +708,7 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
bool is_vig_needed = false;
u32 left_lm_w = left_lm_w_from_mfd(mfd);
u32 flags = 0;
+ u32 off = 0;
if (mdp5_data->ctl == NULL)
return -ENODEV;
@@ -692,18 +792,29 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
break;
case PIPE_TYPE_AUTO:
default:
- if (req->flags & MDP_OV_PIPE_FORCE_DMA)
+ if (req->flags & MDP_OV_PIPE_FORCE_DMA) {
pipe_type = MDSS_MDP_PIPE_TYPE_DMA;
- else if (fmt->is_yuv ||
+ /*
+ * For paths using legacy API's for pipe
+ * allocation, use offset of 2 for allocating
+ * right pipe for pipe type DMA. This is
+ * because from SDM 3.x.x. onwards one DMA
+ * pipe has two instances for multirect.
+ */
+ off = (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT)
+ ? 2 : 0;
+ } else if (fmt->is_yuv ||
(req->flags & MDP_OV_PIPE_SHARE) ||
- is_vig_needed)
+ is_vig_needed) {
pipe_type = MDSS_MDP_PIPE_TYPE_VIG;
- else
+ } else {
pipe_type = MDSS_MDP_PIPE_TYPE_RGB;
+ }
break;
}
- pipe = mdss_mdp_pipe_alloc(mixer, pipe_type, left_blend_pipe);
+ pipe = mdss_mdp_pipe_alloc(mixer, off,
+ pipe_type, left_blend_pipe);
/* RGB pipes can be used instead of DMA */
if (IS_ERR_OR_NULL(pipe) &&
@@ -712,7 +823,7 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
pr_debug("giving RGB pipe for fb%d. flags:0x%x\n",
mfd->index, req->flags);
pipe_type = MDSS_MDP_PIPE_TYPE_RGB;
- pipe = mdss_mdp_pipe_alloc(mixer, pipe_type,
+ pipe = mdss_mdp_pipe_alloc(mixer, off, pipe_type,
left_blend_pipe);
}
@@ -723,7 +834,7 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
pr_debug("giving ViG pipe for fb%d. flags:0x%x\n",
mfd->index, req->flags);
pipe_type = MDSS_MDP_PIPE_TYPE_VIG;
- pipe = mdss_mdp_pipe_alloc(mixer, pipe_type,
+ pipe = mdss_mdp_pipe_alloc(mixer, off, pipe_type,
left_blend_pipe);
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c
index 965a6533dfcb..8d7bd60318ad 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c
@@ -1250,11 +1250,12 @@ cursor_done:
}
struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(struct mdss_mdp_mixer *mixer,
- u32 type, struct mdss_mdp_pipe *left_blend_pipe)
+ u32 off, u32 type, struct mdss_mdp_pipe *left_blend_pipe)
{
struct mdss_mdp_pipe *pipe;
+
mutex_lock(&mdss_mdp_sspp_lock);
- pipe = mdss_mdp_pipe_init(mixer, type, 0, left_blend_pipe);
+ pipe = mdss_mdp_pipe_init(mixer, type, off, left_blend_pipe);
mutex_unlock(&mdss_mdp_sspp_lock);
return pipe;
}
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index dc2b94142f53..a01a41a41269 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -548,7 +548,8 @@ void virtqueue_disable_cb(struct virtqueue *_vq)
if (!(vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT)) {
vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
- vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
+ if (!vq->event)
+ vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
}
}
@@ -580,7 +581,8 @@ unsigned virtqueue_enable_cb_prepare(struct virtqueue *_vq)
* entry. Always do both to keep code simple. */
if (vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
- vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
+ if (!vq->event)
+ vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
}
vring_used_event(&vq->vring) = cpu_to_virtio16(_vq->vdev, last_used_idx = vq->last_used_idx);
END_USE(vq);
@@ -648,10 +650,11 @@ bool virtqueue_enable_cb_delayed(struct virtqueue *_vq)
* more to do. */
/* Depending on the VIRTIO_RING_F_USED_EVENT_IDX feature, we need to
* either clear the flags bit or point the event index at the next
- * entry. Always do both to keep code simple. */
+ * entry. Always update the event index to keep code simple. */
if (vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
- vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
+ if (!vq->event)
+ vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
}
/* TODO: tune this threshold */
bufs = (u16)(vq->avail_idx_shadow - vq->last_used_idx) * 3 / 4;
@@ -770,7 +773,8 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,
/* No callback? Tell other side not to bother us. */
if (!callback) {
vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
- vq->vring.avail->flags = cpu_to_virtio16(vdev, vq->avail_flags_shadow);
+ if (!vq->event)
+ vq->vring.avail->flags = cpu_to_virtio16(vdev, vq->avail_flags_shadow);
}
/* Put everything in free lists. */
diff --git a/drivers/xen/xenbus/xenbus_dev_frontend.c b/drivers/xen/xenbus/xenbus_dev_frontend.c
index 531e76474983..0e0eb10f82a0 100644
--- a/drivers/xen/xenbus/xenbus_dev_frontend.c
+++ b/drivers/xen/xenbus/xenbus_dev_frontend.c
@@ -316,7 +316,7 @@ static int xenbus_write_transaction(unsigned msg_type,
rc = -ENOMEM;
goto out;
}
- } else {
+ } else if (msg_type == XS_TRANSACTION_END) {
list_for_each_entry(trans, &u->transactions, list)
if (trans->handle.id == u->u.msg.tx_id)
break;