summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/msm/glink_spi_xprt.txt44
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt3
-rw-r--r--Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt16
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt82
-rw-r--r--Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt36
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-vidc.txt2
-rw-r--r--Documentation/devicetree/bindings/opp/opp.txt132
-rw-r--r--Documentation/devicetree/bindings/pinctrl/img,pistachio-pinctrl.txt12
-rw-r--r--Documentation/devicetree/bindings/platform/msm/ipa.txt12
-rw-r--r--Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt9
-rwxr-xr-xDocumentation/devicetree/bindings/sound/qcom-audio-dev.txt39
-rw-r--r--Documentation/devicetree/bindings/sound/wcd-spi.txt27
-rw-r--r--Documentation/devicetree/bindings/sound/wcd_codec.txt8
-rw-r--r--Documentation/devicetree/bindings/thermal/tsens.txt17
-rw-r--r--Documentation/devicetree/bindings/usb/dwc3.txt9
-rw-r--r--Documentation/devicetree/bindings/usb/msm-phy.txt13
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.txt1
-rw-r--r--Documentation/kernel-parameters.txt2
-rw-r--r--MAINTAINERS96
-rw-r--r--Makefile2
-rw-r--r--arch/arc/include/asm/bitops.h15
-rw-r--r--arch/arc/include/asm/io.h18
-rw-r--r--arch/arm/boot/dts/at91-sama5d3_xplained.dts1
-rw-r--r--arch/arm/boot/dts/at91-sama5d4_xplained.dts1
-rw-r--r--arch/arm/boot/dts/qcom/batterydata-qrd-skuk-4v4-3000mah.dtsi78
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi6
-rw-r--r--arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi80
-rw-r--r--arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi80
-rw-r--r--arch/arm/boot/dts/qcom/msm-arm-smmu-cobalt.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi50
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-gpu.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8996.dtsi5
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-cdp.dts2
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi6
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mtp.dts2
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi58
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi28
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-v2-cdp.dts2
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-v2-mtp.dts2
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi15
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt.dtsi81
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-pinctrl.dtsi4
-rw-r--r--arch/arm/mach-s3c64xx/dev-audio.c41
-rw-r--r--arch/arm/mach-s3c64xx/include/mach/dma.h52
-rw-r--r--arch/arm/plat-samsung/devs.c11
-rw-r--r--arch/arm64/Kconfig.platforms1
-rw-r--r--arch/arm64/configs/msm-perf_defconfig2
-rw-r--r--arch/arm64/configs/msm_defconfig2
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig8
-rw-r--r--arch/arm64/configs/msmcortex_defconfig8
-rw-r--r--arch/arm64/include/asm/opcodes.h4
-rw-r--r--arch/arm64/kernel/debug-monitors.c21
-rw-r--r--arch/arm64/kernel/vdso/vdso.S3
-rw-r--r--arch/arm64/mm/dma-mapping.c24
-rw-r--r--arch/ia64/include/asm/io.h1
-rw-r--r--arch/mips/alchemy/devboards/db1000.c18
-rw-r--r--arch/mips/alchemy/devboards/db1550.c4
-rw-r--r--arch/mips/kernel/unaligned.c51
-rw-r--r--arch/parisc/include/asm/uaccess.h1
-rw-r--r--arch/parisc/kernel/asm-offsets.c1
-rw-r--r--arch/parisc/kernel/parisc_ksyms.c10
-rw-r--r--arch/parisc/kernel/traps.c3
-rw-r--r--arch/parisc/lib/fixup.S6
-rw-r--r--arch/parisc/mm/fault.c1
-rw-r--r--arch/powerpc/mm/hugetlbpage.c4
-rw-r--r--arch/s390/include/asm/pci.h2
-rw-r--r--arch/s390/kernel/entry.S106
-rw-r--r--arch/s390/kernel/head64.S2
-rw-r--r--arch/s390/kernel/setup.c1
-rw-r--r--arch/s390/pci/pci.c5
-rw-r--r--arch/sh/mm/kmap.c2
-rw-r--r--arch/um/configs/x86_64_defconfig1
-rw-r--r--arch/um/configs/x86_64_um_defconfig1
-rw-r--r--arch/um/drivers/mconsole_kern.c2
-rw-r--r--arch/x86/Kconfig27
-rw-r--r--arch/x86/entry/common.c23
-rw-r--r--arch/x86/include/asm/apic.h2
-rw-r--r--arch/x86/include/asm/hw_irq.h1
-rw-r--r--arch/x86/include/asm/kvm_host.h2
-rw-r--r--arch/x86/include/asm/microcode.h26
-rw-r--r--arch/x86/include/asm/pci_x86.h2
-rw-r--r--arch/x86/include/asm/perf_event.h1
-rw-r--r--arch/x86/include/asm/xen/hypervisor.h2
-rw-r--r--arch/x86/kernel/apic/vector.c88
-rw-r--r--arch/x86/kernel/cpu/microcode/intel.c38
-rw-r--r--arch/x86/kernel/cpu/perf_event.c13
-rw-r--r--arch/x86/kernel/cpu/perf_event.h3
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel.c27
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_ds.c24
-rw-r--r--arch/x86/kernel/cpu/perf_event_knc.c4
-rw-r--r--arch/x86/kernel/ioport.c12
-rw-r--r--arch/x86/kernel/process_64.c12
-rw-r--r--arch/x86/kvm/i8254.c12
-rw-r--r--arch/x86/kvm/vmx.c16
-rw-r--r--arch/x86/kvm/x86.c21
-rw-r--r--arch/x86/mm/tlb.c12
-rw-r--r--arch/x86/pci/common.c26
-rw-r--r--arch/x86/pci/fixup.c7
-rw-r--r--arch/x86/pci/intel_mid_pci.c9
-rw-r--r--arch/x86/pci/irq.c23
-rw-r--r--arch/x86/xen/enlighten.c2
-rw-r--r--arch/xtensa/kernel/head.S2
-rw-r--r--arch/xtensa/mm/cache.c8
-rw-r--r--arch/xtensa/platforms/iss/console.c10
-rw-r--r--block/blk-core.c2
-rw-r--r--crypto/asymmetric_keys/pkcs7_trust.c2
-rw-r--r--crypto/asymmetric_keys/x509_cert_parser.c8
-rw-r--r--crypto/keywrap.c4
-rw-r--r--drivers/acpi/pci_irq.c17
-rw-r--r--drivers/acpi/resource.c14
-rw-r--r--drivers/acpi/sleep.c1
-rw-r--r--drivers/android/binder.c170
-rw-r--r--drivers/base/power/opp/Makefile1
-rw-r--r--drivers/base/power/opp/core.c1239
-rw-r--r--drivers/base/power/opp/cpu.c25
-rw-r--r--drivers/base/power/opp/debugfs.c218
-rw-r--r--drivers/base/power/opp/opp.h107
-rw-r--r--drivers/block/brd.c2
-rw-r--r--drivers/block/mtip32xx/mtip32xx.c267
-rw-r--r--drivers/block/mtip32xx/mtip32xx.h11
-rw-r--r--drivers/block/rbd.c6
-rw-r--r--drivers/bluetooth/ath3k.c8
-rw-r--r--drivers/bluetooth/btusb.c4
-rw-r--r--drivers/char/tpm/tpm-chip.c14
-rw-r--r--drivers/char/tpm/tpm_crb.c4
-rw-r--r--drivers/char/tpm/tpm_eventlog.c14
-rw-r--r--drivers/clk/bcm/clk-bcm2835.c12
-rw-r--r--drivers/clk/msm/clock-gpu-cobalt.c12
-rw-r--r--drivers/clk/msm/clock-mmss-cobalt.c4
-rw-r--r--drivers/clk/rockchip/clk-rk3188.c1
-rw-r--r--drivers/clk/rockchip/clk-rk3368.c48
-rw-r--r--drivers/cpufreq/cpufreq-dt.c309
-rw-r--r--drivers/crypto/atmel-aes.c4
-rw-r--r--drivers/crypto/atmel-sha.c4
-rw-r--r--drivers/crypto/atmel-tdes.c4
-rw-r--r--drivers/crypto/ccp/ccp-crypto-aes-cmac.c36
-rw-r--r--drivers/crypto/ccp/ccp-crypto-sha.c40
-rw-r--r--drivers/crypto/ccp/ccp-crypto.h22
-rw-r--r--drivers/crypto/marvell/cesa.c2
-rw-r--r--drivers/crypto/msm/qcedev.c8
-rw-r--r--drivers/crypto/ux500/cryp/cryp_core.c4
-rw-r--r--drivers/crypto/ux500/hash/hash_core.c4
-rw-r--r--drivers/devfreq/arm-memlat-mon.c12
-rw-r--r--drivers/edac/amd64_edac.c2
-rw-r--r--drivers/edac/sb_edac.c26
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c16
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c23
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c4
-rw-r--r--drivers/gpu/drm/drm_dp_helper.c27
-rw-r--r--drivers/gpu/drm/radeon/atombios_encoders.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_atpx_handler.c8
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c8
-rw-r--r--drivers/gpu/drm/radeon/si_dpm.c6
-rw-r--r--drivers/gpu/drm/udl/udl_fb.c2
-rw-r--r--drivers/gpu/drm/udl/udl_gem.c2
-rw-r--r--drivers/gpu/msm/adreno.c16
-rw-r--r--drivers/gpu/msm/adreno.h2
-rw-r--r--drivers/gpu/msm/adreno_a4xx_snapshot.c10
-rw-r--r--drivers/gpu/msm/adreno_a5xx.c31
-rw-r--r--drivers/gpu/msm/adreno_a5xx.h2
-rw-r--r--drivers/gpu/msm/adreno_a5xx_snapshot.c174
-rw-r--r--drivers/gpu/msm/adreno_dispatch.c41
-rw-r--r--drivers/gpu/msm/adreno_snapshot.c3
-rw-r--r--drivers/gpu/msm/kgsl.c28
-rw-r--r--drivers/gpu/msm/kgsl.h9
-rw-r--r--drivers/gpu/msm/kgsl_debugfs.c2
-rw-r--r--drivers/gpu/msm/kgsl_device.h5
-rw-r--r--drivers/gpu/msm/kgsl_events.c10
-rw-r--r--drivers/gpu/msm/kgsl_iommu.c50
-rw-r--r--drivers/gpu/msm/kgsl_pool.c25
-rw-r--r--drivers/gpu/msm/kgsl_pool.h1
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.c31
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.h2
-rw-r--r--drivers/gpu/msm/kgsl_pwrscale.c3
-rw-r--r--drivers/gpu/msm/kgsl_sharedmem.c137
-rw-r--r--drivers/gpu/msm/kgsl_sharedmem.h60
-rw-r--r--drivers/hid/hid-core.c8
-rw-r--r--drivers/hid/hid-multitouch.c5
-rw-r--r--drivers/hid/i2c-hid/i2c-hid.c16
-rw-r--r--drivers/hid/usbhid/hid-core.c73
-rw-r--r--drivers/hid/wacom_wac.c11
-rw-r--r--drivers/hwmon/max1111.c6
-rw-r--r--drivers/hwtracing/coresight/coresight-tpdm.c4
-rw-r--r--drivers/idle/intel_idle.c108
-rw-r--r--drivers/iio/accel/bmc150-accel-core.c7
-rw-r--r--drivers/iio/adc/qcom-rradc.c317
-rw-r--r--drivers/iio/gyro/bmg160_core.c9
-rw-r--r--drivers/iio/magnetometer/st_magn.h1
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_multicast.c24
-rw-r--r--drivers/infiniband/ulp/isert/ib_isert.c122
-rw-r--r--drivers/infiniband/ulp/isert/ib_isert.h2
-rw-r--r--drivers/infiniband/ulp/srpt/ib_srpt.c59
-rw-r--r--drivers/input/misc/ati_remote2.c36
-rw-r--r--drivers/input/misc/ims-pcu.c4
-rw-r--r--drivers/input/misc/powermate.c3
-rw-r--r--drivers/input/mouse/synaptics.c5
-rw-r--r--drivers/input/touchscreen/ft5x06_ts.c12
-rw-r--r--drivers/input/touchscreen/gt9xx/goodix_tool.c (renamed from input/touchscreen/gt9xx/goodix_tool.c)0
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.c1805
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.h270
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx_firmware.h (renamed from input/touchscreen/gt9xx/gt9xx_firmware.h)0
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx_update.c (renamed from input/touchscreen/gt9xx/gt9xx_update.c)0
-rw-r--r--drivers/input/touchscreen/it7258_ts_i2c.c2017
-rw-r--r--drivers/input/touchscreen/msg21xx_ts.c (renamed from kernel/drivers/input/touchscreen/msg21xx/msg21xx_ts.c)0
-rw-r--r--drivers/iommu/arm-smmu.c14
-rw-r--r--drivers/iommu/iommu.c3
-rw-r--r--drivers/irqchip/irq-gic-v3.c3
-rw-r--r--drivers/leds/leds-qpnp-flash-v2.c404
-rw-r--r--drivers/leds/leds-qpnp-flash.c44
-rw-r--r--drivers/leds/leds.h16
-rw-r--r--drivers/md/bcache/super.c46
-rw-r--r--drivers/md/dm-cache-metadata.c98
-rw-r--r--drivers/md/dm-cache-metadata.h4
-rw-r--r--drivers/md/dm-cache-target.c12
-rw-r--r--drivers/md/dm-snap.c9
-rw-r--r--drivers/md/dm-table.c36
-rw-r--r--drivers/md/dm-thin-metadata.c5
-rw-r--r--drivers/md/dm.c15
-rw-r--r--drivers/md/multipath.c4
-rw-r--r--drivers/md/raid1.c7
-rw-r--r--drivers/md/raid10.c7
-rw-r--r--drivers/md/raid5.c51
-rw-r--r--drivers/md/raid5.h4
-rw-r--r--drivers/media/i2c/adv7511.c21
-rw-r--r--drivers/media/pci/bt8xx/bttv-driver.c26
-rw-r--r--drivers/media/pci/saa7134/saa7134-video.c18
-rw-r--r--drivers/media/platform/coda/coda-bit.c2
-rw-r--r--drivers/media/platform/coda/coda-common.c10
-rw-r--r--drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c3
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c20
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c58
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c212
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h5
-rw-r--r--drivers/media/platform/msm/vidc/hfi_packetization.c29
-rw-r--r--drivers/media/platform/msm/vidc/msm_vdec.c90
-rw-r--r--drivers/media/platform/msm/vidc/msm_venc.c109
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_common.c68
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_debug.c2
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_res_parse.c5
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_resources.h1
-rw-r--r--drivers/media/platform/msm/vidc/venus_hfi.c34
-rw-r--r--drivers/media/platform/msm/vidc/vidc_hfi.h3
-rw-r--r--drivers/media/platform/msm/vidc/vidc_hfi_api.h12
-rw-r--r--drivers/media/platform/msm/vidc/vidc_hfi_helper.h12
-rw-r--r--drivers/media/platform/vsp1/vsp1_sru.c1
-rw-r--r--drivers/media/usb/au0828/au0828-core.c2
-rw-r--r--drivers/media/usb/au0828/au0828-input.c4
-rw-r--r--drivers/media/usb/au0828/au0828-video.c63
-rw-r--r--drivers/media/usb/au0828/au0828.h9
-rw-r--r--drivers/media/usb/pwc/pwc-if.c6
-rw-r--r--drivers/media/usb/usbvision/usbvision-video.c16
-rw-r--r--drivers/media/v4l2-core/v4l2-compat-ioctl32.c21
-rw-r--r--drivers/mfd/wcd934x-regmap.c13
-rw-r--r--drivers/mfd/wcd934x-tables.c35
-rw-r--r--drivers/mfd/wcd9xxx-core.c2
-rw-r--r--drivers/mfd/wcd9xxx-utils.c10
-rw-r--r--drivers/misc/Kconfig4
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/hdcp.c4
-rw-r--r--drivers/misc/mei/bus.c9
-rw-r--r--drivers/misc/qseecom.c104
-rw-r--r--drivers/misc/uid_stat.c156
-rw-r--r--drivers/mmc/card/block.c24
-rw-r--r--drivers/mmc/host/mmc_spi.c6
-rw-r--r--drivers/mmc/host/sdhci-pci-core.c25
-rw-r--r--drivers/mmc/host/sdhci-pci.h3
-rw-r--r--drivers/mmc/host/sdhci.c23
-rw-r--r--drivers/mmc/host/sh_mmcif.c84
-rw-r--r--drivers/mtd/onenand/onenand_base.c3
-rw-r--r--drivers/net/bonding/bond_main.c65
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c4
-rw-r--r--drivers/net/ethernet/jme.c3
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/resource_tracker.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c9
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic.h3
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c24
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c3
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge_main.c11
-rw-r--r--drivers/net/ethernet/qualcomm/qca_spi.c2
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.c10
-rw-r--r--drivers/net/ethernet/rocker/rocker.c10
-rw-r--r--drivers/net/irda/irtty-sir.c10
-rw-r--r--drivers/net/macvtap.c9
-rw-r--r--drivers/net/ppp/ppp_generic.c36
-rw-r--r--drivers/net/rionet.c4
-rw-r--r--drivers/net/tun.c12
-rw-r--r--drivers/net/usb/cdc_ether.c8
-rw-r--r--drivers/net/usb/cdc_ncm.c6
-rw-r--r--drivers/net/usb/qmi_wwan.c7
-rw-r--r--drivers/net/usb/usbnet.c7
-rw-r--r--drivers/net/vrf.c13
-rw-r--r--drivers/net/vxlan.c4
-rw-r--r--drivers/net/wan/farsync.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom.c7
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw.c4
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h3
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c2
-rw-r--r--drivers/nfc/nq-nci.c48
-rw-r--r--drivers/nvdimm/bus.c10
-rw-r--r--drivers/nvdimm/pfn_devs.c2
-rw-r--r--drivers/pci/probe.c14
-rw-r--r--drivers/pcmcia/db1xxx_ss.c11
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm2835.c2
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx.c17
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-nomadik.c2
-rw-r--r--drivers/pinctrl/pinctrl-pistachio.c24
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msmfalcon.c932
-rw-r--r--drivers/pinctrl/sh-pfc/core.c4
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sunxi.c17
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sunxi.h21
-rw-r--r--drivers/platform/msm/gsi/gsi.c29
-rw-r--r--drivers/platform/msm/gsi/gsi_dbg.c6
-rw-r--r--drivers/platform/msm/ipa/ipa_api.c48
-rw-r--r--drivers/platform/msm/ipa/ipa_api.h9
-rw-r--r--drivers/platform/msm/ipa/ipa_clients/Makefile4
-rw-r--r--drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c597
-rw-r--r--drivers/platform/msm/ipa/ipa_common_i.h6
-rw-r--r--drivers/platform/msm/ipa/ipa_uc_offload_common_i.h24
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/Makefile2
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa.c48
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_client.c114
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c240
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_dp.c32
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c22
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_i.h158
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c10
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_rt.c4
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c438
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h514
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c46
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_utils.c34
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c10
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/Makefile2
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa.c93
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c116
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c22
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_i.h207
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c5
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_rt.c4
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c410
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_uc_offload_i.h580
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c46
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_utils.c25
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c5
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c10
-rw-r--r--drivers/platform/x86/ideapad-laptop.c14
-rw-r--r--drivers/power/power_supply_sysfs.c2
-rw-r--r--drivers/power/qcom-charger/qpnp-qnovo.c15
-rw-r--r--drivers/power/qcom-charger/qpnp-smb2.c190
-rw-r--r--drivers/power/qcom-charger/smb-lib.c162
-rw-r--r--drivers/power/qcom-charger/smb-lib.h23
-rw-r--r--drivers/power/qcom-charger/smb-reg.h3
-rw-r--r--drivers/power/qcom-charger/smb138x-charger.c6
-rw-r--r--drivers/regulator/core.c22
-rw-r--r--drivers/regulator/cpr3-mmss-regulator.c64
-rw-r--r--drivers/scsi/aacraid/aacraid.h2
-rw-r--r--drivers/scsi/aacraid/commsup.c37
-rw-r--r--drivers/scsi/aacraid/linit.c12
-rw-r--r--drivers/scsi/aacraid/src.c30
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.c1
-rw-r--r--drivers/scsi/be2iscsi/be_main.c1
-rw-r--r--drivers/scsi/ipr.c10
-rw-r--r--drivers/scsi/scsi_common.c12
-rw-r--r--drivers/scsi/sd.c30
-rw-r--r--drivers/scsi/sd.h7
-rw-r--r--drivers/scsi/sg.c3
-rw-r--r--drivers/scsi/storvsc_drv.c5
-rw-r--r--drivers/scsi/ufs/ufshcd.c3
-rw-r--r--drivers/slimbus/slim-msm-ngd.c69
-rw-r--r--drivers/slimbus/slim-msm.h2
-rw-r--r--drivers/soc/qcom/Kconfig19
-rw-r--r--drivers/soc/qcom/Makefile2
-rw-r--r--drivers/soc/qcom/core_ctl_helper.c22
-rw-r--r--drivers/soc/qcom/gladiator_erp_v2.c2
-rw-r--r--drivers/soc/qcom/glink.c60
-rw-r--r--drivers/soc/qcom/glink_core_if.h7
-rw-r--r--drivers/soc/qcom/glink_spi_xprt.c2192
-rw-r--r--drivers/soc/qcom/glink_xprt_if.h5
-rw-r--r--drivers/soc/qcom/icnss.c8
-rw-r--r--drivers/soc/qcom/irq-helper.c180
-rw-r--r--drivers/soc/qcom/peripheral-loader.c29
-rw-r--r--drivers/soc/qcom/pil-msa.c38
-rw-r--r--drivers/soc/qcom/pil-q6v5-mss.c8
-rw-r--r--drivers/soc/qcom/pil-q6v5.c28
-rw-r--r--drivers/soc/qcom/socinfo.c12
-rw-r--r--drivers/soc/qcom/subsys-pil-tz.c22
-rw-r--r--drivers/soc/qcom/subsystem_restart.c3
-rw-r--r--drivers/spmi/spmi-pmic-arb.c87
-rwxr-xr-xdrivers/staging/android/ion/ion.c5
-rw-r--r--drivers/staging/android/ion/ion_test.c4
-rw-r--r--drivers/staging/comedi/drivers/ni_mio_common.c12
-rw-r--r--drivers/staging/comedi/drivers/ni_tiocmd.c2
-rw-r--r--drivers/target/target_core_transport.c2
-rw-r--r--drivers/thermal/msm-tsens.c60
-rw-r--r--drivers/thermal/thermal_core.c55
-rw-r--r--drivers/tty/serial/8250/8250_port.c18
-rw-r--r--drivers/tty/serial/msm_serial_hs.c261
-rw-r--r--drivers/usb/class/cdc-acm.c3
-rw-r--r--drivers/usb/core/driver.c6
-rw-r--r--drivers/usb/core/hub.c24
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c28
-rw-r--r--drivers/usb/gadget/configfs.c3
-rw-r--r--drivers/usb/gadget/function/f_cdev.c10
-rw-r--r--drivers/usb/gadget/function/f_fs.c385
-rw-r--r--drivers/usb/gadget/function/f_gsi.c199
-rw-r--r--drivers/usb/misc/iowarrior.c6
-rw-r--r--drivers/usb/phy/phy-msm-qusb-v2.c70
-rw-r--r--drivers/usb/phy/phy-msm-qusb.c68
-rw-r--r--drivers/usb/renesas_usbhs/fifo.c4
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c6
-rw-r--r--drivers/usb/serial/cp210x.c1
-rw-r--r--drivers/usb/serial/cypress_m8.c11
-rw-r--r--drivers/usb/serial/digi_acceleport.c19
-rw-r--r--drivers/usb/serial/ftdi_sio.c4
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h8
-rw-r--r--drivers/usb/serial/mct_u232.c9
-rw-r--r--drivers/usb/serial/option.c2
-rw-r--r--drivers/usb/storage/uas.c23
-rw-r--r--drivers/usb/storage/unusual_uas.h7
-rw-r--r--drivers/usb/storage/usb.c5
-rw-r--r--drivers/video/fbdev/msm/mdss.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_compat_utils.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c19
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c7
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c11
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.c14
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.h6
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c1
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.c225
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.h26
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c3
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h12
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c20
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_hwio.h5
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_video.c120
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_layer.c71
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.c1
-rw-r--r--drivers/virtio/virtio_pci_modern.c11
-rw-r--r--drivers/watchdog/rc32434_wdt.c2
-rw-r--r--drivers/xen/events/events_base.c28
-rw-r--r--fs/btrfs/file.c2
-rw-r--r--fs/btrfs/tree-log.c137
-rw-r--r--fs/coredump.c30
-rw-r--r--fs/dcache.c5
-rw-r--r--fs/ext4/ext4.h23
-rw-r--r--fs/ext4/move_extent.c11
-rw-r--r--fs/ext4/super.c47
-rw-r--r--fs/fhandle.c2
-rw-r--r--fs/fs-writeback.c37
-rw-r--r--fs/fuse/cuse.c4
-rw-r--r--fs/fuse/file.c33
-rw-r--r--fs/fuse/fuse_i.h9
-rw-r--r--fs/jbd2/journal.c17
-rw-r--r--fs/nfs/dir.c6
-rw-r--r--fs/nfs/inode.c2
-rw-r--r--fs/nfs/nfs4file.c4
-rw-r--r--fs/nfsd/nfs4proc.c1
-rw-r--r--fs/nfsd/nfs4xdr.c13
-rw-r--r--fs/ocfs2/dlm/dlmconvert.c24
-rw-r--r--fs/ocfs2/dlm/dlmrecovery.c1
-rw-r--r--fs/open.c6
-rw-r--r--fs/overlayfs/super.c33
-rw-r--r--fs/proc_namespace.c2
-rw-r--r--fs/quota/dquot.c3
-rw-r--r--fs/splice.c3
-rw-r--r--fs/xfs/xfs_attr_list.c19
-rw-r--r--include/asm-generic/bitops/lock.h14
-rw-r--r--include/asm-generic/preempt.h4
-rw-r--r--include/linux/cgroup-defs.h3
-rw-r--r--include/linux/compiler-gcc.h2
-rw-r--r--include/linux/dcache.h10
-rw-r--r--include/linux/device-mapper.h2
-rw-r--r--include/linux/filter.h4
-rw-r--r--include/linux/fs.h12
-rw-r--r--include/linux/hdcp_qseecom.h6
-rw-r--r--include/linux/if_bridge.h4
-rw-r--r--include/linux/input/ft5x06_ts.h4
-rw-r--r--include/linux/ipa.h13
-rw-r--r--include/linux/ipa_uc_offload.h259
-rw-r--r--include/linux/kernel.h6
-rw-r--r--include/linux/leds-qpnp-flash-v2.h18
-rw-r--r--include/linux/leds-qpnp-flash.h23
-rw-r--r--include/linux/mfd/wcd934x/registers.h1
-rwxr-xr-xinclude/linux/mfd/wcd9xxx/pdata.h1
-rw-r--r--include/linux/netdevice.h22
-rw-r--r--include/linux/pci.h18
-rw-r--r--include/linux/platform_data/asoc-s3c.h4
-rw-r--r--include/linux/pm_opp.h49
-rw-r--r--include/linux/power_supply.h2
-rw-r--r--include/linux/skbuff.h41
-rw-r--r--include/linux/thermal.h2
-rw-r--r--include/linux/tty.h2
-rw-r--r--include/linux/uid_stat.h29
-rw-r--r--include/linux/usb_usual.h2
-rw-r--r--include/net/activity_stats.h25
-rw-r--r--include/net/bonding.h1
-rw-r--r--include/net/cfg80211.h28
-rw-r--r--include/soc/qcom/camera2.h1
-rw-r--r--include/soc/qcom/icnss.h8
-rw-r--r--include/soc/qcom/irq-helper.h20
-rw-r--r--include/soc/qcom/socinfo.h4
-rw-r--r--include/sound/wcd-dsp-mgr.h118
-rw-r--r--include/sound/wcd-spi.h57
-rw-r--r--include/uapi/linux/msm_ipa.h5
-rw-r--r--include/uapi/linux/msm_mdp_ext.h9
-rw-r--r--include/uapi/linux/nl80211.h30
-rw-r--r--include/uapi/linux/v4l2-controls.h26
-rw-r--r--include/uapi/media/msm_vidc.h123
-rw-r--r--input/touchscreen/gt9xx/gt9xx.c1810
-rw-r--r--input/touchscreen/gt9xx/gt9xx.h241
-rw-r--r--kernel/bpf/helpers.c2
-rw-r--r--kernel/cgroup.c20
-rw-r--r--kernel/drivers/input/touchscreen/synaptics_fw_update.c521
-rw-r--r--kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c282
-rw-r--r--kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h6
-rw-r--r--kernel/events/core.c20
-rw-r--r--kernel/power/hibernate.c1
-rw-r--r--kernel/sched/core.c1
-rw-r--r--kernel/sched/cputime.c14
-rw-r--r--kernel/sched/sched.h13
-rw-r--r--kernel/sysctl_binary.c2
-rw-r--r--kernel/trace/trace.c5
-rw-r--r--kernel/trace/trace_irqsoff.c8
-rw-r--r--kernel/trace/trace_printk.c3
-rw-r--r--kernel/watchdog.c9
-rw-r--r--mm/memcontrol.c44
-rw-r--r--mm/page_alloc.c3
-rw-r--r--mm/page_isolation.c8
-rw-r--r--net/Kconfig8
-rw-r--r--net/Makefile1
-rw-r--r--net/activity_stats.c115
-rw-r--r--net/ax25/ax25_ip.c15
-rw-r--r--net/bluetooth/mgmt.c4
-rw-r--r--net/bridge/br_stp.c13
-rw-r--r--net/core/filter.c33
-rw-r--r--net/core/rtnetlink.c1
-rw-r--r--net/core/skbuff.c22
-rw-r--r--net/dccp/ipv4.c2
-rw-r--r--net/ipv4/devinet.c4
-rw-r--r--net/ipv4/fib_frontend.c20
-rw-r--r--net/ipv4/igmp.c3
-rw-r--r--net/ipv4/ip_output.c5
-rw-r--r--net/ipv4/ip_tunnel.c3
-rw-r--r--net/ipv4/netfilter/nf_nat_masquerade_ipv4.c12
-rw-r--r--net/ipv4/tcp.c14
-rw-r--r--net/ipv4/tcp_ipv4.c2
-rw-r--r--net/ipv4/tcp_metrics.c2
-rw-r--r--net/ipv4/tcp_minisocks.c3
-rw-r--r--net/ipv4/udp.c12
-rw-r--r--net/ipv4/udp_tunnel.c2
-rw-r--r--net/ipv6/exthdrs_core.c6
-rw-r--r--net/ipv6/ip6_gre.c2
-rw-r--r--net/ipv6/ip6_output.c8
-rw-r--r--net/ipv6/ip6_tunnel.c4
-rw-r--r--net/ipv6/mcast.c3
-rw-r--r--net/ipv6/udp.c10
-rw-r--r--net/l2tp/l2tp_ip.c8
-rw-r--r--net/l2tp/l2tp_ip6.c8
-rw-r--r--net/mac80211/ibss.c22
-rw-r--r--net/mac80211/iface.c3
-rw-r--r--net/mac80211/rx.c5
-rw-r--r--net/mac80211/sta_info.c37
-rw-r--r--net/mpls/af_mpls.c3
-rw-r--r--net/netfilter/xt_qtaguid.c4
-rw-r--r--net/packet/af_packet.c37
-rw-r--r--net/sctp/ipv6.c2
-rw-r--r--net/socket.c38
-rw-r--r--net/tipc/socket.c33
-rw-r--r--net/wireless/core.c30
-rw-r--r--net/wireless/nl80211.c43
-rw-r--r--scripts/coccinelle/iterators/use_after_iter.cocci2
-rw-r--r--scripts/kconfig/Makefile4
-rwxr-xr-xscripts/package/mkspec8
-rw-r--r--sound/core/pcm_lib.c2
-rw-r--r--sound/core/timer.c4
-rw-r--r--sound/pci/hda/patch_cirrus.c8
-rw-r--r--sound/pci/hda/patch_conexant.c7
-rw-r--r--sound/pci/hda/patch_hdmi.c4
-rw-r--r--sound/pci/hda/patch_realtek.c61
-rw-r--r--sound/pci/intel8x0.c1
-rwxr-xr-xsound/soc/codecs/Kconfig7
-rw-r--r--sound/soc/codecs/Makefile5
-rwxr-xr-xsound/soc/codecs/audio-ext-clk.c7
-rw-r--r--sound/soc/codecs/wcd-dsp-mgr.c803
-rw-r--r--sound/soc/codecs/wcd-dsp-utils.c221
-rw-r--r--sound/soc/codecs/wcd-dsp-utils.h51
-rw-r--r--sound/soc/codecs/wcd-spi-registers.h43
-rw-r--r--sound/soc/codecs/wcd-spi.c1250
-rw-r--r--[-rwxr-xr-x]sound/soc/codecs/wcd9335.c231
-rw-r--r--sound/soc/codecs/wcd9335.h2
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x.c12
-rw-r--r--sound/soc/msm/msm-cpe-lsm.c100
-rw-r--r--sound/soc/msm/msm-dai-fe.c2
-rw-r--r--sound/soc/msm/msm8996.c56
-rw-r--r--sound/soc/msm/msmcobalt.c77
-rw-r--r--sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c1
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c61
-rw-r--r--sound/soc/msm/qdsp6v2/q6afe.c2
-rw-r--r--sound/soc/samsung/ac97.c26
-rw-r--r--sound/soc/samsung/dma.h2
-rw-r--r--sound/soc/samsung/dmaengine.c4
-rw-r--r--sound/soc/samsung/i2s.c26
-rw-r--r--sound/soc/samsung/pcm.c20
-rw-r--r--sound/soc/samsung/s3c2412-i2s.c4
-rw-r--r--sound/soc/samsung/s3c24xx-i2s.c4
-rw-r--r--sound/soc/samsung/spdif.c10
-rw-r--r--sound/usb/clock.c2
-rw-r--r--sound/usb/endpoint.c3
-rw-r--r--sound/usb/mixer_maps.c14
-rw-r--r--sound/usb/mixer_quirks.c4
-rw-r--r--sound/usb/pcm.c2
-rw-r--r--sound/usb/quirks.c29
-rw-r--r--sound/usb/stream.c6
-rw-r--r--tools/hv/Makefile2
-rw-r--r--tools/perf/util/parse-events.c6
-rw-r--r--tools/perf/util/pmu.c15
-rw-r--r--tools/perf/util/setup.py4
-rw-r--r--virt/kvm/kvm_main.c21
628 files changed, 23682 insertions, 7821 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/glink_spi_xprt.txt b/Documentation/devicetree/bindings/arm/msm/glink_spi_xprt.txt
new file mode 100644
index 000000000000..0a78eb6b91fd
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/glink_spi_xprt.txt
@@ -0,0 +1,44 @@
+Qualcomm Technologies, Inc. G-link SPI Transport
+
+Required properties:
+-compatible : should be "qcom,glink-spi-xprt".
+-label : the name of the subsystem this link connects to.
+
+Optional properties:
+-qcom,remote-fifo-config: Reference to the FIFO configuratio in the remote
+ processor.
+-qcom,qos-config: Reference to the qos configuration elements.It depends on
+ ramp-time.
+-qcom,ramp-time: Worst case time in microseconds to transition to this power
+ state. Power states are numbered by array index position.
+
+Example:
+
+ glink_spi_xprt_wdsp: qcom,glink-spi-xprt-wdsp {
+ compatible = "qcom,glink-spi-xprt";
+ label = "wdsp";
+ qcom,remote-fifo-config = <&glink_fifo_wdsp>;
+ qcom,qos-config = <&glink_qos_wdsp>;
+ qcom,ramp-time = <0x10>,
+ <0x20>,
+ <0x30>,
+ <0x40>;
+ };
+
+ glink_fifo_wdsp: qcom,glink-fifo-config-wdsp {
+ compatible = "qcom,glink-fifo-config";
+ qcom,out-read-idx-reg = <0x12000>;
+ qcom,out-write-idx-reg = <0x12004>;
+ qcom,in-read-idx-reg = <0x1200C>;
+ qcom,in-write-idx-reg = <0x12010>;
+ };
+
+ glink_qos_wdsp: qcom,glink-qos-config-wdsp {
+ compatible = "qcom,glink-qos-config";
+ qcom,flow-info = <0x80 0x0>,
+ <0x70 0x1>,
+ <0x60 0x2>,
+ <0x50 0x3>;
+ qcom,mtu-size = <0x800>;
+ qcom,tput-stats-cycle = <0xa>;
+ };
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
index 9fc942cc627d..90abf0305319 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
@@ -152,7 +152,8 @@ Optional properties:
"dfps_immediate_porch_mode_vfp" = FPS change request is
implemented immediately by changing panel vertical
front porch values.
-- qcom,min-refresh-rate: Minimum refresh rate supported by the panel.
+- qcom,min-refresh-rate: Minimum refresh rate supported by the panel. Used in
+ adaptive variable refresh(AVR) to compute new avr vtotal
- qcom,max-refresh-rate: Maximum refresh rate supported by the panel. If max refresh
rate is not specified, then the frame rate of the panel in
qcom,mdss-dsi-panel-framerate is used.
diff --git a/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt b/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt
index 8a64ccbd66c0..721a4f72563e 100644
--- a/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt
+++ b/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt
@@ -36,13 +36,10 @@ Main node properties:
Definition: Must be one. For details about IIO bindings see:
Documentation/devicetree/bindings/iio/iio-bindings.txt
-Channel subnode properties:
-
-- channel:
- Usage: required
- Value type: <u32>
- Definition: ADC channel number.
- See drivers/iio/adc/qcom-rradc.c for channels within rradc_channel_id
+IIO client nodes need to specify the RRADC channel number while requesting ADC reads.
+The channel list supported by the RRADC driver is available in the enum rradc_channel_id
+located at at drivers/iio/adc/qcom-rradc.c. Clients can use this index from the enum
+as the channel number while requesting ADC reads.
Example:
@@ -53,11 +50,6 @@ Example:
#address-cells = <1>;
#size-cells = <0>;
#io-channel-cells = <1>;
-
- /* Channel node */
- batt_id {
- channel = <0>;
- };
};
/* IIO client node */
diff --git a/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt b/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt
new file mode 100644
index 000000000000..3b2c272d7378
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt
@@ -0,0 +1,82 @@
+ITE Tech. touch controller
+
+The ITE Tech. touch controller is connected to host processor
+via i2c. The controller generates interrupts when the user
+touches the panel. The host controller is expected to read
+the touch coordinates over i2c and pass the coordinates to
+the rest of the system.
+
+Required properties:
+
+ - compatible : should be "ite,it7260_ts"
+ - reg : i2c slave address of the device
+ - interrupt-parent : parent of interrupt
+ - interrupts : touch sample interrupt to indicate presence or release
+ of fingers on the panel.
+ - ite,irq-gpio : irq gpio which is to provide interrupts to host,
+ same as "interrupts" node. It will also
+ contain active low or active high information.
+ - ite,reset-gpio : reset gpio to control the reset of chip
+ - ite,reset-delay : reset delay for controller (ms), default 20
+
+Optional properties:
+ - avdd-supply : Analog power supply needed to power device
+ - vdd-supply : Power source required to pull up i2c bus
+ - ite,wakeup : boolean, use this to support touch-to-wake feature.
+ - ite,palm-detect-en : boolean, use this to send palm-detect-keycode when
+ palm is detected.
+ - ite,fw-name : Specify firmware file name in /etc/firmware
+ - ite,cfg-name : Specify config file name in /etc/firmware
+ - ite,panel-coords : touch panel min x, min y, max x and
+ max y resolution
+ - ite,display-coords : display min x, min y, max x and
+ max y resolution
+ - ite,num-fingers : number of fingers supported by the touch controller
+ - pinctrl-names : This should be defined if a target uses pinctrl framework.
+ See "pinctrl" in Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt.
+ It should specify the names of the configs that pinctrl can install in driver.
+ Following are the pinctrl configs that can be installed:
+ "pmx_ts_active" : Active configuration of pins, this should specify active
+ config defined in pin groups of interrupt and reset gpio.
+ "pmx_ts_suspend" : Disabled configuration of pins, this should specify sleep
+ config defined in pin groups of interrupt and reset gpio.
+ "pmx_ts_release" : Release configuration of pins, this should specify
+ release config defined in pin groups of interrupt and reset gpio.
+ - ite,low-reset : boolean, if the controller needs low-state of the reset gpio while
+ initializing, and reset gpio should be made as high-state to reset the
+ controller. It means the controller needs "active-high" reset gpio.
+ - ite,avdd-lpm-cur : avdd lpm current value(mA) in suspend state.
+
+Required properties palm-detect-en feature:
+ - ite,palm-detect-keycode : The keycode that is required to be sent when
+ palm is detected by the ITE tech driver.
+
+Example:
+ i2c@f9927000 {
+ it7260@46 {
+ compatible = "ite,it7260_ts";
+ reg = <0x46>;
+ interrupt-parent = <&msmgpio>;
+ /* pins used by touchscreen */
+ pinctrl-names = "pmx_ts_active","pmx_ts_suspend","pmx_ts_release";
+ pinctrl-0 = <&ts_int_active &ts_reset_active>;
+ pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
+ pinctrl-2 = <&ts_release>
+ interrupts = <17 0x2>;
+ avdd-supply = <&pm8226_l19>;
+ vdd-supply = <&pm8226_lvs1>;
+ ite,reset-gpio = <&msmgpio 16 0x00>;
+ ite,irq-gpio = <&msmgpio 17 0x2008>;
+ ite,wakeup;
+ ite,palm-detect-en;
+ ite,palm-detect-keycode = <142>;
+ ite,fw-name = "ite7260_fw.bin";
+ ite,cfg-name = "ite7260_cfg.bin";
+ ite,panel-coords = <0 0 320 320>;
+ ite,display-coords = <0 0 320 320>;
+ ite,num-fingers = <2>;
+ ite,reset-delay = <20>;
+ ite,low-reset;
+ ite,vdd-lpm-cur = <3000>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
index 1102cb5d4f6f..51bcb07cbb6e 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
@@ -13,9 +13,25 @@ Required properties:
- reg : Base address and size for flash LED modules
Optional properties:
+- interrupts : Specifies the interrupts associated with flash-led.
+- interrupt-names : Specify the interrupt names associated with interrupts.
- qcom,hdrm-auto-mode : Boolean type to select headroom auto mode enabled or not
-- qcom,isc-delay : Integer type to specify short circuit delay. Valid values are 32, 64,
- 128, 192. Unit is us.
+- qcom,isc-delay-us : Integer type to specify short circuit delay. Valid values are 32, 64,
+ 128, 192. Unit is uS.
+- qcom,warmup-delay-us : Integer type to specify warm up delay. Valid values are 32, 64,
+ 128, 192. Unit is uS.
+- qcom,short-circuit-det : Boolean property which enables short circuit fault detection.
+- qcom,open-circuit-det : Boolean property which enables open circuit fault detection.
+- qcom,vph-droop-det : Boolean property which enables VPH droop detection.
+- qcom,vph-droop-hys-mv : Integer property to specify VPH droop hysteresis. It is only used if
+ qcom,vph-droop-det is specified. Valid values are 0, 25, 50 and 75.
+ Unit is mV.
+- qcom,vph-droop-thresh-mv : Integer property to specify VPH droop threshold. It is only used if
+ qcom,vph-droop-det is specified. Valid values are
+ 2500 to 3200 with step size of 100. Unit is mV.
+- qcom,vph-droop-debounce-us : Integer property to specify VPH droop debounce time. It is only used
+ if qcom,vph-droop-det is specified. Valid values are 0, 8, 16 and 26.
+ Unit is uS.
- qcom,hw-strobe-option : Integer type to specify hardware strobe option. Based on the specified
value, additional GPIO configuration may be required to provide strobing
support. Supported values are:
@@ -93,6 +109,22 @@ Example:
status = "okay";
reg = <0xd300 0x100>;
label = "flash";
+ interrupts = <0x3 0xd3 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x3 0xd3 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x3 0xd3 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x3 0xd3 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x3 0xd3 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0x3 0xd3 0x5 IRQ_TYPE_EDGE_BOTH>,
+ <0x3 0xd3 0x6 IRQ_TYPE_EDGE_BOTH>,
+ <0x3 0xd3 0x7 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "led-fault-irq",
+ "mitigation-irq",
+ "flash-timer-exp-irq",
+ "all-ramp-down-done-irq",
+ "all-ramp-up-done-irq",
+ "led3-ramp-up-done-irq",
+ "led2-ramp-up-done-irq",
+ "led1-ramp-up-done-irq";
qcom,hdrm-auto-mode;
qcom,isc-delay = <192>;
diff --git a/Documentation/devicetree/bindings/media/video/msm-vidc.txt b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
index 1c90f98c2954..b1869803d345 100644
--- a/Documentation/devicetree/bindings/media/video/msm-vidc.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
@@ -130,6 +130,8 @@ value is typically max(latencies of every cluster at all power levels) + 1
- qcom,max-secure-instances = An int containing max number of concurrent secure
instances supported, accounting for venus and system wide limitations like
memory, performance etc.
+- qcom,debug-timeout = A bool indicating that FW errors such as SYS_ERROR,
+ SESSION_ERROR and timeouts will be treated as Fatal.
[Second level nodes]
Context Banks
diff --git a/Documentation/devicetree/bindings/opp/opp.txt b/Documentation/devicetree/bindings/opp/opp.txt
index 0cb44dc21f97..601256fe8c0d 100644
--- a/Documentation/devicetree/bindings/opp/opp.txt
+++ b/Documentation/devicetree/bindings/opp/opp.txt
@@ -45,21 +45,10 @@ Devices supporting OPPs must set their "operating-points-v2" property with
phandle to a OPP table in their DT node. The OPP core will use this phandle to
find the operating points for the device.
-Devices may want to choose OPP tables at runtime and so can provide a list of
-phandles here. But only *one* of them should be chosen at runtime. This must be
-accompanied by a corresponding "operating-points-names" property, to uniquely
-identify the OPP tables.
-
If required, this can be extended for SoC vendor specfic bindings. Such bindings
should be documented as Documentation/devicetree/bindings/power/<vendor>-opp.txt
and should have a compatible description like: "operating-points-v2-<vendor>".
-Optional properties:
-- operating-points-names: Names of OPP tables (required if multiple OPP
- tables are present), to uniquely identify them. The same list must be present
- for all the CPUs which are sharing clock/voltage rails and hence the OPP
- tables.
-
* OPP Table Node
This describes the OPPs belonging to a device. This node can have following
@@ -100,6 +89,14 @@ Optional properties:
Entries for multiple regulators must be present in the same order as
regulators are specified in device's DT node.
+- opp-microvolt-<name>: Named opp-microvolt property. This is exactly similar to
+ the above opp-microvolt property, but allows multiple voltage ranges to be
+ provided for the same OPP. At runtime, the platform can pick a <name> and
+ matching opp-microvolt-<name> property will be enabled for all OPPs. If the
+ platform doesn't pick a specific <name> or the <name> doesn't match with any
+ opp-microvolt-<name> properties, then opp-microvolt property shall be used, if
+ present.
+
- opp-microamp: The maximum current drawn by the device in microamperes
considering system specific parameters (such as transients, process, aging,
maximum operating temperature range etc.) as necessary. This may be used to
@@ -112,6 +109,9 @@ Optional properties:
for few regulators, then this should be marked as zero for them. If it isn't
required for any regulator, then this property need not be present.
+- opp-microamp-<name>: Named opp-microamp property. Similar to
+ opp-microvolt-<name> property, but for microamp instead.
+
- clock-latency-ns: Specifies the maximum possible transition latency (in
nanoseconds) for switching to this OPP from any other OPP.
@@ -123,6 +123,26 @@ Optional properties:
- opp-suspend: Marks the OPP to be used during device suspend. Only one OPP in
the table should have this.
+- opp-supported-hw: This enables us to select only a subset of OPPs from the
+ larger OPP table, based on what version of the hardware we are running on. We
+ still can't have multiple nodes with the same opp-hz value in OPP table.
+
+ It's an user defined array containing a hierarchy of hardware version numbers,
+ supported by the OPP. For example: a platform with hierarchy of three levels
+ of versions (A, B and C), this field should be like <X Y Z>, where X
+ corresponds to Version hierarchy A, Y corresponds to version hierarchy B and Z
+ corresponds to version hierarchy C.
+
+ Each level of hierarchy is represented by a 32 bit value, and so there can be
+ only 32 different supported version per hierarchy. i.e. 1 bit per version. A
+ value of 0xFFFFFFFF will enable the OPP for all versions for that hierarchy
+ level. And a value of 0x00000000 will disable the OPP completely, and so we
+ never want that to happen.
+
+ If 32 values aren't sufficient for a version hierarchy, than that version
+ hierarchy can be contained in multiple 32 bit values. i.e. <X Y Z1 Z2> in the
+ above example, Z1 & Z2 refer to the version hierarchy Z.
+
- status: Marks the node enabled/disabled.
Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together.
@@ -157,20 +177,20 @@ Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together.
compatible = "operating-points-v2";
opp-shared;
- opp00 {
+ opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <970000 975000 985000>;
opp-microamp = <70000>;
clock-latency-ns = <300000>;
opp-suspend;
};
- opp01 {
+ opp@1100000000 {
opp-hz = /bits/ 64 <1100000000>;
opp-microvolt = <980000 1000000 1010000>;
opp-microamp = <80000>;
clock-latency-ns = <310000>;
};
- opp02 {
+ opp@1200000000 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <1025000>;
clock-latency-ns = <290000>;
@@ -236,20 +256,20 @@ independently.
* independently.
*/
- opp00 {
+ opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <970000 975000 985000>;
opp-microamp = <70000>;
clock-latency-ns = <300000>;
opp-suspend;
};
- opp01 {
+ opp@1100000000 {
opp-hz = /bits/ 64 <1100000000>;
opp-microvolt = <980000 1000000 1010000>;
opp-microamp = <80000>;
clock-latency-ns = <310000>;
};
- opp02 {
+ opp@1200000000 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <1025000>;
opp-microamp = <90000;
@@ -312,20 +332,20 @@ DVFS state together.
compatible = "operating-points-v2";
opp-shared;
- opp00 {
+ opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <970000 975000 985000>;
opp-microamp = <70000>;
clock-latency-ns = <300000>;
opp-suspend;
};
- opp01 {
+ opp@1100000000 {
opp-hz = /bits/ 64 <1100000000>;
opp-microvolt = <980000 1000000 1010000>;
opp-microamp = <80000>;
clock-latency-ns = <310000>;
};
- opp02 {
+ opp@1200000000 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <1025000>;
opp-microamp = <90000>;
@@ -338,20 +358,20 @@ DVFS state together.
compatible = "operating-points-v2";
opp-shared;
- opp10 {
+ opp@1300000000 {
opp-hz = /bits/ 64 <1300000000>;
opp-microvolt = <1045000 1050000 1055000>;
opp-microamp = <95000>;
clock-latency-ns = <400000>;
opp-suspend;
};
- opp11 {
+ opp@1400000000 {
opp-hz = /bits/ 64 <1400000000>;
opp-microvolt = <1075000>;
opp-microamp = <100000>;
clock-latency-ns = <400000>;
};
- opp12 {
+ opp@1500000000 {
opp-hz = /bits/ 64 <1500000000>;
opp-microvolt = <1010000 1100000 1110000>;
opp-microamp = <95000>;
@@ -378,7 +398,7 @@ Example 4: Handling multiple regulators
compatible = "operating-points-v2";
opp-shared;
- opp00 {
+ opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <970000>, /* Supply 0 */
<960000>, /* Supply 1 */
@@ -391,7 +411,7 @@ Example 4: Handling multiple regulators
/* OR */
- opp00 {
+ opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <970000 975000 985000>, /* Supply 0 */
<960000 965000 975000>, /* Supply 1 */
@@ -404,7 +424,7 @@ Example 4: Handling multiple regulators
/* OR */
- opp00 {
+ opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <970000 975000 985000>, /* Supply 0 */
<960000 965000 975000>, /* Supply 1 */
@@ -417,7 +437,8 @@ Example 4: Handling multiple regulators
};
};
-Example 5: Multiple OPP tables
+Example 5: opp-supported-hw
+(example: three level hierarchy of versions: cuts, substrate and process)
/ {
cpus {
@@ -426,40 +447,73 @@ Example 5: Multiple OPP tables
...
cpu-supply = <&cpu_supply>
- operating-points-v2 = <&cpu0_opp_table_slow>, <&cpu0_opp_table_fast>;
- operating-points-names = "slow", "fast";
+ operating-points-v2 = <&cpu0_opp_table_slow>;
};
};
- cpu0_opp_table_slow: opp_table_slow {
+ opp_table {
compatible = "operating-points-v2";
status = "okay";
opp-shared;
- opp00 {
+ opp@600000000 {
+ /*
+ * Supports all substrate and process versions for 0xF
+ * cuts, i.e. only first four cuts.
+ */
+ opp-supported-hw = <0xF 0xFFFFFFFF 0xFFFFFFFF>
opp-hz = /bits/ 64 <600000000>;
+ opp-microvolt = <900000 915000 925000>;
...
};
- opp01 {
+ opp@800000000 {
+ /*
+ * Supports:
+ * - cuts: only one, 6th cut (represented by 6th bit).
+ * - substrate: supports 16 different substrate versions
+ * - process: supports 9 different process versions
+ */
+ opp-supported-hw = <0x20 0xff0000ff 0x0000f4f0>
opp-hz = /bits/ 64 <800000000>;
+ opp-microvolt = <900000 915000 925000>;
...
};
};
+};
+
+Example 6: opp-microvolt-<name>, opp-microamp-<name>:
+(example: device with two possible microvolt ranges: slow and fast)
- cpu0_opp_table_fast: opp_table_fast {
+/ {
+ cpus {
+ cpu@0 {
+ compatible = "arm,cortex-a7";
+ ...
+
+ operating-points-v2 = <&cpu0_opp_table>;
+ };
+ };
+
+ cpu0_opp_table: opp_table0 {
compatible = "operating-points-v2";
- status = "okay";
opp-shared;
- opp10 {
+ opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
- ...
+ opp-microvolt-slow = <900000 915000 925000>;
+ opp-microvolt-fast = <970000 975000 985000>;
+ opp-microamp-slow = <70000>;
+ opp-microamp-fast = <71000>;
};
- opp11 {
- opp-hz = /bits/ 64 <1100000000>;
- ...
+ opp@1200000000 {
+ opp-hz = /bits/ 64 <1200000000>;
+ opp-microvolt-slow = <900000 915000 925000>, /* Supply vcc0 */
+ <910000 925000 935000>; /* Supply vcc1 */
+ opp-microvolt-fast = <970000 975000 985000>, /* Supply vcc0 */
+ <960000 965000 975000>; /* Supply vcc1 */
+ opp-microamp = <70000>; /* Will be used for both slow/fast */
};
};
};
diff --git a/Documentation/devicetree/bindings/pinctrl/img,pistachio-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/img,pistachio-pinctrl.txt
index 08a4a32c8eb0..0326154c7925 100644
--- a/Documentation/devicetree/bindings/pinctrl/img,pistachio-pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/img,pistachio-pinctrl.txt
@@ -134,12 +134,12 @@ mfio80 ddr_debug, mips_trace_data, mips_debug
mfio81 dreq0, mips_trace_data, eth_debug
mfio82 dreq1, mips_trace_data, eth_debug
mfio83 mips_pll_lock, mips_trace_data, usb_debug
-mfio84 sys_pll_lock, mips_trace_data, usb_debug
-mfio85 wifi_pll_lock, mips_trace_data, sdhost_debug
-mfio86 bt_pll_lock, mips_trace_data, sdhost_debug
-mfio87 rpu_v_pll_lock, dreq2, socif_debug
-mfio88 rpu_l_pll_lock, dreq3, socif_debug
-mfio89 audio_pll_lock, dreq4, dreq5
+mfio84 audio_pll_lock, mips_trace_data, usb_debug
+mfio85 rpu_v_pll_lock, mips_trace_data, sdhost_debug
+mfio86 rpu_l_pll_lock, mips_trace_data, sdhost_debug
+mfio87 sys_pll_lock, dreq2, socif_debug
+mfio88 wifi_pll_lock, dreq3, socif_debug
+mfio89 bt_pll_lock, dreq4, dreq5
tck
trstn
tdi
diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt
index 222b4fe66697..0aba9aef566a 100644
--- a/Documentation/devicetree/bindings/platform/msm/ipa.txt
+++ b/Documentation/devicetree/bindings/platform/msm/ipa.txt
@@ -76,6 +76,10 @@ memory allocation over a PCIe bridge
- qcom,tethered-flow-control: Boolean context flag to indicate whether
apps based flow control is needed for tethered
call.
+- qcom,rx-polling-sleep-ms: Receive Polling Timeout in millisecond,
+ default is 1 millisecond.
+- qcom,ipa-polling-iteration: IPA Polling Iteration Count,default is 40.
+
IPA pipe sub nodes (A2 static pipes configurations):
-label: two labels are supported, a2-to-ipa and ipa-to-a2 which
@@ -119,6 +123,9 @@ IPA SMMU sub nodes
- qcom,iova-mapping: specifies the start address and size of iova space.
+- qcom,additional-mapping: specifies any addtional mapping needed for this
+ context bank. The format is <iova pa size>
+
IPA SMP2P sub nodes
-compatible: "qcom,smp2pgpio-map-ipa-1-out" - represents the out gpio from
@@ -195,7 +202,10 @@ qcom,ipa@fd4c0000 {
ipa_smmu_ap: ipa_smmu_ap {
compatible = "qcom,ipa-smmu-ap-cb";
iommus = <&anoc2_smmu 0x30>;
- qcom,iova-mapping = <0x10000000 0x40000000>;
+ qcom,iova-mapping = <0x20000000 0x40000000>;
+ qcom,additional-mapping =
+ /* modem tables in IMEM */
+ <0x146BD000 0x146BD000 0x2000>;
};
ipa_smmu_wlan: ipa_smmu_wlan {
diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt
index 368f0b8c9525..5a415d04fbcf 100644
--- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt
+++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt
@@ -56,6 +56,15 @@ Charger specific properties:
Definition: Specifies the DC input power limit in micro-watts.
If the value is not present, 8W is used as default.
+- qcom,thermal-mitigation
+ Usage: optional
+ Value type: Array of <u32>
+ Definition: Array of fast charge current limit values for
+ different system thermal mitigation levels.
+ This should be a flat array that denotes the
+ maximum charge current in mA for each thermal
+ level.
+
=============================================
Second Level Nodes - SMB2 Charger Peripherals
=============================================
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index b30b6b87add6..7fa51e394f5c 100755
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -351,6 +351,9 @@ Required properties:
Required properties:
- compatible : "qcom,msm-cpe-lsm"
+ - qcom,msm-cpe-lsm-id : lsm afe port ID. CPE lsm driver uses
+ this property to find out the input afe port ID. Currently
+ only supported values are 1 and 3.
* wcd_us_euro_gpio
@@ -1367,12 +1370,12 @@ Example:
qcom,mbhc-audio-jack-type = "6-pole-jack";
asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
<&loopback>, <&compress>, <&hostless>,
- <&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>;
+ <&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>, <&cpe3>;
asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", "msm-pcm-dsp.2",
"msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback",
"msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe",
"msm-lsm-client", "msm-pcm-routing", "msm-cpe-lsm",
- "msm-compr-dsp";
+ "msm-compr-dsp", "msm-cpe-lsm.3";
asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>, <&dai_mi2s>,
<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
<&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
@@ -2139,3 +2142,35 @@ Example:
asoc-codec-names = "msm-stub-codec.1";
qcom,wsa-max-devs = <0>;
};
+
+* WCD DSP manager driver
+
+Required properties:
+- compatible : "qcom,wcd-dsp-mgr"
+- qcom,wdsp-components : This is phandle list containing the references to the
+ components of the manager driver. Manager driver will
+ register to component framework with these phandles.
+- qcom,img-filename : String property to provide the dsp image file name that is
+ to be read from file system and downloaded to dsp memory
+Optional properties:
+- qcom,wdsp-cmpnt-dev-name : Property that manager driver will parse, but defined
+ in the child's DT entry that is given to manager driver
+ with phandle. This property will be used by the manager
+ driver in case the manager driver cannot match child's
+ of_node pointer to registered phandle.
+
+Example:
+
+ qcom,wcd-dsp-mgr {
+ compatible = "qcom,wcd-dsp-mgr";
+ qcom,wdsp-components = <&wcd934x_cdc 0>,
+ <&wcd_spi_0 1>,
+ <&glink_spi 2>;
+ qcom,img-filename = "cpe_9340";
+ };
+
+Example of child node that would have qcom,wdsp-cmpnt-dev-name property
+
+ wcd934x_cdc: tavil_codec {
+ qcom,wdsp-cmpnt-dev-name = "tavil_codec";
+ };
diff --git a/Documentation/devicetree/bindings/sound/wcd-spi.txt b/Documentation/devicetree/bindings/sound/wcd-spi.txt
new file mode 100644
index 000000000000..6ea513b026c2
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/wcd-spi.txt
@@ -0,0 +1,27 @@
+WCD audio codec SPI driver support
+
+* wcd_spi
+
+The wcd_spi device is child device node to the master contoller's device node
+and will have properties that the SPI framework or the master controller driver
+expects. The properties listed here are specific to wcd-spi driver.
+
+Required properties:
+
+- compatible : "qcom,wcd-spi-v2"
+
+- qcom,mem-base-addr : Defines the memory base address from the SPI
+ memory map. This will be used as an offset to read
+ and write memory.
+
+Example:
+
+&spi_10 {
+ status = "ok";
+ wcd_spi_0: wcd_spi@0 {
+ compatible = "qcom,wcd-spi-v2";
+ reg = <0>;
+ spi-max-frequency = <24000000>;
+ qcom,mem-base-addr = <0x100000>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/sound/wcd_codec.txt b/Documentation/devicetree/bindings/sound/wcd_codec.txt
index d585595f21c6..e37e7c2bea3c 100644
--- a/Documentation/devicetree/bindings/sound/wcd_codec.txt
+++ b/Documentation/devicetree/bindings/sound/wcd_codec.txt
@@ -92,6 +92,14 @@ Optional properties:
involving DMIC will use the rate defined by
cdc-dmic-sample-rate.
+ - qcom,cdc-ecpp-dmic-rate: Specifies the sample rate of digital mic in HZ to be
+ used by ECPP (Echo Cancellation Ping Pong) block
+ on the codec. The valid set of values are same
+ as that of cdc-dmic-sample-rate, but this rate will
+ only be used by ECPP and all other audio use cases
+ involving DMIC will use the rate defined by
+ cdc-dmic-sample-rate.
+
- qcom,cdc-dmic-clk-drv-strength: Specifies the drive strength for digital microphone
clock in the codec. Accepted values are 2,4,8 and 16.
The clock drive strentgh is in uA. Codec driver will
diff --git a/Documentation/devicetree/bindings/thermal/tsens.txt b/Documentation/devicetree/bindings/thermal/tsens.txt
index 9aa52dde2dd4..684bea131405 100644
--- a/Documentation/devicetree/bindings/thermal/tsens.txt
+++ b/Documentation/devicetree/bindings/thermal/tsens.txt
@@ -34,16 +34,22 @@ Required properties:
should be "qcom,msmfalcon-tsens" for falcon TSENS driver.
The compatible property is used to identify the respective fusemap to use
for the corresponding SoC.
-- reg : offset and length of the TSENS registers.
-- reg : offset and length of the QFPROM registers used for storing
- the calibration data for the individual sensors.
+- reg : offset and length of the TSENS registers with associated property in reg-names
+ as "tsens_physical" and QFPROM registers with associated property in reg-names
+ as "tsens_eeprom_physical". The efuse registers are used for storing
+ the calibration data for the individual sensors. If the gain and offset are
+ programmed by the TSENS control registers then adding the QFPROM register property
+ is optional.
- reg-names : resource names used for the physical address of the TSENS
registers, the QFPROM efuse primary calibration address region,
Should be "tsens_physical" for physical address of the TSENS,
"tsens_eeprom_physical" for physical address where primary
calibration data is stored. This includes the backup
calibration address region if TSENS calibration data is stored
- in the region.
+ in the region. The reg-name "tsens_eeprom_physical" property is
+ optional if the gain and offset are programmed by the TSENS
+ control registers and the status registers directly reports the TSENS
+ temperature readings.
- interrupts : TSENS interrupt to notify Upper/Lower temperature threshold.
- interrupt-names: Should be "tsens-upper-lower" for temperature threshold.
Add "tsens-critical" for Critical temperature threshold notification
@@ -53,7 +59,8 @@ Required properties:
- qcom,slope : One point calibration characterized slope data for each
sensor used to compute the offset. Slope is represented
as ADC code/DegC and the value is multipled by a factor
- of 1000.
+ of 1000. If gain and offset are programmed by the TSENS control
+ registers then this property is optional.
Optional properties:
- qcom,calibration-less-mode : If present the pre-characterized data for offsets
diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index a38104faf261..3136687adb57 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -6,6 +6,10 @@ DWC3- USB3 CONTROLLER. Complies to the generic USB binding properties
Required properties:
- compatible: must be "snps,dwc3"
- reg : Address and length of the register set for the device
+ Required regs are:
+ - "core_base" : USB DWC3 controller register set.
+ - "ahb2phy_base" : AHB2PHY register base. It is used to update read/write
+ wait cycle for accessing PHY.
- interrupts: Interrupts used by the dwc3 controller.
Optional properties:
@@ -61,7 +65,10 @@ This is usually a subnode to DWC3 glue to which it is connected.
dwc3@4a030000 {
compatible = "snps,dwc3";
- reg = <0x4a030000 0xcfff>;
+ reg = <0x07600000 0xfc000>,
+ <0x7416000 0x400>;
+ reg-names = "core_base",
+ "ahb2phy_base";
interrupts = <0 92 4>
usb-phy = <&usb2_phy>, <&usb3,phy>;
tx-fifo-resize;
diff --git a/Documentation/devicetree/bindings/usb/msm-phy.txt b/Documentation/devicetree/bindings/usb/msm-phy.txt
index 2f82fbfda14f..dd9c13b4b5ff 100644
--- a/Documentation/devicetree/bindings/usb/msm-phy.txt
+++ b/Documentation/devicetree/bindings/usb/msm-phy.txt
@@ -180,16 +180,11 @@ Required properties:
- phy_type: Should be one of "ulpi" or "utmi". ChipIdea core uses "ulpi" mode.
Optional properties:
- - reg: Address and length register set to control QUSB2 PHY
- "qscratch_base" : QSCRATCH base register set.
+ - reg-names: Additional registers corresponding with the following:
"tune2_efuse_addr": EFUSE based register address to read TUNE2 parameter.
via the QSCRATCH interface.
"emu_phy_base" : phy base address used for programming emulation target phy.
"ref_clk_addr" : ref_clk bcr address used for on/off ref_clk before reset.
- - reg-names: Should be "qscratch_base". The qscratch register bank
- allows us to manipulate QUSB PHY bits eg. to enable D+ pull-up using s/w
- control in device mode. The reg-names property is required if the
- reg property is specified.
- clocks: a list of phandles to the PHY clocks. Use as per
Documentation/devicetree/bindings/clock/clock-bindings.txt
- clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
@@ -210,10 +205,8 @@ Optional properties:
Example:
qusb_phy: qusb@f9b39000 {
compatible = "qcom,qusb2phy";
- reg = <0x00079000 0x7000>,
- <0x08af8800 0x400>;
- reg-names = "qusb_phy_base",
- "qscratch_base";
+ reg = <0x00079000 0x7000>;
+ reg-names = "qusb_phy_base";
vdd-supply = <&pm8994_s2_corner>;
vdda18-supply = <&pm8994_l6>;
vdda33-supply = <&pm8994_l24>;
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 8570e6a6bfa4..4097b7cd6454 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -120,6 +120,7 @@ intercontrol Inter Control Group
invensense InvenSense Inc.
isee ISEE 2007 S.L.
isil Intersil
+ite ITE Tech. Inc.
jedec JEDEC Solid State Technology Association
karo Ka-Ro electronics GmbH
keymile Keymile GmbH
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 65c1849f8269..024bdaf9af46 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -3936,6 +3936,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
sector if the number is odd);
i = IGNORE_DEVICE (don't bind to this
device);
+ j = NO_REPORT_LUNS (don't use report luns
+ command, uas only);
l = NOT_LOCKABLE (don't try to lock and
unlock ejectable media);
m = MAX_SECTORS_64 (don't transfer more
diff --git a/MAINTAINERS b/MAINTAINERS
index d826f1b9eb02..4c3e1d2ac31b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -230,13 +230,13 @@ F: kernel/sys_ni.c
ABIT UGURU 1,2 HARDWARE MONITOR DRIVER
M: Hans de Goede <hdegoede@redhat.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/abituguru.c
ABIT UGURU 3 HARDWARE MONITOR DRIVER
M: Alistair John Strachan <alistair@devzero.co.uk>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/abituguru3.c
@@ -373,14 +373,14 @@ S: Maintained
ADM1025 HARDWARE MONITOR DRIVER
M: Jean Delvare <jdelvare@suse.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/adm1025
F: drivers/hwmon/adm1025.c
ADM1029 HARDWARE MONITOR DRIVER
M: Corentin Labbe <clabbe.montjoie@gmail.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/adm1029.c
@@ -425,7 +425,7 @@ F: drivers/video/backlight/adp8860_bl.c
ADS1015 HARDWARE MONITOR DRIVER
M: Dirk Eibach <eibach@gdsys.de>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/ads1015
F: drivers/hwmon/ads1015.c
@@ -438,7 +438,7 @@ F: drivers/macintosh/therm_adt746x.c
ADT7475 HARDWARE MONITOR DRIVER
M: Jean Delvare <jdelvare@suse.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/adt7475
F: drivers/hwmon/adt7475.c
@@ -615,7 +615,7 @@ F: include/linux/ccp.h
AMD FAM15H PROCESSOR POWER MONITORING DRIVER
M: Andreas Herrmann <herrmann.der.user@googlemail.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/fam15h_power
F: drivers/hwmon/fam15h_power.c
@@ -779,7 +779,7 @@ F: drivers/input/mouse/bcm5974.c
APPLE SMC DRIVER
M: Henrik Rydberg <rydberg@bitmath.org>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Odd fixes
F: drivers/hwmon/applesmc.c
@@ -1777,7 +1777,7 @@ F: include/media/as3645a.h
ASC7621 HARDWARE MONITOR DRIVER
M: George Joseph <george.joseph@fairview5.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/asc7621
F: drivers/hwmon/asc7621.c
@@ -1864,7 +1864,7 @@ F: drivers/net/wireless/ath/carl9170/
ATK0110 HWMON DRIVER
M: Luca Tettamanti <kronos.it@gmail.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/asus_atk0110.c
@@ -2984,7 +2984,7 @@ F: mm/swap_cgroup.c
CORETEMP HARDWARE MONITORING DRIVER
M: Fenghua Yu <fenghua.yu@intel.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/coretemp
F: drivers/hwmon/coretemp.c
@@ -3549,7 +3549,7 @@ T: git git://git.infradead.org/users/vkoul/slave-dma.git
DME1737 HARDWARE MONITOR DRIVER
M: Juerg Haefliger <juergh@gmail.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/dme1737
F: drivers/hwmon/dme1737.c
@@ -4262,7 +4262,7 @@ F: include/video/exynos_mipi*
F71805F HARDWARE MONITORING DRIVER
M: Jean Delvare <jdelvare@suse.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/f71805f
F: drivers/hwmon/f71805f.c
@@ -4341,7 +4341,7 @@ F: fs/*
FINTEK F75375S HARDWARE MONITOR AND FAN CONTROLLER DRIVER
M: Riku Voipio <riku.voipio@iki.fi>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/f75375s.c
F: include/linux/f75375s.h
@@ -4883,8 +4883,8 @@ F: drivers/media/usb/hackrf/
HARDWARE MONITORING
M: Jean Delvare <jdelvare@suse.com>
M: Guenter Roeck <linux@roeck-us.net>
-L: lm-sensors@lm-sensors.org
-W: http://www.lm-sensors.org/
+L: linux-hwmon@vger.kernel.org
+W: http://hwmon.wiki.kernel.org/
T: quilt http://jdelvare.nerim.net/devel/linux/jdelvare-hwmon/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git
S: Maintained
@@ -5393,7 +5393,7 @@ F: drivers/usb/atm/ueagle-atm.c
INA209 HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/ina209
F: Documentation/devicetree/bindings/i2c/ina209.txt
@@ -5401,7 +5401,7 @@ F: drivers/hwmon/ina209.c
INA2XX HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/ina2xx
F: drivers/hwmon/ina2xx.c
@@ -5884,7 +5884,7 @@ F: drivers/isdn/hardware/eicon/
IT87 HARDWARE MONITORING DRIVER
M: Jean Delvare <jdelvare@suse.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/it87
F: drivers/hwmon/it87.c
@@ -5920,7 +5920,7 @@ F: drivers/media/dvb-frontends/ix2505v*
JC42.4 TEMPERATURE SENSOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/jc42.c
F: Documentation/hwmon/jc42
@@ -5970,14 +5970,14 @@ F: drivers/tty/serial/jsm/
K10TEMP HARDWARE MONITORING DRIVER
M: Clemens Ladisch <clemens@ladisch.de>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/k10temp
F: drivers/hwmon/k10temp.c
K8TEMP HARDWARE MONITORING DRIVER
M: Rudolf Marek <r.marek@assembler.cz>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/k8temp
F: drivers/hwmon/k8temp.c
@@ -6485,27 +6485,27 @@ F: net/llc/
LM73 HARDWARE MONITOR DRIVER
M: Guillaume Ligneul <guillaume.ligneul@gmail.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/lm73.c
LM78 HARDWARE MONITOR DRIVER
M: Jean Delvare <jdelvare@suse.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/lm78
F: drivers/hwmon/lm78.c
LM83 HARDWARE MONITOR DRIVER
M: Jean Delvare <jdelvare@suse.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/lm83
F: drivers/hwmon/lm83.c
LM90 HARDWARE MONITOR DRIVER
M: Jean Delvare <jdelvare@suse.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/lm90
F: Documentation/devicetree/bindings/hwmon/lm90.txt
@@ -6513,7 +6513,7 @@ F: drivers/hwmon/lm90.c
LM95234 HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/lm95234
F: drivers/hwmon/lm95234.c
@@ -6580,7 +6580,7 @@ F: drivers/scsi/sym53c8xx_2/
LTC4261 HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/ltc4261
F: drivers/hwmon/ltc4261.c
@@ -6749,28 +6749,28 @@ F: include/uapi/linux/matroxfb.h
MAX16065 HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/max16065
F: drivers/hwmon/max16065.c
MAX20751 HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/max20751
F: drivers/hwmon/max20751.c
MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER
M: "Hans J. Koch" <hjk@hansjkoch.de>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/max6650
F: drivers/hwmon/max6650.c
MAX6697 HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/max6697
F: Documentation/devicetree/bindings/i2c/max6697.txt
@@ -7303,7 +7303,7 @@ F: drivers/scsi/NCR_D700.*
NCT6775 HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/nct6775
F: drivers/hwmon/nct6775.c
@@ -8064,7 +8064,7 @@ F: drivers/video/logo/logo_parisc*
PC87360 HARDWARE MONITORING DRIVER
M: Jim Cromie <jim.cromie@gmail.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/pc87360
F: drivers/hwmon/pc87360.c
@@ -8076,7 +8076,7 @@ F: drivers/char/pc8736x_gpio.c
PC87427 HARDWARE MONITORING DRIVER
M: Jean Delvare <jdelvare@suse.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/pc87427
F: drivers/hwmon/pc87427.c
@@ -8415,8 +8415,8 @@ F: drivers/rtc/rtc-puv3.c
PMBUS HARDWARE MONITORING DRIVERS
M: Guenter Roeck <linux@roeck-us.net>
-L: lm-sensors@lm-sensors.org
-W: http://www.lm-sensors.org/
+L: linux-hwmon@vger.kernel.org
+W: http://hwmon.wiki.kernel.org/
W: http://www.roeck-us.net/linux/drivers/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git
S: Maintained
@@ -8610,7 +8610,7 @@ F: drivers/media/usb/pwc/*
PWM FAN DRIVER
M: Kamil Debski <k.debski@samsung.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/hwmon/pwm-fan.txt
F: Documentation/hwmon/pwm-fan
@@ -9882,28 +9882,28 @@ F: Documentation/devicetree/bindings/media/i2c/nokia,smia.txt
SMM665 HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/smm665
F: drivers/hwmon/smm665.c
SMSC EMC2103 HARDWARE MONITOR DRIVER
M: Steve Glendinning <steve.glendinning@shawell.net>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/emc2103
F: drivers/hwmon/emc2103.c
SMSC SCH5627 HARDWARE MONITOR DRIVER
M: Hans de Goede <hdegoede@redhat.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Supported
F: Documentation/hwmon/sch5627
F: drivers/hwmon/sch5627.c
SMSC47B397 HARDWARE MONITOR DRIVER
M: Jean Delvare <jdelvare@suse.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/smsc47b397
F: drivers/hwmon/smsc47b397.c
@@ -10830,7 +10830,7 @@ F: include/linux/mmc/sh_mobile_sdhi.h
TMP401 HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/tmp401
F: drivers/hwmon/tmp401.c
@@ -11564,14 +11564,14 @@ F: Documentation/networking/vrf.txt
VT1211 HARDWARE MONITOR DRIVER
M: Juerg Haefliger <juergh@gmail.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/vt1211
F: drivers/hwmon/vt1211.c
VT8231 HARDWARE MONITOR DRIVER
M: Roger Lucas <vt8231@hiddenengine.co.uk>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/vt8231.c
@@ -11590,21 +11590,21 @@ F: drivers/w1/
W83791D HARDWARE MONITORING DRIVER
M: Marc Hulsman <m.hulsman@tudelft.nl>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/w83791d
F: drivers/hwmon/w83791d.c
W83793 HARDWARE MONITORING DRIVER
M: Rudolf Marek <r.marek@assembler.cz>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/w83793
F: drivers/hwmon/w83793.c
W83795 HARDWARE MONITORING DRIVER
M: Jean Delvare <jdelvare@suse.com>
-L: lm-sensors@lm-sensors.org
+L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/w83795.c
diff --git a/Makefile b/Makefile
index d8e74ad1221c..e59197c28c78 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 4
-SUBLEVEL = 6
+SUBLEVEL = 8
EXTRAVERSION =
NAME = Blurry Fish Butt
diff --git a/arch/arc/include/asm/bitops.h b/arch/arc/include/asm/bitops.h
index 57c1f33844d4..0352fb8d21b9 100644
--- a/arch/arc/include/asm/bitops.h
+++ b/arch/arc/include/asm/bitops.h
@@ -35,21 +35,6 @@ static inline void op##_bit(unsigned long nr, volatile unsigned long *m)\
\
m += nr >> 5; \
\
- /* \
- * ARC ISA micro-optimization: \
- * \
- * Instructions dealing with bitpos only consider lower 5 bits \
- * e.g (x << 33) is handled like (x << 1) by ASL instruction \
- * (mem pointer still needs adjustment to point to next word) \
- * \
- * Hence the masking to clamp @nr arg can be elided in general. \
- * \
- * However if @nr is a constant (above assumed in a register), \
- * and greater than 31, gcc can optimize away (x << 33) to 0, \
- * as overflow, given the 32-bit ISA. Thus masking needs to be \
- * done for const @nr, but no code is generated due to gcc \
- * const prop. \
- */ \
nr &= 0x1f; \
\
__asm__ __volatile__( \
diff --git a/arch/arc/include/asm/io.h b/arch/arc/include/asm/io.h
index 694ece8a0243..27b17adea50d 100644
--- a/arch/arc/include/asm/io.h
+++ b/arch/arc/include/asm/io.h
@@ -129,15 +129,23 @@ static inline void __raw_writel(u32 w, volatile void __iomem *addr)
#define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); })
/*
- * Relaxed API for drivers which can handle any ordering themselves
+ * Relaxed API for drivers which can handle barrier ordering themselves
+ *
+ * Also these are defined to perform little endian accesses.
+ * To provide the typical device register semantics of fixed endian,
+ * swap the byte order for Big Endian
+ *
+ * http://lkml.kernel.org/r/201603100845.30602.arnd@arndb.de
*/
#define readb_relaxed(c) __raw_readb(c)
-#define readw_relaxed(c) __raw_readw(c)
-#define readl_relaxed(c) __raw_readl(c)
+#define readw_relaxed(c) ({ u16 __r = le16_to_cpu((__force __le16) \
+ __raw_readw(c)); __r; })
+#define readl_relaxed(c) ({ u32 __r = le32_to_cpu((__force __le32) \
+ __raw_readl(c)); __r; })
#define writeb_relaxed(v,c) __raw_writeb(v,c)
-#define writew_relaxed(v,c) __raw_writew(v,c)
-#define writel_relaxed(v,c) __raw_writel(v,c)
+#define writew_relaxed(v,c) __raw_writew((__force u16) cpu_to_le16(v),c)
+#define writel_relaxed(v,c) __raw_writel((__force u32) cpu_to_le32(v),c)
#include <asm-generic/io.h>
diff --git a/arch/arm/boot/dts/at91-sama5d3_xplained.dts b/arch/arm/boot/dts/at91-sama5d3_xplained.dts
index ff888d21c786..f3e2b96c06a3 100644
--- a/arch/arm/boot/dts/at91-sama5d3_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d3_xplained.dts
@@ -303,6 +303,7 @@
regulator-name = "mmc0-card-supply";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
+ regulator-always-on;
};
gpio_keys {
diff --git a/arch/arm/boot/dts/at91-sama5d4_xplained.dts b/arch/arm/boot/dts/at91-sama5d4_xplained.dts
index 569026e8f96c..da84e65b56ef 100644
--- a/arch/arm/boot/dts/at91-sama5d4_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d4_xplained.dts
@@ -268,5 +268,6 @@
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
vin-supply = <&vcc_3v3_reg>;
+ regulator-always-on;
};
};
diff --git a/arch/arm/boot/dts/qcom/batterydata-qrd-skuk-4v4-3000mah.dtsi b/arch/arm/boot/dts/qcom/batterydata-qrd-skuk-4v4-3000mah.dtsi
new file mode 100644
index 000000000000..76126c21c43a
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/batterydata-qrd-skuk-4v4-3000mah.dtsi
@@ -0,0 +1,78 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+qcom,qrd_msmcobalt_skuk_3000mah {
+ qcom,max-voltage-uv = <4400000>;
+ qcom,nom-batt-capacity-mah = <3000>;
+ qcom,batt-id-kohm = <68>;
+ qcom,battery-beta = <3380>;
+ qcom,battery-type = "qrd_msmcobalt_skuk_300mah";
+ qcom,checksum = <0x0F19>;
+ qcom,fg-profile-data = [
+ 05 B2 1F 6F
+ FC A3 0A 6E
+ FD DB 1D 8C
+ 1D AE 12 C2
+ 23 00 18 7E
+ 52 B4 45 8D
+ 00 00 00 55
+ 00 00 00 0F
+ C5 92 00 00
+ CA A0 CD 95
+ 00 0C 00 1F
+ EC C3 F2 56
+ F3 27 06 7B
+ 12 FF 01 02
+ 3A 21 DA 1C
+ 40 40 09 1C
+ 00 05 00 07
+ 05 B4 1F AC
+ FC EF 0A 57
+ 00 2E 1D 6A
+ 14 BA 0B 12
+ 22 DC 19 40
+ 53 03 45 79
+ 00 00 00 53
+ 00 00 00 0E
+ CC 05 00 00
+ CA 24 BB 3A
+ 00 00 00 1C
+ EC C3 F2 56
+ F2 A2 06 A6
+ 01 C7 06 96
+ 1A CF EA 8B
+ 33 08 33 BA
+ 00 00 10 07
+ 46 66 0C 3A
+ 00 19 00 1C
+ FA 0A 01 98
+ 00 00 00 FF
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ ];
+};
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi
index bb111ba8e2d4..367384a8c3e5 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -70,6 +70,10 @@
qcom,mdss-dsi-dma-trigger = "trigger_sw";
qcom,mdss-dsi-mdp-trigger = "none";
qcom,mdss-dsi-reset-sequence = <1 20>, <0 20>, <1 50>;
+ qcom,mdss-dsi-min-refresh-rate = <55>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
qcom,config-select = <&dsi_dual_nt35597_video_config0>;
diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi
new file mode 100644
index 000000000000..90df1d0c1ac0
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi
@@ -0,0 +1,80 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+qcom,ascent_3450mah {
+ /* #Ascent_860_82209_0000_3450mAh_averaged_MasterSlave_Jul20th2016*/
+ qcom,max-voltage-uv = <4350000>;
+ qcom,nom-batt-capacity-mah = <3450>;
+ qcom,batt-id-kohm = <60>;
+ qcom,battery-beta = <3435>;
+ qcom,battery-type = "ascent_860_82209_0000_3450mah";
+ qcom,checksum = <0xD1D9>;
+ qcom,gui-version = "PMI8998GUI - 0.0.0.82";
+ qcom,fg-profile-data = [
+ 2C 1F 3F FC
+ E9 03 A1 FD
+ 58 1D FD F5
+ 27 12 2C 14
+ 3F 18 FF 22
+ 9B 45 A3 52
+ 55 00 00 00
+ 0E 00 00 00
+ 00 00 1C AC
+ F7 CD 71 B5
+ 1A 00 0C 00
+ 3C EB 54 E4
+ EC 05 7F FA
+ 76 05 F5 02
+ CA F3 82 3A
+ 2A 09 40 40
+ 07 00 05 00
+ 58 1F 42 06
+ 85 03 35 F4
+ 4D 1D 37 F2
+ 23 0A 79 15
+ B7 18 32 23
+ 26 45 72 53
+ 55 00 00 00
+ 0D 00 00 00
+ 00 00 13 CC
+ 03 00 98 BD
+ 16 00 00 00
+ 3C EB 54 E4
+ 9F FC A3 F3
+ 0F FC DF FA
+ FF E5 A9 23
+ CB 33 08 33
+ 07 10 00 00
+ 81 0D 99 45
+ 16 00 19 00
+ 75 01 0A FA
+ FF 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ ];
+};
diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi
new file mode 100644
index 000000000000..2c1edde56d6a
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi
@@ -0,0 +1,80 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+qcom,itech_3000mah {
+ /* #Itech_B00826LF_3000mAh_ver1660_averaged_MasterSlave_Jul20th2016*/
+ qcom,max-voltage-uv = <4350000>;
+ qcom,nom-batt-capacity-mah = <3000>;
+ qcom,batt-id-kohm = <100>;
+ qcom,battery-beta = <3450>;
+ qcom,battery-type = "itech_b00826lf_3000mah_ver1660";
+ qcom,checksum = <0xE06B>;
+ qcom,gui-version = "PMI8998GUI - 0.0.0.82";
+ qcom,fg-profile-data = [
+ A4 1F 6E 05
+ 9C 0A 16 06
+ 32 1D 24 E5
+ 61 0B 1B 15
+ AD 17 8C 22
+ EB 3C 87 4A
+ 5B 00 00 00
+ 12 00 00 00
+ 00 00 62 C2
+ 0C CD D8 C2
+ 19 00 0C 00
+ 7E 00 C7 EC
+ E3 05 5D FA
+ 97 F5 12 12
+ C2 05 90 3B
+ 22 09 40 40
+ 07 00 05 00
+ 7D 1F DE 05
+ 3F 0A 73 06
+ 72 1D E2 F5
+ 6F 12 BF 1D
+ 88 18 FB 22
+ 8D 45 C6 52
+ 54 00 00 00
+ 0F 00 00 00
+ 00 00 BD CD
+ 55 C2 5D C5
+ 14 00 00 00
+ 7E 00 C7 EC
+ 60 06 BB 00
+ B3 FC 61 03
+ 6A 06 78 1B
+ B3 33 08 33
+ 07 10 00 00
+ 3E 0B 99 45
+ 14 00 19 00
+ AE 01 0A FA
+ FF 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ 00 00 00 00
+ ];
+};
diff --git a/arch/arm/boot/dts/qcom/msm-arm-smmu-cobalt.dtsi b/arch/arm/boot/dts/qcom/msm-arm-smmu-cobalt.dtsi
index ab46221089f3..3de3cd5f8de9 100644
--- a/arch/arm/boot/dts/qcom/msm-arm-smmu-cobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-arm-smmu-cobalt.dtsi
@@ -19,7 +19,7 @@
status = "ok";
compatible = "qcom,smmu-v2";
reg = <0x1680000 0x10000>;
- #iommu-cells = <1>;
+ #iommu-cells = <0>;
qcom,register-save;
qcom,skip-init;
#global-interrupts = <2>;
diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
index 47ba50ab8270..c724ce5a8ad9 100644
--- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
@@ -297,47 +297,6 @@
#address-cells = <1>;
#size-cells = <0>;
#io-channel-cells = <1>;
-
- /* Channel node */
- batt_id {
- channel = <3>;
- };
-
- batt_therm {
- channel = <4>;
- };
-
- skin_temp {
- channel = <5>;
- };
-
- usbin_v {
- channel = <6>;
- };
-
- usbin_i {
- channel = <7>;
- };
-
- dcin_v {
- channel = <8>;
- };
-
- dcin_i {
- channel = <9>;
- };
-
- die_temp {
- channel = <0xa>;
- };
-
- chg_temp {
- channel = <0xb>;
- };
-
- gpio {
- channel = <0xc>;
- };
};
pmicobalt_fg: qpnp,fg {
@@ -634,7 +593,16 @@
status = "okay";
reg = <0xd300 0x100>;
label = "flash";
+ interrupts = <0x3 0xd3 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x3 0xd3 0x3 IRQ_TYPE_EDGE_RISING>,
+ <0x3 0xd3 0x4 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "led-fault-irq",
+ "all-ramp-down-done-irq",
+ "all-ramp-up-done-irq";
qcom,hdrm-auto-mode;
+ qcom,short-circuit-det;
+ qcom,open-circuit-det;
+ qcom,vph-droop-det;
qcom,isc-delay = <192>;
pmicobalt_flash0: qcom,flash_0 {
diff --git a/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi
index 25e0d99987db..07423a601b35 100644
--- a/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi
@@ -79,6 +79,8 @@
qcom,snapshot-size = <1048576>; //bytes
+ qcom,gpu-qdss-stm = <0x081c0000 0x40000>; // base addr, size
+
/* Trace bus */
coresight-id = <300>;
coresight-name = "coresight-gfx";
diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi
index 7a26b9f0b227..596a713a9ad0 100644
--- a/arch/arm/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996.dtsi
@@ -1001,6 +1001,7 @@
qcom,cdc-slim-ifd-elemental-addr = [00 00 A0 01 17 02];
qcom,cdc-dmic-sample-rate = <4800000>;
qcom,cdc-mad-dmic-rate = <600000>;
+ qcom,cdc-ecpp-dmic-rate = <1200000>;
qcom,cdc-dmic-clk-drv-strength = <2>;
};
};
@@ -2059,11 +2060,9 @@
qusb_phy0: qusb@7411000 {
compatible = "qcom,qusb2phy";
reg = <0x07411000 0x180>,
- <0x06af8800 0x400>,
<0x0007024c 0x4>,
<0x00388018 0x4>;
reg-names = "qusb_phy_base",
- "qscratch_base",
"tune2_efuse_addr",
"ref_clk_addr";
vdd-supply = <&pm8994_s2_corner>;
@@ -2096,11 +2095,9 @@
qusb_phy1: qusb@7412000 {
compatible = "qcom,qusb2phy";
reg = <0x07412000 0x180>,
- <0x076f8800 0x400>,
<0x0007024c 0x4>,
<0x00388014 0x4>;
reg-names = "qusb_phy_base",
- "qscratch_base",
"tune2_efuse_addr",
"ref_clk_addr";
vdd-supply = <&pm8994_s2_corner>;
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi
index 3ed038069319..aeb9b1c0207f 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi
@@ -16,6 +16,7 @@
cell-index = <0>;
compatible = "qcom,camera-flash";
qcom,flash-source = <&pmicobalt_flash0 &pmicobalt_flash1>;
+ qcom,torch-source = <&pmicobalt_torch0 &pmicobalt_torch1>;
qcom,switch-source = <&pmicobalt_switch0>;
status = "ok";
};
@@ -24,6 +25,7 @@
cell-index = <1>;
compatible = "qcom,camera-flash";
qcom,flash-source = <&pmicobalt_flash2>;
+ qcom,torch-source = <&pmicobalt_torch2>;
qcom,switch-source = <&pmicobalt_switch1>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi
index d152c0049f96..3887999e9bab 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi
@@ -16,6 +16,7 @@
cell-index = <0>;
compatible = "qcom,camera-flash";
qcom,flash-source = <&pmicobalt_flash0 &pmicobalt_flash1>;
+ qcom,torch-source = <&pmicobalt_torch0 &pmicobalt_torch1>;
qcom,switch-source = <&pmicobalt_switch0>;
status = "ok";
};
@@ -24,6 +25,7 @@
cell-index = <1>;
compatible = "qcom,camera-flash";
qcom,flash-source = <&pmicobalt_flash2>;
+ qcom,torch-source = <&pmicobalt_torch2>;
qcom,switch-source = <&pmicobalt_switch1>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts
index c8402ba5b69a..10edf71da2f3 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts
+++ b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts
@@ -17,7 +17,7 @@
#include "msmcobalt-cdp.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. MSM cobalt";
+ model = "Qualcomm Technologies, Inc. MSM COBALT v1 CDP";
compatible = "qcom,msmcobalt-cdp", "qcom,msmcobalt", "qcom,cdp";
qcom,board-id = <1 0>;
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi
index 6ab19b298aa7..d273d757dba5 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi
@@ -79,6 +79,8 @@
qcom,snapshot-size = <1048576>; //bytes
+ qcom,gpu-qdss-stm = <0x161c0000 0x40000>; // base addr, size
+
clocks = <&clock_gfx clk_gpucc_gfx3d_clk>,
<&clock_gcc clk_gcc_gpu_cfg_ahb_clk>,
<&clock_gpu clk_gpucc_rbbmtimer_clk>,
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi
index 0242497c3747..a99ce727c195 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi
@@ -255,6 +255,9 @@
mdss_fb0: qcom,mdss_fb_primary {
cell-index = <0>;
compatible = "qcom,mdss-fb";
+ qcom,cont-splash-memory {
+ linux,contiguous-region = <&cont_splash_mem>;
+ };
};
mdss_fb1: qcom,mdss_fb_wfd {
@@ -296,7 +299,6 @@
<22 512 0 0>,
<22 512 0 1000>;
- qcom,timing-db-mode;
qcom,mmss-ulp-clamp-ctrl-offset = <0x14>;
clocks = <&clock_mmss clk_mmss_mdss_mdp_clk>,
@@ -368,6 +370,7 @@
<0xc828000 0xac>;
reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
+ qcom,timing-db-mode;
wqhd-vddio-supply = <&pmcobalt_l14>;
lab-supply = <&lab_regulator>;
ibb-supply = <&ibb_regulator>;
@@ -405,6 +408,7 @@
<0xc828000 0xac>;
reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
+ qcom,timing-db-mode;
wqhd-vddio-supply = <&pmcobalt_l14>;
lab-supply = <&lab_regulator>;
ibb-supply = <&ibb_regulator>;
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts
index 89ef72c104f5..ea4047df25f6 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts
+++ b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts
@@ -17,7 +17,7 @@
#include "msmcobalt-mtp.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. MSM cobalt v1";
+ model = "Qualcomm Technologies, Inc. MSM COBALT v1 MTP";
compatible = "qcom,msmcobalt-mtp", "qcom,msmcobalt", "qcom,mtp";
qcom,board-id = <8 0>;
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi
index e6a3c3c225ca..aaf9aed30e7f 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi
@@ -510,3 +510,11 @@
};
};
};
+
+/{
+ mtp_batterydata: qcom,battery-data {
+ qcom,batt-id-range-pct = <15>;
+ #include "fg-gen3-batterydata-itech-3000mah.dtsi"
+ #include "fg-gen3-batterydata-ascent-3450mah.dtsi"
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi
index 6db4ed1f45e1..4cbd17f80cb8 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi
@@ -185,7 +185,7 @@
config {
pins = "gpio87", "gpio88";
drive-strength = <2>;
- bias-pull-up;
+ bias-disable;
};
};
};
@@ -1245,7 +1245,7 @@
config {
pins = "gpio45", "gpio46", "gpio47", "gpio48";
drive-strength = <2>;
- bias-disable;
+ bias-pull-up;
};
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi
index 1720aca3b298..425a902568ae 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi
@@ -11,3 +11,61 @@
*/
#include "msmcobalt-mtp.dtsi"
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "split_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_dual_nt35597_truly_video>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+ qcom,platform-reset-gpio = <&tlmm 94 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,panel-mode-gpio = <&tlmm 91 0>;
+};
+
+&mdss_dsi1 {
+ qcom,dsi-pref-prim-pan = <&dsi_dual_nt35597_truly_video>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+ qcom,platform-reset-gpio = <&tlmm 94 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,panel-mode-gpio = <&tlmm 91 0>;
+};
+
+&dsi_sharp_4k_dsc_video {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
+&dsi_sharp_4k_dsc_cmd {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
+&dsi_dual_nt35597_truly_video {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
+&dsi_dual_nt35597_truly_cmd {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi
index 5833b30d1fd1..9f8ecea15568 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi
@@ -944,20 +944,20 @@
qcom,cpr-voltage-ceiling =
<896000 896000 896000 896000 896000
1032000>,
- <632000 696000 768000 828000 896000
- 1032000>,
- <632000 696000 768000 828000 896000
- 1032000>,
- <632000 696000 768000 828000 896000
- 1032000>,
- <632000 696000 768000 828000 896000
- 1032000>,
- <632000 696000 768000 828000 896000
- 1032000>,
- <632000 696000 768000 828000 896000
- 1032000>,
- <632000 696000 768000 828000 896000
- 1032000>;
+ <672000 740000 800000 868000 976000
+ 1100000>,
+ <672000 740000 800000 868000 976000
+ 1100000>,
+ <672000 740000 800000 868000 976000
+ 1100000>,
+ <672000 740000 800000 868000 976000
+ 1100000>,
+ <672000 740000 800000 868000 976000
+ 1100000>,
+ <672000 740000 800000 868000 976000
+ 1100000>,
+ <672000 740000 800000 868000 976000
+ 1100000>;
qcom,cpr-voltage-floor =
<896000 896000 896000 896000 896000
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2-cdp.dts b/arch/arm/boot/dts/qcom/msmcobalt-v2-cdp.dts
index d47213603bdf..7cb8f107abc4 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-v2-cdp.dts
+++ b/arch/arm/boot/dts/qcom/msmcobalt-v2-cdp.dts
@@ -17,7 +17,7 @@
#include "msmcobalt-cdp.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. MSM cobalt v2";
+ model = "Qualcomm Technologies, Inc. MSM COBALT v2 CDP";
compatible = "qcom,msmcobalt-cdp", "qcom,msmcobalt", "qcom,cdp";
qcom,board-id = <1 0>;
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2-mtp.dts b/arch/arm/boot/dts/qcom/msmcobalt-v2-mtp.dts
index eb48e4444ef8..96e671f04df3 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-v2-mtp.dts
+++ b/arch/arm/boot/dts/qcom/msmcobalt-v2-mtp.dts
@@ -17,7 +17,7 @@
#include "msmcobalt-mtp.dtsi"
/ {
- model = "Qualcomm Technologies, Inc. MSM cobalt v2";
+ model = "Qualcomm Technologies, Inc. MSM COBALT v2 MTP";
compatible = "qcom,msmcobalt-mtp", "qcom,msmcobalt", "qcom,mtp";
qcom,board-id = <8 0>;
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi
index ee31a3059406..0860139248d1 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi
@@ -25,14 +25,15 @@
qcom,firmware-name = "venus";
qcom,never-unload-fw;
qcom,sw-power-collapse;
+ qcom,debug-timeout;
qcom,reg-presets =
- <0x80124 0x0002000>,
- <0x80550 0x0000001>,
- <0x80560 0x2222221>,
- <0x80568 0x3333331>,
- <0x80570 0x0000001>,
- <0x80580 0x2222221>,
- <0x80588 0x3333331>;
+ <0x80124 0x00000003>,
+ <0x80550 0x01111111>,
+ <0x80560 0x01111111>,
+ <0x80568 0x01111111>,
+ <0x80570 0x01111111>,
+ <0x80580 0x01111111>,
+ <0x80588 0x01111111>;
qcom,imem-size = <524288>; /* 512 kB */
qcom,max-hw-load = <2563200>; /* Full 4k @ 60 + 1080p @ 60 */
diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi
index f02abd2173ef..33e8c9c4993d 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi
@@ -333,6 +333,11 @@
size = <0 0x2000000>;
linux,cma-default;
};
+
+ cont_splash_mem: splash_region@9d600000 {
+ reg = <0x0 0x9d600000 0x0 0x02400000>;
+ label = "cont_splash_mem";
+ };
};
};
@@ -761,7 +766,7 @@
vdd_gpucc-supply = <&gfx_vreg>;
vdd_mx-supply = <&pmcobalt_s9_level>;
vdd_gpu_mx-supply = <&pmcobalt_s9_level>;
- qcom,gfx3d_clk_src-opp-store-vcorner = <&msm_gpu>;
+ qcom,gfx3d_clk_src-opp-handle = <&msm_gpu>;
qcom,gfxfreq-speedbin0 =
< 0 0 0 >,
< 171000000 1 RPM_SMD_REGULATOR_LEVEL_SVS >,
@@ -815,9 +820,9 @@
< 1324800000 0x04040045 0x06370037 0x2 >,
< 1401600000 0x04040049 0x073a003a 0x2 >,
< 1478400000 0x0404004d 0x073e003e 0x2 >,
- < 1574400000 0x04040052 0x08420042 0x3 >,
- < 1651200000 0x04040056 0x08450045 0x3 >,
- < 1728000000 0x0404005a 0x08480048 0x3 >,
+ < 1574400000 0x04040052 0x08420042 0x2 >,
+ < 1651200000 0x04040056 0x08450045 0x2 >,
+ < 1728000000 0x0404005a 0x08480048 0x2 >,
< 1804800000 0x0404005e 0x094b004b 0x3 >,
< 1881600000 0x04040062 0x094e004e 0x3 >;
@@ -840,10 +845,10 @@
< 1401600000 0x04040049 0x073a003a 0x2 >,
< 1478400000 0x0404004d 0x073e003e 0x2 >,
< 1536000000 0x04040050 0x07400040 0x2 >,
- < 1632000000 0x04040055 0x08440044 0x3 >,
- < 1708800000 0x04040059 0x08470047 0x3 >,
- < 1785600000 0x0404005d 0x094a004a 0x3 >,
- < 1862400000 0x04040061 0x094e004e 0x3 >,
+ < 1632000000 0x04040055 0x08440044 0x2 >,
+ < 1708800000 0x04040059 0x08470047 0x2 >,
+ < 1785600000 0x0404005d 0x094a004a 0x2 >,
+ < 1862400000 0x04040061 0x094e004e 0x2 >,
< 1939200000 0x04040065 0x09510051 0x3 >,
< 2016000000 0x04040069 0x0a540054 0x3 >,
< 2092800000 0x0404006d 0x0a570057 0x3 >;
@@ -983,15 +988,15 @@
/* SVS */
<90 512 80000 640000>,
<90 585 80000 640000>,
- <1 676 80000 160000>,
+ <1 676 80000 80000>,
/* NOMINAL */
<90 512 206000 960000>,
<90 585 206000 960000>,
- <1 676 206000 200000>,
+ <1 676 206000 160000>,
/* TURBO */
<90 512 206000 3600000>,
<90 585 206000 3600000>,
- <1 676 206000 960000>;
+ <1 676 206000 300000>;
qcom,bus-vector-names = "MIN", "SVS", "NOMINAL", "TURBO";
/* IPA RAM mmap */
@@ -1304,6 +1309,35 @@
qcom,rx-ring-size = <0x800>;
};
+ glink_spi_xprt_wdsp: qcom,glink-spi-xprt-wdsp {
+ compatible = "qcom,glink-spi-xprt";
+ label = "wdsp";
+ qcom,remote-fifo-config = <&glink_fifo_wdsp>;
+ qcom,qos-config = <&glink_qos_wdsp>;
+ qcom,ramp-time = <0x10>,
+ <0x20>,
+ <0x30>,
+ <0x40>;
+ };
+
+ glink_fifo_wdsp: qcom,glink-fifo-config-wdsp {
+ compatible = "qcom,glink-fifo-config";
+ qcom,out-read-idx-reg = <0x12000>;
+ qcom,out-write-idx-reg = <0x12004>;
+ qcom,in-read-idx-reg = <0x1200C>;
+ qcom,in-write-idx-reg = <0x12010>;
+ };
+
+ glink_qos_wdsp: qcom,glink-qos-config-wdsp {
+ compatible = "qcom,glink-qos-config";
+ qcom,flow-info = <0x80 0x0>,
+ <0x70 0x1>,
+ <0x60 0x2>,
+ <0x50 0x3>;
+ qcom,mtu-size = <0x800>;
+ qcom,tput-stats-cycle = <0xa>;
+ };
+
qcom,glink_pkt {
compatible = "qcom,glinkpkt";
@@ -1479,7 +1513,7 @@
qcom,pcie-phy-ver = <0x20>;
qcom,use-19p2mhz-aux-clk;
- iommus = <&anoc1_smmu 0x1>;
+ iommus = <&anoc1_smmu>;
qcom,smmu-exist;
qcom,smmu-sid-base = <0x1480>;
@@ -1779,10 +1813,8 @@
qusb_phy0: qusb@c012000 {
compatible = "qcom,qusb2phy-v2";
- reg = <0x0c012000 0x2a8>,
- <0x0a8f8800 0x400>;
- reg-names = "qusb_phy_base",
- "qscratch_base";
+ reg = <0x0c012000 0x2a8>;
+ reg-names = "qusb_phy_base";
vdd-supply = <&pmcobalt_l1>;
vdda18-supply = <&pmcobalt_l12>;
vdda33-supply = <&pmcobalt_l24>;
@@ -2061,27 +2093,22 @@
tsens0: tsens@10aa000 {
compatible = "qcom,msmcobalt-tsens";
- reg = <0x10aa000 0x2000>,
- <0x74230 0x1000>;
- reg-names = "tsens_physical", "tsens_eeprom_physical";
+ reg = <0x10aa000 0x2000>;
+ reg-names = "tsens_physical";
interrupts = <0 458 0>, <0 445 0>;
interrupt-names = "tsens-upper-lower", "tsens-critical";
qcom,sensors = <14>;
- qcom,slope = <2901 2846 3200 3200 3200 3200 3200 3200 3200
- 3200 3200 3200 3200 3200>;
};
tsens1: tsens@10ad000 {
compatible = "qcom,msmcobalt-tsens";
- reg = <0x10ad000 0x2000>,
- <0x75230 0x1000>;
- reg-names = "tsens_physical", "tsens_eeprom_physical";
+ reg = <0x10ad000 0x2000>;
+ reg-names = "tsens_physical";
interrupts = <0 184 0>, <0 430 0>;
interrupt-names = "tsens-upper-lower", "tsens-critical";
qcom,client-id = <14 15 16 17 18 19 20 21>;
qcom,sensor-id = <0 1 3 4 5 6 7 2>;
qcom,sensors = <8>;
- qcom,slope = <2901 2846 3200 3200 3200 3200 3200 3200>;
};
qcom,sensor-information {
@@ -2300,6 +2327,8 @@
qcom,use-sw-aes-xts-algo;
qcom,use-sw-aes-ccm-algo;
qcom,use-sw-ahash-algo;
+ qcom,use-sw-aead-algo;
+ qcom,use-sw-hmac-algo;
};
qcom_cedev: qcedev@1DE0000{
@@ -2891,6 +2920,8 @@
clock-names = "bus_clk", "rot_clk";
clocks = <&clock_mmss clk_mmss_mdss_axi_clk>,
<&clock_mmss clk_mmss_mdss_rot_clk>;
+ proxy-supply = <&gdsc_mdss>;
+ qcom,proxy-consumer-enable;
status = "ok";
};
diff --git a/arch/arm/boot/dts/qcom/msmfalcon-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-pinctrl.dtsi
index a397310d4a61..6a000e4d4fd0 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/msmfalcon-pinctrl.dtsi
@@ -11,9 +11,9 @@
*/
&soc {
- tlmm: pinctrl@03400000 {
+ tlmm: pinctrl@03000000 {
compatible = "qcom,msmfalcon-pinctrl";
- reg = <0x03400000 0xc00000>;
+ reg = <0x03000000 0xc00000>;
interrupts = <0 208 0>;
gpio-controller;
#gpio-cells = <2>;
diff --git a/arch/arm/mach-s3c64xx/dev-audio.c b/arch/arm/mach-s3c64xx/dev-audio.c
index ff780a8d8366..9a42736ef4ac 100644
--- a/arch/arm/mach-s3c64xx/dev-audio.c
+++ b/arch/arm/mach-s3c64xx/dev-audio.c
@@ -54,12 +54,12 @@ static int s3c64xx_i2s_cfg_gpio(struct platform_device *pdev)
static struct resource s3c64xx_iis0_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_IIS0, SZ_256),
- [1] = DEFINE_RES_DMA(DMACH_I2S0_OUT),
- [2] = DEFINE_RES_DMA(DMACH_I2S0_IN),
};
-static struct s3c_audio_pdata i2sv3_pdata = {
+static struct s3c_audio_pdata i2s0_pdata = {
.cfg_gpio = s3c64xx_i2s_cfg_gpio,
+ .dma_playback = DMACH_I2S0_OUT,
+ .dma_capture = DMACH_I2S0_IN,
};
struct platform_device s3c64xx_device_iis0 = {
@@ -68,15 +68,19 @@ struct platform_device s3c64xx_device_iis0 = {
.num_resources = ARRAY_SIZE(s3c64xx_iis0_resource),
.resource = s3c64xx_iis0_resource,
.dev = {
- .platform_data = &i2sv3_pdata,
+ .platform_data = &i2s0_pdata,
},
};
EXPORT_SYMBOL(s3c64xx_device_iis0);
static struct resource s3c64xx_iis1_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_IIS1, SZ_256),
- [1] = DEFINE_RES_DMA(DMACH_I2S1_OUT),
- [2] = DEFINE_RES_DMA(DMACH_I2S1_IN),
+};
+
+static struct s3c_audio_pdata i2s1_pdata = {
+ .cfg_gpio = s3c64xx_i2s_cfg_gpio,
+ .dma_playback = DMACH_I2S1_OUT,
+ .dma_capture = DMACH_I2S1_IN,
};
struct platform_device s3c64xx_device_iis1 = {
@@ -85,19 +89,19 @@ struct platform_device s3c64xx_device_iis1 = {
.num_resources = ARRAY_SIZE(s3c64xx_iis1_resource),
.resource = s3c64xx_iis1_resource,
.dev = {
- .platform_data = &i2sv3_pdata,
+ .platform_data = &i2s1_pdata,
},
};
EXPORT_SYMBOL(s3c64xx_device_iis1);
static struct resource s3c64xx_iisv4_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_IISV4, SZ_256),
- [1] = DEFINE_RES_DMA(DMACH_HSI_I2SV40_TX),
- [2] = DEFINE_RES_DMA(DMACH_HSI_I2SV40_RX),
};
static struct s3c_audio_pdata i2sv4_pdata = {
.cfg_gpio = s3c64xx_i2s_cfg_gpio,
+ .dma_playback = DMACH_HSI_I2SV40_TX,
+ .dma_capture = DMACH_HSI_I2SV40_RX,
.type = {
.i2s = {
.quirks = QUIRK_PRI_6CHAN,
@@ -142,12 +146,12 @@ static int s3c64xx_pcm_cfg_gpio(struct platform_device *pdev)
static struct resource s3c64xx_pcm0_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_PCM0, SZ_256),
- [1] = DEFINE_RES_DMA(DMACH_PCM0_TX),
- [2] = DEFINE_RES_DMA(DMACH_PCM0_RX),
};
static struct s3c_audio_pdata s3c_pcm0_pdata = {
.cfg_gpio = s3c64xx_pcm_cfg_gpio,
+ .dma_capture = DMACH_PCM0_RX,
+ .dma_playback = DMACH_PCM0_TX,
};
struct platform_device s3c64xx_device_pcm0 = {
@@ -163,12 +167,12 @@ EXPORT_SYMBOL(s3c64xx_device_pcm0);
static struct resource s3c64xx_pcm1_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_PCM1, SZ_256),
- [1] = DEFINE_RES_DMA(DMACH_PCM1_TX),
- [2] = DEFINE_RES_DMA(DMACH_PCM1_RX),
};
static struct s3c_audio_pdata s3c_pcm1_pdata = {
.cfg_gpio = s3c64xx_pcm_cfg_gpio,
+ .dma_playback = DMACH_PCM1_TX,
+ .dma_capture = DMACH_PCM1_RX,
};
struct platform_device s3c64xx_device_pcm1 = {
@@ -196,13 +200,14 @@ static int s3c64xx_ac97_cfg_gpe(struct platform_device *pdev)
static struct resource s3c64xx_ac97_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_AC97, SZ_256),
- [1] = DEFINE_RES_DMA(DMACH_AC97_PCMOUT),
- [2] = DEFINE_RES_DMA(DMACH_AC97_PCMIN),
- [3] = DEFINE_RES_DMA(DMACH_AC97_MICIN),
- [4] = DEFINE_RES_IRQ(IRQ_AC97),
+ [1] = DEFINE_RES_IRQ(IRQ_AC97),
};
-static struct s3c_audio_pdata s3c_ac97_pdata;
+static struct s3c_audio_pdata s3c_ac97_pdata = {
+ .dma_playback = DMACH_AC97_PCMOUT,
+ .dma_capture = DMACH_AC97_PCMIN,
+ .dma_capture_mic = DMACH_AC97_MICIN,
+};
static u64 s3c64xx_ac97_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-s3c64xx/include/mach/dma.h b/arch/arm/mach-s3c64xx/include/mach/dma.h
index 096e14073bd9..9c739eafe95c 100644
--- a/arch/arm/mach-s3c64xx/include/mach/dma.h
+++ b/arch/arm/mach-s3c64xx/include/mach/dma.h
@@ -14,38 +14,38 @@
#define S3C64XX_DMA_CHAN(name) ((unsigned long)(name))
/* DMA0/SDMA0 */
-#define DMACH_UART0 S3C64XX_DMA_CHAN("uart0_tx")
-#define DMACH_UART0_SRC2 S3C64XX_DMA_CHAN("uart0_rx")
-#define DMACH_UART1 S3C64XX_DMA_CHAN("uart1_tx")
-#define DMACH_UART1_SRC2 S3C64XX_DMA_CHAN("uart1_rx")
-#define DMACH_UART2 S3C64XX_DMA_CHAN("uart2_tx")
-#define DMACH_UART2_SRC2 S3C64XX_DMA_CHAN("uart2_rx")
-#define DMACH_UART3 S3C64XX_DMA_CHAN("uart3_tx")
-#define DMACH_UART3_SRC2 S3C64XX_DMA_CHAN("uart3_rx")
-#define DMACH_PCM0_TX S3C64XX_DMA_CHAN("pcm0_tx")
-#define DMACH_PCM0_RX S3C64XX_DMA_CHAN("pcm0_rx")
-#define DMACH_I2S0_OUT S3C64XX_DMA_CHAN("i2s0_tx")
-#define DMACH_I2S0_IN S3C64XX_DMA_CHAN("i2s0_rx")
+#define DMACH_UART0 "uart0_tx"
+#define DMACH_UART0_SRC2 "uart0_rx"
+#define DMACH_UART1 "uart1_tx"
+#define DMACH_UART1_SRC2 "uart1_rx"
+#define DMACH_UART2 "uart2_tx"
+#define DMACH_UART2_SRC2 "uart2_rx"
+#define DMACH_UART3 "uart3_tx"
+#define DMACH_UART3_SRC2 "uart3_rx"
+#define DMACH_PCM0_TX "pcm0_tx"
+#define DMACH_PCM0_RX "pcm0_rx"
+#define DMACH_I2S0_OUT "i2s0_tx"
+#define DMACH_I2S0_IN "i2s0_rx"
#define DMACH_SPI0_TX S3C64XX_DMA_CHAN("spi0_tx")
#define DMACH_SPI0_RX S3C64XX_DMA_CHAN("spi0_rx")
-#define DMACH_HSI_I2SV40_TX S3C64XX_DMA_CHAN("i2s2_tx")
-#define DMACH_HSI_I2SV40_RX S3C64XX_DMA_CHAN("i2s2_rx")
+#define DMACH_HSI_I2SV40_TX "i2s2_tx"
+#define DMACH_HSI_I2SV40_RX "i2s2_rx"
/* DMA1/SDMA1 */
-#define DMACH_PCM1_TX S3C64XX_DMA_CHAN("pcm1_tx")
-#define DMACH_PCM1_RX S3C64XX_DMA_CHAN("pcm1_rx")
-#define DMACH_I2S1_OUT S3C64XX_DMA_CHAN("i2s1_tx")
-#define DMACH_I2S1_IN S3C64XX_DMA_CHAN("i2s1_rx")
+#define DMACH_PCM1_TX "pcm1_tx"
+#define DMACH_PCM1_RX "pcm1_rx"
+#define DMACH_I2S1_OUT "i2s1_tx"
+#define DMACH_I2S1_IN "i2s1_rx"
#define DMACH_SPI1_TX S3C64XX_DMA_CHAN("spi1_tx")
#define DMACH_SPI1_RX S3C64XX_DMA_CHAN("spi1_rx")
-#define DMACH_AC97_PCMOUT S3C64XX_DMA_CHAN("ac97_out")
-#define DMACH_AC97_PCMIN S3C64XX_DMA_CHAN("ac97_in")
-#define DMACH_AC97_MICIN S3C64XX_DMA_CHAN("ac97_mic")
-#define DMACH_PWM S3C64XX_DMA_CHAN("pwm")
-#define DMACH_IRDA S3C64XX_DMA_CHAN("irda")
-#define DMACH_EXTERNAL S3C64XX_DMA_CHAN("external")
-#define DMACH_SECURITY_RX S3C64XX_DMA_CHAN("sec_rx")
-#define DMACH_SECURITY_TX S3C64XX_DMA_CHAN("sec_tx")
+#define DMACH_AC97_PCMOUT "ac97_out"
+#define DMACH_AC97_PCMIN "ac97_in"
+#define DMACH_AC97_MICIN "ac97_mic"
+#define DMACH_PWM "pwm"
+#define DMACH_IRDA "irda"
+#define DMACH_EXTERNAL "external"
+#define DMACH_SECURITY_RX "sec_rx"
+#define DMACH_SECURITY_TX "sec_tx"
enum dma_ch {
DMACH_MAX = 32
diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c
index 82074625de5c..e212f9d804bd 100644
--- a/arch/arm/plat-samsung/devs.c
+++ b/arch/arm/plat-samsung/devs.c
@@ -65,6 +65,7 @@
#include <linux/platform_data/usb-ohci-s3c2410.h>
#include <plat/usb-phy.h>
#include <plat/regs-spi.h>
+#include <linux/platform_data/asoc-s3c.h>
#include <linux/platform_data/spi-s3c64xx.h>
static u64 samsung_device_dma_mask = DMA_BIT_MASK(32);
@@ -74,9 +75,12 @@ static u64 samsung_device_dma_mask = DMA_BIT_MASK(32);
static struct resource s3c_ac97_resource[] = {
[0] = DEFINE_RES_MEM(S3C2440_PA_AC97, S3C2440_SZ_AC97),
[1] = DEFINE_RES_IRQ(IRQ_S3C244X_AC97),
- [2] = DEFINE_RES_DMA_NAMED(DMACH_PCM_OUT, "PCM out"),
- [3] = DEFINE_RES_DMA_NAMED(DMACH_PCM_IN, "PCM in"),
- [4] = DEFINE_RES_DMA_NAMED(DMACH_MIC_IN, "Mic in"),
+};
+
+static struct s3c_audio_pdata s3c_ac97_pdata = {
+ .dma_playback = (void *)DMACH_PCM_OUT,
+ .dma_capture = (void *)DMACH_PCM_IN,
+ .dma_capture_mic = (void *)DMACH_MIC_IN,
};
struct platform_device s3c_device_ac97 = {
@@ -87,6 +91,7 @@ struct platform_device s3c_device_ac97 = {
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = &s3c_ac97_pdata,
}
};
#endif /* CONFIG_CPU_S3C2440 */
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index 711a73e401f7..eb02bc09b63d 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -57,6 +57,7 @@ config ARCH_QCOM
select MSM_IRQ
select THERMAL_WRITABLE_TRIPS
select RATIONAL
+ select ARCH_HAS_RESET_CONTROLLER
help
This enables support for the ARMv8 based Qualcomm chipsets.
diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig
index fa6cdb3d055f..210017d4467e 100644
--- a/arch/arm64/configs/msm-perf_defconfig
+++ b/arch/arm64/configs/msm-perf_defconfig
@@ -96,7 +96,6 @@ CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
-# CONFIG_NET_ACTIVITY_STATS is not set
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=y
CONFIG_NF_CONNTRACK_SECMARK=y
@@ -145,6 +144,7 @@ CONFIG_NETFILTER_XT_MATCH_MARK=y
CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
CONFIG_NETFILTER_XT_MATCH_POLICY=y
CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig
index c2902be72848..f263139cd2ec 100644
--- a/arch/arm64/configs/msm_defconfig
+++ b/arch/arm64/configs/msm_defconfig
@@ -98,7 +98,6 @@ CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
-# CONFIG_NET_ACTIVITY_STATS is not set
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=y
CONFIG_NF_CONNTRACK_SECMARK=y
@@ -147,6 +146,7 @@ CONFIG_NETFILTER_XT_MATCH_MARK=y
CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
CONFIG_NETFILTER_XT_MATCH_POLICY=y
CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig
index 239baffa6cdb..3a9fcfc95d00 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -99,7 +99,6 @@ CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
-# CONFIG_NET_ACTIVITY_STATS is not set
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=y
CONFIG_NF_CONNTRACK_SECMARK=y
@@ -148,6 +147,7 @@ CONFIG_NETFILTER_XT_MATCH_MARK=y
CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
CONFIG_NETFILTER_XT_MATCH_POLICY=y
CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
@@ -419,6 +419,7 @@ CONFIG_DUAL_ROLE_USB_INTF=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_VBUS_DRAW=500
CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_NCM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
CONFIG_USB_CONFIGFS_F_MTP=y
@@ -481,6 +482,7 @@ CONFIG_MSM_GLINK=y
CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
CONFIG_MSM_GLINK_SMD_XPRT=y
CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
+CONFIG_MSM_GLINK_SPI_XPRT=y
CONFIG_MSM_SPCOM=y
CONFIG_MSM_SMEM_LOGGING=y
CONFIG_MSM_SMP2P=y
@@ -495,6 +497,7 @@ CONFIG_MSM_GLINK_PKT=y
CONFIG_MSM_SPM=y
CONFIG_QCOM_SCM=y
CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QCOM_IRQ_HELPER=y
CONFIG_QCOM_MEMORY_DUMP_V2=y
CONFIG_ICNSS=y
CONFIG_MSM_RUN_QUEUE_STATS=y
@@ -510,13 +513,16 @@ CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MSM_MPM_OF=y
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_CORE_CTL_HELPER=y
+CONFIG_QCOM_REMOTEQDSS=y
CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_QSEE_IPC_IRQ_BRIDGE=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
CONFIG_QCOM_BIMC_BWMON=y
+CONFIG_ARM_MEMLAT_MON=y
CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y
+CONFIG_DEVFREQ_GOV_MEMLAT=y
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_SPDM_SCM=y
CONFIG_DEVFREQ_SPDM=y
diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig
index 8b9da2d6b0d3..a3bad54c0b5b 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -98,7 +98,6 @@ CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
-# CONFIG_NET_ACTIVITY_STATS is not set
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=y
CONFIG_NF_CONNTRACK_SECMARK=y
@@ -147,6 +146,7 @@ CONFIG_NETFILTER_XT_MATCH_MARK=y
CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
CONFIG_NETFILTER_XT_MATCH_POLICY=y
CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
@@ -421,6 +421,7 @@ CONFIG_DUAL_ROLE_USB_INTF=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_VBUS_DRAW=500
CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_NCM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
CONFIG_USB_CONFIGFS_F_MTP=y
@@ -494,6 +495,7 @@ CONFIG_MSM_GLINK=y
CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
CONFIG_MSM_GLINK_SMD_XPRT=y
CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
+CONFIG_MSM_GLINK_SPI_XPRT=y
CONFIG_MSM_SPCOM=y
CONFIG_MSM_SMEM_LOGGING=y
CONFIG_MSM_SMP2P=y
@@ -510,6 +512,7 @@ CONFIG_MSM_GLINK_PKT=y
CONFIG_MSM_SPM=y
CONFIG_QCOM_SCM=y
CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QCOM_IRQ_HELPER=y
CONFIG_QCOM_MEMORY_DUMP_V2=y
CONFIG_ICNSS=y
CONFIG_MSM_GLADIATOR_ERP_V2=y
@@ -530,6 +533,7 @@ CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MSM_MPM_OF=y
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_CORE_CTL_HELPER=y
+CONFIG_QCOM_REMOTEQDSS=y
CONFIG_MSM_SERVICE_NOTIFIER=y
CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
CONFIG_MSM_RPM_LOG=y
@@ -537,7 +541,9 @@ CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_QSEE_IPC_IRQ_BRIDGE=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
CONFIG_QCOM_BIMC_BWMON=y
+CONFIG_ARM_MEMLAT_MON=y
CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y
+CONFIG_DEVFREQ_GOV_MEMLAT=y
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_SPDM_SCM=y
CONFIG_DEVFREQ_SPDM=y
diff --git a/arch/arm64/include/asm/opcodes.h b/arch/arm64/include/asm/opcodes.h
index 4e603ea36ad3..123f45d92cd1 100644
--- a/arch/arm64/include/asm/opcodes.h
+++ b/arch/arm64/include/asm/opcodes.h
@@ -1 +1,5 @@
+#ifdef CONFIG_CPU_BIG_ENDIAN
+#define CONFIG_CPU_ENDIAN_BE8 CONFIG_CPU_BIG_ENDIAN
+#endif
+
#include <../../arm/include/asm/opcodes.h>
diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c
index 8aee3aeec3e6..c1492ba1f6d1 100644
--- a/arch/arm64/kernel/debug-monitors.c
+++ b/arch/arm64/kernel/debug-monitors.c
@@ -186,20 +186,21 @@ static void clear_regs_spsr_ss(struct pt_regs *regs)
/* EL1 Single Step Handler hooks */
static LIST_HEAD(step_hook);
-static DEFINE_RWLOCK(step_hook_lock);
+static DEFINE_SPINLOCK(step_hook_lock);
void register_step_hook(struct step_hook *hook)
{
- write_lock(&step_hook_lock);
- list_add(&hook->node, &step_hook);
- write_unlock(&step_hook_lock);
+ spin_lock(&step_hook_lock);
+ list_add_rcu(&hook->node, &step_hook);
+ spin_unlock(&step_hook_lock);
}
void unregister_step_hook(struct step_hook *hook)
{
- write_lock(&step_hook_lock);
- list_del(&hook->node);
- write_unlock(&step_hook_lock);
+ spin_lock(&step_hook_lock);
+ list_del_rcu(&hook->node);
+ spin_unlock(&step_hook_lock);
+ synchronize_rcu();
}
/*
@@ -213,15 +214,15 @@ static int call_step_hook(struct pt_regs *regs, unsigned int esr)
struct step_hook *hook;
int retval = DBG_HOOK_ERROR;
- read_lock(&step_hook_lock);
+ rcu_read_lock();
- list_for_each_entry(hook, &step_hook, node) {
+ list_for_each_entry_rcu(hook, &step_hook, node) {
retval = hook->fn(regs, esr);
if (retval == DBG_HOOK_HANDLED)
break;
}
- read_unlock(&step_hook_lock);
+ rcu_read_unlock();
return retval;
}
diff --git a/arch/arm64/kernel/vdso/vdso.S b/arch/arm64/kernel/vdso/vdso.S
index 60c1db54b41a..82379a70ef03 100644
--- a/arch/arm64/kernel/vdso/vdso.S
+++ b/arch/arm64/kernel/vdso/vdso.S
@@ -21,9 +21,8 @@
#include <linux/const.h>
#include <asm/page.h>
- __PAGE_ALIGNED_DATA
-
.globl vdso_start, vdso_end
+ .section .rodata
.balign PAGE_SIZE
vdso_start:
.incbin "arch/arm64/kernel/vdso/vdso.so"
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index 7cf7e4f94f36..06f9ffccd562 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -2115,19 +2115,12 @@ int arm_iommu_attach_device(struct device *dev,
{
int err;
int s1_bypass = 0, is_fast = 0;
- struct iommu_group *group;
iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_FAST, &is_fast);
if (is_fast)
return fast_smmu_attach_device(dev, mapping);
- group = iommu_group_get(dev);
- if (!group) {
- dev_err(dev, "Couldn't get group\n");
- return -ENODEV;
- }
-
- err = iommu_attach_group(mapping->domain, group);
+ err = iommu_attach_device(mapping->domain, dev);
if (err)
return err;
@@ -2154,8 +2147,7 @@ EXPORT_SYMBOL(arm_iommu_attach_device);
void arm_iommu_detach_device(struct device *dev)
{
struct dma_iommu_mapping *mapping;
- int is_fast;
- struct iommu_group *group;
+ int is_fast, s1_bypass = 0;
mapping = to_dma_iommu_mapping(dev);
if (!mapping) {
@@ -2169,16 +2161,14 @@ void arm_iommu_detach_device(struct device *dev)
return;
}
- group = iommu_group_get(dev);
- if (!group) {
- dev_err(dev, "Couldn't get group\n");
- return;
- }
+ iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_S1_BYPASS,
+ &s1_bypass);
- iommu_detach_group(mapping->domain, group);
+ iommu_detach_device(mapping->domain, dev);
kref_put(&mapping->kref, release_iommu_mapping);
dev->archdata.mapping = NULL;
- set_dma_ops(dev, NULL);
+ if (!s1_bypass)
+ set_dma_ops(dev, NULL);
pr_debug("Detached IOMMU controller from %s device.\n", dev_name(dev));
}
diff --git a/arch/ia64/include/asm/io.h b/arch/ia64/include/asm/io.h
index 9041bbe2b7b4..8fdb9c7eeb66 100644
--- a/arch/ia64/include/asm/io.h
+++ b/arch/ia64/include/asm/io.h
@@ -436,6 +436,7 @@ static inline void __iomem * ioremap_cache (unsigned long phys_addr, unsigned lo
return ioremap(phys_addr, size);
}
#define ioremap_cache ioremap_cache
+#define ioremap_uc ioremap_nocache
/*
diff --git a/arch/mips/alchemy/devboards/db1000.c b/arch/mips/alchemy/devboards/db1000.c
index bdeed9d13c6f..433c4b9a9f0a 100644
--- a/arch/mips/alchemy/devboards/db1000.c
+++ b/arch/mips/alchemy/devboards/db1000.c
@@ -503,15 +503,15 @@ int __init db1000_dev_setup(void)
if (board == BCSR_WHOAMI_DB1500) {
c0 = AU1500_GPIO2_INT;
c1 = AU1500_GPIO5_INT;
- d0 = AU1500_GPIO0_INT;
- d1 = AU1500_GPIO3_INT;
+ d0 = 0; /* GPIO number, NOT irq! */
+ d1 = 3; /* GPIO number, NOT irq! */
s0 = AU1500_GPIO1_INT;
s1 = AU1500_GPIO4_INT;
} else if (board == BCSR_WHOAMI_DB1100) {
c0 = AU1100_GPIO2_INT;
c1 = AU1100_GPIO5_INT;
- d0 = AU1100_GPIO0_INT;
- d1 = AU1100_GPIO3_INT;
+ d0 = 0; /* GPIO number, NOT irq! */
+ d1 = 3; /* GPIO number, NOT irq! */
s0 = AU1100_GPIO1_INT;
s1 = AU1100_GPIO4_INT;
@@ -545,15 +545,15 @@ int __init db1000_dev_setup(void)
} else if (board == BCSR_WHOAMI_DB1000) {
c0 = AU1000_GPIO2_INT;
c1 = AU1000_GPIO5_INT;
- d0 = AU1000_GPIO0_INT;
- d1 = AU1000_GPIO3_INT;
+ d0 = 0; /* GPIO number, NOT irq! */
+ d1 = 3; /* GPIO number, NOT irq! */
s0 = AU1000_GPIO1_INT;
s1 = AU1000_GPIO4_INT;
platform_add_devices(db1000_devs, ARRAY_SIZE(db1000_devs));
} else if ((board == BCSR_WHOAMI_PB1500) ||
(board == BCSR_WHOAMI_PB1500R2)) {
c0 = AU1500_GPIO203_INT;
- d0 = AU1500_GPIO201_INT;
+ d0 = 1; /* GPIO number, NOT irq! */
s0 = AU1500_GPIO202_INT;
twosocks = 0;
flashsize = 64;
@@ -566,7 +566,7 @@ int __init db1000_dev_setup(void)
*/
} else if (board == BCSR_WHOAMI_PB1100) {
c0 = AU1100_GPIO11_INT;
- d0 = AU1100_GPIO9_INT;
+ d0 = 9; /* GPIO number, NOT irq! */
s0 = AU1100_GPIO10_INT;
twosocks = 0;
flashsize = 64;
@@ -583,7 +583,6 @@ int __init db1000_dev_setup(void)
} else
return 0; /* unknown board, no further dev setup to do */
- irq_set_irq_type(d0, IRQ_TYPE_EDGE_BOTH);
irq_set_irq_type(c0, IRQ_TYPE_LEVEL_LOW);
irq_set_irq_type(s0, IRQ_TYPE_LEVEL_LOW);
@@ -597,7 +596,6 @@ int __init db1000_dev_setup(void)
c0, d0, /*s0*/0, 0, 0);
if (twosocks) {
- irq_set_irq_type(d1, IRQ_TYPE_EDGE_BOTH);
irq_set_irq_type(c1, IRQ_TYPE_LEVEL_LOW);
irq_set_irq_type(s1, IRQ_TYPE_LEVEL_LOW);
diff --git a/arch/mips/alchemy/devboards/db1550.c b/arch/mips/alchemy/devboards/db1550.c
index 5740bcfdfc7f..6c37b9326f41 100644
--- a/arch/mips/alchemy/devboards/db1550.c
+++ b/arch/mips/alchemy/devboards/db1550.c
@@ -514,7 +514,7 @@ static void __init db1550_devices(void)
AU1000_PCMCIA_MEM_PHYS_ADDR + 0x000400000 - 1,
AU1000_PCMCIA_IO_PHYS_ADDR,
AU1000_PCMCIA_IO_PHYS_ADDR + 0x000010000 - 1,
- AU1550_GPIO3_INT, AU1550_GPIO0_INT,
+ AU1550_GPIO3_INT, 0,
/*AU1550_GPIO21_INT*/0, 0, 0);
db1x_register_pcmcia_socket(
@@ -524,7 +524,7 @@ static void __init db1550_devices(void)
AU1000_PCMCIA_MEM_PHYS_ADDR + 0x004400000 - 1,
AU1000_PCMCIA_IO_PHYS_ADDR + 0x004000000,
AU1000_PCMCIA_IO_PHYS_ADDR + 0x004010000 - 1,
- AU1550_GPIO5_INT, AU1550_GPIO1_INT,
+ AU1550_GPIO5_INT, 1,
/*AU1550_GPIO22_INT*/0, 0, 1);
platform_device_register(&db1550_nand_dev);
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
index 490cea569d57..5c62065cbf22 100644
--- a/arch/mips/kernel/unaligned.c
+++ b/arch/mips/kernel/unaligned.c
@@ -885,7 +885,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
{
union mips_instruction insn;
unsigned long value;
- unsigned int res;
+ unsigned int res, preempted;
unsigned long origpc;
unsigned long orig31;
void __user *fault_addr = NULL;
@@ -1226,27 +1226,36 @@ static void emulate_load_store_insn(struct pt_regs *regs,
if (!access_ok(VERIFY_READ, addr, sizeof(*fpr)))
goto sigbus;
- /*
- * Disable preemption to avoid a race between copying
- * state from userland, migrating to another CPU and
- * updating the hardware vector register below.
- */
- preempt_disable();
-
- res = __copy_from_user_inatomic(fpr, addr,
- sizeof(*fpr));
- if (res)
- goto fault;
-
- /*
- * Update the hardware register if it is in use by the
- * task in this quantum, in order to avoid having to
- * save & restore the whole vector context.
- */
- if (test_thread_flag(TIF_USEDMSA))
- write_msa_wr(wd, fpr, df);
+ do {
+ /*
+ * If we have live MSA context keep track of
+ * whether we get preempted in order to avoid
+ * the register context we load being clobbered
+ * by the live context as it's saved during
+ * preemption. If we don't have live context
+ * then it can't be saved to clobber the value
+ * we load.
+ */
+ preempted = test_thread_flag(TIF_USEDMSA);
+
+ res = __copy_from_user_inatomic(fpr, addr,
+ sizeof(*fpr));
+ if (res)
+ goto fault;
- preempt_enable();
+ /*
+ * Update the hardware register if it is in use
+ * by the task in this quantum, in order to
+ * avoid having to save & restore the whole
+ * vector context.
+ */
+ preempt_disable();
+ if (test_thread_flag(TIF_USEDMSA)) {
+ write_msa_wr(wd, fpr, df);
+ preempted = 0;
+ }
+ preempt_enable();
+ } while (preempted);
break;
case msa_st_op:
diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h
index 0abdd4c607ed..1960b87c1c8b 100644
--- a/arch/parisc/include/asm/uaccess.h
+++ b/arch/parisc/include/asm/uaccess.h
@@ -76,6 +76,7 @@ struct exception_table_entry {
*/
struct exception_data {
unsigned long fault_ip;
+ unsigned long fault_gp;
unsigned long fault_space;
unsigned long fault_addr;
};
diff --git a/arch/parisc/kernel/asm-offsets.c b/arch/parisc/kernel/asm-offsets.c
index d2f62570a7b1..78d30d2ea2d8 100644
--- a/arch/parisc/kernel/asm-offsets.c
+++ b/arch/parisc/kernel/asm-offsets.c
@@ -299,6 +299,7 @@ int main(void)
#endif
BLANK();
DEFINE(EXCDATA_IP, offsetof(struct exception_data, fault_ip));
+ DEFINE(EXCDATA_GP, offsetof(struct exception_data, fault_gp));
DEFINE(EXCDATA_SPACE, offsetof(struct exception_data, fault_space));
DEFINE(EXCDATA_ADDR, offsetof(struct exception_data, fault_addr));
BLANK();
diff --git a/arch/parisc/kernel/parisc_ksyms.c b/arch/parisc/kernel/parisc_ksyms.c
index 568b2c61ea02..3cad8aadc69e 100644
--- a/arch/parisc/kernel/parisc_ksyms.c
+++ b/arch/parisc/kernel/parisc_ksyms.c
@@ -47,11 +47,11 @@ EXPORT_SYMBOL(__cmpxchg_u64);
EXPORT_SYMBOL(lclear_user);
EXPORT_SYMBOL(lstrnlen_user);
-/* Global fixups */
-extern void fixup_get_user_skip_1(void);
-extern void fixup_get_user_skip_2(void);
-extern void fixup_put_user_skip_1(void);
-extern void fixup_put_user_skip_2(void);
+/* Global fixups - defined as int to avoid creation of function pointers */
+extern int fixup_get_user_skip_1;
+extern int fixup_get_user_skip_2;
+extern int fixup_put_user_skip_1;
+extern int fixup_put_user_skip_2;
EXPORT_SYMBOL(fixup_get_user_skip_1);
EXPORT_SYMBOL(fixup_get_user_skip_2);
EXPORT_SYMBOL(fixup_put_user_skip_1);
diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c
index 553b09855cfd..77e2262c97f6 100644
--- a/arch/parisc/kernel/traps.c
+++ b/arch/parisc/kernel/traps.c
@@ -798,6 +798,9 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
if (fault_space == 0 && !faulthandler_disabled())
{
+ /* Clean up and return if in exception table. */
+ if (fixup_exception(regs))
+ return;
pdc_chassis_send_status(PDC_CHASSIS_DIRECT_PANIC);
parisc_terminate("Kernel Fault", regs, code, fault_address);
}
diff --git a/arch/parisc/lib/fixup.S b/arch/parisc/lib/fixup.S
index 536ef66bb94b..1052b747e011 100644
--- a/arch/parisc/lib/fixup.S
+++ b/arch/parisc/lib/fixup.S
@@ -26,6 +26,7 @@
#ifdef CONFIG_SMP
.macro get_fault_ip t1 t2
+ loadgp
addil LT%__per_cpu_offset,%r27
LDREG RT%__per_cpu_offset(%r1),\t1
/* t2 = smp_processor_id() */
@@ -40,14 +41,19 @@
LDREG RT%exception_data(%r1),\t1
/* t1 = this_cpu_ptr(&exception_data) */
add,l \t1,\t2,\t1
+ /* %r27 = t1->fault_gp - restore gp */
+ LDREG EXCDATA_GP(\t1), %r27
/* t1 = t1->fault_ip */
LDREG EXCDATA_IP(\t1), \t1
.endm
#else
.macro get_fault_ip t1 t2
+ loadgp
/* t1 = this_cpu_ptr(&exception_data) */
addil LT%exception_data,%r27
LDREG RT%exception_data(%r1),\t2
+ /* %r27 = t2->fault_gp - restore gp */
+ LDREG EXCDATA_GP(\t2), %r27
/* t1 = t2->fault_ip */
LDREG EXCDATA_IP(\t2), \t1
.endm
diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c
index a762864ec92e..f9064449908a 100644
--- a/arch/parisc/mm/fault.c
+++ b/arch/parisc/mm/fault.c
@@ -151,6 +151,7 @@ int fixup_exception(struct pt_regs *regs)
struct exception_data *d;
d = this_cpu_ptr(&exception_data);
d->fault_ip = regs->iaoq[0];
+ d->fault_gp = regs->gr[27];
d->fault_space = regs->isr;
d->fault_addr = regs->ior;
diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c
index 9833fee493ec..807f1594701d 100644
--- a/arch/powerpc/mm/hugetlbpage.c
+++ b/arch/powerpc/mm/hugetlbpage.c
@@ -486,13 +486,13 @@ static void hugepd_free(struct mmu_gather *tlb, void *hugepte)
{
struct hugepd_freelist **batchp;
- batchp = this_cpu_ptr(&hugepd_freelist_cur);
+ batchp = &get_cpu_var(hugepd_freelist_cur);
if (atomic_read(&tlb->mm->mm_users) < 2 ||
cpumask_equal(mm_cpumask(tlb->mm),
cpumask_of(smp_processor_id()))) {
kmem_cache_free(hugepte_cache, hugepte);
- put_cpu_var(hugepd_freelist_cur);
+ put_cpu_var(hugepd_freelist_cur);
return;
}
diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h
index c873e682b67f..2b2ced9dc00a 100644
--- a/arch/s390/include/asm/pci.h
+++ b/arch/s390/include/asm/pci.h
@@ -45,7 +45,7 @@ struct zpci_fmb {
u64 rpcit_ops;
u64 dma_rbytes;
u64 dma_wbytes;
-} __packed __aligned(16);
+} __packed __aligned(64);
enum zpci_state {
ZPCI_FN_STATE_RESERVED,
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index 857b6526d298..424e6809ad07 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -1197,114 +1197,12 @@ cleanup_critical:
.quad .Lpsw_idle_lpsw
.Lcleanup_save_fpu_regs:
- TSTMSK __LC_CPU_FLAGS,_CIF_FPU
- bor %r14
- clg %r9,BASED(.Lcleanup_save_fpu_regs_done)
- jhe 5f
- clg %r9,BASED(.Lcleanup_save_fpu_regs_fp)
- jhe 4f
- clg %r9,BASED(.Lcleanup_save_fpu_regs_vx_high)
- jhe 3f
- clg %r9,BASED(.Lcleanup_save_fpu_regs_vx_low)
- jhe 2f
- clg %r9,BASED(.Lcleanup_save_fpu_fpc_end)
- jhe 1f
- lg %r2,__LC_CURRENT
- aghi %r2,__TASK_thread
-0: # Store floating-point controls
- stfpc __THREAD_FPU_fpc(%r2)
-1: # Load register save area and check if VX is active
- lg %r3,__THREAD_FPU_regs(%r2)
- TSTMSK __LC_MACHINE_FLAGS,MACHINE_FLAG_VX
- jz 4f # no VX -> store FP regs
-2: # Store vector registers (V0-V15)
- VSTM %v0,%v15,0,%r3 # vstm 0,15,0(3)
-3: # Store vector registers (V16-V31)
- VSTM %v16,%v31,256,%r3 # vstm 16,31,256(3)
- j 5f # -> done, set CIF_FPU flag
-4: # Store floating-point registers
- std 0,0(%r3)
- std 1,8(%r3)
- std 2,16(%r3)
- std 3,24(%r3)
- std 4,32(%r3)
- std 5,40(%r3)
- std 6,48(%r3)
- std 7,56(%r3)
- std 8,64(%r3)
- std 9,72(%r3)
- std 10,80(%r3)
- std 11,88(%r3)
- std 12,96(%r3)
- std 13,104(%r3)
- std 14,112(%r3)
- std 15,120(%r3)
-5: # Set CIF_FPU flag
- oi __LC_CPU_FLAGS+7,_CIF_FPU
- lg %r9,48(%r11) # return from save_fpu_regs
+ larl %r9,save_fpu_regs
br %r14
-.Lcleanup_save_fpu_fpc_end:
- .quad .Lsave_fpu_regs_fpc_end
-.Lcleanup_save_fpu_regs_vx_low:
- .quad .Lsave_fpu_regs_vx_low
-.Lcleanup_save_fpu_regs_vx_high:
- .quad .Lsave_fpu_regs_vx_high
-.Lcleanup_save_fpu_regs_fp:
- .quad .Lsave_fpu_regs_fp
-.Lcleanup_save_fpu_regs_done:
- .quad .Lsave_fpu_regs_done
.Lcleanup_load_fpu_regs:
- TSTMSK __LC_CPU_FLAGS,_CIF_FPU
- bnor %r14
- clg %r9,BASED(.Lcleanup_load_fpu_regs_done)
- jhe 1f
- clg %r9,BASED(.Lcleanup_load_fpu_regs_fp)
- jhe 2f
- clg %r9,BASED(.Lcleanup_load_fpu_regs_vx_high)
- jhe 3f
- clg %r9,BASED(.Lcleanup_load_fpu_regs_vx)
- jhe 4f
- lg %r4,__LC_CURRENT
- aghi %r4,__TASK_thread
- lfpc __THREAD_FPU_fpc(%r4)
- TSTMSK __LC_MACHINE_FLAGS,MACHINE_FLAG_VX
- lg %r4,__THREAD_FPU_regs(%r4) # %r4 <- reg save area
- jz 2f # -> no VX, load FP regs
-4: # Load V0 ..V15 registers
- VLM %v0,%v15,0,%r4
-3: # Load V16..V31 registers
- VLM %v16,%v31,256,%r4
- j 1f
-2: # Load floating-point registers
- ld 0,0(%r4)
- ld 1,8(%r4)
- ld 2,16(%r4)
- ld 3,24(%r4)
- ld 4,32(%r4)
- ld 5,40(%r4)
- ld 6,48(%r4)
- ld 7,56(%r4)
- ld 8,64(%r4)
- ld 9,72(%r4)
- ld 10,80(%r4)
- ld 11,88(%r4)
- ld 12,96(%r4)
- ld 13,104(%r4)
- ld 14,112(%r4)
- ld 15,120(%r4)
-1: # Clear CIF_FPU bit
- ni __LC_CPU_FLAGS+7,255-_CIF_FPU
- lg %r9,48(%r11) # return from load_fpu_regs
+ larl %r9,load_fpu_regs
br %r14
-.Lcleanup_load_fpu_regs_vx:
- .quad .Lload_fpu_regs_vx
-.Lcleanup_load_fpu_regs_vx_high:
- .quad .Lload_fpu_regs_vx_high
-.Lcleanup_load_fpu_regs_fp:
- .quad .Lload_fpu_regs_fp
-.Lcleanup_load_fpu_regs_done:
- .quad .Lload_fpu_regs_done
/*
* Integer constants
diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S
index 58b719fa8067..1ad2407c7f75 100644
--- a/arch/s390/kernel/head64.S
+++ b/arch/s390/kernel/head64.S
@@ -16,7 +16,7 @@
__HEAD
ENTRY(startup_continue)
- tm __LC_STFL_FAC_LIST+6,0x80 # LPP available ?
+ tm __LC_STFL_FAC_LIST+5,0x80 # LPP available ?
jz 0f
xc __LC_LPP+1(7,0),__LC_LPP+1 # clear lpp and current_pid
mvi __LC_LPP,0x80 # and set LPP_MAGIC
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index c837bcacf218..1f581eb61bc2 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -329,6 +329,7 @@ static void __init setup_lowcore(void)
+ PAGE_SIZE - STACK_FRAME_OVERHEAD - sizeof(struct pt_regs);
lc->current_task = (unsigned long) init_thread_union.thread_info.task;
lc->thread_info = (unsigned long) &init_thread_union;
+ lc->lpp = LPP_MAGIC;
lc->machine_flags = S390_lowcore.machine_flags;
lc->stfl_fac_list = S390_lowcore.stfl_fac_list;
memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list,
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
index 7ef12a3ace3a..19442395f413 100644
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -871,8 +871,11 @@ static inline int barsize(u8 size)
static int zpci_mem_init(void)
{
+ BUILD_BUG_ON(!is_power_of_2(__alignof__(struct zpci_fmb)) ||
+ __alignof__(struct zpci_fmb) < sizeof(struct zpci_fmb));
+
zdev_fmb_cache = kmem_cache_create("PCI_FMB_cache", sizeof(struct zpci_fmb),
- 16, 0, NULL);
+ __alignof__(struct zpci_fmb), 0, NULL);
if (!zdev_fmb_cache)
goto error_zdev;
diff --git a/arch/sh/mm/kmap.c b/arch/sh/mm/kmap.c
index ec29e14ec5a8..bf25d7c79a2d 100644
--- a/arch/sh/mm/kmap.c
+++ b/arch/sh/mm/kmap.c
@@ -36,6 +36,7 @@ void *kmap_coherent(struct page *page, unsigned long addr)
BUG_ON(!test_bit(PG_dcache_clean, &page->flags));
+ preempt_disable();
pagefault_disable();
idx = FIX_CMAP_END -
@@ -64,4 +65,5 @@ void kunmap_coherent(void *kvaddr)
}
pagefault_enable();
+ preempt_enable();
}
diff --git a/arch/um/configs/x86_64_defconfig b/arch/um/configs/x86_64_defconfig
index f9ecc8270f54..7a67b7ac1a7e 100644
--- a/arch/um/configs/x86_64_defconfig
+++ b/arch/um/configs/x86_64_defconfig
@@ -53,7 +53,6 @@ CONFIG_UNIX=y
CONFIG_INET=y
# CONFIG_INET_LRO is not set
# CONFIG_IPV6 is not set
-# CONFIG_NET_ACTIVITY_STATS is not set
CONFIG_UML_NET=y
CONFIG_UML_NET_ETHERTAP=y
CONFIG_UML_NET_TUNTAP=y
diff --git a/arch/um/configs/x86_64_um_defconfig b/arch/um/configs/x86_64_um_defconfig
index 487b68bad250..a37e38ef12fe 100644
--- a/arch/um/configs/x86_64_um_defconfig
+++ b/arch/um/configs/x86_64_um_defconfig
@@ -655,7 +655,6 @@ CONFIG_IPV6_MULTIPLE_TABLES=y
# CONFIG_IPV6_SUBTREES is not set
# CONFIG_IPV6_MROUTE is not set
# CONFIG_ANDROID_PARANOID_NETWORK is not set
-# CONFIG_NET_ACTIVITY_STATS is not set
# CONFIG_NETWORK_SECMARK is not set
# CONFIG_NET_PTP_CLASSIFY is not set
# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c
index 29880c9b324e..e22e57298522 100644
--- a/arch/um/drivers/mconsole_kern.c
+++ b/arch/um/drivers/mconsole_kern.c
@@ -133,7 +133,7 @@ void mconsole_proc(struct mc_request *req)
ptr += strlen("proc");
ptr = skip_spaces(ptr);
- file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY);
+ file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY, 0);
if (IS_ERR(file)) {
mconsole_reply(req, "Failed to open file", 1, 0);
printk(KERN_ERR "open /proc/%s: %ld\n", ptr, PTR_ERR(file));
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index d57eca6b3777..737967e2cf28 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1146,22 +1146,23 @@ config MICROCODE
bool "CPU microcode loading support"
default y
depends on CPU_SUP_AMD || CPU_SUP_INTEL
- depends on BLK_DEV_INITRD
select FW_LOADER
---help---
-
If you say Y here, you will be able to update the microcode on
- certain Intel and AMD processors. The Intel support is for the
- IA32 family, e.g. Pentium Pro, Pentium II, Pentium III, Pentium 4,
- Xeon etc. The AMD support is for families 0x10 and later. You will
- obviously need the actual microcode binary data itself which is not
- shipped with the Linux kernel.
-
- This option selects the general module only, you need to select
- at least one vendor specific module as well.
-
- To compile this driver as a module, choose M here: the module
- will be called microcode.
+ Intel and AMD processors. The Intel support is for the IA32 family,
+ e.g. Pentium Pro, Pentium II, Pentium III, Pentium 4, Xeon etc. The
+ AMD support is for families 0x10 and later. You will obviously need
+ the actual microcode binary data itself which is not shipped with
+ the Linux kernel.
+
+ The preferred method to load microcode from a detached initrd is described
+ in Documentation/x86/early-microcode.txt. For that you need to enable
+ CONFIG_BLK_DEV_INITRD in order for the loader to be able to scan the
+ initrd for microcode blobs.
+
+ In addition, you can build-in the microcode into the kernel. For that you
+ need to enable FIRMWARE_IN_KERNEL and add the vendor-supplied microcode
+ to the CONFIG_EXTRA_FIRMWARE config option.
config MICROCODE_INTEL
bool "Intel microcode loading support"
diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c
index 03663740c866..1a4477cedc49 100644
--- a/arch/x86/entry/common.c
+++ b/arch/x86/entry/common.c
@@ -268,6 +268,7 @@ static void exit_to_usermode_loop(struct pt_regs *regs, u32 cached_flags)
/* Called with IRQs disabled. */
__visible inline void prepare_exit_to_usermode(struct pt_regs *regs)
{
+ struct thread_info *ti = pt_regs_to_thread_info(regs);
u32 cached_flags;
if (IS_ENABLED(CONFIG_PROVE_LOCKING) && WARN_ON(!irqs_disabled()))
@@ -275,12 +276,22 @@ __visible inline void prepare_exit_to_usermode(struct pt_regs *regs)
lockdep_sys_exit();
- cached_flags =
- READ_ONCE(pt_regs_to_thread_info(regs)->flags);
+ cached_flags = READ_ONCE(ti->flags);
if (unlikely(cached_flags & EXIT_TO_USERMODE_LOOP_FLAGS))
exit_to_usermode_loop(regs, cached_flags);
+#ifdef CONFIG_COMPAT
+ /*
+ * Compat syscalls set TS_COMPAT. Make sure we clear it before
+ * returning to user mode. We need to clear it *after* signal
+ * handling, because syscall restart has a fixup for compat
+ * syscalls. The fixup is exercised by the ptrace_syscall_32
+ * selftest.
+ */
+ ti->status &= ~TS_COMPAT;
+#endif
+
user_enter();
}
@@ -332,14 +343,6 @@ __visible inline void syscall_return_slowpath(struct pt_regs *regs)
if (unlikely(cached_flags & SYSCALL_EXIT_WORK_FLAGS))
syscall_slow_exit_work(regs, cached_flags);
-#ifdef CONFIG_COMPAT
- /*
- * Compat syscalls set TS_COMPAT. Make sure we clear it before
- * returning to user mode.
- */
- ti->status &= ~TS_COMPAT;
-#endif
-
local_irq_disable();
prepare_exit_to_usermode(regs);
}
diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index a30316bf801a..163769d82475 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -638,8 +638,8 @@ static inline void entering_irq(void)
static inline void entering_ack_irq(void)
{
- ack_APIC_irq();
entering_irq();
+ ack_APIC_irq();
}
static inline void ipi_entering_ack_irq(void)
diff --git a/arch/x86/include/asm/hw_irq.h b/arch/x86/include/asm/hw_irq.h
index 1e3408e88604..59caa55fb9b5 100644
--- a/arch/x86/include/asm/hw_irq.h
+++ b/arch/x86/include/asm/hw_irq.h
@@ -136,6 +136,7 @@ struct irq_alloc_info {
struct irq_cfg {
unsigned int dest_apicid;
u8 vector;
+ u8 old_vector;
};
extern struct irq_cfg *irq_cfg(unsigned int irq);
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 30cfd64295a0..9d2abb2a41d2 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -41,7 +41,7 @@
#define KVM_PIO_PAGE_OFFSET 1
#define KVM_COALESCED_MMIO_PAGE_OFFSET 2
-#define KVM_HALT_POLL_NS_DEFAULT 500000
+#define KVM_HALT_POLL_NS_DEFAULT 400000
#define KVM_IRQCHIP_NUM_PINS KVM_IOAPIC_NUM_PINS
diff --git a/arch/x86/include/asm/microcode.h b/arch/x86/include/asm/microcode.h
index 34e62b1dcfce..712b24ed3a64 100644
--- a/arch/x86/include/asm/microcode.h
+++ b/arch/x86/include/asm/microcode.h
@@ -2,6 +2,7 @@
#define _ASM_X86_MICROCODE_H
#include <linux/earlycpio.h>
+#include <linux/initrd.h>
#define native_rdmsr(msr, val1, val2) \
do { \
@@ -168,4 +169,29 @@ static inline void reload_early_microcode(void) { }
static inline bool
get_builtin_firmware(struct cpio_data *cd, const char *name) { return false; }
#endif
+
+static inline unsigned long get_initrd_start(void)
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+ return initrd_start;
+#else
+ return 0;
+#endif
+}
+
+static inline unsigned long get_initrd_start_addr(void)
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+#ifdef CONFIG_X86_32
+ unsigned long *initrd_start_p = (unsigned long *)__pa_nodebug(&initrd_start);
+
+ return (unsigned long)__pa_nodebug(*initrd_start_p);
+#else
+ return get_initrd_start();
+#endif
+#else /* CONFIG_BLK_DEV_INITRD */
+ return 0;
+#endif
+}
+
#endif /* _ASM_X86_MICROCODE_H */
diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h
index fa1195dae425..164e3f8d3c3d 100644
--- a/arch/x86/include/asm/pci_x86.h
+++ b/arch/x86/include/asm/pci_x86.h
@@ -93,6 +93,8 @@ extern raw_spinlock_t pci_config_lock;
extern int (*pcibios_enable_irq)(struct pci_dev *dev);
extern void (*pcibios_disable_irq)(struct pci_dev *dev);
+extern bool mp_should_keep_irq(struct device *dev);
+
struct pci_raw_ops {
int (*read)(unsigned int domain, unsigned int bus, unsigned int devfn,
int reg, int len, u32 *val);
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index 7bcb861a04e5..5a2ed3ed2f26 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -165,6 +165,7 @@ struct x86_pmu_capability {
#define GLOBAL_STATUS_ASIF BIT_ULL(60)
#define GLOBAL_STATUS_COUNTERS_FROZEN BIT_ULL(59)
#define GLOBAL_STATUS_LBRS_FROZEN BIT_ULL(58)
+#define GLOBAL_STATUS_TRACE_TOPAPMI BIT_ULL(55)
/*
* IBS cpuid feature detection
diff --git a/arch/x86/include/asm/xen/hypervisor.h b/arch/x86/include/asm/xen/hypervisor.h
index 8b2d4bea9962..39171b3646bb 100644
--- a/arch/x86/include/asm/xen/hypervisor.h
+++ b/arch/x86/include/asm/xen/hypervisor.h
@@ -62,4 +62,6 @@ void xen_arch_register_cpu(int num);
void xen_arch_unregister_cpu(int num);
#endif
+extern void xen_set_iopl_mask(unsigned mask);
+
#endif /* _ASM_X86_XEN_HYPERVISOR_H */
diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c
index a35f6b5473f4..7af2505f20c2 100644
--- a/arch/x86/kernel/apic/vector.c
+++ b/arch/x86/kernel/apic/vector.c
@@ -211,6 +211,7 @@ update:
*/
cpumask_and(d->old_domain, d->old_domain, cpu_online_mask);
d->move_in_progress = !cpumask_empty(d->old_domain);
+ d->cfg.old_vector = d->move_in_progress ? d->cfg.vector : 0;
d->cfg.vector = vector;
cpumask_copy(d->domain, vector_cpumask);
success:
@@ -653,46 +654,97 @@ void irq_complete_move(struct irq_cfg *cfg)
}
/*
- * Called with @desc->lock held and interrupts disabled.
+ * Called from fixup_irqs() with @desc->lock held and interrupts disabled.
*/
void irq_force_complete_move(struct irq_desc *desc)
{
struct irq_data *irqdata = irq_desc_get_irq_data(desc);
struct apic_chip_data *data = apic_chip_data(irqdata);
struct irq_cfg *cfg = data ? &data->cfg : NULL;
+ unsigned int cpu;
if (!cfg)
return;
- __irq_complete_move(cfg, cfg->vector);
-
/*
* This is tricky. If the cleanup of @data->old_domain has not been
* done yet, then the following setaffinity call will fail with
* -EBUSY. This can leave the interrupt in a stale state.
*
- * The cleanup cannot make progress because we hold @desc->lock. So in
- * case @data->old_domain is not yet cleaned up, we need to drop the
- * lock and acquire it again. @desc cannot go away, because the
- * hotplug code holds the sparse irq lock.
+ * All CPUs are stuck in stop machine with interrupts disabled so
+ * calling __irq_complete_move() would be completely pointless.
*/
raw_spin_lock(&vector_lock);
- /* Clean out all offline cpus (including ourself) first. */
+ /*
+ * Clean out all offline cpus (including the outgoing one) from the
+ * old_domain mask.
+ */
cpumask_and(data->old_domain, data->old_domain, cpu_online_mask);
- while (!cpumask_empty(data->old_domain)) {
+
+ /*
+ * If move_in_progress is cleared and the old_domain mask is empty,
+ * then there is nothing to cleanup. fixup_irqs() will take care of
+ * the stale vectors on the outgoing cpu.
+ */
+ if (!data->move_in_progress && cpumask_empty(data->old_domain)) {
raw_spin_unlock(&vector_lock);
- raw_spin_unlock(&desc->lock);
- cpu_relax();
- raw_spin_lock(&desc->lock);
+ return;
+ }
+
+ /*
+ * 1) The interrupt is in move_in_progress state. That means that we
+ * have not seen an interrupt since the io_apic was reprogrammed to
+ * the new vector.
+ *
+ * 2) The interrupt has fired on the new vector, but the cleanup IPIs
+ * have not been processed yet.
+ */
+ if (data->move_in_progress) {
/*
- * Reevaluate apic_chip_data. It might have been cleared after
- * we dropped @desc->lock.
+ * In theory there is a race:
+ *
+ * set_ioapic(new_vector) <-- Interrupt is raised before update
+ * is effective, i.e. it's raised on
+ * the old vector.
+ *
+ * So if the target cpu cannot handle that interrupt before
+ * the old vector is cleaned up, we get a spurious interrupt
+ * and in the worst case the ioapic irq line becomes stale.
+ *
+ * But in case of cpu hotplug this should be a non issue
+ * because if the affinity update happens right before all
+ * cpus rendevouz in stop machine, there is no way that the
+ * interrupt can be blocked on the target cpu because all cpus
+ * loops first with interrupts enabled in stop machine, so the
+ * old vector is not yet cleaned up when the interrupt fires.
+ *
+ * So the only way to run into this issue is if the delivery
+ * of the interrupt on the apic/system bus would be delayed
+ * beyond the point where the target cpu disables interrupts
+ * in stop machine. I doubt that it can happen, but at least
+ * there is a theroretical chance. Virtualization might be
+ * able to expose this, but AFAICT the IOAPIC emulation is not
+ * as stupid as the real hardware.
+ *
+ * Anyway, there is nothing we can do about that at this point
+ * w/o refactoring the whole fixup_irq() business completely.
+ * We print at least the irq number and the old vector number,
+ * so we have the necessary information when a problem in that
+ * area arises.
*/
- data = apic_chip_data(irqdata);
- if (!data)
- return;
- raw_spin_lock(&vector_lock);
+ pr_warn("IRQ fixup: irq %d move in progress, old vector %d\n",
+ irqdata->irq, cfg->old_vector);
}
+ /*
+ * If old_domain is not empty, then other cpus still have the irq
+ * descriptor set in their vector array. Clean it up.
+ */
+ for_each_cpu(cpu, data->old_domain)
+ per_cpu(vector_irq, cpu)[cfg->old_vector] = VECTOR_UNUSED;
+
+ /* Cleanup the left overs of the (half finished) move */
+ cpumask_clear(data->old_domain);
+ data->move_in_progress = 0;
raw_spin_unlock(&vector_lock);
}
#endif
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index ce47402eb2f9..ac8975a65280 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -555,10 +555,14 @@ scan_microcode(struct mc_saved_data *mc_saved_data, unsigned long *initrd,
cd.data = NULL;
cd.size = 0;
- cd = find_cpio_data(p, (void *)start, size, &offset);
- if (!cd.data) {
+ /* try built-in microcode if no initrd */
+ if (!size) {
if (!load_builtin_intel_microcode(&cd))
return UCODE_ERROR;
+ } else {
+ cd = find_cpio_data(p, (void *)start, size, &offset);
+ if (!cd.data)
+ return UCODE_ERROR;
}
return get_matching_model_microcode(0, start, cd.data, cd.size,
@@ -694,7 +698,7 @@ int __init save_microcode_in_initrd_intel(void)
if (count == 0)
return ret;
- copy_initrd_ptrs(mc_saved, mc_saved_in_initrd, initrd_start, count);
+ copy_initrd_ptrs(mc_saved, mc_saved_in_initrd, get_initrd_start(), count);
ret = save_microcode(&mc_saved_data, mc_saved, count);
if (ret)
pr_err("Cannot save microcode patches from initrd.\n");
@@ -732,16 +736,20 @@ void __init load_ucode_intel_bsp(void)
struct boot_params *p;
p = (struct boot_params *)__pa_nodebug(&boot_params);
- start = p->hdr.ramdisk_image;
size = p->hdr.ramdisk_size;
- _load_ucode_intel_bsp(
- (struct mc_saved_data *)__pa_nodebug(&mc_saved_data),
- (unsigned long *)__pa_nodebug(&mc_saved_in_initrd),
- start, size);
+ /*
+ * Set start only if we have an initrd image. We cannot use initrd_start
+ * because it is not set that early yet.
+ */
+ start = (size ? p->hdr.ramdisk_image : 0);
+
+ _load_ucode_intel_bsp((struct mc_saved_data *)__pa_nodebug(&mc_saved_data),
+ (unsigned long *)__pa_nodebug(&mc_saved_in_initrd),
+ start, size);
#else
- start = boot_params.hdr.ramdisk_image + PAGE_OFFSET;
size = boot_params.hdr.ramdisk_size;
+ start = (size ? boot_params.hdr.ramdisk_image + PAGE_OFFSET : 0);
_load_ucode_intel_bsp(&mc_saved_data, mc_saved_in_initrd, start, size);
#endif
@@ -752,20 +760,14 @@ void load_ucode_intel_ap(void)
struct mc_saved_data *mc_saved_data_p;
struct ucode_cpu_info uci;
unsigned long *mc_saved_in_initrd_p;
- unsigned long initrd_start_addr;
enum ucode_state ret;
#ifdef CONFIG_X86_32
- unsigned long *initrd_start_p;
- mc_saved_in_initrd_p =
- (unsigned long *)__pa_nodebug(mc_saved_in_initrd);
+ mc_saved_in_initrd_p = (unsigned long *)__pa_nodebug(mc_saved_in_initrd);
mc_saved_data_p = (struct mc_saved_data *)__pa_nodebug(&mc_saved_data);
- initrd_start_p = (unsigned long *)__pa_nodebug(&initrd_start);
- initrd_start_addr = (unsigned long)__pa_nodebug(*initrd_start_p);
#else
- mc_saved_data_p = &mc_saved_data;
mc_saved_in_initrd_p = mc_saved_in_initrd;
- initrd_start_addr = initrd_start;
+ mc_saved_data_p = &mc_saved_data;
#endif
/*
@@ -777,7 +779,7 @@ void load_ucode_intel_ap(void)
collect_cpu_info_early(&uci);
ret = load_microcode(mc_saved_data_p, mc_saved_in_initrd_p,
- initrd_start_addr, &uci);
+ get_initrd_start_addr(), &uci);
if (ret != UCODE_OK)
return;
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
index 2bf79d7c97df..a3aeb2cc361e 100644
--- a/arch/x86/kernel/cpu/perf_event.c
+++ b/arch/x86/kernel/cpu/perf_event.c
@@ -593,6 +593,19 @@ void x86_pmu_disable_all(void)
}
}
+/*
+ * There may be PMI landing after enabled=0. The PMI hitting could be before or
+ * after disable_all.
+ *
+ * If PMI hits before disable_all, the PMU will be disabled in the NMI handler.
+ * It will not be re-enabled in the NMI handler again, because enabled=0. After
+ * handling the NMI, disable_all will be called, which will not change the
+ * state either. If PMI hits after disable_all, the PMU is already disabled
+ * before entering NMI handler. The NMI handler will not change the state
+ * either.
+ *
+ * So either situation is harmless.
+ */
static void x86_pmu_disable(struct pmu *pmu)
{
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h
index d0e35ebb2adb..ee70445fbb1f 100644
--- a/arch/x86/kernel/cpu/perf_event.h
+++ b/arch/x86/kernel/cpu/perf_event.h
@@ -591,6 +591,7 @@ struct x86_pmu {
pebs_active :1,
pebs_broken :1;
int pebs_record_size;
+ int pebs_buffer_size;
void (*drain_pebs)(struct pt_regs *regs);
struct event_constraint *pebs_constraints;
void (*pebs_aliases)(struct perf_event *event);
@@ -907,6 +908,8 @@ void intel_pmu_lbr_init_hsw(void);
void intel_pmu_lbr_init_skl(void);
+void intel_pmu_pebs_data_source_nhm(void);
+
int intel_pmu_setup_lbr_filter(struct perf_event *event);
void intel_pt_interrupt(void);
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c
index e2a430021e46..078de2e86b7a 100644
--- a/arch/x86/kernel/cpu/perf_event_intel.c
+++ b/arch/x86/kernel/cpu/perf_event_intel.c
@@ -1458,7 +1458,15 @@ static __initconst const u64 slm_hw_cache_event_ids
};
/*
- * Use from PMIs where the LBRs are already disabled.
+ * Used from PMIs where the LBRs are already disabled.
+ *
+ * This function could be called consecutively. It is required to remain in
+ * disabled state if called consecutively.
+ *
+ * During consecutive calls, the same disable value will be written to related
+ * registers, so the PMU state remains unchanged. hw.state in
+ * intel_bts_disable_local will remain PERF_HES_STOPPED too in consecutive
+ * calls.
*/
static void __intel_pmu_disable_all(void)
{
@@ -1840,6 +1848,16 @@ again:
if (__test_and_clear_bit(62, (unsigned long *)&status)) {
handled++;
x86_pmu.drain_pebs(regs);
+ /*
+ * There are cases where, even though, the PEBS ovfl bit is set
+ * in GLOBAL_OVF_STATUS, the PEBS events may also have their
+ * overflow bits set for their counters. We must clear them
+ * here because they have been processed as exact samples in
+ * the drain_pebs() routine. They must not be processed again
+ * in the for_each_bit_set() loop for regular samples below.
+ */
+ status &= ~cpuc->pebs_enabled;
+ status &= x86_pmu.intel_ctrl | GLOBAL_STATUS_TRACE_TOPAPMI;
}
/*
@@ -1885,7 +1903,10 @@ again:
goto again;
done:
- __intel_pmu_enable_all(0, true);
+ /* Only restore PMU state when it's active. See x86_pmu_disable(). */
+ if (cpuc->enabled)
+ __intel_pmu_enable_all(0, true);
+
/*
* Only unmask the NMI after the overflow counters
* have been reset. This avoids spurious NMIs on
@@ -3315,6 +3336,7 @@ __init int intel_pmu_init(void)
intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] =
X86_CONFIG(.event=0xb1, .umask=0x3f, .inv=1, .cmask=1);
+ intel_pmu_pebs_data_source_nhm();
x86_add_quirk(intel_nehalem_quirk);
pr_cont("Nehalem events, ");
@@ -3377,6 +3399,7 @@ __init int intel_pmu_init(void)
intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] =
X86_CONFIG(.event=0xb1, .umask=0x3f, .inv=1, .cmask=1);
+ intel_pmu_pebs_data_source_nhm();
pr_cont("Westmere events, ");
break;
diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c
index 5db1c7755548..7abb2b88572e 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_ds.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c
@@ -51,7 +51,8 @@ union intel_x86_pebs_dse {
#define OP_LH (P(OP, LOAD) | P(LVL, HIT))
#define SNOOP_NONE_MISS (P(SNOOP, NONE) | P(SNOOP, MISS))
-static const u64 pebs_data_source[] = {
+/* Version for Sandy Bridge and later */
+static u64 pebs_data_source[] = {
P(OP, LOAD) | P(LVL, MISS) | P(LVL, L3) | P(SNOOP, NA),/* 0x00:ukn L3 */
OP_LH | P(LVL, L1) | P(SNOOP, NONE), /* 0x01: L1 local */
OP_LH | P(LVL, LFB) | P(SNOOP, NONE), /* 0x02: LFB hit */
@@ -70,6 +71,14 @@ static const u64 pebs_data_source[] = {
OP_LH | P(LVL, UNC) | P(SNOOP, NONE), /* 0x0f: uncached */
};
+/* Patch up minor differences in the bits */
+void __init intel_pmu_pebs_data_source_nhm(void)
+{
+ pebs_data_source[0x05] = OP_LH | P(LVL, L3) | P(SNOOP, HIT);
+ pebs_data_source[0x06] = OP_LH | P(LVL, L3) | P(SNOOP, HITM);
+ pebs_data_source[0x07] = OP_LH | P(LVL, L3) | P(SNOOP, HITM);
+}
+
static u64 precise_store_data(u64 status)
{
union intel_x86_pebs_dse dse;
@@ -269,7 +278,7 @@ static int alloc_pebs_buffer(int cpu)
if (!x86_pmu.pebs)
return 0;
- buffer = kzalloc_node(PEBS_BUFFER_SIZE, GFP_KERNEL, node);
+ buffer = kzalloc_node(x86_pmu.pebs_buffer_size, GFP_KERNEL, node);
if (unlikely(!buffer))
return -ENOMEM;
@@ -286,7 +295,7 @@ static int alloc_pebs_buffer(int cpu)
per_cpu(insn_buffer, cpu) = ibuffer;
}
- max = PEBS_BUFFER_SIZE / x86_pmu.pebs_record_size;
+ max = x86_pmu.pebs_buffer_size / x86_pmu.pebs_record_size;
ds->pebs_buffer_base = (u64)(unsigned long)buffer;
ds->pebs_index = ds->pebs_buffer_base;
@@ -1296,6 +1305,7 @@ void __init intel_ds_init(void)
x86_pmu.bts = boot_cpu_has(X86_FEATURE_BTS);
x86_pmu.pebs = boot_cpu_has(X86_FEATURE_PEBS);
+ x86_pmu.pebs_buffer_size = PEBS_BUFFER_SIZE;
if (x86_pmu.pebs) {
char pebs_type = x86_pmu.intel_cap.pebs_trap ? '+' : '-';
int format = x86_pmu.intel_cap.pebs_format;
@@ -1304,6 +1314,14 @@ void __init intel_ds_init(void)
case 0:
printk(KERN_CONT "PEBS fmt0%c, ", pebs_type);
x86_pmu.pebs_record_size = sizeof(struct pebs_record_core);
+ /*
+ * Using >PAGE_SIZE buffers makes the WRMSR to
+ * PERF_GLOBAL_CTRL in intel_pmu_enable_all()
+ * mysteriously hang on Core2.
+ *
+ * As a workaround, we don't do this.
+ */
+ x86_pmu.pebs_buffer_size = PAGE_SIZE;
x86_pmu.drain_pebs = intel_pmu_drain_pebs_core;
break;
diff --git a/arch/x86/kernel/cpu/perf_event_knc.c b/arch/x86/kernel/cpu/perf_event_knc.c
index 5b0c232d1ee6..b931095e86d4 100644
--- a/arch/x86/kernel/cpu/perf_event_knc.c
+++ b/arch/x86/kernel/cpu/perf_event_knc.c
@@ -263,7 +263,9 @@ again:
goto again;
done:
- knc_pmu_enable_all(0);
+ /* Only restore PMU state when it's active. See x86_pmu_disable(). */
+ if (cpuc->enabled)
+ knc_pmu_enable_all(0);
return handled;
}
diff --git a/arch/x86/kernel/ioport.c b/arch/x86/kernel/ioport.c
index 37dae792dbbe..589b3193f102 100644
--- a/arch/x86/kernel/ioport.c
+++ b/arch/x86/kernel/ioport.c
@@ -96,9 +96,14 @@ asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on)
SYSCALL_DEFINE1(iopl, unsigned int, level)
{
struct pt_regs *regs = current_pt_regs();
- unsigned int old = (regs->flags >> 12) & 3;
struct thread_struct *t = &current->thread;
+ /*
+ * Careful: the IOPL bits in regs->flags are undefined under Xen PV
+ * and changing them has no effect.
+ */
+ unsigned int old = t->iopl >> X86_EFLAGS_IOPL_BIT;
+
if (level > 3)
return -EINVAL;
/* Trying to gain more privileges? */
@@ -106,8 +111,9 @@ SYSCALL_DEFINE1(iopl, unsigned int, level)
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
}
- regs->flags = (regs->flags & ~X86_EFLAGS_IOPL) | (level << 12);
- t->iopl = level << 12;
+ regs->flags = (regs->flags & ~X86_EFLAGS_IOPL) |
+ (level << X86_EFLAGS_IOPL_BIT);
+ t->iopl = level << X86_EFLAGS_IOPL_BIT;
set_iopl_mask(t->iopl);
return 0;
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index e835d263a33b..4cbb60fbff3e 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -48,6 +48,7 @@
#include <asm/syscalls.h>
#include <asm/debugreg.h>
#include <asm/switch_to.h>
+#include <asm/xen/hypervisor.h>
asmlinkage extern void ret_from_fork(void);
@@ -411,6 +412,17 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
task_thread_info(prev_p)->flags & _TIF_WORK_CTXSW_PREV))
__switch_to_xtra(prev_p, next_p, tss);
+#ifdef CONFIG_XEN
+ /*
+ * On Xen PV, IOPL bits in pt_regs->flags have no effect, and
+ * current_pt_regs()->flags may not match the current task's
+ * intended IOPL. We need to switch it manually.
+ */
+ if (unlikely(static_cpu_has(X86_FEATURE_XENPV) &&
+ prev->iopl != next->iopl))
+ xen_set_iopl_mask(next->iopl);
+#endif
+
if (static_cpu_has_bug(X86_BUG_SYSRET_SS_ATTRS)) {
/*
* AMD CPUs have a misfeature: SYSRET sets the SS selector but
diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c
index b0ea42b78ccd..ab5318727579 100644
--- a/arch/x86/kvm/i8254.c
+++ b/arch/x86/kvm/i8254.c
@@ -245,7 +245,7 @@ static void kvm_pit_ack_irq(struct kvm_irq_ack_notifier *kian)
* PIC is being reset. Handle it gracefully here
*/
atomic_inc(&ps->pending);
- else if (value > 0)
+ else if (value > 0 && ps->reinject)
/* in this case, we had multiple outstanding pit interrupts
* that we needed to inject. Reinject
*/
@@ -288,7 +288,9 @@ static void pit_do_work(struct kthread_work *work)
* last one has been acked.
*/
spin_lock(&ps->inject_lock);
- if (ps->irq_ack) {
+ if (!ps->reinject)
+ inject = 1;
+ else if (ps->irq_ack) {
ps->irq_ack = 0;
inject = 1;
}
@@ -317,10 +319,10 @@ static enum hrtimer_restart pit_timer_fn(struct hrtimer *data)
struct kvm_kpit_state *ps = container_of(data, struct kvm_kpit_state, timer);
struct kvm_pit *pt = ps->kvm->arch.vpit;
- if (ps->reinject || !atomic_read(&ps->pending)) {
+ if (ps->reinject)
atomic_inc(&ps->pending);
- queue_kthread_work(&pt->worker, &pt->expired);
- }
+
+ queue_kthread_work(&pt->worker, &pt->expired);
if (ps->is_periodic) {
hrtimer_add_expires_ns(&ps->timer, ps->period);
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 0958fa2b7cb7..f34ab71dfd57 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -2637,8 +2637,15 @@ static void nested_vmx_setup_ctls_msrs(struct vcpu_vmx *vmx)
} else
vmx->nested.nested_vmx_ept_caps = 0;
+ /*
+ * Old versions of KVM use the single-context version without
+ * checking for support, so declare that it is supported even
+ * though it is treated as global context. The alternative is
+ * not failing the single-context invvpid, and it is worse.
+ */
if (enable_vpid)
vmx->nested.nested_vmx_vpid_caps = VMX_VPID_INVVPID_BIT |
+ VMX_VPID_EXTENT_SINGLE_CONTEXT_BIT |
VMX_VPID_EXTENT_GLOBAL_CONTEXT_BIT;
else
vmx->nested.nested_vmx_vpid_caps = 0;
@@ -7340,6 +7347,7 @@ static int handle_invept(struct kvm_vcpu *vcpu)
if (!(types & (1UL << type))) {
nested_vmx_failValid(vcpu,
VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID);
+ skip_emulated_instruction(vcpu);
return 1;
}
@@ -7398,6 +7406,7 @@ static int handle_invvpid(struct kvm_vcpu *vcpu)
if (!(types & (1UL << type))) {
nested_vmx_failValid(vcpu,
VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID);
+ skip_emulated_instruction(vcpu);
return 1;
}
@@ -7414,12 +7423,17 @@ static int handle_invvpid(struct kvm_vcpu *vcpu)
}
switch (type) {
+ case VMX_VPID_EXTENT_SINGLE_CONTEXT:
+ /*
+ * Old versions of KVM use the single-context version so we
+ * have to support it; just treat it the same as all-context.
+ */
case VMX_VPID_EXTENT_ALL_CONTEXT:
__vmx_flush_tlb(vcpu, to_vmx(vcpu)->nested.vpid02);
nested_vmx_succeed(vcpu);
break;
default:
- /* Trap single context invalidation invvpid calls */
+ /* Trap individual address invalidation invvpid calls */
BUG_ON(1);
break;
}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index d2945024ed33..7eb4ebd3ebea 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2736,6 +2736,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
}
kvm_make_request(KVM_REQ_STEAL_UPDATE, vcpu);
+ vcpu->arch.switch_db_regs |= KVM_DEBUGREG_RELOAD;
}
void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
@@ -6023,12 +6024,10 @@ static int inject_pending_event(struct kvm_vcpu *vcpu, bool req_int_win)
}
/* try to inject new event if pending */
- if (vcpu->arch.nmi_pending) {
- if (kvm_x86_ops->nmi_allowed(vcpu)) {
- --vcpu->arch.nmi_pending;
- vcpu->arch.nmi_injected = true;
- kvm_x86_ops->set_nmi(vcpu);
- }
+ if (vcpu->arch.nmi_pending && kvm_x86_ops->nmi_allowed(vcpu)) {
+ --vcpu->arch.nmi_pending;
+ vcpu->arch.nmi_injected = true;
+ kvm_x86_ops->set_nmi(vcpu);
} else if (kvm_cpu_has_injectable_intr(vcpu)) {
/*
* Because interrupts can be injected asynchronously, we are
@@ -6473,10 +6472,12 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
if (inject_pending_event(vcpu, req_int_win) != 0)
req_immediate_exit = true;
/* enable NMI/IRQ window open exits if needed */
- else if (vcpu->arch.nmi_pending)
- kvm_x86_ops->enable_nmi_window(vcpu);
- else if (kvm_cpu_has_injectable_intr(vcpu) || req_int_win)
- kvm_x86_ops->enable_irq_window(vcpu);
+ else {
+ if (vcpu->arch.nmi_pending)
+ kvm_x86_ops->enable_nmi_window(vcpu);
+ if (kvm_cpu_has_injectable_intr(vcpu) || req_int_win)
+ kvm_x86_ops->enable_irq_window(vcpu);
+ }
if (kvm_lapic_enabled(vcpu)) {
update_cr8_intercept(vcpu);
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index 8f4cc3dfac32..5fb6adaaa796 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -106,8 +106,6 @@ static void flush_tlb_func(void *info)
if (f->flush_mm != this_cpu_read(cpu_tlbstate.active_mm))
return;
- if (!f->flush_end)
- f->flush_end = f->flush_start + PAGE_SIZE;
count_vm_tlb_event(NR_TLB_REMOTE_FLUSH_RECEIVED);
if (this_cpu_read(cpu_tlbstate.state) == TLBSTATE_OK) {
@@ -135,12 +133,20 @@ void native_flush_tlb_others(const struct cpumask *cpumask,
unsigned long end)
{
struct flush_tlb_info info;
+
+ if (end == 0)
+ end = start + PAGE_SIZE;
info.flush_mm = mm;
info.flush_start = start;
info.flush_end = end;
count_vm_tlb_event(NR_TLB_REMOTE_FLUSH);
- trace_tlb_flush(TLB_REMOTE_SEND_IPI, end - start);
+ if (end == TLB_FLUSH_ALL)
+ trace_tlb_flush(TLB_REMOTE_SEND_IPI, TLB_FLUSH_ALL);
+ else
+ trace_tlb_flush(TLB_REMOTE_SEND_IPI,
+ (end - start) >> PAGE_SHIFT);
+
if (is_uv_system()) {
unsigned int cpu;
diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c
index eccd4d99e6a4..8fd6f44aee83 100644
--- a/arch/x86/pci/common.c
+++ b/arch/x86/pci/common.c
@@ -673,28 +673,22 @@ int pcibios_add_device(struct pci_dev *dev)
return 0;
}
-int pcibios_alloc_irq(struct pci_dev *dev)
+int pcibios_enable_device(struct pci_dev *dev, int mask)
{
- /*
- * If the PCI device was already claimed by core code and has
- * MSI enabled, probing of the pcibios IRQ will overwrite
- * dev->irq. So bail out if MSI is already enabled.
- */
- if (pci_dev_msi_enabled(dev))
- return -EBUSY;
+ int err;
- return pcibios_enable_irq(dev);
-}
+ if ((err = pci_enable_resources(dev, mask)) < 0)
+ return err;
-void pcibios_free_irq(struct pci_dev *dev)
-{
- if (pcibios_disable_irq)
- pcibios_disable_irq(dev);
+ if (!pci_dev_msi_enabled(dev))
+ return pcibios_enable_irq(dev);
+ return 0;
}
-int pcibios_enable_device(struct pci_dev *dev, int mask)
+void pcibios_disable_device (struct pci_dev *dev)
{
- return pci_enable_resources(dev, mask);
+ if (!pci_dev_msi_enabled(dev) && pcibios_disable_irq)
+ pcibios_disable_irq(dev);
}
int pci_ext_cfg_avail(void)
diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c
index e58565556703..0ae7e9fa348d 100644
--- a/arch/x86/pci/fixup.c
+++ b/arch/x86/pci/fixup.c
@@ -540,3 +540,10 @@ static void twinhead_reserve_killing_zone(struct pci_dev *dev)
}
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x27B9, twinhead_reserve_killing_zone);
+
+static void pci_bdwep_bar(struct pci_dev *dev)
+{
+ dev->non_compliant_bars = 1;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6fa0, pci_bdwep_bar);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6fc0, pci_bdwep_bar);
diff --git a/arch/x86/pci/intel_mid_pci.c b/arch/x86/pci/intel_mid_pci.c
index 0d24e7c10145..8b93e634af84 100644
--- a/arch/x86/pci/intel_mid_pci.c
+++ b/arch/x86/pci/intel_mid_pci.c
@@ -215,7 +215,7 @@ static int intel_mid_pci_irq_enable(struct pci_dev *dev)
int polarity;
int ret;
- if (pci_has_managed_irq(dev))
+ if (dev->irq_managed && dev->irq > 0)
return 0;
switch (intel_mid_identify_cpu()) {
@@ -256,13 +256,10 @@ static int intel_mid_pci_irq_enable(struct pci_dev *dev)
static void intel_mid_pci_irq_disable(struct pci_dev *dev)
{
- if (pci_has_managed_irq(dev)) {
+ if (!mp_should_keep_irq(&dev->dev) && dev->irq_managed &&
+ dev->irq > 0) {
mp_unmap_irq(dev->irq);
dev->irq_managed = 0;
- /*
- * Don't reset dev->irq here, otherwise
- * intel_mid_pci_irq_enable() will fail on next call.
- */
}
}
diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c
index 32e70343e6fd..9bd115484745 100644
--- a/arch/x86/pci/irq.c
+++ b/arch/x86/pci/irq.c
@@ -1202,7 +1202,7 @@ static int pirq_enable_irq(struct pci_dev *dev)
struct pci_dev *temp_dev;
int irq;
- if (pci_has_managed_irq(dev))
+ if (dev->irq_managed && dev->irq > 0)
return 0;
irq = IO_APIC_get_PCI_irq_vector(dev->bus->number,
@@ -1230,7 +1230,8 @@ static int pirq_enable_irq(struct pci_dev *dev)
}
dev = temp_dev;
if (irq >= 0) {
- pci_set_managed_irq(dev, irq);
+ dev->irq_managed = 1;
+ dev->irq = irq;
dev_info(&dev->dev, "PCI->APIC IRQ transform: "
"INT %c -> IRQ %d\n", 'A' + pin - 1, irq);
return 0;
@@ -1256,10 +1257,24 @@ static int pirq_enable_irq(struct pci_dev *dev)
return 0;
}
+bool mp_should_keep_irq(struct device *dev)
+{
+ if (dev->power.is_prepared)
+ return true;
+#ifdef CONFIG_PM
+ if (dev->power.runtime_status == RPM_SUSPENDING)
+ return true;
+#endif
+
+ return false;
+}
+
static void pirq_disable_irq(struct pci_dev *dev)
{
- if (io_apic_assign_pci_irqs && pci_has_managed_irq(dev)) {
+ if (io_apic_assign_pci_irqs && !mp_should_keep_irq(&dev->dev) &&
+ dev->irq_managed && dev->irq) {
mp_unmap_irq(dev->irq);
- pci_reset_managed_irq(dev);
+ dev->irq = 0;
+ dev->irq_managed = 0;
}
}
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index b7de78bdc09c..beab8c706ac9 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -961,7 +961,7 @@ static void xen_load_sp0(struct tss_struct *tss,
tss->x86_tss.sp0 = thread->sp0;
}
-static void xen_set_iopl_mask(unsigned mask)
+void xen_set_iopl_mask(unsigned mask)
{
struct physdev_set_iopl set_iopl;
diff --git a/arch/xtensa/kernel/head.S b/arch/xtensa/kernel/head.S
index 9ed55649ac8e..05e1df943856 100644
--- a/arch/xtensa/kernel/head.S
+++ b/arch/xtensa/kernel/head.S
@@ -128,7 +128,7 @@ ENTRY(_startup)
wsr a0, icountlevel
.set _index, 0
- .rept XCHAL_NUM_DBREAK - 1
+ .rept XCHAL_NUM_DBREAK
wsr a0, SREG_DBREAKC + _index
.set _index, _index + 1
.endr
diff --git a/arch/xtensa/mm/cache.c b/arch/xtensa/mm/cache.c
index d75aa1476da7..1a804a2f9a5b 100644
--- a/arch/xtensa/mm/cache.c
+++ b/arch/xtensa/mm/cache.c
@@ -97,11 +97,11 @@ void clear_user_highpage(struct page *page, unsigned long vaddr)
unsigned long paddr;
void *kvaddr = coherent_kvaddr(page, TLBTEMP_BASE_1, vaddr, &paddr);
- pagefault_disable();
+ preempt_disable();
kmap_invalidate_coherent(page, vaddr);
set_bit(PG_arch_1, &page->flags);
clear_page_alias(kvaddr, paddr);
- pagefault_enable();
+ preempt_enable();
}
void copy_user_highpage(struct page *dst, struct page *src,
@@ -113,11 +113,11 @@ void copy_user_highpage(struct page *dst, struct page *src,
void *src_vaddr = coherent_kvaddr(src, TLBTEMP_BASE_2, vaddr,
&src_paddr);
- pagefault_disable();
+ preempt_disable();
kmap_invalidate_coherent(dst, vaddr);
set_bit(PG_arch_1, &dst->flags);
copy_page_alias(dst_vaddr, src_vaddr, dst_paddr, src_paddr);
- pagefault_enable();
+ preempt_enable();
}
#endif /* DCACHE_WAY_SIZE > PAGE_SIZE */
diff --git a/arch/xtensa/platforms/iss/console.c b/arch/xtensa/platforms/iss/console.c
index 70cb408bc20d..92d785fefb6d 100644
--- a/arch/xtensa/platforms/iss/console.c
+++ b/arch/xtensa/platforms/iss/console.c
@@ -100,21 +100,23 @@ static void rs_poll(unsigned long priv)
{
struct tty_port *port = (struct tty_port *)priv;
int i = 0;
+ int rd = 1;
unsigned char c;
spin_lock(&timer_lock);
while (simc_poll(0)) {
- simc_read(0, &c, 1);
+ rd = simc_read(0, &c, 1);
+ if (rd <= 0)
+ break;
tty_insert_flip_char(port, c, TTY_NORMAL);
i++;
}
if (i)
tty_flip_buffer_push(port);
-
-
- mod_timer(&serial_timer, jiffies + SERIAL_TIMER_VALUE);
+ if (rd)
+ mod_timer(&serial_timer, jiffies + SERIAL_TIMER_VALUE);
spin_unlock(&timer_lock);
}
diff --git a/block/blk-core.c b/block/blk-core.c
index 22baa252213c..5101e6a7bd48 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -2218,7 +2218,7 @@ int blk_insert_cloned_request(struct request_queue *q, struct request *rq)
if (q->mq_ops) {
if (blk_queue_io_stat(q))
blk_account_io_start(rq, true);
- blk_mq_insert_request(rq, false, true, true);
+ blk_mq_insert_request(rq, false, true, false);
return 0;
}
diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c
index 90d6d47965b0..ecdb5a2ce085 100644
--- a/crypto/asymmetric_keys/pkcs7_trust.c
+++ b/crypto/asymmetric_keys/pkcs7_trust.c
@@ -178,6 +178,8 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
int cached_ret = -ENOKEY;
int ret;
+ *_trusted = false;
+
for (p = pkcs7->certs; p; p = p->next)
p->seen = false;
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 021d39c0ba75..13c4e5a5fe8c 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -494,7 +494,7 @@ int x509_decode_time(time64_t *_t, size_t hdrlen,
unsigned char tag,
const unsigned char *value, size_t vlen)
{
- static const unsigned char month_lengths[] = { 31, 29, 31, 30, 31, 30,
+ static const unsigned char month_lengths[] = { 31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31 };
const unsigned char *p = value;
unsigned year, mon, day, hour, min, sec, mon_len;
@@ -540,9 +540,9 @@ int x509_decode_time(time64_t *_t, size_t hdrlen,
if (year % 4 == 0) {
mon_len = 29;
if (year % 100 == 0) {
- year /= 100;
- if (year % 4 != 0)
- mon_len = 28;
+ mon_len = 28;
+ if (year % 400 == 0)
+ mon_len = 29;
}
}
}
diff --git a/crypto/keywrap.c b/crypto/keywrap.c
index b1d106ce55f3..72014f963ba7 100644
--- a/crypto/keywrap.c
+++ b/crypto/keywrap.c
@@ -212,7 +212,7 @@ static int crypto_kw_decrypt(struct blkcipher_desc *desc,
SEMIBSIZE))
ret = -EBADMSG;
- memzero_explicit(&block, sizeof(struct crypto_kw_block));
+ memzero_explicit(block, sizeof(struct crypto_kw_block));
return ret;
}
@@ -297,7 +297,7 @@ static int crypto_kw_encrypt(struct blkcipher_desc *desc,
/* establish the IV for the caller to pick up */
memcpy(desc->info, block->A, SEMIBSIZE);
- memzero_explicit(&block, sizeof(struct crypto_kw_block));
+ memzero_explicit(block, sizeof(struct crypto_kw_block));
return 0;
}
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c
index c9336751e5e3..8a10a7ae6a8a 100644
--- a/drivers/acpi/pci_irq.c
+++ b/drivers/acpi/pci_irq.c
@@ -409,7 +409,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
return 0;
}
- if (pci_has_managed_irq(dev))
+ if (dev->irq_managed && dev->irq > 0)
return 0;
entry = acpi_pci_irq_lookup(dev, pin);
@@ -454,7 +454,8 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
kfree(entry);
return rc;
}
- pci_set_managed_irq(dev, rc);
+ dev->irq = rc;
+ dev->irq_managed = 1;
if (link)
snprintf(link_desc, sizeof(link_desc), " -> Link[%s]", link);
@@ -477,9 +478,17 @@ void acpi_pci_irq_disable(struct pci_dev *dev)
u8 pin;
pin = dev->pin;
- if (!pin || !pci_has_managed_irq(dev))
+ if (!pin || !dev->irq_managed || dev->irq <= 0)
return;
+ /* Keep IOAPIC pin configuration when suspending */
+ if (dev->dev.power.is_prepared)
+ return;
+#ifdef CONFIG_PM
+ if (dev->dev.power.runtime_status == RPM_SUSPENDING)
+ return;
+#endif
+
entry = acpi_pci_irq_lookup(dev, pin);
if (!entry)
return;
@@ -499,6 +508,6 @@ void acpi_pci_irq_disable(struct pci_dev *dev)
dev_dbg(&dev->dev, "PCI INT %c disabled\n", pin_name(pin));
if (gsi >= 0) {
acpi_unregister_gsi(gsi);
- pci_reset_managed_irq(dev);
+ dev->irq_managed = 0;
}
}
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index cdc5c2599beb..627f8fbb5e9a 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -26,8 +26,20 @@
#ifdef CONFIG_X86
#define valid_IRQ(i) (((i) != 0) && ((i) != 2))
+static inline bool acpi_iospace_resource_valid(struct resource *res)
+{
+ /* On X86 IO space is limited to the [0 - 64K] IO port range */
+ return res->end < 0x10003;
+}
#else
#define valid_IRQ(i) (true)
+/*
+ * ACPI IO descriptors on arches other than X86 contain MMIO CPU physical
+ * addresses mapping IO space in CPU physical address space, IO space
+ * resources can be placed anywhere in the 64-bit physical address space.
+ */
+static inline bool
+acpi_iospace_resource_valid(struct resource *res) { return true; }
#endif
static bool acpi_dev_resource_len_valid(u64 start, u64 end, u64 len, bool io)
@@ -126,7 +138,7 @@ static void acpi_dev_ioresource_flags(struct resource *res, u64 len,
if (!acpi_dev_resource_len_valid(res->start, res->end, len, true))
res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET;
- if (res->end >= 0x10003)
+ if (!acpi_iospace_resource_valid(res))
res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET;
if (io_decode == ACPI_DECODE_16)
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 0d94621dc856..e3322adaaae0 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -714,6 +714,7 @@ static int acpi_hibernation_enter(void)
static void acpi_hibernation_leave(void)
{
+ pm_set_resume_via_firmware();
/*
* If ACPI is not enabled by the BIOS and the boot kernel, we need to
* enable it here.
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 4c67945ef36f..20d17906fc9b 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -379,6 +379,7 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags)
struct files_struct *files = proc->files;
unsigned long rlim_cur;
unsigned long irqs;
+ int ret;
if (files == NULL)
return -ESRCH;
@@ -389,7 +390,11 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags)
rlim_cur = task_rlimit(proc->tsk, RLIMIT_NOFILE);
unlock_task_sighand(proc->tsk, &irqs);
- return __alloc_fd(files, 0, rlim_cur, flags);
+ preempt_enable_no_resched();
+ ret = __alloc_fd(files, 0, rlim_cur, flags);
+ preempt_disable();
+
+ return ret;
}
/*
@@ -398,8 +403,11 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags)
static void task_fd_install(
struct binder_proc *proc, unsigned int fd, struct file *file)
{
- if (proc->files)
+ if (proc->files) {
+ preempt_enable_no_resched();
__fd_install(proc->files, fd, file);
+ preempt_disable();
+ }
}
/*
@@ -427,6 +435,7 @@ static inline void binder_lock(const char *tag)
{
trace_binder_lock(tag);
mutex_lock(&binder_main_lock);
+ preempt_disable();
trace_binder_locked(tag);
}
@@ -434,8 +443,62 @@ static inline void binder_unlock(const char *tag)
{
trace_binder_unlock(tag);
mutex_unlock(&binder_main_lock);
+ preempt_enable();
+}
+
+static inline void *kzalloc_preempt_disabled(size_t size)
+{
+ void *ptr;
+
+ ptr = kzalloc(size, GFP_NOWAIT);
+ if (ptr)
+ return ptr;
+
+ preempt_enable_no_resched();
+ ptr = kzalloc(size, GFP_KERNEL);
+ preempt_disable();
+
+ return ptr;
+}
+
+static inline long copy_to_user_preempt_disabled(void __user *to, const void *from, long n)
+{
+ long ret;
+
+ preempt_enable_no_resched();
+ ret = copy_to_user(to, from, n);
+ preempt_disable();
+ return ret;
+}
+
+static inline long copy_from_user_preempt_disabled(void *to, const void __user *from, long n)
+{
+ long ret;
+
+ preempt_enable_no_resched();
+ ret = copy_from_user(to, from, n);
+ preempt_disable();
+ return ret;
}
+#define get_user_preempt_disabled(x, ptr) \
+({ \
+ int __ret; \
+ preempt_enable_no_resched(); \
+ __ret = get_user(x, ptr); \
+ preempt_disable(); \
+ __ret; \
+})
+
+#define put_user_preempt_disabled(x, ptr) \
+({ \
+ int __ret; \
+ preempt_enable_no_resched(); \
+ __ret = put_user(x, ptr); \
+ preempt_disable(); \
+ __ret; \
+})
+
static void binder_set_nice(long nice)
{
long min_nice;
@@ -568,6 +631,8 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate,
else
mm = get_task_mm(proc->tsk);
+ preempt_enable_no_resched();
+
if (mm) {
down_write(&mm->mmap_sem);
vma = proc->vma;
@@ -622,6 +687,9 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate,
up_write(&mm->mmap_sem);
mmput(mm);
}
+
+ preempt_disable();
+
return 0;
free_range:
@@ -644,6 +712,9 @@ err_no_vma:
up_write(&mm->mmap_sem);
mmput(mm);
}
+
+ preempt_disable();
+
return -ENOMEM;
}
@@ -903,7 +974,7 @@ static struct binder_node *binder_new_node(struct binder_proc *proc,
return NULL;
}
- node = kzalloc(sizeof(*node), GFP_KERNEL);
+ node = kzalloc_preempt_disabled(sizeof(*node));
if (node == NULL)
return NULL;
binder_stats_created(BINDER_STAT_NODE);
@@ -1040,7 +1111,7 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
else
return ref;
}
- new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+ new_ref = kzalloc_preempt_disabled(sizeof(*ref));
if (new_ref == NULL)
return NULL;
binder_stats_created(BINDER_STAT_REF);
@@ -1438,14 +1509,14 @@ static void binder_transaction(struct binder_proc *proc,
e->to_proc = target_proc->pid;
/* TODO: reuse incoming transaction for reply */
- t = kzalloc(sizeof(*t), GFP_KERNEL);
+ t = kzalloc_preempt_disabled(sizeof(*t));
if (t == NULL) {
return_error = BR_FAILED_REPLY;
goto err_alloc_t_failed;
}
binder_stats_created(BINDER_STAT_TRANSACTION);
- tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
+ tcomplete = kzalloc_preempt_disabled(sizeof(*tcomplete));
if (tcomplete == NULL) {
return_error = BR_FAILED_REPLY;
goto err_alloc_tcomplete_failed;
@@ -1502,14 +1573,14 @@ static void binder_transaction(struct binder_proc *proc,
offp = (binder_size_t *)(t->buffer->data +
ALIGN(tr->data_size, sizeof(void *)));
- if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
+ if (copy_from_user_preempt_disabled(t->buffer->data, (const void __user *)(uintptr_t)
tr->data.ptr.buffer, tr->data_size)) {
binder_user_error("%d:%d got transaction with invalid data ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
- if (copy_from_user(offp, (const void __user *)(uintptr_t)
+ if (copy_from_user_preempt_disabled(offp, (const void __user *)(uintptr_t)
tr->data.ptr.offsets, tr->offsets_size)) {
binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
proc->pid, thread->pid);
@@ -1778,7 +1849,7 @@ static int binder_thread_write(struct binder_proc *proc,
void __user *end = buffer + size;
while (ptr < end && thread->return_error == BR_OK) {
- if (get_user(cmd, (uint32_t __user *)ptr))
+ if (get_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
trace_binder_command(cmd);
@@ -1796,7 +1867,7 @@ static int binder_thread_write(struct binder_proc *proc,
struct binder_ref *ref;
const char *debug_string;
- if (get_user(target, (uint32_t __user *)ptr))
+ if (get_user_preempt_disabled(target, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
if (target == 0 && binder_context_mgr_node &&
@@ -1846,10 +1917,10 @@ static int binder_thread_write(struct binder_proc *proc,
binder_uintptr_t cookie;
struct binder_node *node;
- if (get_user(node_ptr, (binder_uintptr_t __user *)ptr))
+ if (get_user_preempt_disabled(node_ptr, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
- if (get_user(cookie, (binder_uintptr_t __user *)ptr))
+ if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
node = binder_get_node(proc, node_ptr);
@@ -1907,7 +1978,7 @@ static int binder_thread_write(struct binder_proc *proc,
binder_uintptr_t data_ptr;
struct binder_buffer *buffer;
- if (get_user(data_ptr, (binder_uintptr_t __user *)ptr))
+ if (get_user_preempt_disabled(data_ptr, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
@@ -1949,7 +2020,7 @@ static int binder_thread_write(struct binder_proc *proc,
case BC_REPLY: {
struct binder_transaction_data tr;
- if (copy_from_user(&tr, ptr, sizeof(tr)))
+ if (copy_from_user_preempt_disabled(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
@@ -1999,10 +2070,10 @@ static int binder_thread_write(struct binder_proc *proc,
struct binder_ref *ref;
struct binder_ref_death *death;
- if (get_user(target, (uint32_t __user *)ptr))
+ if (get_user_preempt_disabled(target, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
- if (get_user(cookie, (binder_uintptr_t __user *)ptr))
+ if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
ref = binder_get_ref(proc, target);
@@ -2031,7 +2102,7 @@ static int binder_thread_write(struct binder_proc *proc,
proc->pid, thread->pid);
break;
}
- death = kzalloc(sizeof(*death), GFP_KERNEL);
+ death = kzalloc_preempt_disabled(sizeof(*death));
if (death == NULL) {
thread->return_error = BR_ERROR;
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
@@ -2085,8 +2156,7 @@ static int binder_thread_write(struct binder_proc *proc,
struct binder_work *w;
binder_uintptr_t cookie;
struct binder_ref_death *death = NULL;
-
- if (get_user(cookie, (binder_uintptr_t __user *)ptr))
+ if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(cookie);
@@ -2118,7 +2188,8 @@ static int binder_thread_write(struct binder_proc *proc,
wake_up_interruptible(&proc->wait);
}
}
- } break;
+ }
+ break;
default:
pr_err("%d:%d unknown command %d\n",
@@ -2167,7 +2238,7 @@ static int binder_thread_read(struct binder_proc *proc,
int wait_for_proc_work;
if (*consumed == 0) {
- if (put_user(BR_NOOP, (uint32_t __user *)ptr))
+ if (put_user_preempt_disabled(BR_NOOP, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
}
@@ -2178,7 +2249,7 @@ retry:
if (thread->return_error != BR_OK && ptr < end) {
if (thread->return_error2 != BR_OK) {
- if (put_user(thread->return_error2, (uint32_t __user *)ptr))
+ if (put_user_preempt_disabled(thread->return_error2, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
binder_stat_br(proc, thread, thread->return_error2);
@@ -2186,7 +2257,7 @@ retry:
goto done;
thread->return_error2 = BR_OK;
}
- if (put_user(thread->return_error, (uint32_t __user *)ptr))
+ if (put_user_preempt_disabled(thread->return_error, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
binder_stat_br(proc, thread, thread->return_error);
@@ -2264,7 +2335,7 @@ retry:
} break;
case BINDER_WORK_TRANSACTION_COMPLETE: {
cmd = BR_TRANSACTION_COMPLETE;
- if (put_user(cmd, (uint32_t __user *)ptr))
+ if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
@@ -2306,14 +2377,14 @@ retry:
node->has_weak_ref = 0;
}
if (cmd != BR_NOOP) {
- if (put_user(cmd, (uint32_t __user *)ptr))
+ if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
- if (put_user(node->ptr,
+ if (put_user_preempt_disabled(node->ptr, (binder_uintptr_t __user *)
(binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
- if (put_user(node->cookie,
+ if (put_user_preempt_disabled(node->cookie, (binder_uintptr_t __user *)
(binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
@@ -2357,11 +2428,10 @@ retry:
cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;
else
cmd = BR_DEAD_BINDER;
- if (put_user(cmd, (uint32_t __user *)ptr))
+ if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
- if (put_user(death->cookie,
- (binder_uintptr_t __user *)ptr))
+ if (put_user_preempt_disabled(death->cookie, (binder_uintptr_t __user *) ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
binder_stat_br(proc, thread, cmd);
@@ -2428,10 +2498,10 @@ retry:
ALIGN(t->buffer->data_size,
sizeof(void *));
- if (put_user(cmd, (uint32_t __user *)ptr))
+ if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
- if (copy_to_user(ptr, &tr, sizeof(tr)))
+ if (copy_to_user_preempt_disabled(ptr, &tr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
@@ -2473,7 +2543,7 @@ done:
binder_debug(BINDER_DEBUG_THREADS,
"%d:%d BR_SPAWN_LOOPER\n",
proc->pid, thread->pid);
- if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
+ if (put_user_preempt_disabled(BR_SPAWN_LOOPER, (uint32_t __user *) buffer))
return -EFAULT;
binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
}
@@ -2548,7 +2618,7 @@ static struct binder_thread *binder_get_thread(struct binder_proc *proc)
break;
}
if (*p == NULL) {
- thread = kzalloc(sizeof(*thread), GFP_KERNEL);
+ thread = kzalloc_preempt_disabled(sizeof(*thread));
if (thread == NULL)
return NULL;
binder_stats_created(BINDER_STAT_THREAD);
@@ -2652,7 +2722,7 @@ static int binder_ioctl_write_read(struct file *filp,
ret = -EINVAL;
goto out;
}
- if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
+ if (copy_from_user_preempt_disabled(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
@@ -2670,7 +2740,7 @@ static int binder_ioctl_write_read(struct file *filp,
trace_binder_write_done(ret);
if (ret < 0) {
bwr.read_consumed = 0;
- if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
+ if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
@@ -2684,7 +2754,7 @@ static int binder_ioctl_write_read(struct file *filp,
if (!list_empty(&proc->todo))
wake_up_interruptible(&proc->wait);
if (ret < 0) {
- if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
+ if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
@@ -2694,7 +2764,7 @@ static int binder_ioctl_write_read(struct file *filp,
proc->pid, thread->pid,
(u64)bwr.write_consumed, (u64)bwr.write_size,
(u64)bwr.read_consumed, (u64)bwr.read_size);
- if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
+ if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
@@ -2772,7 +2842,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
goto err;
break;
case BINDER_SET_MAX_THREADS:
- if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
+ if (copy_from_user_preempt_disabled(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
ret = -EINVAL;
goto err;
}
@@ -2795,9 +2865,8 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
ret = -EINVAL;
goto err;
}
- if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
- &ver->protocol_version)) {
- ret = -EINVAL;
+ if (put_user_preempt_disabled(BINDER_CURRENT_PROTOCOL_VERSION, &ver->protocol_version)) {
+ ret = -EINVAL;
goto err;
}
break;
@@ -2858,6 +2927,7 @@ static const struct vm_operations_struct binder_vm_ops = {
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
+
struct vm_struct *area;
struct binder_proc *proc = filp->private_data;
const char *failure_string;
@@ -2918,7 +2988,11 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
- if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {
+ /* binder_update_page_range assumes preemption is disabled */
+ preempt_disable();
+ ret = binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma);
+ preempt_enable_no_resched();
+ if (ret) {
ret = -ENOMEM;
failure_string = "alloc small buf";
goto err_alloc_small_buf_failed;
@@ -3188,8 +3262,12 @@ static void binder_deferred_func(struct work_struct *work)
int defer;
do {
- binder_lock(__func__);
+ trace_binder_lock(__func__);
+ mutex_lock(&binder_main_lock);
+ trace_binder_locked(__func__);
+
mutex_lock(&binder_deferred_lock);
+ preempt_disable();
if (!hlist_empty(&binder_deferred_list)) {
proc = hlist_entry(binder_deferred_list.first,
struct binder_proc, deferred_work_node);
@@ -3215,7 +3293,9 @@ static void binder_deferred_func(struct work_struct *work)
if (defer & BINDER_DEFERRED_RELEASE)
binder_deferred_release(proc); /* frees proc */
- binder_unlock(__func__);
+ trace_binder_unlock(__func__);
+ mutex_unlock(&binder_main_lock);
+ preempt_enable_no_resched();
if (files)
put_files_struct(files);
} while (proc);
diff --git a/drivers/base/power/opp/Makefile b/drivers/base/power/opp/Makefile
index 33c1e18c41a4..19837ef04d8e 100644
--- a/drivers/base/power/opp/Makefile
+++ b/drivers/base/power/opp/Makefile
@@ -1,2 +1,3 @@
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
obj-y += core.o cpu.o
+obj-$(CONFIG_DEBUG_FS) += debugfs.o
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index b8e76f75073b..433b60092972 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -13,50 +13,52 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/clk.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/export.h>
+#include <linux/regulator/consumer.h>
#include "opp.h"
/*
- * The root of the list of all devices. All device_opp structures branch off
- * from here, with each device_opp containing the list of opp it supports in
+ * The root of the list of all opp-tables. All opp_table structures branch off
+ * from here, with each opp_table containing the list of opps it supports in
* various states of availability.
*/
-static LIST_HEAD(dev_opp_list);
+static LIST_HEAD(opp_tables);
/* Lock to allow exclusive modification to the device and opp lists */
-DEFINE_MUTEX(dev_opp_list_lock);
+DEFINE_MUTEX(opp_table_lock);
#define opp_rcu_lockdep_assert() \
do { \
RCU_LOCKDEP_WARN(!rcu_read_lock_held() && \
- !lockdep_is_held(&dev_opp_list_lock), \
- "Missing rcu_read_lock() or " \
- "dev_opp_list_lock protection"); \
+ !lockdep_is_held(&opp_table_lock), \
+ "Missing rcu_read_lock() or " \
+ "opp_table_lock protection"); \
} while (0)
-static struct device_list_opp *_find_list_dev(const struct device *dev,
- struct device_opp *dev_opp)
+static struct opp_device *_find_opp_dev(const struct device *dev,
+ struct opp_table *opp_table)
{
- struct device_list_opp *list_dev;
+ struct opp_device *opp_dev;
- list_for_each_entry(list_dev, &dev_opp->dev_list, node)
- if (list_dev->dev == dev)
- return list_dev;
+ list_for_each_entry(opp_dev, &opp_table->dev_list, node)
+ if (opp_dev->dev == dev)
+ return opp_dev;
return NULL;
}
-static struct device_opp *_managed_opp(const struct device_node *np)
+static struct opp_table *_managed_opp(const struct device_node *np)
{
- struct device_opp *dev_opp;
+ struct opp_table *opp_table;
- list_for_each_entry_rcu(dev_opp, &dev_opp_list, node) {
- if (dev_opp->np == np) {
+ list_for_each_entry_rcu(opp_table, &opp_tables, node) {
+ if (opp_table->np == np) {
/*
* Multiple devices can point to the same OPP table and
* so will have same node-pointer, np.
@@ -64,7 +66,7 @@ static struct device_opp *_managed_opp(const struct device_node *np)
* But the OPPs will be considered as shared only if the
* OPP table contains a "opp-shared" property.
*/
- return dev_opp->shared_opp ? dev_opp : NULL;
+ return opp_table->shared_opp ? opp_table : NULL;
}
}
@@ -72,24 +74,24 @@ static struct device_opp *_managed_opp(const struct device_node *np)
}
/**
- * _find_device_opp() - find device_opp struct using device pointer
- * @dev: device pointer used to lookup device OPPs
+ * _find_opp_table() - find opp_table struct using device pointer
+ * @dev: device pointer used to lookup OPP table
*
- * Search list of device OPPs for one containing matching device. Does a RCU
- * reader operation to grab the pointer needed.
+ * Search OPP table for one containing matching device. Does a RCU reader
+ * operation to grab the pointer needed.
*
- * Return: pointer to 'struct device_opp' if found, otherwise -ENODEV or
+ * Return: pointer to 'struct opp_table' if found, otherwise -ENODEV or
* -EINVAL based on type of error.
*
* Locking: For readers, this function must be called under rcu_read_lock().
- * device_opp is a RCU protected pointer, which means that device_opp is valid
+ * opp_table is a RCU protected pointer, which means that opp_table is valid
* as long as we are under RCU lock.
*
- * For Writers, this function must be called with dev_opp_list_lock held.
+ * For Writers, this function must be called with opp_table_lock held.
*/
-struct device_opp *_find_device_opp(struct device *dev)
+struct opp_table *_find_opp_table(struct device *dev)
{
- struct device_opp *dev_opp;
+ struct opp_table *opp_table;
opp_rcu_lockdep_assert();
@@ -98,9 +100,9 @@ struct device_opp *_find_device_opp(struct device *dev)
return ERR_PTR(-EINVAL);
}
- list_for_each_entry_rcu(dev_opp, &dev_opp_list, node)
- if (_find_list_dev(dev, dev_opp))
- return dev_opp;
+ list_for_each_entry_rcu(opp_table, &opp_tables, node)
+ if (_find_opp_dev(dev, opp_table))
+ return opp_table;
return ERR_PTR(-ENODEV);
}
@@ -213,16 +215,16 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo);
*/
unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev)
{
- struct device_opp *dev_opp;
+ struct opp_table *opp_table;
unsigned long clock_latency_ns;
rcu_read_lock();
- dev_opp = _find_device_opp(dev);
- if (IS_ERR(dev_opp))
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table))
clock_latency_ns = 0;
else
- clock_latency_ns = dev_opp->clock_latency_ns_max;
+ clock_latency_ns = opp_table->clock_latency_ns_max;
rcu_read_unlock();
return clock_latency_ns;
@@ -230,6 +232,82 @@ unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev)
EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency);
/**
+ * dev_pm_opp_get_max_volt_latency() - Get max voltage latency in nanoseconds
+ * @dev: device for which we do this operation
+ *
+ * Return: This function returns the max voltage latency in nanoseconds.
+ *
+ * Locking: This function takes rcu_read_lock().
+ */
+unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)
+{
+ struct opp_table *opp_table;
+ struct dev_pm_opp *opp;
+ struct regulator *reg;
+ unsigned long latency_ns = 0;
+ unsigned long min_uV = ~0, max_uV = 0;
+ int ret;
+
+ rcu_read_lock();
+
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table)) {
+ rcu_read_unlock();
+ return 0;
+ }
+
+ reg = opp_table->regulator;
+ if (IS_ERR(reg)) {
+ /* Regulator may not be required for device */
+ if (reg)
+ dev_err(dev, "%s: Invalid regulator (%ld)\n", __func__,
+ PTR_ERR(reg));
+ rcu_read_unlock();
+ return 0;
+ }
+
+ list_for_each_entry_rcu(opp, &opp_table->opp_list, node) {
+ if (!opp->available)
+ continue;
+
+ if (opp->u_volt_min < min_uV)
+ min_uV = opp->u_volt_min;
+ if (opp->u_volt_max > max_uV)
+ max_uV = opp->u_volt_max;
+ }
+
+ rcu_read_unlock();
+
+ /*
+ * The caller needs to ensure that opp_table (and hence the regulator)
+ * isn't freed, while we are executing this routine.
+ */
+ ret = regulator_set_voltage_time(reg, min_uV, max_uV);
+ if (ret > 0)
+ latency_ns = ret * 1000;
+
+ return latency_ns;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_volt_latency);
+
+/**
+ * dev_pm_opp_get_max_transition_latency() - Get max transition latency in
+ * nanoseconds
+ * @dev: device for which we do this operation
+ *
+ * Return: This function returns the max transition latency, in nanoseconds, to
+ * switch from one OPP to other.
+ *
+ * Locking: This function takes rcu_read_lock().
+ */
+unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev)
+{
+ return dev_pm_opp_get_max_volt_latency(dev) +
+ dev_pm_opp_get_max_clock_latency(dev);
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_transition_latency);
+
+/**
* dev_pm_opp_get_suspend_opp() - Get suspend opp
* @dev: device for which we do this operation
*
@@ -244,21 +322,21 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency);
*/
struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev)
{
- struct device_opp *dev_opp;
+ struct opp_table *opp_table;
opp_rcu_lockdep_assert();
- dev_opp = _find_device_opp(dev);
- if (IS_ERR(dev_opp) || !dev_opp->suspend_opp ||
- !dev_opp->suspend_opp->available)
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table) || !opp_table->suspend_opp ||
+ !opp_table->suspend_opp->available)
return NULL;
- return dev_opp->suspend_opp;
+ return opp_table->suspend_opp;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp);
/**
- * dev_pm_opp_get_opp_count() - Get number of opps available in the opp list
+ * dev_pm_opp_get_opp_count() - Get number of opps available in the opp table
* @dev: device for which we do this operation
*
* Return: This function returns the number of available opps if there are any,
@@ -268,21 +346,21 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp);
*/
int dev_pm_opp_get_opp_count(struct device *dev)
{
- struct device_opp *dev_opp;
+ struct opp_table *opp_table;
struct dev_pm_opp *temp_opp;
int count = 0;
rcu_read_lock();
- dev_opp = _find_device_opp(dev);
- if (IS_ERR(dev_opp)) {
- count = PTR_ERR(dev_opp);
- dev_err(dev, "%s: device OPP not found (%d)\n",
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table)) {
+ count = PTR_ERR(opp_table);
+ dev_err(dev, "%s: OPP table not found (%d)\n",
__func__, count);
goto out_unlock;
}
- list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) {
+ list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available)
count++;
}
@@ -299,7 +377,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count);
* @freq: frequency to search for
* @available: true/false - match for available opp
*
- * Return: Searches for exact match in the opp list and returns pointer to the
+ * Return: Searches for exact match in the opp table and returns pointer to the
* matching opp if found, else returns ERR_PTR in case of error and should
* be handled using IS_ERR. Error return values can be:
* EINVAL: for bad pointer
@@ -323,19 +401,20 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
unsigned long freq,
bool available)
{
- struct device_opp *dev_opp;
+ struct opp_table *opp_table;
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
opp_rcu_lockdep_assert();
- dev_opp = _find_device_opp(dev);
- if (IS_ERR(dev_opp)) {
- int r = PTR_ERR(dev_opp);
- dev_err(dev, "%s: device OPP not found (%d)\n", __func__, r);
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table)) {
+ int r = PTR_ERR(opp_table);
+
+ dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r);
return ERR_PTR(r);
}
- list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) {
+ list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available == available &&
temp_opp->rate == freq) {
opp = temp_opp;
@@ -371,7 +450,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact);
struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
unsigned long *freq)
{
- struct device_opp *dev_opp;
+ struct opp_table *opp_table;
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
opp_rcu_lockdep_assert();
@@ -381,11 +460,11 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
return ERR_PTR(-EINVAL);
}
- dev_opp = _find_device_opp(dev);
- if (IS_ERR(dev_opp))
- return ERR_CAST(dev_opp);
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table))
+ return ERR_CAST(opp_table);
- list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) {
+ list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available && temp_opp->rate >= *freq) {
opp = temp_opp;
*freq = opp->rate;
@@ -421,7 +500,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil);
struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
unsigned long *freq)
{
- struct device_opp *dev_opp;
+ struct opp_table *opp_table;
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
opp_rcu_lockdep_assert();
@@ -431,11 +510,11 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
return ERR_PTR(-EINVAL);
}
- dev_opp = _find_device_opp(dev);
- if (IS_ERR(dev_opp))
- return ERR_CAST(dev_opp);
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table))
+ return ERR_CAST(opp_table);
- list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) {
+ list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available) {
/* go to the next node, before choosing prev */
if (temp_opp->rate > *freq)
@@ -451,116 +530,343 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
}
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor);
-/* List-dev Helpers */
-static void _kfree_list_dev_rcu(struct rcu_head *head)
+/*
+ * The caller needs to ensure that opp_table (and hence the clk) isn't freed,
+ * while clk returned here is used.
+ */
+static struct clk *_get_opp_clk(struct device *dev)
+{
+ struct opp_table *opp_table;
+ struct clk *clk;
+
+ rcu_read_lock();
+
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table)) {
+ dev_err(dev, "%s: device opp doesn't exist\n", __func__);
+ clk = ERR_CAST(opp_table);
+ goto unlock;
+ }
+
+ clk = opp_table->clk;
+ if (IS_ERR(clk))
+ dev_err(dev, "%s: No clock available for the device\n",
+ __func__);
+
+unlock:
+ rcu_read_unlock();
+ return clk;
+}
+
+static int _set_opp_voltage(struct device *dev, struct regulator *reg,
+ unsigned long u_volt, unsigned long u_volt_min,
+ unsigned long u_volt_max)
+{
+ int ret;
+
+ /* Regulator not available for device */
+ if (IS_ERR(reg)) {
+ dev_dbg(dev, "%s: regulator not available: %ld\n", __func__,
+ PTR_ERR(reg));
+ return 0;
+ }
+
+ dev_dbg(dev, "%s: voltages (mV): %lu %lu %lu\n", __func__, u_volt_min,
+ u_volt, u_volt_max);
+
+ ret = regulator_set_voltage_triplet(reg, u_volt_min, u_volt,
+ u_volt_max);
+ if (ret)
+ dev_err(dev, "%s: failed to set voltage (%lu %lu %lu mV): %d\n",
+ __func__, u_volt_min, u_volt, u_volt_max, ret);
+
+ return ret;
+}
+
+/**
+ * dev_pm_opp_set_rate() - Configure new OPP based on frequency
+ * @dev: device for which we do this operation
+ * @target_freq: frequency to achieve
+ *
+ * This configures the power-supplies and clock source to the levels specified
+ * by the OPP corresponding to the target_freq.
+ *
+ * Locking: This function takes rcu_read_lock().
+ */
+int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
+{
+ struct opp_table *opp_table;
+ struct dev_pm_opp *old_opp, *opp;
+ struct regulator *reg;
+ struct clk *clk;
+ unsigned long freq, old_freq;
+ unsigned long u_volt, u_volt_min, u_volt_max;
+ unsigned long ou_volt, ou_volt_min, ou_volt_max;
+ int ret;
+
+ if (unlikely(!target_freq)) {
+ dev_err(dev, "%s: Invalid target frequency %lu\n", __func__,
+ target_freq);
+ return -EINVAL;
+ }
+
+ clk = _get_opp_clk(dev);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ freq = clk_round_rate(clk, target_freq);
+ if ((long)freq <= 0)
+ freq = target_freq;
+
+ old_freq = clk_get_rate(clk);
+
+ /* Return early if nothing to do */
+ if (old_freq == freq) {
+ dev_dbg(dev, "%s: old/new frequencies (%lu Hz) are same, nothing to do\n",
+ __func__, freq);
+ return 0;
+ }
+
+ rcu_read_lock();
+
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table)) {
+ dev_err(dev, "%s: device opp doesn't exist\n", __func__);
+ rcu_read_unlock();
+ return PTR_ERR(opp_table);
+ }
+
+ old_opp = dev_pm_opp_find_freq_ceil(dev, &old_freq);
+ if (!IS_ERR(old_opp)) {
+ ou_volt = old_opp->u_volt;
+ ou_volt_min = old_opp->u_volt_min;
+ ou_volt_max = old_opp->u_volt_max;
+ } else {
+ dev_err(dev, "%s: failed to find current OPP for freq %lu (%ld)\n",
+ __func__, old_freq, PTR_ERR(old_opp));
+ }
+
+ opp = dev_pm_opp_find_freq_ceil(dev, &freq);
+ if (IS_ERR(opp)) {
+ ret = PTR_ERR(opp);
+ dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n",
+ __func__, freq, ret);
+ rcu_read_unlock();
+ return ret;
+ }
+
+ u_volt = opp->u_volt;
+ u_volt_min = opp->u_volt_min;
+ u_volt_max = opp->u_volt_max;
+
+ reg = opp_table->regulator;
+
+ rcu_read_unlock();
+
+ /* Scaling up? Scale voltage before frequency */
+ if (freq > old_freq) {
+ ret = _set_opp_voltage(dev, reg, u_volt, u_volt_min,
+ u_volt_max);
+ if (ret)
+ goto restore_voltage;
+ }
+
+ /* Change frequency */
+
+ dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n",
+ __func__, old_freq, freq);
+
+ ret = clk_set_rate(clk, freq);
+ if (ret) {
+ dev_err(dev, "%s: failed to set clock rate: %d\n", __func__,
+ ret);
+ goto restore_voltage;
+ }
+
+ /* Scaling down? Scale voltage after frequency */
+ if (freq < old_freq) {
+ ret = _set_opp_voltage(dev, reg, u_volt, u_volt_min,
+ u_volt_max);
+ if (ret)
+ goto restore_freq;
+ }
+
+ return 0;
+
+restore_freq:
+ if (clk_set_rate(clk, old_freq))
+ dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
+ __func__, old_freq);
+restore_voltage:
+ /* This shouldn't harm even if the voltages weren't updated earlier */
+ if (!IS_ERR(old_opp))
+ _set_opp_voltage(dev, reg, ou_volt, ou_volt_min, ou_volt_max);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate);
+
+/* OPP-dev Helpers */
+static void _kfree_opp_dev_rcu(struct rcu_head *head)
{
- struct device_list_opp *list_dev;
+ struct opp_device *opp_dev;
- list_dev = container_of(head, struct device_list_opp, rcu_head);
- kfree_rcu(list_dev, rcu_head);
+ opp_dev = container_of(head, struct opp_device, rcu_head);
+ kfree_rcu(opp_dev, rcu_head);
}
-static void _remove_list_dev(struct device_list_opp *list_dev,
- struct device_opp *dev_opp)
+static void _remove_opp_dev(struct opp_device *opp_dev,
+ struct opp_table *opp_table)
{
- list_del(&list_dev->node);
- call_srcu(&dev_opp->srcu_head.srcu, &list_dev->rcu_head,
- _kfree_list_dev_rcu);
+ opp_debug_unregister(opp_dev, opp_table);
+ list_del(&opp_dev->node);
+ call_srcu(&opp_table->srcu_head.srcu, &opp_dev->rcu_head,
+ _kfree_opp_dev_rcu);
}
-struct device_list_opp *_add_list_dev(const struct device *dev,
- struct device_opp *dev_opp)
+struct opp_device *_add_opp_dev(const struct device *dev,
+ struct opp_table *opp_table)
{
- struct device_list_opp *list_dev;
+ struct opp_device *opp_dev;
+ int ret;
- list_dev = kzalloc(sizeof(*list_dev), GFP_KERNEL);
- if (!list_dev)
+ opp_dev = kzalloc(sizeof(*opp_dev), GFP_KERNEL);
+ if (!opp_dev)
return NULL;
- /* Initialize list-dev */
- list_dev->dev = dev;
- list_add_rcu(&list_dev->node, &dev_opp->dev_list);
+ /* Initialize opp-dev */
+ opp_dev->dev = dev;
+ list_add_rcu(&opp_dev->node, &opp_table->dev_list);
+
+ /* Create debugfs entries for the opp_table */
+ ret = opp_debug_register(opp_dev, opp_table);
+ if (ret)
+ dev_err(dev, "%s: Failed to register opp debugfs (%d)\n",
+ __func__, ret);
- return list_dev;
+ return opp_dev;
}
/**
- * _add_device_opp() - Find device OPP table or allocate a new one
+ * _add_opp_table() - Find OPP table or allocate a new one
* @dev: device for which we do this operation
*
* It tries to find an existing table first, if it couldn't find one, it
* allocates a new OPP table and returns that.
*
- * Return: valid device_opp pointer if success, else NULL.
+ * Return: valid opp_table pointer if success, else NULL.
*/
-static struct device_opp *_add_device_opp(struct device *dev)
+static struct opp_table *_add_opp_table(struct device *dev)
{
- struct device_opp *dev_opp;
- struct device_list_opp *list_dev;
+ struct opp_table *opp_table;
+ struct opp_device *opp_dev;
+ struct device_node *np;
+ int ret;
- /* Check for existing list for 'dev' first */
- dev_opp = _find_device_opp(dev);
- if (!IS_ERR(dev_opp))
- return dev_opp;
+ /* Check for existing table for 'dev' first */
+ opp_table = _find_opp_table(dev);
+ if (!IS_ERR(opp_table))
+ return opp_table;
/*
- * Allocate a new device OPP table. In the infrequent case where a new
+ * Allocate a new OPP table. In the infrequent case where a new
* device is needed to be added, we pay this penalty.
*/
- dev_opp = kzalloc(sizeof(*dev_opp), GFP_KERNEL);
- if (!dev_opp)
+ opp_table = kzalloc(sizeof(*opp_table), GFP_KERNEL);
+ if (!opp_table)
return NULL;
- INIT_LIST_HEAD(&dev_opp->dev_list);
+ INIT_LIST_HEAD(&opp_table->dev_list);
- list_dev = _add_list_dev(dev, dev_opp);
- if (!list_dev) {
- kfree(dev_opp);
+ opp_dev = _add_opp_dev(dev, opp_table);
+ if (!opp_dev) {
+ kfree(opp_table);
return NULL;
}
- srcu_init_notifier_head(&dev_opp->srcu_head);
- INIT_LIST_HEAD(&dev_opp->opp_list);
+ /*
+ * Only required for backward compatibility with v1 bindings, but isn't
+ * harmful for other cases. And so we do it unconditionally.
+ */
+ np = of_node_get(dev->of_node);
+ if (np) {
+ u32 val;
+
+ if (!of_property_read_u32(np, "clock-latency", &val))
+ opp_table->clock_latency_ns_max = val;
+ of_property_read_u32(np, "voltage-tolerance",
+ &opp_table->voltage_tolerance_v1);
+ of_node_put(np);
+ }
+
+ /* Set regulator to a non-NULL error value */
+ opp_table->regulator = ERR_PTR(-ENXIO);
+
+ /* Find clk for the device */
+ opp_table->clk = clk_get(dev, NULL);
+ if (IS_ERR(opp_table->clk)) {
+ ret = PTR_ERR(opp_table->clk);
+ if (ret != -EPROBE_DEFER)
+ dev_dbg(dev, "%s: Couldn't find clock: %d\n", __func__,
+ ret);
+ }
+
+ srcu_init_notifier_head(&opp_table->srcu_head);
+ INIT_LIST_HEAD(&opp_table->opp_list);
- /* Secure the device list modification */
- list_add_rcu(&dev_opp->node, &dev_opp_list);
- return dev_opp;
+ /* Secure the device table modification */
+ list_add_rcu(&opp_table->node, &opp_tables);
+ return opp_table;
}
/**
- * _kfree_device_rcu() - Free device_opp RCU handler
+ * _kfree_device_rcu() - Free opp_table RCU handler
* @head: RCU head
*/
static void _kfree_device_rcu(struct rcu_head *head)
{
- struct device_opp *device_opp = container_of(head, struct device_opp, rcu_head);
+ struct opp_table *opp_table = container_of(head, struct opp_table,
+ rcu_head);
- kfree_rcu(device_opp, rcu_head);
+ kfree_rcu(opp_table, rcu_head);
}
/**
- * _remove_device_opp() - Removes a device OPP table
- * @dev_opp: device OPP table to be removed.
+ * _remove_opp_table() - Removes a OPP table
+ * @opp_table: OPP table to be removed.
*
- * Removes/frees device OPP table it it doesn't contain any OPPs.
+ * Removes/frees OPP table if it doesn't contain any OPPs.
*/
-static void _remove_device_opp(struct device_opp *dev_opp)
+static void _remove_opp_table(struct opp_table *opp_table)
{
- struct device_list_opp *list_dev;
+ struct opp_device *opp_dev;
+
+ if (!list_empty(&opp_table->opp_list))
+ return;
- if (!list_empty(&dev_opp->opp_list))
+ if (opp_table->supported_hw)
return;
- list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp,
- node);
+ if (opp_table->prop_name)
+ return;
+
+ if (!IS_ERR(opp_table->regulator))
+ return;
- _remove_list_dev(list_dev, dev_opp);
+ /* Release clk */
+ if (!IS_ERR(opp_table->clk))
+ clk_put(opp_table->clk);
+
+ opp_dev = list_first_entry(&opp_table->dev_list, struct opp_device,
+ node);
+
+ _remove_opp_dev(opp_dev, opp_table);
/* dev_list must be empty now */
- WARN_ON(!list_empty(&dev_opp->dev_list));
+ WARN_ON(!list_empty(&opp_table->dev_list));
- list_del_rcu(&dev_opp->node);
- call_srcu(&dev_opp->srcu_head.srcu, &dev_opp->rcu_head,
+ list_del_rcu(&opp_table->node);
+ call_srcu(&opp_table->srcu_head.srcu, &opp_table->rcu_head,
_kfree_device_rcu);
}
@@ -577,17 +883,17 @@ static void _kfree_opp_rcu(struct rcu_head *head)
/**
* _opp_remove() - Remove an OPP from a table definition
- * @dev_opp: points back to the device_opp struct this opp belongs to
+ * @opp_table: points back to the opp_table struct this opp belongs to
* @opp: pointer to the OPP to remove
* @notify: OPP_EVENT_REMOVE notification should be sent or not
*
- * This function removes an opp definition from the opp list.
+ * This function removes an opp definition from the opp table.
*
- * Locking: The internal device_opp and opp structures are RCU protected.
+ * Locking: The internal opp_table and opp structures are RCU protected.
* It is assumed that the caller holds required mutex for an RCU updater
* strategy.
*/
-static void _opp_remove(struct device_opp *dev_opp,
+static void _opp_remove(struct opp_table *opp_table,
struct dev_pm_opp *opp, bool notify)
{
/*
@@ -595,21 +901,23 @@ static void _opp_remove(struct device_opp *dev_opp,
* frequency/voltage list.
*/
if (notify)
- srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp);
+ srcu_notifier_call_chain(&opp_table->srcu_head,
+ OPP_EVENT_REMOVE, opp);
+ opp_debug_remove_one(opp);
list_del_rcu(&opp->node);
- call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu);
+ call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu);
- _remove_device_opp(dev_opp);
+ _remove_opp_table(opp_table);
}
/**
- * dev_pm_opp_remove() - Remove an OPP from OPP list
+ * dev_pm_opp_remove() - Remove an OPP from OPP table
* @dev: device for which we do this operation
* @freq: OPP to remove with matching 'freq'
*
- * This function removes an opp from the opp list.
+ * This function removes an opp from the opp table.
*
- * Locking: The internal device_opp and opp structures are RCU protected.
+ * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where
@@ -618,17 +926,17 @@ static void _opp_remove(struct device_opp *dev_opp,
void dev_pm_opp_remove(struct device *dev, unsigned long freq)
{
struct dev_pm_opp *opp;
- struct device_opp *dev_opp;
+ struct opp_table *opp_table;
bool found = false;
- /* Hold our list modification lock here */
- mutex_lock(&dev_opp_list_lock);
+ /* Hold our table modification lock here */
+ mutex_lock(&opp_table_lock);
- dev_opp = _find_device_opp(dev);
- if (IS_ERR(dev_opp))
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table))
goto unlock;
- list_for_each_entry(opp, &dev_opp->opp_list, node) {
+ list_for_each_entry(opp, &opp_table->opp_list, node) {
if (opp->rate == freq) {
found = true;
break;
@@ -641,14 +949,14 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq)
goto unlock;
}
- _opp_remove(dev_opp, opp, true);
+ _opp_remove(opp_table, opp, true);
unlock:
- mutex_unlock(&dev_opp_list_lock);
+ mutex_unlock(&opp_table_lock);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_remove);
static struct dev_pm_opp *_allocate_opp(struct device *dev,
- struct device_opp **dev_opp)
+ struct opp_table **opp_table)
{
struct dev_pm_opp *opp;
@@ -659,8 +967,8 @@ static struct dev_pm_opp *_allocate_opp(struct device *dev,
INIT_LIST_HEAD(&opp->node);
- *dev_opp = _add_device_opp(dev);
- if (!*dev_opp) {
+ *opp_table = _add_opp_table(dev);
+ if (!*opp_table) {
kfree(opp);
return NULL;
}
@@ -668,21 +976,38 @@ static struct dev_pm_opp *_allocate_opp(struct device *dev,
return opp;
}
+static bool _opp_supported_by_regulators(struct dev_pm_opp *opp,
+ struct opp_table *opp_table)
+{
+ struct regulator *reg = opp_table->regulator;
+
+ if (!IS_ERR(reg) &&
+ !regulator_is_supported_voltage(reg, opp->u_volt_min,
+ opp->u_volt_max)) {
+ pr_warn("%s: OPP minuV: %lu maxuV: %lu, not supported by regulator\n",
+ __func__, opp->u_volt_min, opp->u_volt_max);
+ return false;
+ }
+
+ return true;
+}
+
static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
- struct device_opp *dev_opp)
+ struct opp_table *opp_table)
{
struct dev_pm_opp *opp;
- struct list_head *head = &dev_opp->opp_list;
+ struct list_head *head = &opp_table->opp_list;
+ int ret;
/*
* Insert new OPP in order of increasing frequency and discard if
* already present.
*
- * Need to use &dev_opp->opp_list in the condition part of the 'for'
+ * Need to use &opp_table->opp_list in the condition part of the 'for'
* loop, don't replace it with head otherwise it will become an infinite
* loop.
*/
- list_for_each_entry_rcu(opp, &dev_opp->opp_list, node) {
+ list_for_each_entry_rcu(opp, &opp_table->opp_list, node) {
if (new_opp->rate > opp->rate) {
head = &opp->node;
continue;
@@ -700,9 +1025,20 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
0 : -EEXIST;
}
- new_opp->dev_opp = dev_opp;
+ new_opp->opp_table = opp_table;
list_add_rcu(&new_opp->node, head);
+ ret = opp_debug_create_one(new_opp, opp_table);
+ if (ret)
+ dev_err(dev, "%s: Failed to register opp to debugfs (%d)\n",
+ __func__, ret);
+
+ if (!_opp_supported_by_regulators(new_opp, opp_table)) {
+ new_opp->available = false;
+ dev_warn(dev, "%s: OPP not supported by regulators (%lu)\n",
+ __func__, new_opp->rate);
+ }
+
return 0;
}
@@ -713,14 +1049,14 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
* @u_volt: Voltage in uVolts for this OPP
* @dynamic: Dynamically added OPPs.
*
- * This function adds an opp definition to the opp list and returns status.
+ * This function adds an opp definition to the opp table and returns status.
* The opp is made available by default and it can be controlled using
* dev_pm_opp_enable/disable functions and may be removed by dev_pm_opp_remove.
*
* NOTE: "dynamic" parameter impacts OPPs added by the dev_pm_opp_of_add_table
* and freed by dev_pm_opp_of_remove_table.
*
- * Locking: The internal device_opp and opp structures are RCU protected.
+ * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where
@@ -736,14 +1072,15 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt,
bool dynamic)
{
- struct device_opp *dev_opp;
+ struct opp_table *opp_table;
struct dev_pm_opp *new_opp;
+ unsigned long tol;
int ret;
- /* Hold our list modification lock here */
- mutex_lock(&dev_opp_list_lock);
+ /* Hold our table modification lock here */
+ mutex_lock(&opp_table_lock);
- new_opp = _allocate_opp(dev, &dev_opp);
+ new_opp = _allocate_opp(dev, &opp_table);
if (!new_opp) {
ret = -ENOMEM;
goto unlock;
@@ -751,83 +1088,475 @@ static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt,
/* populate the opp table */
new_opp->rate = freq;
+ tol = u_volt * opp_table->voltage_tolerance_v1 / 100;
new_opp->u_volt = u_volt;
+ new_opp->u_volt_min = u_volt - tol;
+ new_opp->u_volt_max = u_volt + tol;
new_opp->available = true;
new_opp->dynamic = dynamic;
- ret = _opp_add(dev, new_opp, dev_opp);
+ ret = _opp_add(dev, new_opp, opp_table);
if (ret)
goto free_opp;
- mutex_unlock(&dev_opp_list_lock);
+ mutex_unlock(&opp_table_lock);
/*
* Notify the changes in the availability of the operable
* frequency/voltage list.
*/
- srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ADD, new_opp);
+ srcu_notifier_call_chain(&opp_table->srcu_head, OPP_EVENT_ADD, new_opp);
return 0;
free_opp:
- _opp_remove(dev_opp, new_opp, false);
+ _opp_remove(opp_table, new_opp, false);
unlock:
- mutex_unlock(&dev_opp_list_lock);
+ mutex_unlock(&opp_table_lock);
return ret;
}
/* TODO: Support multiple regulators */
-static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev)
+static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
+ struct opp_table *opp_table)
{
u32 microvolt[3] = {0};
u32 val;
int count, ret;
+ struct property *prop = NULL;
+ char name[NAME_MAX];
+
+ /* Search for "opp-microvolt-<name>" */
+ if (opp_table->prop_name) {
+ snprintf(name, sizeof(name), "opp-microvolt-%s",
+ opp_table->prop_name);
+ prop = of_find_property(opp->np, name, NULL);
+ }
- /* Missing property isn't a problem, but an invalid entry is */
- if (!of_find_property(opp->np, "opp-microvolt", NULL))
- return 0;
+ if (!prop) {
+ /* Search for "opp-microvolt" */
+ sprintf(name, "opp-microvolt");
+ prop = of_find_property(opp->np, name, NULL);
+
+ /* Missing property isn't a problem, but an invalid entry is */
+ if (!prop)
+ return 0;
+ }
- count = of_property_count_u32_elems(opp->np, "opp-microvolt");
+ count = of_property_count_u32_elems(opp->np, name);
if (count < 0) {
- dev_err(dev, "%s: Invalid opp-microvolt property (%d)\n",
- __func__, count);
+ dev_err(dev, "%s: Invalid %s property (%d)\n",
+ __func__, name, count);
return count;
}
/* There can be one or three elements here */
if (count != 1 && count != 3) {
- dev_err(dev, "%s: Invalid number of elements in opp-microvolt property (%d)\n",
- __func__, count);
+ dev_err(dev, "%s: Invalid number of elements in %s property (%d)\n",
+ __func__, name, count);
return -EINVAL;
}
- ret = of_property_read_u32_array(opp->np, "opp-microvolt", microvolt,
- count);
+ ret = of_property_read_u32_array(opp->np, name, microvolt, count);
if (ret) {
- dev_err(dev, "%s: error parsing opp-microvolt: %d\n", __func__,
- ret);
+ dev_err(dev, "%s: error parsing %s: %d\n", __func__, name, ret);
return -EINVAL;
}
opp->u_volt = microvolt[0];
- opp->u_volt_min = microvolt[1];
- opp->u_volt_max = microvolt[2];
- if (!of_property_read_u32(opp->np, "opp-microamp", &val))
+ if (count == 1) {
+ opp->u_volt_min = opp->u_volt;
+ opp->u_volt_max = opp->u_volt;
+ } else {
+ opp->u_volt_min = microvolt[1];
+ opp->u_volt_max = microvolt[2];
+ }
+
+ /* Search for "opp-microamp-<name>" */
+ prop = NULL;
+ if (opp_table->prop_name) {
+ snprintf(name, sizeof(name), "opp-microamp-%s",
+ opp_table->prop_name);
+ prop = of_find_property(opp->np, name, NULL);
+ }
+
+ if (!prop) {
+ /* Search for "opp-microamp" */
+ sprintf(name, "opp-microamp");
+ prop = of_find_property(opp->np, name, NULL);
+ }
+
+ if (prop && !of_property_read_u32(opp->np, name, &val))
opp->u_amp = val;
return 0;
}
/**
+ * dev_pm_opp_set_supported_hw() - Set supported platforms
+ * @dev: Device for which supported-hw has to be set.
+ * @versions: Array of hierarchy of versions to match.
+ * @count: Number of elements in the array.
+ *
+ * This is required only for the V2 bindings, and it enables a platform to
+ * specify the hierarchy of versions it supports. OPP layer will then enable
+ * OPPs, which are available for those versions, based on its 'opp-supported-hw'
+ * property.
+ *
+ * Locking: The internal opp_table and opp structures are RCU protected.
+ * Hence this function internally uses RCU updater strategy with mutex locks
+ * to keep the integrity of the internal data structures. Callers should ensure
+ * that this function is *NOT* called under RCU protection or in contexts where
+ * mutex cannot be locked.
+ */
+int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
+ unsigned int count)
+{
+ struct opp_table *opp_table;
+ int ret = 0;
+
+ /* Hold our table modification lock here */
+ mutex_lock(&opp_table_lock);
+
+ opp_table = _add_opp_table(dev);
+ if (!opp_table) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ /* Make sure there are no concurrent readers while updating opp_table */
+ WARN_ON(!list_empty(&opp_table->opp_list));
+
+ /* Do we already have a version hierarchy associated with opp_table? */
+ if (opp_table->supported_hw) {
+ dev_err(dev, "%s: Already have supported hardware list\n",
+ __func__);
+ ret = -EBUSY;
+ goto err;
+ }
+
+ opp_table->supported_hw = kmemdup(versions, count * sizeof(*versions),
+ GFP_KERNEL);
+ if (!opp_table->supported_hw) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ opp_table->supported_hw_count = count;
+ mutex_unlock(&opp_table_lock);
+ return 0;
+
+err:
+ _remove_opp_table(opp_table);
+unlock:
+ mutex_unlock(&opp_table_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_set_supported_hw);
+
+/**
+ * dev_pm_opp_put_supported_hw() - Releases resources blocked for supported hw
+ * @dev: Device for which supported-hw has to be put.
+ *
+ * This is required only for the V2 bindings, and is called for a matching
+ * dev_pm_opp_set_supported_hw(). Until this is called, the opp_table structure
+ * will not be freed.
+ *
+ * Locking: The internal opp_table and opp structures are RCU protected.
+ * Hence this function internally uses RCU updater strategy with mutex locks
+ * to keep the integrity of the internal data structures. Callers should ensure
+ * that this function is *NOT* called under RCU protection or in contexts where
+ * mutex cannot be locked.
+ */
+void dev_pm_opp_put_supported_hw(struct device *dev)
+{
+ struct opp_table *opp_table;
+
+ /* Hold our table modification lock here */
+ mutex_lock(&opp_table_lock);
+
+ /* Check for existing table for 'dev' first */
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table)) {
+ dev_err(dev, "Failed to find opp_table: %ld\n",
+ PTR_ERR(opp_table));
+ goto unlock;
+ }
+
+ /* Make sure there are no concurrent readers while updating opp_table */
+ WARN_ON(!list_empty(&opp_table->opp_list));
+
+ if (!opp_table->supported_hw) {
+ dev_err(dev, "%s: Doesn't have supported hardware list\n",
+ __func__);
+ goto unlock;
+ }
+
+ kfree(opp_table->supported_hw);
+ opp_table->supported_hw = NULL;
+ opp_table->supported_hw_count = 0;
+
+ /* Try freeing opp_table if this was the last blocking resource */
+ _remove_opp_table(opp_table);
+
+unlock:
+ mutex_unlock(&opp_table_lock);
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw);
+
+/**
+ * dev_pm_opp_set_prop_name() - Set prop-extn name
+ * @dev: Device for which the prop-name has to be set.
+ * @name: name to postfix to properties.
+ *
+ * This is required only for the V2 bindings, and it enables a platform to
+ * specify the extn to be used for certain property names. The properties to
+ * which the extension will apply are opp-microvolt and opp-microamp. OPP core
+ * should postfix the property name with -<name> while looking for them.
+ *
+ * Locking: The internal opp_table and opp structures are RCU protected.
+ * Hence this function internally uses RCU updater strategy with mutex locks
+ * to keep the integrity of the internal data structures. Callers should ensure
+ * that this function is *NOT* called under RCU protection or in contexts where
+ * mutex cannot be locked.
+ */
+int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
+{
+ struct opp_table *opp_table;
+ int ret = 0;
+
+ /* Hold our table modification lock here */
+ mutex_lock(&opp_table_lock);
+
+ opp_table = _add_opp_table(dev);
+ if (!opp_table) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ /* Make sure there are no concurrent readers while updating opp_table */
+ WARN_ON(!list_empty(&opp_table->opp_list));
+
+ /* Do we already have a prop-name associated with opp_table? */
+ if (opp_table->prop_name) {
+ dev_err(dev, "%s: Already have prop-name %s\n", __func__,
+ opp_table->prop_name);
+ ret = -EBUSY;
+ goto err;
+ }
+
+ opp_table->prop_name = kstrdup(name, GFP_KERNEL);
+ if (!opp_table->prop_name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ mutex_unlock(&opp_table_lock);
+ return 0;
+
+err:
+ _remove_opp_table(opp_table);
+unlock:
+ mutex_unlock(&opp_table_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_set_prop_name);
+
+/**
+ * dev_pm_opp_put_prop_name() - Releases resources blocked for prop-name
+ * @dev: Device for which the prop-name has to be put.
+ *
+ * This is required only for the V2 bindings, and is called for a matching
+ * dev_pm_opp_set_prop_name(). Until this is called, the opp_table structure
+ * will not be freed.
+ *
+ * Locking: The internal opp_table and opp structures are RCU protected.
+ * Hence this function internally uses RCU updater strategy with mutex locks
+ * to keep the integrity of the internal data structures. Callers should ensure
+ * that this function is *NOT* called under RCU protection or in contexts where
+ * mutex cannot be locked.
+ */
+void dev_pm_opp_put_prop_name(struct device *dev)
+{
+ struct opp_table *opp_table;
+
+ /* Hold our table modification lock here */
+ mutex_lock(&opp_table_lock);
+
+ /* Check for existing table for 'dev' first */
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table)) {
+ dev_err(dev, "Failed to find opp_table: %ld\n",
+ PTR_ERR(opp_table));
+ goto unlock;
+ }
+
+ /* Make sure there are no concurrent readers while updating opp_table */
+ WARN_ON(!list_empty(&opp_table->opp_list));
+
+ if (!opp_table->prop_name) {
+ dev_err(dev, "%s: Doesn't have a prop-name\n", __func__);
+ goto unlock;
+ }
+
+ kfree(opp_table->prop_name);
+ opp_table->prop_name = NULL;
+
+ /* Try freeing opp_table if this was the last blocking resource */
+ _remove_opp_table(opp_table);
+
+unlock:
+ mutex_unlock(&opp_table_lock);
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
+
+/**
+ * dev_pm_opp_set_regulator() - Set regulator name for the device
+ * @dev: Device for which regulator name is being set.
+ * @name: Name of the regulator.
+ *
+ * In order to support OPP switching, OPP layer needs to know the name of the
+ * device's regulator, as the core would be required to switch voltages as well.
+ *
+ * This must be called before any OPPs are initialized for the device.
+ *
+ * Locking: The internal opp_table and opp structures are RCU protected.
+ * Hence this function internally uses RCU updater strategy with mutex locks
+ * to keep the integrity of the internal data structures. Callers should ensure
+ * that this function is *NOT* called under RCU protection or in contexts where
+ * mutex cannot be locked.
+ */
+int dev_pm_opp_set_regulator(struct device *dev, const char *name)
+{
+ struct opp_table *opp_table;
+ struct regulator *reg;
+ int ret;
+
+ mutex_lock(&opp_table_lock);
+
+ opp_table = _add_opp_table(dev);
+ if (!opp_table) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ /* This should be called before OPPs are initialized */
+ if (WARN_ON(!list_empty(&opp_table->opp_list))) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ /* Already have a regulator set */
+ if (WARN_ON(!IS_ERR(opp_table->regulator))) {
+ ret = -EBUSY;
+ goto err;
+ }
+ /* Allocate the regulator */
+ reg = regulator_get_optional(dev, name);
+ if (IS_ERR(reg)) {
+ ret = PTR_ERR(reg);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "%s: no regulator (%s) found: %d\n",
+ __func__, name, ret);
+ goto err;
+ }
+
+ opp_table->regulator = reg;
+
+ mutex_unlock(&opp_table_lock);
+ return 0;
+
+err:
+ _remove_opp_table(opp_table);
+unlock:
+ mutex_unlock(&opp_table_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulator);
+
+/**
+ * dev_pm_opp_put_regulator() - Releases resources blocked for regulator
+ * @dev: Device for which regulator was set.
+ *
+ * Locking: The internal opp_table and opp structures are RCU protected.
+ * Hence this function internally uses RCU updater strategy with mutex locks
+ * to keep the integrity of the internal data structures. Callers should ensure
+ * that this function is *NOT* called under RCU protection or in contexts where
+ * mutex cannot be locked.
+ */
+void dev_pm_opp_put_regulator(struct device *dev)
+{
+ struct opp_table *opp_table;
+
+ mutex_lock(&opp_table_lock);
+
+ /* Check for existing table for 'dev' first */
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table)) {
+ dev_err(dev, "Failed to find opp_table: %ld\n",
+ PTR_ERR(opp_table));
+ goto unlock;
+ }
+
+ if (IS_ERR(opp_table->regulator)) {
+ dev_err(dev, "%s: Doesn't have regulator set\n", __func__);
+ goto unlock;
+ }
+
+ /* Make sure there are no concurrent readers while updating opp_table */
+ WARN_ON(!list_empty(&opp_table->opp_list));
+
+ regulator_put(opp_table->regulator);
+ opp_table->regulator = ERR_PTR(-ENXIO);
+
+ /* Try freeing opp_table if this was the last blocking resource */
+ _remove_opp_table(opp_table);
+
+unlock:
+ mutex_unlock(&opp_table_lock);
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulator);
+
+static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
+ struct device_node *np)
+{
+ unsigned int count = opp_table->supported_hw_count;
+ u32 version;
+ int ret;
+
+ if (!opp_table->supported_hw)
+ return true;
+
+ while (count--) {
+ ret = of_property_read_u32_index(np, "opp-supported-hw", count,
+ &version);
+ if (ret) {
+ dev_warn(dev, "%s: failed to read opp-supported-hw property at index %d: %d\n",
+ __func__, count, ret);
+ return false;
+ }
+
+ /* Both of these are bitwise masks of the versions */
+ if (!(version & opp_table->supported_hw[count]))
+ return false;
+ }
+
+ return true;
+}
+
+/**
* _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings)
* @dev: device for which we do this operation
* @np: device node
*
- * This function adds an opp definition to the opp list and returns status. The
+ * This function adds an opp definition to the opp table and returns status. The
* opp can be controlled using dev_pm_opp_enable/disable functions and may be
* removed by dev_pm_opp_remove.
*
- * Locking: The internal device_opp and opp structures are RCU protected.
+ * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where
@@ -843,16 +1572,16 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev)
*/
static int _opp_add_static_v2(struct device *dev, struct device_node *np)
{
- struct device_opp *dev_opp;
+ struct opp_table *opp_table;
struct dev_pm_opp *new_opp;
u64 rate;
u32 val;
int ret;
- /* Hold our list modification lock here */
- mutex_lock(&dev_opp_list_lock);
+ /* Hold our table modification lock here */
+ mutex_lock(&opp_table_lock);
- new_opp = _allocate_opp(dev, &dev_opp);
+ new_opp = _allocate_opp(dev, &opp_table);
if (!new_opp) {
ret = -ENOMEM;
goto unlock;
@@ -864,6 +1593,12 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
goto free_opp;
}
+ /* Check if the OPP supports hardware's hierarchy of versions or not */
+ if (!_opp_is_supported(dev, opp_table, np)) {
+ dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate);
+ goto free_opp;
+ }
+
/*
* Rate is defined as an unsigned long in clk API, and so casting
* explicitly to its type. Must be fixed once rate is 64 bit
@@ -879,28 +1614,30 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
if (!of_property_read_u32(np, "clock-latency-ns", &val))
new_opp->clock_latency_ns = val;
- ret = opp_parse_supplies(new_opp, dev);
+ ret = opp_parse_supplies(new_opp, dev, opp_table);
if (ret)
goto free_opp;
- ret = _opp_add(dev, new_opp, dev_opp);
+ ret = _opp_add(dev, new_opp, opp_table);
if (ret)
goto free_opp;
/* OPP to select on device suspend */
if (of_property_read_bool(np, "opp-suspend")) {
- if (dev_opp->suspend_opp)
+ if (opp_table->suspend_opp) {
dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n",
- __func__, dev_opp->suspend_opp->rate,
+ __func__, opp_table->suspend_opp->rate,
new_opp->rate);
- else
- dev_opp->suspend_opp = new_opp;
+ } else {
+ new_opp->suspend = true;
+ opp_table->suspend_opp = new_opp;
+ }
}
- if (new_opp->clock_latency_ns > dev_opp->clock_latency_ns_max)
- dev_opp->clock_latency_ns_max = new_opp->clock_latency_ns;
+ if (new_opp->clock_latency_ns > opp_table->clock_latency_ns_max)
+ opp_table->clock_latency_ns_max = new_opp->clock_latency_ns;
- mutex_unlock(&dev_opp_list_lock);
+ mutex_unlock(&opp_table_lock);
pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu\n",
__func__, new_opp->turbo, new_opp->rate, new_opp->u_volt,
@@ -911,13 +1648,13 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
* Notify the changes in the availability of the operable
* frequency/voltage list.
*/
- srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ADD, new_opp);
+ srcu_notifier_call_chain(&opp_table->srcu_head, OPP_EVENT_ADD, new_opp);
return 0;
free_opp:
- _opp_remove(dev_opp, new_opp, false);
+ _opp_remove(opp_table, new_opp, false);
unlock:
- mutex_unlock(&dev_opp_list_lock);
+ mutex_unlock(&opp_table_lock);
return ret;
}
@@ -927,11 +1664,11 @@ unlock:
* @freq: Frequency in Hz for this OPP
* @u_volt: Voltage in uVolts for this OPP
*
- * This function adds an opp definition to the opp list and returns status.
+ * This function adds an opp definition to the opp table and returns status.
* The opp is made available by default and it can be controlled using
* dev_pm_opp_enable/disable functions.
*
- * Locking: The internal device_opp and opp structures are RCU protected.
+ * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where
@@ -963,7 +1700,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_add);
* copy operation, returns 0 if no modification was done OR modification was
* successful.
*
- * Locking: The internal device_opp and opp structures are RCU protected.
+ * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks to
* keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where
@@ -972,7 +1709,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_add);
static int _opp_set_availability(struct device *dev, unsigned long freq,
bool availability_req)
{
- struct device_opp *dev_opp;
+ struct opp_table *opp_table;
struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV);
int r = 0;
@@ -981,18 +1718,18 @@ static int _opp_set_availability(struct device *dev, unsigned long freq,
if (!new_opp)
return -ENOMEM;
- mutex_lock(&dev_opp_list_lock);
+ mutex_lock(&opp_table_lock);
- /* Find the device_opp */
- dev_opp = _find_device_opp(dev);
- if (IS_ERR(dev_opp)) {
- r = PTR_ERR(dev_opp);
+ /* Find the opp_table */
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table)) {
+ r = PTR_ERR(opp_table);
dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r);
goto unlock;
}
/* Do we have the frequency? */
- list_for_each_entry(tmp_opp, &dev_opp->opp_list, node) {
+ list_for_each_entry(tmp_opp, &opp_table->opp_list, node) {
if (tmp_opp->rate == freq) {
opp = tmp_opp;
break;
@@ -1013,21 +1750,21 @@ static int _opp_set_availability(struct device *dev, unsigned long freq,
new_opp->available = availability_req;
list_replace_rcu(&opp->node, &new_opp->node);
- mutex_unlock(&dev_opp_list_lock);
- call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu);
+ mutex_unlock(&opp_table_lock);
+ call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu);
/* Notify the change of the OPP availability */
if (availability_req)
- srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ENABLE,
- new_opp);
+ srcu_notifier_call_chain(&opp_table->srcu_head,
+ OPP_EVENT_ENABLE, new_opp);
else
- srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_DISABLE,
- new_opp);
+ srcu_notifier_call_chain(&opp_table->srcu_head,
+ OPP_EVENT_DISABLE, new_opp);
return 0;
unlock:
- mutex_unlock(&dev_opp_list_lock);
+ mutex_unlock(&opp_table_lock);
kfree(new_opp);
return r;
}
@@ -1041,7 +1778,7 @@ unlock:
* corresponding error value. It is meant to be used for users an OPP available
* after being temporarily made unavailable with dev_pm_opp_disable.
*
- * Locking: The internal device_opp and opp structures are RCU protected.
+ * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function indirectly uses RCU and mutex locks to keep the
* integrity of the internal data structures. Callers should ensure that
* this function is *NOT* called under RCU protection or in contexts where
@@ -1067,7 +1804,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_enable);
* control by users to make this OPP not available until the circumstances are
* right to make it available again (with a call to dev_pm_opp_enable).
*
- * Locking: The internal device_opp and opp structures are RCU protected.
+ * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function indirectly uses RCU and mutex locks to keep the
* integrity of the internal data structures. Callers should ensure that
* this function is *NOT* called under RCU protection or in contexts where
@@ -1085,26 +1822,26 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_disable);
/**
* dev_pm_opp_get_notifier() - find notifier_head of the device with opp
- * @dev: device pointer used to lookup device OPPs.
+ * @dev: device pointer used to lookup OPP table.
*
* Return: pointer to notifier head if found, otherwise -ENODEV or
* -EINVAL based on type of error casted as pointer. value must be checked
* with IS_ERR to determine valid pointer or error result.
*
- * Locking: This function must be called under rcu_read_lock(). dev_opp is a RCU
- * protected pointer. The reason for the same is that the opp pointer which is
- * returned will remain valid for use with opp_get_{voltage, freq} only while
+ * Locking: This function must be called under rcu_read_lock(). opp_table is a
+ * RCU protected pointer. The reason for the same is that the opp pointer which
+ * is returned will remain valid for use with opp_get_{voltage, freq} only while
* under the locked area. The pointer returned must be used prior to unlocking
* with rcu_read_unlock() to maintain the integrity of the pointer.
*/
struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev)
{
- struct device_opp *dev_opp = _find_device_opp(dev);
+ struct opp_table *opp_table = _find_opp_table(dev);
- if (IS_ERR(dev_opp))
- return ERR_CAST(dev_opp); /* matching type */
+ if (IS_ERR(opp_table))
+ return ERR_CAST(opp_table); /* matching type */
- return &dev_opp->srcu_head;
+ return &opp_table->srcu_head;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier);
@@ -1112,11 +1849,11 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier);
/**
* dev_pm_opp_of_remove_table() - Free OPP table entries created from static DT
* entries
- * @dev: device pointer used to lookup device OPPs.
+ * @dev: device pointer used to lookup OPP table.
*
* Free OPPs created using static entries present in DT.
*
- * Locking: The internal device_opp and opp structures are RCU protected.
+ * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function indirectly uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where
@@ -1124,38 +1861,38 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier);
*/
void dev_pm_opp_of_remove_table(struct device *dev)
{
- struct device_opp *dev_opp;
+ struct opp_table *opp_table;
struct dev_pm_opp *opp, *tmp;
- /* Hold our list modification lock here */
- mutex_lock(&dev_opp_list_lock);
+ /* Hold our table modification lock here */
+ mutex_lock(&opp_table_lock);
- /* Check for existing list for 'dev' */
- dev_opp = _find_device_opp(dev);
- if (IS_ERR(dev_opp)) {
- int error = PTR_ERR(dev_opp);
+ /* Check for existing table for 'dev' */
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table)) {
+ int error = PTR_ERR(opp_table);
if (error != -ENODEV)
- WARN(1, "%s: dev_opp: %d\n",
+ WARN(1, "%s: opp_table: %d\n",
IS_ERR_OR_NULL(dev) ?
"Invalid device" : dev_name(dev),
error);
goto unlock;
}
- /* Find if dev_opp manages a single device */
- if (list_is_singular(&dev_opp->dev_list)) {
+ /* Find if opp_table manages a single device */
+ if (list_is_singular(&opp_table->dev_list)) {
/* Free static OPPs */
- list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) {
+ list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) {
if (!opp->dynamic)
- _opp_remove(dev_opp, opp, true);
+ _opp_remove(opp_table, opp, true);
}
} else {
- _remove_list_dev(_find_list_dev(dev, dev_opp), dev_opp);
+ _remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table);
}
unlock:
- mutex_unlock(&dev_opp_list_lock);
+ mutex_unlock(&opp_table_lock);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table);
@@ -1176,22 +1913,22 @@ struct device_node *_of_get_opp_desc_node(struct device *dev)
static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
{
struct device_node *np;
- struct device_opp *dev_opp;
+ struct opp_table *opp_table;
int ret = 0, count = 0;
- mutex_lock(&dev_opp_list_lock);
+ mutex_lock(&opp_table_lock);
- dev_opp = _managed_opp(opp_np);
- if (dev_opp) {
+ opp_table = _managed_opp(opp_np);
+ if (opp_table) {
/* OPPs are already managed */
- if (!_add_list_dev(dev, dev_opp))
+ if (!_add_opp_dev(dev, opp_table))
ret = -ENOMEM;
- mutex_unlock(&dev_opp_list_lock);
+ mutex_unlock(&opp_table_lock);
return ret;
}
- mutex_unlock(&dev_opp_list_lock);
+ mutex_unlock(&opp_table_lock);
- /* We have opp-list node now, iterate over it and add OPPs */
+ /* We have opp-table node now, iterate over it and add OPPs */
for_each_available_child_of_node(opp_np, np) {
count++;
@@ -1207,19 +1944,19 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
if (WARN_ON(!count))
return -ENOENT;
- mutex_lock(&dev_opp_list_lock);
+ mutex_lock(&opp_table_lock);
- dev_opp = _find_device_opp(dev);
- if (WARN_ON(IS_ERR(dev_opp))) {
- ret = PTR_ERR(dev_opp);
- mutex_unlock(&dev_opp_list_lock);
+ opp_table = _find_opp_table(dev);
+ if (WARN_ON(IS_ERR(opp_table))) {
+ ret = PTR_ERR(opp_table);
+ mutex_unlock(&opp_table_lock);
goto free_table;
}
- dev_opp->np = opp_np;
- dev_opp->shared_opp = of_property_read_bool(opp_np, "opp-shared");
+ opp_table->np = opp_np;
+ opp_table->shared_opp = of_property_read_bool(opp_np, "opp-shared");
- mutex_unlock(&dev_opp_list_lock);
+ mutex_unlock(&opp_table_lock);
return 0;
@@ -1248,7 +1985,7 @@ static int _of_add_opp_table_v1(struct device *dev)
*/
nr = prop->length / sizeof(u32);
if (nr % 2) {
- dev_err(dev, "%s: Invalid OPP list\n", __func__);
+ dev_err(dev, "%s: Invalid OPP table\n", __func__);
return -EINVAL;
}
@@ -1268,11 +2005,11 @@ static int _of_add_opp_table_v1(struct device *dev)
/**
* dev_pm_opp_of_add_table() - Initialize opp table from device tree
- * @dev: device pointer used to lookup device OPPs.
+ * @dev: device pointer used to lookup OPP table.
*
* Register the initial OPP table with the OPP library for given device.
*
- * Locking: The internal device_opp and opp structures are RCU protected.
+ * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function indirectly uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where
diff --git a/drivers/base/power/opp/cpu.c b/drivers/base/power/opp/cpu.c
index 7b445e88a0d5..ba2bdbd932ef 100644
--- a/drivers/base/power/opp/cpu.c
+++ b/drivers/base/power/opp/cpu.c
@@ -31,7 +31,7 @@
* @table: Cpufreq table returned back to caller
*
* Generate a cpufreq table for a provided device- this assumes that the
- * opp list is already initialized and ready for usage.
+ * opp table is already initialized and ready for usage.
*
* This function allocates required memory for the cpufreq table. It is
* expected that the caller does the required maintenance such as freeing
@@ -44,7 +44,7 @@
* WARNING: It is important for the callers to ensure refreshing their copy of
* the table if any of the mentioned functions have been invoked in the interim.
*
- * Locking: The internal device_opp and opp structures are RCU protected.
+ * Locking: The internal opp_table and opp structures are RCU protected.
* Since we just use the regular accessor functions to access the internal data
* structures, we use RCU read lock inside this function. As a result, users of
* this function DONOT need to use explicit locks for invoking.
@@ -122,15 +122,15 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table);
/* Required only for V1 bindings, as v2 can manage it from DT itself */
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask)
{
- struct device_list_opp *list_dev;
- struct device_opp *dev_opp;
+ struct opp_device *opp_dev;
+ struct opp_table *opp_table;
struct device *dev;
int cpu, ret = 0;
- mutex_lock(&dev_opp_list_lock);
+ mutex_lock(&opp_table_lock);
- dev_opp = _find_device_opp(cpu_dev);
- if (IS_ERR(dev_opp)) {
+ opp_table = _find_opp_table(cpu_dev);
+ if (IS_ERR(opp_table)) {
ret = -EINVAL;
goto unlock;
}
@@ -146,15 +146,15 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask)
continue;
}
- list_dev = _add_list_dev(dev, dev_opp);
- if (!list_dev) {
- dev_err(dev, "%s: failed to add list-dev for cpu%d device\n",
+ opp_dev = _add_opp_dev(dev, opp_table);
+ if (!opp_dev) {
+ dev_err(dev, "%s: failed to add opp-dev for cpu%d device\n",
__func__, cpu);
continue;
}
}
unlock:
- mutex_unlock(&dev_opp_list_lock);
+ mutex_unlock(&opp_table_lock);
return ret;
}
@@ -214,7 +214,6 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table);
/*
* Works only for OPP v2 bindings.
*
- * cpumask should be already set to mask of cpu_dev->id.
* Returns -ENOENT if operating-points-v2 bindings aren't supported.
*/
int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask)
@@ -230,6 +229,8 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask
return -ENOENT;
}
+ cpumask_set_cpu(cpu_dev->id, cpumask);
+
/* OPPs are shared ? */
if (!of_property_read_bool(np, "opp-shared"))
goto put_cpu_node;
diff --git a/drivers/base/power/opp/debugfs.c b/drivers/base/power/opp/debugfs.c
new file mode 100644
index 000000000000..ef1ae6b52042
--- /dev/null
+++ b/drivers/base/power/opp/debugfs.c
@@ -0,0 +1,218 @@
+/*
+ * Generic OPP debugfs interface
+ *
+ * Copyright (C) 2015-2016 Viresh Kumar <viresh.kumar@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/limits.h>
+
+#include "opp.h"
+
+static struct dentry *rootdir;
+
+static void opp_set_dev_name(const struct device *dev, char *name)
+{
+ if (dev->parent)
+ snprintf(name, NAME_MAX, "%s-%s", dev_name(dev->parent),
+ dev_name(dev));
+ else
+ snprintf(name, NAME_MAX, "%s", dev_name(dev));
+}
+
+void opp_debug_remove_one(struct dev_pm_opp *opp)
+{
+ debugfs_remove_recursive(opp->dentry);
+}
+
+int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
+{
+ struct dentry *pdentry = opp_table->dentry;
+ struct dentry *d;
+ char name[25]; /* 20 chars for 64 bit value + 5 (opp:\0) */
+
+ /* Rate is unique to each OPP, use it to give opp-name */
+ snprintf(name, sizeof(name), "opp:%lu", opp->rate);
+
+ /* Create per-opp directory */
+ d = debugfs_create_dir(name, pdentry);
+ if (!d)
+ return -ENOMEM;
+
+ if (!debugfs_create_bool("available", S_IRUGO, d, &opp->available))
+ return -ENOMEM;
+
+ if (!debugfs_create_bool("dynamic", S_IRUGO, d, &opp->dynamic))
+ return -ENOMEM;
+
+ if (!debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo))
+ return -ENOMEM;
+
+ if (!debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend))
+ return -ENOMEM;
+
+ if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate))
+ return -ENOMEM;
+
+ if (!debugfs_create_ulong("u_volt_target", S_IRUGO, d, &opp->u_volt))
+ return -ENOMEM;
+
+ if (!debugfs_create_ulong("u_volt_min", S_IRUGO, d, &opp->u_volt_min))
+ return -ENOMEM;
+
+ if (!debugfs_create_ulong("u_volt_max", S_IRUGO, d, &opp->u_volt_max))
+ return -ENOMEM;
+
+ if (!debugfs_create_ulong("u_amp", S_IRUGO, d, &opp->u_amp))
+ return -ENOMEM;
+
+ if (!debugfs_create_ulong("clock_latency_ns", S_IRUGO, d,
+ &opp->clock_latency_ns))
+ return -ENOMEM;
+
+ opp->dentry = d;
+ return 0;
+}
+
+static int opp_list_debug_create_dir(struct opp_device *opp_dev,
+ struct opp_table *opp_table)
+{
+ const struct device *dev = opp_dev->dev;
+ struct dentry *d;
+
+ opp_set_dev_name(dev, opp_table->dentry_name);
+
+ /* Create device specific directory */
+ d = debugfs_create_dir(opp_table->dentry_name, rootdir);
+ if (!d) {
+ dev_err(dev, "%s: Failed to create debugfs dir\n", __func__);
+ return -ENOMEM;
+ }
+
+ opp_dev->dentry = d;
+ opp_table->dentry = d;
+
+ return 0;
+}
+
+static int opp_list_debug_create_link(struct opp_device *opp_dev,
+ struct opp_table *opp_table)
+{
+ const struct device *dev = opp_dev->dev;
+ char name[NAME_MAX];
+ struct dentry *d;
+
+ opp_set_dev_name(opp_dev->dev, name);
+
+ /* Create device specific directory link */
+ d = debugfs_create_symlink(name, rootdir, opp_table->dentry_name);
+ if (!d) {
+ dev_err(dev, "%s: Failed to create link\n", __func__);
+ return -ENOMEM;
+ }
+
+ opp_dev->dentry = d;
+
+ return 0;
+}
+
+/**
+ * opp_debug_register - add a device opp node to the debugfs 'opp' directory
+ * @opp_dev: opp-dev pointer for device
+ * @opp_table: the device-opp being added
+ *
+ * Dynamically adds device specific directory in debugfs 'opp' directory. If the
+ * device-opp is shared with other devices, then links will be created for all
+ * devices except the first.
+ *
+ * Return: 0 on success, otherwise negative error.
+ */
+int opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table)
+{
+ if (!rootdir) {
+ pr_debug("%s: Uninitialized rootdir\n", __func__);
+ return -EINVAL;
+ }
+
+ if (opp_table->dentry)
+ return opp_list_debug_create_link(opp_dev, opp_table);
+
+ return opp_list_debug_create_dir(opp_dev, opp_table);
+}
+
+static void opp_migrate_dentry(struct opp_device *opp_dev,
+ struct opp_table *opp_table)
+{
+ struct opp_device *new_dev;
+ const struct device *dev;
+ struct dentry *dentry;
+
+ /* Look for next opp-dev */
+ list_for_each_entry(new_dev, &opp_table->dev_list, node)
+ if (new_dev != opp_dev)
+ break;
+
+ /* new_dev is guaranteed to be valid here */
+ dev = new_dev->dev;
+ debugfs_remove_recursive(new_dev->dentry);
+
+ opp_set_dev_name(dev, opp_table->dentry_name);
+
+ dentry = debugfs_rename(rootdir, opp_dev->dentry, rootdir,
+ opp_table->dentry_name);
+ if (!dentry) {
+ dev_err(dev, "%s: Failed to rename link from: %s to %s\n",
+ __func__, dev_name(opp_dev->dev), dev_name(dev));
+ return;
+ }
+
+ new_dev->dentry = dentry;
+ opp_table->dentry = dentry;
+}
+
+/**
+ * opp_debug_unregister - remove a device opp node from debugfs opp directory
+ * @opp_dev: opp-dev pointer for device
+ * @opp_table: the device-opp being removed
+ *
+ * Dynamically removes device specific directory from debugfs 'opp' directory.
+ */
+void opp_debug_unregister(struct opp_device *opp_dev,
+ struct opp_table *opp_table)
+{
+ if (opp_dev->dentry == opp_table->dentry) {
+ /* Move the real dentry object under another device */
+ if (!list_is_singular(&opp_table->dev_list)) {
+ opp_migrate_dentry(opp_dev, opp_table);
+ goto out;
+ }
+ opp_table->dentry = NULL;
+ }
+
+ debugfs_remove_recursive(opp_dev->dentry);
+
+out:
+ opp_dev->dentry = NULL;
+}
+
+static int __init opp_debug_init(void)
+{
+ /* Create /sys/kernel/debug/opp directory */
+ rootdir = debugfs_create_dir("opp", NULL);
+ if (!rootdir) {
+ pr_err("%s: Failed to create root directory\n", __func__);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+core_initcall(opp_debug_init);
diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h
index 7366b2aa8997..f67f806fcf3a 100644
--- a/drivers/base/power/opp/opp.h
+++ b/drivers/base/power/opp/opp.h
@@ -17,17 +17,21 @@
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/limits.h>
#include <linux/pm_opp.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
+struct clk;
+struct regulator;
+
/* Lock to allow exclusive modification to the device and opp lists */
-extern struct mutex dev_opp_list_lock;
+extern struct mutex opp_table_lock;
/*
* Internal data structure organization with the OPP layer library is as
* follows:
- * dev_opp_list (root)
+ * opp_tables (root)
* |- device 1 (represents voltage domain 1)
* | |- opp 1 (availability, freq, voltage)
* | |- opp 2 ..
@@ -36,23 +40,24 @@ extern struct mutex dev_opp_list_lock;
* |- device 2 (represents the next voltage domain)
* ...
* `- device m (represents mth voltage domain)
- * device 1, 2.. are represented by dev_opp structure while each opp
+ * device 1, 2.. are represented by opp_table structure while each opp
* is represented by the opp structure.
*/
/**
* struct dev_pm_opp - Generic OPP description structure
- * @node: opp list node. The nodes are maintained throughout the lifetime
+ * @node: opp table node. The nodes are maintained throughout the lifetime
* of boot. It is expected only an optimal set of OPPs are
* added to the library by the SoC framework.
- * RCU usage: opp list is traversed with RCU locks. node
+ * RCU usage: opp table is traversed with RCU locks. node
* modification is possible realtime, hence the modifications
- * are protected by the dev_opp_list_lock for integrity.
+ * are protected by the opp_table_lock for integrity.
* IMPORTANT: the opp nodes should be maintained in increasing
* order.
- * @dynamic: not-created from static DT entries.
* @available: true/false - marks if this OPP as available or not
+ * @dynamic: not-created from static DT entries.
* @turbo: true if turbo (boost) OPP
+ * @suspend: true if suspend OPP
* @rate: Frequency in hertz
* @u_volt: Target voltage in microvolts corresponding to this OPP
* @u_volt_min: Minimum voltage in microvolts corresponding to this OPP
@@ -60,9 +65,10 @@ extern struct mutex dev_opp_list_lock;
* @u_amp: Maximum current drawn by the device in microamperes
* @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's
* frequency from any other OPP's frequency.
- * @dev_opp: points back to the device_opp struct this opp belongs to
+ * @opp_table: points back to the opp_table struct this opp belongs to
* @rcu_head: RCU callback head used for deferred freeing
* @np: OPP's device node.
+ * @dentry: debugfs dentry pointer (per opp)
*
* This structure stores the OPP information for a given device.
*/
@@ -72,6 +78,7 @@ struct dev_pm_opp {
bool available;
bool dynamic;
bool turbo;
+ bool suspend;
unsigned long rate;
unsigned long u_volt;
@@ -80,40 +87,60 @@ struct dev_pm_opp {
unsigned long u_amp;
unsigned long clock_latency_ns;
- struct device_opp *dev_opp;
+ struct opp_table *opp_table;
struct rcu_head rcu_head;
struct device_node *np;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
};
/**
- * struct device_list_opp - devices managed by 'struct device_opp'
+ * struct opp_device - devices managed by 'struct opp_table'
* @node: list node
* @dev: device to which the struct object belongs
* @rcu_head: RCU callback head used for deferred freeing
+ * @dentry: debugfs dentry pointer (per device)
*
- * This is an internal data structure maintaining the list of devices that are
- * managed by 'struct device_opp'.
+ * This is an internal data structure maintaining the devices that are managed
+ * by 'struct opp_table'.
*/
-struct device_list_opp {
+struct opp_device {
struct list_head node;
const struct device *dev;
struct rcu_head rcu_head;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
};
/**
- * struct device_opp - Device opp structure
- * @node: list node - contains the devices with OPPs that
+ * struct opp_table - Device opp structure
+ * @node: table node - contains the devices with OPPs that
* have been registered. Nodes once added are not modified in this
- * list.
- * RCU usage: nodes are not modified in the list of device_opp,
- * however addition is possible and is secured by dev_opp_list_lock
+ * table.
+ * RCU usage: nodes are not modified in the table of opp_table,
+ * however addition is possible and is secured by opp_table_lock
* @srcu_head: notifier head to notify the OPP availability changes.
* @rcu_head: RCU callback head used for deferred freeing
* @dev_list: list of devices that share these OPPs
- * @opp_list: list of opps
+ * @opp_list: table of opps
* @np: struct device_node pointer for opp's DT node.
+ * @clock_latency_ns_max: Max clock latency in nanoseconds.
* @shared_opp: OPP is shared between multiple devices.
+ * @suspend_opp: Pointer to OPP to be used during device suspend.
+ * @supported_hw: Array of version number to support.
+ * @supported_hw_count: Number of elements in supported_hw array.
+ * @prop_name: A name to postfix to many DT properties, while parsing them.
+ * @clk: Device's clock handle
+ * @regulator: Supply regulator
+ * @dentry: debugfs dentry pointer of the real device directory (not links).
+ * @dentry_name: Name of the real dentry.
+ *
+ * @voltage_tolerance_v1: In percentage, for v1 bindings only.
*
* This is an internal data structure maintaining the link to opps attached to
* a device. This structure is not meant to be shared to users as it is
@@ -123,7 +150,7 @@ struct device_list_opp {
* need to wait for the grace period of both of them before freeing any
* resources. And so we have used kfree_rcu() from within call_srcu() handlers.
*/
-struct device_opp {
+struct opp_table {
struct list_head node;
struct srcu_notifier_head srcu_head;
@@ -133,14 +160,48 @@ struct device_opp {
struct device_node *np;
unsigned long clock_latency_ns_max;
+
+ /* For backward compatibility with v1 bindings */
+ unsigned int voltage_tolerance_v1;
+
bool shared_opp;
struct dev_pm_opp *suspend_opp;
+
+ unsigned int *supported_hw;
+ unsigned int supported_hw_count;
+ const char *prop_name;
+ struct clk *clk;
+ struct regulator *regulator;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+ char dentry_name[NAME_MAX];
+#endif
};
/* Routines internal to opp core */
-struct device_opp *_find_device_opp(struct device *dev);
-struct device_list_opp *_add_list_dev(const struct device *dev,
- struct device_opp *dev_opp);
+struct opp_table *_find_opp_table(struct device *dev);
+struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_table);
struct device_node *_of_get_opp_desc_node(struct device *dev);
+#ifdef CONFIG_DEBUG_FS
+void opp_debug_remove_one(struct dev_pm_opp *opp);
+int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table);
+int opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table);
+void opp_debug_unregister(struct opp_device *opp_dev, struct opp_table *opp_table);
+#else
+static inline void opp_debug_remove_one(struct dev_pm_opp *opp) {}
+
+static inline int opp_debug_create_one(struct dev_pm_opp *opp,
+ struct opp_table *opp_table)
+{ return 0; }
+static inline int opp_debug_register(struct opp_device *opp_dev,
+ struct opp_table *opp_table)
+{ return 0; }
+
+static inline void opp_debug_unregister(struct opp_device *opp_dev,
+ struct opp_table *opp_table)
+{ }
+#endif /* DEBUG_FS */
+
#endif /* __DRIVER_OPP_H__ */
diff --git a/drivers/block/brd.c b/drivers/block/brd.c
index a5880f4ab40e..1914c63ca8b1 100644
--- a/drivers/block/brd.c
+++ b/drivers/block/brd.c
@@ -338,7 +338,7 @@ static blk_qc_t brd_make_request(struct request_queue *q, struct bio *bio)
if (unlikely(bio->bi_rw & REQ_DISCARD)) {
if (sector & ((PAGE_SIZE >> SECTOR_SHIFT) - 1) ||
- bio->bi_iter.bi_size & PAGE_MASK)
+ bio->bi_iter.bi_size & ~PAGE_MASK)
goto io_error;
discard_from_brd(brd, sector, bio->bi_iter.bi_size);
goto out;
diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
index 3457ac8c03e2..55d3d1da72de 100644
--- a/drivers/block/mtip32xx/mtip32xx.c
+++ b/drivers/block/mtip32xx/mtip32xx.c
@@ -173,7 +173,13 @@ static struct mtip_cmd *mtip_get_int_command(struct driver_data *dd)
{
struct request *rq;
+ if (mtip_check_surprise_removal(dd->pdev))
+ return NULL;
+
rq = blk_mq_alloc_request(dd->queue, 0, __GFP_RECLAIM, true);
+ if (IS_ERR(rq))
+ return NULL;
+
return blk_mq_rq_to_pdu(rq);
}
@@ -233,15 +239,9 @@ static void mtip_async_complete(struct mtip_port *port,
"Command tag %d failed due to TFE\n", tag);
}
- /* Unmap the DMA scatter list entries */
- dma_unmap_sg(&dd->pdev->dev, cmd->sg, cmd->scatter_ents, cmd->direction);
-
rq = mtip_rq_from_tag(dd, tag);
- if (unlikely(cmd->unaligned))
- up(&port->cmd_slot_unal);
-
- blk_mq_end_request(rq, status ? -EIO : 0);
+ blk_mq_complete_request(rq, status);
}
/*
@@ -581,6 +581,8 @@ static void mtip_completion(struct mtip_port *port,
dev_warn(&port->dd->pdev->dev,
"Internal command %d completed with TFE\n", tag);
+ command->comp_func = NULL;
+ command->comp_data = NULL;
complete(waiting);
}
@@ -618,8 +620,6 @@ static void mtip_handle_tfe(struct driver_data *dd)
port = dd->port;
- set_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags);
-
if (test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags)) {
cmd = mtip_cmd_from_tag(dd, MTIP_TAG_INTERNAL);
dbg_printk(MTIP_DRV_NAME " TFE for the internal command\n");
@@ -628,7 +628,7 @@ static void mtip_handle_tfe(struct driver_data *dd)
cmd->comp_func(port, MTIP_TAG_INTERNAL,
cmd, PORT_IRQ_TF_ERR);
}
- goto handle_tfe_exit;
+ return;
}
/* clear the tag accumulator */
@@ -701,7 +701,7 @@ static void mtip_handle_tfe(struct driver_data *dd)
fail_reason = "thermal shutdown";
}
if (buf[288] == 0xBF) {
- set_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag);
+ set_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag);
dev_info(&dd->pdev->dev,
"Drive indicates rebuild has failed. Secure erase required.\n");
fail_all_ncq_cmds = 1;
@@ -771,11 +771,6 @@ static void mtip_handle_tfe(struct driver_data *dd)
}
}
print_tags(dd, "reissued (TFE)", tagaccum, cmd_cnt);
-
-handle_tfe_exit:
- /* clear eh_active */
- clear_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags);
- wake_up_interruptible(&port->svc_wait);
}
/*
@@ -1007,6 +1002,7 @@ static bool mtip_pause_ncq(struct mtip_port *port,
(fis->features == 0x27 || fis->features == 0x72 ||
fis->features == 0x62 || fis->features == 0x26))) {
clear_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag);
+ clear_bit(MTIP_DDF_REBUILD_FAILED_BIT, &port->dd->dd_flag);
/* Com reset after secure erase or lowlevel format */
mtip_restart_port(port);
clear_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags);
@@ -1021,12 +1017,14 @@ static bool mtip_pause_ncq(struct mtip_port *port,
*
* @port Pointer to port data structure
* @timeout Max duration to wait (ms)
+ * @atomic gfp_t flag to indicate blockable context or not
*
* return value
* 0 Success
* -EBUSY Commands still active
*/
-static int mtip_quiesce_io(struct mtip_port *port, unsigned long timeout)
+static int mtip_quiesce_io(struct mtip_port *port, unsigned long timeout,
+ gfp_t atomic)
{
unsigned long to;
unsigned int n;
@@ -1037,16 +1035,21 @@ static int mtip_quiesce_io(struct mtip_port *port, unsigned long timeout)
to = jiffies + msecs_to_jiffies(timeout);
do {
if (test_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags) &&
- test_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags)) {
+ test_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags) &&
+ atomic == GFP_KERNEL) {
msleep(20);
continue; /* svc thd is actively issuing commands */
}
- msleep(100);
+ if (atomic == GFP_KERNEL)
+ msleep(100);
+ else {
+ cpu_relax();
+ udelay(100);
+ }
+
if (mtip_check_surprise_removal(port->dd->pdev))
goto err_fault;
- if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &port->dd->dd_flag))
- goto err_fault;
/*
* Ignore s_active bit 0 of array element 0.
@@ -1099,6 +1102,7 @@ static int mtip_exec_internal_command(struct mtip_port *port,
struct mtip_cmd *int_cmd;
struct driver_data *dd = port->dd;
int rv = 0;
+ unsigned long start;
/* Make sure the buffer is 8 byte aligned. This is asic specific. */
if (buffer & 0x00000007) {
@@ -1107,6 +1111,10 @@ static int mtip_exec_internal_command(struct mtip_port *port,
}
int_cmd = mtip_get_int_command(dd);
+ if (!int_cmd) {
+ dbg_printk(MTIP_DRV_NAME "Unable to allocate tag for PIO cmd\n");
+ return -EFAULT;
+ }
set_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags);
@@ -1119,7 +1127,7 @@ static int mtip_exec_internal_command(struct mtip_port *port,
if (fis->command != ATA_CMD_STANDBYNOW1) {
/* wait for io to complete if non atomic */
if (mtip_quiesce_io(port,
- MTIP_QUIESCE_IO_TIMEOUT_MS) < 0) {
+ MTIP_QUIESCE_IO_TIMEOUT_MS, atomic) < 0) {
dev_warn(&dd->pdev->dev,
"Failed to quiesce IO\n");
mtip_put_int_command(dd, int_cmd);
@@ -1162,6 +1170,8 @@ static int mtip_exec_internal_command(struct mtip_port *port,
/* Populate the command header */
int_cmd->command_header->byte_count = 0;
+ start = jiffies;
+
/* Issue the command to the hardware */
mtip_issue_non_ncq_command(port, MTIP_TAG_INTERNAL);
@@ -1170,10 +1180,12 @@ static int mtip_exec_internal_command(struct mtip_port *port,
if ((rv = wait_for_completion_interruptible_timeout(
&wait,
msecs_to_jiffies(timeout))) <= 0) {
+
if (rv == -ERESTARTSYS) { /* interrupted */
dev_err(&dd->pdev->dev,
- "Internal command [%02X] was interrupted after %lu ms\n",
- fis->command, timeout);
+ "Internal command [%02X] was interrupted after %u ms\n",
+ fis->command,
+ jiffies_to_msecs(jiffies - start));
rv = -EINTR;
goto exec_ic_exit;
} else if (rv == 0) /* timeout */
@@ -2897,6 +2909,42 @@ static int mtip_ftl_rebuild_poll(struct driver_data *dd)
return -EFAULT;
}
+static void mtip_softirq_done_fn(struct request *rq)
+{
+ struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq);
+ struct driver_data *dd = rq->q->queuedata;
+
+ /* Unmap the DMA scatter list entries */
+ dma_unmap_sg(&dd->pdev->dev, cmd->sg, cmd->scatter_ents,
+ cmd->direction);
+
+ if (unlikely(cmd->unaligned))
+ up(&dd->port->cmd_slot_unal);
+
+ blk_mq_end_request(rq, rq->errors);
+}
+
+static void mtip_abort_cmd(struct request *req, void *data,
+ bool reserved)
+{
+ struct driver_data *dd = data;
+
+ dbg_printk(MTIP_DRV_NAME " Aborting request, tag = %d\n", req->tag);
+
+ clear_bit(req->tag, dd->port->cmds_to_issue);
+ req->errors = -EIO;
+ mtip_softirq_done_fn(req);
+}
+
+static void mtip_queue_cmd(struct request *req, void *data,
+ bool reserved)
+{
+ struct driver_data *dd = data;
+
+ set_bit(req->tag, dd->port->cmds_to_issue);
+ blk_abort_request(req);
+}
+
/*
* service thread to issue queued commands
*
@@ -2909,7 +2957,7 @@ static int mtip_ftl_rebuild_poll(struct driver_data *dd)
static int mtip_service_thread(void *data)
{
struct driver_data *dd = (struct driver_data *)data;
- unsigned long slot, slot_start, slot_wrap;
+ unsigned long slot, slot_start, slot_wrap, to;
unsigned int num_cmd_slots = dd->slot_groups * 32;
struct mtip_port *port = dd->port;
@@ -2924,9 +2972,7 @@ static int mtip_service_thread(void *data)
* is in progress nor error handling is active
*/
wait_event_interruptible(port->svc_wait, (port->flags) &&
- !(port->flags & MTIP_PF_PAUSE_IO));
-
- set_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags);
+ (port->flags & MTIP_PF_SVC_THD_WORK));
if (kthread_should_stop() ||
test_bit(MTIP_PF_SVC_THD_STOP_BIT, &port->flags))
@@ -2936,6 +2982,8 @@ static int mtip_service_thread(void *data)
&dd->dd_flag)))
goto st_out;
+ set_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags);
+
restart_eh:
/* Demux bits: start with error handling */
if (test_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags)) {
@@ -2946,6 +2994,32 @@ restart_eh:
if (test_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags))
goto restart_eh;
+ if (test_bit(MTIP_PF_TO_ACTIVE_BIT, &port->flags)) {
+ to = jiffies + msecs_to_jiffies(5000);
+
+ do {
+ mdelay(100);
+ } while (atomic_read(&dd->irq_workers_active) != 0 &&
+ time_before(jiffies, to));
+
+ if (atomic_read(&dd->irq_workers_active) != 0)
+ dev_warn(&dd->pdev->dev,
+ "Completion workers still active!");
+
+ spin_lock(dd->queue->queue_lock);
+ blk_mq_all_tag_busy_iter(*dd->tags.tags,
+ mtip_queue_cmd, dd);
+ spin_unlock(dd->queue->queue_lock);
+
+ set_bit(MTIP_PF_ISSUE_CMDS_BIT, &dd->port->flags);
+
+ if (mtip_device_reset(dd))
+ blk_mq_all_tag_busy_iter(*dd->tags.tags,
+ mtip_abort_cmd, dd);
+
+ clear_bit(MTIP_PF_TO_ACTIVE_BIT, &dd->port->flags);
+ }
+
if (test_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags)) {
slot = 1;
/* used to restrict the loop to one iteration */
@@ -2978,10 +3052,8 @@ restart_eh:
}
if (test_bit(MTIP_PF_REBUILD_BIT, &port->flags)) {
- if (mtip_ftl_rebuild_poll(dd) < 0)
- set_bit(MTIP_DDF_REBUILD_FAILED_BIT,
- &dd->dd_flag);
- clear_bit(MTIP_PF_REBUILD_BIT, &port->flags);
+ if (mtip_ftl_rebuild_poll(dd) == 0)
+ clear_bit(MTIP_PF_REBUILD_BIT, &port->flags);
}
}
@@ -3096,7 +3168,7 @@ static int mtip_hw_get_identify(struct driver_data *dd)
if (buf[288] == 0xBF) {
dev_info(&dd->pdev->dev,
"Drive indicates rebuild has failed.\n");
- /* TODO */
+ set_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag);
}
}
@@ -3270,20 +3342,25 @@ out1:
return rv;
}
-static void mtip_standby_drive(struct driver_data *dd)
+static int mtip_standby_drive(struct driver_data *dd)
{
- if (dd->sr)
- return;
+ int rv = 0;
+ if (dd->sr || !dd->port)
+ return -ENODEV;
/*
* Send standby immediate (E0h) to the drive so that it
* saves its state.
*/
if (!test_bit(MTIP_PF_REBUILD_BIT, &dd->port->flags) &&
- !test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag))
- if (mtip_standby_immediate(dd->port))
+ !test_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag) &&
+ !test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag)) {
+ rv = mtip_standby_immediate(dd->port);
+ if (rv)
dev_warn(&dd->pdev->dev,
"STANDBY IMMEDIATE failed\n");
+ }
+ return rv;
}
/*
@@ -3296,10 +3373,6 @@ static void mtip_standby_drive(struct driver_data *dd)
*/
static int mtip_hw_exit(struct driver_data *dd)
{
- /*
- * Send standby immediate (E0h) to the drive so that it
- * saves its state.
- */
if (!dd->sr) {
/* de-initialize the port. */
mtip_deinit_port(dd->port);
@@ -3341,8 +3414,7 @@ static int mtip_hw_shutdown(struct driver_data *dd)
* Send standby immediate (E0h) to the drive so that it
* saves its state.
*/
- if (!dd->sr && dd->port)
- mtip_standby_immediate(dd->port);
+ mtip_standby_drive(dd);
return 0;
}
@@ -3365,7 +3437,7 @@ static int mtip_hw_suspend(struct driver_data *dd)
* Send standby immediate (E0h) to the drive
* so that it saves its state.
*/
- if (mtip_standby_immediate(dd->port) != 0) {
+ if (mtip_standby_drive(dd) != 0) {
dev_err(&dd->pdev->dev,
"Failed standby-immediate command\n");
return -EFAULT;
@@ -3603,6 +3675,28 @@ static int mtip_block_getgeo(struct block_device *dev,
return 0;
}
+static int mtip_block_open(struct block_device *dev, fmode_t mode)
+{
+ struct driver_data *dd;
+
+ if (dev && dev->bd_disk) {
+ dd = (struct driver_data *) dev->bd_disk->private_data;
+
+ if (dd) {
+ if (test_bit(MTIP_DDF_REMOVAL_BIT,
+ &dd->dd_flag)) {
+ return -ENODEV;
+ }
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+
+void mtip_block_release(struct gendisk *disk, fmode_t mode)
+{
+}
+
/*
* Block device operation function.
*
@@ -3610,6 +3704,8 @@ static int mtip_block_getgeo(struct block_device *dev,
* layer.
*/
static const struct block_device_operations mtip_block_ops = {
+ .open = mtip_block_open,
+ .release = mtip_block_release,
.ioctl = mtip_block_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = mtip_block_compat_ioctl,
@@ -3671,10 +3767,9 @@ static int mtip_submit_request(struct blk_mq_hw_ctx *hctx, struct request *rq)
rq_data_dir(rq))) {
return -ENODATA;
}
- if (unlikely(test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag)))
+ if (unlikely(test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag) ||
+ test_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag)))
return -ENODATA;
- if (test_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag))
- return -ENXIO;
}
if (rq->cmd_flags & REQ_DISCARD) {
@@ -3786,11 +3881,33 @@ static int mtip_init_cmd(void *data, struct request *rq, unsigned int hctx_idx,
return 0;
}
+static enum blk_eh_timer_return mtip_cmd_timeout(struct request *req,
+ bool reserved)
+{
+ struct driver_data *dd = req->q->queuedata;
+ int ret = BLK_EH_RESET_TIMER;
+
+ if (reserved)
+ goto exit_handler;
+
+ if (test_bit(req->tag, dd->port->cmds_to_issue))
+ goto exit_handler;
+
+ if (test_and_set_bit(MTIP_PF_TO_ACTIVE_BIT, &dd->port->flags))
+ goto exit_handler;
+
+ wake_up_interruptible(&dd->port->svc_wait);
+exit_handler:
+ return ret;
+}
+
static struct blk_mq_ops mtip_mq_ops = {
.queue_rq = mtip_queue_rq,
.map_queue = blk_mq_map_queue,
.init_request = mtip_init_cmd,
.exit_request = mtip_free_cmd,
+ .complete = mtip_softirq_done_fn,
+ .timeout = mtip_cmd_timeout,
};
/*
@@ -3857,7 +3974,6 @@ static int mtip_block_initialize(struct driver_data *dd)
mtip_hw_debugfs_init(dd);
-skip_create_disk:
memset(&dd->tags, 0, sizeof(dd->tags));
dd->tags.ops = &mtip_mq_ops;
dd->tags.nr_hw_queues = 1;
@@ -3867,12 +3983,13 @@ skip_create_disk:
dd->tags.numa_node = dd->numa_node;
dd->tags.flags = BLK_MQ_F_SHOULD_MERGE;
dd->tags.driver_data = dd;
+ dd->tags.timeout = MTIP_NCQ_CMD_TIMEOUT_MS;
rv = blk_mq_alloc_tag_set(&dd->tags);
if (rv) {
dev_err(&dd->pdev->dev,
"Unable to allocate request queue\n");
- goto block_queue_alloc_init_error;
+ goto block_queue_alloc_tag_error;
}
/* Allocate the request queue. */
@@ -3887,6 +4004,7 @@ skip_create_disk:
dd->disk->queue = dd->queue;
dd->queue->queuedata = dd;
+skip_create_disk:
/* Initialize the protocol layer. */
wait_for_rebuild = mtip_hw_get_identify(dd);
if (wait_for_rebuild < 0) {
@@ -3983,8 +4101,9 @@ kthread_run_error:
read_capacity_error:
init_hw_cmds_error:
blk_cleanup_queue(dd->queue);
- blk_mq_free_tag_set(&dd->tags);
block_queue_alloc_init_error:
+ blk_mq_free_tag_set(&dd->tags);
+block_queue_alloc_tag_error:
mtip_hw_debugfs_exit(dd);
disk_index_error:
spin_lock(&rssd_index_lock);
@@ -4001,6 +4120,22 @@ protocol_init_error:
return rv;
}
+static void mtip_no_dev_cleanup(struct request *rq, void *data, bool reserv)
+{
+ struct driver_data *dd = (struct driver_data *)data;
+ struct mtip_cmd *cmd;
+
+ if (likely(!reserv))
+ blk_mq_complete_request(rq, -ENODEV);
+ else if (test_bit(MTIP_PF_IC_ACTIVE_BIT, &dd->port->flags)) {
+
+ cmd = mtip_cmd_from_tag(dd, MTIP_TAG_INTERNAL);
+ if (cmd->comp_func)
+ cmd->comp_func(dd->port, MTIP_TAG_INTERNAL,
+ cmd, -ENODEV);
+ }
+}
+
/*
* Block layer deinitialization function.
*
@@ -4032,12 +4167,23 @@ static int mtip_block_remove(struct driver_data *dd)
}
}
- if (!dd->sr)
- mtip_standby_drive(dd);
+ if (!dd->sr) {
+ /*
+ * Explicitly wait here for IOs to quiesce,
+ * as mtip_standby_drive usually won't wait for IOs.
+ */
+ if (!mtip_quiesce_io(dd->port, MTIP_QUIESCE_IO_TIMEOUT_MS,
+ GFP_KERNEL))
+ mtip_standby_drive(dd);
+ }
else
dev_info(&dd->pdev->dev, "device %s surprise removal\n",
dd->disk->disk_name);
+ blk_mq_freeze_queue_start(dd->queue);
+ blk_mq_stop_hw_queues(dd->queue);
+ blk_mq_all_tag_busy_iter(dd->tags.tags[0], mtip_no_dev_cleanup, dd);
+
/*
* Delete our gendisk structure. This also removes the device
* from /dev
@@ -4047,7 +4193,8 @@ static int mtip_block_remove(struct driver_data *dd)
dd->bdev = NULL;
}
if (dd->disk) {
- del_gendisk(dd->disk);
+ if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag))
+ del_gendisk(dd->disk);
if (dd->disk->queue) {
blk_cleanup_queue(dd->queue);
blk_mq_free_tag_set(&dd->tags);
@@ -4088,7 +4235,8 @@ static int mtip_block_shutdown(struct driver_data *dd)
dev_info(&dd->pdev->dev,
"Shutting down %s ...\n", dd->disk->disk_name);
- del_gendisk(dd->disk);
+ if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag))
+ del_gendisk(dd->disk);
if (dd->disk->queue) {
blk_cleanup_queue(dd->queue);
blk_mq_free_tag_set(&dd->tags);
@@ -4433,7 +4581,7 @@ static void mtip_pci_remove(struct pci_dev *pdev)
struct driver_data *dd = pci_get_drvdata(pdev);
unsigned long flags, to;
- set_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag);
+ set_bit(MTIP_DDF_REMOVAL_BIT, &dd->dd_flag);
spin_lock_irqsave(&dev_lock, flags);
list_del_init(&dd->online_list);
@@ -4450,12 +4598,17 @@ static void mtip_pci_remove(struct pci_dev *pdev)
} while (atomic_read(&dd->irq_workers_active) != 0 &&
time_before(jiffies, to));
+ if (!dd->sr)
+ fsync_bdev(dd->bdev);
+
if (atomic_read(&dd->irq_workers_active) != 0) {
dev_warn(&dd->pdev->dev,
"Completion workers still active!\n");
}
- blk_mq_stop_hw_queues(dd->queue);
+ blk_set_queue_dying(dd->queue);
+ set_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag);
+
/* Clean up the block layer. */
mtip_block_remove(dd);
diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h
index 3274784008eb..7617888f7944 100644
--- a/drivers/block/mtip32xx/mtip32xx.h
+++ b/drivers/block/mtip32xx/mtip32xx.h
@@ -134,16 +134,24 @@ enum {
MTIP_PF_EH_ACTIVE_BIT = 1, /* error handling */
MTIP_PF_SE_ACTIVE_BIT = 2, /* secure erase */
MTIP_PF_DM_ACTIVE_BIT = 3, /* download microcde */
+ MTIP_PF_TO_ACTIVE_BIT = 9, /* timeout handling */
MTIP_PF_PAUSE_IO = ((1 << MTIP_PF_IC_ACTIVE_BIT) |
(1 << MTIP_PF_EH_ACTIVE_BIT) |
(1 << MTIP_PF_SE_ACTIVE_BIT) |
- (1 << MTIP_PF_DM_ACTIVE_BIT)),
+ (1 << MTIP_PF_DM_ACTIVE_BIT) |
+ (1 << MTIP_PF_TO_ACTIVE_BIT)),
MTIP_PF_SVC_THD_ACTIVE_BIT = 4,
MTIP_PF_ISSUE_CMDS_BIT = 5,
MTIP_PF_REBUILD_BIT = 6,
MTIP_PF_SVC_THD_STOP_BIT = 8,
+ MTIP_PF_SVC_THD_WORK = ((1 << MTIP_PF_EH_ACTIVE_BIT) |
+ (1 << MTIP_PF_ISSUE_CMDS_BIT) |
+ (1 << MTIP_PF_REBUILD_BIT) |
+ (1 << MTIP_PF_SVC_THD_STOP_BIT) |
+ (1 << MTIP_PF_TO_ACTIVE_BIT)),
+
/* below are bit numbers in 'dd_flag' defined in driver_data */
MTIP_DDF_SEC_LOCK_BIT = 0,
MTIP_DDF_REMOVE_PENDING_BIT = 1,
@@ -153,6 +161,7 @@ enum {
MTIP_DDF_RESUME_BIT = 6,
MTIP_DDF_INIT_DONE_BIT = 7,
MTIP_DDF_REBUILD_FAILED_BIT = 8,
+ MTIP_DDF_REMOVAL_BIT = 9,
MTIP_DDF_STOP_IO = ((1 << MTIP_DDF_REMOVE_PENDING_BIT) |
(1 << MTIP_DDF_SEC_LOCK_BIT) |
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 81ea69fee7ca..fbdddd6f94b8 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -1955,7 +1955,7 @@ static struct ceph_osd_request *rbd_osd_req_create(
osdc = &rbd_dev->rbd_client->client->osdc;
osd_req = ceph_osdc_alloc_request(osdc, snapc, num_ops, false,
- GFP_ATOMIC);
+ GFP_NOIO);
if (!osd_req)
return NULL; /* ENOMEM */
@@ -2004,7 +2004,7 @@ rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request)
rbd_dev = img_request->rbd_dev;
osdc = &rbd_dev->rbd_client->client->osdc;
osd_req = ceph_osdc_alloc_request(osdc, snapc, num_osd_ops,
- false, GFP_ATOMIC);
+ false, GFP_NOIO);
if (!osd_req)
return NULL; /* ENOMEM */
@@ -2506,7 +2506,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
bio_chain_clone_range(&bio_list,
&bio_offset,
clone_size,
- GFP_ATOMIC);
+ GFP_NOIO);
if (!obj_request->bio_list)
goto out_unwind;
} else if (type == OBJ_REQUEST_PAGES) {
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index fa893c3ec408..0beaa52df66b 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -82,6 +82,7 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x0489, 0xe05f) },
{ USB_DEVICE(0x0489, 0xe076) },
{ USB_DEVICE(0x0489, 0xe078) },
+ { USB_DEVICE(0x0489, 0xe095) },
{ USB_DEVICE(0x04c5, 0x1330) },
{ USB_DEVICE(0x04CA, 0x3004) },
{ USB_DEVICE(0x04CA, 0x3005) },
@@ -92,6 +93,7 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x04CA, 0x300d) },
{ USB_DEVICE(0x04CA, 0x300f) },
{ USB_DEVICE(0x04CA, 0x3010) },
+ { USB_DEVICE(0x04CA, 0x3014) },
{ USB_DEVICE(0x0930, 0x0219) },
{ USB_DEVICE(0x0930, 0x021c) },
{ USB_DEVICE(0x0930, 0x0220) },
@@ -113,10 +115,12 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x13d3, 0x3362) },
{ USB_DEVICE(0x13d3, 0x3375) },
{ USB_DEVICE(0x13d3, 0x3393) },
+ { USB_DEVICE(0x13d3, 0x3395) },
{ USB_DEVICE(0x13d3, 0x3402) },
{ USB_DEVICE(0x13d3, 0x3408) },
{ USB_DEVICE(0x13d3, 0x3423) },
{ USB_DEVICE(0x13d3, 0x3432) },
+ { USB_DEVICE(0x13d3, 0x3472) },
{ USB_DEVICE(0x13d3, 0x3474) },
/* Atheros AR5BBU12 with sflash firmware */
@@ -144,6 +148,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe076), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe095), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
@@ -154,6 +159,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x04ca, 0x300d), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
@@ -175,10 +181,12 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3395), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3472), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU22 with sflash firmware */
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 968897108c76..79107597a594 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -196,6 +196,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe076), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe095), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
@@ -206,6 +207,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x04ca, 0x300d), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
@@ -227,10 +229,12 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3395), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3472), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU12 with sflash firmware */
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 45cc39aabeee..252142524ff2 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -136,11 +136,13 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev,
chip->cdev.owner = chip->pdev->driver->owner;
chip->cdev.kobj.parent = &chip->dev.kobj;
+ devm_add_action(dev, (void (*)(void *)) put_device, &chip->dev);
+
return chip;
}
EXPORT_SYMBOL_GPL(tpmm_chip_alloc);
-static int tpm_dev_add_device(struct tpm_chip *chip)
+static int tpm_add_char_device(struct tpm_chip *chip)
{
int rc;
@@ -151,7 +153,6 @@ static int tpm_dev_add_device(struct tpm_chip *chip)
chip->devname, MAJOR(chip->dev.devt),
MINOR(chip->dev.devt), rc);
- device_unregister(&chip->dev);
return rc;
}
@@ -162,16 +163,17 @@ static int tpm_dev_add_device(struct tpm_chip *chip)
chip->devname, MAJOR(chip->dev.devt),
MINOR(chip->dev.devt), rc);
+ cdev_del(&chip->cdev);
return rc;
}
return rc;
}
-static void tpm_dev_del_device(struct tpm_chip *chip)
+static void tpm_del_char_device(struct tpm_chip *chip)
{
cdev_del(&chip->cdev);
- device_unregister(&chip->dev);
+ device_del(&chip->dev);
}
static int tpm1_chip_register(struct tpm_chip *chip)
@@ -222,7 +224,7 @@ int tpm_chip_register(struct tpm_chip *chip)
tpm_add_ppi(chip);
- rc = tpm_dev_add_device(chip);
+ rc = tpm_add_char_device(chip);
if (rc)
goto out_err;
@@ -274,6 +276,6 @@ void tpm_chip_unregister(struct tpm_chip *chip)
sysfs_remove_link(&chip->pdev->kobj, "ppi");
tpm1_chip_unregister(chip);
- tpm_dev_del_device(chip);
+ tpm_del_char_device(chip);
}
EXPORT_SYMBOL_GPL(tpm_chip_unregister);
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index 4bb9727c1047..61e64293b765 100644
--- a/drivers/char/tpm/tpm_crb.c
+++ b/drivers/char/tpm/tpm_crb.c
@@ -310,11 +310,11 @@ static int crb_acpi_remove(struct acpi_device *device)
struct device *dev = &device->dev;
struct tpm_chip *chip = dev_get_drvdata(dev);
- tpm_chip_unregister(chip);
-
if (chip->flags & TPM_CHIP_FLAG_TPM2)
tpm2_shutdown(chip, TPM2_SU_CLEAR);
+ tpm_chip_unregister(chip);
+
return 0;
}
diff --git a/drivers/char/tpm/tpm_eventlog.c b/drivers/char/tpm/tpm_eventlog.c
index bd72fb04225e..4e6940acf639 100644
--- a/drivers/char/tpm/tpm_eventlog.c
+++ b/drivers/char/tpm/tpm_eventlog.c
@@ -232,7 +232,7 @@ static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v)
{
struct tcpa_event *event = v;
struct tcpa_event temp_event;
- char *tempPtr;
+ char *temp_ptr;
int i;
memcpy(&temp_event, event, sizeof(struct tcpa_event));
@@ -242,10 +242,16 @@ static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v)
temp_event.event_type = do_endian_conversion(event->event_type);
temp_event.event_size = do_endian_conversion(event->event_size);
- tempPtr = (char *)&temp_event;
+ temp_ptr = (char *) &temp_event;
- for (i = 0; i < sizeof(struct tcpa_event) + temp_event.event_size; i++)
- seq_putc(m, tempPtr[i]);
+ for (i = 0; i < (sizeof(struct tcpa_event) - 1) ; i++)
+ seq_putc(m, temp_ptr[i]);
+
+ temp_ptr = (char *) v;
+
+ for (i = (sizeof(struct tcpa_event) - 1);
+ i < (sizeof(struct tcpa_event) + temp_event.event_size); i++)
+ seq_putc(m, temp_ptr[i]);
return 0;
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index 39bf5820297e..4f9830c1b121 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -1097,13 +1097,15 @@ static int bcm2835_pll_divider_set_rate(struct clk_hw *hw,
struct bcm2835_pll_divider *divider = bcm2835_pll_divider_from_hw(hw);
struct bcm2835_cprman *cprman = divider->cprman;
const struct bcm2835_pll_divider_data *data = divider->data;
- u32 cm;
- int ret;
+ u32 cm, div, max_div = 1 << A2W_PLL_DIV_BITS;
- ret = clk_divider_ops.set_rate(hw, rate, parent_rate);
- if (ret)
- return ret;
+ div = DIV_ROUND_UP_ULL(parent_rate, rate);
+
+ div = min(div, max_div);
+ if (div == max_div)
+ div = 0;
+ cprman_write(cprman, data->a2w_reg, div);
cm = cprman_read(cprman, data->cm_reg);
cprman_write(cprman, data->cm_reg, cm | data->load_mask);
cprman_write(cprman, data->cm_reg, cm & ~data->load_mask);
diff --git a/drivers/clk/msm/clock-gpu-cobalt.c b/drivers/clk/msm/clock-gpu-cobalt.c
index ce3e7916e658..7230c7a2bc04 100644
--- a/drivers/clk/msm/clock-gpu-cobalt.c
+++ b/drivers/clk/msm/clock-gpu-cobalt.c
@@ -109,7 +109,7 @@ static struct alpha_pll_clk gpu_pll0_pll = {
.parent = &gpucc_xo.c,
.dbg_name = "gpu_pll0_pll",
.ops = &clk_ops_fabia_alpha_pll,
- VDD_GPU_PLL_FMAX_MAP1(NOMINAL, 1300000500),
+ VDD_GPU_PLL_FMAX_MAP1(MIN, 1300000500),
CLK_INIT(gpu_pll0_pll.c),
},
};
@@ -168,7 +168,7 @@ static struct alpha_pll_clk gpu_pll1_pll = {
.parent = &gpucc_xo.c,
.dbg_name = "gpu_pll1_pll",
.ops = &clk_ops_fabia_alpha_pll,
- VDD_GPU_PLL_FMAX_MAP1(NOMINAL, 1300000500),
+ VDD_GPU_PLL_FMAX_MAP1(MIN, 1300000500),
CLK_INIT(gpu_pll1_pll.c),
},
};
@@ -670,15 +670,15 @@ static struct clk_lookup msm_clocks_gfxcc_cobalt[] = {
static void msm_gfxcc_hamster_fixup(void)
{
- gpu_pll0_pll.c.fmax[VDD_DIG_NOMINAL] = 1420000500;
- gpu_pll1_pll.c.fmax[VDD_DIG_NOMINAL] = 1420000500;
+ gpu_pll0_pll.c.fmax[VDD_DIG_MIN] = 1420000500;
+ gpu_pll1_pll.c.fmax[VDD_DIG_MIN] = 1420000500;
gfx3d_clk_src.freq_tbl = ftbl_gfx3d_clk_src_vq;
}
static void msm_gfxcc_cobalt_v2_fixup(void)
{
- gpu_pll0_pll.c.fmax[VDD_DIG_NOMINAL] = 1420000500;
- gpu_pll1_pll.c.fmax[VDD_DIG_NOMINAL] = 1420000500;
+ gpu_pll0_pll.c.fmax[VDD_DIG_MIN] = 1420000500;
+ gpu_pll1_pll.c.fmax[VDD_DIG_MIN] = 1420000500;
gfx3d_clk_src.freq_tbl = ftbl_gfx3d_clk_src_v2;
}
diff --git a/drivers/clk/msm/clock-mmss-cobalt.c b/drivers/clk/msm/clock-mmss-cobalt.c
index 53c657ef3a25..288abb133743 100644
--- a/drivers/clk/msm/clock-mmss-cobalt.c
+++ b/drivers/clk/msm/clock-mmss-cobalt.c
@@ -1124,6 +1124,7 @@ static struct rcg_clk dp_pixel_clk_src = {
.dbg_name = "dp_pixel_clk_src",
.parent = &ext_dp_phy_pll_vco.c,
.ops = &clk_ops_rcg_dp,
+ .flags = CLKFLAG_NO_RATE_CACHE,
VDD_DIG_FMAX_MAP3(LOWER, 148380000, LOW, 296740000,
NOMINAL, 593470000),
CLK_INIT(dp_pixel_clk_src.c),
@@ -2033,6 +2034,7 @@ static struct branch_clk mmss_mdss_dp_pixel_clk = {
.c = {
.dbg_name = "mmss_mdss_dp_pixel_clk",
.parent = &dp_pixel_clk_src.c,
+ .flags = CLKFLAG_NO_RATE_CACHE,
.ops = &clk_ops_branch,
CLK_INIT(mmss_mdss_dp_pixel_clk.c),
},
@@ -2801,8 +2803,10 @@ int msm_mmsscc_cobalt_probe(struct platform_device *pdev)
ext_dp_phy_pll_link.dev = &pdev->dev;
ext_dp_phy_pll_link.clk_id = "dp_link_src";
+ ext_dp_phy_pll_link.c.flags = CLKFLAG_NO_RATE_CACHE;
ext_dp_phy_pll_vco.dev = &pdev->dev;
ext_dp_phy_pll_vco.clk_id = "dp_vco_div";
+ ext_dp_phy_pll_vco.c.flags = CLKFLAG_NO_RATE_CACHE;
is_vq = of_device_is_compatible(pdev->dev.of_node,
"qcom,mmsscc-hamster");
diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
index abb47608713b..fe728f8dcbe4 100644
--- a/drivers/clk/rockchip/clk-rk3188.c
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -718,6 +718,7 @@ static const char *const rk3188_critical_clocks[] __initconst = {
"hclk_peri",
"pclk_cpu",
"pclk_peri",
+ "hclk_cpubus"
};
static void __init rk3188_common_clk_init(struct device_node *np)
diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
index 7e6b783e6eee..1b148694b633 100644
--- a/drivers/clk/rockchip/clk-rk3368.c
+++ b/drivers/clk/rockchip/clk-rk3368.c
@@ -165,7 +165,7 @@ static const struct rockchip_cpuclk_reg_data rk3368_cpuclkb_data = {
.core_reg = RK3368_CLKSEL_CON(0),
.div_core_shift = 0,
.div_core_mask = 0x1f,
- .mux_core_shift = 15,
+ .mux_core_shift = 7,
};
static const struct rockchip_cpuclk_reg_data rk3368_cpuclkl_data = {
@@ -218,29 +218,29 @@ static const struct rockchip_cpuclk_reg_data rk3368_cpuclkl_data = {
}
static struct rockchip_cpuclk_rate_table rk3368_cpuclkb_rates[] __initdata = {
- RK3368_CPUCLKB_RATE(1512000000, 2, 6, 6),
- RK3368_CPUCLKB_RATE(1488000000, 2, 5, 5),
- RK3368_CPUCLKB_RATE(1416000000, 2, 5, 5),
- RK3368_CPUCLKB_RATE(1200000000, 2, 4, 4),
- RK3368_CPUCLKB_RATE(1008000000, 2, 4, 4),
- RK3368_CPUCLKB_RATE( 816000000, 2, 3, 3),
- RK3368_CPUCLKB_RATE( 696000000, 2, 3, 3),
- RK3368_CPUCLKB_RATE( 600000000, 2, 2, 2),
- RK3368_CPUCLKB_RATE( 408000000, 2, 2, 2),
- RK3368_CPUCLKB_RATE( 312000000, 2, 2, 2),
+ RK3368_CPUCLKB_RATE(1512000000, 1, 5, 5),
+ RK3368_CPUCLKB_RATE(1488000000, 1, 4, 4),
+ RK3368_CPUCLKB_RATE(1416000000, 1, 4, 4),
+ RK3368_CPUCLKB_RATE(1200000000, 1, 3, 3),
+ RK3368_CPUCLKB_RATE(1008000000, 1, 3, 3),
+ RK3368_CPUCLKB_RATE( 816000000, 1, 2, 2),
+ RK3368_CPUCLKB_RATE( 696000000, 1, 2, 2),
+ RK3368_CPUCLKB_RATE( 600000000, 1, 1, 1),
+ RK3368_CPUCLKB_RATE( 408000000, 1, 1, 1),
+ RK3368_CPUCLKB_RATE( 312000000, 1, 1, 1),
};
static struct rockchip_cpuclk_rate_table rk3368_cpuclkl_rates[] __initdata = {
- RK3368_CPUCLKL_RATE(1512000000, 2, 7, 7),
- RK3368_CPUCLKL_RATE(1488000000, 2, 6, 6),
- RK3368_CPUCLKL_RATE(1416000000, 2, 6, 6),
- RK3368_CPUCLKL_RATE(1200000000, 2, 5, 5),
- RK3368_CPUCLKL_RATE(1008000000, 2, 5, 5),
- RK3368_CPUCLKL_RATE( 816000000, 2, 4, 4),
- RK3368_CPUCLKL_RATE( 696000000, 2, 3, 3),
- RK3368_CPUCLKL_RATE( 600000000, 2, 3, 3),
- RK3368_CPUCLKL_RATE( 408000000, 2, 2, 2),
- RK3368_CPUCLKL_RATE( 312000000, 2, 2, 2),
+ RK3368_CPUCLKL_RATE(1512000000, 1, 6, 6),
+ RK3368_CPUCLKL_RATE(1488000000, 1, 5, 5),
+ RK3368_CPUCLKL_RATE(1416000000, 1, 5, 5),
+ RK3368_CPUCLKL_RATE(1200000000, 1, 4, 4),
+ RK3368_CPUCLKL_RATE(1008000000, 1, 4, 4),
+ RK3368_CPUCLKL_RATE( 816000000, 1, 3, 3),
+ RK3368_CPUCLKL_RATE( 696000000, 1, 2, 2),
+ RK3368_CPUCLKL_RATE( 600000000, 1, 2, 2),
+ RK3368_CPUCLKL_RATE( 408000000, 1, 1, 1),
+ RK3368_CPUCLKL_RATE( 312000000, 1, 1, 1),
};
static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
@@ -384,10 +384,10 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
* Clock-Architecture Diagram 3
*/
- COMPOSITE(0, "aclk_vepu", mux_pll_src_cpll_gpll_usb_p, 0,
+ COMPOSITE(0, "aclk_vepu", mux_pll_src_cpll_gpll_npll_usb_p, 0,
RK3368_CLKSEL_CON(15), 6, 2, MFLAGS, 0, 5, DFLAGS,
RK3368_CLKGATE_CON(4), 6, GFLAGS),
- COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_usb_p, 0,
+ COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_npll_usb_p, 0,
RK3368_CLKSEL_CON(15), 14, 2, MFLAGS, 8, 5, DFLAGS,
RK3368_CLKGATE_CON(4), 7, GFLAGS),
@@ -442,7 +442,7 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
GATE(SCLK_HDMI_HDCP, "sclk_hdmi_hdcp", "xin24m", 0,
RK3368_CLKGATE_CON(4), 13, GFLAGS),
GATE(SCLK_HDMI_CEC, "sclk_hdmi_cec", "xin32k", 0,
- RK3368_CLKGATE_CON(5), 12, GFLAGS),
+ RK3368_CLKGATE_CON(4), 12, GFLAGS),
COMPOSITE_NODIV(0, "vip_src", mux_pll_src_cpll_gpll_p, 0,
RK3368_CLKSEL_CON(21), 15, 1, MFLAGS,
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
index 90d64081ddb3..f951f911786e 100644
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -31,9 +31,8 @@
struct private_data {
struct device *cpu_dev;
- struct regulator *cpu_reg;
struct thermal_cooling_device *cdev;
- unsigned int voltage_tolerance; /* in percentage */
+ const char *reg_name;
};
static struct freq_attr *cpufreq_dt_attr[] = {
@@ -44,175 +43,128 @@ static struct freq_attr *cpufreq_dt_attr[] = {
static int set_target(struct cpufreq_policy *policy, unsigned int index)
{
- struct dev_pm_opp *opp;
- struct cpufreq_frequency_table *freq_table = policy->freq_table;
- struct clk *cpu_clk = policy->clk;
struct private_data *priv = policy->driver_data;
- struct device *cpu_dev = priv->cpu_dev;
- struct regulator *cpu_reg = priv->cpu_reg;
- unsigned long volt = 0, volt_old = 0, tol = 0;
- unsigned int old_freq, new_freq;
- long freq_Hz, freq_exact;
- int ret;
-
- freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000);
- if (freq_Hz <= 0)
- freq_Hz = freq_table[index].frequency * 1000;
- freq_exact = freq_Hz;
- new_freq = freq_Hz / 1000;
- old_freq = clk_get_rate(cpu_clk) / 1000;
+ return dev_pm_opp_set_rate(priv->cpu_dev,
+ policy->freq_table[index].frequency * 1000);
+}
- if (!IS_ERR(cpu_reg)) {
- unsigned long opp_freq;
+/*
+ * An earlier version of opp-v1 bindings used to name the regulator
+ * "cpu0-supply", we still need to handle that for backwards compatibility.
+ */
+static const char *find_supply_name(struct device *dev)
+{
+ struct device_node *np;
+ struct property *pp;
+ int cpu = dev->id;
+ const char *name = NULL;
- rcu_read_lock();
- opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
- if (IS_ERR(opp)) {
- rcu_read_unlock();
- dev_err(cpu_dev, "failed to find OPP for %ld\n",
- freq_Hz);
- return PTR_ERR(opp);
- }
- volt = dev_pm_opp_get_voltage(opp);
- opp_freq = dev_pm_opp_get_freq(opp);
- rcu_read_unlock();
- tol = volt * priv->voltage_tolerance / 100;
- volt_old = regulator_get_voltage(cpu_reg);
- dev_dbg(cpu_dev, "Found OPP: %ld kHz, %ld uV\n",
- opp_freq / 1000, volt);
- }
+ np = of_node_get(dev->of_node);
- dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n",
- old_freq / 1000, (volt_old > 0) ? volt_old / 1000 : -1,
- new_freq / 1000, volt ? volt / 1000 : -1);
+ /* This must be valid for sure */
+ if (WARN_ON(!np))
+ return NULL;
- /* scaling up? scale voltage before frequency */
- if (!IS_ERR(cpu_reg) && new_freq > old_freq) {
- ret = regulator_set_voltage_tol(cpu_reg, volt, tol);
- if (ret) {
- dev_err(cpu_dev, "failed to scale voltage up: %d\n",
- ret);
- return ret;
+ /* Try "cpu0" for older DTs */
+ if (!cpu) {
+ pp = of_find_property(np, "cpu0-supply", NULL);
+ if (pp) {
+ name = "cpu0";
+ goto node_put;
}
}
- ret = clk_set_rate(cpu_clk, freq_exact);
- if (ret) {
- dev_err(cpu_dev, "failed to set clock rate: %d\n", ret);
- if (!IS_ERR(cpu_reg) && volt_old > 0)
- regulator_set_voltage_tol(cpu_reg, volt_old, tol);
- return ret;
+ pp = of_find_property(np, "cpu-supply", NULL);
+ if (pp) {
+ name = "cpu";
+ goto node_put;
}
- /* scaling down? scale voltage after frequency */
- if (!IS_ERR(cpu_reg) && new_freq < old_freq) {
- ret = regulator_set_voltage_tol(cpu_reg, volt, tol);
- if (ret) {
- dev_err(cpu_dev, "failed to scale voltage down: %d\n",
- ret);
- clk_set_rate(cpu_clk, old_freq * 1000);
- }
- }
-
- return ret;
+ dev_dbg(dev, "no regulator for cpu%d\n", cpu);
+node_put:
+ of_node_put(np);
+ return name;
}
-static int allocate_resources(int cpu, struct device **cdev,
- struct regulator **creg, struct clk **cclk)
+static int resources_available(void)
{
struct device *cpu_dev;
struct regulator *cpu_reg;
struct clk *cpu_clk;
int ret = 0;
- char *reg_cpu0 = "cpu0", *reg_cpu = "cpu", *reg;
+ const char *name;
- cpu_dev = get_cpu_device(cpu);
+ cpu_dev = get_cpu_device(0);
if (!cpu_dev) {
- pr_err("failed to get cpu%d device\n", cpu);
+ pr_err("failed to get cpu0 device\n");
return -ENODEV;
}
- /* Try "cpu0" for older DTs */
- if (!cpu)
- reg = reg_cpu0;
- else
- reg = reg_cpu;
-
-try_again:
- cpu_reg = regulator_get_optional(cpu_dev, reg);
- if (IS_ERR(cpu_reg)) {
+ cpu_clk = clk_get(cpu_dev, NULL);
+ ret = PTR_ERR_OR_ZERO(cpu_clk);
+ if (ret) {
/*
- * If cpu's regulator supply node is present, but regulator is
- * not yet registered, we should try defering probe.
+ * If cpu's clk node is present, but clock is not yet
+ * registered, we should try defering probe.
*/
- if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) {
- dev_dbg(cpu_dev, "cpu%d regulator not ready, retry\n",
- cpu);
- return -EPROBE_DEFER;
- }
-
- /* Try with "cpu-supply" */
- if (reg == reg_cpu0) {
- reg = reg_cpu;
- goto try_again;
- }
+ if (ret == -EPROBE_DEFER)
+ dev_dbg(cpu_dev, "clock not ready, retry\n");
+ else
+ dev_err(cpu_dev, "failed to get clock: %d\n", ret);
- dev_dbg(cpu_dev, "no regulator for cpu%d: %ld\n",
- cpu, PTR_ERR(cpu_reg));
+ return ret;
}
- cpu_clk = clk_get(cpu_dev, NULL);
- if (IS_ERR(cpu_clk)) {
- /* put regulator */
- if (!IS_ERR(cpu_reg))
- regulator_put(cpu_reg);
+ clk_put(cpu_clk);
- ret = PTR_ERR(cpu_clk);
+ name = find_supply_name(cpu_dev);
+ /* Platform doesn't require regulator */
+ if (!name)
+ return 0;
+ cpu_reg = regulator_get_optional(cpu_dev, name);
+ ret = PTR_ERR_OR_ZERO(cpu_reg);
+ if (ret) {
/*
- * If cpu's clk node is present, but clock is not yet
- * registered, we should try defering probe.
+ * If cpu's regulator supply node is present, but regulator is
+ * not yet registered, we should try defering probe.
*/
if (ret == -EPROBE_DEFER)
- dev_dbg(cpu_dev, "cpu%d clock not ready, retry\n", cpu);
+ dev_dbg(cpu_dev, "cpu0 regulator not ready, retry\n");
else
- dev_err(cpu_dev, "failed to get cpu%d clock: %d\n", cpu,
- ret);
- } else {
- *cdev = cpu_dev;
- *creg = cpu_reg;
- *cclk = cpu_clk;
+ dev_dbg(cpu_dev, "no regulator for cpu0: %d\n", ret);
+
+ return ret;
}
- return ret;
+ regulator_put(cpu_reg);
+ return 0;
}
static int cpufreq_init(struct cpufreq_policy *policy)
{
struct cpufreq_frequency_table *freq_table;
- struct device_node *np;
struct private_data *priv;
struct device *cpu_dev;
- struct regulator *cpu_reg;
struct clk *cpu_clk;
struct dev_pm_opp *suspend_opp;
- unsigned long min_uV = ~0, max_uV = 0;
unsigned int transition_latency;
- bool need_update = false;
+ bool opp_v1 = false;
+ const char *name;
int ret;
- ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk);
- if (ret) {
- pr_err("%s: Failed to allocate resources: %d\n", __func__, ret);
- return ret;
+ cpu_dev = get_cpu_device(policy->cpu);
+ if (!cpu_dev) {
+ pr_err("failed to get cpu%d device\n", policy->cpu);
+ return -ENODEV;
}
- np = of_node_get(cpu_dev->of_node);
- if (!np) {
- dev_err(cpu_dev, "failed to find cpu%d node\n", policy->cpu);
- ret = -ENOENT;
- goto out_put_reg_clk;
+ cpu_clk = clk_get(cpu_dev, NULL);
+ if (IS_ERR(cpu_clk)) {
+ ret = PTR_ERR(cpu_clk);
+ dev_err(cpu_dev, "%s: failed to get clk: %d\n", __func__, ret);
+ return ret;
}
/* Get OPP-sharing information from "operating-points-v2" bindings */
@@ -223,9 +175,23 @@ static int cpufreq_init(struct cpufreq_policy *policy)
* finding shared-OPPs for backward compatibility.
*/
if (ret == -ENOENT)
- need_update = true;
+ opp_v1 = true;
else
- goto out_node_put;
+ goto out_put_clk;
+ }
+
+ /*
+ * OPP layer will be taking care of regulators now, but it needs to know
+ * the name of the regulator first.
+ */
+ name = find_supply_name(cpu_dev);
+ if (name) {
+ ret = dev_pm_opp_set_regulator(cpu_dev, name);
+ if (ret) {
+ dev_err(cpu_dev, "Failed to set regulator for cpu%d: %d\n",
+ policy->cpu, ret);
+ goto out_put_clk;
+ }
}
/*
@@ -246,12 +212,12 @@ static int cpufreq_init(struct cpufreq_policy *policy)
*/
ret = dev_pm_opp_get_opp_count(cpu_dev);
if (ret <= 0) {
- pr_debug("OPP table is not ready, deferring probe\n");
+ dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
ret = -EPROBE_DEFER;
goto out_free_opp;
}
- if (need_update) {
+ if (opp_v1) {
struct cpufreq_dt_platform_data *pd = cpufreq_get_driver_data();
if (!pd || !pd->independent_clocks)
@@ -265,10 +231,6 @@ static int cpufreq_init(struct cpufreq_policy *policy)
if (ret)
dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
__func__, ret);
-
- of_property_read_u32(np, "clock-latency", &transition_latency);
- } else {
- transition_latency = dev_pm_opp_get_max_clock_latency(cpu_dev);
}
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -277,62 +239,16 @@ static int cpufreq_init(struct cpufreq_policy *policy)
goto out_free_opp;
}
- of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance);
-
- if (!transition_latency)
- transition_latency = CPUFREQ_ETERNAL;
-
- if (!IS_ERR(cpu_reg)) {
- unsigned long opp_freq = 0;
-
- /*
- * Disable any OPPs where the connected regulator isn't able to
- * provide the specified voltage and record minimum and maximum
- * voltage levels.
- */
- while (1) {
- struct dev_pm_opp *opp;
- unsigned long opp_uV, tol_uV;
-
- rcu_read_lock();
- opp = dev_pm_opp_find_freq_ceil(cpu_dev, &opp_freq);
- if (IS_ERR(opp)) {
- rcu_read_unlock();
- break;
- }
- opp_uV = dev_pm_opp_get_voltage(opp);
- rcu_read_unlock();
-
- tol_uV = opp_uV * priv->voltage_tolerance / 100;
- if (regulator_is_supported_voltage(cpu_reg,
- opp_uV - tol_uV,
- opp_uV + tol_uV)) {
- if (opp_uV < min_uV)
- min_uV = opp_uV;
- if (opp_uV > max_uV)
- max_uV = opp_uV;
- } else {
- dev_pm_opp_disable(cpu_dev, opp_freq);
- }
-
- opp_freq++;
- }
-
- ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV);
- if (ret > 0)
- transition_latency += ret * 1000;
- }
+ priv->reg_name = name;
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
if (ret) {
- pr_err("failed to init cpufreq table: %d\n", ret);
+ dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
goto out_free_priv;
}
priv->cpu_dev = cpu_dev;
- priv->cpu_reg = cpu_reg;
policy->driver_data = priv;
-
policy->clk = cpu_clk;
rcu_read_lock();
@@ -357,9 +273,11 @@ static int cpufreq_init(struct cpufreq_policy *policy)
cpufreq_dt_attr[1] = &cpufreq_freq_attr_scaling_boost_freqs;
}
- policy->cpuinfo.transition_latency = transition_latency;
+ transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev);
+ if (!transition_latency)
+ transition_latency = CPUFREQ_ETERNAL;
- of_node_put(np);
+ policy->cpuinfo.transition_latency = transition_latency;
return 0;
@@ -369,12 +287,10 @@ out_free_priv:
kfree(priv);
out_free_opp:
dev_pm_opp_of_cpumask_remove_table(policy->cpus);
-out_node_put:
- of_node_put(np);
-out_put_reg_clk:
+ if (name)
+ dev_pm_opp_put_regulator(cpu_dev);
+out_put_clk:
clk_put(cpu_clk);
- if (!IS_ERR(cpu_reg))
- regulator_put(cpu_reg);
return ret;
}
@@ -386,9 +302,10 @@ static int cpufreq_exit(struct cpufreq_policy *policy)
cpufreq_cooling_unregister(priv->cdev);
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
+ if (priv->reg_name)
+ dev_pm_opp_put_regulator(priv->cpu_dev);
+
clk_put(policy->clk);
- if (!IS_ERR(priv->cpu_reg))
- regulator_put(priv->cpu_reg);
kfree(priv);
return 0;
@@ -407,8 +324,13 @@ static void cpufreq_ready(struct cpufreq_policy *policy)
* thermal DT code takes care of matching them.
*/
if (of_find_property(np, "#cooling-cells", NULL)) {
- priv->cdev = of_cpufreq_cooling_register(np,
- policy->related_cpus);
+ u32 power_coefficient = 0;
+
+ of_property_read_u32(np, "dynamic-power-coefficient",
+ &power_coefficient);
+
+ priv->cdev = of_cpufreq_power_cooling_register(np,
+ policy->related_cpus, power_coefficient, NULL);
if (IS_ERR(priv->cdev)) {
dev_err(priv->cpu_dev,
"running cpufreq without cooling device: %ld\n",
@@ -436,9 +358,6 @@ static struct cpufreq_driver dt_cpufreq_driver = {
static int dt_cpufreq_probe(struct platform_device *pdev)
{
- struct device *cpu_dev;
- struct regulator *cpu_reg;
- struct clk *cpu_clk;
int ret;
/*
@@ -448,19 +367,15 @@ static int dt_cpufreq_probe(struct platform_device *pdev)
*
* FIXME: Is checking this only for CPU0 sufficient ?
*/
- ret = allocate_resources(0, &cpu_dev, &cpu_reg, &cpu_clk);
+ ret = resources_available();
if (ret)
return ret;
- clk_put(cpu_clk);
- if (!IS_ERR(cpu_reg))
- regulator_put(cpu_reg);
-
dt_cpufreq_driver.driver_data = dev_get_platdata(&pdev->dev);
ret = cpufreq_register_driver(&dt_cpufreq_driver);
if (ret)
- dev_err(cpu_dev, "failed register driver: %d\n", ret);
+ dev_err(&pdev->dev, "failed register driver: %d\n", ret);
return ret;
}
diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c
index fb16d812c8f5..1dffb13e5c2f 100644
--- a/drivers/crypto/atmel-aes.c
+++ b/drivers/crypto/atmel-aes.c
@@ -1396,9 +1396,9 @@ static int atmel_aes_probe(struct platform_device *pdev)
}
aes_dd->io_base = devm_ioremap_resource(&pdev->dev, aes_res);
- if (!aes_dd->io_base) {
+ if (IS_ERR(aes_dd->io_base)) {
dev_err(dev, "can't ioremap\n");
- err = -ENOMEM;
+ err = PTR_ERR(aes_dd->io_base);
goto res_err;
}
diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c
index 3178f84d2757..0dadb6332f0e 100644
--- a/drivers/crypto/atmel-sha.c
+++ b/drivers/crypto/atmel-sha.c
@@ -1405,9 +1405,9 @@ static int atmel_sha_probe(struct platform_device *pdev)
}
sha_dd->io_base = devm_ioremap_resource(&pdev->dev, sha_res);
- if (!sha_dd->io_base) {
+ if (IS_ERR(sha_dd->io_base)) {
dev_err(dev, "can't ioremap\n");
- err = -ENOMEM;
+ err = PTR_ERR(sha_dd->io_base);
goto res_err;
}
diff --git a/drivers/crypto/atmel-tdes.c b/drivers/crypto/atmel-tdes.c
index 2c7a628d0375..bf467d7be35c 100644
--- a/drivers/crypto/atmel-tdes.c
+++ b/drivers/crypto/atmel-tdes.c
@@ -1417,9 +1417,9 @@ static int atmel_tdes_probe(struct platform_device *pdev)
}
tdes_dd->io_base = devm_ioremap_resource(&pdev->dev, tdes_res);
- if (!tdes_dd->io_base) {
+ if (IS_ERR(tdes_dd->io_base)) {
dev_err(dev, "can't ioremap\n");
- err = -ENOMEM;
+ err = PTR_ERR(tdes_dd->io_base);
goto res_err;
}
diff --git a/drivers/crypto/ccp/ccp-crypto-aes-cmac.c b/drivers/crypto/ccp/ccp-crypto-aes-cmac.c
index d89f20c04266..3d9acc53d247 100644
--- a/drivers/crypto/ccp/ccp-crypto-aes-cmac.c
+++ b/drivers/crypto/ccp/ccp-crypto-aes-cmac.c
@@ -220,6 +220,39 @@ static int ccp_aes_cmac_digest(struct ahash_request *req)
return ccp_aes_cmac_finup(req);
}
+static int ccp_aes_cmac_export(struct ahash_request *req, void *out)
+{
+ struct ccp_aes_cmac_req_ctx *rctx = ahash_request_ctx(req);
+ struct ccp_aes_cmac_exp_ctx state;
+
+ state.null_msg = rctx->null_msg;
+ memcpy(state.iv, rctx->iv, sizeof(state.iv));
+ state.buf_count = rctx->buf_count;
+ memcpy(state.buf, rctx->buf, sizeof(state.buf));
+
+ /* 'out' may not be aligned so memcpy from local variable */
+ memcpy(out, &state, sizeof(state));
+
+ return 0;
+}
+
+static int ccp_aes_cmac_import(struct ahash_request *req, const void *in)
+{
+ struct ccp_aes_cmac_req_ctx *rctx = ahash_request_ctx(req);
+ struct ccp_aes_cmac_exp_ctx state;
+
+ /* 'in' may not be aligned so memcpy to local variable */
+ memcpy(&state, in, sizeof(state));
+
+ memset(rctx, 0, sizeof(*rctx));
+ rctx->null_msg = state.null_msg;
+ memcpy(rctx->iv, state.iv, sizeof(rctx->iv));
+ rctx->buf_count = state.buf_count;
+ memcpy(rctx->buf, state.buf, sizeof(rctx->buf));
+
+ return 0;
+}
+
static int ccp_aes_cmac_setkey(struct crypto_ahash *tfm, const u8 *key,
unsigned int key_len)
{
@@ -352,10 +385,13 @@ int ccp_register_aes_cmac_algs(struct list_head *head)
alg->final = ccp_aes_cmac_final;
alg->finup = ccp_aes_cmac_finup;
alg->digest = ccp_aes_cmac_digest;
+ alg->export = ccp_aes_cmac_export;
+ alg->import = ccp_aes_cmac_import;
alg->setkey = ccp_aes_cmac_setkey;
halg = &alg->halg;
halg->digestsize = AES_BLOCK_SIZE;
+ halg->statesize = sizeof(struct ccp_aes_cmac_exp_ctx);
base = &halg->base;
snprintf(base->cra_name, CRYPTO_MAX_ALG_NAME, "cmac(aes)");
diff --git a/drivers/crypto/ccp/ccp-crypto-sha.c b/drivers/crypto/ccp/ccp-crypto-sha.c
index d14b3f28e010..8ef06fad8b14 100644
--- a/drivers/crypto/ccp/ccp-crypto-sha.c
+++ b/drivers/crypto/ccp/ccp-crypto-sha.c
@@ -207,6 +207,43 @@ static int ccp_sha_digest(struct ahash_request *req)
return ccp_sha_finup(req);
}
+static int ccp_sha_export(struct ahash_request *req, void *out)
+{
+ struct ccp_sha_req_ctx *rctx = ahash_request_ctx(req);
+ struct ccp_sha_exp_ctx state;
+
+ state.type = rctx->type;
+ state.msg_bits = rctx->msg_bits;
+ state.first = rctx->first;
+ memcpy(state.ctx, rctx->ctx, sizeof(state.ctx));
+ state.buf_count = rctx->buf_count;
+ memcpy(state.buf, rctx->buf, sizeof(state.buf));
+
+ /* 'out' may not be aligned so memcpy from local variable */
+ memcpy(out, &state, sizeof(state));
+
+ return 0;
+}
+
+static int ccp_sha_import(struct ahash_request *req, const void *in)
+{
+ struct ccp_sha_req_ctx *rctx = ahash_request_ctx(req);
+ struct ccp_sha_exp_ctx state;
+
+ /* 'in' may not be aligned so memcpy to local variable */
+ memcpy(&state, in, sizeof(state));
+
+ memset(rctx, 0, sizeof(*rctx));
+ rctx->type = state.type;
+ rctx->msg_bits = state.msg_bits;
+ rctx->first = state.first;
+ memcpy(rctx->ctx, state.ctx, sizeof(rctx->ctx));
+ rctx->buf_count = state.buf_count;
+ memcpy(rctx->buf, state.buf, sizeof(rctx->buf));
+
+ return 0;
+}
+
static int ccp_sha_setkey(struct crypto_ahash *tfm, const u8 *key,
unsigned int key_len)
{
@@ -403,9 +440,12 @@ static int ccp_register_sha_alg(struct list_head *head,
alg->final = ccp_sha_final;
alg->finup = ccp_sha_finup;
alg->digest = ccp_sha_digest;
+ alg->export = ccp_sha_export;
+ alg->import = ccp_sha_import;
halg = &alg->halg;
halg->digestsize = def->digest_size;
+ halg->statesize = sizeof(struct ccp_sha_exp_ctx);
base = &halg->base;
snprintf(base->cra_name, CRYPTO_MAX_ALG_NAME, "%s", def->name);
diff --git a/drivers/crypto/ccp/ccp-crypto.h b/drivers/crypto/ccp/ccp-crypto.h
index 76a96f0f44c6..a326ec20bfa8 100644
--- a/drivers/crypto/ccp/ccp-crypto.h
+++ b/drivers/crypto/ccp/ccp-crypto.h
@@ -129,6 +129,15 @@ struct ccp_aes_cmac_req_ctx {
struct ccp_cmd cmd;
};
+struct ccp_aes_cmac_exp_ctx {
+ unsigned int null_msg;
+
+ u8 iv[AES_BLOCK_SIZE];
+
+ unsigned int buf_count;
+ u8 buf[AES_BLOCK_SIZE];
+};
+
/***** SHA related defines *****/
#define MAX_SHA_CONTEXT_SIZE SHA256_DIGEST_SIZE
#define MAX_SHA_BLOCK_SIZE SHA256_BLOCK_SIZE
@@ -171,6 +180,19 @@ struct ccp_sha_req_ctx {
struct ccp_cmd cmd;
};
+struct ccp_sha_exp_ctx {
+ enum ccp_sha_type type;
+
+ u64 msg_bits;
+
+ unsigned int first;
+
+ u8 ctx[MAX_SHA_CONTEXT_SIZE];
+
+ unsigned int buf_count;
+ u8 buf[MAX_SHA_BLOCK_SIZE];
+};
+
/***** Common Context Structure *****/
struct ccp_ctx {
int (*complete)(struct crypto_async_request *req, int ret);
diff --git a/drivers/crypto/marvell/cesa.c b/drivers/crypto/marvell/cesa.c
index c0656e7f37b5..80239ae69527 100644
--- a/drivers/crypto/marvell/cesa.c
+++ b/drivers/crypto/marvell/cesa.c
@@ -420,7 +420,7 @@ static int mv_cesa_probe(struct platform_device *pdev)
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
cesa->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(cesa->regs))
- return -ENOMEM;
+ return PTR_ERR(cesa->regs);
ret = mv_cesa_dev_dma_init(cesa);
if (ret)
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index 51f50698e597..e63f061175ad 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -1,6 +1,6 @@
/* Qualcomm CE device driver.
*
- * Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1543,7 +1543,7 @@ static int qcedev_check_cipher_params(struct qcedev_cipher_op_req *req,
}
/* Check for sum of all dst length is equal to data_len */
for (i = 0; i < req->entries; i++) {
- if (req->vbuf.dst[i].len >= ULONG_MAX - total) {
+ if (req->vbuf.dst[i].len >= U32_MAX - total) {
pr_err("%s: Integer overflow on total req dst vbuf length\n",
__func__);
goto error;
@@ -1557,7 +1557,7 @@ static int qcedev_check_cipher_params(struct qcedev_cipher_op_req *req,
}
/* Check for sum of all src length is equal to data_len */
for (i = 0, total = 0; i < req->entries; i++) {
- if (req->vbuf.src[i].len > ULONG_MAX - total) {
+ if (req->vbuf.src[i].len > U32_MAX - total) {
pr_err("%s: Integer overflow on total req src vbuf length\n",
__func__);
goto error;
@@ -1619,7 +1619,7 @@ static int qcedev_check_sha_params(struct qcedev_sha_op_req *req,
/* Check for sum of all src length is equal to data_len */
for (i = 0, total = 0; i < req->entries; i++) {
- if (req->data[i].len > ULONG_MAX - total) {
+ if (req->data[i].len > U32_MAX - total) {
pr_err("%s: Integer overflow on total req buf length\n",
__func__);
goto sha_error;
diff --git a/drivers/crypto/ux500/cryp/cryp_core.c b/drivers/crypto/ux500/cryp/cryp_core.c
index 4c243c1ffc7f..790f7cadc1ed 100644
--- a/drivers/crypto/ux500/cryp/cryp_core.c
+++ b/drivers/crypto/ux500/cryp/cryp_core.c
@@ -1440,9 +1440,9 @@ static int ux500_cryp_probe(struct platform_device *pdev)
device_data->phybase = res->start;
device_data->base = devm_ioremap_resource(dev, res);
- if (!device_data->base) {
+ if (IS_ERR(device_data->base)) {
dev_err(dev, "[%s]: ioremap failed!", __func__);
- ret = -ENOMEM;
+ ret = PTR_ERR(device_data->base);
goto out;
}
diff --git a/drivers/crypto/ux500/hash/hash_core.c b/drivers/crypto/ux500/hash/hash_core.c
index f47d112041b2..66b1c3313e2e 100644
--- a/drivers/crypto/ux500/hash/hash_core.c
+++ b/drivers/crypto/ux500/hash/hash_core.c
@@ -1675,9 +1675,9 @@ static int ux500_hash_probe(struct platform_device *pdev)
device_data->phybase = res->start;
device_data->base = devm_ioremap_resource(dev, res);
- if (!device_data->base) {
+ if (IS_ERR(device_data->base)) {
dev_err(dev, "%s: ioremap() failed!\n", __func__);
- ret = -ENOMEM;
+ ret = PTR_ERR(device_data->base);
goto out;
}
spin_lock_init(&device_data->ctx_lock);
diff --git a/drivers/devfreq/arm-memlat-mon.c b/drivers/devfreq/arm-memlat-mon.c
index 370d7d95042b..4fb0a5ffda50 100644
--- a/drivers/devfreq/arm-memlat-mon.c
+++ b/drivers/devfreq/arm-memlat-mon.c
@@ -311,19 +311,19 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev)
hw->of_node = of_parse_phandle(dev->of_node, "qcom,target-dev", 0);
if (!hw->of_node) {
dev_err(dev, "Couldn't find a target device\n");
- goto err_out;
+ return -ENODEV;
}
if (get_mask_from_dev_handle(pdev, &cpu_grp->cpus)) {
dev_err(dev, "CPU list is empty\n");
- goto err_out;
+ return -ENODEV;
}
hw->num_cores = cpumask_weight(&cpu_grp->cpus);
hw->core_stats = devm_kzalloc(dev, hw->num_cores *
sizeof(*(hw->core_stats)), GFP_KERNEL);
if (!hw->core_stats)
- goto err_out;
+ return -ENOMEM;
for_each_cpu(cpu, &cpu_grp->cpus)
hw->core_stats[cpu - cpumask_first(&cpu_grp->cpus)].id = cpu;
@@ -335,14 +335,10 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev)
ret = register_memlat(dev, hw);
if (ret) {
pr_err("Mem Latency Gov registration failed\n");
- goto err_out;
+ return ret;
}
return 0;
-
-err_out:
- kfree(cpu_grp);
- return -EINVAL;
}
static struct of_device_id match_table[] = {
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index 9eee13ef83a5..d87a47547ba5 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -1452,7 +1452,7 @@ static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range,
u64 chan_off;
u64 dram_base = get_dram_base(pvt, range);
u64 hole_off = f10_dhar_offset(pvt);
- u64 dct_sel_base_off = (pvt->dct_sel_hi & 0xFFFFFC00) << 16;
+ u64 dct_sel_base_off = (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16;
if (hi_rng) {
/*
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index 429309c62699..cbee3179ec08 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -1117,8 +1117,8 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
edac_dbg(0, "TAD#%d: up to %u.%03u GB (0x%016Lx), socket interleave %d, memory interleave %d, TGT: %d, %d, %d, %d, reg=0x%08x\n",
n_tads, gb, (mb*1000)/1024,
((u64)tmp_mb) << 20L,
- (u32)TAD_SOCK(reg),
- (u32)TAD_CH(reg),
+ (u32)(1 << TAD_SOCK(reg)),
+ (u32)TAD_CH(reg) + 1,
(u32)TAD_TGT0(reg),
(u32)TAD_TGT1(reg),
(u32)TAD_TGT2(reg),
@@ -1396,7 +1396,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
}
ch_way = TAD_CH(reg) + 1;
- sck_way = TAD_SOCK(reg) + 1;
+ sck_way = 1 << TAD_SOCK(reg);
if (ch_way == 3)
idx = addr >> 6;
@@ -1453,7 +1453,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
n_tads,
addr,
limit,
- (u32)TAD_SOCK(reg),
+ sck_way,
ch_way,
offset,
idx,
@@ -1468,18 +1468,12 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
offset, addr);
return -EINVAL;
}
- addr -= offset;
- /* Store the low bits [0:6] of the addr */
- ch_addr = addr & 0x7f;
- /* Remove socket wayness and remove 6 bits */
- addr >>= 6;
- addr = div_u64(addr, sck_xch);
-#if 0
- /* Divide by channel way */
- addr = addr / ch_way;
-#endif
- /* Recover the last 6 bits */
- ch_addr |= addr << 6;
+
+ ch_addr = addr - offset;
+ ch_addr >>= (6 + shiftup);
+ ch_addr /= ch_way * sck_way;
+ ch_addr <<= (6 + shiftup);
+ ch_addr |= addr & ((1 << (6 + shiftup)) - 1);
/*
* Step 3) Decode rank
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
index 5a8fbadbd27b..8ac49812a716 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
@@ -63,6 +63,10 @@ bool amdgpu_has_atpx(void) {
return amdgpu_atpx_priv.atpx_detected;
}
+bool amdgpu_has_atpx_dgpu_power_cntl(void) {
+ return amdgpu_atpx_priv.atpx.functions.power_cntl;
+}
+
/**
* amdgpu_atpx_call - call an ATPX method
*
@@ -142,10 +146,6 @@ static void amdgpu_atpx_parse_functions(struct amdgpu_atpx_functions *f, u32 mas
*/
static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx)
{
- /* make sure required functions are enabled */
- /* dGPU power control is required */
- atpx->functions.power_cntl = true;
-
if (atpx->functions.px_params) {
union acpi_object *info;
struct atpx_px_params output;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index c961fe093e12..9d88023df836 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -61,6 +61,12 @@ static const char *amdgpu_asic_name[] = {
"LAST",
};
+#if defined(CONFIG_VGA_SWITCHEROO)
+bool amdgpu_has_atpx_dgpu_power_cntl(void);
+#else
+static inline bool amdgpu_has_atpx_dgpu_power_cntl(void) { return false; }
+#endif
+
bool amdgpu_device_is_px(struct drm_device *dev)
{
struct amdgpu_device *adev = dev->dev_private;
@@ -1469,7 +1475,7 @@ int amdgpu_device_init(struct amdgpu_device *adev,
if (amdgpu_runtime_pm == 1)
runtime = true;
- if (amdgpu_device_is_px(ddev))
+ if (amdgpu_device_is_px(ddev) && amdgpu_has_atpx_dgpu_power_cntl())
runtime = true;
vga_switcheroo_register_client(adev->pdev, &amdgpu_switcheroo_ops, runtime);
if (runtime)
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c
index 272110cc18c2..ea87033bfaf6 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c
@@ -898,14 +898,6 @@ static int gmc_v7_0_early_init(void *handle)
gmc_v7_0_set_gart_funcs(adev);
gmc_v7_0_set_irq_funcs(adev);
- if (adev->flags & AMD_IS_APU) {
- adev->mc.vram_type = AMDGPU_VRAM_TYPE_UNKNOWN;
- } else {
- u32 tmp = RREG32(mmMC_SEQ_MISC0);
- tmp &= MC_SEQ_MISC0__MT__MASK;
- adev->mc.vram_type = gmc_v7_0_convert_vram_type(tmp);
- }
-
return 0;
}
@@ -926,6 +918,14 @@ static int gmc_v7_0_sw_init(void *handle)
if (r)
return r;
+ if (adev->flags & AMD_IS_APU) {
+ adev->mc.vram_type = AMDGPU_VRAM_TYPE_UNKNOWN;
+ } else {
+ u32 tmp = RREG32(mmMC_SEQ_MISC0);
+ tmp &= MC_SEQ_MISC0__MT__MASK;
+ adev->mc.vram_type = gmc_v7_0_convert_vram_type(tmp);
+ }
+
r = amdgpu_irq_add_id(adev, 146, &adev->mc.vm_fault);
if (r)
return r;
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
index ba4ad00ba8b4..08423089fb84 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
@@ -852,14 +852,6 @@ static int gmc_v8_0_early_init(void *handle)
gmc_v8_0_set_gart_funcs(adev);
gmc_v8_0_set_irq_funcs(adev);
- if (adev->flags & AMD_IS_APU) {
- adev->mc.vram_type = AMDGPU_VRAM_TYPE_UNKNOWN;
- } else {
- u32 tmp = RREG32(mmMC_SEQ_MISC0);
- tmp &= MC_SEQ_MISC0__MT__MASK;
- adev->mc.vram_type = gmc_v8_0_convert_vram_type(tmp);
- }
-
return 0;
}
@@ -870,6 +862,8 @@ static int gmc_v8_0_late_init(void *handle)
return amdgpu_irq_get(adev, &adev->mc.vm_fault, 0);
}
+#define mmMC_SEQ_MISC0_FIJI 0xA71
+
static int gmc_v8_0_sw_init(void *handle)
{
int r;
@@ -880,6 +874,19 @@ static int gmc_v8_0_sw_init(void *handle)
if (r)
return r;
+ if (adev->flags & AMD_IS_APU) {
+ adev->mc.vram_type = AMDGPU_VRAM_TYPE_UNKNOWN;
+ } else {
+ u32 tmp;
+
+ if (adev->asic_type == CHIP_FIJI)
+ tmp = RREG32(mmMC_SEQ_MISC0_FIJI);
+ else
+ tmp = RREG32(mmMC_SEQ_MISC0);
+ tmp &= MC_SEQ_MISC0__MT__MASK;
+ adev->mc.vram_type = gmc_v8_0_convert_vram_type(tmp);
+ }
+
r = amdgpu_irq_add_id(adev, 146, &adev->mc.vm_fault);
if (r)
return r;
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c
index 2cf50180cc51..b1c7a9b3631b 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c
@@ -32,8 +32,8 @@
#include "oss/oss_2_4_d.h"
#include "oss/oss_2_4_sh_mask.h"
-#include "gmc/gmc_8_1_d.h"
-#include "gmc/gmc_8_1_sh_mask.h"
+#include "gmc/gmc_7_1_d.h"
+#include "gmc/gmc_7_1_sh_mask.h"
#include "gca/gfx_8_0_d.h"
#include "gca/gfx_8_0_enum.h"
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index 9535c5b60387..7e5a97204051 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -178,7 +178,7 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
{
struct drm_dp_aux_msg msg;
unsigned int retry;
- int err;
+ int err = 0;
memset(&msg, 0, sizeof(msg));
msg.address = offset;
@@ -186,6 +186,8 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
msg.buffer = buffer;
msg.size = size;
+ mutex_lock(&aux->hw_mutex);
+
/*
* The specification doesn't give any recommendation on how often to
* retry native transactions. We used to retry 7 times like for
@@ -194,25 +196,24 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
*/
for (retry = 0; retry < 32; retry++) {
- mutex_lock(&aux->hw_mutex);
err = aux->transfer(aux, &msg);
- mutex_unlock(&aux->hw_mutex);
if (err < 0) {
if (err == -EBUSY)
continue;
- return err;
+ goto unlock;
}
switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) {
case DP_AUX_NATIVE_REPLY_ACK:
if (err < size)
- return -EPROTO;
- return err;
+ err = -EPROTO;
+ goto unlock;
case DP_AUX_NATIVE_REPLY_NACK:
- return -EIO;
+ err = -EIO;
+ goto unlock;
case DP_AUX_NATIVE_REPLY_DEFER:
usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100);
@@ -221,7 +222,11 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
}
DRM_DEBUG_KMS("too many retries, giving up\n");
- return -EIO;
+ err = -EIO;
+
+unlock:
+ mutex_unlock(&aux->hw_mutex);
+ return err;
}
/**
@@ -543,9 +548,7 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
int max_retries = max(7, drm_dp_i2c_retry_count(msg, dp_aux_i2c_speed_khz));
for (retry = 0, defer_i2c = 0; retry < (max_retries + defer_i2c); retry++) {
- mutex_lock(&aux->hw_mutex);
ret = aux->transfer(aux, msg);
- mutex_unlock(&aux->hw_mutex);
if (ret < 0) {
if (ret == -EBUSY)
continue;
@@ -684,6 +687,8 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
memset(&msg, 0, sizeof(msg));
+ mutex_lock(&aux->hw_mutex);
+
for (i = 0; i < num; i++) {
msg.address = msgs[i].addr;
drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
@@ -738,6 +743,8 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
msg.size = 0;
(void)drm_dp_i2c_do_msg(aux, &msg);
+ mutex_unlock(&aux->hw_mutex);
+
return err;
}
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c
index bb292143997e..adf74f4366bb 100644
--- a/drivers/gpu/drm/radeon/atombios_encoders.c
+++ b/drivers/gpu/drm/radeon/atombios_encoders.c
@@ -892,8 +892,6 @@ atombios_dig_encoder_setup2(struct drm_encoder *encoder, int action, int panel_m
else
args.v1.ucLaneNum = 4;
- if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode) && (dp_clock == 270000))
- args.v1.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ;
switch (radeon_encoder->encoder_id) {
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER1;
@@ -910,6 +908,10 @@ atombios_dig_encoder_setup2(struct drm_encoder *encoder, int action, int panel_m
args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKB;
else
args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKA;
+
+ if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode) && (dp_clock == 270000))
+ args.v1.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ;
+
break;
case 2:
case 3:
diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
index c4b4f298a283..9bc408c9f9f6 100644
--- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
+++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
@@ -62,6 +62,10 @@ bool radeon_has_atpx(void) {
return radeon_atpx_priv.atpx_detected;
}
+bool radeon_has_atpx_dgpu_power_cntl(void) {
+ return radeon_atpx_priv.atpx.functions.power_cntl;
+}
+
/**
* radeon_atpx_call - call an ATPX method
*
@@ -141,10 +145,6 @@ static void radeon_atpx_parse_functions(struct radeon_atpx_functions *f, u32 mas
*/
static int radeon_atpx_validate(struct radeon_atpx *atpx)
{
- /* make sure required functions are enabled */
- /* dGPU power control is required */
- atpx->functions.power_cntl = true;
-
if (atpx->functions.px_params) {
union acpi_object *info;
struct atpx_px_params output;
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index c566993a2ec3..f78f111e68de 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -103,6 +103,12 @@ static const char radeon_family_name[][16] = {
"LAST",
};
+#if defined(CONFIG_VGA_SWITCHEROO)
+bool radeon_has_atpx_dgpu_power_cntl(void);
+#else
+static inline bool radeon_has_atpx_dgpu_power_cntl(void) { return false; }
+#endif
+
#define RADEON_PX_QUIRK_DISABLE_PX (1 << 0)
#define RADEON_PX_QUIRK_LONG_WAKEUP (1 << 1)
@@ -1433,7 +1439,7 @@ int radeon_device_init(struct radeon_device *rdev,
* ignore it */
vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode);
- if (rdev->flags & RADEON_IS_PX)
+ if ((rdev->flags & RADEON_IS_PX) && radeon_has_atpx_dgpu_power_cntl())
runtime = true;
vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops, runtime);
if (runtime)
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index a82b891ae1fe..7285adb27099 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -2926,9 +2926,11 @@ static struct si_dpm_quirk si_dpm_quirk_list[] = {
/* PITCAIRN - https://bugs.freedesktop.org/show_bug.cgi?id=76490 */
{ PCI_VENDOR_ID_ATI, 0x6810, 0x1462, 0x3036, 0, 120000 },
{ PCI_VENDOR_ID_ATI, 0x6811, 0x174b, 0xe271, 0, 120000 },
+ { PCI_VENDOR_ID_ATI, 0x6811, 0x174b, 0x2015, 0, 120000 },
{ PCI_VENDOR_ID_ATI, 0x6810, 0x174b, 0xe271, 85000, 90000 },
{ PCI_VENDOR_ID_ATI, 0x6811, 0x1462, 0x2015, 0, 120000 },
{ PCI_VENDOR_ID_ATI, 0x6811, 0x1043, 0x2015, 0, 120000 },
+ { PCI_VENDOR_ID_ATI, 0x6811, 0x148c, 0x2015, 0, 120000 },
{ 0, 0, 0, 0 },
};
@@ -3008,6 +3010,10 @@ 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;
diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
index 62c7b1dafaa4..73e41a8613da 100644
--- a/drivers/gpu/drm/udl/udl_fb.c
+++ b/drivers/gpu/drm/udl/udl_fb.c
@@ -539,7 +539,7 @@ static int udlfb_create(struct drm_fb_helper *helper,
out_destroy_fbi:
drm_fb_helper_release_fbi(helper);
out_gfree:
- drm_gem_object_unreference(&ufbdev->ufb.obj->base);
+ drm_gem_object_unreference_unlocked(&ufbdev->ufb.obj->base);
out:
return ret;
}
diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
index 2a0a784ab6ee..d7528e0d8442 100644
--- a/drivers/gpu/drm/udl/udl_gem.c
+++ b/drivers/gpu/drm/udl/udl_gem.c
@@ -52,7 +52,7 @@ udl_gem_create(struct drm_file *file,
return ret;
}
- drm_gem_object_unreference(&obj->base);
+ drm_gem_object_unreference_unlocked(&obj->base);
*handle_p = handle;
return 0;
}
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 3052166c7a18..b57fe05b21d5 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -579,7 +579,6 @@ void adreno_cp_callback(struct adreno_device *adreno_dev, int bit)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
- kgsl_schedule_work(&device->event_work);
adreno_dispatcher_schedule(device);
}
@@ -1130,7 +1129,10 @@ static int adreno_init(struct kgsl_device *device)
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
int ret;
- kgsl_pwrctrl_change_state(device, KGSL_STATE_INIT);
+ ret = kgsl_pwrctrl_change_state(device, KGSL_STATE_INIT);
+ if (ret)
+ return ret;
+
/*
* initialization only needs to be done once initially until
* device is shutdown
@@ -1595,6 +1597,8 @@ static int adreno_stop(struct kgsl_device *device)
adreno_ringbuffer_stop(adreno_dev);
+ kgsl_pwrscale_update_stats(device);
+
adreno_irqctrl(adreno_dev, 0);
adreno_ocmem_free(adreno_dev);
@@ -2117,6 +2121,14 @@ bool adreno_hw_isidle(struct adreno_device *adreno_dev)
const struct adreno_gpu_core *gpucore = adreno_dev->gpucore;
unsigned int reg_rbbm_status;
+ if (adreno_is_a540(adreno_dev))
+ /**
+ * Due to CRC idle throttling GPU
+ * idle hysteresys can take up to
+ * 3usec for expire - account for it
+ */
+ udelay(5);
+
adreno_readreg(adreno_dev, ADRENO_REG_RBBM_STATUS,
&reg_rbbm_status);
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 9f462bca26ce..f5fb4e48c3ee 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -613,11 +613,13 @@ struct adreno_vbif_platform {
* struct adreno_vbif_snapshot_registers - Holds an array of vbif registers
* listed for snapshot dump for a particular core
* @version: vbif version
+ * @mask: vbif revision mask
* @registers: vbif registers listed for snapshot dump
* @count: count of vbif registers listed for snapshot
*/
struct adreno_vbif_snapshot_registers {
const unsigned int version;
+ const unsigned int mask;
const unsigned int *registers;
const int count;
};
diff --git a/drivers/gpu/msm/adreno_a4xx_snapshot.c b/drivers/gpu/msm/adreno_a4xx_snapshot.c
index 6921af5c0ab5..540b42b984c0 100644
--- a/drivers/gpu/msm/adreno_a4xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a4xx_snapshot.c
@@ -168,15 +168,15 @@ static const unsigned int a4xx_vbif_ver_20050000_registers[] = {
static const struct adreno_vbif_snapshot_registers
a4xx_vbif_snapshot_registers[] = {
- { 0x20000000, a4xx_vbif_ver_20000000_registers,
+ { 0x20000000, 0xFFFF0000, a4xx_vbif_ver_20000000_registers,
ARRAY_SIZE(a4xx_vbif_ver_20000000_registers)/2},
- { 0x20020000, a4xx_vbif_ver_20020000_registers,
+ { 0x20020000, 0xFFFF0000, a4xx_vbif_ver_20020000_registers,
ARRAY_SIZE(a4xx_vbif_ver_20020000_registers)/2},
- { 0x20050000, a4xx_vbif_ver_20050000_registers,
+ { 0x20050000, 0xFFFF0000, a4xx_vbif_ver_20050000_registers,
ARRAY_SIZE(a4xx_vbif_ver_20050000_registers)/2},
- { 0x20070000, a4xx_vbif_ver_20020000_registers,
+ { 0x20070000, 0xFFFF0000, a4xx_vbif_ver_20020000_registers,
ARRAY_SIZE(a4xx_vbif_ver_20020000_registers)/2},
- { 0x20090000, a4xx_vbif_ver_20050000_registers,
+ { 0x20090000, 0xFFFF0000, a4xx_vbif_ver_20050000_registers,
ARRAY_SIZE(a4xx_vbif_ver_20050000_registers)/2},
};
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index 96f72c59e4cd..467b385f6d56 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -2373,17 +2373,25 @@ static int a5xx_microcode_read(struct adreno_device *adreno_dev)
{
int ret;
- ret = _load_firmware(KGSL_DEVICE(adreno_dev),
- adreno_dev->gpucore->pm4fw_name, &adreno_dev->pm4,
- &adreno_dev->pm4_fw_size, &adreno_dev->pm4_fw_version);
- if (ret)
- return ret;
+ if (adreno_dev->pm4.hostptr == NULL) {
+ ret = _load_firmware(KGSL_DEVICE(adreno_dev),
+ adreno_dev->gpucore->pm4fw_name,
+ &adreno_dev->pm4,
+ &adreno_dev->pm4_fw_size,
+ &adreno_dev->pm4_fw_version);
+ if (ret)
+ return ret;
+ }
- ret = _load_firmware(KGSL_DEVICE(adreno_dev),
- adreno_dev->gpucore->pfpfw_name, &adreno_dev->pfp,
- &adreno_dev->pfp_fw_size, &adreno_dev->pfp_fw_version);
- if (ret)
- return ret;
+ if (adreno_dev->pfp.hostptr == NULL) {
+ ret = _load_firmware(KGSL_DEVICE(adreno_dev),
+ adreno_dev->gpucore->pfpfw_name,
+ &adreno_dev->pfp,
+ &adreno_dev->pfp_fw_size,
+ &adreno_dev->pfp_fw_version);
+ if (ret)
+ return ret;
+ }
ret = _load_gpmu_firmware(adreno_dev);
if (ret)
@@ -3058,7 +3066,6 @@ static void a5xx_irq_storm_worker(struct work_struct *work)
mutex_unlock(&device->mutex);
/* Reschedule just to make sure everything retires */
- kgsl_schedule_work(&device->event_work);
adreno_dispatcher_schedule(device);
}
@@ -3109,8 +3116,6 @@ static void a5xx_cp_callback(struct adreno_device *adreno_dev, int bit)
}
a5xx_preemption_trigger(adreno_dev);
-
- kgsl_schedule_work(&device->event_work);
adreno_dispatcher_schedule(device);
}
diff --git a/drivers/gpu/msm/adreno_a5xx.h b/drivers/gpu/msm/adreno_a5xx.h
index 7965bb7b5440..27d5a4b31c71 100644
--- a/drivers/gpu/msm/adreno_a5xx.h
+++ b/drivers/gpu/msm/adreno_a5xx.h
@@ -52,7 +52,7 @@
#define A5XX_CP_CTXRECORD_MAGIC_REF 0x27C4BAFCUL
/* Size of each CP preemption record */
-#define A5XX_CP_CTXRECORD_SIZE_IN_BYTES 0x100000
+#define A5XX_CP_CTXRECORD_SIZE_IN_BYTES 0x10000
/* Size of the preemption counter block (in bytes) */
#define A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE (16 * 4)
diff --git a/drivers/gpu/msm/adreno_a5xx_snapshot.c b/drivers/gpu/msm/adreno_a5xx_snapshot.c
index 4f368a8f93f3..04d82844a5e9 100644
--- a/drivers/gpu/msm/adreno_a5xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a5xx_snapshot.c
@@ -128,6 +128,9 @@ static const struct adreno_debugbus_block a5xx_debugbus_blocks[] = {
#define A5XX_NUM_AXI_ARB_BLOCKS 2
#define A5XX_NUM_XIN_BLOCKS 4
+/* Width of A5XX_CP_DRAW_STATE_ADDR is 8 bits */
+#define A5XX_CP_DRAW_STATE_ADDR_WIDTH 8
+
/* a5xx_snapshot_cp_pm4() - Dump PM4 data in snapshot */
static size_t a5xx_snapshot_cp_pm4(struct kgsl_device *device, u8 *buf,
size_t remain, void *priv)
@@ -326,8 +329,7 @@ static void a5xx_snapshot_debugbus(struct kgsl_device *device,
}
}
-static const unsigned int a5xx_vbif_ver_20040000_registers[] = {
- /* VBIF version 0x20040000*/
+static const unsigned int a5xx_vbif_ver_20xxxxxx_registers[] = {
0x3000, 0x3007, 0x300C, 0x3014, 0x3018, 0x302C, 0x3030, 0x3030,
0x3034, 0x3036, 0x3038, 0x3038, 0x303C, 0x303D, 0x3040, 0x3040,
0x3042, 0x3042, 0x3049, 0x3049, 0x3058, 0x3058, 0x305A, 0x3061,
@@ -341,10 +343,8 @@ static const unsigned int a5xx_vbif_ver_20040000_registers[] = {
static const struct adreno_vbif_snapshot_registers
a5xx_vbif_snapshot_registers[] = {
- { 0x20040000, a5xx_vbif_ver_20040000_registers,
- ARRAY_SIZE(a5xx_vbif_ver_20040000_registers)/2},
- { 0x20040001, a5xx_vbif_ver_20040000_registers,
- ARRAY_SIZE(a5xx_vbif_ver_20040000_registers)/2},
+ { 0x20000000, 0xFF000000, a5xx_vbif_ver_20xxxxxx_registers,
+ ARRAY_SIZE(a5xx_vbif_ver_20xxxxxx_registers)/2},
};
/*
@@ -379,7 +379,7 @@ static const unsigned int a5xx_registers[] = {
/* VPC */
0x0E60, 0x0E7C,
/* UCHE */
- 0x0E80, 0x0E8E, 0x0E90, 0x0E96, 0xEA0, 0xEA8, 0xEB0, 0xEB2,
+ 0x0E80, 0x0E8F, 0x0E90, 0x0E96, 0xEA0, 0xEA8, 0xEB0, 0xEB2,
/* RB CTX 0 */
0xE140, 0xE147, 0xE150, 0xE187, 0xE1A0, 0xE1A9, 0xE1B0, 0xE1B6,
@@ -414,49 +414,49 @@ static const unsigned int a5xx_registers[] = {
0xB000, 0xB97F, 0xB9A0, 0xB9BF,
};
-/*
- * The HLSQ registers can only be read via the crash dumper (not AHB) so they
- * need to be in their own array because the array above does double duty for
- * the fallback path too
- */
-static const unsigned int a5xx_hlsq_registers[] = {
+struct a5xx_hlsq_sp_tp_regs {
+ unsigned int statetype;
+ unsigned int ahbaddr;
+ unsigned int size;
+ uint64_t offset;
+};
+
+static struct a5xx_hlsq_sp_tp_regs a5xx_hlsq_sp_tp_registers[] = {
+ /* HSLQ non context. 0xe32 - 0xe3f are holes so don't include them */
+ { 0x35, 0xE00, 0x32 },
+ /* HLSQ CTX 0 2D */
+ { 0x31, 0x2080, 0x1 },
+ /* HLSQ CTX 1 2D */
+ { 0x33, 0x2480, 0x1 },
+ /* HLSQ CTX 0 3D. 0xe7e2 - 0xe7ff are holes so don't inculde them */
+ { 0x32, 0xE780, 0x62 },
+ /* HLSQ CTX 1 3D. 0xefe2 - 0xefff are holes so don't include them */
+ { 0x34, 0xEF80, 0x62 },
+
/* SP non context */
- 0x0EC0, 0xEC2, 0xED0, 0xEE0, 0xEF0, 0xEF2, 0xEFA, 0xEFF,
+ { 0x3f, 0x0EC0, 0x40 },
/* SP CTX 0 2D */
- 0x2040, 0x2040,
+ { 0x3d, 0x2040, 0x1 },
/* SP CTX 1 2D */
- 0x2440, 0x2440,
- /* SP CTXT 0 3D */
- 0xE580, 0xE580, 0xE584, 0xE58B, 0xE590, 0xE5B1, 0xE5C0, 0xE5DF,
- 0xE5F0, 0xE5F9, 0xE600, 0xE608, 0xE610, 0xE631, 0xE640, 0xE661,
- 0xE670, 0xE673, 0xE6F0, 0xE6F0,
- /* SP CTXT 1 3D */
- 0xED80, 0xED80, 0xED84, 0xED8B, 0xED90, 0xEDB1, 0xEDC0, 0xEDDF,
- 0xEDF0, 0xEDF9, 0xEE00, 0xEE08, 0xEE10, 0xEE31, 0xEE40, 0xEE61,
- 0xEE70, 0xEE73, 0xEEF0, 0xEEF0,
- /* TP non context */
- 0xF00, 0xF03, 0xF08, 0xF08, 0xF10, 0xF1B,
- /* TP CTX 0 2D */
- 0x2000, 0x2009,
- /* TP CTX 1 2D */
- 0x2400, 0x2409,
+ { 0x3b, 0x2440, 0x1 },
+ /* SP CTX 0 3D */
+ { 0x3e, 0xE580, 0x180 },
+ /* SP CTX 1 3D */
+ { 0x3c, 0xED80, 0x180 },
+
+ /* TP non context. 0x0f1c - 0x0f3f are holes so don't include them */
+ { 0x3a, 0x0F00, 0x1c },
+ /* TP CTX 0 2D. 0x200a - 0x200f are holes so don't include them */
+ { 0x38, 0x2000, 0xa },
+ /* TP CTX 1 2D. 0x240a - 0x240f are holes so don't include them */
+ { 0x36, 0x2400, 0xa },
/* TP CTX 0 3D */
- 0xE700, 0xE707, 0xE70E, 0xE731,
- 0xE750, 0xE751, 0xE75A, 0xE764, 0xE76C, 0xE77F,
+ { 0x39, 0xE700, 0x80 },
/* TP CTX 1 3D */
- 0xEF00, 0xEF07, 0xEF0E, 0xEF31,
- 0xEF50, 0xEF51, 0xEF5A, 0xEF64, 0xEF6C, 0xEF7F,
- /* HLSQ non context */
- 0xE00, 0xE01, 0xE04, 0xE06, 0xE08, 0xE09, 0xE10, 0xE17,
- 0xE20, 0xE25,
- /* HLSQ CTXT 0 3D */
- 0xE784, 0xE789, 0xE78B, 0xE796, 0xE7A0, 0xE7A2, 0xE7B0, 0xE7BB,
- 0xE7C0, 0xE7DD, 0xE7E0, 0xE7E1,
- /* HLSQ CTXT 1 3D */
- 0xEF84, 0xEF89, 0xEF8B, 0xEF96, 0xEFA0, 0xEFA2, 0xEFB0, 0xEFBB,
- 0xEFC0, 0xEFDD, 0xEFE0, 0xEFE1,
+ { 0x37, 0xEF00, 0x80 },
};
+
#define A5XX_NUM_SHADER_BANKS 4
#define A5XX_SHADER_STATETYPE_SHIFT 8
@@ -652,7 +652,6 @@ static struct cdregs {
unsigned int size;
} _a5xx_cd_registers[] = {
{ a5xx_registers, ARRAY_SIZE(a5xx_registers) },
- { a5xx_hlsq_registers, ARRAY_SIZE(a5xx_hlsq_registers) },
};
#define REG_PAIR_COUNT(_a, _i) \
@@ -776,6 +775,46 @@ static void _a5xx_do_crashdump(struct kgsl_device *device)
crash_dump_valid = true;
}
+static int get_hlsq_registers(struct kgsl_device *device,
+ const struct a5xx_hlsq_sp_tp_regs *regs, unsigned int *data)
+{
+ unsigned int i;
+ unsigned int *src = registers.hostptr + regs->offset;
+
+ for (i = 0; i < regs->size; i++) {
+ *data++ = regs->ahbaddr + i;
+ *data++ = *(src + i);
+ }
+
+ return (2 * regs->size);
+}
+
+static size_t a5xx_snapshot_dump_hlsq_sp_tp_regs(struct kgsl_device *device,
+ u8 *buf, size_t remain, void *priv)
+{
+ struct kgsl_snapshot_regs *header = (struct kgsl_snapshot_regs *)buf;
+ unsigned int *data = (unsigned int *)(buf + sizeof(*header));
+ int count = 0, i;
+
+ /* Figure out how many registers we are going to dump */
+ for (i = 0; i < ARRAY_SIZE(a5xx_hlsq_sp_tp_registers); i++)
+ count += a5xx_hlsq_sp_tp_registers[i].size;
+
+ if (remain < (count * 8) + sizeof(*header)) {
+ SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(a5xx_hlsq_sp_tp_registers); i++)
+ data += get_hlsq_registers(device,
+ &a5xx_hlsq_sp_tp_registers[i], data);
+
+ header->count = count;
+
+ /* Return the size of the section */
+ return (count * 8) + sizeof(*header);
+}
+
/*
* a5xx_snapshot() - A5XX GPU snapshot function
* @adreno_dev: Device being snapshotted
@@ -806,6 +845,10 @@ void a5xx_snapshot(struct adreno_device *adreno_dev,
a5xx_vbif_snapshot_registers,
ARRAY_SIZE(a5xx_vbif_snapshot_registers));
+ /* Dump SP TP HLSQ registers */
+ kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS, snapshot,
+ a5xx_snapshot_dump_hlsq_sp_tp_regs, NULL);
+
/* CP_PFP indexed registers */
kgsl_snapshot_indexed_registers(device, snapshot,
A5XX_CP_PFP_STAT_ADDR, A5XX_CP_PFP_STAT_DATA,
@@ -819,7 +862,7 @@ void a5xx_snapshot(struct adreno_device *adreno_dev,
/* CP_DRAW_STATE */
kgsl_snapshot_indexed_registers(device, snapshot,
A5XX_CP_DRAW_STATE_ADDR, A5XX_CP_DRAW_STATE_DATA,
- 0, 128);
+ 0, 1 << A5XX_CP_DRAW_STATE_ADDR_WIDTH);
/*
* CP needs to be halted on a530v1 before reading CP_PFP_UCODE_DBG_DATA
@@ -878,8 +921,8 @@ void a5xx_snapshot(struct adreno_device *adreno_dev,
}
-static int _a5xx_crashdump_init(struct a5xx_shader_block *block, uint64_t *ptr,
- uint64_t *offset)
+static int _a5xx_crashdump_init_shader(struct a5xx_shader_block *block,
+ uint64_t *ptr, uint64_t *offset)
{
int qwords = 0;
unsigned int j;
@@ -908,6 +951,31 @@ static int _a5xx_crashdump_init(struct a5xx_shader_block *block, uint64_t *ptr,
return qwords;
}
+static int _a5xx_crashdump_init_hlsq(struct a5xx_hlsq_sp_tp_regs *regs,
+ uint64_t *ptr, uint64_t *offset)
+{
+ int qwords = 0;
+
+ /* Program the aperture */
+ ptr[qwords++] =
+ (regs->statetype << A5XX_SHADER_STATETYPE_SHIFT);
+ ptr[qwords++] = (((uint64_t) A5XX_HLSQ_DBG_READ_SEL << 44)) |
+ (1 << 21) | 1;
+
+ /* Read all the data in one chunk */
+ ptr[qwords++] = registers.gpuaddr + *offset;
+ ptr[qwords++] =
+ (((uint64_t) A5XX_HLSQ_DBG_AHB_READ_APERTURE << 44)) |
+ regs->size;
+
+ /* Remember the offset of the first bank for easy access */
+ regs->offset = *offset;
+
+ *offset += regs->size * sizeof(unsigned int);
+
+ return qwords;
+}
+
void a5xx_crashdump_init(struct adreno_device *adreno_dev)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
@@ -954,6 +1022,11 @@ void a5xx_crashdump_init(struct adreno_device *adreno_dev)
data_size += a5xx_shader_blocks[i].sz * sizeof(unsigned int) *
A5XX_NUM_SHADER_BANKS;
}
+ for (i = 0; i < ARRAY_SIZE(a5xx_hlsq_sp_tp_registers); i++) {
+ script_size += 32;
+ data_size +=
+ a5xx_hlsq_sp_tp_registers[i].size * sizeof(unsigned int);
+ }
/* Now allocate the script and data buffers */
@@ -968,7 +1041,6 @@ void a5xx_crashdump_init(struct adreno_device *adreno_dev)
kgsl_free_global(KGSL_DEVICE(adreno_dev), &capturescript);
return;
}
-
/* Build the crash script */
ptr = (uint64_t *) capturescript.hostptr;
@@ -987,9 +1059,13 @@ void a5xx_crashdump_init(struct adreno_device *adreno_dev)
/* Program each shader block */
for (i = 0; i < ARRAY_SIZE(a5xx_shader_blocks); i++) {
- ptr += _a5xx_crashdump_init(&a5xx_shader_blocks[i], ptr,
+ ptr += _a5xx_crashdump_init_shader(&a5xx_shader_blocks[i], ptr,
&offset);
}
+ /* Program the hlsq sp tp register sets */
+ for (i = 0; i < ARRAY_SIZE(a5xx_hlsq_sp_tp_registers); i++)
+ ptr += _a5xx_crashdump_init_hlsq(&a5xx_hlsq_sp_tp_registers[i],
+ ptr, &offset);
*ptr++ = 0;
*ptr++ = 0;
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index ac3805800691..5d3b2b8a7266 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -284,6 +284,7 @@ static void _retire_marker(struct kgsl_cmdbatch *cmdbatch)
struct kgsl_context *context = cmdbatch->context;
struct adreno_context *drawctxt = ADRENO_CONTEXT(cmdbatch->context);
struct kgsl_device *device = context->device;
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
/*
* Write the start and end timestamp to the memstore to keep the
@@ -301,7 +302,16 @@ static void _retire_marker(struct kgsl_cmdbatch *cmdbatch)
/* Retire pending GPU events for the object */
kgsl_process_event_group(device, &context->events);
- trace_adreno_cmdbatch_retired(cmdbatch, -1, 0, 0, drawctxt->rb,
+ /*
+ * For A3xx we still get the rptr from the CP_RB_RPTR instead of
+ * rptr scratch out address. At this point GPU clocks turned off.
+ * So avoid reading GPU register directly for A3xx.
+ */
+ if (adreno_is_a3xx(adreno_dev))
+ trace_adreno_cmdbatch_retired(cmdbatch, -1, 0, 0, drawctxt->rb,
+ 0);
+ else
+ trace_adreno_cmdbatch_retired(cmdbatch, -1, 0, 0, drawctxt->rb,
adreno_get_rptr(drawctxt->rb));
kgsl_cmdbatch_destroy(cmdbatch);
}
@@ -613,12 +623,13 @@ static int sendcmd(struct adreno_device *adreno_dev,
}
}
- mutex_unlock(&device->mutex);
if (ret) {
dispatcher->inflight--;
dispatch_q->inflight--;
+ mutex_unlock(&device->mutex);
+
/*
* Don't log a message in case of:
* -ENOENT means that the context was detached before the
@@ -642,6 +653,8 @@ static int sendcmd(struct adreno_device *adreno_dev,
time.ticks, (unsigned long) secs, nsecs / 1000, drawctxt->rb,
adreno_get_rptr(drawctxt->rb));
+ mutex_unlock(&device->mutex);
+
cmdbatch->submit_ticks = time.ticks;
dispatch_q->cmd_q[dispatch_q->tail] = cmdbatch;
@@ -1923,9 +1936,20 @@ static void retire_cmdbatch(struct adreno_device *adreno_dev,
if (test_bit(CMDBATCH_FLAG_PROFILE, &cmdbatch->priv))
cmdbatch_profile_ticks(adreno_dev, cmdbatch, &start, &end);
- trace_adreno_cmdbatch_retired(cmdbatch, (int) dispatcher->inflight,
- start, end, ADRENO_CMDBATCH_RB(cmdbatch),
- adreno_get_rptr(drawctxt->rb));
+ /*
+ * For A3xx we still get the rptr from the CP_RB_RPTR instead of
+ * rptr scratch out address. At this point GPU clocks turned off.
+ * So avoid reading GPU register directly for A3xx.
+ */
+ if (adreno_is_a3xx(adreno_dev))
+ trace_adreno_cmdbatch_retired(cmdbatch,
+ (int) dispatcher->inflight, start, end,
+ ADRENO_CMDBATCH_RB(cmdbatch), 0);
+ else
+ trace_adreno_cmdbatch_retired(cmdbatch,
+ (int) dispatcher->inflight, start, end,
+ ADRENO_CMDBATCH_RB(cmdbatch),
+ adreno_get_rptr(drawctxt->rb));
drawctxt->submit_retire_ticks[drawctxt->ticks_index] =
end - cmdbatch->submit_ticks;
@@ -2099,19 +2123,18 @@ static void adreno_dispatcher_work(struct work_struct *work)
break;
}
+ kgsl_process_event_groups(device);
+
/*
* dispatcher_do_fault() returns 0 if no faults occurred. If that is the
* case, then clean up preemption and try to schedule more work
*/
if (dispatcher_do_fault(adreno_dev) == 0) {
+
/* Clean up after preemption */
if (gpudev->preemption_schedule)
gpudev->preemption_schedule(adreno_dev);
- /* Re-kick the event engine to catch stragglers */
- if (dispatcher->inflight == 0 && count != 0)
- kgsl_schedule_work(&device->event_work);
-
/* Run the scheduler for to dispatch new commands */
_adreno_dispatcher_issuecmds(adreno_dev);
}
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index b069b16c75ef..0eff3da0e494 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -1118,7 +1118,8 @@ static const struct adreno_vbif_snapshot_registers *vbif_registers(
adreno_readreg(adreno_dev, ADRENO_REG_VBIF_VERSION, &version);
for (i = 0; i < count; i++) {
- if (list[i].version == version)
+ if ((list[i].version & list[i].mask) ==
+ (version & list[i].mask))
return &list[i];
}
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index f77dbb7f20af..c203ac7bfe8c 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -2144,8 +2144,8 @@ static int kgsl_setup_dmabuf_useraddr(struct kgsl_device *device,
}
up_read(&current->mm->mmap_sem);
- if (dmabuf == NULL)
- return -ENODEV;
+ if (IS_ERR_OR_NULL(dmabuf))
+ return dmabuf ? PTR_ERR(dmabuf) : -ENODEV;
ret = kgsl_setup_dma_buf(device, pagetable, entry, dmabuf);
if (ret) {
@@ -2249,7 +2249,7 @@ static long _gpuobj_map_dma_buf(struct kgsl_device *device,
if (ret)
return ret;
- if (buf.fd == 0)
+ if (buf.fd < 0)
return -EINVAL;
*fd = buf.fd;
@@ -3663,19 +3663,15 @@ static int kgsl_mmap(struct file *file, struct vm_area_struct *vma)
if (cache == KGSL_CACHEMODE_WRITEBACK
|| cache == KGSL_CACHEMODE_WRITETHROUGH) {
- struct scatterlist *s;
int i;
unsigned long addr = vma->vm_start;
+ struct kgsl_memdesc *m = &entry->memdesc;
+
+ for (i = 0; i < m->page_count; i++) {
+ struct page *page = m->pages[i];
- for_each_sg(entry->memdesc.sgt->sgl, s,
- entry->memdesc.sgt->nents, i) {
- int j;
- for (j = 0; j < (s->length >> PAGE_SHIFT); j++) {
- struct page *page = sg_page(s);
- page = nth_page(page, j);
- vm_insert_page(vma, addr, page);
- addr += PAGE_SIZE;
- }
+ vm_insert_page(vma, addr, page);
+ addr += PAGE_SIZE;
}
}
@@ -3913,7 +3909,7 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
goto error_close_mmu;
status = kgsl_allocate_global(device, &device->memstore,
- KGSL_MEMSTORE_SIZE, 0, 0);
+ KGSL_MEMSTORE_SIZE, 0, KGSL_MEMDESC_CONTIG);
if (status != 0)
goto error_close_mmu;
@@ -3957,8 +3953,8 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
PM_QOS_DEFAULT_VALUE);
}
-
- device->events_wq = create_singlethread_workqueue("kgsl-events");
+ device->events_wq = alloc_workqueue("kgsl-events",
+ WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
/* Initalize the snapshot engine */
kgsl_device_snapshot_init(device);
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index c172021c8944..ee7149e1fd41 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -163,6 +163,8 @@ struct kgsl_memdesc_ops {
#define KGSL_MEMDESC_PRIVILEGED BIT(6)
/* The memdesc is TZ locked content protection */
#define KGSL_MEMDESC_TZ_LOCKED BIT(7)
+/* The memdesc is allocated through contiguous memory */
+#define KGSL_MEMDESC_CONTIG BIT(8)
/**
* struct kgsl_memdesc - GPU memory object descriptor
@@ -179,8 +181,9 @@ struct kgsl_memdesc_ops {
* @ops: Function hooks for the memdesc memory type
* @flags: Flags set from userspace
* @dev: Pointer to the struct device that owns this memory
- * @memmap: bitmap of pages for mmapsize
- * @memmap_len: Number of bits for memmap
+ * @attrs: dma attributes for this memory
+ * @pages: An array of pointers to allocated pages
+ * @page_count: Total number of pages allocated
*/
struct kgsl_memdesc {
struct kgsl_pagetable *pagetable;
@@ -197,6 +200,8 @@ struct kgsl_memdesc {
uint64_t flags;
struct device *dev;
struct dma_attrs attrs;
+ struct page **pages;
+ unsigned int page_count;
};
/*
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index 766cd811588c..93ac790f3a55 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -150,7 +150,7 @@ static int print_mem_entry(int id, void *ptr, void *data)
(unsigned long *) m->useraddr,
m->size, entry->id, flags,
memtype_str(kgsl_memdesc_usermem_type(m)),
- usage, m->sgt->nents, m->mapsize);
+ usage, (m->sgt ? m->sgt->nents : 0), m->mapsize);
if (entry->metadata[0] != 0)
seq_printf(s, " %s", entry->metadata);
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 4159a5fe375f..f55b795b1d2b 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -272,7 +272,6 @@ struct kgsl_device {
int mem_log;
int pwr_log;
struct kgsl_pwrscale pwrscale;
- struct work_struct event_work;
int reset_counter; /* Track how many GPU core resets have occured */
int cff_dump_enable;
@@ -292,8 +291,6 @@ struct kgsl_device {
.cmdbatch_gate = COMPLETION_INITIALIZER((_dev).cmdbatch_gate),\
.idle_check_ws = __WORK_INITIALIZER((_dev).idle_check_ws,\
kgsl_idle_check),\
- .event_work = __WORK_INITIALIZER((_dev).event_work,\
- kgsl_process_events),\
.context_idr = IDR_INIT((_dev).context_idr),\
.wait_queue = __WAIT_QUEUE_HEAD_INITIALIZER((_dev).wait_queue),\
.active_cnt_wq = __WAIT_QUEUE_HEAD_INITIALIZER((_dev).active_cnt_wq),\
@@ -602,7 +599,7 @@ void kgsl_process_event_group(struct kgsl_device *device,
struct kgsl_event_group *group);
void kgsl_flush_event_group(struct kgsl_device *device,
struct kgsl_event_group *group);
-void kgsl_process_events(struct work_struct *work);
+void kgsl_process_event_groups(struct kgsl_device *device);
void kgsl_context_destroy(struct kref *kref);
diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c
index 6f70b9ddd376..6e8abf36c50f 100644
--- a/drivers/gpu/msm/kgsl_events.c
+++ b/drivers/gpu/msm/kgsl_events.c
@@ -314,22 +314,16 @@ EXPORT_SYMBOL(kgsl_add_event);
static DEFINE_RWLOCK(group_lock);
static LIST_HEAD(group_list);
-/**
- * kgsl_process_events() - Work queue for processing new timestamp events
- * @work: Pointer to a work_struct
- */
-void kgsl_process_events(struct work_struct *work)
+void kgsl_process_event_groups(struct kgsl_device *device)
{
struct kgsl_event_group *group;
- struct kgsl_device *device = container_of(work, struct kgsl_device,
- event_work);
read_lock(&group_lock);
list_for_each_entry(group, &group_list, group)
_process_event_group(device, group, false);
read_unlock(&group_lock);
}
-EXPORT_SYMBOL(kgsl_process_events);
+EXPORT_SYMBOL(kgsl_process_event_groups);
/**
* kgsl_del_event_group() - Remove a GPU event group
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 865cd9d8f498..b467ef81d257 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -1627,16 +1627,34 @@ kgsl_iommu_map(struct kgsl_pagetable *pt,
uint64_t addr = memdesc->gpuaddr;
uint64_t size = memdesc->size;
unsigned int flags = _get_protection_flags(memdesc);
+ struct sg_table *sgt = NULL;
- ret = _iommu_map_sg_sync_pc(pt, addr, memdesc, memdesc->sgt->sgl,
- memdesc->sgt->nents, flags);
+ /*
+ * For paged memory allocated through kgsl, memdesc->pages is not NULL.
+ * Allocate sgt here just for its map operation. Contiguous memory
+ * already has its sgt, so no need to allocate it here.
+ */
+ if (memdesc->pages != NULL)
+ sgt = kgsl_alloc_sgt_from_pages(memdesc);
+ else
+ sgt = memdesc->sgt;
+
+ if (IS_ERR(sgt))
+ return PTR_ERR(sgt);
+
+ ret = _iommu_map_sg_sync_pc(pt, addr, memdesc, sgt->sgl,
+ sgt->nents, flags);
if (ret)
- return ret;
+ goto done;
ret = _iommu_map_guard_page(pt, memdesc, addr + size, flags);
if (ret)
_iommu_unmap_sync_pc(pt, memdesc, addr, size);
+done:
+ if (memdesc->pages != NULL)
+ kgsl_free_sgt(sgt);
+
return ret;
}
@@ -1647,6 +1665,8 @@ static int kgsl_iommu_map_offset(struct kgsl_pagetable *pt,
{
int pg_sz;
unsigned int protflags = _get_protection_flags(memdesc);
+ int ret;
+ struct sg_table *sgt = NULL;
pg_sz = (1 << kgsl_memdesc_get_align(memdesc));
if (!IS_ALIGNED(virtaddr | virtoffset | physoffset | size, pg_sz))
@@ -1655,9 +1675,27 @@ static int kgsl_iommu_map_offset(struct kgsl_pagetable *pt,
if (size == 0)
return -EINVAL;
- return _iommu_map_sg_offset_sync_pc(pt, virtaddr + virtoffset,
- memdesc, memdesc->sgt->sgl, memdesc->sgt->nents,
- physoffset, size, protflags);
+ /*
+ * For paged memory allocated through kgsl, memdesc->pages is not NULL.
+ * Allocate sgt here just for its map operation. Contiguous memory
+ * already has its sgt, so no need to allocate it here.
+ */
+ if (memdesc->pages != NULL)
+ sgt = kgsl_alloc_sgt_from_pages(memdesc);
+ else
+ sgt = memdesc->sgt;
+
+ if (IS_ERR(sgt))
+ return PTR_ERR(sgt);
+
+ ret = _iommu_map_sg_offset_sync_pc(pt, virtaddr + virtoffset,
+ memdesc, sgt->sgl, sgt->nents,
+ physoffset, size, protflags);
+
+ if (memdesc->pages != NULL)
+ kgsl_free_sgt(sgt);
+
+ return ret;
}
/* This function must be called with context bank attached */
diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c
index 7fb3b37ac191..7967b19779db 100644
--- a/drivers/gpu/msm/kgsl_pool.c
+++ b/drivers/gpu/msm/kgsl_pool.c
@@ -263,6 +263,31 @@ void kgsl_pool_free_sgt(struct sg_table *sgt)
}
}
+/**
+ * kgsl_pool_free_pages() - Free pages in the pages array
+ * @pages: pointer of the pages array
+ *
+ * Free the pages by collapsing any physical adjacent pages.
+ * Pages are added back to the pool, if pool has sufficient space
+ * otherwise they are given back to system.
+ */
+void kgsl_pool_free_pages(struct page **pages, unsigned int pcount)
+{
+ int i;
+
+ if (pages == NULL || pcount == 0)
+ return;
+
+ for (i = 0; i < pcount;) {
+ /*
+ * Free each page or compound page group individually.
+ */
+ struct page *p = pages[i];
+
+ i += 1 << compound_order(p);
+ kgsl_pool_free_page(p);
+ }
+}
static int kgsl_pool_idx_lookup(unsigned int order)
{
int i;
diff --git a/drivers/gpu/msm/kgsl_pool.h b/drivers/gpu/msm/kgsl_pool.h
index f2cdda19140b..efbfa96f1498 100644
--- a/drivers/gpu/msm/kgsl_pool.h
+++ b/drivers/gpu/msm/kgsl_pool.h
@@ -34,6 +34,7 @@ kgsl_gfp_mask(unsigned int page_order)
}
void kgsl_pool_free_sgt(struct sg_table *sgt);
+void kgsl_pool_free_pages(struct page **pages, unsigned int page_count);
void kgsl_init_page_pools(void);
void kgsl_exit_page_pools(void);
int kgsl_pool_alloc_page(int *page_size, struct page **pages,
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 2b9eef8b6351..11b323e9d40c 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -363,6 +363,8 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
if (new_level == old_level)
return;
+ kgsl_pwrscale_update_stats(device);
+
/*
* Set the active and previous powerlevel first in case the clocks are
* off - if we don't do this then the pwrlevel change won't take effect
@@ -934,6 +936,31 @@ static ssize_t kgsl_pwrctrl_gpu_available_frequencies_show(
return num_chars;
}
+static ssize_t kgsl_pwrctrl_gpu_clock_stats_show(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ struct kgsl_pwrctrl *pwr;
+ int index, num_chars = 0;
+
+ if (device == NULL)
+ return 0;
+ pwr = &device->pwrctrl;
+ mutex_lock(&device->mutex);
+ kgsl_pwrscale_update_stats(device);
+ mutex_unlock(&device->mutex);
+ for (index = 0; index < pwr->num_pwrlevels - 1; index++)
+ num_chars += snprintf(buf + num_chars, PAGE_SIZE - num_chars,
+ "%llu ", pwr->clock_times[index]);
+
+ if (num_chars < PAGE_SIZE)
+ buf[num_chars++] = '\n';
+
+ return num_chars;
+}
+
static ssize_t kgsl_pwrctrl_reset_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -1201,6 +1228,9 @@ static DEVICE_ATTR(gpubusy, 0444, kgsl_pwrctrl_gpubusy_show,
static DEVICE_ATTR(gpu_available_frequencies, 0444,
kgsl_pwrctrl_gpu_available_frequencies_show,
NULL);
+static DEVICE_ATTR(gpu_clock_stats, 0444,
+ kgsl_pwrctrl_gpu_clock_stats_show,
+ NULL);
static DEVICE_ATTR(max_pwrlevel, 0644,
kgsl_pwrctrl_max_pwrlevel_show,
kgsl_pwrctrl_max_pwrlevel_store);
@@ -1249,6 +1279,7 @@ static const struct device_attribute *pwrctrl_attr_list[] = {
&dev_attr_deep_nap_timer,
&dev_attr_gpubusy,
&dev_attr_gpu_available_frequencies,
+ &dev_attr_gpu_clock_stats,
&dev_attr_max_pwrlevel,
&dev_attr_min_pwrlevel,
&dev_attr_thermal_pwrlevel,
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index 0029c389484f..8fd06531aa81 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -122,6 +122,7 @@ struct kgsl_regulator {
* @min_pwrlevel - minimum allowable powerlevel per the user
* @num_pwrlevels - number of available power levels
* @interval_timeout - timeout in jiffies to be idle before a power event
+ * @clock_times - Each GPU frequency's accumulated active time in us
* @strtstp_sleepwake - true if the device supports low latency GPU start/stop
* @regulators - array of pointers to kgsl_regulator structs
* @pcl - bus scale identifier
@@ -178,6 +179,7 @@ struct kgsl_pwrctrl {
unsigned int min_pwrlevel;
unsigned int num_pwrlevels;
unsigned long interval_timeout;
+ u64 clock_times[KGSL_MAX_PWRLEVELS];
bool strtstp_sleepwake;
struct kgsl_regulator regulators[KGSL_MAX_REGULATORS];
uint32_t pcl;
diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c
index 4f6677d9a1de..d90aec42f30a 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.c
+++ b/drivers/gpu/msm/kgsl_pwrscale.c
@@ -127,6 +127,7 @@ EXPORT_SYMBOL(kgsl_pwrscale_busy);
*/
void kgsl_pwrscale_update_stats(struct kgsl_device *device)
{
+ struct kgsl_pwrctrl *pwrctrl = &device->pwrctrl;
struct kgsl_pwrscale *psc = &device->pwrscale;
BUG_ON(!mutex_is_locked(&device->mutex));
@@ -150,6 +151,8 @@ void kgsl_pwrscale_update_stats(struct kgsl_device *device)
device->pwrscale.accum_stats.busy_time += stats.busy_time;
device->pwrscale.accum_stats.ram_time += stats.ram_time;
device->pwrscale.accum_stats.ram_wait += stats.ram_wait;
+ pwrctrl->clock_times[pwrctrl->active_pwrlevel] +=
+ stats.busy_time;
}
}
EXPORT_SYMBOL(kgsl_pwrscale_update_stats);
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index 50dcd39fac58..73edc3f7e146 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -313,10 +313,6 @@ kgsl_sharedmem_init_sysfs(void)
drv_attr_list);
}
-static int kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
- struct kgsl_pagetable *pagetable,
- uint64_t size);
-
static int kgsl_cma_alloc_secure(struct kgsl_device *device,
struct kgsl_memdesc *memdesc, uint64_t size);
@@ -358,8 +354,7 @@ static int kgsl_page_alloc_vmfault(struct kgsl_memdesc *memdesc,
struct vm_area_struct *vma,
struct vm_fault *vmf)
{
- int i, pgoff;
- struct scatterlist *s = memdesc->sgt->sgl;
+ int pgoff;
unsigned int offset;
offset = ((unsigned long) vmf->virtual_address - vma->vm_start);
@@ -369,30 +364,15 @@ static int kgsl_page_alloc_vmfault(struct kgsl_memdesc *memdesc,
pgoff = offset >> PAGE_SHIFT;
- /*
- * The sglist might be comprised of mixed blocks of memory depending
- * on how many 64K pages were allocated. This means we have to do math
- * to find the actual 4K page to map in user space
- */
-
- for (i = 0; i < memdesc->sgt->nents; i++) {
- int npages = s->length >> PAGE_SHIFT;
-
- if (pgoff < npages) {
- struct page *page = sg_page(s);
+ if (pgoff < memdesc->page_count) {
+ struct page *page = memdesc->pages[pgoff];
- page = nth_page(page, pgoff);
+ get_page(page);
+ vmf->page = page;
- get_page(page);
- vmf->page = page;
+ memdesc->mapsize += PAGE_SIZE;
- memdesc->mapsize += PAGE_SIZE;
-
- return 0;
- }
-
- pgoff -= npages;
- s = sg_next(s);
+ return 0;
}
return VM_FAULT_SIGBUS;
@@ -455,9 +435,15 @@ static void kgsl_page_alloc_free(struct kgsl_memdesc *memdesc)
for_each_sg_page(memdesc->sgt->sgl, &sg_iter,
memdesc->sgt->nents, 0)
ClearPagePrivate(sg_page_iter_page(&sg_iter));
+
}
- kgsl_pool_free_sgt(memdesc->sgt);
+ /* Free pages using the pages array for non secure paged memory */
+ if (memdesc->pages != NULL)
+ kgsl_pool_free_pages(memdesc->pages, memdesc->page_count);
+ else
+ kgsl_pool_free_sgt(memdesc->sgt);
+
}
/*
@@ -477,31 +463,10 @@ static int kgsl_page_alloc_map_kernel(struct kgsl_memdesc *memdesc)
return -ENOMEM;
mutex_lock(&kernel_map_global_lock);
- if (!memdesc->hostptr) {
+ if ((!memdesc->hostptr) && (memdesc->pages != NULL)) {
pgprot_t page_prot = pgprot_writecombine(PAGE_KERNEL);
- struct page **pages = NULL;
- struct scatterlist *sg;
- int npages = PAGE_ALIGN(memdesc->size) >> PAGE_SHIFT;
- int sglen = memdesc->sgt->nents;
- int i, count = 0;
-
- /* create a list of pages to call vmap */
- pages = kgsl_malloc(npages * sizeof(struct page *));
- if (pages == NULL) {
- ret = -ENOMEM;
- goto done;
- }
-
- for_each_sg(memdesc->sgt->sgl, sg, sglen, i) {
- struct page *page = sg_page(sg);
- int j;
-
- for (j = 0; j < sg->length >> PAGE_SHIFT; j++)
- pages[count++] = page++;
- }
-
- memdesc->hostptr = vmap(pages, count,
+ memdesc->hostptr = vmap(memdesc->pages, memdesc->page_count,
VM_IOREMAP, page_prot);
if (memdesc->hostptr)
KGSL_STATS_ADD(memdesc->size,
@@ -509,11 +474,10 @@ static int kgsl_page_alloc_map_kernel(struct kgsl_memdesc *memdesc)
&kgsl_driver.stats.vmalloc_max);
else
ret = -ENOMEM;
- kgsl_free(pages);
}
if (memdesc->hostptr)
memdesc->hostptr_count++;
-done:
+
mutex_unlock(&kernel_map_global_lock);
return ret;
@@ -672,7 +636,7 @@ static inline int get_page_size(size_t size, unsigned int align)
}
#endif
-static int
+int
kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
uint64_t size)
@@ -681,7 +645,6 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
unsigned int j, page_size, len_alloc;
unsigned int pcount = 0;
size_t len;
- struct page **pages = NULL;
unsigned int align;
size = PAGE_ALIGN(size);
@@ -712,18 +675,17 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
memdesc->pagetable = pagetable;
memdesc->ops = &kgsl_page_alloc_ops;
- memdesc->sgt = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
- if (memdesc->sgt == NULL)
- return -ENOMEM;
-
/*
- * Allocate space to store the list of pages to send to vmap. This is an
- * array of pointers so we can track 1024 pages per page of allocation
+ * Allocate space to store the list of pages. This is an array of
+ * pointers so we can track 1024 pages per page of allocation.
+ * Keep this array around for non global non secure buffers that
+ * are allocated by kgsl. This helps with improving the vm fault
+ * routine by finding the faulted page in constant time.
*/
- pages = kgsl_malloc(len_alloc * sizeof(struct page *));
+ memdesc->pages = kgsl_malloc(len_alloc * sizeof(struct page *));
- if (pages == NULL) {
+ if (memdesc->pages == NULL) {
ret = -ENOMEM;
goto done;
}
@@ -734,9 +696,9 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
int page_count;
page_count = kgsl_pool_alloc_page(&page_size,
- pages + pcount, len_alloc - pcount,
+ memdesc->pages + pcount,
+ len_alloc - pcount,
&align);
-
if (page_count <= 0) {
if (page_count == -EAGAIN)
continue;
@@ -760,16 +722,12 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
pcount += page_count;
len -= page_size;
memdesc->size += page_size;
+ memdesc->page_count += page_count;
/* Get the needed page size for the next iteration */
page_size = get_page_size(len, align);
}
- ret = sg_alloc_table_from_pages(memdesc->sgt, pages, pcount, 0,
- memdesc->size, GFP_KERNEL);
- if (ret)
- goto done;
-
/* Call to the hypervisor to lock any secure buffer allocations */
if (memdesc->flags & KGSL_MEMFLAGS_SECURE) {
unsigned int i;
@@ -778,10 +736,27 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
int source_vm = VMID_HLOS;
int dest_vm = VMID_CP_PIXEL;
+ memdesc->sgt = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (memdesc->sgt == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ ret = sg_alloc_table_from_pages(memdesc->sgt, memdesc->pages,
+ memdesc->page_count, 0, memdesc->size, GFP_KERNEL);
+ if (ret) {
+ kfree(memdesc->sgt);
+ goto done;
+ }
+
ret = hyp_assign_table(memdesc->sgt, &source_vm, 1,
&dest_vm, &dest_perms, 1);
- if (ret)
+ if (ret) {
+ sg_free_table(memdesc->sgt);
+ kfree(memdesc->sgt);
+ memdesc->sgt = NULL;
goto done;
+ }
/* Set private bit for each sg to indicate that its secured */
for_each_sg(memdesc->sgt->sgl, sg, memdesc->sgt->nents, i)
@@ -793,6 +768,14 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
KGSL_STATS_ADD(memdesc->size, &kgsl_driver.stats.secure,
&kgsl_driver.stats.secure_max);
+ /*
+ * We don't need the array for secure buffers because they are
+ * not mapped to CPU
+ */
+ kgsl_free(memdesc->pages);
+ memdesc->pages = NULL;
+ memdesc->page_count = 0;
+
/* Don't map and zero the locked secure buffer */
goto done;
}
@@ -802,19 +785,18 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
done:
if (ret) {
- if (pages) {
+ if (memdesc->pages) {
unsigned int count = 1;
for (j = 0; j < pcount; j += count) {
- count = 1 << compound_order(pages[j]);
- kgsl_pool_free_page(pages[j]);
+ count = 1 << compound_order(memdesc->pages[j]);
+ kgsl_pool_free_page(memdesc->pages[j]);
}
}
- kfree(memdesc->sgt);
+ kgsl_free(memdesc->pages);
memset(memdesc, 0, sizeof(*memdesc));
}
- kgsl_free(pages);
return ret;
}
@@ -837,6 +819,9 @@ void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc)
kfree(memdesc->sgt);
}
+ if (memdesc->pages)
+ kgsl_free(memdesc->pages);
+
memset(memdesc, 0, sizeof(*memdesc));
}
EXPORT_SYMBOL(kgsl_sharedmem_free);
diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h
index 5093ebd6e51a..c05aaecb5284 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.h
+++ b/drivers/gpu/msm/kgsl_sharedmem.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-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
@@ -71,6 +71,10 @@ int kgsl_allocate_user(struct kgsl_device *device,
void kgsl_get_memory_usage(char *str, size_t len, uint64_t memflags);
+int kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
+ struct kgsl_pagetable *pagetable,
+ uint64_t size);
+
#define MEMFLAGS(_flags, _mask, _shift) \
((unsigned int) (((_flags) & (_mask)) >> (_shift)))
@@ -266,7 +270,16 @@ static inline int kgsl_allocate_global(struct kgsl_device *device,
memdesc->flags = flags;
memdesc->priv = priv;
- ret = kgsl_sharedmem_alloc_contig(device, memdesc, NULL, (size_t) size);
+ if ((memdesc->priv & KGSL_MEMDESC_CONTIG) != 0)
+ ret = kgsl_sharedmem_alloc_contig(device, memdesc, NULL,
+ (size_t) size);
+ else {
+ ret = kgsl_sharedmem_page_alloc_user(memdesc, NULL,
+ (size_t) size);
+ if (ret == 0)
+ kgsl_memdesc_map(memdesc);
+ }
+
if (ret == 0)
kgsl_mmu_add_global(device, memdesc);
@@ -293,4 +306,47 @@ static inline void kgsl_free_global(struct kgsl_device *device,
void kgsl_sharedmem_set_noretry(bool val);
bool kgsl_sharedmem_get_noretry(void);
+/**
+ * kgsl_alloc_sgt_from_pages() - Allocate a sg table
+ *
+ * @memdesc: memory descriptor of the allocation
+ *
+ * Allocate and return pointer to a sg table
+ */
+static inline struct sg_table *kgsl_alloc_sgt_from_pages(
+ struct kgsl_memdesc *m)
+{
+ int ret;
+ struct sg_table *sgt;
+
+ sgt = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (sgt == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ ret = sg_alloc_table_from_pages(sgt, m->pages, m->page_count, 0,
+ m->size, GFP_KERNEL);
+ if (ret) {
+ kfree(sgt);
+ return ERR_PTR(ret);
+ }
+
+ return sgt;
+}
+
+/**
+ * kgsl_free_sgt() - Free a sg table structure
+ *
+ * @sgt: sg table pointer to be freed
+ *
+ * Free the sg table allocated using sgt and free the
+ * sgt structure itself
+ */
+static inline void kgsl_free_sgt(struct sg_table *sgt)
+{
+ if (sgt != NULL) {
+ sg_free_table(sgt);
+ kfree(sgt);
+ }
+}
+
#endif /* __KGSL_SHAREDMEM_H */
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index c6f7a694f67a..ec791e169f8f 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1897,6 +1897,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) },
@@ -2615,9 +2616,10 @@ int hid_add_device(struct hid_device *hdev)
/*
* Scan generic devices for group information
*/
- if (hid_ignore_special_drivers ||
- (!hdev->group &&
- !hid_match_id(hdev, hid_have_special_driver))) {
+ if (hid_ignore_special_drivers) {
+ hdev->group = HID_GROUP_GENERIC;
+ } else if (!hdev->group &&
+ !hid_match_id(hdev, hid_have_special_driver)) {
ret = hid_scan_report(hdev);
if (ret)
hid_warn(hdev, "bad device descriptor (%d)\n", ret);
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 591d4ad7708f..7ecd96bdf834 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -396,6 +396,11 @@ static void mt_feature_mapping(struct hid_device *hdev,
td->is_buttonpad = true;
break;
+ case 0xff0000c5:
+ /* Retrieve the Win8 blob once to enable some devices */
+ if (usage->usage_index == 0)
+ mt_get_feature(hdev, field->report);
+ break;
}
}
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index 10bd8e6e4c9c..0b80633bae91 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -282,17 +282,21 @@ static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType,
u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister);
u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength);
+ u16 size;
+ int args_len;
+ int index = 0;
+
+ i2c_hid_dbg(ihid, "%s\n", __func__);
+
+ if (data_len > ihid->bufsize)
+ return -EINVAL;
- /* hid_hw_* already checked that data_len < HID_MAX_BUFFER_SIZE */
- u16 size = 2 /* size */ +
+ size = 2 /* size */ +
(reportID ? 1 : 0) /* reportID */ +
data_len /* buf */;
- int args_len = (reportID >= 0x0F ? 1 : 0) /* optional third byte */ +
+ args_len = (reportID >= 0x0F ? 1 : 0) /* optional third byte */ +
2 /* dataRegister */ +
size /* args */;
- int index = 0;
-
- i2c_hid_dbg(ihid, "%s\n", __func__);
if (!use_data && maxOutputLength == 0)
return -ENOSYS;
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 5dd426fee8cc..0df32fe0e345 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -951,14 +951,6 @@ static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count)
return ret;
}
-static void usbhid_restart_queues(struct usbhid_device *usbhid)
-{
- if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl))
- usbhid_restart_out_queue(usbhid);
- if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
- usbhid_restart_ctrl_queue(usbhid);
-}
-
static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
{
struct usbhid_device *usbhid = hid->driver_data;
@@ -1404,6 +1396,37 @@ static void hid_cease_io(struct usbhid_device *usbhid)
usb_kill_urb(usbhid->urbout);
}
+static void hid_restart_io(struct hid_device *hid)
+{
+ struct usbhid_device *usbhid = hid->driver_data;
+ int clear_halt = test_bit(HID_CLEAR_HALT, &usbhid->iofl);
+ int reset_pending = test_bit(HID_RESET_PENDING, &usbhid->iofl);
+
+ spin_lock_irq(&usbhid->lock);
+ clear_bit(HID_SUSPENDED, &usbhid->iofl);
+ usbhid_mark_busy(usbhid);
+
+ if (clear_halt || reset_pending)
+ schedule_work(&usbhid->reset_work);
+ usbhid->retry_delay = 0;
+ spin_unlock_irq(&usbhid->lock);
+
+ if (reset_pending || !test_bit(HID_STARTED, &usbhid->iofl))
+ return;
+
+ if (!clear_halt) {
+ if (hid_start_in(hid) < 0)
+ hid_io_error(hid);
+ }
+
+ spin_lock_irq(&usbhid->lock);
+ if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl))
+ usbhid_restart_out_queue(usbhid);
+ if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
+ usbhid_restart_ctrl_queue(usbhid);
+ spin_unlock_irq(&usbhid->lock);
+}
+
/* Treat USB reset pretty much the same as suspend/resume */
static int hid_pre_reset(struct usb_interface *intf)
{
@@ -1453,14 +1476,14 @@ static int hid_post_reset(struct usb_interface *intf)
return 1;
}
+ /* No need to do another reset or clear a halted endpoint */
spin_lock_irq(&usbhid->lock);
clear_bit(HID_RESET_PENDING, &usbhid->iofl);
+ clear_bit(HID_CLEAR_HALT, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
- status = hid_start_in(hid);
- if (status < 0)
- hid_io_error(hid);
- usbhid_restart_queues(usbhid);
+
+ hid_restart_io(hid);
return 0;
}
@@ -1483,25 +1506,9 @@ void usbhid_put_power(struct hid_device *hid)
#ifdef CONFIG_PM
static int hid_resume_common(struct hid_device *hid, bool driver_suspended)
{
- struct usbhid_device *usbhid = hid->driver_data;
- int status;
-
- spin_lock_irq(&usbhid->lock);
- clear_bit(HID_SUSPENDED, &usbhid->iofl);
- usbhid_mark_busy(usbhid);
-
- if (test_bit(HID_CLEAR_HALT, &usbhid->iofl) ||
- test_bit(HID_RESET_PENDING, &usbhid->iofl))
- schedule_work(&usbhid->reset_work);
- usbhid->retry_delay = 0;
-
- usbhid_restart_queues(usbhid);
- spin_unlock_irq(&usbhid->lock);
-
- status = hid_start_in(hid);
- if (status < 0)
- hid_io_error(hid);
+ int status = 0;
+ hid_restart_io(hid);
if (driver_suspended && hid->driver && hid->driver->resume)
status = hid->driver->resume(hid);
return status;
@@ -1570,12 +1577,8 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
static int hid_resume(struct usb_interface *intf)
{
struct hid_device *hid = usb_get_intfdata (intf);
- struct usbhid_device *usbhid = hid->driver_data;
int status;
- if (!test_bit(HID_STARTED, &usbhid->iofl))
- return 0;
-
status = hid_resume_common(hid, true);
dev_dbg(&intf->dev, "resume status %d\n", status);
return 0;
@@ -1584,10 +1587,8 @@ static int hid_resume(struct usb_interface *intf)
static int hid_reset_resume(struct usb_interface *intf)
{
struct hid_device *hid = usb_get_intfdata(intf);
- struct usbhid_device *usbhid = hid->driver_data;
int status;
- clear_bit(HID_SUSPENDED, &usbhid->iofl);
status = hid_post_reset(intf);
if (status >= 0 && hid->driver && hid->driver->reset_resume) {
int ret = hid->driver->reset_resume(hid);
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 01a4f05c1642..3c0f47ac8e53 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -2493,6 +2493,17 @@ void wacom_setup_device_quirks(struct wacom *wacom)
}
/*
+ * Hack for the Bamboo One:
+ * the device presents a PAD/Touch interface as most Bamboos and even
+ * sends ghosts PAD data on it. However, later, we must disable this
+ * ghost interface, and we can not detect it unless we set it here
+ * to WACOM_DEVICETYPE_PAD or WACOM_DEVICETYPE_TOUCH.
+ */
+ if (features->type == BAMBOO_PEN &&
+ features->pktlen == WACOM_PKGLEN_BBTOUCH3)
+ features->device_type |= WACOM_DEVICETYPE_PAD;
+
+ /*
* Raw Wacom-mode pen and touch events both come from interface
* 0, whose HID descriptor has an application usage of 0xFF0D
* (i.e., WACOM_VENDORDEFINED_PEN). We route pen packets back
diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c
index 36544c4f653c..303d0c9df907 100644
--- a/drivers/hwmon/max1111.c
+++ b/drivers/hwmon/max1111.c
@@ -85,6 +85,9 @@ static struct max1111_data *the_max1111;
int max1111_read_channel(int channel)
{
+ if (!the_max1111 || !the_max1111->spi)
+ return -ENODEV;
+
return max1111_read(&the_max1111->spi->dev, channel);
}
EXPORT_SYMBOL(max1111_read_channel);
@@ -258,6 +261,9 @@ static int max1111_remove(struct spi_device *spi)
{
struct max1111_data *data = spi_get_drvdata(spi);
+#ifdef CONFIG_SHARPSL_PM
+ the_max1111 = NULL;
+#endif
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group);
sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group);
diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
index a80f07b88b42..3a11b061e5b0 100644
--- a/drivers/hwtracing/coresight/coresight-tpdm.c
+++ b/drivers/hwtracing/coresight/coresight-tpdm.c
@@ -2647,11 +2647,11 @@ static ssize_t tpdm_store_dsb_edge_ctrl_mask(struct device *dev,
size_t size)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
- unsigned start, end, val;
+ unsigned long start, end, val;
uint32_t set;
int i, bit, reg;
- if (sscanf(buf, "%ui %ui %ui", &start, &end, &val) != 3)
+ if (sscanf(buf, "%lx %lx %lx", &start, &end, &val) != 3)
return -EINVAL;
if (!test_bit(TPDM_DS_DSB, drvdata->datasets) ||
(start >= TPDM_DSB_MAX_LINES) || (end >= TPDM_DSB_MAX_LINES))
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index cd4510a63375..146eed70bdf4 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -65,7 +65,7 @@
#include <asm/mwait.h>
#include <asm/msr.h>
-#define INTEL_IDLE_VERSION "0.4"
+#define INTEL_IDLE_VERSION "0.4.1"
#define PREFIX "intel_idle: "
static struct cpuidle_driver intel_idle_driver = {
@@ -994,36 +994,92 @@ static void intel_idle_cpuidle_devices_uninit(void)
}
/*
- * intel_idle_state_table_update()
- *
- * Update the default state_table for this CPU-id
+ * ivt_idle_state_table_update(void)
*
- * Currently used to access tuned IVT multi-socket targets
+ * Tune IVT multi-socket targets
* Assumption: num_sockets == (max_package_num + 1)
*/
-void intel_idle_state_table_update(void)
+static void ivt_idle_state_table_update(void)
{
/* IVT uses a different table for 1-2, 3-4, and > 4 sockets */
- if (boot_cpu_data.x86_model == 0x3e) { /* IVT */
- int cpu, package_num, num_sockets = 1;
-
- for_each_online_cpu(cpu) {
- package_num = topology_physical_package_id(cpu);
- if (package_num + 1 > num_sockets) {
- num_sockets = package_num + 1;
-
- if (num_sockets > 4) {
- cpuidle_state_table = ivt_cstates_8s;
- return;
- }
+ int cpu, package_num, num_sockets = 1;
+
+ for_each_online_cpu(cpu) {
+ package_num = topology_physical_package_id(cpu);
+ if (package_num + 1 > num_sockets) {
+ num_sockets = package_num + 1;
+
+ if (num_sockets > 4) {
+ cpuidle_state_table = ivt_cstates_8s;
+ return;
}
}
+ }
+
+ if (num_sockets > 2)
+ cpuidle_state_table = ivt_cstates_4s;
+
+ /* else, 1 and 2 socket systems use default ivt_cstates */
+}
+/*
+ * sklh_idle_state_table_update(void)
+ *
+ * On SKL-H (model 0x5e) disable C8 and C9 if:
+ * C10 is enabled and SGX disabled
+ */
+static void sklh_idle_state_table_update(void)
+{
+ unsigned long long msr;
+ unsigned int eax, ebx, ecx, edx;
+
+
+ /* if PC10 disabled via cmdline intel_idle.max_cstate=7 or shallower */
+ if (max_cstate <= 7)
+ return;
+
+ /* if PC10 not present in CPUID.MWAIT.EDX */
+ if ((mwait_substates & (0xF << 28)) == 0)
+ return;
+
+ rdmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr);
+
+ /* PC10 is not enabled in PKG C-state limit */
+ if ((msr & 0xF) != 8)
+ return;
+
+ ecx = 0;
+ cpuid(7, &eax, &ebx, &ecx, &edx);
+
+ /* if SGX is present */
+ if (ebx & (1 << 2)) {
- if (num_sockets > 2)
- cpuidle_state_table = ivt_cstates_4s;
- /* else, 1 and 2 socket systems use default ivt_cstates */
+ rdmsrl(MSR_IA32_FEATURE_CONTROL, msr);
+
+ /* if SGX is enabled */
+ if (msr & (1 << 18))
+ return;
+ }
+
+ skl_cstates[5].disabled = 1; /* C8-SKL */
+ skl_cstates[6].disabled = 1; /* C9-SKL */
+}
+/*
+ * intel_idle_state_table_update()
+ *
+ * Update the default state_table for this CPU-id
+ */
+
+static void intel_idle_state_table_update(void)
+{
+ switch (boot_cpu_data.x86_model) {
+
+ case 0x3e: /* IVT */
+ ivt_idle_state_table_update();
+ break;
+ case 0x5e: /* SKL-H */
+ sklh_idle_state_table_update();
+ break;
}
- return;
}
/*
@@ -1063,6 +1119,14 @@ static int __init intel_idle_cpuidle_driver_init(void)
if (num_substates == 0)
continue;
+ /* if state marked as disabled, skip it */
+ if (cpuidle_state_table[cstate].disabled != 0) {
+ pr_debug(PREFIX "state %s is disabled",
+ cpuidle_state_table[cstate].name);
+ continue;
+ }
+
+
if (((mwait_cstate + 1) > 2) &&
!boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
mark_tsc_unstable("TSC halts in idle"
diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c
index 2d33f1e821db..291c61a41c9a 100644
--- a/drivers/iio/accel/bmc150-accel-core.c
+++ b/drivers/iio/accel/bmc150-accel-core.c
@@ -547,7 +547,7 @@ static int bmc150_accel_get_axis(struct bmc150_accel_data *data,
{
int ret;
int axis = chan->scan_index;
- unsigned int raw_val;
+ __le16 raw_val;
mutex_lock(&data->mutex);
ret = bmc150_accel_set_power_state(data, true);
@@ -557,14 +557,14 @@ static int bmc150_accel_get_axis(struct bmc150_accel_data *data,
}
ret = regmap_bulk_read(data->regmap, BMC150_ACCEL_AXIS_TO_REG(axis),
- &raw_val, 2);
+ &raw_val, sizeof(raw_val));
if (ret < 0) {
dev_err(data->dev, "Error reading axis %d\n", axis);
bmc150_accel_set_power_state(data, false);
mutex_unlock(&data->mutex);
return ret;
}
- *val = sign_extend32(raw_val >> chan->scan_type.shift,
+ *val = sign_extend32(le16_to_cpu(raw_val) >> chan->scan_type.shift,
chan->scan_type.realbits - 1);
ret = bmc150_accel_set_power_state(data, false);
mutex_unlock(&data->mutex);
@@ -988,6 +988,7 @@ static const struct iio_event_spec bmc150_accel_event = {
.realbits = (bits), \
.storagebits = 16, \
.shift = 16 - (bits), \
+ .endianness = IIO_LE, \
}, \
.event_spec = &bmc150_accel_event, \
.num_event_specs = 1 \
diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c
index dea0448c365c..ae2df4f7ff0d 100644
--- a/drivers/iio/adc/qcom-rradc.c
+++ b/drivers/iio/adc/qcom-rradc.c
@@ -149,12 +149,12 @@
#define FG_ADC_RR_TEMP_FS_VOLTAGE_NUM 5000000
#define FG_ADC_RR_TEMP_FS_VOLTAGE_DEN 3
#define FG_ADC_RR_DIE_TEMP_OFFSET 600000
-#define FG_ADC_RR_DIE_TEMP_SLOPE 2000
-#define FG_ADC_RR_DIE_TEMP_OFFSET_DEGC 25
+#define FG_ADC_RR_DIE_TEMP_SLOPE 2
+#define FG_ADC_RR_DIE_TEMP_OFFSET_MILLI_DEGC 25000
#define FG_ADC_RR_CHG_TEMP_OFFSET 1288000
-#define FG_ADC_RR_CHG_TEMP_SLOPE 4000
-#define FG_ADC_RR_CHG_TEMP_OFFSET_DEGC 27
+#define FG_ADC_RR_CHG_TEMP_SLOPE 4
+#define FG_ADC_RR_CHG_TEMP_OFFSET_MILLI_DEGC 27000
#define FG_ADC_RR_VOLT_INPUT_FACTOR 8
#define FG_ADC_RR_CURR_INPUT_FACTOR 2
@@ -162,6 +162,9 @@
#define FG_ADC_KELVINMIL_CELSIUSMIL 273150
#define FG_ADC_RR_GPIO_FS_RANGE 5000
+#define FG_RR_ADC_COHERENT_CHECK_RETRY 5
+#define FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN 16
+#define FG_RR_ADC_STS_CHANNEL_READING_MASK 0x3
/*
* The channel number is not a physical index in hardware,
@@ -171,21 +174,20 @@
* the RR ADC before RR_ADC_MAX.
*/
enum rradc_channel_id {
- RR_ADC_BATT_ID_5 = 0,
- RR_ADC_BATT_ID_15,
- RR_ADC_BATT_ID_150,
- RR_ADC_BATT_ID,
+ RR_ADC_BATT_ID = 0,
RR_ADC_BATT_THERM,
RR_ADC_SKIN_TEMP,
- RR_ADC_USBIN_V,
RR_ADC_USBIN_I,
- RR_ADC_DCIN_V,
+ RR_ADC_USBIN_V,
RR_ADC_DCIN_I,
+ RR_ADC_DCIN_V,
RR_ADC_DIE_TEMP,
RR_ADC_CHG_TEMP,
RR_ADC_GPIO,
- RR_ADC_ATEST,
- RR_ADC_TM_ADC,
+ RR_ADC_CHG_HOT_TEMP,
+ RR_ADC_CHG_TOO_HOT_TEMP,
+ RR_ADC_SKIN_HOT_TEMP,
+ RR_ADC_SKIN_TOO_HOT_TEMP,
RR_ADC_MAX
};
@@ -205,51 +207,75 @@ struct rradc_channels {
long info_mask;
u8 lsb;
u8 msb;
+ u8 sts;
int (*scale)(struct rradc_chip *chip, struct rradc_chan_prop *prop,
u16 adc_code, int *result);
};
struct rradc_chan_prop {
enum rradc_channel_id channel;
+ uint32_t channel_data;
int (*scale)(struct rradc_chip *chip, struct rradc_chan_prop *prop,
u16 adc_code, int *result);
};
static int rradc_read(struct rradc_chip *rr_adc, u16 offset, u8 *data, int len)
{
- int rc = 0;
+ int rc = 0, retry_cnt = 0, i = 0;
+ u8 data_check[FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN];
+ bool coherent_err = false;
+
+ if (len > FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN) {
+ pr_err("Increase the buffer length\n");
+ return -EINVAL;
+ }
+
+ while (retry_cnt < FG_RR_ADC_COHERENT_CHECK_RETRY) {
+ rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset,
+ data, len);
+ if (rc < 0) {
+ pr_err("rr_adc reg 0x%x failed :%d\n", offset, rc);
+ return rc;
+ }
+
+ rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset,
+ data_check, len);
+ if (rc < 0) {
+ pr_err("rr_adc reg 0x%x failed :%d\n", offset, rc);
+ return rc;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (data[i] != data_check[i])
+ coherent_err = true;
+ }
+
+ if (coherent_err) {
+ retry_cnt++;
+ coherent_err = false;
+ pr_debug("retry_cnt:%d\n", retry_cnt);
+ } else {
+ break;
+ }
+ }
- rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset, data, len);
- if (rc < 0)
- pr_err("rr adc read reg %d failed with %d\n", offset, rc);
+ if (retry_cnt == FG_RR_ADC_COHERENT_CHECK_RETRY)
+ pr_err("Retry exceeded for coherrency check\n");
return rc;
}
static int rradc_post_process_batt_id(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 adc_code,
- int *result_mohms)
+ int *result_ohms)
{
uint32_t current_value;
int64_t r_id;
- switch (prop->channel) {
- case RR_ADC_BATT_ID_5:
- current_value = FG_ADC_RR_BATT_ID_5_MA;
- break;
- case RR_ADC_BATT_ID_15:
- current_value = FG_ADC_RR_BATT_ID_15_MA;
- break;
- case RR_ADC_BATT_ID_150:
- current_value = FG_ADC_RR_BATT_ID_150_MA;
- break;
- default:
- return -EINVAL;
- }
-
+ current_value = prop->channel_data;
r_id = ((int64_t)adc_code * FG_ADC_RR_FS_VOLTAGE_MV);
r_id = div64_s64(r_id, (FG_MAX_ADC_READINGS * current_value));
- *result_mohms = (r_id * FG_ADC_SCALE_MILLI_FACTOR);
+ *result_ohms = (r_id * FG_ADC_SCALE_MILLI_FACTOR);
return 0;
}
@@ -270,30 +296,30 @@ static int rradc_post_process_therm(struct rradc_chip *chip,
static int rradc_post_process_volt(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 adc_code,
- int *result_mv)
+ int *result_uv)
{
- int64_t mv = 0;
+ int64_t uv = 0;
/* 8x input attenuation; 2.5V ADC full scale */
- mv = ((int64_t)adc_code * FG_ADC_RR_VOLT_INPUT_FACTOR);
- mv *= FG_ADC_RR_FS_VOLTAGE_MV;
- mv = div64_s64(mv, FG_MAX_ADC_READINGS);
- *result_mv = mv;
+ uv = ((int64_t)adc_code * FG_ADC_RR_VOLT_INPUT_FACTOR);
+ uv *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR);
+ uv = div64_s64(uv, FG_MAX_ADC_READINGS);
+ *result_uv = uv;
return 0;
}
static int rradc_post_process_curr(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 adc_code,
- int *result_ma)
+ int *result_ua)
{
- int64_t ma = 0;
+ int64_t ua = 0;
/* 0.5 V/A; 2.5V ADC full scale */
- ma = ((int64_t)adc_code * FG_ADC_RR_CURR_INPUT_FACTOR);
- ma *= FG_ADC_RR_FS_VOLTAGE_MV;
- ma = div64_s64(ma, FG_MAX_ADC_READINGS);
- *result_ma = ma;
+ ua = ((int64_t)adc_code * FG_ADC_RR_CURR_INPUT_FACTOR);
+ ua *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR);
+ ua = div64_s64(ua, FG_MAX_ADC_READINGS);
+ *result_ua = ua;
return 0;
}
@@ -309,8 +335,41 @@ static int rradc_post_process_die_temp(struct rradc_chip *chip,
FG_MAX_ADC_READINGS));
temp -= FG_ADC_RR_DIE_TEMP_OFFSET;
temp = div64_s64(temp, FG_ADC_RR_DIE_TEMP_SLOPE);
- temp += FG_ADC_RR_DIE_TEMP_OFFSET_DEGC;
- *result_millidegc = (temp * FG_ADC_SCALE_MILLI_FACTOR);
+ temp += FG_ADC_RR_DIE_TEMP_OFFSET_MILLI_DEGC;
+ *result_millidegc = temp;
+
+ return 0;
+}
+
+static int rradc_post_process_chg_temp_hot(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 adc_code,
+ int *result_millidegc)
+{
+ int64_t temp = 0;
+
+ temp = (int64_t) adc_code * 4;
+ temp = temp * FG_ADC_RR_TEMP_FS_VOLTAGE_NUM;
+ temp = div64_s64(temp, (FG_ADC_RR_TEMP_FS_VOLTAGE_DEN *
+ FG_MAX_ADC_READINGS));
+ temp = FG_ADC_RR_CHG_TEMP_OFFSET - temp;
+ temp = div64_s64(temp, FG_ADC_RR_CHG_TEMP_SLOPE);
+ temp = temp + FG_ADC_RR_CHG_TEMP_OFFSET_MILLI_DEGC;
+ *result_millidegc = temp;
+
+ return 0;
+}
+
+static int rradc_post_process_skin_temp_hot(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 adc_code,
+ int *result_millidegc)
+{
+ int64_t temp = 0;
+
+ temp = (int64_t) adc_code;
+ temp = div64_s64(temp, 2);
+ temp = temp - 30;
+ temp *= FG_ADC_SCALE_MILLI_FACTOR;
+ *result_millidegc = temp;
return 0;
}
@@ -326,8 +385,8 @@ static int rradc_post_process_chg_temp(struct rradc_chip *chip,
FG_MAX_ADC_READINGS));
temp = FG_ADC_RR_CHG_TEMP_OFFSET - temp;
temp = div64_s64(temp, FG_ADC_RR_CHG_TEMP_SLOPE);
- temp = temp + FG_ADC_RR_CHG_TEMP_OFFSET_DEGC;
- *result_millidegc = (temp * FG_ADC_SCALE_MILLI_FACTOR);
+ temp = temp + FG_ADC_RR_CHG_TEMP_OFFSET_MILLI_DEGC;
+ *result_millidegc = temp;
return 0;
}
@@ -346,63 +405,80 @@ static int rradc_post_process_gpio(struct rradc_chip *chip,
return 0;
}
-#define RR_ADC_CHAN(_dname, _type, _mask, _scale, _lsb, _msb) \
+#define RR_ADC_CHAN(_dname, _type, _mask, _scale, _lsb, _msb, _sts) \
{ \
- .datasheet_name = __stringify(_dname), \
+ .datasheet_name = (_dname), \
.type = _type, \
.info_mask = _mask, \
.scale = _scale, \
.lsb = _lsb, \
.msb = _msb, \
+ .sts = _sts, \
}, \
-#define RR_ADC_CHAN_TEMP(_dname, _scale, _lsb, _msb) \
+#define RR_ADC_CHAN_TEMP(_dname, _scale, _lsb, _msb, _sts) \
RR_ADC_CHAN(_dname, IIO_TEMP, \
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \
- _scale, _lsb, _msb) \
+ _scale, _lsb, _msb, _sts) \
-#define RR_ADC_CHAN_VOLT(_dname, _scale, _lsb, _msb) \
+#define RR_ADC_CHAN_VOLT(_dname, _scale, _lsb, _msb, _sts) \
RR_ADC_CHAN(_dname, IIO_VOLTAGE, \
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\
- _scale, _lsb, _msb) \
+ _scale, _lsb, _msb, _sts) \
-#define RR_ADC_CHAN_CURRENT(_dname, _scale, _lsb, _msb) \
+#define RR_ADC_CHAN_CURRENT(_dname, _scale, _lsb, _msb, _sts) \
RR_ADC_CHAN(_dname, IIO_CURRENT, \
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\
- _scale, _lsb, _msb) \
+ _scale, _lsb, _msb, _sts) \
-#define RR_ADC_CHAN_RESISTANCE(_dname, _scale, _lsb, _msb) \
+#define RR_ADC_CHAN_RESISTANCE(_dname, _scale, _lsb, _msb, _sts) \
RR_ADC_CHAN(_dname, IIO_RESISTANCE, \
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\
- _scale, _lsb, _msb) \
+ _scale, _lsb, _msb, _sts) \
static const struct rradc_channels rradc_chans[] = {
- RR_ADC_CHAN_RESISTANCE("batt_id_5", rradc_post_process_batt_id,
- FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB)
- RR_ADC_CHAN_RESISTANCE("batt_id_15", rradc_post_process_batt_id,
- FG_ADC_RR_BATT_ID_15_LSB, FG_ADC_RR_BATT_ID_15_MSB)
- RR_ADC_CHAN_RESISTANCE("batt_id_150", rradc_post_process_batt_id,
- FG_ADC_RR_BATT_ID_150_LSB, FG_ADC_RR_BATT_ID_150_MSB)
RR_ADC_CHAN_RESISTANCE("batt_id", rradc_post_process_batt_id,
- FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB)
+ FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB,
+ FG_ADC_RR_BATT_ID_STS)
RR_ADC_CHAN_TEMP("batt_therm", &rradc_post_process_therm,
- FG_ADC_RR_BATT_THERM_LSB, FG_ADC_RR_BATT_THERM_MSB)
+ FG_ADC_RR_BATT_THERM_LSB, FG_ADC_RR_BATT_THERM_MSB,
+ FG_ADC_RR_BATT_THERM_STS)
RR_ADC_CHAN_TEMP("skin_temp", &rradc_post_process_therm,
- FG_ADC_RR_SKIN_TEMP_LSB, FG_ADC_RR_SKIN_TEMP_MSB)
+ FG_ADC_RR_SKIN_TEMP_LSB, FG_ADC_RR_SKIN_TEMP_MSB,
+ FG_ADC_RR_AUX_THERM_STS)
RR_ADC_CHAN_CURRENT("usbin_i", &rradc_post_process_curr,
- FG_ADC_RR_USB_IN_V_LSB, FG_ADC_RR_USB_IN_V_MSB)
+ FG_ADC_RR_USB_IN_I_LSB, FG_ADC_RR_USB_IN_I_MSB,
+ FG_ADC_RR_USB_IN_I_STS)
RR_ADC_CHAN_VOLT("usbin_v", &rradc_post_process_volt,
- FG_ADC_RR_USB_IN_I_LSB, FG_ADC_RR_USB_IN_I_MSB)
+ FG_ADC_RR_USB_IN_V_LSB, FG_ADC_RR_USB_IN_V_MSB,
+ FG_ADC_RR_USB_IN_V_STS)
RR_ADC_CHAN_CURRENT("dcin_i", &rradc_post_process_curr,
- FG_ADC_RR_DC_IN_V_LSB, FG_ADC_RR_DC_IN_V_MSB)
+ FG_ADC_RR_DC_IN_I_LSB, FG_ADC_RR_DC_IN_I_MSB,
+ FG_ADC_RR_DC_IN_I_STS)
RR_ADC_CHAN_VOLT("dcin_v", &rradc_post_process_volt,
- FG_ADC_RR_DC_IN_I_LSB, FG_ADC_RR_DC_IN_I_MSB)
+ FG_ADC_RR_DC_IN_V_LSB, FG_ADC_RR_DC_IN_V_MSB,
+ FG_ADC_RR_DC_IN_V_STS)
RR_ADC_CHAN_TEMP("die_temp", &rradc_post_process_die_temp,
- FG_ADC_RR_PMI_DIE_TEMP_LSB, FG_ADC_RR_PMI_DIE_TEMP_MSB)
+ FG_ADC_RR_PMI_DIE_TEMP_LSB, FG_ADC_RR_PMI_DIE_TEMP_MSB,
+ FG_ADC_RR_PMI_DIE_TEMP_STS)
RR_ADC_CHAN_TEMP("chg_temp", &rradc_post_process_chg_temp,
- FG_ADC_RR_CHARGER_TEMP_LSB, FG_ADC_RR_CHARGER_TEMP_MSB)
+ FG_ADC_RR_CHARGER_TEMP_LSB, FG_ADC_RR_CHARGER_TEMP_MSB,
+ FG_ADC_RR_CHARGER_TEMP_STS)
RR_ADC_CHAN_VOLT("gpio", &rradc_post_process_gpio,
- FG_ADC_RR_GPIO_LSB, FG_ADC_RR_GPIO_MSB)
+ FG_ADC_RR_GPIO_LSB, FG_ADC_RR_GPIO_MSB,
+ FG_ADC_RR_GPIO_STS)
+ RR_ADC_CHAN_TEMP("chg_temp_hot", &rradc_post_process_chg_temp_hot,
+ FG_ADC_RR_CHARGER_HOT, FG_ADC_RR_CHARGER_HOT,
+ FG_ADC_RR_CHARGER_TEMP_STS)
+ RR_ADC_CHAN_TEMP("chg_temp_too_hot", &rradc_post_process_chg_temp_hot,
+ FG_ADC_RR_CHARGER_TOO_HOT, FG_ADC_RR_CHARGER_TOO_HOT,
+ FG_ADC_RR_CHARGER_TEMP_STS)
+ RR_ADC_CHAN_TEMP("skin_temp_hot", &rradc_post_process_skin_temp_hot,
+ FG_ADC_RR_SKIN_HOT, FG_ADC_RR_SKIN_HOT,
+ FG_ADC_RR_AUX_THERM_STS)
+ RR_ADC_CHAN_TEMP("skin_temp_too_hot", &rradc_post_process_skin_temp_hot,
+ FG_ADC_RR_SKIN_TOO_HOT, FG_ADC_RR_SKIN_TOO_HOT,
+ FG_ADC_RR_AUX_THERM_STS)
};
static int rradc_do_conversion(struct rradc_chip *chip,
@@ -411,15 +487,44 @@ static int rradc_do_conversion(struct rradc_chip *chip,
int rc = 0, bytes_to_read = 0;
u8 buf[6];
u16 offset = 0, batt_id_5 = 0, batt_id_15 = 0, batt_id_150 = 0;
+ u16 status = 0;
mutex_lock(&chip->lock);
+ if ((prop->channel != RR_ADC_BATT_ID) &&
+ (prop->channel != RR_ADC_CHG_HOT_TEMP) &&
+ (prop->channel != RR_ADC_CHG_TOO_HOT_TEMP) &&
+ (prop->channel != RR_ADC_SKIN_HOT_TEMP) &&
+ (prop->channel != RR_ADC_SKIN_TOO_HOT_TEMP)) {
+ /* BATT_ID STS bit does not get set initially */
+ status = rradc_chans[prop->channel].sts;
+ rc = rradc_read(chip, status, buf, 1);
+ if (rc < 0) {
+ pr_err("status read failed:%d\n", rc);
+ goto fail;
+ }
+
+ buf[0] &= FG_RR_ADC_STS_CHANNEL_READING_MASK;
+ if (buf[0] != FG_RR_ADC_STS_CHANNEL_READING_MASK) {
+ pr_warn("%s is not ready; nothing to read\n",
+ rradc_chans[prop->channel].datasheet_name);
+ rc = -ENODATA;
+ goto fail;
+ }
+ }
+
offset = rradc_chans[prop->channel].lsb;
if (prop->channel == RR_ADC_BATT_ID)
bytes_to_read = 6;
+ else if ((prop->channel == RR_ADC_CHG_HOT_TEMP) ||
+ (prop->channel == RR_ADC_CHG_TOO_HOT_TEMP) ||
+ (prop->channel == RR_ADC_SKIN_HOT_TEMP) ||
+ (prop->channel == RR_ADC_SKIN_TOO_HOT_TEMP))
+ bytes_to_read = 1;
else
bytes_to_read = 2;
+ buf[0] = 0;
rc = rradc_read(chip, offset, buf, bytes_to_read);
if (rc) {
pr_err("read data failed\n");
@@ -427,19 +532,33 @@ static int rradc_do_conversion(struct rradc_chip *chip,
}
if (prop->channel == RR_ADC_BATT_ID) {
- batt_id_150 = (buf[4] << 8) | buf[5];
- batt_id_15 = (buf[2] << 8) | buf[3];
- batt_id_5 = (buf[0] << 8) | buf[1];
+ batt_id_150 = (buf[5] << 8) | buf[4];
+ batt_id_15 = (buf[3] << 8) | buf[2];
+ batt_id_5 = (buf[1] << 8) | buf[0];
+ if ((!batt_id_150) && (!batt_id_15) && (!batt_id_5)) {
+ pr_err("Invalid batt_id values with all zeros\n");
+ rc = -EINVAL;
+ goto fail;
+ }
+
if (batt_id_150 <= FG_ADC_RR_BATT_ID_RANGE) {
pr_debug("Batt_id_150 is chosen\n");
*data = batt_id_150;
+ prop->channel_data = FG_ADC_RR_BATT_ID_150_MA;
} else if (batt_id_15 <= FG_ADC_RR_BATT_ID_RANGE) {
pr_debug("Batt_id_15 is chosen\n");
*data = batt_id_15;
+ prop->channel_data = FG_ADC_RR_BATT_ID_15_MA;
} else {
pr_debug("Batt_id_5 is chosen\n");
*data = batt_id_5;
+ prop->channel_data = FG_ADC_RR_BATT_ID_5_MA;
}
+ } else if ((prop->channel == RR_ADC_CHG_HOT_TEMP) ||
+ (prop->channel == RR_ADC_CHG_TOO_HOT_TEMP) ||
+ (prop->channel == RR_ADC_SKIN_HOT_TEMP) ||
+ (prop->channel == RR_ADC_SKIN_TOO_HOT_TEMP)) {
+ *data = buf[0];
} else {
*data = (buf[1] << 8) | buf[0];
}
@@ -458,6 +577,11 @@ static int rradc_read_raw(struct iio_dev *indio_dev,
u16 adc_code;
int rc = 0;
+ if (chan->address >= RR_ADC_MAX) {
+ pr_err("Invalid channel index:%ld\n", chan->address);
+ return -EINVAL;
+ }
+
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
prop = &chip->chan_props[chan->address];
@@ -477,10 +601,6 @@ static int rradc_read_raw(struct iio_dev *indio_dev,
*val = (int) adc_code;
return IIO_VAL_INT;
- case IIO_CHAN_INFO_SCALE:
- *val = 0;
- *val2 = 1000;
- return IIO_VAL_INT_PLUS_MICRO;
default:
rc = -EINVAL;
break;
@@ -498,15 +618,11 @@ static int rradc_get_dt_data(struct rradc_chip *chip, struct device_node *node)
{
const struct rradc_channels *rradc_chan;
struct iio_chan_spec *iio_chan;
- struct device_node *child;
- unsigned int index = 0, chan, base;
+ unsigned int i = 0, base;
int rc = 0;
struct rradc_chan_prop prop;
- chip->nchannels = of_get_available_child_count(node);
- if (!chip->nchannels || (chip->nchannels >= RR_ADC_MAX))
- return -EINVAL;
-
+ chip->nchannels = RR_ADC_MAX;
chip->iio_chans = devm_kcalloc(chip->dev, chip->nchannels,
sizeof(*chip->iio_chans), GFP_KERNEL);
if (!chip->iio_chans)
@@ -529,30 +645,21 @@ static int rradc_get_dt_data(struct rradc_chip *chip, struct device_node *node)
chip->base = base;
iio_chan = chip->iio_chans;
- for_each_available_child_of_node(node, child) {
- rc = of_property_read_u32(child, "channel", &chan);
- if (rc) {
- dev_err(chip->dev, "invalid channel number %d\n", chan);
- return rc;
- }
-
- if (chan > RR_ADC_MAX || chan < RR_ADC_BATT_ID_5) {
- dev_err(chip->dev, "invalid channel number %d\n", chan);
- return -EINVAL;
- }
-
- prop.channel = chan;
- prop.scale = rradc_chans[chan].scale;
- chip->chan_props[index] = prop;
+ for (i = 0; i < RR_ADC_MAX; i++) {
+ prop.channel = i;
+ prop.scale = rradc_chans[i].scale;
+ /* Private channel data used for selecting batt_id */
+ prop.channel_data = 0;
+ chip->chan_props[i] = prop;
- rradc_chan = &rradc_chans[chan];
+ rradc_chan = &rradc_chans[i];
iio_chan->channel = prop.channel;
iio_chan->datasheet_name = rradc_chan->datasheet_name;
+ iio_chan->extend_name = rradc_chan->datasheet_name;
iio_chan->info_mask_separate = rradc_chan->info_mask;
iio_chan->type = rradc_chan->type;
- iio_chan->indexed = 1;
- iio_chan->address = index++;
+ iio_chan->address = i;
iio_chan++;
}
diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c
index 02ff789852a0..acb3b303d800 100644
--- a/drivers/iio/gyro/bmg160_core.c
+++ b/drivers/iio/gyro/bmg160_core.c
@@ -452,7 +452,7 @@ static int bmg160_get_temp(struct bmg160_data *data, int *val)
static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val)
{
int ret;
- unsigned int raw_val;
+ __le16 raw_val;
mutex_lock(&data->mutex);
ret = bmg160_set_power_state(data, true);
@@ -462,7 +462,7 @@ static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val)
}
ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(axis), &raw_val,
- 2);
+ sizeof(raw_val));
if (ret < 0) {
dev_err(data->dev, "Error reading axis %d\n", axis);
bmg160_set_power_state(data, false);
@@ -470,7 +470,7 @@ static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val)
return ret;
}
- *val = sign_extend32(raw_val, 15);
+ *val = sign_extend32(le16_to_cpu(raw_val), 15);
ret = bmg160_set_power_state(data, false);
mutex_unlock(&data->mutex);
if (ret < 0)
@@ -733,6 +733,7 @@ static const struct iio_event_spec bmg160_event = {
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
+ .endianness = IIO_LE, \
}, \
.event_spec = &bmg160_event, \
.num_event_specs = 1 \
@@ -780,7 +781,7 @@ static irqreturn_t bmg160_trigger_handler(int irq, void *p)
mutex_unlock(&data->mutex);
goto err;
}
- data->buffer[i++] = ret;
+ data->buffer[i++] = val;
}
mutex_unlock(&data->mutex);
diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h
index 06a4d9c35581..9daca4681922 100644
--- a/drivers/iio/magnetometer/st_magn.h
+++ b/drivers/iio/magnetometer/st_magn.h
@@ -44,6 +44,7 @@ static inline int st_magn_allocate_ring(struct iio_dev *indio_dev)
static inline void st_magn_deallocate_ring(struct iio_dev *indio_dev)
{
}
+#define ST_MAGN_TRIGGER_SET_STATE NULL
#endif /* CONFIG_IIO_BUFFER */
#endif /* ST_MAGN_H */
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index f357ca67a41c..87799de90a1d 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -456,7 +456,10 @@ out_locked:
return status;
}
-static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast)
+/*
+ * Caller must hold 'priv->lock'
+ */
+static int ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ib_sa_multicast *multicast;
@@ -466,6 +469,10 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast)
ib_sa_comp_mask comp_mask;
int ret = 0;
+ if (!priv->broadcast ||
+ !test_bit(IPOIB_FLAG_OPER_UP, &priv->flags))
+ return -EINVAL;
+
ipoib_dbg_mcast(priv, "joining MGID %pI6\n", mcast->mcmember.mgid.raw);
rec.mgid = mcast->mcmember.mgid;
@@ -525,20 +532,23 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast)
rec.join_state = 4;
#endif
}
+ spin_unlock_irq(&priv->lock);
multicast = ib_sa_join_multicast(&ipoib_sa_client, priv->ca, priv->port,
&rec, comp_mask, GFP_KERNEL,
ipoib_mcast_join_complete, mcast);
+ spin_lock_irq(&priv->lock);
if (IS_ERR(multicast)) {
ret = PTR_ERR(multicast);
ipoib_warn(priv, "ib_sa_join_multicast failed, status %d\n", ret);
- spin_lock_irq(&priv->lock);
/* Requeue this join task with a backoff delay */
__ipoib_mcast_schedule_join_thread(priv, mcast, 1);
clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
spin_unlock_irq(&priv->lock);
complete(&mcast->done);
+ spin_lock_irq(&priv->lock);
}
+ return 0;
}
void ipoib_mcast_join_task(struct work_struct *work)
@@ -620,9 +630,10 @@ void ipoib_mcast_join_task(struct work_struct *work)
/* Found the next unjoined group */
init_completion(&mcast->done);
set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
- spin_unlock_irq(&priv->lock);
- ipoib_mcast_join(dev, mcast);
- spin_lock_irq(&priv->lock);
+ if (ipoib_mcast_join(dev, mcast)) {
+ spin_unlock_irq(&priv->lock);
+ return;
+ }
} else if (!delay_until ||
time_before(mcast->delay_until, delay_until))
delay_until = mcast->delay_until;
@@ -641,10 +652,9 @@ out:
if (mcast) {
init_completion(&mcast->done);
set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
+ ipoib_mcast_join(dev, mcast);
}
spin_unlock_irq(&priv->lock);
- if (mcast)
- ipoib_mcast_join(dev, mcast);
}
int ipoib_mcast_start_thread(struct net_device *dev)
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index 8a51c3b5d657..b0edb66a291b 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -66,6 +66,7 @@ isert_rdma_accept(struct isert_conn *isert_conn);
struct rdma_cm_id *isert_setup_id(struct isert_np *isert_np);
static void isert_release_work(struct work_struct *work);
+static void isert_wait4flush(struct isert_conn *isert_conn);
static inline bool
isert_prot_cmd(struct isert_conn *conn, struct se_cmd *cmd)
@@ -815,12 +816,31 @@ isert_put_conn(struct isert_conn *isert_conn)
kref_put(&isert_conn->kref, isert_release_kref);
}
+static void
+isert_handle_unbound_conn(struct isert_conn *isert_conn)
+{
+ struct isert_np *isert_np = isert_conn->cm_id->context;
+
+ mutex_lock(&isert_np->mutex);
+ if (!list_empty(&isert_conn->node)) {
+ /*
+ * This means iscsi doesn't know this connection
+ * so schedule a cleanup ourselves
+ */
+ list_del_init(&isert_conn->node);
+ isert_put_conn(isert_conn);
+ complete(&isert_conn->wait);
+ queue_work(isert_release_wq, &isert_conn->release_work);
+ }
+ mutex_unlock(&isert_np->mutex);
+}
+
/**
* isert_conn_terminate() - Initiate connection termination
* @isert_conn: isert connection struct
*
* Notes:
- * In case the connection state is FULL_FEATURE, move state
+ * In case the connection state is BOUND, move state
* to TEMINATING and start teardown sequence (rdma_disconnect).
* In case the connection state is UP, complete flush as well.
*
@@ -832,23 +852,19 @@ isert_conn_terminate(struct isert_conn *isert_conn)
{
int err;
- switch (isert_conn->state) {
- case ISER_CONN_TERMINATING:
- break;
- case ISER_CONN_UP:
- case ISER_CONN_FULL_FEATURE: /* FALLTHRU */
- isert_info("Terminating conn %p state %d\n",
- isert_conn, isert_conn->state);
- isert_conn->state = ISER_CONN_TERMINATING;
- err = rdma_disconnect(isert_conn->cm_id);
- if (err)
- isert_warn("Failed rdma_disconnect isert_conn %p\n",
- isert_conn);
- break;
- default:
- isert_warn("conn %p teminating in state %d\n",
- isert_conn, isert_conn->state);
- }
+ if (isert_conn->state >= ISER_CONN_TERMINATING)
+ return;
+
+ isert_info("Terminating conn %p state %d\n",
+ isert_conn, isert_conn->state);
+ isert_conn->state = ISER_CONN_TERMINATING;
+ err = rdma_disconnect(isert_conn->cm_id);
+ if (err)
+ isert_warn("Failed rdma_disconnect isert_conn %p\n",
+ isert_conn);
+
+ isert_info("conn %p completing wait\n", isert_conn);
+ complete(&isert_conn->wait);
}
static int
@@ -882,35 +898,27 @@ static int
isert_disconnected_handler(struct rdma_cm_id *cma_id,
enum rdma_cm_event_type event)
{
- struct isert_np *isert_np = cma_id->context;
- struct isert_conn *isert_conn;
- bool terminating = false;
-
- if (isert_np->cm_id == cma_id)
- return isert_np_cma_handler(cma_id->context, event);
-
- isert_conn = cma_id->qp->qp_context;
+ struct isert_conn *isert_conn = cma_id->qp->qp_context;
mutex_lock(&isert_conn->mutex);
- terminating = (isert_conn->state == ISER_CONN_TERMINATING);
- isert_conn_terminate(isert_conn);
- mutex_unlock(&isert_conn->mutex);
-
- isert_info("conn %p completing wait\n", isert_conn);
- complete(&isert_conn->wait);
-
- if (terminating)
- goto out;
-
- mutex_lock(&isert_np->mutex);
- if (!list_empty(&isert_conn->node)) {
- list_del_init(&isert_conn->node);
- isert_put_conn(isert_conn);
- queue_work(isert_release_wq, &isert_conn->release_work);
+ switch (isert_conn->state) {
+ case ISER_CONN_TERMINATING:
+ break;
+ case ISER_CONN_UP:
+ isert_conn_terminate(isert_conn);
+ isert_wait4flush(isert_conn);
+ isert_handle_unbound_conn(isert_conn);
+ break;
+ case ISER_CONN_BOUND:
+ case ISER_CONN_FULL_FEATURE: /* FALLTHRU */
+ iscsit_cause_connection_reinstatement(isert_conn->conn, 0);
+ break;
+ default:
+ isert_warn("conn %p teminating in state %d\n",
+ isert_conn, isert_conn->state);
}
- mutex_unlock(&isert_np->mutex);
+ mutex_unlock(&isert_conn->mutex);
-out:
return 0;
}
@@ -929,12 +937,16 @@ isert_connect_error(struct rdma_cm_id *cma_id)
static int
isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
{
+ struct isert_np *isert_np = cma_id->context;
int ret = 0;
isert_info("%s (%d): status %d id %p np %p\n",
rdma_event_msg(event->event), event->event,
event->status, cma_id, cma_id->context);
+ if (isert_np->cm_id == cma_id)
+ return isert_np_cma_handler(cma_id->context, event->event);
+
switch (event->event) {
case RDMA_CM_EVENT_CONNECT_REQUEST:
ret = isert_connect_request(cma_id, event);
@@ -980,13 +992,10 @@ isert_post_recvm(struct isert_conn *isert_conn, u32 count)
rx_wr--;
rx_wr->next = NULL; /* mark end of work requests list */
- isert_conn->post_recv_buf_count += count;
ret = ib_post_recv(isert_conn->qp, isert_conn->rx_wr,
&rx_wr_failed);
- if (ret) {
+ if (ret)
isert_err("ib_post_recv() failed with ret: %d\n", ret);
- isert_conn->post_recv_buf_count -= count;
- }
return ret;
}
@@ -1002,12 +1011,9 @@ isert_post_recv(struct isert_conn *isert_conn, struct iser_rx_desc *rx_desc)
rx_wr.num_sge = 1;
rx_wr.next = NULL;
- isert_conn->post_recv_buf_count++;
ret = ib_post_recv(isert_conn->qp, &rx_wr, &rx_wr_failed);
- if (ret) {
+ if (ret)
isert_err("ib_post_recv() failed with ret: %d\n", ret);
- isert_conn->post_recv_buf_count--;
- }
return ret;
}
@@ -1120,12 +1126,9 @@ isert_rdma_post_recvl(struct isert_conn *isert_conn)
rx_wr.sg_list = &sge;
rx_wr.num_sge = 1;
- isert_conn->post_recv_buf_count++;
ret = ib_post_recv(isert_conn->qp, &rx_wr, &rx_wr_fail);
- if (ret) {
+ if (ret)
isert_err("ib_post_recv() failed: %d\n", ret);
- isert_conn->post_recv_buf_count--;
- }
return ret;
}
@@ -1620,7 +1623,6 @@ isert_rcv_completion(struct iser_rx_desc *desc,
ib_dma_sync_single_for_device(ib_dev, rx_dma, rx_buflen,
DMA_FROM_DEVICE);
- isert_conn->post_recv_buf_count--;
}
static int
@@ -2035,7 +2037,8 @@ is_isert_tx_desc(struct isert_conn *isert_conn, void *wr_id)
void *start = isert_conn->rx_descs;
int len = ISERT_QP_MAX_RECV_DTOS * sizeof(*isert_conn->rx_descs);
- if (wr_id >= start && wr_id < start + len)
+ if ((wr_id >= start && wr_id < start + len) ||
+ (wr_id == isert_conn->login_req_buf))
return false;
return true;
@@ -2059,10 +2062,6 @@ isert_cq_comp_err(struct isert_conn *isert_conn, struct ib_wc *wc)
isert_unmap_tx_desc(desc, ib_dev);
else
isert_completion_put(desc, isert_cmd, ib_dev, true);
- } else {
- isert_conn->post_recv_buf_count--;
- if (!isert_conn->post_recv_buf_count)
- iscsit_cause_connection_reinstatement(isert_conn->conn, 0);
}
}
@@ -3193,6 +3192,7 @@ accept_wait:
conn->context = isert_conn;
isert_conn->conn = conn;
+ isert_conn->state = ISER_CONN_BOUND;
isert_set_conn_info(np, conn, isert_conn);
diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h
index 3d7fbc47c343..1874d21daee0 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.h
+++ b/drivers/infiniband/ulp/isert/ib_isert.h
@@ -50,6 +50,7 @@ enum iser_ib_op_code {
enum iser_conn_state {
ISER_CONN_INIT,
ISER_CONN_UP,
+ ISER_CONN_BOUND,
ISER_CONN_FULL_FEATURE,
ISER_CONN_TERMINATING,
ISER_CONN_DOWN,
@@ -144,7 +145,6 @@ struct isert_device;
struct isert_conn {
enum iser_conn_state state;
- int post_recv_buf_count;
u32 responder_resources;
u32 initiator_depth;
bool pi_support;
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index 2e2fe818ca9f..eaabf3125846 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -1737,47 +1737,6 @@ send_sense:
return -1;
}
-/**
- * srpt_rx_mgmt_fn_tag() - Process a task management function by tag.
- * @ch: RDMA channel of the task management request.
- * @fn: Task management function to perform.
- * @req_tag: Tag of the SRP task management request.
- * @mgmt_ioctx: I/O context of the task management request.
- *
- * Returns zero if the target core will process the task management
- * request asynchronously.
- *
- * Note: It is assumed that the initiator serializes tag-based task management
- * requests.
- */
-static int srpt_rx_mgmt_fn_tag(struct srpt_send_ioctx *ioctx, u64 tag)
-{
- struct srpt_device *sdev;
- struct srpt_rdma_ch *ch;
- struct srpt_send_ioctx *target;
- int ret, i;
-
- ret = -EINVAL;
- ch = ioctx->ch;
- BUG_ON(!ch);
- BUG_ON(!ch->sport);
- sdev = ch->sport->sdev;
- BUG_ON(!sdev);
- spin_lock_irq(&sdev->spinlock);
- for (i = 0; i < ch->rq_size; ++i) {
- target = ch->ioctx_ring[i];
- if (target->cmd.se_lun == ioctx->cmd.se_lun &&
- target->cmd.tag == tag &&
- srpt_get_cmd_state(target) != SRPT_STATE_DONE) {
- ret = 0;
- /* now let the target core abort &target->cmd; */
- break;
- }
- }
- spin_unlock_irq(&sdev->spinlock);
- return ret;
-}
-
static int srp_tmr_to_tcm(int fn)
{
switch (fn) {
@@ -1812,7 +1771,6 @@ static void srpt_handle_tsk_mgmt(struct srpt_rdma_ch *ch,
struct se_cmd *cmd;
struct se_session *sess = ch->sess;
uint64_t unpacked_lun;
- uint32_t tag = 0;
int tcm_tmr;
int rc;
@@ -1828,25 +1786,10 @@ static void srpt_handle_tsk_mgmt(struct srpt_rdma_ch *ch,
srpt_set_cmd_state(send_ioctx, SRPT_STATE_MGMT);
send_ioctx->cmd.tag = srp_tsk->tag;
tcm_tmr = srp_tmr_to_tcm(srp_tsk->tsk_mgmt_func);
- if (tcm_tmr < 0) {
- send_ioctx->cmd.se_tmr_req->response =
- TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED;
- goto fail;
- }
unpacked_lun = srpt_unpack_lun((uint8_t *)&srp_tsk->lun,
sizeof(srp_tsk->lun));
-
- if (srp_tsk->tsk_mgmt_func == SRP_TSK_ABORT_TASK) {
- rc = srpt_rx_mgmt_fn_tag(send_ioctx, srp_tsk->task_tag);
- if (rc < 0) {
- send_ioctx->cmd.se_tmr_req->response =
- TMR_TASK_DOES_NOT_EXIST;
- goto fail;
- }
- tag = srp_tsk->task_tag;
- }
rc = target_submit_tmr(&send_ioctx->cmd, sess, NULL, unpacked_lun,
- srp_tsk, tcm_tmr, GFP_KERNEL, tag,
+ srp_tsk, tcm_tmr, GFP_KERNEL, srp_tsk->task_tag,
TARGET_SCF_ACK_KREF);
if (rc != 0) {
send_ioctx->cmd.se_tmr_req->response = TMR_FUNCTION_REJECTED;
diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c
index cfd58e87da26..1c5914cae853 100644
--- a/drivers/input/misc/ati_remote2.c
+++ b/drivers/input/misc/ati_remote2.c
@@ -817,26 +817,49 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d
ar2->udev = udev;
+ /* Sanity check, first interface must have an endpoint */
+ if (alt->desc.bNumEndpoints < 1 || !alt->endpoint) {
+ dev_err(&interface->dev,
+ "%s(): interface 0 must have an endpoint\n", __func__);
+ r = -ENODEV;
+ goto fail1;
+ }
ar2->intf[0] = interface;
ar2->ep[0] = &alt->endpoint[0].desc;
+ /* Sanity check, the device must have two interfaces */
ar2->intf[1] = usb_ifnum_to_if(udev, 1);
+ if ((udev->actconfig->desc.bNumInterfaces < 2) || !ar2->intf[1]) {
+ dev_err(&interface->dev, "%s(): need 2 interfaces, found %d\n",
+ __func__, udev->actconfig->desc.bNumInterfaces);
+ r = -ENODEV;
+ goto fail1;
+ }
+
r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2);
if (r)
goto fail1;
+
+ /* Sanity check, second interface must have an endpoint */
alt = ar2->intf[1]->cur_altsetting;
+ if (alt->desc.bNumEndpoints < 1 || !alt->endpoint) {
+ dev_err(&interface->dev,
+ "%s(): interface 1 must have an endpoint\n", __func__);
+ r = -ENODEV;
+ goto fail2;
+ }
ar2->ep[1] = &alt->endpoint[0].desc;
r = ati_remote2_urb_init(ar2);
if (r)
- goto fail2;
+ goto fail3;
ar2->channel_mask = channel_mask;
ar2->mode_mask = mode_mask;
r = ati_remote2_setup(ar2, ar2->channel_mask);
if (r)
- goto fail2;
+ goto fail3;
usb_make_path(udev, ar2->phys, sizeof(ar2->phys));
strlcat(ar2->phys, "/input0", sizeof(ar2->phys));
@@ -845,11 +868,11 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d
r = sysfs_create_group(&udev->dev.kobj, &ati_remote2_attr_group);
if (r)
- goto fail2;
+ goto fail3;
r = ati_remote2_input_init(ar2);
if (r)
- goto fail3;
+ goto fail4;
usb_set_intfdata(interface, ar2);
@@ -857,10 +880,11 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d
return 0;
- fail3:
+ fail4:
sysfs_remove_group(&udev->dev.kobj, &ati_remote2_attr_group);
- fail2:
+ fail3:
ati_remote2_urb_cleanup(ar2);
+ fail2:
usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
fail1:
kfree(ar2);
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
index ac1fa5f44580..9c0ea36913b4 100644
--- a/drivers/input/misc/ims-pcu.c
+++ b/drivers/input/misc/ims-pcu.c
@@ -1663,6 +1663,8 @@ static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pc
pcu->ctrl_intf = usb_ifnum_to_if(pcu->udev,
union_desc->bMasterInterface0);
+ if (!pcu->ctrl_intf)
+ return -EINVAL;
alt = pcu->ctrl_intf->cur_altsetting;
pcu->ep_ctrl = &alt->endpoint[0].desc;
@@ -1670,6 +1672,8 @@ static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pc
pcu->data_intf = usb_ifnum_to_if(pcu->udev,
union_desc->bSlaveInterface0);
+ if (!pcu->data_intf)
+ return -EINVAL;
alt = pcu->data_intf->cur_altsetting;
if (alt->desc.bNumEndpoints != 2) {
diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c
index 63b539d3daba..84909a12ff36 100644
--- a/drivers/input/misc/powermate.c
+++ b/drivers/input/misc/powermate.c
@@ -307,6 +307,9 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i
int error = -ENOMEM;
interface = intf->cur_altsetting;
+ if (interface->desc.bNumEndpoints < 1)
+ return -EINVAL;
+
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -EIO;
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 6025eb430c0a..a41d8328c064 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -862,8 +862,9 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse,
if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
return;
- /* Bug in FW 8.1, buttons are reported only when ExtBit is 1 */
- if (SYN_ID_FULL(priv->identity) == 0x801 &&
+ /* Bug in FW 8.1 & 8.2, buttons are reported only when ExtBit is 1 */
+ if ((SYN_ID_FULL(priv->identity) == 0x801 ||
+ SYN_ID_FULL(priv->identity) == 0x802) &&
!((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02))
return;
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c
index c9905a4a87df..d619c1d06e9e 100644
--- a/drivers/input/touchscreen/ft5x06_ts.c
+++ b/drivers/input/touchscreen/ft5x06_ts.c
@@ -200,8 +200,9 @@ static int ft5x06_handle_touchdata(struct ft5x06_ts_data *data)
pointid = (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4;
if (pointid >= FT_MAX_ID)
break;
- else
- event->touch_point++;
+
+ event->touch_point++;
+
event->x[i] =
(s16) (buf[FT_TOUCH_X_H_POS + FT_TOUCH_STEP * i] & 0x0F) <<
8 | (s16) buf[FT_TOUCH_X_L_POS + FT_TOUCH_STEP * i];
@@ -419,10 +420,8 @@ static int ft5x06_ts_probe(struct i2c_client *client,
}
data = kzalloc(sizeof(struct ft5x06_ts_data), GFP_KERNEL);
- if (!data) {
- dev_err(&client->dev, "Not enough memory\n");
+ if (!data)
return -ENOMEM;
- }
input_dev = input_allocate_device();
if (!input_dev) {
@@ -445,6 +444,7 @@ static int ft5x06_ts_probe(struct i2c_client *client,
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(BTN_TOUCH, input_dev->keybit);
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
pdata->x_max, 0, 0);
@@ -587,7 +587,7 @@ free_mem:
return err;
}
-static int __devexit ft5x06_ts_remove(struct i2c_client *client)
+static int ft5x06_ts_remove(struct i2c_client *client)
{
struct ft5x06_ts_data *data = i2c_get_clientdata(client);
diff --git a/input/touchscreen/gt9xx/goodix_tool.c b/drivers/input/touchscreen/gt9xx/goodix_tool.c
index 3dfe4e1d334e..3dfe4e1d334e 100644
--- a/input/touchscreen/gt9xx/goodix_tool.c
+++ b/drivers/input/touchscreen/gt9xx/goodix_tool.c
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.c b/drivers/input/touchscreen/gt9xx/gt9xx.c
new file mode 100644
index 000000000000..6615c3a039a0
--- /dev/null
+++ b/drivers/input/touchscreen/gt9xx/gt9xx.c
@@ -0,0 +1,1805 @@
+/* drivers/input/touchscreen/gt9xx.c
+ *
+ * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
+ *
+ * 2010 - 2013 Goodix Technology.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * Version: 1.8
+ * Authors: andrew@goodix.com, meta@goodix.com
+ * Release Date: 2013/04/25
+ * Revision record:
+ * V1.0:
+ * first Release. By Andrew, 2012/08/31
+ * V1.2:
+ * modify gtp_reset_guitar,slot report,tracking_id & 0x0F.
+ * By Andrew, 2012/10/15
+ * V1.4:
+ * modify gt9xx_update.c. By Andrew, 2012/12/12
+ * V1.6:
+ * 1. new heartbeat/esd_protect mechanism(add external watchdog)
+ * 2. doze mode, sliding wakeup
+ * 3. 3 more cfg_group(GT9 Sensor_ID: 0~5)
+ * 3. config length verification
+ * 4. names & comments
+ * By Meta, 2013/03/11
+ * V1.8:
+ * 1. pen/stylus identification
+ * 2. read double check & fixed config support
+ * 2. new esd & slide wakeup optimization
+ * By Meta, 2013/06/08
+ */
+
+#include "gt9xx.h"
+
+#if GTP_ICS_SLOT_REPORT
+#include <linux/input/mt.h>
+#endif
+
+#define GOODIX_DEV_NAME "Goodix Capacitive TouchScreen"
+#define CFG_MAX_TOUCH_POINTS 5
+#define GOODIX_COORDS_ARR_SIZE 4
+#define MAX_BUTTONS 4
+
+/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
+#define GTP_I2C_ADDRESS_HIGH 0x14
+#define GTP_I2C_ADDRESS_LOW 0x5D
+
+#define RESET_DELAY_T3_US 200 /* T3: > 100us */
+#define RESET_DELAY_T4 20 /* T4: > 5ms */
+
+#define PHY_BUF_SIZE 32
+
+#define GTP_MAX_TOUCH 5
+#define GTP_ESD_CHECK_CIRCLE_MS 2000
+
+#if GTP_HAVE_TOUCH_KEY
+static const u16 touch_key_array[] = {KEY_MENU, KEY_HOMEPAGE, KEY_BACK};
+
+#if GTP_DEBUG_ON
+static const int key_codes[] = {
+ KEY_HOME, KEY_BACK, KEY_MENU, KEY_SEARCH
+};
+static const char *const key_names[] = {
+ "Key_Home", "Key_Back", "Key_Menu", "Key_Search"
+};
+#endif
+#endif
+
+static void gtp_reset_guitar(struct goodix_ts_data *ts, int ms);
+static void gtp_int_sync(struct goodix_ts_data *ts, int ms);
+static int gtp_i2c_test(struct i2c_client *client);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void goodix_ts_early_suspend(struct early_suspend *h);
+static void goodix_ts_late_resume(struct early_suspend *h);
+#endif
+
+#if GTP_ESD_PROTECT
+static struct delayed_work gtp_esd_check_work;
+static struct workqueue_struct *gtp_esd_check_workqueue;
+static void gtp_esd_check_func(struct work_struct *work);
+static int gtp_init_ext_watchdog(struct i2c_client *client);
+struct i2c_client *i2c_connect_client;
+#endif
+
+#if GTP_SLIDE_WAKEUP
+enum doze_status {
+ DOZE_DISABLED = 0,
+ DOZE_ENABLED = 1,
+ DOZE_WAKEUP = 2,
+};
+static enum doze_status = DOZE_DISABLED;
+static s8 gtp_enter_doze(struct goodix_ts_data *ts);
+#endif
+bool init_done;
+static u8 chip_gt9xxs; /* true if ic is gt9xxs, like gt915s */
+u8 grp_cfg_version;
+
+/*******************************************************
+Function:
+ Read data from the i2c slave device.
+Input:
+ client: i2c device.
+ buf[0~1]: read start address.
+ buf[2~len-1]: read data buffer.
+ len: GTP_ADDR_LENGTH + read bytes count
+Output:
+ numbers of i2c_msgs to transfer:
+ 2: succeed, otherwise: failed
+*********************************************************/
+int gtp_i2c_read(struct i2c_client *client, u8 *buf, int len)
+{
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
+ struct i2c_msg msgs[2];
+ int ret = -EIO;
+ int retries = 0;
+
+ GTP_DEBUG_FUNC();
+
+ msgs[0].flags = !I2C_M_RD;
+ msgs[0].addr = client->addr;
+ msgs[0].len = GTP_ADDR_LENGTH;
+ msgs[0].buf = &buf[0];
+
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].addr = client->addr;
+ msgs[1].len = len - GTP_ADDR_LENGTH;
+ msgs[1].buf = &buf[GTP_ADDR_LENGTH];
+
+ while (retries < 5) {
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret == 2)
+ break;
+ retries++;
+ }
+ if (retries >= 5) {
+#if GTP_SLIDE_WAKEUP
+ /* reset chip would quit doze mode */
+ if (doze_status == DOZE_ENABLED)
+ return ret;
+#endif
+ GTP_DEBUG("I2C communication timeout, resetting chip...");
+ if (init_done)
+ gtp_reset_guitar(ts, 10);
+ else
+ dev_warn(&client->dev,
+ "<GTP> gtp_reset_guitar exit init_done=%d:\n",
+ init_done);
+ }
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Write data to the i2c slave device.
+Input:
+ client: i2c device.
+ buf[0~1]: write start address.
+ buf[2~len-1]: data buffer
+ len: GTP_ADDR_LENGTH + write bytes count
+Output:
+ numbers of i2c_msgs to transfer:
+ 1: succeed, otherwise: failed
+*********************************************************/
+int gtp_i2c_write(struct i2c_client *client, u8 *buf, int len)
+{
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
+ struct i2c_msg msg;
+ int ret = -EIO;
+ int retries = 0;
+
+ GTP_DEBUG_FUNC();
+
+ msg.flags = !I2C_M_RD;
+ msg.addr = client->addr;
+ msg.len = len;
+ msg.buf = buf;
+
+ while (retries < 5) {
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret == 1)
+ break;
+ retries++;
+ }
+ if ((retries >= 5)) {
+#if GTP_SLIDE_WAKEUP
+ if (doze_status == DOZE_ENABLED)
+ return ret;
+#endif
+ GTP_DEBUG("I2C communication timeout, resetting chip...");
+ if (init_done)
+ gtp_reset_guitar(ts, 10);
+ else
+ dev_warn(&client->dev,
+ "<GTP> gtp_reset_guitar exit init_done=%d:\n",
+ init_done);
+ }
+ return ret;
+}
+/*******************************************************
+Function:
+ i2c read twice, compare the results
+Input:
+ client: i2c device
+ addr: operate address
+ rxbuf: read data to store, if compare successful
+ len: bytes to read
+Output:
+ FAIL: read failed
+ SUCCESS: read successful
+*********************************************************/
+int gtp_i2c_read_dbl_check(struct i2c_client *client,
+ u16 addr, u8 *rxbuf, int len)
+{
+ u8 buf[16] = {0};
+ u8 confirm_buf[16] = {0};
+ u8 retry = 0;
+
+ while (retry++ < 3) {
+ memset(buf, 0xAA, 16);
+ buf[0] = (u8)(addr >> 8);
+ buf[1] = (u8)(addr & 0xFF);
+ gtp_i2c_read(client, buf, len + 2);
+
+ memset(confirm_buf, 0xAB, 16);
+ confirm_buf[0] = (u8)(addr >> 8);
+ confirm_buf[1] = (u8)(addr & 0xFF);
+ gtp_i2c_read(client, confirm_buf, len + 2);
+
+ if (!memcmp(buf, confirm_buf, len + 2))
+ break;
+ }
+ if (retry < 3) {
+ memcpy(rxbuf, confirm_buf + 2, len);
+ return SUCCESS;
+ }
+ dev_err(&client->dev,
+ "i2c read 0x%04X, %d bytes, double check failed!", addr, len);
+ return FAIL;
+}
+
+/*******************************************************
+Function:
+ Send config data.
+Input:
+ client: i2c device.
+Output:
+ result of i2c write operation.
+ > 0: succeed, otherwise: failed
+*********************************************************/
+static int gtp_send_cfg(struct goodix_ts_data *ts)
+{
+ int ret;
+#if GTP_DRIVER_SEND_CFG
+ int retry = 0;
+
+ if (ts->fixed_cfg) {
+ dev_dbg(&ts->client->dev,
+ "Ic fixed config, no config sent!");
+ ret = 2;
+ } else {
+ for (retry = 0; retry < 5; retry++) {
+ ret = gtp_i2c_write(ts->client,
+ ts->config_data,
+ GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH);
+ if (ret > 0)
+ break;
+ }
+ }
+#endif
+
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Disable irq function
+Input:
+ ts: goodix i2c_client private data
+Output:
+ None.
+*********************************************************/
+void gtp_irq_disable(struct goodix_ts_data *ts)
+{
+ unsigned long irqflags;
+
+ GTP_DEBUG_FUNC();
+
+ spin_lock_irqsave(&ts->irq_lock, irqflags);
+ if (!ts->irq_is_disabled) {
+ ts->irq_is_disabled = true;
+ disable_irq_nosync(ts->client->irq);
+ }
+ spin_unlock_irqrestore(&ts->irq_lock, irqflags);
+}
+
+/*******************************************************
+Function:
+ Enable irq function
+Input:
+ ts: goodix i2c_client private data
+Output:
+ None.
+*********************************************************/
+void gtp_irq_enable(struct goodix_ts_data *ts)
+{
+ unsigned long irqflags = 0;
+
+ GTP_DEBUG_FUNC();
+
+ spin_lock_irqsave(&ts->irq_lock, irqflags);
+ if (ts->irq_is_disabled) {
+ enable_irq(ts->client->irq);
+ ts->irq_is_disabled = false;
+ }
+ spin_unlock_irqrestore(&ts->irq_lock, irqflags);
+}
+
+/*******************************************************
+Function:
+ Report touch point event
+Input:
+ ts: goodix i2c_client private data
+ id: trackId
+ x: input x coordinate
+ y: input y coordinate
+ w: input pressure
+Output:
+ None.
+*********************************************************/
+static void gtp_touch_down(struct goodix_ts_data *ts, int id, int x, int y,
+ int w)
+{
+#if GTP_CHANGE_X2Y
+ GTP_SWAP(x, y);
+#endif
+
+#if GTP_ICS_SLOT_REPORT
+ input_mt_slot(ts->input_dev, id);
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
+#else
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
+ input_mt_sync(ts->input_dev);
+#endif
+
+ GTP_DEBUG("ID:%d, X:%d, Y:%d, W:%d", id, x, y, w);
+}
+
+/*******************************************************
+Function:
+ Report touch release event
+Input:
+ ts: goodix i2c_client private data
+Output:
+ None.
+*********************************************************/
+static void gtp_touch_up(struct goodix_ts_data *ts, int id)
+{
+#if GTP_ICS_SLOT_REPORT
+ input_mt_slot(ts->input_dev, id);
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
+ GTP_DEBUG("Touch id[%2d] release!", id);
+#else
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0);
+ input_mt_sync(ts->input_dev);
+#endif
+}
+
+
+
+/*******************************************************
+Function:
+ Goodix touchscreen work function
+Input:
+ work: work struct of goodix_workqueue
+Output:
+ None.
+*********************************************************/
+static void goodix_ts_work_func(struct work_struct *work)
+{
+ u8 end_cmd[3] = { GTP_READ_COOR_ADDR >> 8,
+ GTP_READ_COOR_ADDR & 0xFF, 0};
+ u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1] = {
+ GTP_READ_COOR_ADDR >> 8,
+ GTP_READ_COOR_ADDR & 0xFF};
+ u8 touch_num = 0;
+ u8 finger = 0;
+ static u16 pre_touch;
+ static u8 pre_key;
+#if GTP_WITH_PEN
+ static u8 pre_pen;
+#endif
+ u8 key_value = 0;
+ u8 *coor_data = NULL;
+ s32 input_x = 0;
+ s32 input_y = 0;
+ s32 input_w = 0;
+ s32 id = 0;
+ s32 i = 0;
+ int ret = -1;
+ struct goodix_ts_data *ts = NULL;
+
+#if GTP_SLIDE_WAKEUP
+ u8 doze_buf[3] = {0x81, 0x4B};
+#endif
+
+ GTP_DEBUG_FUNC();
+
+ ts = container_of(work, struct goodix_ts_data, work);
+#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
+ if (ts->enter_update)
+ return;
+#endif
+
+#if GTP_SLIDE_WAKEUP
+ if (doze_status == DOZE_ENABLED) {
+ ret = gtp_i2c_read(ts->client, doze_buf, 3);
+ GTP_DEBUG("0x814B = 0x%02X", doze_buf[2]);
+ if (ret > 0) {
+ if (doze_buf[2] == 0xAA) {
+ dev_dbg(&ts->client->dev,
+ "Slide(0xAA) To Light up the screen!");
+ doze_status = DOZE_WAKEUP;
+ input_report_key(
+ ts->input_dev, KEY_POWER, 1);
+ input_sync(ts->input_dev);
+ input_report_key(
+ ts->input_dev, KEY_POWER, 0);
+ input_sync(ts->input_dev);
+ /* clear 0x814B */
+ doze_buf[2] = 0x00;
+ gtp_i2c_write(ts->client, doze_buf, 3);
+ } else if (doze_buf[2] == 0xBB) {
+ dev_dbg(&ts->client->dev,
+ "Slide(0xBB) To Light up the screen!");
+ doze_status = DOZE_WAKEUP;
+ input_report_key(ts->input_dev, KEY_POWER, 1);
+ input_sync(ts->input_dev);
+ input_report_key(ts->input_dev, KEY_POWER, 0);
+ input_sync(ts->input_dev);
+ /* clear 0x814B*/
+ doze_buf[2] = 0x00;
+ gtp_i2c_write(ts->client, doze_buf, 3);
+ } else if (0xC0 == (doze_buf[2] & 0xC0)) {
+ dev_dbg(&ts->client->dev,
+ "double click to light up the screen!");
+ doze_status = DOZE_WAKEUP;
+ input_report_key(ts->input_dev, KEY_POWER, 1);
+ input_sync(ts->input_dev);
+ input_report_key(ts->input_dev, KEY_POWER, 0);
+ input_sync(ts->input_dev);
+ /* clear 0x814B */
+ doze_buf[2] = 0x00;
+ gtp_i2c_write(ts->client, doze_buf, 3);
+ } else {
+ gtp_enter_doze(ts);
+ }
+ }
+ if (ts->use_irq)
+ gtp_irq_enable(ts);
+
+ return;
+ }
+#endif
+
+ ret = gtp_i2c_read(ts->client, point_data, 12);
+ if (ret < 0) {
+ dev_err(&ts->client->dev,
+ "I2C transfer error. errno:%d\n ", ret);
+ goto exit_work_func;
+ }
+
+ finger = point_data[GTP_ADDR_LENGTH];
+ if ((finger & 0x80) == 0)
+ goto exit_work_func;
+
+ touch_num = finger & 0x0f;
+ if (touch_num > GTP_MAX_TOUCH)
+ goto exit_work_func;
+
+ if (touch_num > 1) {
+ u8 buf[8 * GTP_MAX_TOUCH] = { (GTP_READ_COOR_ADDR + 10) >> 8,
+ (GTP_READ_COOR_ADDR + 10) & 0xff };
+
+ ret = gtp_i2c_read(ts->client, buf,
+ 2 + 8 * (touch_num - 1));
+ memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));
+ }
+
+#if GTP_HAVE_TOUCH_KEY
+ key_value = point_data[3 + 8 * touch_num];
+
+ if (key_value || pre_key) {
+ for (i = 0; i < ARRAY_SIZE(touch_key_array); i++) {
+#if GTP_DEBUG_ON
+ for (ret = 0; ret < 4; ++ret) {
+ if (key_codes[ret] == touch_key_array[i]) {
+ GTP_DEBUG("Key: %s %s",
+ key_names[ret],
+ (key_value & (0x01 << i))
+ ? "Down" : "Up");
+ break;
+ }
+ }
+#endif
+
+ input_report_key(ts->input_dev,
+ touch_key_array[i], key_value & (0x01<<i));
+ }
+ touch_num = 0;
+ pre_touch = 0;
+ }
+#endif
+ pre_key = key_value;
+
+ GTP_DEBUG("pre_touch:%02x, finger:%02x.", pre_touch, finger);
+
+#if GTP_ICS_SLOT_REPORT
+#if GTP_WITH_PEN
+ if (pre_pen && (touch_num == 0)) {
+ GTP_DEBUG("Pen touch UP(Slot)!");
+ input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
+ input_mt_slot(ts->input_dev, 5);
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
+ pre_pen = 0;
+ }
+#endif
+ if (pre_touch || touch_num) {
+ s32 pos = 0;
+ u16 touch_index = 0;
+
+ coor_data = &point_data[3];
+ if (touch_num) {
+ id = coor_data[pos] & 0x0F;
+#if GTP_WITH_PEN
+ id = coor_data[pos];
+ if (id == 128) {
+ GTP_DEBUG("Pen touch DOWN(Slot)!");
+ input_x = coor_data[pos + 1]
+ | (coor_data[pos + 2] << 8);
+ input_y = coor_data[pos + 3]
+ | (coor_data[pos + 4] << 8);
+ input_w = coor_data[pos + 5]
+ | (coor_data[pos + 6] << 8);
+
+ input_report_key(ts->input_dev,
+ BTN_TOOL_PEN, 1);
+ input_mt_slot(ts->input_dev, 5);
+ input_report_abs(ts->input_dev,
+ ABS_MT_TRACKING_ID, 5);
+ input_report_abs(ts->input_dev,
+ ABS_MT_POSITION_X, input_x);
+ input_report_abs(ts->input_dev,
+ ABS_MT_POSITION_Y, input_y);
+ input_report_abs(ts->input_dev,
+ ABS_MT_TOUCH_MAJOR, input_w);
+ GTP_DEBUG("Pen/Stylus: (%d, %d)[%d]",
+ input_x, input_y, input_w);
+ pre_pen = 1;
+ pre_touch = 0;
+ }
+#endif
+
+ touch_index |= (0x01<<id);
+ }
+
+ GTP_DEBUG("id = %d,touch_index = 0x%x, pre_touch = 0x%x\n",
+ id, touch_index, pre_touch);
+ for (i = 0; i < GTP_MAX_TOUCH; i++) {
+#if GTP_WITH_PEN
+ if (pre_pen == 1)
+ break;
+#endif
+ if (touch_index & (0x01<<i)) {
+ input_x = coor_data[pos + 1] |
+ coor_data[pos + 2] << 8;
+ input_y = coor_data[pos + 3] |
+ coor_data[pos + 4] << 8;
+ input_w = coor_data[pos + 5] |
+ coor_data[pos + 6] << 8;
+
+ gtp_touch_down(ts, id,
+ input_x, input_y, input_w);
+ pre_touch |= 0x01 << i;
+
+ pos += 8;
+ id = coor_data[pos] & 0x0F;
+ touch_index |= (0x01<<id);
+ } else {
+ gtp_touch_up(ts, i);
+ pre_touch &= ~(0x01 << i);
+ }
+ }
+ }
+#else
+ input_report_key(ts->input_dev, BTN_TOUCH, (touch_num || key_value));
+ if (touch_num) {
+ for (i = 0; i < touch_num; i++) {
+ coor_data = &point_data[i * 8 + 3];
+
+ id = coor_data[0];
+ input_x = coor_data[1] | coor_data[2] << 8;
+ input_y = coor_data[3] | coor_data[4] << 8;
+ input_w = coor_data[5] | coor_data[6] << 8;
+#if GTP_WITH_PEN
+ if (id == 128) {
+ GTP_DEBUG("Pen touch DOWN!");
+ input_report_key(ts->input_dev,
+ BTN_TOOL_PEN, 1);
+ pre_pen = 1;
+ id = 0;
+ }
+#endif
+ gtp_touch_down(ts, id, input_x, input_y, input_w);
+ }
+ } else if (pre_touch) {
+#if GTP_WITH_PEN
+ if (pre_pen == 1) {
+ GTP_DEBUG("Pen touch UP!");
+ input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
+ pre_pen = 0;
+ }
+#endif
+ GTP_DEBUG("Touch Released!");
+ gtp_touch_up(ts, 0);
+ }
+
+ pre_touch = touch_num;
+#endif
+
+ input_sync(ts->input_dev);
+
+exit_work_func:
+ if (!ts->gtp_rawdiff_mode) {
+ ret = gtp_i2c_write(ts->client, end_cmd, 3);
+ if (ret < 0)
+ dev_warn(&ts->client->dev, "I2C write end_cmd error!\n");
+
+ }
+ if (ts->use_irq)
+ gtp_irq_enable(ts);
+
+ return;
+}
+
+/*******************************************************
+Function:
+ Timer interrupt service routine for polling mode.
+Input:
+ timer: timer struct pointer
+Output:
+ Timer work mode.
+ HRTIMER_NORESTART: no restart mode
+*********************************************************/
+static enum hrtimer_restart goodix_ts_timer_handler(struct hrtimer *timer)
+{
+ struct goodix_ts_data
+ *ts = container_of(timer, struct goodix_ts_data, timer);
+
+ GTP_DEBUG_FUNC();
+
+ queue_work(ts->goodix_wq, &ts->work);
+ hrtimer_start(&ts->timer, ktime_set(0, (GTP_POLL_TIME + 6) * 1000000),
+ HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+}
+
+/*******************************************************
+Function:
+ External interrupt service routine for interrupt mode.
+Input:
+ irq: interrupt number.
+ dev_id: private data pointer
+Output:
+ Handle Result.
+ IRQ_HANDLED: interrupt handled successfully
+*********************************************************/
+static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
+{
+ struct goodix_ts_data *ts = dev_id;
+
+ GTP_DEBUG_FUNC();
+
+ gtp_irq_disable(ts);
+
+ queue_work(ts->goodix_wq, &ts->work);
+
+ return IRQ_HANDLED;
+}
+/*******************************************************
+Function:
+ Synchronization.
+Input:
+ ms: synchronization time in millisecond.
+Output:
+ None.
+*******************************************************/
+void gtp_int_sync(struct goodix_ts_data *ts, int ms)
+{
+ gpio_direction_output(ts->pdata->irq_gpio, 0);
+ msleep(ms);
+ gpio_direction_input(ts->pdata->irq_gpio);
+}
+
+/*******************************************************
+Function:
+ Reset chip.
+Input:
+ ms: reset time in millisecond, must >10ms
+Output:
+ None.
+*******************************************************/
+static void gtp_reset_guitar(struct goodix_ts_data *ts, int ms)
+{
+ GTP_DEBUG_FUNC();
+
+ /* This reset sequence will selcet I2C slave address */
+ gpio_direction_output(ts->pdata->reset_gpio, 0);
+ msleep(ms);
+
+ if (ts->client->addr == GTP_I2C_ADDRESS_HIGH)
+ gpio_direction_output(ts->pdata->irq_gpio, 1);
+ else
+ gpio_direction_output(ts->pdata->irq_gpio, 0);
+
+ usleep(RESET_DELAY_T3_US);
+ gpio_direction_output(ts->pdata->reset_gpio, 1);
+ msleep(RESET_DELAY_T4);
+
+ gpio_direction_input(ts->pdata->reset_gpio);
+
+ gtp_int_sync(ts, 50);
+
+#if GTP_ESD_PROTECT
+ gtp_init_ext_watchdog(ts->client);
+#endif
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#if GTP_SLIDE_WAKEUP
+/*******************************************************
+Function:
+ Enter doze mode for sliding wakeup.
+Input:
+ ts: goodix tp private data
+Output:
+ 1: succeed, otherwise failed
+*******************************************************/
+static s8 gtp_enter_doze(struct goodix_ts_data *ts)
+{
+ int ret = -1;
+ s8 retry = 0;
+ u8 i2c_control_buf[3] = {
+ (u8)(GTP_REG_SLEEP >> 8),
+ (u8)GTP_REG_SLEEP, 8};
+
+ GTP_DEBUG_FUNC();
+
+#if GTP_DBL_CLK_WAKEUP
+ i2c_control_buf[2] = 0x09;
+#endif
+ gtp_irq_disable(ts);
+
+ GTP_DEBUG("entering doze mode...");
+ while (retry++ < 5) {
+ i2c_control_buf[0] = 0x80;
+ i2c_control_buf[1] = 0x46;
+ ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
+ if (ret < 0) {
+ GTP_DEBUG(
+ "failed to set doze flag into 0x8046, %d",
+ retry);
+ continue;
+ }
+ i2c_control_buf[0] = 0x80;
+ i2c_control_buf[1] = 0x40;
+ ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
+ if (ret > 0) {
+ doze_status = DOZE_ENABLED;
+ dev_dbg(&ts->client->dev,
+ "GTP has been working in doze mode!");
+ gtp_irq_enable(ts);
+ return ret;
+ }
+ msleep(20);
+ }
+ dev_err(&ts->client->dev, "GTP send doze cmd failed.\n");
+ gtp_irq_enable(ts);
+ return ret;
+}
+#else
+/*******************************************************
+Function:
+ Enter sleep mode.
+Input:
+ ts: private data.
+Output:
+ Executive outcomes.
+ 1: succeed, otherwise failed.
+*******************************************************/
+static s8 gtp_enter_sleep(struct goodix_ts_data *ts)
+{
+ int ret = -1;
+ s8 retry = 0;
+ u8 i2c_control_buf[3] = {
+ (u8)(GTP_REG_SLEEP >> 8),
+ (u8)GTP_REG_SLEEP, 5};
+
+ GTP_DEBUG_FUNC();
+
+ ret = gpio_direction_output(ts->pdata->irq_gpio, 0);
+ usleep(5000);
+ while (retry++ < 5) {
+ ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
+ if (ret > 0) {
+ dev_dbg(&ts->client->dev,
+ "GTP enter sleep!");
+ return ret;
+ }
+ msleep(20);
+ }
+ dev_err(&ts->client->dev, "GTP send sleep cmd failed.\n");
+ return ret;
+}
+#endif
+
+/*******************************************************
+Function:
+ Wakeup from sleep.
+Input:
+ ts: private data.
+Output:
+ Executive outcomes.
+ >0: succeed, otherwise: failed.
+*******************************************************/
+static s8 gtp_wakeup_sleep(struct goodix_ts_data *ts)
+{
+ u8 retry = 0;
+ s8 ret = -1;
+
+ GTP_DEBUG_FUNC();
+
+#if GTP_POWER_CTRL_SLEEP
+ while (retry++ < 5) {
+ gtp_reset_guitar(ts, 20);
+
+ ret = gtp_send_cfg(ts);
+ if (ret > 0) {
+ dev_dbg(&ts->client->dev,
+ "Wakeup sleep send config success.");
+ continue;
+ }
+ dev_dbg(&ts->client->dev, "GTP Wakeup!");
+ return 1;
+ }
+#else
+ while (retry++ < 10) {
+#if GTP_SLIDE_WAKEUP
+ /* wakeup not by slide */
+ if (doze_status != DOZE_WAKEUP)
+ gtp_reset_guitar(ts, 10);
+ else
+ /* wakeup by slide */
+ doze_status = DOZE_DISABLED;
+#else
+ if (chip_gt9xxs == 1) {
+ gtp_reset_guitar(ts, 10);
+ } else {
+ ret = gpio_direction_output(ts->pdata->irq_gpio, 1);
+ usleep(5000);
+ }
+#endif
+ ret = gtp_i2c_test(ts->client);
+ if (ret > 0) {
+ dev_dbg(&ts->client->dev, "GTP wakeup sleep.");
+#if (!GTP_SLIDE_WAKEUP)
+ if (chip_gt9xxs == 0) {
+ gtp_int_sync(ts, 25);
+ msleep(20);
+#if GTP_ESD_PROTECT
+ gtp_init_ext_watchdog(ts->client);
+#endif
+ }
+#endif
+ return ret;
+ }
+ gtp_reset_guitar(ts, 20);
+ }
+#endif
+
+ dev_err(&ts->client->dev, "GTP wakeup sleep failed.\n");
+ return ret;
+}
+#endif /* !CONFIG_HAS_EARLYSUSPEND */
+
+/*******************************************************
+Function:
+ Initialize gtp.
+Input:
+ ts: goodix private data
+Output:
+ Executive outcomes.
+ > =0: succeed, otherwise: failed
+*******************************************************/
+static int gtp_init_panel(struct goodix_ts_data *ts)
+{
+ struct i2c_client *client = ts->client;
+ unsigned char *config_data;
+ int ret = -EIO;
+
+#if GTP_DRIVER_SEND_CFG
+ int i;
+ u8 check_sum = 0;
+ u8 opr_buf[16];
+ u8 sensor_id = 0;
+
+ u8 cfg_info_group1[] = CTP_CFG_GROUP1;
+ u8 cfg_info_group2[] = CTP_CFG_GROUP2;
+ u8 cfg_info_group3[] = CTP_CFG_GROUP3;
+ u8 cfg_info_group4[] = CTP_CFG_GROUP4;
+ u8 cfg_info_group5[] = CTP_CFG_GROUP5;
+ u8 cfg_info_group6[] = CTP_CFG_GROUP6;
+ u8 *send_cfg_buf[] = {cfg_info_group1, cfg_info_group2,
+ cfg_info_group3, cfg_info_group4,
+ cfg_info_group5, cfg_info_group6};
+
+ u8 cfg_info_len[] = {ARRAY_SIZE(cfg_info_group1),
+ ARRAY_SIZE(cfg_info_group2),
+ ARRAY_SIZE(cfg_info_group3),
+ ARRAY_SIZE(cfg_info_group4),
+ ARRAY_SIZE(cfg_info_group5),
+ ARRAY_SIZE(cfg_info_group6)};
+
+ GTP_DEBUG("Config Groups\' Lengths: %d, %d, %d, %d, %d, %d",
+ cfg_info_len[0], cfg_info_len[1], cfg_info_len[2],
+ cfg_info_len[3], cfg_info_len[4], cfg_info_len[5]);
+
+ ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1);
+ if (ret == SUCCESS) {
+ if (opr_buf[0] != 0xBE) {
+ ts->fw_error = 1;
+ dev_err(&client->dev,
+ "Firmware error, no config sent!");
+ return -EINVAL;
+ }
+ }
+ if ((!cfg_info_len[1]) && (!cfg_info_len[2]) && (!cfg_info_len[3])
+ && (!cfg_info_len[4]) && (!cfg_info_len[5])) {
+ sensor_id = 0;
+ } else {
+ ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID,
+ &sensor_id, 1);
+ if (ret == SUCCESS) {
+ if (sensor_id >= 0x06) {
+ dev_err(&client->dev,
+ "Invalid sensor_id(0x%02X), No Config Sent!",
+ sensor_id);
+ return -EINVAL;
+ }
+ } else {
+ dev_err(&client->dev,
+ "Failed to get sensor_id, No config sent!");
+ return -EINVAL;
+ }
+ }
+ GTP_DEBUG("Sensor_ID: %d", sensor_id);
+
+ ts->gtp_cfg_len = cfg_info_len[sensor_id];
+
+ if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH) {
+ dev_err(&client->dev,
+ "Sensor_ID(%d) matches with NULL or INVALID CONFIG GROUP! NO Config Sent! You need to check you header file CFG_GROUP section!\n",
+ sensor_id);
+ return -EINVAL;
+ }
+ ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA,
+ &opr_buf[0], 1);
+
+ if (ret == SUCCESS) {
+ if (opr_buf[0] < 90) {
+ /* backup group config version */
+ grp_cfg_version = send_cfg_buf[sensor_id][0];
+ send_cfg_buf[sensor_id][0] = 0x00;
+ ts->fixed_cfg = 0;
+ } else {
+ /* treated as fixed config, not send config */
+ dev_warn(&client->dev,
+ "Ic fixed config with config version(%d, 0x%02X)",
+ opr_buf[0], opr_buf[0]);
+ ts->fixed_cfg = 1;
+ }
+ } else {
+ dev_err(&client->dev,
+ "Failed to get ic config version!No config sent!");
+ return -EINVAL;
+ }
+
+ config_data = devm_kzalloc(&client->dev,
+ GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH,
+ GFP_KERNEL);
+ if (!config_data) {
+ dev_err(&client->dev,
+ "Not enough memory for panel config data\n");
+ return -ENOMEM;
+ }
+
+ ts->config_data = config_data;
+ config_data[0] = GTP_REG_CONFIG_DATA >> 8;
+ config_data[1] = GTP_REG_CONFIG_DATA & 0xff;
+ memset(&config_data[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH);
+ memcpy(&config_data[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id],
+ ts->gtp_cfg_len);
+
+#if GTP_CUSTOM_CFG
+ config_data[RESOLUTION_LOC] =
+ (unsigned char)(GTP_MAX_WIDTH && 0xFF);
+ config_data[RESOLUTION_LOC + 1] =
+ (unsigned char)(GTP_MAX_WIDTH >> 8);
+ config_data[RESOLUTION_LOC + 2] =
+ (unsigned char)(GTP_MAX_HEIGHT && 0xFF);
+ config_data[RESOLUTION_LOC + 3] =
+ (unsigned char)(GTP_MAX_HEIGHT >> 8);
+
+ if (GTP_INT_TRIGGER == 0)
+ config_data[TRIGGER_LOC] &= 0xfe;
+ else if (GTP_INT_TRIGGER == 1)
+ config_data[TRIGGER_LOC] |= 0x01;
+#endif /* !GTP_CUSTOM_CFG */
+
+ check_sum = 0;
+ for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++)
+ check_sum += config_data[i];
+
+ config_data[ts->gtp_cfg_len] = (~check_sum) + 1;
+
+#else /* DRIVER NOT SEND CONFIG */
+ ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH;
+ ret = gtp_i2c_read(ts->client, config_data,
+ ts->gtp_cfg_len + GTP_ADDR_LENGTH);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Read Config Failed, Using DEFAULT Resolution & INT Trigger!\n");
+ ts->abs_x_max = GTP_MAX_WIDTH;
+ ts->abs_y_max = GTP_MAX_HEIGHT;
+ ts->int_trigger_type = GTP_INT_TRIGGER;
+ }
+#endif /* !DRIVER NOT SEND CONFIG */
+
+ GTP_DEBUG_FUNC();
+ if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0)) {
+ ts->abs_x_max = (config_data[RESOLUTION_LOC + 1] << 8)
+ + config_data[RESOLUTION_LOC];
+ ts->abs_y_max = (config_data[RESOLUTION_LOC + 3] << 8)
+ + config_data[RESOLUTION_LOC + 2];
+ ts->int_trigger_type = (config_data[TRIGGER_LOC]) & 0x03;
+ }
+ ret = gtp_send_cfg(ts);
+ if (ret < 0)
+ dev_err(&client->dev, "%s: Send config error.\n", __func__);
+
+ GTP_DEBUG("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x",
+ ts->abs_x_max, ts->abs_y_max,
+ ts->int_trigger_type);
+
+ msleep(20);
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Read chip version.
+Input:
+ client: i2c device
+ version: buffer to keep ic firmware version
+Output:
+ read operation return.
+ 2: succeed, otherwise: failed
+*******************************************************/
+int gtp_read_version(struct i2c_client *client, u16 *version)
+{
+ int ret = -EIO;
+ u8 buf[8] = { GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff };
+
+ GTP_DEBUG_FUNC();
+
+ ret = gtp_i2c_read(client, buf, sizeof(buf));
+ if (ret < 0) {
+ dev_err(&client->dev, "GTP read version failed.\n");
+ return ret;
+ }
+
+ if (version)
+ *version = (buf[7] << 8) | buf[6];
+
+ if (buf[5] == 0x00) {
+ dev_dbg(&client->dev, "IC Version: %c%c%c_%02x%02x\n", buf[2],
+ buf[3], buf[4], buf[7], buf[6]);
+ } else {
+ if (buf[5] == 'S' || buf[5] == 's')
+ chip_gt9xxs = 1;
+ dev_dbg(&client->dev, "IC Version: %c%c%c%c_%02x%02x\n", buf[2],
+ buf[3], buf[4], buf[5], buf[7], buf[6]);
+ }
+ return ret;
+}
+
+/*******************************************************
+Function:
+ I2c test Function.
+Input:
+ client:i2c client.
+Output:
+ Executive outcomes.
+ 2: succeed, otherwise failed.
+*******************************************************/
+static int gtp_i2c_test(struct i2c_client *client)
+{
+ u8 buf[3] = { GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff };
+ int retry = 5;
+ int ret = -EIO;
+
+ GTP_DEBUG_FUNC();
+
+ while (retry--) {
+ ret = gtp_i2c_read(client, buf, 3);
+ if (ret > 0)
+ return ret;
+ dev_err(&client->dev, "GTP i2c test failed time %d.\n", retry);
+ msleep(20);
+ }
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Request gpio(INT & RST) ports.
+Input:
+ ts: private data.
+Output:
+ Executive outcomes.
+ = 0: succeed, != 0: failed
+*******************************************************/
+static int gtp_request_io_port(struct goodix_ts_data *ts)
+{
+ struct i2c_client *client = ts->client;
+ struct goodix_ts_platform_data *pdata = ts->pdata;
+ int ret;
+
+ if (gpio_is_valid(pdata->irq_gpio)) {
+ ret = gpio_request(pdata->irq_gpio, "goodix_ts_irq_gpio");
+ if (ret) {
+ dev_err(&client->dev, "irq gpio request failed\n");
+ goto pwr_off;
+ }
+ ret = gpio_direction_input(pdata->irq_gpio);
+ if (ret) {
+ dev_err(&client->dev,
+ "set_direction for irq gpio failed\n");
+ goto free_irq_gpio;
+ }
+ } else {
+ dev_err(&client->dev, "irq gpio is invalid!\n");
+ ret = -EINVAL;
+ goto free_irq_gpio;
+ }
+
+ if (gpio_is_valid(pdata->reset_gpio)) {
+ ret = gpio_request(pdata->reset_gpio, "goodix_ts__reset_gpio");
+ if (ret) {
+ dev_err(&client->dev, "reset gpio request failed\n");
+ goto free_irq_gpio;
+ }
+
+ ret = gpio_direction_output(pdata->reset_gpio, 0);
+ if (ret) {
+ dev_err(&client->dev,
+ "set_direction for reset gpio failed\n");
+ goto free_reset_gpio;
+ }
+ } else {
+ dev_err(&client->dev, "reset gpio is invalid!\n");
+ ret = -EINVAL;
+ goto free_reset_gpio;
+ }
+ gpio_direction_input(pdata->reset_gpio);
+
+ return ret;
+
+free_reset_gpio:
+ if (gpio_is_valid(pdata->reset_gpio))
+ gpio_free(pdata->reset_gpio);
+free_irq_gpio:
+ if (gpio_is_valid(pdata->irq_gpio))
+ gpio_free(pdata->irq_gpio);
+pwr_off:
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Request interrupt.
+Input:
+ ts: private data.
+Output:
+ Executive outcomes.
+ 0: succeed, -1: failed.
+*******************************************************/
+static int gtp_request_irq(struct goodix_ts_data *ts)
+{
+ int ret;
+ const u8 irq_table[] = GTP_IRQ_TAB;
+
+ GTP_DEBUG("INT trigger type:%x, irq=%d", ts->int_trigger_type,
+ ts->client->irq);
+
+ ret = request_irq(ts->client->irq, goodix_ts_irq_handler,
+ irq_table[ts->int_trigger_type],
+ ts->client->name, ts);
+ if (ret) {
+ dev_err(&ts->client->dev, "Request IRQ failed!ERRNO:%d.\n",
+ ret);
+ gpio_direction_input(ts->pdata->irq_gpio);
+
+ hrtimer_init(&ts->timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ ts->timer.function = goodix_ts_timer_handler;
+ hrtimer_start(&ts->timer, ktime_set(1, 0),
+ HRTIMER_MODE_REL);
+ ts->use_irq = false;
+ return ret;
+ }
+ gtp_irq_disable(ts);
+ ts->use_irq = true;
+ return 0;
+}
+
+/*******************************************************
+Function:
+ Request input device Function.
+Input:
+ ts:private data.
+Output:
+ Executive outcomes.
+ 0: succeed, otherwise: failed.
+*******************************************************/
+static int gtp_request_input_dev(struct goodix_ts_data *ts)
+{
+ int ret;
+ char phys[PHY_BUF_SIZE];
+#if GTP_HAVE_TOUCH_KEY
+ int index = 0;
+#endif
+
+ GTP_DEBUG_FUNC();
+
+ ts->input_dev = input_allocate_device();
+ if (ts->input_dev == NULL) {
+ dev_err(&ts->client->dev,
+ "Failed to allocate input device.\n");
+ return -ENOMEM;
+ }
+
+ ts->input_dev->evbit[0] =
+ BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+#if GTP_ICS_SLOT_REPORT
+ __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
+ input_mt_init_slots(ts->input_dev, 10);/* in case of "out of memory" */
+#else
+ ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+#endif
+
+#if GTP_HAVE_TOUCH_KEY
+ for (index = 0; index < ARRAY_SIZE(touch_key_array); index++) {
+ input_set_capability(ts->input_dev,
+ EV_KEY, touch_key_array[index]);
+ }
+#endif
+
+#if GTP_SLIDE_WAKEUP
+ input_set_capability(ts->input_dev, EV_KEY, KEY_POWER);
+#endif
+
+#if GTP_WITH_PEN
+ /* pen support */
+ __set_bit(BTN_TOOL_PEN, ts->input_dev->keybit);
+ __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
+ __set_bit(INPUT_PROP_POINTER, ts->input_dev->propbit);
+#endif
+
+#if GTP_CHANGE_X2Y
+ GTP_SWAP(ts->abs_x_max, ts->abs_y_max);
+#endif
+
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X,
+ 0, ts->abs_x_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y,
+ 0, ts->abs_y_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR,
+ 0, 255, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, 255, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID,
+ 0, 255, 0, 0);
+
+ snprintf(phys, PHY_BUF_SIZE, "input/ts");
+ ts->input_dev->name = GOODIX_DEV_NAME;
+ ts->input_dev->phys = phys;
+ ts->input_dev->id.bustype = BUS_I2C;
+ ts->input_dev->id.vendor = 0xDEAD;
+ ts->input_dev->id.product = 0xBEEF;
+ ts->input_dev->id.version = 10427;
+
+ ret = input_register_device(ts->input_dev);
+ if (ret) {
+ dev_err(&ts->client->dev,
+ "Register %s input device failed.\n",
+ ts->input_dev->name);
+ goto exit_free_inputdev;
+ }
+
+ return 0;
+
+exit_free_inputdev:
+ input_free_device(ts->input_dev);
+ ts->input_dev = NULL;
+ return ret;
+}
+
+/*******************************************************
+Function:
+ I2c probe.
+Input:
+ client: i2c device struct.
+ id: device id.
+Output:
+ Executive outcomes.
+ 0: succeed.
+*******************************************************/
+
+static int goodix_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct goodix_ts_data *ts;
+ u16 version_info;
+ int ret;
+
+ dev_dbg(&client->dev, "GTP I2C Address: 0x%02x\n", client->addr);
+
+#if GTP_ESD_PROTECT
+ i2c_connect_client = client;
+#endif
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "GTP I2C not supported\n");
+ return -ENODEV;
+ }
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ memset(ts, 0, sizeof(*ts));
+ ts->client = client;
+ /* For kernel 2.6.39 later we spin_lock_init(&ts->irq_lock)
+ * For 2.6.39 & before, use ts->irq_lock = SPIN_LOCK_UNLOCKED
+ */
+ spin_lock_init(&ts->irq_lock);
+ i2c_set_clientdata(client, ts);
+
+ ts->gtp_rawdiff_mode = 0;
+
+ ret = gtp_request_io_port(ts);
+ if (ret) {
+ dev_err(&client->dev, "GTP request IO port failed.\n");
+ goto exit_power_off;
+ }
+
+ gtp_reset_guitar(ts, 20);
+
+ ret = gtp_i2c_test(client);
+ if (ret != 2) {
+ dev_err(&client->dev, "I2C communication ERROR!\n");
+ goto exit_free_io_port;
+ }
+
+#if GTP_AUTO_UPDATE
+ ret = gup_init_update_proc(ts);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "GTP Create firmware update thread error.\n");
+ goto exit_free_io_port;
+ }
+#endif
+
+ ret = gtp_init_panel(ts);
+ if (ret < 0) {
+ dev_err(&client->dev, "GTP init panel failed.\n");
+ ts->abs_x_max = GTP_MAX_WIDTH;
+ ts->abs_y_max = GTP_MAX_HEIGHT;
+ ts->int_trigger_type = GTP_INT_TRIGGER;
+ }
+
+ ret = gtp_request_input_dev(ts);
+ if (ret) {
+ dev_err(&client->dev, "GTP request input dev failed.\n");
+ goto exit_free_inputdev;
+ }
+
+ ts->goodix_wq = create_singlethread_workqueue("goodix_wq");
+ INIT_WORK(&ts->work, goodix_ts_work_func);
+
+ ret = gtp_request_irq(ts);
+ if (ret < 0)
+ dev_info(&client->dev, "GTP works in polling mode.\n");
+ else
+ dev_info(&client->dev, "GTP works in interrupt mode.\n");
+
+ ret = gtp_read_version(client, &version_info);
+ if (ret != 2) {
+ dev_err(&client->dev, "Read version failed.\n");
+ goto exit_free_irq;
+ }
+ if (ts->use_irq)
+ gtp_irq_enable(ts);
+
+#if GTP_CREATE_WR_NODE
+ init_wr_node(client);
+#endif
+
+#if GTP_ESD_PROTECT
+ gtp_esd_switch(client, SWITCH_ON);
+#endif
+ init_done = true;
+ return 0;
+exit_free_irq:
+ if (ts->use_irq)
+ free_irq(client->irq, ts);
+ else
+ hrtimer_cancel(&ts->timer);
+ cancel_work_sync(&ts->work);
+ flush_workqueue(ts->goodix_wq);
+ destroy_workqueue(ts->goodix_wq);
+
+ input_unregister_device(ts->input_dev);
+ if (ts->input_dev) {
+ input_free_device(ts->input_dev);
+ ts->input_dev = NULL;
+ }
+exit_free_inputdev:
+ kfree(ts->config_data);
+exit_free_io_port:
+exit_power_off:
+ i2c_set_clientdata(client, NULL);
+ kfree(ts);
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Goodix touchscreen driver release function.
+Input:
+ client: i2c device struct.
+Output:
+ Executive outcomes. 0---succeed.
+*******************************************************/
+static int goodix_ts_remove(struct i2c_client *client)
+{
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
+
+ GTP_DEBUG_FUNC();
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ts->early_suspend);
+#endif
+
+#if GTP_CREATE_WR_NODE
+ uninit_wr_node();
+#endif
+
+#if GTP_ESD_PROTECT
+ cancel_work_sync(gtp_esd_check_workqueue);
+ flush_workqueue(gtp_esd_check_workqueue);
+ destroy_workqueue(gtp_esd_check_workqueue);
+#endif
+
+ if (ts) {
+ if (ts->use_irq)
+ free_irq(client->irq, ts);
+ else
+ hrtimer_cancel(&ts->timer);
+
+ cancel_work_sync(&ts->work);
+ flush_workqueue(ts->goodix_wq);
+ destroy_workqueue(ts->goodix_wq);
+
+ input_unregister_device(ts->input_dev);
+ if (ts->input_dev) {
+ input_free_device(ts->input_dev);
+ ts->input_dev = NULL;
+ }
+ kfree(ts->config_data);
+
+ if (gpio_is_valid(ts->pdata->reset_gpio))
+ gpio_free(ts->pdata->reset_gpio);
+ if (gpio_is_valid(ts->pdata->irq_gpio))
+ gpio_free(ts->pdata->irq_gpio);
+
+ i2c_set_clientdata(client, NULL);
+ kfree(ts);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/*******************************************************
+Function:
+ Early suspend function.
+Input:
+ h: early_suspend struct.
+Output:
+ None.
+*******************************************************/
+static void goodix_ts_early_suspend(struct early_suspend *h)
+{
+ struct goodix_ts_data *ts;
+ s8 ret = -1;
+
+ ts = container_of(h, struct goodix_ts_data, early_suspend);
+
+ GTP_DEBUG_FUNC();
+
+#if GTP_ESD_PROTECT
+ ts->gtp_is_suspend = 1;
+ gtp_esd_switch(ts->client, SWITCH_OFF);
+#endif
+
+#if GTP_SLIDE_WAKEUP
+ ret = gtp_enter_doze(ts);
+#else
+ if (ts->use_irq)
+ gtp_irq_disable(ts);
+ else
+ hrtimer_cancel(&ts->timer);
+ ret = gtp_enter_sleep(ts);
+#endif
+ if (ret < 0)
+ dev_err(&ts->client->dev, "GTP early suspend failed.\n");
+ /* to avoid waking up while not sleeping,
+ * delay 48 + 10ms to ensure reliability
+ */
+ msleep(58);
+}
+
+/*******************************************************
+Function:
+ Late resume function.
+Input:
+ h: early_suspend struct.
+Output:
+ None.
+*******************************************************/
+static void goodix_ts_late_resume(struct early_suspend *h)
+{
+ struct goodix_ts_data *ts;
+ s8 ret = -1;
+
+ ts = container_of(h, struct goodix_ts_data, early_suspend);
+
+ GTP_DEBUG_FUNC();
+
+ ret = gtp_wakeup_sleep(ts);
+
+#if GTP_SLIDE_WAKEUP
+ doze_status = DOZE_DISABLED;
+#endif
+
+ if (ret < 0)
+ dev_err(&ts->client->dev, "GTP later resume failed.\n");
+
+ if (ts->use_irq)
+ gtp_irq_enable(ts);
+ else
+ hrtimer_start(&ts->timer,
+ ktime_set(1, 0), HRTIMER_MODE_REL);
+
+#if GTP_ESD_PROTECT
+ ts->gtp_is_suspend = 0;
+ gtp_esd_switch(ts->client, SWITCH_ON);
+#endif
+}
+#endif
+
+#if GTP_ESD_PROTECT
+/*******************************************************
+Function:
+ switch on & off esd delayed work
+Input:
+ client: i2c device
+ on: SWITCH_ON / SWITCH_OFF
+Output:
+ void
+*********************************************************/
+void gtp_esd_switch(struct i2c_client *client, int on)
+{
+ struct goodix_ts_data *ts;
+
+ ts = i2c_get_clientdata(client);
+ if (on == SWITCH_ON) {
+ /* switch on esd */
+ if (!ts->esd_running) {
+ ts->esd_running = 1;
+ dev_dbg(&client->dev, "Esd started\n");
+ queue_delayed_work(gtp_esd_check_workqueue,
+ &gtp_esd_check_work, GTP_ESD_CHECK_CIRCLE);
+ }
+ } else {
+ /* switch off esd */
+ if (ts->esd_running) {
+ ts->esd_running = 0;
+ dev_dbg(&client->dev, "Esd cancelled\n");
+ cancel_delayed_work_sync(&gtp_esd_check_work);
+ }
+ }
+}
+
+/*******************************************************
+Function:
+ Initialize external watchdog for esd protect
+Input:
+ client: i2c device.
+Output:
+ result of i2c write operation.
+ 1: succeed, otherwise: failed
+*********************************************************/
+static int gtp_init_ext_watchdog(struct i2c_client *client)
+{
+ /* in case of recursively reset by calling gtp_i2c_write*/
+ struct i2c_msg msg;
+ u8 opr_buffer[4] = {0x80, 0x40, 0xAA, 0xAA};
+ int ret;
+ int retries = 0;
+
+ GTP_DEBUG("Init external watchdog...");
+ GTP_DEBUG_FUNC();
+
+ msg.flags = !I2C_M_RD;
+ msg.addr = client->addr;
+ msg.len = 4;
+ msg.buf = opr_buffer;
+
+ while (retries < 5) {
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret == 1)
+ return 1;
+ retries++;
+ }
+ if (retries >= 5)
+ dev_err(&client->dev, "init external watchdog failed!");
+ return 0;
+}
+
+/*******************************************************
+Function:
+ Esd protect function.
+ Added external watchdog by meta, 2013/03/07
+Input:
+ work: delayed work
+Output:
+ None.
+*******************************************************/
+static void gtp_esd_check_func(struct work_struct *work)
+{
+ s32 i;
+ s32 ret = -1;
+ struct goodix_ts_data *ts = NULL;
+ u8 test[4] = {0x80, 0x40};
+
+ GTP_DEBUG_FUNC();
+
+ ts = i2c_get_clientdata(i2c_connect_client);
+
+ if (ts->gtp_is_suspend) {
+ dev_dbg(&ts->client->dev, "Esd terminated!\n");
+ ts->esd_running = 0;
+ return;
+ }
+#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
+ if (ts->enter_update)
+ return;
+#endif
+
+ for (i = 0; i < 3; i++) {
+ ret = gtp_i2c_read(ts->client, test, 4);
+
+ GTP_DEBUG("0x8040 = 0x%02X, 0x8041 = 0x%02X", test[2], test[3]);
+ if ((ret < 0)) {
+ /* IC works abnormally..*/
+ continue;
+ } else {
+ if ((test[2] == 0xAA) || (test[3] != 0xAA)) {
+ /* IC works abnormally..*/
+ i = 3;
+ break;
+ }
+ /* IC works normally, Write 0x8040 0xAA*/
+ test[2] = 0xAA;
+ gtp_i2c_write(ts->client, test, 3);
+ break;
+ }
+ }
+ if (i >= 3) {
+ dev_err(&ts->client->dev,
+ "IC Working ABNORMALLY, Resetting Guitar...\n");
+ gtp_reset_guitar(ts, 50);
+ }
+
+ if (!ts->gtp_is_suspend)
+ queue_delayed_work(gtp_esd_check_workqueue,
+ &gtp_esd_check_work, GTP_ESD_CHECK_CIRCLE);
+ else {
+ dev_dbg(&ts->client->dev, "Esd terminated!\n");
+ ts->esd_running = 0;
+ }
+
+ return;
+}
+#endif
+
+static const struct i2c_device_id goodix_ts_id[] = {
+ { GTP_I2C_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver goodix_ts_driver = {
+ .probe = goodix_ts_probe,
+ .remove = goodix_ts_remove,
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ .suspend = goodix_ts_early_suspend,
+ .resume = goodix_ts_late_resume,
+#endif
+ .id_table = goodix_ts_id,
+ .driver = {
+ .name = GTP_I2C_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/*******************************************************
+Function:
+ Driver Install function.
+Input:
+ None.
+Output:
+ Executive Outcomes. 0---succeed.
+********************************************************/
+static int __init goodix_ts_init(void)
+{
+ int ret;
+
+ GTP_DEBUG_FUNC();
+#if GTP_ESD_PROTECT
+ INIT_DELAYED_WORK(&gtp_esd_check_work, gtp_esd_check_func);
+ gtp_esd_check_workqueue = create_workqueue("gtp_esd_check");
+#endif
+ ret = i2c_add_driver(&goodix_ts_driver);
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Driver uninstall function.
+Input:
+ None.
+Output:
+ Executive Outcomes. 0---succeed.
+********************************************************/
+static void __exit goodix_ts_exit(void)
+{
+ GTP_DEBUG_FUNC();
+ i2c_del_driver(&goodix_ts_driver);
+}
+
+late_initcall(goodix_ts_init);
+module_exit(goodix_ts_exit);
+
+MODULE_DESCRIPTION("GTP Series Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.h b/drivers/input/touchscreen/gt9xx/gt9xx.h
new file mode 100644
index 000000000000..48fa2ad2faca
--- /dev/null
+++ b/drivers/input/touchscreen/gt9xx/gt9xx.h
@@ -0,0 +1,270 @@
+/* drivers/input/touchscreen/gt9xx.h
+ *
+ * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
+ *
+ * 2010 - 2013 Goodix Technology.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#ifndef _GOODIX_GT9XX_H_
+#define _GOODIX_GT9XX_H_
+
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/debugfs.h>
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#define GOODIX_SUSPEND_LEVEL 1
+#endif
+
+struct goodix_ts_platform_data {
+ int irq_gpio;
+ u32 irq_gpio_flags;
+ int reset_gpio;
+ u32 reset_gpio_flags;
+ int ldo_en_gpio;
+ u32 ldo_en_gpio_flags;
+ u32 family_id;
+ u32 x_max;
+ u32 y_max;
+ u32 x_min;
+ u32 y_min;
+ u32 panel_minx;
+ u32 panel_miny;
+ u32 panel_maxx;
+ u32 panel_maxy;
+ bool no_force_update;
+ bool i2c_pull_up;
+};
+struct goodix_ts_data {
+ spinlock_t irq_lock;
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct goodix_ts_platform_data *pdata;
+ struct hrtimer timer;
+ struct workqueue_struct *goodix_wq;
+ struct work_struct work;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+ struct early_suspend early_suspend;
+#endif
+ s32 irq_is_disabled;
+ s32 use_irq;
+ u16 abs_x_max;
+ u16 abs_y_max;
+ u8 max_touch_num;
+ u8 int_trigger_type;
+ u8 green_wake_mode;
+ u8 chip_type;
+ u8 *config_data;
+ u8 enter_update;
+ u8 gtp_is_suspend;
+ u8 gtp_rawdiff_mode;
+ u8 gtp_cfg_len;
+ u8 fixed_cfg;
+ u8 esd_running;
+ u8 fw_error;
+};
+
+extern u16 show_len;
+extern u16 total_len;
+
+/***************************PART1:ON/OFF define*******************************/
+#define GTP_CUSTOM_CFG 0
+#define GTP_CHANGE_X2Y 0
+#define GTP_DRIVER_SEND_CFG 1
+#define GTP_HAVE_TOUCH_KEY 1
+#define GTP_POWER_CTRL_SLEEP 1
+#define GTP_ICS_SLOT_REPORT 0
+
+/* auto updated by .bin file as default */
+#define GTP_AUTO_UPDATE 0
+/* auto updated by head_fw_array in gt9xx_firmware.h,
+ * function together with GTP_AUTO_UPDATE
+ */
+#define GTP_HEADER_FW_UPDATE 0
+
+#define GTP_CREATE_WR_NODE 0
+#define GTP_ESD_PROTECT 0
+#define GTP_WITH_PEN 0
+
+#define GTP_SLIDE_WAKEUP 0
+/* double-click wakeup, function together with GTP_SLIDE_WAKEUP */
+#define GTP_DBL_CLK_WAKEUP 0
+
+#define GTP_DEBUG_ON 1
+#define GTP_DEBUG_ARRAY_ON 0
+#define GTP_DEBUG_FUNC_ON 0
+
+/*************************** PART2:TODO define *******************************/
+/* STEP_1(REQUIRED): Define Configuration Information Group(s) */
+/* Sensor_ID Map: */
+/* sensor_opt1 sensor_opt2 Sensor_ID
+ * GND GND 0
+ * VDDIO GND 1
+ * NC GND 2
+ * GND NC/300K 3
+ * VDDIO NC/300K 4
+ * NC NC/300K 5
+*/
+/* Define your own default or for Sensor_ID == 0 config here */
+/* The predefined one is just a sample config,
+ * which is not suitable for your tp in most cases.
+ */
+#define CTP_CFG_GROUP1 {\
+ 0x41, 0x1C, 0x02, 0xC0, 0x03, 0x0A, 0x05, 0x01, 0x01, 0x0F,\
+ 0x23, 0x0F, 0x5F, 0x41, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x00, 0x0A,\
+ 0x28, 0x00, 0xB8, 0x0B, 0x00, 0x00, 0x00, 0x9A, 0x03, 0x25,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x64, 0x32, 0x00, 0x00,\
+ 0x00, 0x32, 0x8C, 0x94, 0x05, 0x01, 0x05, 0x00, 0x00, 0x96,\
+ 0x0C, 0x22, 0xD8, 0x0E, 0x23, 0x56, 0x11, 0x25, 0xFF, 0x13,\
+ 0x28, 0xA7, 0x15, 0x2E, 0x00, 0x00, 0x10, 0x30, 0x48, 0x00,\
+ 0x56, 0x4A, 0x3A, 0xFF, 0xFF, 0x16, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x01, 0x1B, 0x14, 0x0D, 0x19, 0x00, 0x00, 0x01, 0x00,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x1A, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0E, 0x0C,\
+ 0x0A, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\
+ 0xFF, 0xFF, 0x1D, 0x1E, 0x1F, 0x20, 0x22, 0x24, 0x28, 0x29,\
+ 0x0C, 0x0A, 0x08, 0x00, 0x02, 0x04, 0x05, 0x06, 0x0E, 0xFF,\
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x01\
+ }
+
+/* Define your config for Sensor_ID == 1 here, if needed */
+#define CTP_CFG_GROUP2 {\
+ }
+
+/* Define your config for Sensor_ID == 2 here, if needed */
+#define CTP_CFG_GROUP3 {\
+ }
+
+/* Define your config for Sensor_ID == 3 here, if needed */
+#define CTP_CFG_GROUP4 {\
+ }
+
+/* Define your config for Sensor_ID == 4 here, if needed */
+#define CTP_CFG_GROUP5 {\
+ }
+
+/* Define your config for Sensor_ID == 5 here, if needed */
+#define CTP_CFG_GROUP6 {\
+ }
+
+#define GTP_IRQ_TAB {\
+ IRQ_TYPE_EDGE_RISING,\
+ IRQ_TYPE_EDGE_FALLING,\
+ IRQ_TYPE_LEVEL_LOW,\
+ IRQ_TYPE_LEVEL_HIGH\
+ }
+
+/* STEP_3(optional): Specify your special config info if needed */
+#define GTP_IRQ_TAB_RISING 0
+#define GTP_IRQ_TAB_FALLING 1
+#if GTP_CUSTOM_CFG
+#define GTP_MAX_HEIGHT 800
+#define GTP_MAX_WIDTH 480
+#define GTP_INT_TRIGGER GTP_IRQ_TAB_RISING
+#else
+#define GTP_MAX_HEIGHT 4096
+#define GTP_MAX_WIDTH 4096
+#define GTP_INT_TRIGGER GTP_IRQ_TAB_FALLING
+#endif
+
+#define GTP_MAX_TOUCH 5
+#define GTP_ESD_CHECK_CIRCLE 2000 /* jiffy: ms */
+
+/***************************PART3:OTHER define*********************************/
+#define GTP_DRIVER_VERSION "V1.8<2013/06/08>"
+#define GTP_I2C_NAME "Goodix-TS"
+#define GTP_POLL_TIME 10 /* jiffy: ms*/
+#define GTP_ADDR_LENGTH 2
+#define GTP_CONFIG_MIN_LENGTH 186
+#define GTP_CONFIG_MAX_LENGTH 240
+#define FAIL 0
+#define SUCCESS 1
+#define SWITCH_OFF 0
+#define SWITCH_ON 1
+
+/* Registers define */
+#define GTP_READ_COOR_ADDR 0x814E
+#define GTP_REG_SLEEP 0x8040
+#define GTP_REG_SENSOR_ID 0x814A
+#define GTP_REG_CONFIG_DATA 0x8047
+#define GTP_REG_VERSION 0x8140
+
+#define RESOLUTION_LOC 3
+#define TRIGGER_LOC 8
+
+/* Log define */
+#define GTP_DEBUG(fmt, arg...) do {\
+ if (GTP_DEBUG_ON) {\
+ pr_debug("<<-GTP-DEBUG->> [%d]"fmt"\n",\
+ __LINE__, ##arg); } \
+ } while (0)
+
+#define GTP_DEBUG_ARRAY(array, num) do {\
+ s32 i; \
+ u8 *a = array; \
+ if (GTP_DEBUG_ARRAY_ON) {\
+ pr_debug("<<-GTP-DEBUG-ARRAY->>\n");\
+ for (i = 0; i < (num); i++) { \
+ pr_debug("%02x ", (a)[i]);\
+ if ((i + 1) % 10 == 0) { \
+ pr_debug("\n");\
+ } \
+ } \
+ pr_debug("\n");\
+ } \
+ } while (0)
+
+#define GTP_DEBUG_FUNC() do {\
+ if (GTP_DEBUG_FUNC_ON)\
+ pr_debug("<<-GTP-FUNC->> Func:%s@Line:%d\n",\
+ __func__, __LINE__);\
+ } while (0)
+
+#define GTP_SWAP(x, y) do {\
+ typeof(x) z = x;\
+ x = y;\
+ y = z;\
+ } while (0)
+/*****************************End of Part III********************************/
+
+void gtp_esd_switch(struct i2c_client *client, int on);
+
+#if GTP_CREATE_WR_NODE
+extern s32 init_wr_node(struct i2c_client *client);
+extern void uninit_wr_node(void);
+#endif
+
+#if GTP_AUTO_UPDATE
+extern u8 gup_init_update_proc(struct goodix_ts_data *ts);
+#endif
+#endif /* _GOODIX_GT9XX_H_ */
diff --git a/input/touchscreen/gt9xx/gt9xx_firmware.h b/drivers/input/touchscreen/gt9xx/gt9xx_firmware.h
index 3998bf0023f8..3998bf0023f8 100644
--- a/input/touchscreen/gt9xx/gt9xx_firmware.h
+++ b/drivers/input/touchscreen/gt9xx/gt9xx_firmware.h
diff --git a/input/touchscreen/gt9xx/gt9xx_update.c b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
index f564a6b3aaed..f564a6b3aaed 100644
--- a/input/touchscreen/gt9xx/gt9xx_update.c
+++ b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
diff --git a/drivers/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c
index 773ece9eb1d4..c60a2b5a94b0 100644
--- a/drivers/input/touchscreen/it7258_ts_i2c.c
+++ b/drivers/input/touchscreen/it7258_ts_i2c.c
@@ -22,12 +22,30 @@
#include <linux/firmware.h>
#include <linux/gpio.h>
#include <linux/slab.h>
-#include <linux/wakelock.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/fb.h>
+#include <linux/debugfs.h>
+#include <linux/input/mt.h>
+#include <linux/string.h>
#define MAX_BUFFER_SIZE 144
#define DEVICE_NAME "IT7260"
#define SCREEN_X_RESOLUTION 320
#define SCREEN_Y_RESOLUTION 320
+#define DEBUGFS_DIR_NAME "ts_debug"
+#define FW_NAME "it7260_fw.bin"
+#define CFG_NAME "it7260_cfg.bin"
+#define VER_BUFFER_SIZE 4
+#define IT_FW_CHECK(x, y) \
+ (((x)[0] < (y)->data[8]) || ((x)[1] < (y)->data[9]) || \
+ ((x)[2] < (y)->data[10]) || ((x)[3] < (y)->data[11]))
+#define IT_CFG_CHECK(x, y) \
+ (((x)[0] < (y)->data[(y)->size - 8]) || \
+ ((x)[1] < (y)->data[(y)->size - 7]) || \
+ ((x)[2] < (y)->data[(y)->size - 6]) || \
+ ((x)[3] < (y)->data[(y)->size - 5]))
+#define IT7260_COORDS_ARR_SIZE 4
/* all commands writes go to this idx */
#define BUF_COMMAND 0x20
@@ -50,23 +68,26 @@
#define CMD_IDENT_CHIP 0x00
/* VERSION_LENGTH bytes of data in response */
#define CMD_READ_VERSIONS 0x01
-#define VER_FIRMWARE 0x00
-#define VER_CONFIG 0x06
+#define SUB_CMD_READ_FIRMWARE_VERSION 0x00
+#define SUB_CMD_READ_CONFIG_VERSION 0x06
#define VERSION_LENGTH 10
/* subcommand is zero, next byte is power mode */
#define CMD_PWR_CTL 0x04
+/* active mode */
+#define PWR_CTL_ACTIVE_MODE 0x00
/* idle mode */
#define PWR_CTL_LOW_POWER_MODE 0x01
/* sleep mode */
#define PWR_CTL_SLEEP_MODE 0x02
+#define WAIT_CHANGE_MODE 20
/* command is not documented in the datasheet v1.0.0.7 */
#define CMD_UNKNOWN_7 0x07
#define CMD_FIRMWARE_REINIT_C 0x0C
/* needs to be followed by 4 bytes of zeroes */
#define CMD_CALIBRATE 0x13
#define CMD_FIRMWARE_UPGRADE 0x60
-#define FIRMWARE_MODE_ENTER 0x00
-#define FIRMWARE_MODE_EXIT 0x80
+#define SUB_CMD_ENTER_FW_UPGRADE_MODE 0x00
+#define SUB_CMD_EXIT_FW_UPGRADE_MODE 0x80
/* address for FW read/write */
#define CMD_SET_START_OFFSET 0x61
/* subcommand is number of bytes to write */
@@ -78,11 +99,7 @@
#define FW_WRITE_CHUNK_SIZE 128
#define FW_WRITE_RETRY_COUNT 4
#define CHIP_FLASH_SIZE 0x8000
-#define SYSFS_FW_UPLOAD_MODE_MANUAL 2
-#define SYSFS_RESULT_FAIL (-1)
-#define SYSFS_RESULT_NOT_DONE 0
-#define SYSFS_RESULT_SUCCESS 1
-#define DEVICE_READY_MAX_WAIT 500
+#define DEVICE_READY_MAX_WAIT 10
/* result of reading with BUF_QUERY bits */
#define CMD_STATUS_BITS 0x07
@@ -95,9 +112,28 @@
/* no new data but finder(s) still down */
#define BT_INFO_NONE_BUT_DOWN 0x08
-/* use this to include integers in commands */
-#define CMD_UINT16(v) ((uint8_t)(v)) , ((uint8_t)((v) >> 8))
+#define PD_FLAGS_DATA_TYPE_BITS 0xF0
+/* other types (like chip-detected gestures) exist but we do not care */
+#define PD_FLAGS_DATA_TYPE_TOUCH 0x00
+/* a bit for each finger data that is valid (from lsb to msb) */
+#define PD_FLAGS_HAVE_FINGERS 0x07
+#define PD_PALM_FLAG_BIT 0x01
+#define FD_PRESSURE_BITS 0x0F
+#define FD_PRESSURE_NONE 0x00
+#define FD_PRESSURE_LIGHT 0x02
+
+#define IT_VTG_MIN_UV 1800000
+#define IT_VTG_MAX_UV 1800000
+#define IT_ACTIVE_LOAD_UA 15000
+#define IT_I2C_VTG_MIN_UV 2600000
+#define IT_I2C_VTG_MAX_UV 3300000
+#define IT_I2C_ACTIVE_LOAD_UA 10000
+#define DELAY_VTG_REG_EN 170
+#define PINCTRL_STATE_ACTIVE "pmx_ts_active"
+#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend"
+#define PINCTRL_STATE_RELEASE "pmx_ts_release"
+#define IT_I2C_WAIT 1000
struct FingerData {
uint8_t xLo;
@@ -112,80 +148,130 @@ struct PointData {
struct FingerData fd[3];
} __packed;
-#define PD_FLAGS_DATA_TYPE_BITS 0xF0
-/* other types (like chip-detected gestures) exist but we do not care */
-#define PD_FLAGS_DATA_TYPE_TOUCH 0x00
-/* set if pen touched, clear if finger(s) */
-#define PD_FLAGS_NOT_PEN 0x08
-/* a bit for each finger data that is valid (from lsb to msb) */
-#define PD_FLAGS_HAVE_FINGERS 0x07
-#define PD_PALM_FLAG_BIT 0x01
-#define FD_PRESSURE_BITS 0x0F
-#define FD_PRESSURE_NONE 0x00
-#define FD_PRESSURE_HOVER 0x01
-#define FD_PRESSURE_LIGHT 0x02
-#define FD_PRESSURE_NORMAL 0x04
-#define FD_PRESSURE_HIGH 0x08
-#define FD_PRESSURE_HEAVY 0x0F
+struct IT7260_ts_platform_data {
+ u32 irq_gpio;
+ u32 irq_gpio_flags;
+ u32 reset_gpio;
+ u32 reset_gpio_flags;
+ bool wakeup;
+ bool palm_detect_en;
+ u16 palm_detect_keycode;
+ const char *fw_name;
+ const char *cfg_name;
+ unsigned int panel_minx;
+ unsigned int panel_miny;
+ unsigned int panel_maxx;
+ unsigned int panel_maxy;
+ unsigned int disp_minx;
+ unsigned int disp_miny;
+ unsigned int disp_maxx;
+ unsigned int disp_maxy;
+ unsigned num_of_fingers;
+ unsigned int reset_delay;
+ unsigned int avdd_lpm_cur;
+ bool low_reset;
+};
struct IT7260_ts_data {
struct i2c_client *client;
struct input_dev *input_dev;
+ const struct IT7260_ts_platform_data *pdata;
+ struct regulator *vdd;
+ struct regulator *avdd;
+ bool device_needs_wakeup;
+ bool suspended;
+ bool fw_upgrade_result;
+ bool cfg_upgrade_result;
+ bool fw_cfg_uploading;
+ struct work_struct work_pm_relax;
+ bool calibration_success;
+ bool had_finger_down;
+ char fw_name[MAX_BUFFER_SIZE];
+ char cfg_name[MAX_BUFFER_SIZE];
+ struct mutex fw_cfg_mutex;
+ u8 fw_ver[VER_BUFFER_SIZE];
+ u8 cfg_ver[VER_BUFFER_SIZE];
+#ifdef CONFIG_FB
+ struct notifier_block fb_notif;
+#endif
+ struct dentry *dir;
+ struct pinctrl *ts_pinctrl;
+ struct pinctrl_state *pinctrl_state_active;
+ struct pinctrl_state *pinctrl_state_suspend;
+ struct pinctrl_state *pinctrl_state_release;
};
-static int8_t fwUploadResult;
-static int8_t calibrationWasSuccessful;
-static bool devicePresent;
-static DEFINE_MUTEX(sleepModeMutex);
-static bool chipAwake;
-static bool hadFingerDown;
-static bool isDeviceSuspend;
-static struct input_dev *input_dev;
+/* Function declarations */
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data);
+static int IT7260_ts_resume(struct device *dev);
+static int IT7260_ts_suspend(struct device *dev);
+
static struct IT7260_ts_data *gl_ts;
-#define LOGE(...) pr_err(DEVICE_NAME ": " __VA_ARGS__)
-#define LOGI(...) printk(DEVICE_NAME ": " __VA_ARGS__)
+static int IT7260_debug_suspend_set(void *_data, u64 val)
+{
+ if (val)
+ IT7260_ts_suspend(&gl_ts->client->dev);
+ else
+ IT7260_ts_resume(&gl_ts->client->dev);
+
+ return 0;
+}
+
+static int IT7260_debug_suspend_get(void *_data, u64 *val)
+{
+ *val = gl_ts->suspended;
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, IT7260_debug_suspend_get,
+ IT7260_debug_suspend_set, "%lld\n");
/* internal use func - does not make sure chip is ready before read */
-static bool i2cReadNoReadyCheck(uint8_t bufferIndex, uint8_t *dataBuffer,
- uint16_t dataLength)
+static bool IT7260_i2cReadNoReadyCheck(uint8_t buf_index, uint8_t *buffer,
+ uint16_t buf_len)
{
struct i2c_msg msgs[2] = {
{
.addr = gl_ts->client->addr,
.flags = I2C_M_NOSTART,
.len = 1,
- .buf = &bufferIndex
+ .buf = &buf_index
},
{
.addr = gl_ts->client->addr,
.flags = I2C_M_RD,
- .len = dataLength,
- .buf = dataBuffer
+ .len = buf_len,
+ .buf = buffer
}
};
- memset(dataBuffer, 0xFF, dataLength);
+ memset(buffer, 0xFF, buf_len);
return i2c_transfer(gl_ts->client->adapter, msgs, 2);
}
-static bool i2cWriteNoReadyCheck(uint8_t bufferIndex,
- const uint8_t *dataBuffer, uint16_t dataLength)
+static bool IT7260_i2cWriteNoReadyCheck(uint8_t buf_index,
+ const uint8_t *buffer, uint16_t buf_len)
{
uint8_t txbuf[257];
struct i2c_msg msg = {
.addr = gl_ts->client->addr,
.flags = 0,
- .len = dataLength + 1,
+ .len = buf_len + 1,
.buf = txbuf
};
/* just to be careful */
- BUG_ON(dataLength > sizeof(txbuf) - 1);
+ if (buf_len > sizeof(txbuf) - 1) {
+ dev_err(&gl_ts->client->dev, "buf length is out of limit\n");
+ return false;
+ }
- txbuf[0] = bufferIndex;
- memcpy(txbuf + 1, dataBuffer, dataLength);
+ txbuf[0] = buf_index;
+ memcpy(txbuf + 1, buffer, buf_len);
return i2c_transfer(gl_ts->client->adapter, &msg, 1);
}
@@ -195,81 +281,84 @@ static bool i2cWriteNoReadyCheck(uint8_t bufferIndex,
* register reads/writes. This function ascertains it is ready
* for that too. the results of this call often were ignored.
*/
-static bool waitDeviceReady(bool forever, bool slowly)
+static bool IT7260_waitDeviceReady(bool forever, bool slowly)
{
- uint8_t ucQuery;
+ uint8_t query;
uint32_t count = DEVICE_READY_MAX_WAIT;
do {
- if (!i2cReadNoReadyCheck(BUF_QUERY, &ucQuery, sizeof(ucQuery)))
- ucQuery = CMD_STATUS_BUSY;
+ if (!IT7260_i2cReadNoReadyCheck(BUF_QUERY, &query,
+ sizeof(query)))
+ query = CMD_STATUS_BUSY;
if (slowly)
- mdelay(1000);
+ msleep(IT_I2C_WAIT);
if (!forever)
count--;
- } while ((ucQuery & CMD_STATUS_BUSY) && count);
+ } while ((query & CMD_STATUS_BUSY) && count);
- return !ucQuery;
+ return !query;
}
-static bool i2cRead(uint8_t bufferIndex, uint8_t *dataBuffer,
- uint16_t dataLength)
+static bool IT7260_i2cRead(uint8_t buf_index, uint8_t *buffer,
+ uint16_t buf_len)
{
- waitDeviceReady(false, false);
- return i2cReadNoReadyCheck(bufferIndex, dataBuffer, dataLength);
+ IT7260_waitDeviceReady(false, false);
+ return IT7260_i2cReadNoReadyCheck(buf_index, buffer, buf_len);
}
-static bool i2cWrite(uint8_t bufferIndex, const uint8_t *dataBuffer,
- uint16_t dataLength)
+static bool IT7260_i2cWrite(uint8_t buf_index, const uint8_t *buffer,
+ uint16_t buf_len)
{
- waitDeviceReady(false, false);
- return i2cWriteNoReadyCheck(bufferIndex, dataBuffer, dataLength);
+ IT7260_waitDeviceReady(false, false);
+ return IT7260_i2cWriteNoReadyCheck(buf_index, buffer, buf_len);
}
-static bool chipFirmwareReinitialize(uint8_t cmdOfChoice)
+static bool IT7260_firmware_reinitialize(u8 command)
{
- uint8_t cmd[] = {cmdOfChoice};
+ uint8_t cmd[] = {command};
uint8_t rsp[2];
- if (!i2cWrite(BUF_COMMAND, cmd, sizeof(cmd)))
+ if (!IT7260_i2cWrite(BUF_COMMAND, cmd, sizeof(cmd)))
return false;
- if (!i2cRead(BUF_RESPONSE, rsp, sizeof(rsp)))
+ if (!IT7260_i2cRead(BUF_RESPONSE, rsp, sizeof(rsp)))
return false;
/* a reply of two zero bytes signifies success */
return !rsp[0] && !rsp[1];
}
-static bool chipFirmwareUpgradeModeEnterExit(bool enter)
+static bool IT7260_enter_exit_fw_ugrade_mode(bool enter)
{
uint8_t cmd[] = {CMD_FIRMWARE_UPGRADE, 0, 'I', 'T', '7', '2',
'6', '0', 0x55, 0xAA};
uint8_t resp[2];
- cmd[1] = enter ? FIRMWARE_MODE_ENTER : FIRMWARE_MODE_EXIT;
- if (!i2cWrite(BUF_COMMAND, cmd, sizeof(cmd)))
+ cmd[1] = enter ? SUB_CMD_ENTER_FW_UPGRADE_MODE :
+ SUB_CMD_EXIT_FW_UPGRADE_MODE;
+ if (!IT7260_i2cWrite(BUF_COMMAND, cmd, sizeof(cmd)))
return false;
- if (!i2cRead(BUF_RESPONSE, resp, sizeof(resp)))
+ if (!IT7260_i2cRead(BUF_RESPONSE, resp, sizeof(resp)))
return false;
/* a reply of two zero bytes signifies success */
return !resp[0] && !resp[1];
}
-static bool chipSetStartOffset(uint16_t offset)
+static bool IT7260_chipSetStartOffset(uint16_t offset)
{
- uint8_t cmd[] = {CMD_SET_START_OFFSET, 0, CMD_UINT16(offset)};
+ uint8_t cmd[] = {CMD_SET_START_OFFSET, 0, ((uint8_t)(offset)),
+ ((uint8_t)((offset) >> 8))};
uint8_t resp[2];
- if (!i2cWrite(BUF_COMMAND, cmd, 4))
+ if (!IT7260_i2cWrite(BUF_COMMAND, cmd, 4))
return false;
- if (!i2cRead(BUF_RESPONSE, resp, sizeof(resp)))
+ if (!IT7260_i2cRead(BUF_RESPONSE, resp, sizeof(resp)))
return false;
@@ -278,371 +367,629 @@ static bool chipSetStartOffset(uint16_t offset)
}
-/* write fwLength bytes from fwData at chip offset writeStartOffset */
-static bool chipFlashWriteAndVerify(unsigned int fwLength,
- const uint8_t *fwData, uint16_t writeStartOffset)
+/* write fw_length bytes from fw_data at chip offset wr_start_offset */
+static bool IT7260_fw_flash_write_verify(unsigned int fw_length,
+ const uint8_t *fw_data, uint16_t wr_start_offset)
{
- uint32_t curDataOfst;
+ uint32_t cur_data_off;
- for (curDataOfst = 0; curDataOfst < fwLength;
- curDataOfst += FW_WRITE_CHUNK_SIZE) {
+ for (cur_data_off = 0; cur_data_off < fw_length;
+ cur_data_off += FW_WRITE_CHUNK_SIZE) {
- uint8_t cmdWrite[2 + FW_WRITE_CHUNK_SIZE] = {CMD_FW_WRITE};
- uint8_t bufRead[FW_WRITE_CHUNK_SIZE];
- uint8_t cmdRead[2] = {CMD_FW_READ};
- unsigned i, nRetries;
- uint32_t curWriteSz;
+ uint8_t cmd_write[2 + FW_WRITE_CHUNK_SIZE] = {CMD_FW_WRITE};
+ uint8_t buf_read[FW_WRITE_CHUNK_SIZE];
+ uint8_t cmd_read[2] = {CMD_FW_READ};
+ unsigned i, retries;
+ uint32_t cur_wr_size;
/* figure out how much to write */
- curWriteSz = fwLength - curDataOfst;
- if (curWriteSz > FW_WRITE_CHUNK_SIZE)
- curWriteSz = FW_WRITE_CHUNK_SIZE;
+ cur_wr_size = fw_length - cur_data_off;
+ if (cur_wr_size > FW_WRITE_CHUNK_SIZE)
+ cur_wr_size = FW_WRITE_CHUNK_SIZE;
/* prepare the write command */
- cmdWrite[1] = curWriteSz;
- for (i = 0; i < curWriteSz; i++)
- cmdWrite[i + 2] = fwData[curDataOfst + i];
+ cmd_write[1] = cur_wr_size;
+ for (i = 0; i < cur_wr_size; i++)
+ cmd_write[i + 2] = fw_data[cur_data_off + i];
/* prepare the read command */
- cmdRead[1] = curWriteSz;
+ cmd_read[1] = cur_wr_size;
- for (nRetries = 0; nRetries < FW_WRITE_RETRY_COUNT;
- nRetries++) {
+ for (retries = 0; retries < FW_WRITE_RETRY_COUNT;
+ retries++) {
/* set write offset and write the data */
- chipSetStartOffset(writeStartOffset + curDataOfst);
- i2cWrite(BUF_COMMAND, cmdWrite, 2 + curWriteSz);
+ IT7260_chipSetStartOffset(
+ wr_start_offset + cur_data_off);
+ IT7260_i2cWrite(BUF_COMMAND, cmd_write,
+ cur_wr_size + 2);
/* set offset and read the data back */
- chipSetStartOffset(writeStartOffset + curDataOfst);
- i2cWrite(BUF_COMMAND, cmdRead, sizeof(cmdRead));
- i2cRead(BUF_RESPONSE, bufRead, curWriteSz);
+ IT7260_chipSetStartOffset(
+ wr_start_offset + cur_data_off);
+ IT7260_i2cWrite(BUF_COMMAND, cmd_read,
+ sizeof(cmd_read));
+ IT7260_i2cRead(BUF_RESPONSE, buf_read, cur_wr_size);
/* verify. If success break out of retry loop */
i = 0;
- while (i < curWriteSz && bufRead[i] == cmdWrite[i + 2])
+ while (i < cur_wr_size &&
+ buf_read[i] == cmd_write[i + 2])
i++;
- if (i == curWriteSz)
+ if (i == cur_wr_size)
break;
- pr_err("write of data offset %u failed on try %u at byte %u/%u\n",
- curDataOfst, nRetries, i, curWriteSz);
}
/* if we've failed after all the retries, tell the caller */
- if (nRetries == FW_WRITE_RETRY_COUNT)
+ if (retries == FW_WRITE_RETRY_COUNT) {
+ dev_err(&gl_ts->client->dev,
+ "write of data offset %u failed on try %u at byte %u/%u\n",
+ cur_data_off, retries, i, cur_wr_size);
return false;
+ }
}
return true;
}
-static bool chipFirmwareUpload(uint32_t fwLen, const uint8_t *fwData,
- uint32_t cfgLen, const uint8_t *cfgData)
+/*
+ * this code to get versions from the chip via i2c transactions, and save
+ * them in driver data structure.
+ */
+static void IT7260_get_chip_versions(struct device *dev)
{
- bool success = false;
+ static const u8 cmd_read_fw_ver[] = {CMD_READ_VERSIONS,
+ SUB_CMD_READ_FIRMWARE_VERSION};
+ static const u8 cmd_read_cfg_ver[] = {CMD_READ_VERSIONS,
+ SUB_CMD_READ_CONFIG_VERSION};
+ u8 ver_fw[VERSION_LENGTH], ver_cfg[VERSION_LENGTH];
+ bool ret = true;
- /* enter fw upload mode */
- if (!chipFirmwareUpgradeModeEnterExit(true))
- return false;
+ ret = IT7260_i2cWrite(BUF_COMMAND, cmd_read_fw_ver,
+ sizeof(cmd_read_fw_ver));
+ if (ret) {
+ ret = IT7260_i2cRead(BUF_RESPONSE, ver_fw, VERSION_LENGTH);
+ if (ret)
+ memcpy(gl_ts->fw_ver, ver_fw + (5 * sizeof(u8)),
+ VER_BUFFER_SIZE * sizeof(u8));
+ }
+ if (!ret)
+ dev_err(dev, "failed to read fw version from chip\n");
+
+ ret = IT7260_i2cWrite(BUF_COMMAND, cmd_read_cfg_ver,
+ sizeof(cmd_read_cfg_ver));
+ if (ret) {
+ ret = IT7260_i2cRead(BUF_RESPONSE, ver_cfg, VERSION_LENGTH)
+ && ret;
+ if (ret)
+ memcpy(gl_ts->cfg_ver, ver_cfg + (1 * sizeof(u8)),
+ VER_BUFFER_SIZE * sizeof(u8));
+ }
+ if (!ret)
+ dev_err(dev, "failed to read cfg version from chip\n");
- /* flash the firmware if requested */
- if (fwLen && fwData && !chipFlashWriteAndVerify(fwLen, fwData, 0)) {
- LOGE("failed to upload touch firmware\n");
- goto out;
+ dev_info(dev, "Current fw{%X.%X.%X.%X} cfg{%X.%X.%X.%X}\n",
+ gl_ts->fw_ver[0], gl_ts->fw_ver[1], gl_ts->fw_ver[2],
+ gl_ts->fw_ver[3], gl_ts->cfg_ver[0], gl_ts->cfg_ver[1],
+ gl_ts->cfg_ver[2], gl_ts->cfg_ver[3]);
+}
+
+static int IT7260_cfg_upload(struct device *dev, bool force)
+{
+ const struct firmware *cfg = NULL;
+ int ret;
+ bool success, cfg_upgrade = false;
+
+ ret = request_firmware(&cfg, gl_ts->cfg_name, dev);
+ if (ret) {
+ dev_err(dev, "failed to get config data %s for it7260 %d\n",
+ gl_ts->cfg_name, ret);
+ return ret;
}
- /* flash config data if requested */
- if (fwLen && fwData && !chipFlashWriteAndVerify(cfgLen, cfgData,
- CHIP_FLASH_SIZE - cfgLen)) {
- LOGE("failed to upload touch cfg data\n");
+ /*
+ * This compares the cfg version number from chip and the cfg
+ * data file. IT flashes only when version of cfg data file is
+ * greater than that of chip or if it is set for force cfg upgrade.
+ */
+ if (force)
+ cfg_upgrade = true;
+ else if (IT_CFG_CHECK(gl_ts->cfg_ver, cfg))
+ cfg_upgrade = true;
+
+ if (!cfg_upgrade) {
+ dev_err(dev, "CFG upgrade no required ...\n");
+ ret = -EFAULT;
goto out;
- }
+ } else {
+ dev_info(dev, "Config upgrading...\n");
+
+ disable_irq(gl_ts->client->irq);
+ /* enter cfg upload mode */
+ success = IT7260_enter_exit_fw_ugrade_mode(true);
+ if (!success) {
+ dev_err(dev, "Can't enter cfg upgrade mode\n");
+ ret = -EIO;
+ goto out;
+ }
+ /* flash config data if requested */
+ success = IT7260_fw_flash_write_verify(cfg->size, cfg->data,
+ CHIP_FLASH_SIZE - cfg->size);
+ if (!success) {
+ dev_err(dev, "failed to upgrade touch cfg data\n");
+ IT7260_enter_exit_fw_ugrade_mode(false);
+ IT7260_firmware_reinitialize(CMD_FIRMWARE_REINIT_6F);
+ ret = -EIO;
+ goto out;
+ } else {
+ memcpy(gl_ts->cfg_ver, cfg->data +
+ (cfg->size - 8 * sizeof(u8)),
+ VER_BUFFER_SIZE * sizeof(u8));
+ dev_info(dev, "CFG upgrade is success. New cfg ver: %X.%X.%X.%X\n",
+ gl_ts->cfg_ver[0], gl_ts->cfg_ver[1],
+ gl_ts->cfg_ver[2], gl_ts->cfg_ver[3]);
- success = true;
+ }
+ enable_irq(gl_ts->client->irq);
+ }
out:
- return chipFirmwareUpgradeModeEnterExit(false) &&
- chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_6F) && success;
-}
+ release_firmware(cfg);
+ return ret;
+}
-/*
- * both buffers should be VERSION_LENGTH in size,
- * but only a part of them is significant
- */
-static bool chipGetVersions(uint8_t *verFw, uint8_t *verCfg, bool logIt)
+static int IT7260_fw_upload(struct device *dev, bool force)
{
- /*
- * this code to get versions is reproduced as was written, but it does
- * not make sense. Something here *PROBABLY IS* wrong
- */
- static const uint8_t cmdReadFwVer[] = {CMD_READ_VERSIONS, VER_FIRMWARE};
- static const uint8_t cmdReadCfgVer[] = {CMD_READ_VERSIONS, VER_CONFIG};
- bool ret = true;
+ const struct firmware *fw = NULL;
+ int ret;
+ bool success, fw_upgrade = false;
+
+ ret = request_firmware(&fw, gl_ts->fw_name, dev);
+ if (ret) {
+ dev_err(dev, "failed to get firmware %s for it7260 %d\n",
+ gl_ts->fw_name, ret);
+ return ret;
+ }
/*
- * this structure is so that we definitely do all the calls, but still
- * return a status in case anyone cares
+ * This compares the fw version number from chip and the fw data
+ * file. It flashes only when version of fw data file is greater
+ * than that of chip or it it is set for force fw upgrade.
*/
- ret = i2cWrite(BUF_COMMAND, cmdReadFwVer, sizeof(cmdReadFwVer)) && ret;
- ret = i2cRead(BUF_RESPONSE, verFw, VERSION_LENGTH) && ret;
- ret = i2cWrite(BUF_COMMAND, cmdReadCfgVer,
- sizeof(cmdReadCfgVer)) && ret;
- ret = i2cRead(BUF_RESPONSE, verCfg, VERSION_LENGTH) && ret;
+ if (force)
+ fw_upgrade = true;
+ else if (IT_FW_CHECK(gl_ts->fw_ver, fw))
+ fw_upgrade = true;
+
+ if (!fw_upgrade) {
+ dev_err(dev, "FW upgrade not required ...\n");
+ ret = -EFAULT;
+ goto out;
+ } else {
+ dev_info(dev, "Firmware upgrading...\n");
+
+ disable_irq(gl_ts->client->irq);
+ /* enter fw upload mode */
+ success = IT7260_enter_exit_fw_ugrade_mode(true);
+ if (!success) {
+ dev_err(dev, "Can't enter fw upgrade mode\n");
+ ret = -EIO;
+ goto out;
+ }
+ /* flash the firmware if requested */
+ success = IT7260_fw_flash_write_verify(fw->size, fw->data, 0);
+ if (!success) {
+ dev_err(dev, "failed to upgrade touch firmware\n");
+ IT7260_enter_exit_fw_ugrade_mode(false);
+ IT7260_firmware_reinitialize(CMD_FIRMWARE_REINIT_6F);
+ ret = -EIO;
+ goto out;
+ } else {
+ memcpy(gl_ts->fw_ver, fw->data + (8 * sizeof(u8)),
+ VER_BUFFER_SIZE * sizeof(u8));
+ dev_info(dev, "FW upgrade is success. New fw ver: %X.%X.%X.%X\n",
+ gl_ts->fw_ver[0], gl_ts->fw_ver[1],
+ gl_ts->fw_ver[2], gl_ts->fw_ver[3]);
+ }
+ enable_irq(gl_ts->client->irq);
+ }
- if (logIt)
- LOGI("current versions: fw@{%X,%X,%X,%X}, cfg@{%X,%X,%X,%X}\n",
- verFw[5], verFw[6], verFw[7], verFw[8],
- verCfg[1], verCfg[2], verCfg[3], verCfg[4]);
+out:
+ release_firmware(fw);
return ret;
}
-static ssize_t sysfsUpgradeStore(struct device *dev,
+static int IT7260_ts_chipLowPowerMode(const u8 sleep_type)
+{
+ const uint8_t cmd_sleep[] = {CMD_PWR_CTL, 0x00, sleep_type};
+ uint8_t dummy;
+
+ if (sleep_type)
+ IT7260_i2cWriteNoReadyCheck(BUF_COMMAND, cmd_sleep,
+ sizeof(cmd_sleep));
+ else
+ IT7260_i2cReadNoReadyCheck(BUF_QUERY, &dummy, sizeof(dummy));
+
+ msleep(WAIT_CHANGE_MODE);
+ return 0;
+}
+
+static ssize_t sysfs_fw_upgrade_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- const struct firmware *fw, *cfg;
- uint8_t verFw[10], verCfg[10];
- unsigned fwLen = 0, cfgLen = 0;
- bool manualUpgrade, success;
int mode = 0, ret;
- ret = request_firmware(&fw, "it7260.fw", dev);
- if (ret)
- LOGE("failed to get firmware for it7260\n");
- else
- fwLen = fw->size;
+ if (gl_ts->suspended) {
+ dev_err(dev, "Device is suspended, can't flash fw!!!\n");
+ return -EBUSY;
+ }
- ret = request_firmware(&cfg, "it7260.cfg", dev);
- if (ret)
- LOGE("failed to get config data for it7260\n");
- else
- cfgLen = cfg->size;
+ ret = kstrtoint(buf, 10, &mode);
+ if (!ret) {
+ dev_err(dev, "failed to read input for sysfs\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&gl_ts->fw_cfg_mutex);
+ if (mode == 1) {
+ gl_ts->fw_cfg_uploading = true;
+ ret = IT7260_fw_upload(dev, false);
+ if (ret) {
+ dev_err(dev, "Failed to flash fw: %d", ret);
+ gl_ts->fw_upgrade_result = false;
+ } else {
+ gl_ts->fw_upgrade_result = true;
+ }
+ gl_ts->fw_cfg_uploading = false;
+ }
+ mutex_unlock(&gl_ts->fw_cfg_mutex);
+
+ return count;
+}
+
+static ssize_t sysfs_cfg_upgrade_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int mode = 0, ret;
+
+ if (gl_ts->suspended) {
+ dev_err(dev, "Device is suspended, can't flash cfg!!!\n");
+ return -EBUSY;
+ }
+
+ ret = kstrtoint(buf, 10, &mode);
+ if (!ret) {
+ dev_err(dev, "failed to read input for sysfs\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&gl_ts->fw_cfg_mutex);
+ if (mode == 1) {
+ gl_ts->fw_cfg_uploading = true;
+ ret = IT7260_cfg_upload(dev, false);
+ if (ret) {
+ dev_err(dev, "Failed to flash cfg: %d", ret);
+ gl_ts->cfg_upgrade_result = false;
+ } else {
+ gl_ts->cfg_upgrade_result = true;
+ }
+ gl_ts->fw_cfg_uploading = false;
+ }
+ mutex_unlock(&gl_ts->fw_cfg_mutex);
+
+ return count;
+}
+
+static ssize_t sysfs_fw_upgrade_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return scnprintf(buf, MAX_BUFFER_SIZE, "%d\n",
+ gl_ts->fw_upgrade_result);
+}
+
+static ssize_t sysfs_cfg_upgrade_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return scnprintf(buf, MAX_BUFFER_SIZE, "%d\n",
+ gl_ts->cfg_upgrade_result);
+}
+
+static ssize_t sysfs_force_fw_upgrade_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int mode = 0, ret;
+
+ if (gl_ts->suspended) {
+ dev_err(dev, "Device is suspended, can't flash fw!!!\n");
+ return -EBUSY;
+ }
ret = kstrtoint(buf, 10, &mode);
- manualUpgrade = mode == SYSFS_FW_UPLOAD_MODE_MANUAL;
- LOGI("firmware found %ub of fw and %ub of config in %s mode\n",
- fwLen, cfgLen, manualUpgrade ? "manual" : "normal");
-
- chipGetVersions(verFw, verCfg, true);
-
- fwUploadResult = SYSFS_RESULT_NOT_DONE;
- if (fwLen && cfgLen) {
- if (manualUpgrade || (verFw[5] < fw->data[8] || verFw[6] <
- fw->data[9] || verFw[7] < fw->data[10] || verFw[8] <
- fw->data[11]) || (verCfg[1] < cfg->data[cfgLen - 8]
- || verCfg[2] < cfg->data[cfgLen - 7] || verCfg[3] <
- cfg->data[cfgLen - 6] ||
- verCfg[4] < cfg->data[cfgLen - 5])){
- LOGI("firmware/config will be upgraded\n");
- disable_irq(gl_ts->client->irq);
- success = chipFirmwareUpload(fwLen, fw->data, cfgLen,
- cfg->data);
- enable_irq(gl_ts->client->irq);
-
- fwUploadResult = success ?
- SYSFS_RESULT_SUCCESS : SYSFS_RESULT_FAIL;
- LOGI("upload %s\n", success ? "success" : "failed");
+ if (!ret) {
+ dev_err(dev, "failed to read input for sysfs\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&gl_ts->fw_cfg_mutex);
+ if (mode == 1) {
+ gl_ts->fw_cfg_uploading = true;
+ ret = IT7260_fw_upload(dev, true);
+ if (ret) {
+ dev_err(dev, "Failed to force flash fw: %d", ret);
+ gl_ts->fw_upgrade_result = false;
} else {
- LOGI("firmware/config upgrade not needed\n");
+ gl_ts->fw_upgrade_result = true;
}
+ gl_ts->fw_cfg_uploading = false;
}
+ mutex_unlock(&gl_ts->fw_cfg_mutex);
- if (fwLen)
- release_firmware(fw);
+ return count;
+}
- if (cfgLen)
- release_firmware(cfg);
+static ssize_t sysfs_force_cfg_upgrade_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int mode = 0, ret;
+
+ if (gl_ts->suspended) {
+ dev_err(dev, "Device is suspended, can't flash cfg!!!\n");
+ return -EBUSY;
+ }
+
+ ret = kstrtoint(buf, 10, &mode);
+ if (!ret) {
+ dev_err(dev, "failed to read input for sysfs\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&gl_ts->fw_cfg_mutex);
+ if (mode == 1) {
+ gl_ts->fw_cfg_uploading = true;
+ ret = IT7260_cfg_upload(dev, true);
+ if (ret) {
+ dev_err(dev, "Failed to force flash cfg: %d", ret);
+ gl_ts->cfg_upgrade_result = false;
+ } else {
+ gl_ts->cfg_upgrade_result = true;
+ }
+ gl_ts->fw_cfg_uploading = false;
+ }
+ mutex_unlock(&gl_ts->fw_cfg_mutex);
return count;
}
-static ssize_t sysfsUpgradeShow(struct device *dev,
+static ssize_t sysfs_force_fw_upgrade_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, MAX_BUFFER_SIZE, "%d", gl_ts->fw_upgrade_result);
+}
+
+static ssize_t sysfs_force_cfg_upgrade_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return snprintf(buf, MAX_BUFFER_SIZE, "%d", fwUploadResult);
+ return snprintf(buf, MAX_BUFFER_SIZE, "%d", gl_ts->cfg_upgrade_result);
}
-static ssize_t sysfsCalibrationShow(struct device *dev,
+static ssize_t sysfs_calibration_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return snprintf(buf, MAX_BUFFER_SIZE, "%d", calibrationWasSuccessful);
+ return scnprintf(buf, MAX_BUFFER_SIZE, "%d\n",
+ gl_ts->calibration_success);
}
-static bool chipSendCalibrationCmd(bool autoTuneOn)
+static bool IT7260_chipSendCalibrationCmd(bool auto_tune_on)
{
- uint8_t cmdCalibrate[] = {CMD_CALIBRATE, 0, autoTuneOn ? 1 : 0, 0, 0};
- return i2cWrite(BUF_COMMAND, cmdCalibrate, sizeof(cmdCalibrate));
+ uint8_t cmd_calibrate[] = {CMD_CALIBRATE, 0,
+ auto_tune_on ? 1 : 0, 0, 0};
+ return IT7260_i2cWrite(BUF_COMMAND, cmd_calibrate,
+ sizeof(cmd_calibrate));
}
-static ssize_t sysfsCalibrationStore(struct device *dev,
+static ssize_t sysfs_calibration_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
uint8_t resp;
- if (!chipSendCalibrationCmd(false))
- LOGE("failed to send calibration command\n");
- else {
- calibrationWasSuccessful =
- i2cRead(BUF_RESPONSE, &resp, sizeof(resp))
- ? SYSFS_RESULT_SUCCESS : SYSFS_RESULT_FAIL;
+ if (!IT7260_chipSendCalibrationCmd(false)) {
+ dev_err(dev, "failed to send calibration command\n");
+ } else {
+ gl_ts->calibration_success =
+ IT7260_i2cRead(BUF_RESPONSE, &resp, sizeof(resp));
/*
* previous logic that was here never called
- * chipFirmwareReinitialize() due to checking a
+ * IT7260_firmware_reinitialize() due to checking a
* guaranteed-not-null value against null. We now
* call it. Hopefully this is OK
*/
if (!resp)
- LOGI("chipFirmwareReinitialize -> %s\n",
- chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_6F)
+ dev_dbg(dev, "IT7260_firmware_reinitialize-> %s\n",
+ IT7260_firmware_reinitialize(CMD_FIRMWARE_REINIT_6F)
? "success" : "fail");
}
return count;
}
-static ssize_t sysfsPointShow(struct device *dev,
+static ssize_t sysfs_point_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- uint8_t pointData[sizeof(struct PointData)];
+ uint8_t point_data[sizeof(struct PointData)];
bool readSuccess;
ssize_t ret;
- readSuccess = i2cReadNoReadyCheck(BUF_POINT_INFO, pointData,
- sizeof(pointData));
- ret = snprintf(buf, MAX_BUFFER_SIZE,
- "point_show read ret[%d]--point[%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x]=\n",
- readSuccess, pointData[0], pointData[1], pointData[2],
- pointData[3], pointData[4], pointData[5], pointData[6],
- pointData[7], pointData[8], pointData[9], pointData[10],
- pointData[11], pointData[12], pointData[13]);
-
- LOGI("%s", buf);
+ readSuccess = IT7260_i2cReadNoReadyCheck(BUF_POINT_INFO, point_data,
+ sizeof(point_data));
+
+ if (readSuccess) {
+ ret = scnprintf(buf, MAX_BUFFER_SIZE,
+ "point_show read ret[%d]--point[%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x]\n",
+ readSuccess, point_data[0], point_data[1],
+ point_data[2], point_data[3], point_data[4],
+ point_data[5], point_data[6], point_data[7],
+ point_data[8], point_data[9], point_data[10],
+ point_data[11], point_data[12], point_data[13]);
+ } else {
+ ret = scnprintf(buf, MAX_BUFFER_SIZE,
+ "failed to read point data\n");
+ }
+ dev_dbg(dev, "%s", buf);
return ret;
}
-static ssize_t sysfsPointStore(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t sysfs_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- return count;
+ return scnprintf(buf, MAX_BUFFER_SIZE,
+ "fw{%X.%X.%X.%X} cfg{%X.%X.%X.%X}\n",
+ gl_ts->fw_ver[0], gl_ts->fw_ver[1], gl_ts->fw_ver[2],
+ gl_ts->fw_ver[3], gl_ts->cfg_ver[0], gl_ts->cfg_ver[1],
+ gl_ts->cfg_ver[2], gl_ts->cfg_ver[3]);
}
-static ssize_t sysfsStatusShow(struct device *dev,
+static ssize_t sysfs_sleep_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return snprintf(buf, MAX_BUFFER_SIZE, "%d\n", devicePresent ? 1 : 0);
+ /*
+ * The usefulness of this was questionable at best - we were at least
+ * leaking a byte of kernel data (by claiming to return a byte but not
+ * writing to buf. To fix this now we actually return the sleep status
+ */
+ *buf = gl_ts->suspended ? '1' : '0';
+ return 1;
}
-static ssize_t sysfsStatusStore(struct device *dev,
+static ssize_t sysfs_sleep_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- uint8_t verFw[10], verCfg[10];
+ int go_to_sleep, ret;
+
+ ret = kstrtoint(buf, 10, &go_to_sleep);
- chipGetVersions(verFw, verCfg, true);
+ /* (gl_ts->suspended == true && goToSleepVal > 0) means
+ * device is already suspended and you want it to be in sleep,
+ * (gl_ts->suspended == false && goToSleepVal == 0) means
+ * device is already active and you also want it to be active.
+ */
+ if ((gl_ts->suspended && go_to_sleep > 0) ||
+ (!gl_ts->suspended && go_to_sleep == 0))
+ dev_err(dev, "duplicate request to %s chip\n",
+ go_to_sleep ? "sleep" : "wake");
+ else if (go_to_sleep) {
+ disable_irq(gl_ts->client->irq);
+ IT7260_ts_chipLowPowerMode(PWR_CTL_SLEEP_MODE);
+ dev_dbg(dev, "touch is going to sleep...\n");
+ } else {
+ IT7260_ts_chipLowPowerMode(PWR_CTL_ACTIVE_MODE);
+ enable_irq(gl_ts->client->irq);
+ dev_dbg(dev, "touch is going to wake!\n");
+ }
+ gl_ts->suspended = go_to_sleep;
return count;
}
-static ssize_t sysfsVersionShow(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t sysfs_cfg_name_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
{
- uint8_t verFw[10], verCfg[10];
+ char *strptr;
- chipGetVersions(verFw, verCfg, false);
- return snprintf(buf, MAX_BUFFER_SIZE, "%x,%x,%x,%x # %x,%x,%x,%x\n",
- verFw[5], verFw[6], verFw[7], verFw[8],
- verCfg[1], verCfg[2], verCfg[3], verCfg[4]);
-}
+ if (count >= MAX_BUFFER_SIZE) {
+ dev_err(dev, "Input over %d chars long\n", MAX_BUFFER_SIZE);
+ return -EINVAL;
+ }
+
+ strptr = strnstr(buf, ".bin", count);
+ if (!strptr) {
+ dev_err(dev, "Input is invalid cfg file\n");
+ return -EINVAL;
+ }
+
+ strlcpy(gl_ts->cfg_name, buf, count);
-static ssize_t sysfsVersionStore(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
return count;
}
-static ssize_t sysfsSleepShow(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t sysfs_cfg_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- /*
- * The usefulness of this was questionable at best - we were at least
- * leaking a byte of kernel data (by claiming to return a byte but not
- * writing to buf. To fix this now we actually return the sleep status
- */
- if (!mutex_lock_interruptible(&sleepModeMutex)) {
- *buf = chipAwake ? '1' : '0';
- mutex_unlock(&sleepModeMutex);
- return 1;
- } else {
- return -EINTR;
- }
+ if (strnlen(gl_ts->cfg_name, MAX_BUFFER_SIZE) > 0)
+ return scnprintf(buf, MAX_BUFFER_SIZE, "%s\n",
+ gl_ts->cfg_name);
+ else
+ return scnprintf(buf, MAX_BUFFER_SIZE,
+ "No config file name given\n");
}
-static ssize_t sysfsSleepStore(struct device *dev,
+static ssize_t sysfs_fw_name_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- static const uint8_t cmdGoSleep[] = {CMD_PWR_CTL,
- 0x00, PWR_CTL_SLEEP_MODE};
- int goToSleepVal, ret;
- bool goToWake;
- uint8_t dummy;
+ char *strptr;
- ret = kstrtoint(buf, 10, &goToSleepVal);
- /* convert to bool of proper polarity */
- goToWake = !goToSleepVal;
-
- if (!mutex_lock_interruptible(&sleepModeMutex)) {
- if ((chipAwake && goToWake) || (!chipAwake && !goToWake))
- LOGE("duplicate request to %s chip\n",
- goToWake ? "wake" : "sleep");
- else if (goToWake) {
- i2cReadNoReadyCheck(BUF_QUERY, &dummy, sizeof(dummy));
- enable_irq(gl_ts->client->irq);
- LOGI("touch is going to wake!\n");
- } else {
- disable_irq(gl_ts->client->irq);
- i2cWriteNoReadyCheck(BUF_COMMAND, cmdGoSleep,
- sizeof(cmdGoSleep));
- LOGI("touch is going to sleep...\n");
- }
- chipAwake = goToWake;
- mutex_unlock(&sleepModeMutex);
- return count;
- } else {
- return -EINTR;
+ if (count >= MAX_BUFFER_SIZE) {
+ dev_err(dev, "Input over %d chars long\n", MAX_BUFFER_SIZE);
+ return -EINVAL;
}
-}
-
-static DEVICE_ATTR(status, S_IRUGO|S_IWUSR|S_IWGRP,
- sysfsStatusShow, sysfsStatusStore);
-static DEVICE_ATTR(version, S_IRUGO|S_IWUSR|S_IWGRP,
- sysfsVersionShow, sysfsVersionStore);
-static DEVICE_ATTR(sleep, S_IRUGO|S_IWUSR|S_IWGRP,
- sysfsSleepShow, sysfsSleepStore);
+ strptr = strnstr(buf, ".bin", count);
+ if (!strptr) {
+ dev_err(dev, "Input is invalid fw file\n");
+ return -EINVAL;
+ }
-static struct attribute *it7260_attrstatus[] = {
- &dev_attr_status.attr,
- &dev_attr_version.attr,
- &dev_attr_sleep.attr,
- NULL
-};
+ strlcpy(gl_ts->fw_name, buf, count);
+ return count;
+}
-static const struct attribute_group it7260_attrstatus_group = {
- .attrs = it7260_attrstatus,
-};
+static ssize_t sysfs_fw_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (strnlen(gl_ts->fw_name, MAX_BUFFER_SIZE) > 0)
+ return scnprintf(buf, MAX_BUFFER_SIZE, "%s\n",
+ gl_ts->fw_name);
+ else
+ return scnprintf(buf, MAX_BUFFER_SIZE,
+ "No firmware file name given\n");
+}
-static DEVICE_ATTR(calibration, S_IRUGO|S_IWUSR|S_IWGRP,
- sysfsCalibrationShow, sysfsCalibrationStore);
-static DEVICE_ATTR(upgrade, S_IRUGO|S_IWUSR|S_IWGRP,
- sysfsUpgradeShow, sysfsUpgradeStore);
-static DEVICE_ATTR(point, S_IRUGO|S_IWUSR|S_IWGRP,
- sysfsPointShow, sysfsPointStore);
+static DEVICE_ATTR(version, S_IRUGO | S_IWUSR,
+ sysfs_version_show, NULL);
+static DEVICE_ATTR(sleep, S_IRUGO | S_IWUSR,
+ sysfs_sleep_show, sysfs_sleep_store);
+static DEVICE_ATTR(calibration, S_IRUGO | S_IWUSR,
+ sysfs_calibration_show, sysfs_calibration_store);
+static DEVICE_ATTR(fw_update, S_IRUGO | S_IWUSR,
+ sysfs_fw_upgrade_show, sysfs_fw_upgrade_store);
+static DEVICE_ATTR(cfg_update, S_IRUGO | S_IWUSR,
+ sysfs_cfg_upgrade_show, sysfs_cfg_upgrade_store);
+static DEVICE_ATTR(point, S_IRUGO | S_IWUSR,
+ sysfs_point_show, NULL);
+static DEVICE_ATTR(fw_name, S_IRUGO | S_IWUSR,
+ sysfs_fw_name_show, sysfs_fw_name_store);
+static DEVICE_ATTR(cfg_name, S_IRUGO | S_IWUSR,
+ sysfs_cfg_name_show, sysfs_cfg_name_store);
+static DEVICE_ATTR(force_fw_update, S_IRUGO | S_IWUSR,
+ sysfs_force_fw_upgrade_show,
+ sysfs_force_fw_upgrade_store);
+static DEVICE_ATTR(force_cfg_update, S_IRUGO | S_IWUSR,
+ sysfs_force_cfg_upgrade_show,
+ sysfs_force_cfg_upgrade_store);
static struct attribute *it7260_attributes[] = {
+ &dev_attr_version.attr,
+ &dev_attr_sleep.attr,
&dev_attr_calibration.attr,
- &dev_attr_upgrade.attr,
+ &dev_attr_fw_update.attr,
+ &dev_attr_cfg_update.attr,
&dev_attr_point.attr,
+ &dev_attr_fw_name.attr,
+ &dev_attr_cfg_name.attr,
+ &dev_attr_force_fw_update.attr,
+ &dev_attr_force_cfg_update.attr,
NULL
};
@@ -650,199 +997,714 @@ static const struct attribute_group it7260_attr_group = {
.attrs = it7260_attributes,
};
-static void chipExternalCalibration(bool autoTuneEnabled)
+static void IT7260_chipExternalCalibration(bool autoTuneEnabled)
{
uint8_t resp[2];
- LOGI("sent calibration command -> %d\n",
- chipSendCalibrationCmd(autoTuneEnabled));
- waitDeviceReady(true, true);
- i2cReadNoReadyCheck(BUF_RESPONSE, resp, sizeof(resp));
- chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_C);
+ dev_dbg(&gl_ts->client->dev, "sent calibration command -> %d\n",
+ IT7260_chipSendCalibrationCmd(autoTuneEnabled));
+ IT7260_waitDeviceReady(true, true);
+ IT7260_i2cReadNoReadyCheck(BUF_RESPONSE, resp, sizeof(resp));
+ IT7260_firmware_reinitialize(CMD_FIRMWARE_REINIT_C);
}
-void sendCalibrationCmd(void)
+void IT7260_sendCalibrationCmd(void)
{
- chipExternalCalibration(false);
+ IT7260_chipExternalCalibration(false);
}
-EXPORT_SYMBOL(sendCalibrationCmd);
+EXPORT_SYMBOL(IT7260_sendCalibrationCmd);
-static void readFingerData(uint16_t *xP, uint16_t *yP, uint8_t *pressureP,
- const struct FingerData *fd)
+static void IT7260_ts_release_all(void)
{
- uint16_t x = fd->xLo;
- uint16_t y = fd->yLo;
+ int finger;
- x += ((uint16_t)(fd->hi & 0x0F)) << 8;
- y += ((uint16_t)(fd->hi & 0xF0)) << 4;
+ for (finger = 0; finger < gl_ts->pdata->num_of_fingers; finger++) {
+ input_mt_slot(gl_ts->input_dev, finger);
+ input_mt_report_slot_state(gl_ts->input_dev,
+ MT_TOOL_FINGER, 0);
+ }
- if (xP)
- *xP = x;
- if (yP)
- *yP = y;
- if (pressureP)
- *pressureP = fd->pressure & FD_PRESSURE_BITS;
+ input_report_key(gl_ts->input_dev, BTN_TOUCH, 0);
+ input_sync(gl_ts->input_dev);
}
-static void readTouchDataPoint(void)
+static irqreturn_t IT7260_ts_threaded_handler(int irq, void *devid)
{
- struct PointData pointData;
- uint8_t devStatus;
- uint8_t pressure = FD_PRESSURE_NONE;
- uint16_t x, y;
+ struct PointData point_data;
+ struct input_dev *input_dev = gl_ts->input_dev;
+ u8 dev_status, finger, touch_count = 0, finger_status;
+ u8 pressure = FD_PRESSURE_NONE;
+ u16 x, y;
+ bool palm_detected;
/* verify there is point data to read & it is readable and valid */
- i2cReadNoReadyCheck(BUF_QUERY, &devStatus, sizeof(devStatus));
- if (!((devStatus & PT_INFO_BITS) & PT_INFO_YES)) {
- pr_err("readTouchDataPoint() called when no data available (0x%02X)\n",
- devStatus);
- return;
- }
- if (!i2cReadNoReadyCheck(BUF_POINT_INFO, (void *)&pointData,
- sizeof(pointData))) {
- pr_err("readTouchDataPoint() failed to read point data buffer\n");
- return;
- }
- if ((pointData.flags & PD_FLAGS_DATA_TYPE_BITS) !=
+ IT7260_i2cReadNoReadyCheck(BUF_QUERY, &dev_status, sizeof(dev_status));
+ if (!((dev_status & PT_INFO_BITS) & PT_INFO_YES))
+ return IRQ_HANDLED;
+ if (!IT7260_i2cReadNoReadyCheck(BUF_POINT_INFO, (void *)&point_data,
+ sizeof(point_data))) {
+ dev_err(&gl_ts->client->dev,
+ "failed to read point data buffer\n");
+ return IRQ_HANDLED;
+ }
+
+ /* Check if controller moves from idle to active state */
+ if ((point_data.flags & PD_FLAGS_DATA_TYPE_BITS) !=
PD_FLAGS_DATA_TYPE_TOUCH) {
- pr_err("readTouchDataPoint() dropping non-point data of type 0x%02X\n",
- pointData.flags);
- return;
+ /*
+ * This code adds the touch-to-wake functionality to the ITE
+ * tech driver. When user puts a finger on touch controller in
+ * idle state, the controller moves to active state and driver
+ * sends the KEY_WAKEUP event to wake the device. The
+ * pm_stay_awake() call tells the pm core to stay awake until
+ * the CPU cores are up already. The schedule_work() call
+ * schedule a work that tells the pm core to relax once the CPU
+ * cores are up.
+ */
+ if (gl_ts->device_needs_wakeup) {
+ pm_stay_awake(&gl_ts->client->dev);
+ input_report_key(input_dev, KEY_WAKEUP, 1);
+ input_sync(input_dev);
+ input_report_key(input_dev, KEY_WAKEUP, 0);
+ input_sync(input_dev);
+ schedule_work(&gl_ts->work_pm_relax);
+ return IRQ_HANDLED;
+ }
}
- if ((pointData.flags & PD_FLAGS_HAVE_FINGERS) & 1)
- readFingerData(&x, &y, &pressure, pointData.fd);
+ palm_detected = point_data.palm & PD_PALM_FLAG_BIT;
+ if (palm_detected && gl_ts->pdata->palm_detect_en) {
+ input_report_key(input_dev,
+ gl_ts->pdata->palm_detect_keycode, 1);
+ input_sync(input_dev);
+ input_report_key(input_dev,
+ gl_ts->pdata->palm_detect_keycode, 0);
+ input_sync(input_dev);
+ }
- if (pressure >= FD_PRESSURE_LIGHT) {
+ for (finger = 0; finger < gl_ts->pdata->num_of_fingers; finger++) {
+ finger_status = point_data.flags & (0x01 << finger);
+
+ input_mt_slot(input_dev, finger);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
+ finger_status != 0);
+
+ x = point_data.fd[finger].xLo +
+ (((u16)(point_data.fd[finger].hi & 0x0F)) << 8);
+ y = point_data.fd[finger].yLo +
+ (((u16)(point_data.fd[finger].hi & 0xF0)) << 4);
+
+ pressure = point_data.fd[finger].pressure & FD_PRESSURE_BITS;
+
+ if (finger_status) {
+ if (pressure >= FD_PRESSURE_LIGHT) {
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ input_report_abs(input_dev,
+ ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev,
+ ABS_MT_POSITION_Y, y);
+ touch_count++;
+ }
+ }
+ }
- if (!hadFingerDown)
- hadFingerDown = true;
+ input_report_key(input_dev, BTN_TOUCH, touch_count > 0);
+ input_sync(input_dev);
- readFingerData(&x, &y, &pressure, pointData.fd);
+ return IRQ_HANDLED;
+}
- input_report_abs(gl_ts->input_dev, ABS_X, x);
- input_report_abs(gl_ts->input_dev, ABS_Y, y);
- input_report_key(gl_ts->input_dev, BTN_TOUCH, 1);
- input_sync(gl_ts->input_dev);
+static void IT7260_ts_work_func(struct work_struct *work)
+{
+ pm_relax(&gl_ts->client->dev);
+}
- } else if (hadFingerDown) {
- hadFingerDown = false;
+static int IT7260_chipIdentify(void)
+{
+ static const uint8_t cmd_ident[] = {CMD_IDENT_CHIP};
+ static const uint8_t expected_id[] = {0x0A, 'I', 'T', 'E', '7',
+ '2', '6', '0'};
+ uint8_t chip_id[10] = {0,};
- input_report_key(gl_ts->input_dev, BTN_TOUCH, 0);
- input_sync(gl_ts->input_dev);
+ IT7260_waitDeviceReady(false, false);
+
+ if (!IT7260_i2cWriteNoReadyCheck(BUF_COMMAND, cmd_ident,
+ sizeof(cmd_ident))) {
+ dev_err(&gl_ts->client->dev, "failed to write CMD_IDENT_CHIP\n");
+ return -ENODEV;
}
+ IT7260_waitDeviceReady(false, false);
+
+ if (!IT7260_i2cReadNoReadyCheck(BUF_RESPONSE, chip_id,
+ sizeof(chip_id))) {
+ dev_err(&gl_ts->client->dev, "failed to read chip-id\n");
+ return -ENODEV;
+ }
+ dev_info(&gl_ts->client->dev,
+ "IT7260_chipIdentify read id: %02X %c%c%c%c%c%c%c %c%c\n",
+ chip_id[0], chip_id[1], chip_id[2], chip_id[3], chip_id[4],
+ chip_id[5], chip_id[6], chip_id[7], chip_id[8], chip_id[9]);
+
+ if (memcmp(chip_id, expected_id, sizeof(expected_id)))
+ return -EINVAL;
+
+ if (chip_id[8] == '5' && chip_id[9] == '6')
+ dev_info(&gl_ts->client->dev, "rev BX3 found\n");
+ else if (chip_id[8] == '6' && chip_id[9] == '6')
+ dev_info(&gl_ts->client->dev, "rev BX4 found\n");
+ else
+ dev_info(&gl_ts->client->dev, "unknown revision (0x%02X 0x%02X) found\n",
+ chip_id[8], chip_id[9]);
+
+ return 0;
}
-static irqreturn_t IT7260_ts_threaded_handler(int irq, void *devid)
+static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA)
{
- readTouchDataPoint();
- return IRQ_HANDLED;
+ return (regulator_count_voltages(reg) > 0) ?
+ regulator_set_optimum_mode(reg, load_uA) : 0;
}
-static bool chipIdentifyIT7260(void)
+static int IT7260_regulator_configure(bool on)
{
- static const uint8_t cmdIdent[] = {CMD_IDENT_CHIP};
- static const uint8_t expectedID[] = {0x0A, 'I', 'T', 'E', '7',
- '2', '6', '0'};
- uint8_t chipID[10] = {0,};
+ int retval;
- waitDeviceReady(true, false);
+ if (on == false)
+ goto hw_shutdown;
- if (!i2cWriteNoReadyCheck(BUF_COMMAND, cmdIdent, sizeof(cmdIdent))) {
- LOGE("i2cWrite() failed\n");
- return false;
+ gl_ts->vdd = devm_regulator_get(&gl_ts->client->dev, "vdd");
+ if (IS_ERR(gl_ts->vdd)) {
+ dev_err(&gl_ts->client->dev,
+ "%s: Failed to get vdd regulator\n", __func__);
+ return PTR_ERR(gl_ts->vdd);
}
- waitDeviceReady(true, false);
+ if (regulator_count_voltages(gl_ts->vdd) > 0) {
+ retval = regulator_set_voltage(gl_ts->vdd,
+ IT_VTG_MIN_UV, IT_VTG_MAX_UV);
+ if (retval) {
+ dev_err(&gl_ts->client->dev,
+ "regulator set_vtg failed retval =%d\n",
+ retval);
+ goto err_set_vtg_vdd;
+ }
+ }
- if (!i2cReadNoReadyCheck(BUF_RESPONSE, chipID, sizeof(chipID))) {
- LOGE("i2cRead() failed\n");
- return false;
+ gl_ts->avdd = devm_regulator_get(&gl_ts->client->dev, "avdd");
+ if (IS_ERR(gl_ts->avdd)) {
+ dev_err(&gl_ts->client->dev,
+ "%s: Failed to get i2c regulator\n", __func__);
+ retval = PTR_ERR(gl_ts->avdd);
+ goto err_get_vtg_i2c;
}
- LOGI("chipIdentifyIT7260 read id: %02X %c%c%c%c%c%c%ci %c%c\n",
- chipID[0], chipID[1], chipID[2], chipID[3], chipID[4],
- chipID[5], chipID[6], chipID[7], chipID[8], chipID[9]);
- if (memcmp(chipID, expectedID, sizeof(expectedID)))
- return false;
+ if (regulator_count_voltages(gl_ts->avdd) > 0) {
+ retval = regulator_set_voltage(gl_ts->avdd,
+ IT_I2C_VTG_MIN_UV, IT_I2C_VTG_MAX_UV);
+ if (retval) {
+ dev_err(&gl_ts->client->dev,
+ "reg set i2c vtg failed retval =%d\n",
+ retval);
+ goto err_set_vtg_i2c;
+ }
+ }
- if (chipID[8] == '5' && chipID[9] == '6')
- LOGI("rev BX3 found\n");
- else if (chipID[8] == '6' && chipID[9] == '6')
- LOGI("rev BX4 found\n");
- else
- LOGI("unknown revision (0x%02X 0x%02X) found\n",
- chipID[8], chipID[9]);
+ return 0;
- return true;
+err_set_vtg_i2c:
+err_get_vtg_i2c:
+ if (regulator_count_voltages(gl_ts->vdd) > 0)
+ regulator_set_voltage(gl_ts->vdd, 0, IT_VTG_MAX_UV);
+err_set_vtg_vdd:
+ return retval;
+
+hw_shutdown:
+ if (regulator_count_voltages(gl_ts->vdd) > 0)
+ regulator_set_voltage(gl_ts->vdd, 0, IT_VTG_MAX_UV);
+ if (regulator_count_voltages(gl_ts->avdd) > 0)
+ regulator_set_voltage(gl_ts->avdd, 0, IT_I2C_VTG_MAX_UV);
+ return 0;
+};
+
+static int IT7260_power_on(bool on)
+{
+ int retval;
+
+ if (on == false)
+ goto power_off;
+
+ retval = reg_set_optimum_mode_check(gl_ts->vdd,
+ IT_ACTIVE_LOAD_UA);
+ if (retval < 0) {
+ dev_err(&gl_ts->client->dev,
+ "Regulator vdd set_opt failed rc=%d\n",
+ retval);
+ return retval;
+ }
+
+ retval = regulator_enable(gl_ts->vdd);
+ if (retval) {
+ dev_err(&gl_ts->client->dev,
+ "Regulator vdd enable failed rc=%d\n",
+ retval);
+ goto error_reg_en_vdd;
+ }
+
+ retval = reg_set_optimum_mode_check(gl_ts->avdd,
+ IT_I2C_ACTIVE_LOAD_UA);
+ if (retval < 0) {
+ dev_err(&gl_ts->client->dev,
+ "Regulator avdd set_opt failed rc=%d\n",
+ retval);
+ goto error_reg_opt_i2c;
+ }
+
+ retval = regulator_enable(gl_ts->avdd);
+ if (retval) {
+ dev_err(&gl_ts->client->dev,
+ "Regulator avdd enable failed rc=%d\n",
+ retval);
+ goto error_reg_en_avdd;
+ }
+
+ return 0;
+
+error_reg_en_avdd:
+ reg_set_optimum_mode_check(gl_ts->avdd, 0);
+error_reg_opt_i2c:
+ regulator_disable(gl_ts->vdd);
+error_reg_en_vdd:
+ reg_set_optimum_mode_check(gl_ts->vdd, 0);
+ return retval;
+
+power_off:
+ reg_set_optimum_mode_check(gl_ts->vdd, 0);
+ regulator_disable(gl_ts->vdd);
+ reg_set_optimum_mode_check(gl_ts->avdd, 0);
+ regulator_disable(gl_ts->avdd);
+
+ return 0;
+}
+
+static int IT7260_gpio_configure(bool on)
+{
+ int retval = 0;
+
+ if (on) {
+ if (gpio_is_valid(gl_ts->pdata->irq_gpio)) {
+ /* configure touchscreen irq gpio */
+ retval = gpio_request(gl_ts->pdata->irq_gpio,
+ "ite_irq_gpio");
+ if (retval) {
+ dev_err(&gl_ts->client->dev,
+ "unable to request irq gpio [%d]\n",
+ retval);
+ goto err_irq_gpio_req;
+ }
+
+ retval = gpio_direction_input(gl_ts->pdata->irq_gpio);
+ if (retval) {
+ dev_err(&gl_ts->client->dev,
+ "unable to set direction for irq gpio [%d]\n",
+ retval);
+ goto err_irq_gpio_dir;
+ }
+ } else {
+ dev_err(&gl_ts->client->dev,
+ "irq gpio not provided\n");
+ goto err_irq_gpio_req;
+ }
+
+ if (gpio_is_valid(gl_ts->pdata->reset_gpio)) {
+ /* configure touchscreen reset out gpio */
+ retval = gpio_request(gl_ts->pdata->reset_gpio,
+ "ite_reset_gpio");
+ if (retval) {
+ dev_err(&gl_ts->client->dev,
+ "unable to request reset gpio [%d]\n",
+ retval);
+ goto err_reset_gpio_req;
+ }
+
+ retval = gpio_direction_output(
+ gl_ts->pdata->reset_gpio, 1);
+ if (retval) {
+ dev_err(&gl_ts->client->dev,
+ "unable to set direction for reset gpio [%d]\n",
+ retval);
+ goto err_reset_gpio_dir;
+ }
+
+ if (gl_ts->pdata->low_reset)
+ gpio_set_value(gl_ts->pdata->reset_gpio, 0);
+ else
+ gpio_set_value(gl_ts->pdata->reset_gpio, 1);
+
+ msleep(gl_ts->pdata->reset_delay);
+ } else {
+ dev_err(&gl_ts->client->dev,
+ "reset gpio not provided\n");
+ goto err_reset_gpio_req;
+ }
+ } else {
+ if (gpio_is_valid(gl_ts->pdata->irq_gpio))
+ gpio_free(gl_ts->pdata->irq_gpio);
+ if (gpio_is_valid(gl_ts->pdata->reset_gpio)) {
+ /*
+ * This is intended to save leakage current
+ * only. Even if the call(gpio_direction_input)
+ * fails, only leakage current will be more but
+ * functionality will not be affected.
+ */
+ retval = gpio_direction_input(gl_ts->pdata->reset_gpio);
+ if (retval) {
+ dev_err(&gl_ts->client->dev,
+ "unable to set direction for gpio reset [%d]\n",
+ retval);
+ }
+ gpio_free(gl_ts->pdata->reset_gpio);
+ }
+ }
+
+ return 0;
+
+err_reset_gpio_dir:
+ if (gpio_is_valid(gl_ts->pdata->reset_gpio))
+ gpio_free(gl_ts->pdata->reset_gpio);
+err_reset_gpio_req:
+err_irq_gpio_dir:
+ if (gpio_is_valid(gl_ts->pdata->irq_gpio))
+ gpio_free(gl_ts->pdata->irq_gpio);
+err_irq_gpio_req:
+ return retval;
+}
+
+#if CONFIG_OF
+static int IT7260_get_dt_coords(struct device *dev, char *name,
+ struct IT7260_ts_platform_data *pdata)
+{
+ u32 coords[IT7260_COORDS_ARR_SIZE];
+ struct property *prop;
+ struct device_node *np = dev->of_node;
+ int coords_size, rc;
+
+ prop = of_find_property(np, name, NULL);
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+
+ coords_size = prop->length / sizeof(u32);
+ if (coords_size != IT7260_COORDS_ARR_SIZE) {
+ dev_err(dev, "invalid %s\n", name);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32_array(np, name, coords, coords_size);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read %s\n", name);
+ return rc;
+ }
+
+ if (strcmp(name, "ite,panel-coords") == 0) {
+ pdata->panel_minx = coords[0];
+ pdata->panel_miny = coords[1];
+ pdata->panel_maxx = coords[2];
+ pdata->panel_maxy = coords[3];
+
+ if (pdata->panel_maxx == 0 || pdata->panel_minx > 0)
+ rc = -EINVAL;
+ else if (pdata->panel_maxy == 0 || pdata->panel_miny > 0)
+ rc = -EINVAL;
+
+ if (rc) {
+ dev_err(dev, "Invalid panel resolution %d\n", rc);
+ return rc;
+ }
+ } else if (strcmp(name, "ite,display-coords") == 0) {
+ pdata->disp_minx = coords[0];
+ pdata->disp_miny = coords[1];
+ pdata->disp_maxx = coords[2];
+ pdata->disp_maxy = coords[3];
+ } else {
+ dev_err(dev, "unsupported property %s\n", name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int IT7260_parse_dt(struct device *dev,
+ struct IT7260_ts_platform_data *pdata)
+{
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+ int rc;
+
+ /* reset, irq gpio info */
+ pdata->reset_gpio = of_get_named_gpio_flags(np,
+ "ite,reset-gpio", 0, &pdata->reset_gpio_flags);
+ pdata->irq_gpio = of_get_named_gpio_flags(np,
+ "ite,irq-gpio", 0, &pdata->irq_gpio_flags);
+
+ rc = of_property_read_u32(np, "ite,num-fingers", &temp_val);
+ if (!rc)
+ pdata->num_of_fingers = temp_val;
+ else if (rc != -EINVAL) {
+ dev_err(dev, "Unable to read reset delay\n");
+ return rc;
+ }
+
+ pdata->wakeup = of_property_read_bool(np, "ite,wakeup");
+ pdata->palm_detect_en = of_property_read_bool(np, "ite,palm-detect-en");
+ if (pdata->palm_detect_en) {
+ rc = of_property_read_u32(np, "ite,palm-detect-keycode",
+ &temp_val);
+ if (!rc) {
+ pdata->palm_detect_keycode = temp_val;
+ } else {
+ dev_err(dev, "Unable to read palm-detect-keycode\n");
+ return rc;
+ }
+ }
+
+ rc = of_property_read_string(np, "ite,fw-name", &pdata->fw_name);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read fw image name %d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_string(np, "ite,cfg-name", &pdata->cfg_name);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read cfg image name %d\n", rc);
+ return rc;
+ }
+
+ snprintf(gl_ts->fw_name, MAX_BUFFER_SIZE, "%s",
+ (pdata->fw_name != NULL) ? pdata->fw_name : FW_NAME);
+ snprintf(gl_ts->cfg_name, MAX_BUFFER_SIZE, "%s",
+ (pdata->cfg_name != NULL) ? pdata->cfg_name : CFG_NAME);
+
+ rc = of_property_read_u32(np, "ite,reset-delay", &temp_val);
+ if (!rc)
+ pdata->reset_delay = temp_val;
+ else if (rc != -EINVAL) {
+ dev_err(dev, "Unable to read reset delay\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(np, "ite,avdd-lpm-cur", &temp_val);
+ if (!rc) {
+ pdata->avdd_lpm_cur = temp_val;
+ } else if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read avdd lpm current value %d\n", rc);
+ return rc;
+ }
+
+ pdata->low_reset = of_property_read_bool(np, "ite,low-reset");
+
+ rc = IT7260_get_dt_coords(dev, "ite,display-coords", pdata);
+ if (rc && (rc != -EINVAL))
+ return rc;
+
+ rc = IT7260_get_dt_coords(dev, "ite,panel-coords", pdata);
+ if (rc && (rc != -EINVAL))
+ return rc;
+
+ return 0;
+}
+#else
+static inline int IT7260_ts_parse_dt(struct device *dev,
+ struct IT7260_ts_platform_data *pdata)
+{
+ return 0;
+}
+#endif
+
+static int IT7260_ts_pinctrl_init(struct IT7260_ts_data *ts_data)
+{
+ int retval;
+
+ /* Get pinctrl if target uses pinctrl */
+ ts_data->ts_pinctrl = devm_pinctrl_get(&(ts_data->client->dev));
+ if (IS_ERR_OR_NULL(ts_data->ts_pinctrl)) {
+ retval = PTR_ERR(ts_data->ts_pinctrl);
+ dev_dbg(&ts_data->client->dev,
+ "Target does not use pinctrl %d\n", retval);
+ goto err_pinctrl_get;
+ }
+
+ ts_data->pinctrl_state_active
+ = pinctrl_lookup_state(ts_data->ts_pinctrl,
+ PINCTRL_STATE_ACTIVE);
+ if (IS_ERR_OR_NULL(ts_data->pinctrl_state_active)) {
+ retval = PTR_ERR(ts_data->pinctrl_state_active);
+ dev_err(&ts_data->client->dev,
+ "Can not lookup %s pinstate %d\n",
+ PINCTRL_STATE_ACTIVE, retval);
+ goto err_pinctrl_lookup;
+ }
+
+ ts_data->pinctrl_state_suspend
+ = pinctrl_lookup_state(ts_data->ts_pinctrl,
+ PINCTRL_STATE_SUSPEND);
+ if (IS_ERR_OR_NULL(ts_data->pinctrl_state_suspend)) {
+ retval = PTR_ERR(ts_data->pinctrl_state_suspend);
+ dev_err(&ts_data->client->dev,
+ "Can not lookup %s pinstate %d\n",
+ PINCTRL_STATE_SUSPEND, retval);
+ goto err_pinctrl_lookup;
+ }
+
+ ts_data->pinctrl_state_release
+ = pinctrl_lookup_state(ts_data->ts_pinctrl,
+ PINCTRL_STATE_RELEASE);
+ if (IS_ERR_OR_NULL(ts_data->pinctrl_state_release)) {
+ retval = PTR_ERR(ts_data->pinctrl_state_release);
+ dev_dbg(&ts_data->client->dev,
+ "Can not lookup %s pinstate %d\n",
+ PINCTRL_STATE_RELEASE, retval);
+ }
+
+ return 0;
+
+err_pinctrl_lookup:
+ devm_pinctrl_put(ts_data->ts_pinctrl);
+err_pinctrl_get:
+ ts_data->ts_pinctrl = NULL;
+ return retval;
}
static int IT7260_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- static const uint8_t cmdStart[] = {CMD_UNKNOWN_7};
- struct IT7260_i2c_platform_data *pdata;
+ static const uint8_t cmd_start[] = {CMD_UNKNOWN_7};
+ struct IT7260_ts_platform_data *pdata;
uint8_t rsp[2];
- int ret = -1;
+ int ret = -1, err;
+ struct dentry *temp;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- LOGE("need I2C_FUNC_I2C\n");
- ret = -ENODEV;
- goto err_out;
+ dev_err(&client->dev, "need I2C_FUNC_I2C\n");
+ return -ENODEV;
}
- if (!client->irq) {
- LOGE("need IRQ\n");
- ret = -ENODEV;
- goto err_out;
- }
- gl_ts = kzalloc(sizeof(*gl_ts), GFP_KERNEL);
- if (!gl_ts) {
- ret = -ENOMEM;
- goto err_out;
- }
+ gl_ts = devm_kzalloc(&client->dev, sizeof(*gl_ts), GFP_KERNEL);
+ if (!gl_ts)
+ return -ENOMEM;
gl_ts->client = client;
i2c_set_clientdata(client, gl_ts);
- pdata = client->dev.platform_data;
- if (sysfs_create_group(&(client->dev.kobj), &it7260_attrstatus_group)) {
- dev_err(&client->dev, "failed to register sysfs #1\n");
- goto err_sysfs_grp_create_1;
+ if (client->dev.platform_data == NULL)
+ return -ENODEV;
+
+ if (client->dev.of_node) {
+ pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ ret = IT7260_parse_dt(&client->dev, pdata);
+ if (ret)
+ return ret;
+ } else {
+ pdata = client->dev.platform_data;
+ }
+
+ if (!pdata)
+ return -ENOMEM;
+
+ gl_ts->pdata = pdata;
+
+ ret = IT7260_regulator_configure(true);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to configure regulators\n");
+ goto err_reg_configure;
}
- if (!chipIdentifyIT7260()) {
- LOGI("chipIdentifyIT7260 FAIL");
- goto err_ident_fail_or_input_alloc;
+ ret = IT7260_power_on(true);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to power on\n");
+ goto err_power_device;
}
- input_dev = input_allocate_device();
- if (!input_dev) {
- LOGE("failed to allocate input device\n");
+ /*
+ * After enabling regulators, controller needs a delay to come to
+ * an active state.
+ */
+ msleep(DELAY_VTG_REG_EN);
+
+ ret = IT7260_ts_pinctrl_init(gl_ts);
+ if (!ret && gl_ts->ts_pinctrl) {
+ /*
+ * Pinctrl handle is optional. If pinctrl handle is found
+ * let pins to be configured in active state. If not
+ * found continue further without error.
+ */
+ ret = pinctrl_select_state(gl_ts->ts_pinctrl,
+ gl_ts->pinctrl_state_active);
+ if (ret < 0) {
+ dev_err(&gl_ts->client->dev,
+ "failed to select pin to active state %d",
+ ret);
+ }
+ } else {
+ ret = IT7260_gpio_configure(true);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to configure gpios\n");
+ goto err_gpio_config;
+ }
+ }
+
+ ret = IT7260_chipIdentify();
+ if (ret) {
+ dev_err(&client->dev, "Failed to identify chip %d!!!", ret);
+ goto err_identification_fail;
+ }
+
+ IT7260_get_chip_versions(&client->dev);
+
+ gl_ts->input_dev = input_allocate_device();
+ if (!gl_ts->input_dev) {
+ dev_err(&client->dev, "failed to allocate input device\n");
ret = -ENOMEM;
- goto err_ident_fail_or_input_alloc;
- }
- gl_ts->input_dev = input_dev;
-
- input_dev->name = DEVICE_NAME;
- input_dev->phys = "I2C";
- input_dev->id.bustype = BUS_I2C;
- input_dev->id.vendor = 0x0001;
- input_dev->id.product = 0x7260;
- set_bit(EV_SYN, input_dev->evbit);
- set_bit(EV_KEY, input_dev->evbit);
- set_bit(EV_ABS, input_dev->evbit);
- set_bit(INPUT_PROP_DIRECT,input_dev->propbit);
- set_bit(BTN_TOUCH, input_dev->keybit);
- set_bit(KEY_SLEEP,input_dev->keybit);
- set_bit(KEY_WAKEUP,input_dev->keybit);
- set_bit(KEY_POWER,input_dev->keybit);
- input_set_abs_params(input_dev, ABS_X, 0, SCREEN_X_RESOLUTION, 0, 0);
- input_set_abs_params(input_dev, ABS_Y, 0, SCREEN_Y_RESOLUTION, 0, 0);
-
- if (input_register_device(input_dev)) {
- LOGE("failed to register input device\n");
+ goto err_input_alloc;
+ }
+
+ /* Initialize mutex for fw and cfg upgrade */
+ mutex_init(&gl_ts->fw_cfg_mutex);
+
+ gl_ts->input_dev->name = DEVICE_NAME;
+ gl_ts->input_dev->phys = "I2C";
+ gl_ts->input_dev->id.bustype = BUS_I2C;
+ gl_ts->input_dev->id.vendor = 0x0001;
+ gl_ts->input_dev->id.product = 0x7260;
+ set_bit(EV_SYN, gl_ts->input_dev->evbit);
+ set_bit(EV_KEY, gl_ts->input_dev->evbit);
+ set_bit(EV_ABS, gl_ts->input_dev->evbit);
+ set_bit(INPUT_PROP_DIRECT, gl_ts->input_dev->propbit);
+ set_bit(BTN_TOUCH, gl_ts->input_dev->keybit);
+ input_set_abs_params(gl_ts->input_dev, ABS_MT_POSITION_X,
+ gl_ts->pdata->disp_minx, gl_ts->pdata->disp_maxx, 0, 0);
+ input_set_abs_params(gl_ts->input_dev, ABS_MT_POSITION_Y,
+ gl_ts->pdata->disp_miny, gl_ts->pdata->disp_maxy, 0, 0);
+ input_mt_init_slots(gl_ts->input_dev, gl_ts->pdata->num_of_fingers, 0);
+
+ input_set_drvdata(gl_ts->input_dev, gl_ts);
+
+ if (pdata->wakeup) {
+ set_bit(KEY_WAKEUP, gl_ts->input_dev->keybit);
+ INIT_WORK(&gl_ts->work_pm_relax, IT7260_ts_work_func);
+ device_init_wakeup(&client->dev, pdata->wakeup);
+ }
+
+ if (pdata->palm_detect_en)
+ set_bit(gl_ts->pdata->palm_detect_keycode,
+ gl_ts->input_dev->keybit);
+
+ if (input_register_device(gl_ts->input_dev)) {
+ dev_err(&client->dev, "failed to register input device\n");
goto err_input_register;
}
@@ -854,45 +1716,271 @@ static int IT7260_ts_probe(struct i2c_client *client,
if (sysfs_create_group(&(client->dev.kobj), &it7260_attr_group)) {
dev_err(&client->dev, "failed to register sysfs #2\n");
- goto err_sysfs_grp_create_2;
+ goto err_sysfs_grp_create;
}
+
+#if defined(CONFIG_FB)
+ gl_ts->fb_notif.notifier_call = fb_notifier_callback;
+
+ ret = fb_register_client(&gl_ts->fb_notif);
+ if (ret)
+ dev_err(&client->dev, "Unable to register fb_notifier %d\n",
+ ret);
+#endif
- devicePresent = true;
+ IT7260_i2cWriteNoReadyCheck(BUF_COMMAND, cmd_start, sizeof(cmd_start));
+ msleep(pdata->reset_delay);
+ IT7260_i2cReadNoReadyCheck(BUF_RESPONSE, rsp, sizeof(rsp));
+ msleep(pdata->reset_delay);
+
+ gl_ts->dir = debugfs_create_dir(DEBUGFS_DIR_NAME, NULL);
+ if (gl_ts->dir == NULL || IS_ERR(gl_ts->dir)) {
+ dev_err(&client->dev,
+ "%s: Failed to create debugfs directory, ret = %ld\n",
+ __func__, PTR_ERR(gl_ts->dir));
+ ret = PTR_ERR(gl_ts->dir);
+ goto err_create_debugfs_dir;
+ }
- i2cWriteNoReadyCheck(BUF_COMMAND, cmdStart, sizeof(cmdStart));
- mdelay(10);
- i2cReadNoReadyCheck(BUF_RESPONSE, rsp, sizeof(rsp));
- mdelay(10);
+ temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, gl_ts->dir,
+ gl_ts, &debug_suspend_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ dev_err(&client->dev,
+ "%s: Failed to create suspend debugfs file, ret = %ld\n",
+ __func__, PTR_ERR(temp));
+ ret = PTR_ERR(temp);
+ goto err_create_debugfs_file;
+ }
return 0;
-err_sysfs_grp_create_2:
+err_create_debugfs_file:
+ debugfs_remove_recursive(gl_ts->dir);
+err_create_debugfs_dir:
+#if defined(CONFIG_FB)
+ if (fb_unregister_client(&gl_ts->fb_notif))
+ dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n");
+#endif
+ sysfs_remove_group(&(client->dev.kobj), &it7260_attr_group);
+
+err_sysfs_grp_create:
free_irq(client->irq, gl_ts);
err_irq_reg:
- input_unregister_device(input_dev);
- input_dev = NULL;
+ input_unregister_device(gl_ts->input_dev);
err_input_register:
- if (input_dev)
- input_free_device(input_dev);
+ if (pdata->wakeup) {
+ cancel_work_sync(&gl_ts->work_pm_relax);
+ device_init_wakeup(&client->dev, false);
+ }
+ if (gl_ts->input_dev)
+ input_free_device(gl_ts->input_dev);
+ gl_ts->input_dev = NULL;
+
+err_input_alloc:
+err_identification_fail:
+ if (gl_ts->ts_pinctrl) {
+ if (IS_ERR_OR_NULL(gl_ts->pinctrl_state_release)) {
+ devm_pinctrl_put(gl_ts->ts_pinctrl);
+ gl_ts->ts_pinctrl = NULL;
+ } else {
+ err = pinctrl_select_state(gl_ts->ts_pinctrl,
+ gl_ts->pinctrl_state_release);
+ if (err)
+ dev_err(&gl_ts->client->dev,
+ "failed to select relase pinctrl state %d\n",
+ err);
+ }
+ } else {
+ if (gpio_is_valid(pdata->reset_gpio))
+ gpio_free(pdata->reset_gpio);
+ if (gpio_is_valid(pdata->irq_gpio))
+ gpio_free(pdata->irq_gpio);
+ }
-err_ident_fail_or_input_alloc:
- sysfs_remove_group(&(client->dev.kobj), &it7260_attrstatus_group);
+err_gpio_config:
+ IT7260_power_on(false);
-err_sysfs_grp_create_1:
- kfree(gl_ts);
+err_power_device:
+ IT7260_regulator_configure(false);
-err_out:
+err_reg_configure:
return ret;
}
static int IT7260_ts_remove(struct i2c_client *client)
{
- devicePresent = false;
+ int ret;
+
+ debugfs_remove_recursive(gl_ts->dir);
+#if defined(CONFIG_FB)
+ if (fb_unregister_client(&gl_ts->fb_notif))
+ dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n");
+#endif
+ sysfs_remove_group(&(client->dev.kobj), &it7260_attr_group);
+ free_irq(client->irq, gl_ts);
+ input_unregister_device(gl_ts->input_dev);
+ if (gl_ts->input_dev)
+ input_free_device(gl_ts->input_dev);
+ gl_ts->input_dev = NULL;
+ if (gl_ts->pdata->wakeup) {
+ cancel_work_sync(&gl_ts->work_pm_relax);
+ device_init_wakeup(&client->dev, false);
+ }
+ if (gl_ts->ts_pinctrl) {
+ if (IS_ERR_OR_NULL(gl_ts->pinctrl_state_release)) {
+ devm_pinctrl_put(gl_ts->ts_pinctrl);
+ gl_ts->ts_pinctrl = NULL;
+ } else {
+ ret = pinctrl_select_state(gl_ts->ts_pinctrl,
+ gl_ts->pinctrl_state_release);
+ if (ret)
+ dev_err(&gl_ts->client->dev,
+ "failed to select relase pinctrl state %d\n",
+ ret);
+ }
+ } else {
+ if (gpio_is_valid(gl_ts->pdata->reset_gpio))
+ gpio_free(gl_ts->pdata->reset_gpio);
+ if (gpio_is_valid(gl_ts->pdata->irq_gpio))
+ gpio_free(gl_ts->pdata->irq_gpio);
+ }
+ IT7260_power_on(false);
+ IT7260_regulator_configure(false);
+
return 0;
}
+#if defined(CONFIG_FB)
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct fb_event *evdata = data;
+ int *blank;
+
+ if (evdata && evdata->data && gl_ts && gl_ts->client) {
+ if (event == FB_EVENT_BLANK) {
+ blank = evdata->data;
+ if (*blank == FB_BLANK_UNBLANK)
+ IT7260_ts_resume(&(gl_ts->client->dev));
+ else if (*blank == FB_BLANK_POWERDOWN ||
+ *blank == FB_BLANK_VSYNC_SUSPEND)
+ IT7260_ts_suspend(&(gl_ts->client->dev));
+ }
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int IT7260_ts_resume(struct device *dev)
+{
+ int retval;
+
+ if (device_may_wakeup(dev)) {
+ if (gl_ts->device_needs_wakeup) {
+ /* Set active current for the avdd regulator */
+ if (gl_ts->pdata->avdd_lpm_cur) {
+ retval = reg_set_optimum_mode_check(gl_ts->avdd,
+ IT_I2C_ACTIVE_LOAD_UA);
+ if (retval < 0)
+ dev_err(dev, "Regulator avdd set_opt failed at resume rc=%d\n",
+ retval);
+ }
+
+ gl_ts->device_needs_wakeup = false;
+ disable_irq_wake(gl_ts->client->irq);
+ }
+ return 0;
+ }
+
+ if (gl_ts->ts_pinctrl) {
+ retval = pinctrl_select_state(gl_ts->ts_pinctrl,
+ gl_ts->pinctrl_state_active);
+ if (retval < 0) {
+ dev_err(dev, "Cannot get default pinctrl state %d\n",
+ retval);
+ goto err_pinctrl_select_suspend;
+ }
+ }
+
+ enable_irq(gl_ts->client->irq);
+ gl_ts->suspended = false;
+ return 0;
+
+err_pinctrl_select_suspend:
+ return retval;
+}
+
+static int IT7260_ts_suspend(struct device *dev)
+{
+ int retval;
+
+ if (gl_ts->fw_cfg_uploading) {
+ dev_dbg(dev, "Fw/cfg uploading. Can't go to suspend.\n");
+ return -EBUSY;
+ }
+
+ if (device_may_wakeup(dev)) {
+ if (!gl_ts->device_needs_wakeup) {
+ /* put the device in low power idle mode */
+ IT7260_ts_chipLowPowerMode(PWR_CTL_LOW_POWER_MODE);
+
+ /* Set lpm current for avdd regulator */
+ if (gl_ts->pdata->avdd_lpm_cur) {
+ retval = reg_set_optimum_mode_check(gl_ts->avdd,
+ gl_ts->pdata->avdd_lpm_cur);
+ if (retval < 0)
+ dev_err(dev, "Regulator avdd set_opt failed at suspend rc=%d\n",
+ retval);
+ }
+
+ gl_ts->device_needs_wakeup = true;
+ enable_irq_wake(gl_ts->client->irq);
+ }
+ return 0;
+ }
+
+ disable_irq(gl_ts->client->irq);
+
+ IT7260_ts_release_all();
+
+ if (gl_ts->ts_pinctrl) {
+ retval = pinctrl_select_state(gl_ts->ts_pinctrl,
+ gl_ts->pinctrl_state_suspend);
+ if (retval < 0) {
+ dev_err(dev, "Cannot get idle pinctrl state %d\n",
+ retval);
+ goto err_pinctrl_select_suspend;
+ }
+ }
+
+ gl_ts->suspended = true;
+
+ return 0;
+
+err_pinctrl_select_suspend:
+ return retval;
+}
+
+static const struct dev_pm_ops IT7260_ts_dev_pm_ops = {
+ .suspend = IT7260_ts_suspend,
+ .resume = IT7260_ts_resume,
+};
+#else
+static int IT7260_ts_resume(struct device *dev)
+{
+ return 0;
+}
+
+static int IT7260_ts_suspend(struct device *dev)
+{
+ return 0;
+}
+#endif
+
static const struct i2c_device_id IT7260_ts_id[] = {
{ DEVICE_NAME, 0},
{}
@@ -901,33 +1989,22 @@ static const struct i2c_device_id IT7260_ts_id[] = {
MODULE_DEVICE_TABLE(i2c, IT7260_ts_id);
static const struct of_device_id IT7260_match_table[] = {
- { .compatible = "ITE,IT7260_ts",},
+ { .compatible = "ite,it7260_ts",},
{},
};
-static int IT7260_ts_resume(struct i2c_client *i2cdev)
-{
- isDeviceSuspend = false;
- return 0;
-}
-
-static int IT7260_ts_suspend(struct i2c_client *i2cdev, pm_message_t pmesg)
-{
- isDeviceSuspend = true;
- return 0;
-}
-
static struct i2c_driver IT7260_ts_driver = {
.driver = {
.owner = THIS_MODULE,
.name = DEVICE_NAME,
.of_match_table = IT7260_match_table,
+#ifdef CONFIG_PM
+ .pm = &IT7260_ts_dev_pm_ops,
+#endif
},
.probe = IT7260_ts_probe,
.remove = IT7260_ts_remove,
.id_table = IT7260_ts_id,
- .resume = IT7260_ts_resume,
- .suspend = IT7260_ts_suspend,
};
module_i2c_driver(IT7260_ts_driver);
diff --git a/kernel/drivers/input/touchscreen/msg21xx/msg21xx_ts.c b/drivers/input/touchscreen/msg21xx_ts.c
index 4eb7fd4b1cc9..4eb7fd4b1cc9 100644
--- a/kernel/drivers/input/touchscreen/msg21xx/msg21xx_ts.c
+++ b/drivers/input/touchscreen/msg21xx_ts.c
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index db4b66bb18ed..55eff5ae04e4 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -2848,10 +2848,16 @@ static struct iommu_group *arm_smmu_device_group(struct device *dev)
struct iommu_group *group;
int ret;
- if (dev_is_pci(dev))
- group = pci_device_group(dev);
- else
- group = generic_device_group(dev);
+ /*
+ * We used to call pci_device_group here for dev_is_pci(dev)
+ * devices. However, that causes the root complex device to be
+ * placed in the same group as endpoint devices (and probably puts
+ * all endpoint devices in the same group as well), which makes
+ * things tricky in the DMA layer since we don't actually want to
+ * attach *everybody* in the group when one client calls attach.
+ * Instead, we'll just allocate a new group for everybody here.
+ */
+ group = generic_device_group(dev);
if (IS_ERR(group))
return group;
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 5118c2f32a4c..8ab502d80270 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -851,7 +851,8 @@ struct iommu_group *iommu_group_get_for_dev(struct device *dev)
if (!group->default_domain) {
group->default_domain = __iommu_domain_alloc(dev->bus,
IOMMU_DOMAIN_DMA);
- group->domain = group->default_domain;
+ if (!group->domain)
+ group->domain = group->default_domain;
}
ret = iommu_group_add_device(group, dev);
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 022473473971..190d294197a7 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -588,9 +588,6 @@ static int gic_populate_rdist(void)
u64 offset = ptr - gic_data.redist_regions[i].redist_base;
gic_data_rdist_rd_base() = ptr;
gic_data_rdist()->phys_base = gic_data.redist_regions[i].phys_base + offset;
- pr_info("CPU%d: found redistributor %lx region %d:%pa\n",
- smp_processor_id(), mpidr, i,
- &gic_data_rdist()->phys_base);
return 0;
}
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index 786ffa822851..291720db72ce 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -16,13 +16,21 @@
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/regmap.h>
+#include <linux/power_supply.h>
#include <linux/platform_device.h>
+#include <linux/interrupt.h>
#include <linux/regulator/consumer.h>
+#include <linux/leds-qpnp-flash.h>
#include <linux/leds-qpnp-flash-v2.h>
+#include "leds.h"
+#define FLASH_LED_REG_LED_STATUS1(base) (base + 0x08)
+#define FLASH_LED_REG_LED_STATUS2(base) (base + 0x09)
+#define FLASH_LED_REG_INT_RT_STS(base) (base + 0x10)
#define FLASH_LED_REG_SAFETY_TMR(base) (base + 0x40)
#define FLASH_LED_REG_TGR_CURRENT(base) (base + 0x43)
#define FLASH_LED_REG_MOD_CTRL(base) (base + 0x46)
@@ -32,20 +40,40 @@
#define FLASH_LED_EN_LED_CTRL(base) (base + 0x4C)
#define FLASH_LED_REG_HDRM_PRGM(base) (base + 0x4D)
#define FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(base) (base + 0x50)
+#define FLASH_LED_REG_WARMUP_DELAY(base) (base + 0x51)
#define FLASH_LED_REG_ISC_DELAY(base) (base + 0x52)
+#define FLASH_LED_REG_VPH_DROOP_THRESHOLD(base) (base + 0x61)
+#define FLASH_LED_REG_VPH_DROOP_DEBOUNCE(base) (base + 0x62)
+#define FLASH_LED_REG_CURRENT_DERATE_EN(base) (base + 0x76)
#define FLASH_LED_HDRM_MODE_PRGM_MASK GENMASK(7, 0)
#define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4)
#define FLASH_LED_CURRENT_MASK GENMASK(6, 0)
#define FLASH_LED_ENABLE_MASK GENMASK(2, 0)
#define FLASH_LED_SAFETY_TMR_MASK GENMASK(7, 0)
-#define FLASH_LED_ISC_DELAY_MASK GENMASK(1, 0)
+#define FLASH_LED_INT_RT_STS_MASK GENMASK(7, 0)
+#define FLASH_LED_ISC_WARMUP_DELAY_MASK GENMASK(1, 0)
+#define FLASH_LED_CURRENT_DERATE_EN_MASK GENMASK(2, 0)
+#define FLASH_LED_VPH_DROOP_DEBOUNCE_MASK GENMASK(1, 0)
+#define FLASH_LED_VPH_DROOP_HYSTERESIS_MASK GENMASK(5, 4)
+#define FLASH_LED_VPH_DROOP_THRESHOLD_MASK GENMASK(2, 0)
#define FLASH_LED_MOD_CTRL_MASK BIT(7)
#define FLASH_LED_HW_SW_STROBE_SEL_MASK BIT(2)
-
-#define FLASH_LED_HEADROOM_AUTO_MODE_ENABLED true
-#define FLASH_LED_ISC_DELAY_SHIFT 6
-#define FLASH_LED_ISC_DELAY_DEFAULT_US 3
+#define FLASH_LED_VPH_DROOP_FAULT_MASK BIT(4)
+
+#define VPH_DROOP_DEBOUNCE_US_TO_VAL(val_us) (val_us / 8)
+#define VPH_DROOP_HYST_MV_TO_VAL(val_mv) (val_mv / 25)
+#define VPH_DROOP_THRESH_MV_TO_VAL(val_mv) ((val_mv / 100) - 25)
+
+#define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6
+#define FLASH_LED_WARMUP_DELAY_DEFAULT 2
+#define FLASH_LED_ISC_DELAY_DEFAULT 3
+#define FLASH_LED_VPH_DROOP_DEBOUNCE_DEFAULT 2
+#define FLASH_LED_VPH_DROOP_HYST_DEFAULT 2
+#define FLASH_LED_VPH_DROOP_THRESH_DEFAULT 5
+#define FLASH_LED_VPH_DROOP_DEBOUNCE_MAX 3
+#define FLASH_LED_VPH_DROOP_HYST_MAX 3
+#define FLASH_LED_VPH_DROOP_THRESH_MAX 7
#define FLASH_LED_SAFETY_TMR_VAL_OFFSET 1
#define FLASH_LED_SAFETY_TMR_VAL_DIVISOR 10
#define FLASH_LED_SAFETY_TMR_ENABLE BIT(7)
@@ -69,6 +97,9 @@
#define FLASH_LED_SAFETY_TMR_DISABLED 0x13
#define FLASH_LED_MIN_CURRENT_MA 25
+/* notifier call chain for flash-led irqs */
+static ATOMIC_NOTIFIER_HEAD(irq_notifier_list);
+
enum flash_led_type {
FLASH_LED_TYPE_FLASH,
FLASH_LED_TYPE_TORCH,
@@ -125,9 +156,17 @@ struct flash_switch_data {
* Flash LED configuration read from device tree
*/
struct flash_led_platform_data {
- u8 isc_delay_us;
- u8 hw_strobe_option;
- bool hdrm_auto_mode_en;
+ int all_ramp_up_done_irq;
+ int all_ramp_down_done_irq;
+ int led_fault_irq;
+ u8 isc_delay;
+ u8 warmup_delay;
+ u8 current_derate_en_cfg;
+ u8 vph_droop_threshold;
+ u8 vph_droop_hysteresis;
+ u8 vph_droop_debounce;
+ u8 hw_strobe_option;
+ bool hdrm_auto_mode_en;
};
/*
@@ -139,6 +178,8 @@ struct qpnp_flash_led {
struct regmap *regmap;
struct flash_node_data *fnode;
struct flash_switch_data *snode;
+ struct power_supply *bms_psy;
+ struct notifier_block nb;
spinlock_t lock;
int num_fnodes;
int num_snodes;
@@ -147,19 +188,36 @@ struct qpnp_flash_led {
};
static int
+qpnp_flash_led_read(struct qpnp_flash_led *led, u16 addr, u8 *data)
+{
+ int rc;
+ uint val;
+
+ rc = regmap_read(led->regmap, addr, &val);
+ if (rc < 0)
+ dev_err(&led->pdev->dev, "Unable to read from 0x%04X rc = %d\n",
+ addr, rc);
+ else
+ dev_dbg(&led->pdev->dev, "Read 0x%02X from addr 0x%04X\n",
+ val, addr);
+
+ *data = (u8)val;
+ return rc;
+}
+
+static int
qpnp_flash_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask,
- u8 val)
+ u8 val)
{
int rc;
rc = regmap_update_bits(led->regmap, addr, mask, val);
if (rc < 0)
- dev_err(&led->pdev->dev,
- "Unable to update bits from 0x%02X, rc = %d\n",
- addr, rc);
+ dev_err(&led->pdev->dev, "Unable to update bits from 0x%04X, rc = %d\n",
+ addr, rc);
else
- dev_dbg(&led->pdev->dev, "Wrote 0x%02X to addr 0x%02X\n",
- val, addr);
+ dev_dbg(&led->pdev->dev, "Wrote 0x%02X to addr 0x%04X\n",
+ val, addr);
return rc;
}
@@ -195,7 +253,43 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_ISC_DELAY(led->base),
- FLASH_LED_ISC_DELAY_MASK, led->pdata->isc_delay_us);
+ FLASH_LED_ISC_WARMUP_DELAY_MASK,
+ led->pdata->isc_delay);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_WARMUP_DELAY(led->base),
+ FLASH_LED_ISC_WARMUP_DELAY_MASK,
+ led->pdata->warmup_delay);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_CURRENT_DERATE_EN(led->base),
+ FLASH_LED_CURRENT_DERATE_EN_MASK,
+ led->pdata->current_derate_en_cfg);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_VPH_DROOP_DEBOUNCE(led->base),
+ FLASH_LED_VPH_DROOP_DEBOUNCE_MASK,
+ led->pdata->vph_droop_debounce);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_VPH_DROOP_THRESHOLD(led->base),
+ FLASH_LED_VPH_DROOP_THRESHOLD_MASK,
+ led->pdata->vph_droop_threshold);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_VPH_DROOP_THRESHOLD(led->base),
+ FLASH_LED_VPH_DROOP_HYSTERESIS_MASK,
+ led->pdata->vph_droop_hysteresis);
if (rc < 0)
return rc;
@@ -459,13 +553,21 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
return 0;
}
-int qpnp_flash_led_prepare(struct led_classdev *led_cdev, int options)
+int qpnp_flash_led_prepare(struct led_trigger *trig, int options)
{
- struct flash_switch_data *snode =
- container_of(led_cdev, struct flash_switch_data, cdev);
- struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev);
+ struct led_classdev *led_cdev = trigger_to_lcdev(trig);
+ struct flash_switch_data *snode;
+ struct qpnp_flash_led *led;
int rc, val = 0;
+ if (!led_cdev) {
+ pr_err("Invalid led_trigger provided\n");
+ return -EINVAL;
+ }
+
+ snode = container_of(led_cdev, struct flash_switch_data, cdev);
+ led = dev_get_drvdata(&snode->pdev->dev);
+
if (!(options & (ENABLE_REGULATOR | QUERY_MAX_CURRENT))) {
dev_err(&led->pdev->dev, "Invalid options %d\n", options);
return -EINVAL;
@@ -521,6 +623,112 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
spin_unlock(&led->lock);
}
+static int flash_led_psy_notifier_call(struct notifier_block *nb,
+ unsigned long ev, void *v)
+{
+ struct power_supply *psy = v;
+ struct qpnp_flash_led *led =
+ container_of(nb, struct qpnp_flash_led, nb);
+
+ if (ev != PSY_EVENT_PROP_CHANGED)
+ return NOTIFY_OK;
+
+ if (!strcmp(psy->desc->name, "bms")) {
+ led->bms_psy = power_supply_get_by_name("bms");
+ if (!led->bms_psy)
+ dev_err(&led->pdev->dev, "Failed to get bms power_supply\n");
+ else
+ power_supply_unreg_notifier(&led->nb);
+ }
+
+ return NOTIFY_OK;
+}
+
+static int flash_led_psy_register_notifier(struct qpnp_flash_led *led)
+{
+ int rc;
+
+ led->nb.notifier_call = flash_led_psy_notifier_call;
+ rc = power_supply_reg_notifier(&led->nb);
+ if (rc < 0) {
+ pr_err("Couldn't register psy notifier, rc = %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+/* irq handler */
+static irqreturn_t qpnp_flash_led_irq_handler(int irq, void *_led)
+{
+ struct qpnp_flash_led *led = _led;
+ enum flash_led_irq_type irq_type = INVALID_IRQ;
+ int rc;
+ u8 irq_status, led_status1, led_status2;
+
+ dev_dbg(&led->pdev->dev, "irq received, irq=%d\n", irq);
+
+ rc = qpnp_flash_led_read(led,
+ FLASH_LED_REG_INT_RT_STS(led->base), &irq_status);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev, "Failed to read interrupt status reg, rc=%d\n",
+ rc);
+ goto exit;
+ }
+
+ if (irq == led->pdata->all_ramp_up_done_irq)
+ irq_type = ALL_RAMP_UP_DONE_IRQ;
+ else if (irq == led->pdata->all_ramp_down_done_irq)
+ irq_type = ALL_RAMP_DOWN_DONE_IRQ;
+ else if (irq == led->pdata->led_fault_irq)
+ irq_type = LED_FAULT_IRQ;
+
+ if (irq_type == ALL_RAMP_UP_DONE_IRQ)
+ atomic_notifier_call_chain(&irq_notifier_list,
+ irq_type, NULL);
+
+ if (irq_type == LED_FAULT_IRQ) {
+ rc = qpnp_flash_led_read(led,
+ FLASH_LED_REG_LED_STATUS1(led->base), &led_status1);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev, "Failed to read led_status1 reg, rc=%d\n",
+ rc);
+ goto exit;
+ }
+
+ rc = qpnp_flash_led_read(led,
+ FLASH_LED_REG_LED_STATUS2(led->base), &led_status2);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev, "Failed to read led_status2 reg, rc=%d\n",
+ rc);
+ goto exit;
+ }
+
+ if (led_status1)
+ dev_emerg(&led->pdev->dev, "led short/open fault detected! led_status1=%x\n",
+ led_status1);
+
+ if (led_status2 & FLASH_LED_VPH_DROOP_FAULT_MASK)
+ dev_emerg(&led->pdev->dev, "led vph_droop fault detected!\n");
+ }
+
+ dev_dbg(&led->pdev->dev, "irq handled, irq_type=%x, irq_status=%x\n",
+ irq_type, irq_status);
+
+exit:
+ return IRQ_HANDLED;
+}
+
+int qpnp_flash_led_register_irq_notifier(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_register(&irq_notifier_list, nb);
+}
+
+int qpnp_flash_led_unregister_irq_notifier(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(&irq_notifier_list, nb);
+}
+
static int qpnp_flash_led_regulator_setup(struct qpnp_flash_led *led,
struct flash_switch_data *snode, bool on)
{
@@ -901,28 +1109,116 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
{
int rc;
u32 val;
+ bool short_circuit_det, open_circuit_det, vph_droop_det;
- led->pdata->hdrm_auto_mode_en = FLASH_LED_HEADROOM_AUTO_MODE_ENABLED;
led->pdata->hdrm_auto_mode_en = of_property_read_bool(node,
"qcom,hdrm-auto-mode");
- led->pdata->isc_delay_us = FLASH_LED_ISC_DELAY_DEFAULT_US;
- rc = of_property_read_u32(node, "qcom,isc-delay", &val);
+ led->pdata->isc_delay = FLASH_LED_ISC_DELAY_DEFAULT;
+ rc = of_property_read_u32(node, "qcom,isc-delay-us", &val);
+ if (!rc) {
+ led->pdata->isc_delay =
+ val >> FLASH_LED_ISC_WARMUP_DELAY_SHIFT;
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev,
+ "Unable to read ISC delay, rc=%d\n", rc);
+ return rc;
+ }
+
+ led->pdata->warmup_delay = FLASH_LED_WARMUP_DELAY_DEFAULT;
+ rc = of_property_read_u32(node, "qcom,warmup-delay-us", &val);
+ if (!rc) {
+ led->pdata->warmup_delay =
+ val >> FLASH_LED_ISC_WARMUP_DELAY_SHIFT;
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev,
+ "Unable to read WARMUP delay, rc=%d\n", rc);
+ return rc;
+ }
+
+ short_circuit_det =
+ of_property_read_bool(node, "qcom,short-circuit-det");
+ open_circuit_det = of_property_read_bool(node, "qcom,open-circuit-det");
+ vph_droop_det = of_property_read_bool(node, "qcom,vph-droop-det");
+ led->pdata->current_derate_en_cfg = (vph_droop_det << 2) |
+ (open_circuit_det << 1) | short_circuit_det;
+
+ led->pdata->vph_droop_debounce = FLASH_LED_VPH_DROOP_DEBOUNCE_DEFAULT;
+ rc = of_property_read_u32(node, "qcom,vph-droop-debounce-us", &val);
if (!rc) {
- led->pdata->isc_delay_us = val >> FLASH_LED_ISC_DELAY_SHIFT;
+ led->pdata->vph_droop_debounce =
+ VPH_DROOP_DEBOUNCE_US_TO_VAL(val);
} else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev, "Unable to read ISC delay\n");
+ dev_err(&led->pdev->dev,
+ "Unable to read VPH droop debounce, rc=%d\n", rc);
return rc;
}
+ if (led->pdata->vph_droop_debounce > FLASH_LED_VPH_DROOP_DEBOUNCE_MAX) {
+ dev_err(&led->pdev->dev,
+ "Invalid VPH droop debounce specified");
+ return -EINVAL;
+ }
+
+ led->pdata->vph_droop_threshold = FLASH_LED_VPH_DROOP_THRESH_DEFAULT;
+ rc = of_property_read_u32(node, "qcom,vph-droop-threshold-mv", &val);
+ if (!rc) {
+ led->pdata->vph_droop_threshold =
+ VPH_DROOP_THRESH_MV_TO_VAL(val);
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev,
+ "Unable to read VPH droop threshold, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (led->pdata->vph_droop_threshold > FLASH_LED_VPH_DROOP_THRESH_MAX) {
+ dev_err(&led->pdev->dev,
+ "Invalid VPH droop threshold specified");
+ return -EINVAL;
+ }
+
+ led->pdata->vph_droop_hysteresis =
+ FLASH_LED_VPH_DROOP_HYST_DEFAULT;
+ rc = of_property_read_u32(node, "qcom,vph-droop-hysteresis-mv", &val);
+ if (!rc) {
+ led->pdata->vph_droop_hysteresis =
+ VPH_DROOP_HYST_MV_TO_VAL(val);
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev,
+ "Unable to read VPH droop hysteresis, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (led->pdata->vph_droop_hysteresis > FLASH_LED_VPH_DROOP_HYST_MAX) {
+ dev_err(&led->pdev->dev,
+ "Invalid VPH droop hysteresis specified");
+ return -EINVAL;
+ }
+
rc = of_property_read_u32(node, "qcom,hw-strobe-option", &val);
if (!rc) {
led->pdata->hw_strobe_option = (u8)val;
} else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev, "Unable to parse hw strobe option\n");
+ dev_err(&led->pdev->dev,
+ "Unable to parse hw strobe option, rc=%d\n", rc);
return rc;
}
+ led->pdata->all_ramp_up_done_irq =
+ of_irq_get_byname(node, "all-ramp-up-done-irq");
+ if (led->pdata->all_ramp_up_done_irq < 0)
+ dev_dbg(&led->pdev->dev, "all-ramp-up-done-irq not used\n");
+
+ led->pdata->all_ramp_down_done_irq =
+ of_irq_get_byname(node, "all-ramp-down-done-irq");
+ if (led->pdata->all_ramp_down_done_irq < 0)
+ dev_dbg(&led->pdev->dev, "all-ramp-down-done-irq not used\n");
+
+ led->pdata->led_fault_irq =
+ of_irq_get_byname(node, "led-fault-irq");
+ if (led->pdata->led_fault_irq < 0)
+ dev_dbg(&led->pdev->dev, "led-fault-irq not used\n");
+
return 0;
}
@@ -1033,11 +1329,64 @@ static int qpnp_flash_led_probe(struct platform_device *pdev)
}
}
+ /* setup irqs */
+ if (led->pdata->all_ramp_up_done_irq >= 0) {
+ rc = devm_request_threaded_irq(&led->pdev->dev,
+ led->pdata->all_ramp_up_done_irq,
+ NULL, qpnp_flash_led_irq_handler,
+ IRQF_ONESHOT,
+ "qpnp_flash_led_all_ramp_up_done_irq", led);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Unable to request all_ramp_up_done(%d) IRQ(err:%d)\n",
+ led->pdata->all_ramp_up_done_irq, rc);
+ goto error_switch_register;
+ }
+ }
+
+ if (led->pdata->all_ramp_down_done_irq >= 0) {
+ rc = devm_request_threaded_irq(&led->pdev->dev,
+ led->pdata->all_ramp_down_done_irq,
+ NULL, qpnp_flash_led_irq_handler,
+ IRQF_ONESHOT,
+ "qpnp_flash_led_all_ramp_down_done_irq", led);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Unable to request all_ramp_down_done(%d) IRQ(err:%d)\n",
+ led->pdata->all_ramp_down_done_irq, rc);
+ goto error_switch_register;
+ }
+ }
+
+ if (led->pdata->led_fault_irq >= 0) {
+ rc = devm_request_threaded_irq(&led->pdev->dev,
+ led->pdata->led_fault_irq,
+ NULL, qpnp_flash_led_irq_handler,
+ IRQF_ONESHOT,
+ "qpnp_flash_led_fault_irq", led);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Unable to request led_fault(%d) IRQ(err:%d)\n",
+ led->pdata->led_fault_irq, rc);
+ goto error_switch_register;
+ }
+ }
+
+ led->bms_psy = power_supply_get_by_name("bms");
+ if (!led->bms_psy) {
+ rc = flash_led_psy_register_notifier(led);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Couldn't register psy notifier, rc = %d\n",
+ rc);
+ goto error_switch_register;
+ }
+ }
+
rc = qpnp_flash_led_init_settings(led);
if (rc < 0) {
dev_err(&pdev->dev,
"Failed to initialize flash LED, rc=%d\n", rc);
- goto error_switch_register;
+ goto unreg_notifier;
}
spin_lock_init(&led->lock);
@@ -1046,6 +1395,8 @@ static int qpnp_flash_led_probe(struct platform_device *pdev)
return 0;
+unreg_notifier:
+ power_supply_unreg_notifier(&led->nb);
error_switch_register:
while (i > 0)
led_classdev_unregister(&led->snode[--i].cdev);
@@ -1078,6 +1429,7 @@ static int qpnp_flash_led_remove(struct platform_device *pdev)
while (i > 0)
led_classdev_unregister(&led->fnode[--i].cdev);
+ power_supply_unreg_notifier(&led->nb);
return 0;
}
diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c
index c45b217ee754..3e19cf6796a3 100644
--- a/drivers/leds/leds-qpnp-flash.c
+++ b/drivers/leds/leds-qpnp-flash.c
@@ -26,11 +26,12 @@
#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 "leds.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)
@@ -1154,6 +1155,47 @@ error_regulator_enable:
return rc;
}
+int qpnp_flash_led_prepare(struct led_trigger *trig, int options)
+{
+ struct led_classdev *led_cdev = trigger_to_lcdev(trig);
+ struct flash_node_data *flash_node;
+ struct qpnp_flash_led *led;
+ int rc, val = 0;
+
+ 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 & (ENABLE_REGULATOR | QUERY_MAX_CURRENT))) {
+ 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 & QUERY_MAX_CURRENT) {
+ val = qpnp_flash_led_get_max_avail_current(flash_node, led);
+ if (val < 0) {
+ dev_err(&led->pdev->dev,
+ "query max current failed, rc=%d\n", val);
+ return val;
+ }
+ }
+
+ return val;
+}
+
static void qpnp_flash_led_work(struct work_struct *work)
{
struct flash_node_data *flash_node = container_of(work,
diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
index 4238fbc31d35..61de87e2ad80 100644
--- a/drivers/leds/leds.h
+++ b/drivers/leds/leds.h
@@ -44,6 +44,22 @@ static inline int led_get_brightness(struct led_classdev *led_cdev)
return led_cdev->brightness;
}
+static inline struct led_classdev *trigger_to_lcdev(struct led_trigger *trig)
+{
+ struct led_classdev *led_cdev;
+
+ read_lock(&trig->leddev_list_lock);
+ list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) {
+ if (!strcmp(led_cdev->default_trigger, trig->name)) {
+ read_unlock(&trig->leddev_list_lock);
+ return led_cdev;
+ }
+ }
+
+ read_unlock(&trig->leddev_list_lock);
+ return NULL;
+}
+
void led_init_core(struct led_classdev *led_cdev);
void led_stop_software_blink(struct led_classdev *led_cdev);
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index 8d0ead98eb6e..a296425a7270 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -1015,8 +1015,12 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c)
*/
atomic_set(&dc->count, 1);
- if (bch_cached_dev_writeback_start(dc))
+ /* Block writeback thread, but spawn it */
+ down_write(&dc->writeback_lock);
+ if (bch_cached_dev_writeback_start(dc)) {
+ up_write(&dc->writeback_lock);
return -ENOMEM;
+ }
if (BDEV_STATE(&dc->sb) == BDEV_STATE_DIRTY) {
bch_sectors_dirty_init(dc);
@@ -1028,6 +1032,9 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c)
bch_cached_dev_run(dc);
bcache_device_link(&dc->disk, c, "bdev");
+ /* Allow the writeback thread to proceed */
+ up_write(&dc->writeback_lock);
+
pr_info("Caching %s as %s on set %pU",
bdevname(dc->bdev, buf), dc->disk.disk->disk_name,
dc->disk.c->sb.set_uuid);
@@ -1366,6 +1373,9 @@ static void cache_set_flush(struct closure *cl)
struct btree *b;
unsigned i;
+ if (!c)
+ closure_return(cl);
+
bch_cache_accounting_destroy(&c->accounting);
kobject_put(&c->internal);
@@ -1828,11 +1838,12 @@ static int cache_alloc(struct cache_sb *sb, struct cache *ca)
return 0;
}
-static void register_cache(struct cache_sb *sb, struct page *sb_page,
+static int register_cache(struct cache_sb *sb, struct page *sb_page,
struct block_device *bdev, struct cache *ca)
{
char name[BDEVNAME_SIZE];
- const char *err = "cannot allocate memory";
+ const char *err = NULL;
+ int ret = 0;
memcpy(&ca->sb, sb, sizeof(struct cache_sb));
ca->bdev = bdev;
@@ -1847,27 +1858,35 @@ static void register_cache(struct cache_sb *sb, struct page *sb_page,
if (blk_queue_discard(bdev_get_queue(ca->bdev)))
ca->discard = CACHE_DISCARD(&ca->sb);
- if (cache_alloc(sb, ca) != 0)
+ ret = cache_alloc(sb, ca);
+ if (ret != 0)
goto err;
- err = "error creating kobject";
- if (kobject_add(&ca->kobj, &part_to_dev(bdev->bd_part)->kobj, "bcache"))
- goto err;
+ if (kobject_add(&ca->kobj, &part_to_dev(bdev->bd_part)->kobj, "bcache")) {
+ err = "error calling kobject_add";
+ ret = -ENOMEM;
+ goto out;
+ }
mutex_lock(&bch_register_lock);
err = register_cache_set(ca);
mutex_unlock(&bch_register_lock);
- if (err)
- goto err;
+ if (err) {
+ ret = -ENODEV;
+ goto out;
+ }
pr_info("registered cache device %s", bdevname(bdev, name));
+
out:
kobject_put(&ca->kobj);
- return;
+
err:
- pr_notice("error opening %s: %s", bdevname(bdev, name), err);
- goto out;
+ if (err)
+ pr_notice("error opening %s: %s", bdevname(bdev, name), err);
+
+ return ret;
}
/* Global interfaces/init */
@@ -1965,7 +1984,8 @@ static ssize_t register_bcache(struct kobject *k, struct kobj_attribute *attr,
if (!ca)
goto err_close;
- register_cache(sb, sb_page, bdev, ca);
+ if (register_cache(sb, sb_page, bdev, ca) != 0)
+ goto err_close;
}
out:
if (sb_page)
diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c
index f6543f3a970f..27f2ef300f8b 100644
--- a/drivers/md/dm-cache-metadata.c
+++ b/drivers/md/dm-cache-metadata.c
@@ -867,19 +867,40 @@ static int blocks_are_unmapped_or_clean(struct dm_cache_metadata *cmd,
return 0;
}
-#define WRITE_LOCK(cmd) \
- if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) \
+#define WRITE_LOCK(cmd) \
+ down_write(&cmd->root_lock); \
+ if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
+ up_write(&cmd->root_lock); \
return -EINVAL; \
- down_write(&cmd->root_lock)
+ }
#define WRITE_LOCK_VOID(cmd) \
- if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) \
+ down_write(&cmd->root_lock); \
+ if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
+ up_write(&cmd->root_lock); \
return; \
- down_write(&cmd->root_lock)
+ }
#define WRITE_UNLOCK(cmd) \
up_write(&cmd->root_lock)
+#define READ_LOCK(cmd) \
+ down_read(&cmd->root_lock); \
+ if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
+ up_read(&cmd->root_lock); \
+ return -EINVAL; \
+ }
+
+#define READ_LOCK_VOID(cmd) \
+ down_read(&cmd->root_lock); \
+ if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
+ up_read(&cmd->root_lock); \
+ return; \
+ }
+
+#define READ_UNLOCK(cmd) \
+ up_read(&cmd->root_lock)
+
int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size)
{
int r;
@@ -1015,22 +1036,20 @@ int dm_cache_load_discards(struct dm_cache_metadata *cmd,
{
int r;
- down_read(&cmd->root_lock);
+ READ_LOCK(cmd);
r = __load_discards(cmd, fn, context);
- up_read(&cmd->root_lock);
+ READ_UNLOCK(cmd);
return r;
}
-dm_cblock_t dm_cache_size(struct dm_cache_metadata *cmd)
+int dm_cache_size(struct dm_cache_metadata *cmd, dm_cblock_t *result)
{
- dm_cblock_t r;
+ READ_LOCK(cmd);
+ *result = cmd->cache_blocks;
+ READ_UNLOCK(cmd);
- down_read(&cmd->root_lock);
- r = cmd->cache_blocks;
- up_read(&cmd->root_lock);
-
- return r;
+ return 0;
}
static int __remove(struct dm_cache_metadata *cmd, dm_cblock_t cblock)
@@ -1188,9 +1207,9 @@ int dm_cache_load_mappings(struct dm_cache_metadata *cmd,
{
int r;
- down_read(&cmd->root_lock);
+ READ_LOCK(cmd);
r = __load_mappings(cmd, policy, fn, context);
- up_read(&cmd->root_lock);
+ READ_UNLOCK(cmd);
return r;
}
@@ -1215,18 +1234,18 @@ static int __dump_mappings(struct dm_cache_metadata *cmd)
void dm_cache_dump(struct dm_cache_metadata *cmd)
{
- down_read(&cmd->root_lock);
+ READ_LOCK_VOID(cmd);
__dump_mappings(cmd);
- up_read(&cmd->root_lock);
+ READ_UNLOCK(cmd);
}
int dm_cache_changed_this_transaction(struct dm_cache_metadata *cmd)
{
int r;
- down_read(&cmd->root_lock);
+ READ_LOCK(cmd);
r = cmd->changed;
- up_read(&cmd->root_lock);
+ READ_UNLOCK(cmd);
return r;
}
@@ -1276,9 +1295,9 @@ int dm_cache_set_dirty(struct dm_cache_metadata *cmd,
void dm_cache_metadata_get_stats(struct dm_cache_metadata *cmd,
struct dm_cache_statistics *stats)
{
- down_read(&cmd->root_lock);
+ READ_LOCK_VOID(cmd);
*stats = cmd->stats;
- up_read(&cmd->root_lock);
+ READ_UNLOCK(cmd);
}
void dm_cache_metadata_set_stats(struct dm_cache_metadata *cmd,
@@ -1312,9 +1331,9 @@ int dm_cache_get_free_metadata_block_count(struct dm_cache_metadata *cmd,
{
int r = -EINVAL;
- down_read(&cmd->root_lock);
+ READ_LOCK(cmd);
r = dm_sm_get_nr_free(cmd->metadata_sm, result);
- up_read(&cmd->root_lock);
+ READ_UNLOCK(cmd);
return r;
}
@@ -1324,9 +1343,9 @@ int dm_cache_get_metadata_dev_size(struct dm_cache_metadata *cmd,
{
int r = -EINVAL;
- down_read(&cmd->root_lock);
+ READ_LOCK(cmd);
r = dm_sm_get_nr_blocks(cmd->metadata_sm, result);
- up_read(&cmd->root_lock);
+ READ_UNLOCK(cmd);
return r;
}
@@ -1417,7 +1436,13 @@ int dm_cache_write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *
int dm_cache_metadata_all_clean(struct dm_cache_metadata *cmd, bool *result)
{
- return blocks_are_unmapped_or_clean(cmd, 0, cmd->cache_blocks, result);
+ int r;
+
+ READ_LOCK(cmd);
+ r = blocks_are_unmapped_or_clean(cmd, 0, cmd->cache_blocks, result);
+ READ_UNLOCK(cmd);
+
+ return r;
}
void dm_cache_metadata_set_read_only(struct dm_cache_metadata *cmd)
@@ -1440,10 +1465,7 @@ int dm_cache_metadata_set_needs_check(struct dm_cache_metadata *cmd)
struct dm_block *sblock;
struct cache_disk_superblock *disk_super;
- /*
- * We ignore fail_io for this function.
- */
- down_write(&cmd->root_lock);
+ WRITE_LOCK(cmd);
set_bit(NEEDS_CHECK, &cmd->flags);
r = superblock_lock(cmd, &sblock);
@@ -1458,19 +1480,17 @@ int dm_cache_metadata_set_needs_check(struct dm_cache_metadata *cmd)
dm_bm_unlock(sblock);
out:
- up_write(&cmd->root_lock);
+ WRITE_UNLOCK(cmd);
return r;
}
-bool dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd)
+int dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd, bool *result)
{
- bool needs_check;
+ READ_LOCK(cmd);
+ *result = !!test_bit(NEEDS_CHECK, &cmd->flags);
+ READ_UNLOCK(cmd);
- down_read(&cmd->root_lock);
- needs_check = !!test_bit(NEEDS_CHECK, &cmd->flags);
- up_read(&cmd->root_lock);
-
- return needs_check;
+ return 0;
}
int dm_cache_metadata_abort(struct dm_cache_metadata *cmd)
diff --git a/drivers/md/dm-cache-metadata.h b/drivers/md/dm-cache-metadata.h
index 2ffee21f318d..8528744195e5 100644
--- a/drivers/md/dm-cache-metadata.h
+++ b/drivers/md/dm-cache-metadata.h
@@ -66,7 +66,7 @@ void dm_cache_metadata_close(struct dm_cache_metadata *cmd);
* origin blocks to map to.
*/
int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size);
-dm_cblock_t dm_cache_size(struct dm_cache_metadata *cmd);
+int dm_cache_size(struct dm_cache_metadata *cmd, dm_cblock_t *result);
int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd,
sector_t discard_block_size,
@@ -137,7 +137,7 @@ int dm_cache_write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *
*/
int dm_cache_metadata_all_clean(struct dm_cache_metadata *cmd, bool *result);
-bool dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd);
+int dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd, bool *result);
int dm_cache_metadata_set_needs_check(struct dm_cache_metadata *cmd);
void dm_cache_metadata_set_read_only(struct dm_cache_metadata *cmd);
void dm_cache_metadata_set_read_write(struct dm_cache_metadata *cmd);
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index 2fd4c8296144..515f83e7d9ab 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -987,9 +987,14 @@ static void notify_mode_switch(struct cache *cache, enum cache_metadata_mode mod
static void set_cache_mode(struct cache *cache, enum cache_metadata_mode new_mode)
{
- bool needs_check = dm_cache_metadata_needs_check(cache->cmd);
+ bool needs_check;
enum cache_metadata_mode old_mode = get_cache_mode(cache);
+ if (dm_cache_metadata_needs_check(cache->cmd, &needs_check)) {
+ DMERR("unable to read needs_check flag, setting failure mode");
+ new_mode = CM_FAIL;
+ }
+
if (new_mode == CM_WRITE && needs_check) {
DMERR("%s: unable to switch cache to write mode until repaired.",
cache_device_name(cache));
@@ -3513,6 +3518,7 @@ static void cache_status(struct dm_target *ti, status_type_t type,
char buf[BDEVNAME_SIZE];
struct cache *cache = ti->private;
dm_cblock_t residency;
+ bool needs_check;
switch (type) {
case STATUSTYPE_INFO:
@@ -3586,7 +3592,9 @@ static void cache_status(struct dm_target *ti, status_type_t type,
else
DMEMIT("rw ");
- if (dm_cache_metadata_needs_check(cache->cmd))
+ r = dm_cache_metadata_needs_check(cache->cmd, &needs_check);
+
+ if (r || needs_check)
DMEMIT("needs_check ");
else
DMEMIT("- ");
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 61f184ad081c..e108deebbaaa 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -1106,6 +1106,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
int i;
int r = -EINVAL;
char *origin_path, *cow_path;
+ dev_t origin_dev, cow_dev;
unsigned args_used, num_flush_bios = 1;
fmode_t origin_mode = FMODE_READ;
@@ -1136,11 +1137,19 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
ti->error = "Cannot get origin device";
goto bad_origin;
}
+ origin_dev = s->origin->bdev->bd_dev;
cow_path = argv[0];
argv++;
argc--;
+ cow_dev = dm_get_dev_t(cow_path);
+ if (cow_dev && cow_dev == origin_dev) {
+ ti->error = "COW device cannot be the same as origin device";
+ r = -EINVAL;
+ goto bad_cow;
+ }
+
r = dm_get_device(ti, cow_path, dm_table_get_mode(ti->table), &s->cow);
if (r) {
ti->error = "Cannot get COW device";
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 061152a43730..cb5d0daf53bb 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -365,6 +365,26 @@ static int upgrade_mode(struct dm_dev_internal *dd, fmode_t new_mode,
}
/*
+ * Convert the path to a device
+ */
+dev_t dm_get_dev_t(const char *path)
+{
+ dev_t uninitialized_var(dev);
+ struct block_device *bdev;
+
+ bdev = lookup_bdev(path);
+ if (IS_ERR(bdev))
+ dev = name_to_dev_t(path);
+ else {
+ dev = bdev->bd_dev;
+ bdput(bdev);
+ }
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(dm_get_dev_t);
+
+/*
* Add a device to the list, or just increment the usage count if
* it's already present.
*/
@@ -372,23 +392,15 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
struct dm_dev **result)
{
int r;
- dev_t uninitialized_var(dev);
+ dev_t dev;
struct dm_dev_internal *dd;
struct dm_table *t = ti->table;
- struct block_device *bdev;
BUG_ON(!t);
- /* convert the path to a device */
- bdev = lookup_bdev(path);
- if (IS_ERR(bdev)) {
- dev = name_to_dev_t(path);
- if (!dev)
- return -ENODEV;
- } else {
- dev = bdev->bd_dev;
- bdput(bdev);
- }
+ dev = dm_get_dev_t(path);
+ if (!dev)
+ return -ENODEV;
dd = find_device(&t->devices, dev);
if (!dd) {
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
index c219a053c7f6..911ada643364 100644
--- a/drivers/md/dm-thin-metadata.c
+++ b/drivers/md/dm-thin-metadata.c
@@ -1943,5 +1943,8 @@ bool dm_pool_metadata_needs_check(struct dm_pool_metadata *pmd)
void dm_pool_issue_prefetches(struct dm_pool_metadata *pmd)
{
- dm_tm_issue_prefetches(pmd->tm);
+ down_read(&pmd->root_lock);
+ if (!pmd->fail_io)
+ dm_tm_issue_prefetches(pmd->tm);
+ up_read(&pmd->root_lock);
}
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index c46c9dc9b667..479fdbb3dcb2 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1109,12 +1109,8 @@ static void rq_completed(struct mapped_device *md, int rw, bool run_queue)
* back into ->request_fn() could deadlock attempting to grab the
* queue lock again.
*/
- if (run_queue) {
- if (md->queue->mq_ops)
- blk_mq_run_hw_queues(md->queue, true);
- else
- blk_run_queue_async(md->queue);
- }
+ if (!md->queue->mq_ops && run_queue)
+ blk_run_queue_async(md->queue);
/*
* dm_put() must be at the end of this function. See the comment above
@@ -1214,9 +1210,9 @@ static void dm_requeue_original_request(struct mapped_device *md,
{
int rw = rq_data_dir(rq);
+ rq_end_stats(md, rq);
dm_unprep_request(rq);
- rq_end_stats(md, rq);
if (!rq->q->mq_ops)
old_requeue_request(rq);
else {
@@ -1336,7 +1332,10 @@ static void dm_complete_request(struct request *rq, int error)
struct dm_rq_target_io *tio = tio_from_request(rq);
tio->error = error;
- blk_complete_request(rq);
+ if (!rq->q->mq_ops)
+ blk_complete_request(rq);
+ else
+ blk_mq_complete_request(rq, error);
}
/*
diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c
index 0a72ab6e6c20..dd483bb2e111 100644
--- a/drivers/md/multipath.c
+++ b/drivers/md/multipath.c
@@ -129,7 +129,9 @@ static void multipath_make_request(struct mddev *mddev, struct bio * bio)
}
multipath = conf->multipaths + mp_bh->path;
- mp_bh->bio = *bio;
+ bio_init(&mp_bh->bio);
+ __bio_clone_fast(&mp_bh->bio, bio);
+
mp_bh->bio.bi_iter.bi_sector += multipath->rdev->data_offset;
mp_bh->bio.bi_bdev = multipath->rdev->bdev;
mp_bh->bio.bi_rw |= REQ_FAILFAST_TRANSPORT;
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index c4b913409226..515554c7365b 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -2274,6 +2274,7 @@ static void handle_write_finished(struct r1conf *conf, struct r1bio *r1_bio)
if (fail) {
spin_lock_irq(&conf->device_lock);
list_add(&r1_bio->retry_list, &conf->bio_end_io_list);
+ conf->nr_queued++;
spin_unlock_irq(&conf->device_lock);
md_wakeup_thread(conf->mddev->thread);
} else {
@@ -2391,8 +2392,10 @@ static void raid1d(struct md_thread *thread)
LIST_HEAD(tmp);
spin_lock_irqsave(&conf->device_lock, flags);
if (!test_bit(MD_CHANGE_PENDING, &mddev->flags)) {
- list_add(&tmp, &conf->bio_end_io_list);
- list_del_init(&conf->bio_end_io_list);
+ while (!list_empty(&conf->bio_end_io_list)) {
+ list_move(conf->bio_end_io_list.prev, &tmp);
+ conf->nr_queued--;
+ }
}
spin_unlock_irqrestore(&conf->device_lock, flags);
while (!list_empty(&tmp)) {
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index ce959b4ae4df..ebb0dd612ebd 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -2664,6 +2664,7 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio)
if (fail) {
spin_lock_irq(&conf->device_lock);
list_add(&r10_bio->retry_list, &conf->bio_end_io_list);
+ conf->nr_queued++;
spin_unlock_irq(&conf->device_lock);
md_wakeup_thread(conf->mddev->thread);
} else {
@@ -2691,8 +2692,10 @@ static void raid10d(struct md_thread *thread)
LIST_HEAD(tmp);
spin_lock_irqsave(&conf->device_lock, flags);
if (!test_bit(MD_CHANGE_PENDING, &mddev->flags)) {
- list_add(&tmp, &conf->bio_end_io_list);
- list_del_init(&conf->bio_end_io_list);
+ while (!list_empty(&conf->bio_end_io_list)) {
+ list_move(conf->bio_end_io_list.prev, &tmp);
+ conf->nr_queued--;
+ }
}
spin_unlock_irqrestore(&conf->device_lock, flags);
while (!list_empty(&tmp)) {
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 704ef7fcfbf8..10ce885445f6 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -340,8 +340,7 @@ static void release_inactive_stripe_list(struct r5conf *conf,
int hash)
{
int size;
- unsigned long do_wakeup = 0;
- int i = 0;
+ bool do_wakeup = false;
unsigned long flags;
if (hash == NR_STRIPE_HASH_LOCKS) {
@@ -362,19 +361,15 @@ static void release_inactive_stripe_list(struct r5conf *conf,
!list_empty(list))
atomic_dec(&conf->empty_inactive_list_nr);
list_splice_tail_init(list, conf->inactive_list + hash);
- do_wakeup |= 1 << hash;
+ do_wakeup = true;
spin_unlock_irqrestore(conf->hash_locks + hash, flags);
}
size--;
hash--;
}
- for (i = 0; i < NR_STRIPE_HASH_LOCKS; i++) {
- if (do_wakeup & (1 << i))
- wake_up(&conf->wait_for_stripe[i]);
- }
-
if (do_wakeup) {
+ wake_up(&conf->wait_for_stripe);
if (atomic_read(&conf->active_stripes) == 0)
wake_up(&conf->wait_for_quiescent);
if (conf->retry_read_aligned)
@@ -687,15 +682,14 @@ raid5_get_active_stripe(struct r5conf *conf, sector_t sector,
if (!sh) {
set_bit(R5_INACTIVE_BLOCKED,
&conf->cache_state);
- wait_event_exclusive_cmd(
- conf->wait_for_stripe[hash],
+ wait_event_lock_irq(
+ conf->wait_for_stripe,
!list_empty(conf->inactive_list + hash) &&
(atomic_read(&conf->active_stripes)
< (conf->max_nr_stripes * 3 / 4)
|| !test_bit(R5_INACTIVE_BLOCKED,
&conf->cache_state)),
- spin_unlock_irq(conf->hash_locks + hash),
- spin_lock_irq(conf->hash_locks + hash));
+ *(conf->hash_locks + hash));
clear_bit(R5_INACTIVE_BLOCKED,
&conf->cache_state);
} else {
@@ -720,9 +714,6 @@ raid5_get_active_stripe(struct r5conf *conf, sector_t sector,
}
} while (sh == NULL);
- if (!list_empty(conf->inactive_list + hash))
- wake_up(&conf->wait_for_stripe[hash]);
-
spin_unlock_irq(conf->hash_locks + hash);
return sh;
}
@@ -2091,6 +2082,14 @@ static int resize_chunks(struct r5conf *conf, int new_disks, int new_sectors)
unsigned long cpu;
int err = 0;
+ /*
+ * Never shrink. And mddev_suspend() could deadlock if this is called
+ * from raid5d. In that case, scribble_disks and scribble_sectors
+ * should equal to new_disks and new_sectors
+ */
+ if (conf->scribble_disks >= new_disks &&
+ conf->scribble_sectors >= new_sectors)
+ return 0;
mddev_suspend(conf->mddev);
get_online_cpus();
for_each_present_cpu(cpu) {
@@ -2112,6 +2111,10 @@ static int resize_chunks(struct r5conf *conf, int new_disks, int new_sectors)
}
put_online_cpus();
mddev_resume(conf->mddev);
+ if (!err) {
+ conf->scribble_disks = new_disks;
+ conf->scribble_sectors = new_sectors;
+ }
return err;
}
@@ -2192,7 +2195,7 @@ static int resize_stripes(struct r5conf *conf, int newsize)
cnt = 0;
list_for_each_entry(nsh, &newstripes, lru) {
lock_device_hash_lock(conf, hash);
- wait_event_exclusive_cmd(conf->wait_for_stripe[hash],
+ wait_event_cmd(conf->wait_for_stripe,
!list_empty(conf->inactive_list + hash),
unlock_device_hash_lock(conf, hash),
lock_device_hash_lock(conf, hash));
@@ -4238,7 +4241,6 @@ static void break_stripe_batch_list(struct stripe_head *head_sh,
WARN_ON_ONCE(sh->state & ((1 << STRIPE_ACTIVE) |
(1 << STRIPE_SYNCING) |
(1 << STRIPE_REPLACED) |
- (1 << STRIPE_PREREAD_ACTIVE) |
(1 << STRIPE_DELAYED) |
(1 << STRIPE_BIT_DELAY) |
(1 << STRIPE_FULL_WRITE) |
@@ -4253,6 +4255,7 @@ static void break_stripe_batch_list(struct stripe_head *head_sh,
(1 << STRIPE_REPLACED)));
set_mask_bits(&sh->state, ~(STRIPE_EXPAND_SYNC_FLAGS |
+ (1 << STRIPE_PREREAD_ACTIVE) |
(1 << STRIPE_DEGRADED)),
head_sh->state & (1 << STRIPE_INSYNC));
@@ -6414,6 +6417,12 @@ static int raid5_alloc_percpu(struct r5conf *conf)
}
put_online_cpus();
+ if (!err) {
+ conf->scribble_disks = max(conf->raid_disks,
+ conf->previous_raid_disks);
+ conf->scribble_sectors = max(conf->chunk_sectors,
+ conf->prev_chunk_sectors);
+ }
return err;
}
@@ -6504,9 +6513,7 @@ static struct r5conf *setup_conf(struct mddev *mddev)
seqcount_init(&conf->gen_lock);
mutex_init(&conf->cache_size_mutex);
init_waitqueue_head(&conf->wait_for_quiescent);
- for (i = 0; i < NR_STRIPE_HASH_LOCKS; i++) {
- init_waitqueue_head(&conf->wait_for_stripe[i]);
- }
+ init_waitqueue_head(&conf->wait_for_stripe);
init_waitqueue_head(&conf->wait_for_overlap);
INIT_LIST_HEAD(&conf->handle_list);
INIT_LIST_HEAD(&conf->hold_list);
@@ -7015,8 +7022,8 @@ static int run(struct mddev *mddev)
}
if (discard_supported &&
- mddev->queue->limits.max_discard_sectors >= stripe &&
- mddev->queue->limits.discard_granularity >= stripe)
+ mddev->queue->limits.max_discard_sectors >= (stripe >> 9) &&
+ mddev->queue->limits.discard_granularity >= stripe)
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,
mddev->queue);
else
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index a415e1cd39b8..517d4b68a1be 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -510,6 +510,8 @@ struct r5conf {
* conversions
*/
} __percpu *percpu;
+ int scribble_disks;
+ int scribble_sectors;
#ifdef CONFIG_HOTPLUG_CPU
struct notifier_block cpu_notify;
#endif
@@ -522,7 +524,7 @@ struct r5conf {
atomic_t empty_inactive_list_nr;
struct llist_head released_stripes;
wait_queue_head_t wait_for_quiescent;
- wait_queue_head_t wait_for_stripe[NR_STRIPE_HASH_LOCKS];
+ wait_queue_head_t wait_for_stripe;
wait_queue_head_t wait_for_overlap;
unsigned long cache_state;
#define R5_INACTIVE_BLOCKED 1 /* release of inactive stripes blocked,
diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c
index e4900df1140b..c24839cfcc35 100644
--- a/drivers/media/i2c/adv7511.c
+++ b/drivers/media/i2c/adv7511.c
@@ -1161,12 +1161,23 @@ static void adv7511_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd, in
}
}
+static void adv7511_notify_no_edid(struct v4l2_subdev *sd)
+{
+ struct adv7511_state *state = get_adv7511_state(sd);
+ struct adv7511_edid_detect ed;
+
+ /* We failed to read the EDID, so send an event for this. */
+ ed.present = false;
+ ed.segment = adv7511_rd(sd, 0xc4);
+ v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed);
+ v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x0);
+}
+
static void adv7511_edid_handler(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct adv7511_state *state = container_of(dwork, struct adv7511_state, edid_handler);
struct v4l2_subdev *sd = &state->sd;
- struct adv7511_edid_detect ed;
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
@@ -1191,9 +1202,7 @@ static void adv7511_edid_handler(struct work_struct *work)
}
/* We failed to read the EDID, so send an event for this. */
- ed.present = false;
- ed.segment = adv7511_rd(sd, 0xc4);
- v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed);
+ adv7511_notify_no_edid(sd);
v4l2_dbg(1, debug, sd, "%s: no edid found\n", __func__);
}
@@ -1264,7 +1273,6 @@ static void adv7511_check_monitor_present_status(struct v4l2_subdev *sd)
/* update read only ctrls */
v4l2_ctrl_s_ctrl(state->hotplug_ctrl, adv7511_have_hotplug(sd) ? 0x1 : 0x0);
v4l2_ctrl_s_ctrl(state->rx_sense_ctrl, adv7511_have_rx_sense(sd) ? 0x1 : 0x0);
- v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0);
if ((status & MASK_ADV7511_HPD_DETECT) && ((status & MASK_ADV7511_MSEN_DETECT) || state->edid.segments)) {
v4l2_dbg(1, debug, sd, "%s: hotplug and (rx-sense or edid)\n", __func__);
@@ -1294,6 +1302,7 @@ static void adv7511_check_monitor_present_status(struct v4l2_subdev *sd)
}
adv7511_s_power(sd, false);
memset(&state->edid, 0, sizeof(struct adv7511_state_edid));
+ adv7511_notify_no_edid(sd);
}
}
@@ -1370,6 +1379,7 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd)
}
/* one more segment read ok */
state->edid.segments = segment + 1;
+ v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x1);
if (((state->edid.data[0x7e] >> 1) + 1) > state->edid.segments) {
/* Request next EDID segment */
v4l2_dbg(1, debug, sd, "%s: request segment %d\n", __func__, state->edid.segments);
@@ -1389,7 +1399,6 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd)
ed.present = true;
ed.segment = 0;
state->edid_detect_counter++;
- v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0);
v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed);
return ed.present;
}
diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c
index 15a4ebc2844d..51dbef2f9a48 100644
--- a/drivers/media/pci/bt8xx/bttv-driver.c
+++ b/drivers/media/pci/bt8xx/bttv-driver.c
@@ -2334,6 +2334,19 @@ static int bttv_g_fmt_vid_overlay(struct file *file, void *priv,
return 0;
}
+static void bttv_get_width_mask_vid_cap(const struct bttv_format *fmt,
+ unsigned int *width_mask,
+ unsigned int *width_bias)
+{
+ if (fmt->flags & FORMAT_FLAGS_PLANAR) {
+ *width_mask = ~15; /* width must be a multiple of 16 pixels */
+ *width_bias = 8; /* nearest */
+ } else {
+ *width_mask = ~3; /* width must be a multiple of 4 pixels */
+ *width_bias = 2; /* nearest */
+ }
+}
+
static int bttv_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
@@ -2343,6 +2356,7 @@ static int bttv_try_fmt_vid_cap(struct file *file, void *priv,
enum v4l2_field field;
__s32 width, height;
__s32 height2;
+ unsigned int width_mask, width_bias;
int rc;
fmt = format_by_fourcc(f->fmt.pix.pixelformat);
@@ -2375,9 +2389,9 @@ static int bttv_try_fmt_vid_cap(struct file *file, void *priv,
width = f->fmt.pix.width;
height = f->fmt.pix.height;
+ bttv_get_width_mask_vid_cap(fmt, &width_mask, &width_bias);
rc = limit_scaled_size_lock(fh, &width, &height, field,
- /* width_mask: 4 pixels */ ~3,
- /* width_bias: nearest */ 2,
+ width_mask, width_bias,
/* adjust_size */ 1,
/* adjust_crop */ 0);
if (0 != rc)
@@ -2410,6 +2424,7 @@ static int bttv_s_fmt_vid_cap(struct file *file, void *priv,
struct bttv_fh *fh = priv;
struct bttv *btv = fh->btv;
__s32 width, height;
+ unsigned int width_mask, width_bias;
enum v4l2_field field;
retval = bttv_switch_type(fh, f->type);
@@ -2424,9 +2439,10 @@ static int bttv_s_fmt_vid_cap(struct file *file, void *priv,
height = f->fmt.pix.height;
field = f->fmt.pix.field;
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ bttv_get_width_mask_vid_cap(fmt, &width_mask, &width_bias);
retval = limit_scaled_size_lock(fh, &width, &height, f->fmt.pix.field,
- /* width_mask: 4 pixels */ ~3,
- /* width_bias: nearest */ 2,
+ width_mask, width_bias,
/* adjust_size */ 1,
/* adjust_crop */ 1);
if (0 != retval)
@@ -2434,8 +2450,6 @@ static int bttv_s_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.field = field;
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
-
/* update our state informations */
fh->fmt = fmt;
fh->cap.field = f->fmt.pix.field;
diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
index 518086c7aed5..15e56c07b217 100644
--- a/drivers/media/pci/saa7134/saa7134-video.c
+++ b/drivers/media/pci/saa7134/saa7134-video.c
@@ -1219,10 +1219,13 @@ static int saa7134_g_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.height = dev->height;
f->fmt.pix.field = dev->field;
f->fmt.pix.pixelformat = dev->fmt->fourcc;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * dev->fmt->depth) >> 3;
+ if (dev->fmt->planar)
+ f->fmt.pix.bytesperline = f->fmt.pix.width;
+ else
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * dev->fmt->depth) / 8;
f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
+ (f->fmt.pix.height * f->fmt.pix.width * dev->fmt->depth) / 8;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
}
@@ -1298,10 +1301,13 @@ static int saa7134_try_fmt_vid_cap(struct file *file, void *priv,
if (f->fmt.pix.height > maxh)
f->fmt.pix.height = maxh;
f->fmt.pix.width &= ~0x03;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fmt->depth) >> 3;
+ if (fmt->planar)
+ f->fmt.pix.bytesperline = f->fmt.pix.width;
+ else
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) / 8;
f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
+ (f->fmt.pix.height * f->fmt.pix.width * fmt->depth) / 8;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c
index 654e964f84a2..d76511c1c1e3 100644
--- a/drivers/media/platform/coda/coda-bit.c
+++ b/drivers/media/platform/coda/coda-bit.c
@@ -1342,7 +1342,7 @@ static void coda_finish_encode(struct coda_ctx *ctx)
/* Calculate bytesused field */
if (dst_buf->sequence == 0) {
- vb2_set_plane_payload(&dst_buf->vb2_buf, 0,
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr +
ctx->vpu_header_size[0] +
ctx->vpu_header_size[1] +
ctx->vpu_header_size[2]);
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
index 15516a6e3a39..323aad3c89de 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/coda/coda-common.c
@@ -2119,14 +2119,12 @@ static int coda_probe(struct platform_device *pdev)
pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
- if (of_id) {
+ if (of_id)
dev->devtype = of_id->data;
- } else if (pdev_id) {
+ else if (pdev_id)
dev->devtype = &coda_devdata[pdev_id->driver_data];
- } else {
- ret = -EINVAL;
- goto err_v4l2_register;
- }
+ else
+ return -EINVAL;
spin_lock_init(&dev->irqlock);
INIT_LIST_HEAD(&dev->instances);
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
index 6b43efce453f..03a61407aef8 100644
--- a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
+++ b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
@@ -788,8 +788,9 @@ static int cam_smmu_map_buffer_and_add_to_list(int idx, int ion_fd,
rc = msm_dma_map_sg_lazy(iommu_cb_set.cb_info[idx].dev, table->sgl,
table->nents, dma_dir, buf);
- if (!rc) {
+ if (rc != table->nents) {
pr_err("Error: msm_dma_map_sg_lazy failed\n");
+ rc = -ENOMEM;
goto err_unmap_sg;
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
index 84bd3fe3fb85..c12e95d3310a 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
@@ -756,9 +756,14 @@ static int32_t msm_flash_get_pmic_source_info(
"qcom,current",
&fctrl->flash_op_current[i]);
if (rc < 0) {
- pr_err("current: read failed\n");
- of_node_put(flash_src_node);
- continue;
+ rc = of_property_read_u32(flash_src_node,
+ "qcom,current-ma",
+ &fctrl->flash_op_current[i]);
+ if (rc < 0) {
+ pr_err("current: read failed\n");
+ of_node_put(flash_src_node);
+ continue;
+ }
}
/* Read max-current */
@@ -776,8 +781,13 @@ static int32_t msm_flash_get_pmic_source_info(
"qcom,duration",
&fctrl->flash_max_duration[i]);
if (rc < 0) {
- pr_err("duration: read failed\n");
- of_node_put(flash_src_node);
+ rc = of_property_read_u32(flash_src_node,
+ "qcom,duration-ms",
+ &fctrl->flash_max_duration[i]);
+ if (rc < 0) {
+ pr_err("duration: read failed\n");
+ of_node_put(flash_src_node);
+ }
/* Non-fatal; this property is optional */
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
index 9bf5738d838c..02b83c969958 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
@@ -283,6 +283,49 @@ static int32_t msm_sensor_fill_actuator_subdevid_by_name(
return rc;
}
+static int32_t msm_sensor_fill_flash_subdevid_by_name(
+ struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ struct device_node *src_node = NULL;
+ uint32_t val = 0, flash_name_len;
+ int32_t *flash_subdev_id;
+ struct msm_sensor_info_t *sensor_info;
+ struct device_node *of_node = s_ctrl->of_node;
+
+ if (!of_node || !s_ctrl->sensordata->flash_name)
+ return -EINVAL;
+
+ sensor_info = s_ctrl->sensordata->sensor_info;
+ flash_subdev_id = &sensor_info->subdev_id[SUB_MODULE_LED_FLASH];
+
+ *flash_subdev_id = -1;
+
+ flash_name_len = strlen(s_ctrl->sensordata->flash_name);
+ if (flash_name_len >= MAX_SENSOR_NAME)
+ return -EINVAL;
+
+ if (flash_name_len == 0)
+ return 0;
+
+ src_node = of_parse_phandle(of_node, "qcom,led-flash-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,flash cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ *flash_subdev_id = val;
+ of_node_put(src_node);
+ src_node = NULL;
+ }
+ return rc;
+}
+
static int32_t msm_sensor_fill_ois_subdevid_by_name(
struct msm_sensor_ctrl_t *s_ctrl)
{
@@ -872,6 +915,7 @@ CSID_TG:
s_ctrl->sensordata->eeprom_name = slave_info->eeprom_name;
s_ctrl->sensordata->actuator_name = slave_info->actuator_name;
s_ctrl->sensordata->ois_name = slave_info->ois_name;
+ s_ctrl->sensordata->flash_name = slave_info->flash_name;
/*
* Update eeporm subdevice Id by input eeprom name
*/
@@ -895,6 +939,12 @@ CSID_TG:
goto free_camera_info;
}
+ rc = msm_sensor_fill_flash_subdevid_by_name(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_camera_info;
+ }
+
/* Power up and probe sensor */
rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
if (rc < 0) {
@@ -911,14 +961,6 @@ CSID_TG:
s_ctrl->is_probe_succeed = 1;
/*
- * Update the subdevice id of flash-src based on availability in kernel.
- */
- if (strlen(slave_info->flash_name) == 0) {
- s_ctrl->sensordata->sensor_info->
- subdev_id[SUB_MODULE_LED_FLASH] = -1;
- }
-
- /*
* Create /dev/videoX node, comment for now until dummy /dev/videoX
* node is created and used by HAL
*/
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
index c141797bcd3c..c8d14a6d253b 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -77,6 +77,142 @@
#define SDE_ROTREG_READ(base, off) \
readl_relaxed(base + (off))
+/* Invalid software timestamp value for initialization */
+#define SDE_REGDMA_SWTS_INVALID (~0)
+
+/**
+ * sde_hw_rotator_elapsed_swts - Find difference of 2 software timestamps
+ * @ts_curr: current software timestamp
+ * @ts_prev: previous software timestamp
+ * @return: the amount ts_curr is ahead of ts_prev
+ */
+static int sde_hw_rotator_elapsed_swts(u32 ts_curr, u32 ts_prev)
+{
+ u32 diff = (ts_curr - ts_prev) & SDE_REGDMA_SWTS_MASK;
+
+ return sign_extend32(diff, (SDE_REGDMA_SWTS_SHIFT - 1));
+}
+
+/**
+ * sde_hw_rotator_pending_swts - Check if the given context is still pending
+ * @rot: Pointer to hw rotator
+ * @ctx: Pointer to rotator context
+ * @pswts: Pointer to returned reference software timestamp, optional
+ * @return: true if context has pending requests
+ */
+static int sde_hw_rotator_pending_swts(struct sde_hw_rotator *rot,
+ struct sde_hw_rotator_context *ctx, u32 *pswts)
+{
+ u32 swts;
+ int ts_diff;
+ bool pending;
+
+ if (ctx->last_regdma_timestamp == SDE_REGDMA_SWTS_INVALID)
+ swts = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);
+ else
+ swts = ctx->last_regdma_timestamp;
+
+ if (ctx->q_id == ROT_QUEUE_LOW_PRIORITY)
+ swts >>= SDE_REGDMA_SWTS_SHIFT;
+
+ swts &= SDE_REGDMA_SWTS_MASK;
+
+ ts_diff = sde_hw_rotator_elapsed_swts(ctx->timestamp, swts);
+
+ if (pswts)
+ *pswts = swts;
+
+ pending = (ts_diff > 0) ? true : false;
+
+ SDEROT_DBG("ts:0x%x, queue_id:%d, swts:0x%x, pending:%d\n",
+ ctx->timestamp, ctx->q_id, swts, pending);
+ return pending;
+}
+
+/**
+ * sde_hw_rotator_enable_irq - Enable hw rotator interrupt with ref. count
+ * Also, clear rotator/regdma irq status.
+ * @rot: Pointer to hw rotator
+ */
+static void sde_hw_rotator_enable_irq(struct sde_hw_rotator *rot)
+{
+ SDEROT_DBG("irq_num:%d enabled:%d\n", rot->irq_num,
+ atomic_read(&rot->irq_enabled));
+
+ if (!atomic_read(&rot->irq_enabled)) {
+ if (rot->mode == ROT_REGDMA_OFF)
+ SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
+ ROT_DONE_MASK);
+ else
+ SDE_ROTREG_WRITE(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INT_CLEAR, REGDMA_INT_MASK);
+
+ enable_irq(rot->irq_num);
+ }
+ atomic_inc(&rot->irq_enabled);
+}
+
+/**
+ * sde_hw_rotator_disable_irq - Disable hw rotator interrupt with ref. count
+ * Also, clear rotator/regdma irq enable masks.
+ * @rot: Pointer to hw rotator
+ */
+static void sde_hw_rotator_disable_irq(struct sde_hw_rotator *rot)
+{
+ SDEROT_DBG("irq_num:%d enabled:%d\n", rot->irq_num,
+ atomic_read(&rot->irq_enabled));
+
+ if (!atomic_read(&rot->irq_enabled)) {
+ SDEROT_ERR("irq %d is already disabled\n", rot->irq_num);
+ return;
+ }
+
+ if (!atomic_dec_return(&rot->irq_enabled)) {
+ if (rot->mode == ROT_REGDMA_OFF)
+ SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_EN, 0);
+ else
+ SDE_ROTREG_WRITE(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INT_EN, 0);
+ /* disable irq after last pending irq is handled, if any */
+ synchronize_irq(rot->irq_num);
+ disable_irq_nosync(rot->irq_num);
+ }
+}
+
+/**
+ * sde_hw_rotator_dump_status - Dump hw rotator status on error
+ * @rot: Pointer to hw rotator
+ */
+static void sde_hw_rotator_dump_status(struct sde_hw_rotator *rot)
+{
+ SDEROT_ERR(
+ "op_mode = %x, int_en = %x, int_status = %x\n",
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_OP_MODE),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INT_EN),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INT_STATUS));
+
+ SDEROT_ERR(
+ "ts = %x, q0_status = %x, q1_status = %x, block_status = %x\n",
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_TIMESTAMP_REG),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_QUEUE_0_STATUS),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_QUEUE_1_STATUS),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_BLOCK_STATUS));
+
+ SDEROT_ERR(
+ "invalid_cmd_offset = %x, fsm_state = %x\n",
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INVALID_CMD_RAM_OFFSET),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_FSM_STATE));
+}
+
/**
* sde_hw_rotator_get_ctx(): Retrieve rotator context from rotator HW based
* on provided session_id. Each rotator has a different session_id.
@@ -476,7 +612,7 @@ static u32 sde_hw_rotator_start_no_regdma(struct sde_hw_rotator_context *ctx,
SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_EN, 1);
SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_CLEAR, 1);
reinit_completion(&ctx->rot_comp);
- enable_irq(rot->irq_num);
+ sde_hw_rotator_enable_irq(rot);
}
SDE_REGDMA_WRITE(wrptr, ROTTOP_START_CTRL, 1);
@@ -572,9 +708,6 @@ static u32 sde_hw_rotator_start_regdma(struct sde_hw_rotator_context *ctx,
wrptr = sde_hw_rotator_get_regdma_segment(ctx);
- if (rot->irq_num >= 0)
- reinit_completion(&ctx->regdma_comp);
-
/*
* Last ROT command must be ROT_START before REGDMA start
*/
@@ -676,7 +809,7 @@ static u32 sde_hw_rotator_wait_done_no_regdma(
SDEROT_WARN(
"Timeout waiting, but rotator job is done!!\n");
- disable_irq_nosync(rot->irq_num);
+ sde_hw_rotator_disable_irq(rot);
}
spin_unlock_irqrestore(&rot->rotisr_lock, flags);
} else {
@@ -719,13 +852,15 @@ static u32 sde_hw_rotator_wait_done_regdma(
u32 last_isr;
u32 last_ts;
u32 int_id;
+ u32 swts;
u32 sts = 0;
unsigned long flags;
if (rot->irq_num >= 0) {
SDEROT_DBG("Wait for REGDMA completion, ctx:%p, ts:%X\n",
ctx, ctx->timestamp);
- rc = wait_for_completion_timeout(&ctx->regdma_comp,
+ rc = wait_event_timeout(ctx->regdma_waitq,
+ !sde_hw_rotator_pending_swts(rot, ctx, &swts),
KOFF_TIMEOUT);
spin_lock_irqsave(&rot->rotisr_lock, flags);
@@ -738,11 +873,12 @@ static u32 sde_hw_rotator_wait_done_regdma(
status, int_id, last_ts);
if (rc == 0 || (status & REGDMA_INT_ERR_MASK)) {
+ bool pending;
+
+ pending = sde_hw_rotator_pending_swts(rot, ctx, &swts);
SDEROT_ERR(
- "Timeout wait for regdma interrupt status, ts:%X\n",
- ctx->timestamp);
- SDEROT_ERR("last_isr:0x%X, last_ts:0x%X, rc=%d\n",
- last_isr, last_ts, rc);
+ "Timeout wait for regdma interrupt status, ts:0x%X/0x%X pending:%d\n",
+ ctx->timestamp, swts, pending);
if (status & REGDMA_WATCHDOG_INT)
SDEROT_ERR("REGDMA watchdog interrupt\n");
@@ -753,24 +889,13 @@ static u32 sde_hw_rotator_wait_done_regdma(
else if (status & REGDMA_INVALID_CMD)
SDEROT_ERR("REGDMA invalid command\n");
+ sde_hw_rotator_dump_status(rot);
status = ROT_ERROR_BIT;
- } else if (queue_id == ROT_QUEUE_HIGH_PRIORITY) {
- /* Got to match exactly with interrupt ID */
- int_id = REGDMA_QUEUE0_INT0 << int_id;
-
- SDE_ROTREG_WRITE(rot->mdss_base,
- REGDMA_CSR_REGDMA_INT_CLEAR,
- int_id);
-
- status = 0;
- } else if (queue_id == ROT_QUEUE_LOW_PRIORITY) {
- /* Matching interrupt ID */
- int_id = REGDMA_QUEUE1_INT0 << int_id;
-
- SDE_ROTREG_WRITE(rot->mdss_base,
- REGDMA_CSR_REGDMA_INT_CLEAR,
- int_id);
-
+ } else {
+ if (rc == 1)
+ SDEROT_WARN(
+ "REGDMA done but no irq, ts:0x%X/0x%X\n",
+ ctx->timestamp, swts);
status = 0;
}
@@ -1007,7 +1132,7 @@ static struct sde_rot_hw_resource *sde_hw_rotator_alloc_ext(
}
if (resinfo->rot->irq_num >= 0)
- enable_irq(resinfo->rot->irq_num);
+ sde_hw_rotator_enable_irq(resinfo->rot);
SDEROT_DBG("New rotator resource:%p, priority:%d\n",
resinfo, wb_id);
@@ -1036,7 +1161,7 @@ static void sde_hw_rotator_free_ext(struct sde_rot_mgr *mgr,
hw->pending_count);
if (resinfo->rot->irq_num >= 0)
- disable_irq(resinfo->rot->irq_num);
+ sde_hw_rotator_disable_irq(resinfo->rot);
devm_kfree(&mgr->pdev->dev, resinfo);
}
@@ -1078,8 +1203,10 @@ static struct sde_hw_rotator_context *sde_hw_rotator_alloc_rotctx(
ctx->q_id * SDE_HW_ROT_REGDMA_TOTAL_CTX +
sde_hw_rotator_get_regdma_ctxidx(ctx));
+ ctx->last_regdma_timestamp = SDE_REGDMA_SWTS_INVALID;
+
init_completion(&ctx->rot_comp);
- init_completion(&ctx->regdma_comp);
+ init_waitqueue_head(&ctx->regdma_waitq);
/* Store rotator context for lookup purpose */
sde_hw_rotator_put_ctx(ctx);
@@ -1419,7 +1546,7 @@ static irqreturn_t sde_hw_rotator_rotirq_handler(int irq, void *ptr)
if (isr & ROT_DONE_MASK) {
if (rot->irq_num >= 0)
- disable_irq_nosync(rot->irq_num);
+ sde_hw_rotator_disable_irq(rot);
SDEROT_DBG("Notify rotator complete\n");
/* Normal rotator only 1 session, no need to lookup */
@@ -1456,6 +1583,8 @@ static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
u32 q_id;
isr = SDE_ROTREG_READ(rot->mdss_base, REGDMA_CSR_REGDMA_INT_STATUS);
+ /* acknowledge interrupt before reading latest timestamp */
+ SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR, isr);
ts = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);
SDEROT_DBG("intr_status = %8.8x, sw_TS:%X\n", isr, ts);
@@ -1480,30 +1609,23 @@ static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
}
ctx = rot->rotCtx[q_id][ts & SDE_HW_ROT_REGDMA_SEG_MASK];
- WARN_ON(ctx == NULL);
/*
* Wake up all waiting context from the current and previous
* SW Timestamp.
*/
- do {
+ while (ctx &&
+ sde_hw_rotator_elapsed_swts(ctx->timestamp, ts) >= 0) {
ctx->last_regdma_isr_status = isr;
ctx->last_regdma_timestamp = ts;
SDEROT_DBG(
"regdma complete: ctx:%p, ts:%X\n", ctx, ts);
- complete_all(&ctx->regdma_comp);
+ wake_up_all(&ctx->regdma_waitq);
ts = (ts - 1) & SDE_REGDMA_SWTS_MASK;
ctx = rot->rotCtx[q_id]
[ts & SDE_HW_ROT_REGDMA_SEG_MASK];
- } while (ctx && (ctx->last_regdma_timestamp == 0));
-
- /*
- * Clear corresponding regdma interrupt because it is a level
- * interrupt
- */
- SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR,
- isr);
+ };
spin_unlock(&rot->rotisr_lock);
ret = IRQ_HANDLED;
@@ -1526,16 +1648,13 @@ static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
if (ctx && ctx->last_regdma_isr_status == 0) {
ctx->last_regdma_isr_status = isr;
ctx->last_regdma_timestamp = ts;
- complete_all(&ctx->regdma_comp);
+ wake_up_all(&ctx->regdma_waitq);
SDEROT_DBG("Wakeup rotctx[%d][%d]:%p\n",
i, j, ctx);
}
}
}
- SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR,
- isr);
-
spin_unlock(&rot->rotisr_lock);
ret = IRQ_HANDLED;
}
@@ -1810,6 +1929,7 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr)
disable_irq(rot->irq_num);
}
}
+ atomic_set(&rot->irq_enabled, 0);
setup_rotator_ops(&rot->ops, rot->mode);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
index 610caf16c764..272b15e01e8b 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
@@ -202,7 +202,7 @@ struct sde_hw_rotator_context {
u32 *regdma_wrptr;
u32 timestamp;
struct completion rot_comp;
- struct completion regdma_comp;
+ wait_queue_head_t regdma_waitq;
struct sde_dbg_buf src_dbgbuf;
struct sde_dbg_buf dst_dbgbuf;
u32 last_regdma_isr_status;
@@ -253,6 +253,7 @@ struct sde_hw_rotator {
/* logical interrupt number */
int irq_num;
+ atomic_t irq_enabled;
/* internal ION memory for SW timestamp */
struct ion_client *iclient;
@@ -260,8 +261,6 @@ struct sde_hw_rotator {
void *swts_buffer;
u32 highest_bank;
- struct completion rot_comp;
- struct completion regdma_comp;
spinlock_t rotctx_lock;
spinlock_t rotisr_lock;
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index 4a26ab920016..9b9b74436d88 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -672,6 +672,15 @@ static int get_hfi_extradata_index(enum hal_extradata_id index)
case HAL_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI:
ret = HFI_PROPERTY_PARAM_VDEC_CONTENT_LIGHT_LEVEL_SEI_EXTRADATA;
break;
+ case HAL_EXTRADATA_PQ_INFO:
+ ret = HFI_PROPERTY_PARAM_VENC_OVERRIDE_QP_EXTRADATA;
+ break;
+ case HAL_EXTRADATA_VUI_DISPLAY_INFO:
+ ret = HFI_PROPERTY_PARAM_VUI_DISPLAY_INFO_EXTRADATA;
+ break;
+ case HAL_EXTRADATA_VPX_COLORSPACE:
+ ret = HFI_PROPERTY_PARAM_VDEC_VPX_COLORSPACE_EXTRADATA;
+ break;
default:
dprintk(VIDC_WARN, "Extradata index not found: %d\n", index);
break;
@@ -2124,6 +2133,26 @@ int create_pkt_cmd_session_set_property(
pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
break;
}
+ case HAL_PARAM_VENC_VIDEO_SIGNAL_INFO:
+ {
+ struct hal_video_signal_info *hal = pdata;
+ struct hfi_video_signal_metadata *signal_info =
+ (struct hfi_video_signal_metadata *)
+ &pkt->rg_property_data[1];
+
+ signal_info->enable = true;
+ signal_info->video_format = MSM_VIDC_NTSC;
+ signal_info->video_full_range = hal->full_range;
+ signal_info->color_description = MSM_VIDC_COLOR_DESC_PRESENT;
+ signal_info->color_primaries = hal->color_space;
+ signal_info->transfer_characteristics = hal->transfer_chars;
+ signal_info->matrix_coeffs = hal->matrix_coeffs;
+
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_PARAM_VENC_VIDEO_SIGNAL_INFO;
+ pkt->size += sizeof(u32) + sizeof(*signal_info);
+ break;
+ }
/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
case HAL_CONFIG_BUFFER_REQUIREMENTS:
case HAL_CONFIG_PRIORITY:
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index d8c6e30204d1..96fefea39241 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -247,7 +247,7 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = {
.name = "Extradata Type",
.type = V4L2_CTRL_TYPE_MENU,
.minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE,
- .maximum = V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI,
+ .maximum = V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE,
.default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE,
.menu_skip_mask = ~(
(1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) |
@@ -273,7 +273,10 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = {
(1 << V4L2_MPEG_VIDC_EXTRADATA_VQZIP_SEI) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_OUTPUT_CROP) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_DISPLAY_COLOUR_SEI) |
- (1 << V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI)
+ (1 <<
+ V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI) |
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY) |
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE)
),
.qmenu = mpeg_video_vidc_extradata,
},
@@ -1086,13 +1089,6 @@ int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
return -EINVAL;
}
- rc = msm_comm_try_get_bufreqs(inst);
- if (rc) {
- dprintk(VIDC_ERR, "Getting buffer requirements failed: %d\n",
- rc);
- return rc;
- }
-
hdev = inst->core->device;
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
fmt = inst->fmts[CAPTURE_PORT];
@@ -1135,13 +1131,6 @@ int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
stride = inst->prop.width[CAPTURE_PORT];
scanlines = inst->prop.height[CAPTURE_PORT];
- rc = msm_comm_try_get_bufreqs(inst);
- if (rc) {
- dprintk(VIDC_ERR,
- "%s: Failed : Buffer requirements\n", __func__);
- goto exit;
- }
-
if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
plane_sizes = &inst->bufq[OUTPUT_PORT].vb2_bufq.plane_sizes[0];
for (i = 0; i < fmt->num_planes; ++i) {
@@ -1176,10 +1165,10 @@ int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
scanlines = VENUS_Y_SCANLINES(color_format,
inst->prop.height[CAPTURE_PORT]);
- bufreq = get_buff_req_buffer(inst,
- msm_comm_get_hal_output_buffer(inst));
f->fmt.pix_mp.plane_fmt[0].sizeimage =
- bufreq ? bufreq->buffer_size : 0;
+ fmt->get_frame_size(0,
+ inst->prop.height[CAPTURE_PORT],
+ inst->prop.width[CAPTURE_PORT]);
extra_idx = EXTRADATA_IDX(fmt->num_planes);
if (extra_idx && extra_idx < VIDEO_MAX_PLANES) {
@@ -1316,28 +1305,6 @@ static int update_output_buffer_size(struct msm_vidc_inst *inst,
goto exit;
}
- /* Query buffer requirements from firmware */
- rc = msm_comm_try_get_bufreqs(inst);
- if (rc)
- dprintk(VIDC_WARN,
- "Failed to get buf req, %d\n", rc);
-
- /* Read back updated firmware size */
- for (i = 0; i < num_planes; ++i) {
- enum hal_buffer type = msm_comm_get_hal_output_buffer(inst);
-
- if (EXTRADATA_IDX(num_planes) &&
- i == EXTRADATA_IDX(num_planes)) {
- type = HAL_BUFFER_EXTRADATA_OUTPUT;
- }
-
- bufreq = get_buff_req_buffer(inst, type);
- f->fmt.pix_mp.plane_fmt[i].sizeimage = bufreq ?
- bufreq->buffer_size : 0;
- dprintk(VIDC_DBG,
- "updated buffer size for plane[%d] = %d\n",
- i, f->fmt.pix_mp.plane_fmt[i].sizeimage);
- }
exit:
return rc;
}
@@ -1377,10 +1344,12 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
{
struct msm_vidc_format *fmt = NULL;
struct hal_frame_size frame_sz;
+ unsigned int extra_idx = 0;
int rc = 0;
int ret = 0;
int i;
int max_input_size = 0;
+ struct hal_buffer_requirements *bufreq;
if (!inst || !f) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
@@ -1425,23 +1394,22 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
HAL_PARAM_FRAME_SIZE, &frame_sz);
}
- ret = ret || msm_comm_try_get_bufreqs(inst);
- if (ret) {
- for (i = 0; i < fmt->num_planes; ++i) {
- f->fmt.pix_mp.plane_fmt[i].sizeimage =
- get_frame_size(inst, fmt, f->type, i);
- }
- } else {
- rc = update_output_buffer_size(inst, f,
- fmt->num_planes);
- if (rc) {
- dprintk(VIDC_ERR,
- "%s - failed to update buffer size: %d\n",
- __func__, rc);
- goto err_invalid_fmt;
- }
+ f->fmt.pix_mp.plane_fmt[0].sizeimage =
+ fmt->get_frame_size(0,
+ f->fmt.pix_mp.height, f->fmt.pix_mp.width);
+
+ extra_idx = EXTRADATA_IDX(fmt->num_planes);
+ if (extra_idx && extra_idx < VIDEO_MAX_PLANES) {
+ bufreq = get_buff_req_buffer(inst,
+ HAL_BUFFER_EXTRADATA_OUTPUT);
+ f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage =
+ bufreq ? bufreq->buffer_size : 0;
}
+ for (i = 0; i < fmt->num_planes; ++i)
+ inst->bufq[CAPTURE_PORT].vb2_bufq.plane_sizes[i] =
+ f->fmt.pix_mp.plane_fmt[i].sizeimage;
+
f->fmt.pix_mp.num_planes = fmt->num_planes;
for (i = 0; i < fmt->num_planes; ++i) {
inst->bufq[CAPTURE_PORT].vb2_bufq.plane_sizes[i] =
@@ -1619,6 +1587,13 @@ static int msm_vdec_queue_setup(struct vb2_queue *q,
return -EINVAL;
}
+ rc = msm_comm_try_get_bufreqs(inst);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "%s: Failed : Buffer requirements\n", __func__);
+ goto exit;
+ }
+
switch (q->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
*num_planes = inst->fmts[OUTPUT_PORT]->num_planes;
@@ -1693,7 +1668,7 @@ static int msm_vdec_queue_setup(struct vb2_queue *q,
inst->buff_req.buffer[1].buffer_count_actual,
inst->buff_req.buffer[1].buffer_size,
inst->buff_req.buffer[1].buffer_alignment);
- sizes[0] = bufreq->buffer_size;
+ sizes[0] = inst->bufq[CAPTURE_PORT].vb2_bufq.plane_sizes[0];
/*
* Set actual buffer count to firmware for DPB buffers.
@@ -1734,6 +1709,7 @@ static int msm_vdec_queue_setup(struct vb2_queue *q,
rc = -EINVAL;
break;
}
+exit:
return rc;
}
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 5c7408740e95..c08084a54e86 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -725,7 +725,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.name = "Extradata Type",
.type = V4L2_CTRL_TYPE_MENU,
.minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE,
- .maximum = V4L2_MPEG_VIDC_EXTRADATA_ROI_QP,
+ .maximum = V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO,
.default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE,
.menu_skip_mask = ~(
(1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) |
@@ -747,7 +747,8 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
(1 << V4L2_MPEG_VIDC_EXTRADATA_LTR) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS)|
- (1 << V4L2_MPEG_VIDC_EXTRADATA_ROI_QP)
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_ROI_QP) |
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO)
),
.qmenu = mpeg_video_vidc_extradata,
},
@@ -1241,6 +1242,46 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.default_value = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE,
.step = 1,
},
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE,
+ .name = "Set Color space",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = MSM_VIDC_BT709_5,
+ .maximum = MSM_VIDC_BT2020,
+ .default_value = MSM_VIDC_BT601_6_625,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE,
+ .name = "Set Color space range",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE_DISABLE,
+ .maximum = V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE_ENABLE,
+ .default_value = V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE_DISABLE,
+ .step = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS,
+ .name = "Set Color space transfer characterstics",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = MSM_VIDC_TRANSFER_BT709_5,
+ .maximum = MSM_VIDC_TRANSFER_BT_2020_12,
+ .default_value = MSM_VIDC_TRANSFER_601_6_625,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS,
+ .name = "Set Color space matrix coefficients",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = MSM_VIDC_MATRIX_BT_709_5,
+ .maximum = MSM_VIDC_MATRIX_BT_2020_CONST,
+ .default_value = MSM_VIDC_MATRIX_601_6_625,
+ .step = 1,
+ .qmenu = NULL,
+ },
+
};
#define NUM_CTRLS ARRAY_SIZE(msm_venc_ctrls)
@@ -1512,6 +1553,7 @@ static int msm_venc_queue_setup(struct vb2_queue *q,
case V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO:
case V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS:
case V4L2_MPEG_VIDC_EXTRADATA_ROI_QP:
+ case V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO:
*num_planes = *num_planes + 1;
break;
default:
@@ -2103,6 +2145,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
int baselayerid = 0;
int frameqp = 0;
int pic_order_cnt = 0;
+ struct hal_video_signal_info signal_info = {0};
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
@@ -3091,6 +3134,64 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
pdata = &pic_order_cnt;
break;
}
+ case V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE:
+ {
+ signal_info.color_space = ctrl->val;
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE);
+ signal_info.full_range = temp_ctrl ? temp_ctrl->val : 0;
+ temp_ctrl =
+ TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS);
+ signal_info.transfer_chars = temp_ctrl ? temp_ctrl->val : 0;
+ temp_ctrl =
+ TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS);
+ signal_info.matrix_coeffs = temp_ctrl ? temp_ctrl->val : 0;
+ property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO;
+ pdata = &signal_info;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE:
+ {
+ signal_info.full_range = ctrl->val;
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE);
+ signal_info.color_space = temp_ctrl ? temp_ctrl->val : 0;
+ temp_ctrl =
+ TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS);
+ signal_info.transfer_chars = temp_ctrl ? temp_ctrl->val : 0;
+ temp_ctrl =
+ TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS);
+ signal_info.matrix_coeffs = temp_ctrl ? temp_ctrl->val : 0;
+ property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO;
+ pdata = &signal_info;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS:
+ {
+ signal_info.transfer_chars = ctrl->val;
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE);
+ signal_info.full_range = temp_ctrl ? temp_ctrl->val : 0;
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE);
+ signal_info.color_space = temp_ctrl ? temp_ctrl->val : 0;
+ temp_ctrl =
+ TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS);
+ signal_info.matrix_coeffs = temp_ctrl ? temp_ctrl->val : 0;
+ property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO;
+ pdata = &signal_info;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS:
+ {
+ signal_info.matrix_coeffs = ctrl->val;
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE);
+ signal_info.full_range = temp_ctrl ? temp_ctrl->val : 0;
+ temp_ctrl =
+ TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS);
+ signal_info.transfer_chars = temp_ctrl ? temp_ctrl->val : 0;
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE);
+ signal_info.color_space = temp_ctrl ? temp_ctrl->val : 0;
+ property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO;
+ pdata = &signal_info;
+ break;
+ }
case V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC:
if (ctrl->val == V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_ENABLE) {
rc = msm_venc_set_csc(inst);
@@ -3514,10 +3615,6 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
}
hdev = inst->core->device;
- if (msm_vidc_vpe_csc_601_to_709) {
- msm_venc_set_csc(inst);
- }
-
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats,
ARRAY_SIZE(venc_formats), f->fmt.pix_mp.pixelformat,
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 05bfabce2bb2..1f071ba36ec1 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -76,6 +76,9 @@ const char *const mpeg_video_vidc_extradata[] = {
"Extradata output crop",
"Extradata display colour SEI",
"Extradata light level SEI",
+ "Extradata PQ Info",
+ "Extradata display VUI",
+ "Extradata vpx color space",
};
struct getprop_buf {
@@ -86,6 +89,7 @@ struct getprop_buf {
static void msm_comm_generate_session_error(struct msm_vidc_inst *inst);
static void msm_comm_generate_sys_error(struct msm_vidc_inst *inst);
static void handle_session_error(enum hal_command_response cmd, void *data);
+static void msm_vidc_print_running_insts(struct msm_vidc_core *core);
bool msm_comm_turbo_session(struct msm_vidc_inst *inst)
{
@@ -208,7 +212,8 @@ int msm_comm_ctrl_init(struct msm_vidc_inst *inst,
}
if (!ctrl) {
- dprintk(VIDC_ERR, "%s - invalid ctrl\n", __func__);
+ dprintk(VIDC_ERR, "%s - invalid ctrl %s\n", __func__,
+ drv_ctrls[idx].name);
return -EINVAL;
}
@@ -879,11 +884,13 @@ static int wait_for_sess_signal_receipt(struct msm_vidc_inst *inst,
enum hal_command_response cmd)
{
int rc = 0;
+ struct hfi_device *hdev;
if (!IS_HAL_SESSION_CMD(cmd)) {
dprintk(VIDC_ERR, "Invalid inst cmd response: %d\n", cmd);
return -EINVAL;
}
+ hdev = (struct hfi_device *)(inst->core->device);
rc = wait_for_completion_timeout(
&inst->completions[SESSION_MSG_INDEX(cmd)],
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
@@ -891,7 +898,11 @@ static int wait_for_sess_signal_receipt(struct msm_vidc_inst *inst,
dprintk(VIDC_ERR, "Wait interrupted or timed out: %d\n",
SESSION_MSG_INDEX(cmd));
msm_comm_kill_session(inst);
- BUG_ON(msm_vidc_debug_timeout);
+ call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data);
+ dprintk(VIDC_ERR,
+ "sess resp timeout can potentially crash the system\n");
+
+ BUG_ON(inst->core->resources.debug_timeout);
rc = -EIO;
} else {
rc = 0;
@@ -1623,6 +1634,13 @@ static void handle_sys_error(enum hal_command_response cmd, void *data)
core->state = VIDC_CORE_UNINIT;
}
mutex_unlock(&core->lock);
+
+ msm_vidc_print_running_insts(core);
+ call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data);
+ dprintk(VIDC_ERR,
+ "SYS_ERROR can potentially crash the system\n");
+
+ BUG_ON(core->resources.debug_timeout);
}
void msm_comm_session_clean(struct msm_vidc_inst *inst)
@@ -1704,6 +1722,19 @@ static struct vb2_buffer *get_vb_from_device_addr(struct buf_queue *bufq,
return vb;
}
+static void msm_vidc_try_suspend(struct msm_vidc_inst *inst)
+{
+ bool batch_mode;
+
+ batch_mode = msm_comm_g_ctrl_for_id(inst, V4L2_CID_VIDC_QBUF_MODE)
+ == V4L2_VIDC_QBUF_BATCHED;
+ if (batch_mode) {
+ dprintk(VIDC_DBG,
+ "Trying to suspend Venus after finishing Batch\n");
+ msm_comm_suspend(inst->core->id);
+ }
+}
+
static void handle_ebd(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_data_done *response = data;
@@ -1775,6 +1806,8 @@ static void handle_ebd(enum hal_command_response cmd, void *data)
msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_EBD);
}
+ msm_vidc_try_suspend(inst);
+
put_inst(inst);
}
@@ -2074,6 +2107,7 @@ static void handle_fbd(enum hal_command_response cmd, void *data)
msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_FBD);
}
+ msm_vidc_try_suspend(inst);
err_handle_fbd:
put_inst(inst);
}
@@ -2393,7 +2427,11 @@ static int msm_comm_session_abort(struct msm_vidc_inst *inst)
dprintk(VIDC_ERR,
"%s: Wait interrupted or timed out [%p]: %d\n",
__func__, inst, abort_completion);
- BUG_ON(msm_vidc_debug_timeout);
+ call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data);
+ dprintk(VIDC_ERR,
+ "ABORT timeout can potentially crash the system\n");
+
+ BUG_ON(inst->core->resources.debug_timeout);
rc = -EBUSY;
} else {
rc = 0;
@@ -2463,6 +2501,7 @@ void msm_comm_handle_thermal_event()
int msm_comm_check_core_init(struct msm_vidc_core *core)
{
int rc = 0;
+ struct hfi_device *hdev;
mutex_lock(&core->lock);
if (core->state >= VIDC_CORE_INIT_DONE) {
@@ -2471,13 +2510,18 @@ int msm_comm_check_core_init(struct msm_vidc_core *core)
goto exit;
}
dprintk(VIDC_DBG, "Waiting for SYS_INIT_DONE\n");
+ hdev = (struct hfi_device *)core->device;
rc = wait_for_completion_timeout(
&core->completions[SYS_MSG_INDEX(HAL_SYS_INIT_DONE)],
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
dprintk(VIDC_ERR, "%s: Wait interrupted or timed out: %d\n",
__func__, SYS_MSG_INDEX(HAL_SYS_INIT_DONE));
- BUG_ON(msm_vidc_debug_timeout);
+ call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data);
+ dprintk(VIDC_ERR,
+ "SYS_INIT timeout can potentially crash the system\n");
+
+ BUG_ON(core->resources.debug_timeout);
rc = -EIO;
goto exit;
} else {
@@ -3944,7 +3988,11 @@ int msm_comm_try_get_prop(struct msm_vidc_inst *inst, enum hal_property ptype,
SESSION_MSG_INDEX(HAL_SESSION_PROPERTY_INFO));
inst->state = MSM_VIDC_CORE_INVALID;
msm_comm_kill_session(inst);
- BUG_ON(msm_vidc_debug_timeout);
+ call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data);
+ dprintk(VIDC_ERR,
+ "SESS_PROP timeout can potentially crash the system\n");
+
+ BUG_ON(inst->core->resources.debug_timeout);
rc = -ETIMEDOUT;
goto exit;
} else {
@@ -4660,6 +4708,16 @@ enum hal_extradata_id msm_comm_get_hal_extradata_index(
case V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI:
ret = HAL_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI;
break;
+ case V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO:
+ ret = HAL_EXTRADATA_PQ_INFO;
+ break;
+
+ case V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY:
+ ret = HAL_EXTRADATA_VUI_DISPLAY_INFO;
+ break;
+ case V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE:
+ ret = HAL_EXTRADATA_VPX_COLORSPACE;
+ break;
default:
dprintk(VIDC_WARN, "Extradata not found: %d\n", index);
break;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
index fb79661dd2d7..7976d6e8a603 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
@@ -171,8 +171,6 @@ struct dentry *msm_vidc_debugfs_init_drv(void)
&msm_vidc_fw_low_power_mode) &&
__debugfs_create(u32, "debug_output", &msm_vidc_debug_out) &&
__debugfs_create(u32, "hw_rsp_timeout", &msm_vidc_hw_rsp_timeout) &&
- __debugfs_create(bool, "enable_vpe_csc_601_709",
- &msm_vidc_vpe_csc_601_to_709) &&
__debugfs_create(bool, "sys_idle_indicator",
&msm_vidc_sys_idle_indicator) &&
__debugfs_create(u32, "firmware_unload_delay",
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 4503e46b044d..2bb91ccc6c26 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
@@ -1103,6 +1103,11 @@ int read_platform_resources_from_dt(
res->never_unload_fw = of_property_read_bool(pdev->dev.of_node,
"qcom,never-unload-fw");
+ res->debug_timeout = of_property_read_bool(pdev->dev.of_node,
+ "qcom,debug-timeout");
+
+ res->debug_timeout |= msm_vidc_debug_timeout;
+
of_property_read_u32(pdev->dev.of_node,
"qcom,pm-qos-latency-us", &res->pm_qos_latency_us);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
index 1fc1888e81c6..c61605c7e405 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_resources.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
@@ -185,6 +185,7 @@ struct msm_vidc_platform_resources {
const char *fw_name;
const char *hfi_version;
bool never_unload_fw;
+ bool debug_timeout;
uint32_t pm_qos_latency_us;
uint32_t max_inst_count;
uint32_t max_secure_inst_count;
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index ac53b3bcb4ed..50c0eb351d4f 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -1297,16 +1297,31 @@ static int venus_hfi_suspend(void *dev)
return -ENOTSUPP;
}
+ dprintk(VIDC_DBG, "Suspending Venus\n");
+ rc = flush_delayed_work(&venus_hfi_pm_work);
+
+ return rc;
+}
+
+static int venus_hfi_flush_debug_queue(void *dev)
+{
+ int rc = 0;
+ struct venus_hfi_device *device = (struct venus_hfi_device *) dev;
+
+ if (!device) {
+ dprintk(VIDC_ERR, "%s invalid device\n", __func__);
+ return -EINVAL;
+ }
+
mutex_lock(&device->lock);
if (device->power_enabled) {
dprintk(VIDC_DBG, "Venus is busy\n");
rc = -EBUSY;
- } else {
- dprintk(VIDC_DBG, "Venus is power suspended\n");
- rc = 0;
+ goto exit;
}
-
+ __flush_debug_queue(device, NULL);
+exit:
mutex_unlock(&device->lock);
return rc;
}
@@ -3322,6 +3337,7 @@ static void __process_sys_error(struct venus_hfi_device *device)
static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet)
{
bool local_packet = false;
+ enum vidc_msg_prio log_level = VIDC_FW;
if (!device) {
dprintk(VIDC_ERR, "%s: Invalid params\n", __func__);
@@ -3337,6 +3353,13 @@ static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet)
}
local_packet = true;
+
+ /*
+ * Local packek is used when something FATAL occurred.
+ * It is good to print these logs by default.
+ */
+
+ log_level = VIDC_ERR;
}
while (!__iface_dbgq_read(device, packet)) {
@@ -3353,7 +3376,7 @@ static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet)
} else {
struct hfi_msg_sys_debug_packet *pkt =
(struct hfi_msg_sys_debug_packet *) packet;
- dprintk(VIDC_FW, "%s", pkt->rg_msg_data);
+ dprintk(log_level, "%s", pkt->rg_msg_data);
}
}
@@ -4629,6 +4652,7 @@ static void venus_init_hfi_callbacks(struct hfi_device *hdev)
hdev->get_fw_info = venus_hfi_get_fw_info;
hdev->get_core_capabilities = venus_hfi_get_core_capabilities;
hdev->suspend = venus_hfi_suspend;
+ hdev->flush_debug_queue = venus_hfi_flush_debug_queue;
hdev->get_core_clock_rate = venus_hfi_get_core_clock_rate;
hdev->get_default_properties = venus_hfi_get_default_properties;
}
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
index cbb4e3569b13..4cbb59d12f92 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.h
@@ -85,6 +85,7 @@
#define HFI_EXTRADATA_STREAM_USERDATA 0x0000000E
#define HFI_EXTRADATA_FRAME_QP 0x0000000F
#define HFI_EXTRADATA_FRAME_BITS_INFO 0x00000010
+#define HFI_EXTRADATA_VPX_COLORSPACE 0x00000014
#define HFI_EXTRADATA_MULTISLICE_INFO 0x7F100000
#define HFI_EXTRADATA_NUM_CONCEALED_MB 0x7F100001
#define HFI_EXTRADATA_INDEX 0x7F100002
@@ -253,6 +254,8 @@ struct hfi_extradata_header {
(HFI_PROPERTY_PARAM_VENC_OX_START + 0x007)
#define HFI_PROPERTY_PARAM_VENC_ROI_QP_EXTRADATA \
(HFI_PROPERTY_PARAM_VENC_OX_START + 0x008)
+#define HFI_PROPERTY_PARAM_VENC_OVERRIDE_QP_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VENC_OX_START + 0x009)
#define HFI_PROPERTY_CONFIG_VENC_OX_START \
(HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x6000)
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index b2231869c499..34ab36a4647b 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -124,6 +124,9 @@ enum hal_extradata_id {
HAL_EXTRADATA_OUTPUT_CROP,
HAL_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI,
HAL_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI,
+ HAL_EXTRADATA_PQ_INFO,
+ HAL_EXTRADATA_VUI_DISPLAY_INFO,
+ HAL_EXTRADATA_VPX_COLORSPACE,
};
enum hal_property {
@@ -238,6 +241,7 @@ enum hal_property {
HAL_CONFIG_VENC_BLUR_RESOLUTION,
HAL_PARAM_VENC_SESSION_QP_RANGE_PACKED,
HAL_PARAM_VENC_H264_TRANSFORM_8x8,
+ HAL_PARAM_VENC_VIDEO_SIGNAL_INFO,
};
enum hal_domain {
@@ -991,6 +995,13 @@ struct hal_vpe_color_space_conversion {
u32 csc_limit[HAL_MAX_LIMIT_COEFFS];
};
+struct hal_video_signal_info {
+ u32 color_space;
+ u32 transfer_chars;
+ u32 matrix_coeffs;
+ bool full_range;
+};
+
enum vidc_resource_id {
VIDC_RESOURCE_NONE,
VIDC_RESOURCE_OCMEM,
@@ -1499,6 +1510,7 @@ struct hfi_device {
int (*session_clean)(void *sess);
int (*get_core_capabilities)(void *dev);
int (*suspend)(void *dev);
+ int (*flush_debug_queue)(void *dev);
unsigned long (*get_core_clock_rate)(void *dev, bool actual_rate);
enum hal_default_properties (*get_default_properties)(void *dev);
};
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index ff043e9a819b..23240746baf1 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -350,7 +350,7 @@ struct hfi_buffer_info {
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01B)
#define HFI_PROPERTY_PARAM_VENC_LTRMODE \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01C)
-#define HFI_PROPERTY_PARAM_VENC_VIDEO_FULL_RANGE \
+#define HFI_PROPERTY_PARAM_VENC_VIDEO_SIGNAL_INFO \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01D)
#define HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01E)
@@ -671,6 +671,16 @@ struct hfi_frame_size {
u32 height;
};
+struct hfi_video_signal_metadata {
+ u32 enable;
+ u32 video_format;
+ u32 video_full_range;
+ u32 color_description;
+ u32 color_primaries;
+ u32 transfer_characteristics;
+ u32 matrix_coeffs;
+};
+
struct hfi_h264_vui_timing_info {
u32 enable;
u32 fixed_frame_rate;
diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c
index 6310acab60e7..d41ae950d1a1 100644
--- a/drivers/media/platform/vsp1/vsp1_sru.c
+++ b/drivers/media/platform/vsp1/vsp1_sru.c
@@ -154,6 +154,7 @@ static int sru_s_stream(struct v4l2_subdev *subdev, int enable)
mutex_lock(sru->ctrls.lock);
ctrl0 |= vsp1_sru_read(sru, VI6_SRU_CTRL0)
& (VI6_SRU_CTRL0_PARAM0_MASK | VI6_SRU_CTRL0_PARAM1_MASK);
+ vsp1_sru_write(sru, VI6_SRU_CTRL0, ctrl0);
mutex_unlock(sru->ctrls.lock);
vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c
index 0934024fb89d..d91ded795c93 100644
--- a/drivers/media/usb/au0828/au0828-core.c
+++ b/drivers/media/usb/au0828/au0828-core.c
@@ -159,7 +159,7 @@ static void au0828_usb_disconnect(struct usb_interface *interface)
Set the status so poll routines can check and avoid
access after disconnect.
*/
- dev->dev_state = DEV_DISCONNECTED;
+ set_bit(DEV_DISCONNECTED, &dev->dev_state);
au0828_rc_unregister(dev);
/* Digital TV */
diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c
index b0f067971979..3d6687f0407d 100644
--- a/drivers/media/usb/au0828/au0828-input.c
+++ b/drivers/media/usb/au0828/au0828-input.c
@@ -130,7 +130,7 @@ static int au0828_get_key_au8522(struct au0828_rc *ir)
bool first = true;
/* do nothing if device is disconnected */
- if (ir->dev->dev_state == DEV_DISCONNECTED)
+ if (test_bit(DEV_DISCONNECTED, &ir->dev->dev_state))
return 0;
/* Check IR int */
@@ -260,7 +260,7 @@ static void au0828_rc_stop(struct rc_dev *rc)
cancel_delayed_work_sync(&ir->work);
/* do nothing if device is disconnected */
- if (ir->dev->dev_state != DEV_DISCONNECTED) {
+ if (!test_bit(DEV_DISCONNECTED, &ir->dev->dev_state)) {
/* Disable IR */
au8522_rc_clear(ir, 0xe0, 1 << 4);
}
diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c
index 45c622e234f7..7b2fe1b56039 100644
--- a/drivers/media/usb/au0828/au0828-video.c
+++ b/drivers/media/usb/au0828/au0828-video.c
@@ -104,14 +104,13 @@ static inline void print_err_status(struct au0828_dev *dev,
static int check_dev(struct au0828_dev *dev)
{
- if (dev->dev_state & DEV_DISCONNECTED) {
+ if (test_bit(DEV_DISCONNECTED, &dev->dev_state)) {
pr_info("v4l2 ioctl: device not present\n");
return -ENODEV;
}
- if (dev->dev_state & DEV_MISCONFIGURED) {
- pr_info("v4l2 ioctl: device is misconfigured; "
- "close and open it again\n");
+ if (test_bit(DEV_MISCONFIGURED, &dev->dev_state)) {
+ pr_info("v4l2 ioctl: device is misconfigured; close and open it again\n");
return -EIO;
}
return 0;
@@ -519,8 +518,8 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb)
if (!dev)
return 0;
- if ((dev->dev_state & DEV_DISCONNECTED) ||
- (dev->dev_state & DEV_MISCONFIGURED))
+ if (test_bit(DEV_DISCONNECTED, &dev->dev_state) ||
+ test_bit(DEV_MISCONFIGURED, &dev->dev_state))
return 0;
if (urb->status < 0) {
@@ -766,10 +765,10 @@ static int au0828_stream_interrupt(struct au0828_dev *dev)
int ret = 0;
dev->stream_state = STREAM_INTERRUPT;
- if (dev->dev_state == DEV_DISCONNECTED)
+ if (test_bit(DEV_DISCONNECTED, &dev->dev_state))
return -ENODEV;
else if (ret) {
- dev->dev_state = DEV_MISCONFIGURED;
+ set_bit(DEV_MISCONFIGURED, &dev->dev_state);
dprintk(1, "%s device is misconfigured!\n", __func__);
return ret;
}
@@ -958,7 +957,7 @@ static int au0828_v4l2_open(struct file *filp)
int ret;
dprintk(1,
- "%s called std_set %d dev_state %d stream users %d users %d\n",
+ "%s called std_set %d dev_state %ld stream users %d users %d\n",
__func__, dev->std_set_in_tuner_core, dev->dev_state,
dev->streaming_users, dev->users);
@@ -977,7 +976,7 @@ static int au0828_v4l2_open(struct file *filp)
au0828_analog_stream_enable(dev);
au0828_analog_stream_reset(dev);
dev->stream_state = STREAM_OFF;
- dev->dev_state |= DEV_INITIALIZED;
+ set_bit(DEV_INITIALIZED, &dev->dev_state);
}
dev->users++;
mutex_unlock(&dev->lock);
@@ -991,7 +990,7 @@ static int au0828_v4l2_close(struct file *filp)
struct video_device *vdev = video_devdata(filp);
dprintk(1,
- "%s called std_set %d dev_state %d stream users %d users %d\n",
+ "%s called std_set %d dev_state %ld stream users %d users %d\n",
__func__, dev->std_set_in_tuner_core, dev->dev_state,
dev->streaming_users, dev->users);
@@ -1007,7 +1006,7 @@ static int au0828_v4l2_close(struct file *filp)
del_timer_sync(&dev->vbi_timeout);
}
- if (dev->dev_state == DEV_DISCONNECTED)
+ if (test_bit(DEV_DISCONNECTED, &dev->dev_state))
goto end;
if (dev->users == 1) {
@@ -1036,7 +1035,7 @@ static void au0828_init_tuner(struct au0828_dev *dev)
.type = V4L2_TUNER_ANALOG_TV,
};
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
if (dev->std_set_in_tuner_core)
@@ -1108,7 +1107,7 @@ static int vidioc_querycap(struct file *file, void *priv,
struct video_device *vdev = video_devdata(file);
struct au0828_dev *dev = video_drvdata(file);
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
strlcpy(cap->driver, "au0828", sizeof(cap->driver));
@@ -1151,7 +1150,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
{
struct au0828_dev *dev = video_drvdata(file);
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
f->fmt.pix.width = dev->width;
@@ -1170,7 +1169,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
{
struct au0828_dev *dev = video_drvdata(file);
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
return au0828_set_format(dev, VIDIOC_TRY_FMT, f);
@@ -1182,7 +1181,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct au0828_dev *dev = video_drvdata(file);
int rc;
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
rc = check_dev(dev);
@@ -1204,7 +1203,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
{
struct au0828_dev *dev = video_drvdata(file);
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
if (norm == dev->std)
@@ -1236,7 +1235,7 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
{
struct au0828_dev *dev = video_drvdata(file);
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
*norm = dev->std;
@@ -1259,7 +1258,7 @@ static int vidioc_enum_input(struct file *file, void *priv,
[AU0828_VMUX_DEBUG] = "tv debug"
};
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
tmp = input->index;
@@ -1289,7 +1288,7 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
{
struct au0828_dev *dev = video_drvdata(file);
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
*i = dev->ctrl_input;
@@ -1300,7 +1299,7 @@ static void au0828_s_input(struct au0828_dev *dev, int index)
{
int i;
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
switch (AUVI_INPUT(index).type) {
@@ -1385,7 +1384,7 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
{
struct au0828_dev *dev = video_drvdata(file);
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
a->index = dev->ctrl_ainput;
@@ -1405,7 +1404,7 @@ static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio
if (a->index != dev->ctrl_ainput)
return -EINVAL;
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
return 0;
}
@@ -1417,7 +1416,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
if (t->index != 0)
return -EINVAL;
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
strcpy(t->name, "Auvitek tuner");
@@ -1437,7 +1436,7 @@ static int vidioc_s_tuner(struct file *file, void *priv,
if (t->index != 0)
return -EINVAL;
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
au0828_init_tuner(dev);
@@ -1459,7 +1458,7 @@ static int vidioc_g_frequency(struct file *file, void *priv,
if (freq->tuner != 0)
return -EINVAL;
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
freq->frequency = dev->ctrl_freq;
return 0;
@@ -1474,7 +1473,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
if (freq->tuner != 0)
return -EINVAL;
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
au0828_init_tuner(dev);
@@ -1500,7 +1499,7 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
{
struct au0828_dev *dev = video_drvdata(file);
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
format->fmt.vbi.samples_per_line = dev->vbi_width;
@@ -1526,7 +1525,7 @@ static int vidioc_cropcap(struct file *file, void *priv,
if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
cc->bounds.left = 0;
@@ -1548,7 +1547,7 @@ static int vidioc_g_register(struct file *file, void *priv,
{
struct au0828_dev *dev = video_drvdata(file);
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
reg->val = au0828_read(dev, reg->reg);
@@ -1561,7 +1560,7 @@ static int vidioc_s_register(struct file *file, void *priv,
{
struct au0828_dev *dev = video_drvdata(file);
- dprintk(1, "%s called std_set %d dev_state %d\n", __func__,
+ dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
return au0828_writereg(dev, reg->reg, reg->val);
diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h
index 60b59391ea2a..d1b6405a05a4 100644
--- a/drivers/media/usb/au0828/au0828.h
+++ b/drivers/media/usb/au0828/au0828.h
@@ -21,6 +21,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/bitops.h>
#include <linux/usb.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
@@ -122,9 +123,9 @@ enum au0828_stream_state {
/* device state */
enum au0828_dev_state {
- DEV_INITIALIZED = 0x01,
- DEV_DISCONNECTED = 0x02,
- DEV_MISCONFIGURED = 0x04
+ DEV_INITIALIZED = 0,
+ DEV_DISCONNECTED = 1,
+ DEV_MISCONFIGURED = 2
};
struct au0828_dev;
@@ -248,7 +249,7 @@ struct au0828_dev {
int input_type;
int std_set_in_tuner_core;
unsigned int ctrl_input;
- enum au0828_dev_state dev_state;
+ long unsigned int dev_state; /* defined at enum au0828_dev_state */;
enum au0828_stream_state stream_state;
wait_queue_head_t open;
diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c
index b79c36fd8cd2..58f23bcfe94e 100644
--- a/drivers/media/usb/pwc/pwc-if.c
+++ b/drivers/media/usb/pwc/pwc-if.c
@@ -91,6 +91,7 @@ static const struct usb_device_id pwc_device_table [] = {
{ USB_DEVICE(0x0471, 0x0312) },
{ USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */
{ USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC PC Camera */
+ { USB_DEVICE(0x0471, 0x032C) }, /* Philips SPC 880NC PC Camera */
{ USB_DEVICE(0x069A, 0x0001) }, /* Askey */
{ USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */
{ USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */
@@ -811,6 +812,11 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
name = "Philips SPC 900NC webcam";
type_id = 740;
break;
+ case 0x032C:
+ PWC_INFO("Philips SPC 880NC USB webcam detected.\n");
+ name = "Philips SPC 880NC webcam";
+ type_id = 740;
+ break;
default:
return -ENODEV;
break;
diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c
index b693206f66dd..d1dc1a198e3e 100644
--- a/drivers/media/usb/usbvision/usbvision-video.c
+++ b/drivers/media/usb/usbvision/usbvision-video.c
@@ -1463,9 +1463,23 @@ static int usbvision_probe(struct usb_interface *intf,
if (usbvision_device_data[model].interface >= 0)
interface = &dev->actconfig->interface[usbvision_device_data[model].interface]->altsetting[0];
- else
+ else if (ifnum < dev->actconfig->desc.bNumInterfaces)
interface = &dev->actconfig->interface[ifnum]->altsetting[0];
+ else {
+ dev_err(&intf->dev, "interface %d is invalid, max is %d\n",
+ ifnum, dev->actconfig->desc.bNumInterfaces - 1);
+ ret = -ENODEV;
+ goto err_usb;
+ }
+
+ if (interface->desc.bNumEndpoints < 2) {
+ dev_err(&intf->dev, "interface %d has %d endpoints, but must"
+ " have minimum 2\n", ifnum, interface->desc.bNumEndpoints);
+ ret = -ENODEV;
+ goto err_usb;
+ }
endpoint = &interface->endpoint[1].desc;
+
if (!usb_endpoint_xfer_isoc(endpoint)) {
dev_err(&intf->dev, "%s: interface %d. has non-ISO endpoint!\n",
__func__, ifnum);
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index fc9e4395c21d..2da7fd7deacd 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -425,7 +425,8 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
get_user(kp->index, &up->index) ||
get_user(kp->type, &up->type) ||
get_user(kp->flags, &up->flags) ||
- get_user(kp->memory, &up->memory))
+ get_user(kp->memory, &up->memory) ||
+ get_user(kp->length, &up->length))
return -EFAULT;
if (V4L2_TYPE_IS_OUTPUT(kp->type))
@@ -437,9 +438,6 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
return -EFAULT;
if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
- if (get_user(kp->length, &up->length))
- return -EFAULT;
-
num_planes = kp->length;
if (num_planes == 0) {
kp->m.planes = NULL;
@@ -472,16 +470,14 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
} else {
switch (kp->memory) {
case V4L2_MEMORY_MMAP:
- if (get_user(kp->length, &up->length) ||
- get_user(kp->m.offset, &up->m.offset))
+ if (get_user(kp->m.offset, &up->m.offset))
return -EFAULT;
break;
case V4L2_MEMORY_USERPTR:
{
compat_long_t tmp;
- if (get_user(kp->length, &up->length) ||
- get_user(tmp, &up->m.userptr))
+ if (get_user(tmp, &up->m.userptr))
return -EFAULT;
kp->m.userptr = (unsigned long)compat_ptr(tmp);
@@ -523,7 +519,8 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) ||
put_user(kp->sequence, &up->sequence) ||
put_user(kp->reserved2, &up->reserved2) ||
- put_user(kp->reserved, &up->reserved))
+ put_user(kp->reserved, &up->reserved) ||
+ put_user(kp->length, &up->length))
return -EFAULT;
if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
@@ -546,13 +543,11 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
} else {
switch (kp->memory) {
case V4L2_MEMORY_MMAP:
- if (put_user(kp->length, &up->length) ||
- put_user(kp->m.offset, &up->m.offset))
+ if (put_user(kp->m.offset, &up->m.offset))
return -EFAULT;
break;
case V4L2_MEMORY_USERPTR:
- if (put_user(kp->length, &up->length) ||
- put_user(kp->m.userptr, &up->m.userptr))
+ if (put_user(kp->m.userptr, &up->m.userptr))
return -EFAULT;
break;
case V4L2_MEMORY_OVERLAY:
diff --git a/drivers/mfd/wcd934x-regmap.c b/drivers/mfd/wcd934x-regmap.c
index 7f16f1f3f417..398f0086537a 100644
--- a/drivers/mfd/wcd934x-regmap.c
+++ b/drivers/mfd/wcd934x-regmap.c
@@ -1815,6 +1815,8 @@ static bool wcd934x_is_readable_register(struct device *dev, unsigned int reg)
pg_num = reg >> 0x8;
if (pg_num == 0x80)
pg_num = WCD934X_PAGE_0X80;
+ else if (pg_num == 0x50)
+ pg_num = WCD934X_PAGE_0x50;
else if (pg_num > 0xF)
return false;
@@ -1835,6 +1837,8 @@ static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg)
pg_num = reg >> 0x8;
if (pg_num == 0x80)
pg_num = WCD934X_PAGE_0X80;
+ else if (pg_num == 0x50)
+ pg_num = WCD934X_PAGE_0x50;
else if (pg_num > 0xF)
return false;
@@ -1844,6 +1848,15 @@ static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg)
if (reg_tbl && reg_tbl[reg_offset] == WCD934X_READ)
return true;
+ /*
+ * Need to mark volatile for registers that are writable but
+ * only few bits are read-only
+ */
+ switch (reg) {
+ case WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL:
+ return true;
+ }
+
return false;
}
diff --git a/drivers/mfd/wcd934x-tables.c b/drivers/mfd/wcd934x-tables.c
index ab5d18cd8493..db963d08b66e 100644
--- a/drivers/mfd/wcd934x-tables.c
+++ b/drivers/mfd/wcd934x-tables.c
@@ -1983,6 +1983,40 @@ const u8 wcd934x_page15_reg_access[WCD934X_PAGE_SIZE] = {
WCD934X_READ_WRITE,
};
+const u8 wcd934x_page_0x50_reg_access[WCD934X_PAGE_SIZE] = {
+ [WCD934X_REG(WCD934X_PAGE80_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_0)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_1)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_2)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_3)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_0)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_1)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_2)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_3)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_0)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_1)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_2)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_3)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_0)] = WCD934X_READ,
+ [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_1)] = WCD934X_READ,
+ [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_2)] = WCD934X_READ,
+ [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_3)] = WCD934X_READ,
+ [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_CFG)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_STATUS)] = WCD934X_READ,
+ [WCD934X_REG(WCD934X_CODEC_CPR_NOM_CX_VDD)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_SVS_CX_VDD)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_CX_VDD)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_NOM_MX_VDD)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_SVS_MX_VDD)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MX_VDD)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_MAX_SVS2_STEP)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_CTL)] = WCD934X_READ_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_STATUS)] = WCD934X_READ,
+ [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_START)] = WCD934X_WRITE,
+ [WCD934X_REG(WCD934X_CODEC_CPR_CPR_STATUS)] = WCD934X_READ_WRITE,
+};
+
const u8 wcd934x_page_0x80_reg_access[WCD934X_PAGE_SIZE] = {
[WCD934X_REG(WCD934X_PAGE80_PAGE_REGISTER)] = WCD934X_READ_WRITE,
[WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_0)] = WCD934X_READ_WRITE,
@@ -2116,5 +2150,6 @@ const u8 * const wcd934x_reg[WCD934X_NUM_PAGES] = {
[WCD934X_PAGE_13] = wcd934x_page13_reg_access,
[WCD934X_PAGE_14] = wcd934x_page14_reg_access,
[WCD934X_PAGE_15] = wcd934x_page15_reg_access,
+ [WCD934X_PAGE_0x50] = wcd934x_page_0x50_reg_access,
[WCD934X_PAGE_0X80] = wcd934x_page_0x80_reg_access,
};
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index 31ac6624b8b4..981d372277ee 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -1339,7 +1339,6 @@ static int wcd9xxx_slim_probe(struct slim_device *slim)
if (ret) {
dev_err(&slim->dev, "%s: failed to get slimbus %s logical address: %d\n",
__func__, wcd9xxx->slim->name, ret);
- ret = -EPROBE_DEFER;
goto err_reset;
}
wcd9xxx->read_dev = wcd9xxx_slim_read_device;
@@ -1364,7 +1363,6 @@ static int wcd9xxx_slim_probe(struct slim_device *slim)
if (ret) {
dev_err(&slim->dev, "%s: failed to get slimbus %s logical address: %d\n",
__func__, wcd9xxx->slim->name, ret);
- ret = -EPROBE_DEFER;
goto err_slim_add;
}
wcd9xxx_inf_la = wcd9xxx->slim_slave->laddr;
diff --git a/drivers/mfd/wcd9xxx-utils.c b/drivers/mfd/wcd9xxx-utils.c
index 22d61d96a11d..38286831a02c 100644
--- a/drivers/mfd/wcd9xxx-utils.c
+++ b/drivers/mfd/wcd9xxx-utils.c
@@ -298,6 +298,7 @@ struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev)
struct wcd9xxx_pdata *pdata;
u32 dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED;
u32 mad_dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED;
+ u32 ecpp_dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED;
u32 dmic_clk_drive = WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED;
u32 prop_val;
@@ -358,6 +359,15 @@ struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev)
pdata->mclk_rate,
"mad_dmic_rate");
+ if (!(wcd9xxx_read_of_property_u32(dev, "qcom,cdc-ecpp-dmic-rate",
+ &prop_val)))
+ ecpp_dmic_sample_rate = prop_val;
+
+ pdata->ecpp_dmic_sample_rate = wcd9xxx_validate_dmic_sample_rate(dev,
+ ecpp_dmic_sample_rate,
+ pdata->mclk_rate,
+ "ecpp_dmic_rate");
+
if (!(of_property_read_u32(dev->of_node,
"qcom,cdc-dmic-clk-drv-strength",
&prop_val)))
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 055b19432e7c..8a08ca61062a 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -412,10 +412,6 @@ config TI_DAC7512
This driver can also be built as a module. If so, the module
will be called ti_dac7512.
-config UID_STAT
- bool "UID based statistics tracking exported to /proc/uid_stat"
- default n
-
config VMWARE_BALLOON
tristate "VMware Balloon Driver"
depends on VMWARE_VMCI && X86 && HYPERVISOR_GUEST
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index fa36baace850..6acb70964fb8 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -35,7 +35,6 @@ obj-$(CONFIG_ISL29020) += isl29020.o
obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
obj-$(CONFIG_DS1682) += ds1682.o
obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o
-obj-$(CONFIG_UID_STAT) += uid_stat.o
obj-$(CONFIG_C2PORT) += c2port/
obj-$(CONFIG_HMC6352) += hmc6352.o
obj-y += eeprom/
diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c
index 7d1b9e9121a9..0c6f1de2465b 100644
--- a/drivers/misc/hdcp.c
+++ b/drivers/misc/hdcp.c
@@ -456,6 +456,7 @@ struct hdcp_lib_handle {
atomic_t hdcp_off;
uint32_t session_id;
bool legacy_app;
+ enum hdcp_device_type device_type;
struct task_struct *thread;
struct completion topo_wait;
@@ -901,7 +902,7 @@ static int hdcp_lib_session_init(struct hdcp_lib_handle *handle)
req_buf =
(struct hdcp_lib_session_init_req *)handle->qseecom_handle->sbuf;
req_buf->commandid = HDCP_SESSION_INIT;
- req_buf->deviceid = HDCP_TXMTR_HDMI;
+ req_buf->deviceid = handle->device_type;
rsp_buf = (struct hdcp_lib_session_init_rsp *)
(handle->qseecom_handle->sbuf +
QSEECOM_ALIGN(sizeof(struct hdcp_lib_session_init_req)));
@@ -2060,6 +2061,7 @@ int hdcp_library_register(struct hdcp_register_data *data)
handle->tethered = data->tethered;
handle->hdcp_app_init = NULL;
handle->hdcp_txmtr_init = NULL;
+ handle->device_type = data->device_type;
pr_debug("tethered %d\n", handle->tethered);
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index 0b05aa938799..1a173d0af694 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -53,6 +53,11 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
bus = cl->dev;
mutex_lock(&bus->device_lock);
+ if (bus->dev_state != MEI_DEV_ENABLED) {
+ rets = -ENODEV;
+ goto out;
+ }
+
if (!mei_cl_is_connected(cl)) {
rets = -ENODEV;
goto out;
@@ -109,6 +114,10 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
bus = cl->dev;
mutex_lock(&bus->device_lock);
+ if (bus->dev_state != MEI_DEV_ENABLED) {
+ rets = -ENODEV;
+ goto out;
+ }
cb = mei_cl_read_cb(cl, NULL);
if (cb)
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 273728482227..779994a1c9dd 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -1371,7 +1371,7 @@ static int qseecom_set_client_mem_param(struct qseecom_dev_handle *data,
if ((req.ifd_data_fd <= 0) || (req.virt_sb_base == NULL) ||
(req.sb_len == 0)) {
- pr_err("Inavlid input(s)ion_fd(%d), sb_len(%d), vaddr(0x%p)\n",
+ pr_err("Inavlid input(s)ion_fd(%d), sb_len(%d), vaddr(0x%pK)\n",
req.ifd_data_fd, req.sb_len, req.virt_sb_base);
return -EFAULT;
}
@@ -2100,7 +2100,7 @@ static int qseecom_unload_app(struct qseecom_dev_handle *data,
if (!memcmp(data->client.app_name, "keymaste", strlen("keymaste"))) {
pr_debug("Do not unload keymaster app from tz\n");
- return 0;
+ goto unload_exit;
}
__qseecom_cleanup_app(data);
@@ -2152,7 +2152,7 @@ static int qseecom_unload_app(struct qseecom_dev_handle *data,
pr_err("scm_call to unload app (id = %d) failed\n",
req.app_id);
ret = -EFAULT;
- goto not_release_exit;
+ goto unload_exit;
} else {
pr_warn("App id %d now unloaded\n", req.app_id);
}
@@ -2160,7 +2160,7 @@ static int qseecom_unload_app(struct qseecom_dev_handle *data,
pr_err("app (%d) unload_failed!!\n",
data->client.app_id);
ret = -EFAULT;
- goto not_release_exit;
+ goto unload_exit;
}
if (resp.result == QSEOS_RESULT_SUCCESS)
pr_debug("App (%d) is unloaded!!\n",
@@ -2170,7 +2170,7 @@ static int qseecom_unload_app(struct qseecom_dev_handle *data,
if (ret) {
pr_err("process_incomplete_cmd fail err: %d\n",
ret);
- goto not_release_exit;
+ goto unload_exit;
}
}
}
@@ -2200,7 +2200,6 @@ static int qseecom_unload_app(struct qseecom_dev_handle *data,
unload_exit:
qseecom_unmap_ion_allocated_memory(data);
data->released = true;
-not_release_exit:
return ret;
}
@@ -2225,7 +2224,7 @@ int __qseecom_process_rpmb_svc_cmd(struct qseecom_dev_handle *data_ptr,
void *req_buf = NULL;
if ((req_ptr == NULL) || (send_svc_ireq_ptr == NULL)) {
- pr_err("Error with pointer: req_ptr = %p, send_svc_ptr = %p\n",
+ pr_err("Error with pointer: req_ptr = %pK, send_svc_ptr = %pK\n",
req_ptr, send_svc_ireq_ptr);
return -EINVAL;
}
@@ -2272,7 +2271,7 @@ int __qseecom_process_fsm_key_svc_cmd(struct qseecom_dev_handle *data_ptr,
uint32_t reqd_len_sb_in = 0;
if ((req_ptr == NULL) || (send_svc_ireq_ptr == NULL)) {
- pr_err("Error with pointer: req_ptr = %p, send_svc_ptr = %p\n",
+ pr_err("Error with pointer: req_ptr = %pK, send_svc_ptr = %pK\n",
req_ptr, send_svc_ireq_ptr);
return -EINVAL;
}
@@ -2877,7 +2876,7 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup,
(!cleanup) &&
((uint64_t)sg_dma_address(sg_ptr->sgl)
>= PHY_ADDR_4G - sg->length)) {
- pr_err("App %s sgl PA exceeds 4G: phy_addr=%pad, len=%x\n",
+ pr_err("App %s sgl PA exceeds 4G: phy_addr=%pKad, len=%x\n",
data->client.app_name,
&(sg_dma_address(sg_ptr->sgl)),
sg->length);
@@ -2934,7 +2933,7 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup,
(!cleanup) &&
((uint64_t)(sg_dma_address(sg))
>= PHY_ADDR_4G - sg->length)) {
- pr_err("App %s sgl PA exceeds 4G: phy_addr=%pad, len=%x\n",
+ pr_err("App %s sgl PA exceeds 4G: phy_addr=%pKad, len=%x\n",
data->client.app_name,
&(sg_dma_address(sg)),
sg->length);
@@ -4092,7 +4091,7 @@ int qseecom_send_command(struct qseecom_handle *handle, void *send_buf,
if (ret)
return ret;
- pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
+ pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%pK\n",
req.resp_len, req.resp_buf);
return ret;
}
@@ -4152,41 +4151,80 @@ static int qseecom_reentrancy_send_resp(struct qseecom_dev_handle *data)
return 0;
}
-static int __qseecom_send_modfd_resp(struct qseecom_dev_handle *data,
- void __user *argp, bool is_64bit_addr)
+static int __validate_send_modfd_resp_inputs(struct qseecom_dev_handle *data,
+ struct qseecom_send_modfd_listener_resp *resp,
+ struct qseecom_registered_listener_list *this_lstnr)
{
- struct qseecom_send_modfd_listener_resp resp;
int i;
- struct qseecom_registered_listener_list *this_lstnr = NULL;
- if (copy_from_user(&resp, argp, sizeof(resp))) {
- pr_err("copy_from_user failed");
+ if (!data || !resp || !this_lstnr) {
+ pr_err("listener handle or resp msg is null\n");
return -EINVAL;
}
- this_lstnr = __qseecom_find_svc(data->listener.id);
- if (this_lstnr == NULL)
+
+ if (resp->resp_buf_ptr == NULL) {
+ pr_err("resp buffer is null\n");
+ return -EINVAL;
+ }
+ /* validate resp buf length */
+ if ((resp->resp_len == 0) ||
+ (resp->resp_len > this_lstnr->sb_length)) {
+ pr_err("resp buf length %d not valid\n", resp->resp_len);
return -EINVAL;
+ }
- if (resp.resp_buf_ptr == NULL) {
- pr_err("Invalid resp_buf_ptr\n");
+ if ((uintptr_t)resp->resp_buf_ptr > (ULONG_MAX - resp->resp_len)) {
+ pr_err("Integer overflow in resp_len & resp_buf\n");
return -EINVAL;
}
+ if ((uintptr_t)this_lstnr->user_virt_sb_base >
+ (ULONG_MAX - this_lstnr->sb_length)) {
+ pr_err("Integer overflow in user_virt_sb_base & sb_length\n");
+ return -EINVAL;
+ }
+ /* validate resp buf */
+ if (((uintptr_t)resp->resp_buf_ptr <
+ (uintptr_t)this_lstnr->user_virt_sb_base) ||
+ ((uintptr_t)resp->resp_buf_ptr >=
+ ((uintptr_t)this_lstnr->user_virt_sb_base +
+ this_lstnr->sb_length)) ||
+ (((uintptr_t)resp->resp_buf_ptr + resp->resp_len) >
+ ((uintptr_t)this_lstnr->user_virt_sb_base +
+ this_lstnr->sb_length))) {
+ pr_err("resp buf is out of shared buffer region\n");
+ return -EINVAL;
+ }
+
/* validate offsets */
for (i = 0; i < MAX_ION_FD; i++) {
- if (resp.ifd_data[i].cmd_buf_offset >= resp.resp_len) {
+ if (resp->ifd_data[i].cmd_buf_offset >= resp->resp_len) {
pr_err("Invalid offset %d = 0x%x\n",
- i, resp.ifd_data[i].cmd_buf_offset);
+ i, resp->ifd_data[i].cmd_buf_offset);
return -EINVAL;
}
}
- if ((resp.resp_buf_ptr < this_lstnr->user_virt_sb_base) ||
- ((uintptr_t)resp.resp_buf_ptr >=
- ((uintptr_t)this_lstnr->user_virt_sb_base +
- this_lstnr->sb_length))) {
- pr_err("resp_buf_ptr address not within shared buffer\n");
+ return 0;
+}
+
+static int __qseecom_send_modfd_resp(struct qseecom_dev_handle *data,
+ void __user *argp, bool is_64bit_addr)
+{
+ struct qseecom_send_modfd_listener_resp resp;
+ struct qseecom_registered_listener_list *this_lstnr = NULL;
+
+ if (copy_from_user(&resp, argp, sizeof(resp))) {
+ pr_err("copy_from_user failed");
return -EINVAL;
}
+
+ this_lstnr = __qseecom_find_svc(data->listener.id);
+ if (this_lstnr == NULL)
+ return -EINVAL;
+
+ if (__validate_send_modfd_resp_inputs(data, &resp, this_lstnr))
+ return -EINVAL;
+
resp.resp_buf_ptr = this_lstnr->sb_virt +
(uintptr_t)(resp.resp_buf_ptr - this_lstnr->user_virt_sb_base);
@@ -6339,7 +6377,7 @@ long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg)
ret = -EINVAL;
break;
}
- pr_debug("SET_MEM_PARAM: qseecom addr = 0x%p\n", data);
+ pr_debug("SET_MEM_PARAM: qseecom addr = 0x%pK\n", data);
ret = qseecom_set_client_mem_param(data, argp);
if (ret)
pr_err("failed Qqseecom_set_mem_param request: %d\n",
@@ -6355,7 +6393,7 @@ long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg)
break;
}
data->type = QSEECOM_CLIENT_APP;
- pr_debug("LOAD_APP_REQ: qseecom_addr = 0x%p\n", data);
+ pr_debug("LOAD_APP_REQ: qseecom_addr = 0x%pK\n", data);
mutex_lock(&app_access_lock);
atomic_inc(&data->ioctl_count);
ret = qseecom_load_app(data, argp);
@@ -6373,7 +6411,7 @@ long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg)
ret = -EINVAL;
break;
}
- pr_debug("UNLOAD_APP: qseecom_addr = 0x%p\n", data);
+ pr_debug("UNLOAD_APP: qseecom_addr = 0x%pK\n", data);
mutex_lock(&app_access_lock);
atomic_inc(&data->ioctl_count);
ret = qseecom_unload_app(data, false);
@@ -6504,7 +6542,7 @@ long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg)
data->type = QSEECOM_CLIENT_APP;
mutex_lock(&app_access_lock);
atomic_inc(&data->ioctl_count);
- pr_debug("APP_LOAD_QUERY: qseecom_addr = 0x%p\n", data);
+ pr_debug("APP_LOAD_QUERY: qseecom_addr = 0x%pK\n", data);
ret = qseecom_query_app_loaded(data, argp);
atomic_dec(&data->ioctl_count);
mutex_unlock(&app_access_lock);
@@ -6823,7 +6861,7 @@ static int qseecom_release(struct inode *inode, struct file *file)
int ret = 0;
if (data->released == false) {
- pr_debug("data: released=false, type=%d, mode=%d, data=0x%p\n",
+ pr_debug("data: released=false, type=%d, mode=%d, data=0x%pK\n",
data->type, data->mode, data);
switch (data->type) {
case QSEECOM_LISTENER_SERVICE:
diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c
deleted file mode 100644
index 2141124a6c12..000000000000
--- a/drivers/misc/uid_stat.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/* drivers/misc/uid_stat.c
- *
- * Copyright (C) 2008 - 2009 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <asm/atomic.h>
-
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/proc_fs.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/stat.h>
-#include <linux/uid_stat.h>
-#include <net/activity_stats.h>
-
-static DEFINE_SPINLOCK(uid_lock);
-static LIST_HEAD(uid_list);
-static struct proc_dir_entry *parent;
-
-struct uid_stat {
- struct list_head link;
- uid_t uid;
- atomic_t tcp_rcv;
- atomic_t tcp_snd;
-};
-
-static struct uid_stat *find_uid_stat(uid_t uid) {
- unsigned long flags;
- struct uid_stat *entry;
-
- spin_lock_irqsave(&uid_lock, flags);
- list_for_each_entry(entry, &uid_list, link) {
- if (entry->uid == uid) {
- spin_unlock_irqrestore(&uid_lock, flags);
- return entry;
- }
- }
- spin_unlock_irqrestore(&uid_lock, flags);
- return NULL;
-}
-
-static int tcp_snd_read_proc(char *page, char **start, off_t off,
- int count, int *eof, void *data)
-{
- int len;
- unsigned int bytes;
- char *p = page;
- struct uid_stat *uid_entry = (struct uid_stat *) data;
- if (!data)
- return 0;
-
- bytes = (unsigned int) (atomic_read(&uid_entry->tcp_snd) + INT_MIN);
- p += sprintf(p, "%u\n", bytes);
- len = (p - page) - off;
- *eof = (len <= count) ? 1 : 0;
- *start = page + off;
- return len;
-}
-
-static int tcp_rcv_read_proc(char *page, char **start, off_t off,
- int count, int *eof, void *data)
-{
- int len;
- unsigned int bytes;
- char *p = page;
- struct uid_stat *uid_entry = (struct uid_stat *) data;
- if (!data)
- return 0;
-
- bytes = (unsigned int) (atomic_read(&uid_entry->tcp_rcv) + INT_MIN);
- p += sprintf(p, "%u\n", bytes);
- len = (p - page) - off;
- *eof = (len <= count) ? 1 : 0;
- *start = page + off;
- return len;
-}
-
-/* Create a new entry for tracking the specified uid. */
-static struct uid_stat *create_stat(uid_t uid) {
- unsigned long flags;
- char uid_s[32];
- struct uid_stat *new_uid;
- struct proc_dir_entry *entry;
-
- /* Create the uid stat struct and append it to the list. */
- if ((new_uid = kmalloc(sizeof(struct uid_stat), GFP_KERNEL)) == NULL)
- return NULL;
-
- new_uid->uid = uid;
- /* Counters start at INT_MIN, so we can track 4GB of network traffic. */
- atomic_set(&new_uid->tcp_rcv, INT_MIN);
- atomic_set(&new_uid->tcp_snd, INT_MIN);
-
- spin_lock_irqsave(&uid_lock, flags);
- list_add_tail(&new_uid->link, &uid_list);
- spin_unlock_irqrestore(&uid_lock, flags);
-
- sprintf(uid_s, "%d", uid);
- entry = proc_mkdir(uid_s, parent);
-
- /* Keep reference to uid_stat so we know what uid to read stats from. */
- create_proc_read_entry("tcp_snd", S_IRUGO, entry , tcp_snd_read_proc,
- (void *) new_uid);
-
- create_proc_read_entry("tcp_rcv", S_IRUGO, entry, tcp_rcv_read_proc,
- (void *) new_uid);
-
- return new_uid;
-}
-
-int uid_stat_tcp_snd(uid_t uid, int size) {
- struct uid_stat *entry;
- activity_stats_update();
- if ((entry = find_uid_stat(uid)) == NULL &&
- ((entry = create_stat(uid)) == NULL)) {
- return -1;
- }
- atomic_add(size, &entry->tcp_snd);
- return 0;
-}
-
-int uid_stat_tcp_rcv(uid_t uid, int size) {
- struct uid_stat *entry;
- activity_stats_update();
- if ((entry = find_uid_stat(uid)) == NULL &&
- ((entry = create_stat(uid)) == NULL)) {
- return -1;
- }
- atomic_add(size, &entry->tcp_rcv);
- return 0;
-}
-
-static int __init uid_stat_init(void)
-{
- parent = proc_mkdir("uid_stat", NULL);
- if (!parent) {
- pr_err("uid_stat: failed to create proc entry\n");
- return -1;
- }
- return 0;
-}
-
-__initcall(uid_stat_init);
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index dafdbd99ce71..b116122c5767 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -935,6 +935,14 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
struct mmc_card *card;
int err = 0, ioc_err = 0;
+ /*
+ * The caller must have CAP_SYS_RAWIO, and must be calling this on the
+ * whole block device, not on a partition. This prevents overspray
+ * between sibling partitions.
+ */
+ if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
+ return -EPERM;
+
idata = mmc_blk_ioctl_copy_from_user(ic_ptr);
if (IS_ERR_OR_NULL(idata))
return PTR_ERR(idata);
@@ -993,6 +1001,14 @@ static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev,
int i, err = 0, ioc_err = 0;
__u64 num_of_cmds;
+ /*
+ * The caller must have CAP_SYS_RAWIO, and must be calling this on the
+ * whole block device, not on a partition. This prevents overspray
+ * between sibling partitions.
+ */
+ if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
+ return -EPERM;
+
if (copy_from_user(&num_of_cmds, &user->num_of_cmds,
sizeof(num_of_cmds)))
return -EFAULT;
@@ -1048,14 +1064,6 @@ cmd_err:
static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
- /*
- * The caller must have CAP_SYS_RAWIO, and must be calling this on the
- * whole block device, not on a partition. This prevents overspray
- * between sibling partitions.
- */
- if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
- return -EPERM;
-
switch (cmd) {
case MMC_IOC_CMD:
return mmc_blk_ioctl_cmd(bdev,
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index 1c1b45ef3faf..aad3243a48fc 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -1436,6 +1436,12 @@ static int mmc_spi_probe(struct spi_device *spi)
host->pdata->cd_debounce);
if (status != 0)
goto fail_add_host;
+
+ /* The platform has a CD GPIO signal that may support
+ * interrupts, so let mmc_gpiod_request_cd_irq() decide
+ * if polling is needed or not.
+ */
+ mmc->caps &= ~MMC_CAP_NEEDS_POLL;
mmc_gpiod_request_cd_irq(mmc);
}
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 45ee07d3a761..610154836d79 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -390,6 +390,7 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
slot->cd_idx = 0;
slot->cd_override_level = true;
if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BXT_SD ||
+ slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BXTM_SD ||
slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_APL_SD)
slot->host->mmc_host_ops.get_cd = bxt_get_cd;
@@ -1173,6 +1174,30 @@ static const struct pci_device_id pci_ids[] = {
{
.vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_BXTM_EMMC,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_BXTM_SDIO,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sdio,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_BXTM_SD,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_APL_EMMC,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index d1a0b4db60db..89e7151684a1 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -28,6 +28,9 @@
#define PCI_DEVICE_ID_INTEL_BXT_SD 0x0aca
#define PCI_DEVICE_ID_INTEL_BXT_EMMC 0x0acc
#define PCI_DEVICE_ID_INTEL_BXT_SDIO 0x0ad0
+#define PCI_DEVICE_ID_INTEL_BXTM_SD 0x1aca
+#define PCI_DEVICE_ID_INTEL_BXTM_EMMC 0x1acc
+#define PCI_DEVICE_ID_INTEL_BXTM_SDIO 0x1ad0
#define PCI_DEVICE_ID_INTEL_APL_SD 0x5aca
#define PCI_DEVICE_ID_INTEL_APL_EMMC 0x5acc
#define PCI_DEVICE_ID_INTEL_APL_SDIO 0x5ad0
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 36b75f048e76..5f7eac922c54 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -754,9 +754,20 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
if (!data)
target_timeout = cmd->busy_timeout * 1000;
else {
- target_timeout = data->timeout_ns / 1000;
- if (host->clock)
- target_timeout += data->timeout_clks / host->clock;
+ target_timeout = DIV_ROUND_UP(data->timeout_ns, 1000);
+ if (host->clock && data->timeout_clks) {
+ unsigned long long val;
+
+ /*
+ * data->timeout_clks is in units of clock cycles.
+ * host->clock is in Hz. target_timeout is in us.
+ * Hence, us = 1000000 * cycles / Hz. Round up.
+ */
+ val = 1000000 * data->timeout_clks;
+ if (do_div(val, host->clock))
+ target_timeout++;
+ target_timeout += val;
+ }
}
/*
@@ -3921,14 +3932,14 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
host->timeout_clk *= 1000;
+ if (override_timeout_clk)
+ host->timeout_clk = override_timeout_clk;
+
mmc->max_busy_timeout = host->ops->get_max_timeout_count ?
host->ops->get_max_timeout_count(host) : 1 << 27;
mmc->max_busy_timeout /= host->timeout_clk;
}
- if (override_timeout_clk)
- host->timeout_clk = override_timeout_clk;
-
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index ad9ffea7d659..6234eab38ff3 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -397,38 +397,26 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
}
static struct dma_chan *
-sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
- struct sh_mmcif_plat_data *pdata,
- enum dma_transfer_direction direction)
+sh_mmcif_request_dma_pdata(struct sh_mmcif_host *host, uintptr_t slave_id)
{
- struct dma_slave_config cfg = { 0, };
- struct dma_chan *chan;
- void *slave_data = NULL;
- struct resource *res;
- struct device *dev = sh_mmcif_host_to_dev(host);
dma_cap_mask_t mask;
- int ret;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
+ if (slave_id <= 0)
+ return NULL;
- if (pdata)
- slave_data = direction == DMA_MEM_TO_DEV ?
- (void *)pdata->slave_id_tx :
- (void *)pdata->slave_id_rx;
-
- chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
- slave_data, dev,
- direction == DMA_MEM_TO_DEV ? "tx" : "rx");
-
- dev_dbg(dev, "%s: %s: got channel %p\n", __func__,
- direction == DMA_MEM_TO_DEV ? "TX" : "RX", chan);
+ return dma_request_channel(mask, shdma_chan_filter, (void *)slave_id);
+}
- if (!chan)
- return NULL;
+static int sh_mmcif_dma_slave_config(struct sh_mmcif_host *host,
+ struct dma_chan *chan,
+ enum dma_transfer_direction direction)
+{
+ struct resource *res;
+ struct dma_slave_config cfg = { 0, };
res = platform_get_resource(host->pd, IORESOURCE_MEM, 0);
-
cfg.direction = direction;
if (direction == DMA_DEV_TO_MEM) {
@@ -439,38 +427,42 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
}
- ret = dmaengine_slave_config(chan, &cfg);
- if (ret < 0) {
- dma_release_channel(chan);
- return NULL;
- }
-
- return chan;
+ return dmaengine_slave_config(chan, &cfg);
}
-static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
- struct sh_mmcif_plat_data *pdata)
+static void sh_mmcif_request_dma(struct sh_mmcif_host *host)
{
struct device *dev = sh_mmcif_host_to_dev(host);
host->dma_active = false;
- if (pdata) {
- if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0)
- return;
- } else if (!dev->of_node) {
- return;
+ /* We can only either use DMA for both Tx and Rx or not use it at all */
+ if (IS_ENABLED(CONFIG_SUPERH) && dev->platform_data) {
+ struct sh_mmcif_plat_data *pdata = dev->platform_data;
+
+ host->chan_tx = sh_mmcif_request_dma_pdata(host,
+ pdata->slave_id_tx);
+ host->chan_rx = sh_mmcif_request_dma_pdata(host,
+ pdata->slave_id_rx);
+ } else {
+ host->chan_tx = dma_request_slave_channel(dev, "tx");
+ host->chan_rx = dma_request_slave_channel(dev, "rx");
}
+ dev_dbg(dev, "%s: got channel TX %p RX %p\n", __func__, host->chan_tx,
+ host->chan_rx);
- /* We can only either use DMA for both Tx and Rx or not use it at all */
- host->chan_tx = sh_mmcif_request_dma_one(host, pdata, DMA_MEM_TO_DEV);
- if (!host->chan_tx)
- return;
+ if (!host->chan_tx || !host->chan_rx ||
+ sh_mmcif_dma_slave_config(host, host->chan_tx, DMA_MEM_TO_DEV) ||
+ sh_mmcif_dma_slave_config(host, host->chan_rx, DMA_DEV_TO_MEM))
+ goto error;
- host->chan_rx = sh_mmcif_request_dma_one(host, pdata, DMA_DEV_TO_MEM);
- if (!host->chan_rx) {
+ return;
+
+error:
+ if (host->chan_tx)
dma_release_channel(host->chan_tx);
- host->chan_tx = NULL;
- }
+ if (host->chan_rx)
+ dma_release_channel(host->chan_rx);
+ host->chan_tx = host->chan_rx = NULL;
}
static void sh_mmcif_release_dma(struct sh_mmcif_host *host)
@@ -1102,7 +1094,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->power_mode == MMC_POWER_UP) {
if (!host->card_present) {
/* See if we also get DMA */
- sh_mmcif_request_dma(host, dev->platform_data);
+ sh_mmcif_request_dma(host);
host->card_present = true;
}
sh_mmcif_set_power(host, ios);
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 43b3392ffee7..652d01832873 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -2599,6 +2599,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
*/
static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
+ struct onenand_chip *this = mtd->priv;
int ret;
ret = onenand_block_isbad(mtd, ofs);
@@ -2610,7 +2611,7 @@ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
}
onenand_get_device(mtd, FL_WRITING);
- ret = mtd_block_markbad(mtd, ofs);
+ ret = this->block_markbad(mtd, ofs);
onenand_release_device(mtd);
return ret;
}
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 28bbca0af238..b3d70a7a5262 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -3260,6 +3260,30 @@ static int bond_close(struct net_device *bond_dev)
return 0;
}
+/* fold stats, assuming all rtnl_link_stats64 fields are u64, but
+ * that some drivers can provide 32bit values only.
+ */
+static void bond_fold_stats(struct rtnl_link_stats64 *_res,
+ const struct rtnl_link_stats64 *_new,
+ const struct rtnl_link_stats64 *_old)
+{
+ const u64 *new = (const u64 *)_new;
+ const u64 *old = (const u64 *)_old;
+ u64 *res = (u64 *)_res;
+ int i;
+
+ for (i = 0; i < sizeof(*_res) / sizeof(u64); i++) {
+ u64 nv = new[i];
+ u64 ov = old[i];
+
+ /* detects if this particular field is 32bit only */
+ if (((nv | ov) >> 32) == 0)
+ res[i] += (u32)nv - (u32)ov;
+ else
+ res[i] += nv - ov;
+ }
+}
+
static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev,
struct rtnl_link_stats64 *stats)
{
@@ -3268,43 +3292,23 @@ static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev,
struct list_head *iter;
struct slave *slave;
+ spin_lock(&bond->stats_lock);
memcpy(stats, &bond->bond_stats, sizeof(*stats));
- bond_for_each_slave(bond, slave, iter) {
- const struct rtnl_link_stats64 *sstats =
+ rcu_read_lock();
+ bond_for_each_slave_rcu(bond, slave, iter) {
+ const struct rtnl_link_stats64 *new =
dev_get_stats(slave->dev, &temp);
- struct rtnl_link_stats64 *pstats = &slave->slave_stats;
-
- stats->rx_packets += sstats->rx_packets - pstats->rx_packets;
- stats->rx_bytes += sstats->rx_bytes - pstats->rx_bytes;
- stats->rx_errors += sstats->rx_errors - pstats->rx_errors;
- stats->rx_dropped += sstats->rx_dropped - pstats->rx_dropped;
-
- stats->tx_packets += sstats->tx_packets - pstats->tx_packets;;
- stats->tx_bytes += sstats->tx_bytes - pstats->tx_bytes;
- stats->tx_errors += sstats->tx_errors - pstats->tx_errors;
- stats->tx_dropped += sstats->tx_dropped - pstats->tx_dropped;
-
- stats->multicast += sstats->multicast - pstats->multicast;
- stats->collisions += sstats->collisions - pstats->collisions;
-
- stats->rx_length_errors += sstats->rx_length_errors - pstats->rx_length_errors;
- stats->rx_over_errors += sstats->rx_over_errors - pstats->rx_over_errors;
- stats->rx_crc_errors += sstats->rx_crc_errors - pstats->rx_crc_errors;
- stats->rx_frame_errors += sstats->rx_frame_errors - pstats->rx_frame_errors;
- stats->rx_fifo_errors += sstats->rx_fifo_errors - pstats->rx_fifo_errors;
- stats->rx_missed_errors += sstats->rx_missed_errors - pstats->rx_missed_errors;
-
- stats->tx_aborted_errors += sstats->tx_aborted_errors - pstats->tx_aborted_errors;
- stats->tx_carrier_errors += sstats->tx_carrier_errors - pstats->tx_carrier_errors;
- stats->tx_fifo_errors += sstats->tx_fifo_errors - pstats->tx_fifo_errors;
- stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors - pstats->tx_heartbeat_errors;
- stats->tx_window_errors += sstats->tx_window_errors - pstats->tx_window_errors;
+
+ bond_fold_stats(stats, new, &slave->slave_stats);
/* save off the slave stats for the next run */
- memcpy(pstats, sstats, sizeof(*sstats));
+ memcpy(&slave->slave_stats, new, sizeof(*new));
}
+ rcu_read_unlock();
+
memcpy(&bond->bond_stats, stats, sizeof(*stats));
+ spin_unlock(&bond->stats_lock);
return stats;
}
@@ -4118,6 +4122,7 @@ void bond_setup(struct net_device *bond_dev)
struct bonding *bond = netdev_priv(bond_dev);
spin_lock_init(&bond->mode_lock);
+ spin_lock_init(&bond->stats_lock);
bond->params = bonding_defaults;
/* Initialize pointers */
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index 17f017ab4dac..0fb3f8de88e9 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -1197,7 +1197,7 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev,
dev->stats.tx_bytes += tx_cb_ptr->skb->len;
dma_unmap_single(&dev->dev,
dma_unmap_addr(tx_cb_ptr, dma_addr),
- tx_cb_ptr->skb->len,
+ dma_unmap_len(tx_cb_ptr, dma_len),
DMA_TO_DEVICE);
bcmgenet_free_cb(tx_cb_ptr);
} else if (dma_unmap_addr(tx_cb_ptr, dma_addr)) {
@@ -1308,7 +1308,7 @@ static int bcmgenet_xmit_single(struct net_device *dev,
}
dma_unmap_addr_set(tx_cb_ptr, dma_addr, mapping);
- dma_unmap_len_set(tx_cb_ptr, dma_len, skb->len);
+ dma_unmap_len_set(tx_cb_ptr, dma_len, skb_len);
length_status = (skb_len << DMA_BUFLENGTH_SHIFT) | dma_desc_flags |
(priv->hw_params->qtag_mask << DMA_TX_QTAG_SHIFT) |
DMA_TX_APPEND_CRC;
diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c
index 060dd3922974..973dade2d07f 100644
--- a/drivers/net/ethernet/jme.c
+++ b/drivers/net/ethernet/jme.c
@@ -3312,13 +3312,14 @@ jme_resume(struct device *dev)
jme_reset_phy_processor(jme);
jme_phy_calibration(jme);
jme_phy_setEA(jme);
- jme_start_irq(jme);
netif_device_attach(netdev);
atomic_inc(&jme->link_changing);
jme_reset_link(jme);
+ jme_start_irq(jme);
+
return 0;
}
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index ed622fa29dfa..a4ac6fedac75 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -3404,7 +3404,7 @@ static int mvneta_probe(struct platform_device *pdev)
dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
dev->hw_features |= dev->features;
dev->vlan_features |= dev->features;
- dev->priv_flags |= IFF_UNICAST_FLT;
+ dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE;
dev->gso_max_segs = MVNETA_MAX_TSO_SEGS;
err = register_netdev(dev);
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index cad6c44df91c..d314d96dcb1c 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -3132,7 +3132,7 @@ static int verify_qp_parameters(struct mlx4_dev *dev,
case QP_TRANS_RTS2RTS:
case QP_TRANS_SQD2SQD:
case QP_TRANS_SQD2RTS:
- if (slave != mlx4_master_func_num(dev))
+ if (slave != mlx4_master_func_num(dev)) {
if (optpar & MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH) {
port = (qp_ctx->pri_path.sched_queue >> 6 & 1) + 1;
if (dev->caps.port_mask[port] != MLX4_PORT_TYPE_IB)
@@ -3151,6 +3151,7 @@ static int verify_qp_parameters(struct mlx4_dev *dev,
if (qp_ctx->alt_path.mgid_index >= num_gids)
return -EINVAL;
}
+ }
break;
default:
break;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
index 4365c8bccc6d..605f6410f867 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
@@ -61,6 +61,8 @@ struct mlxsw_sp {
#define MLXSW_SP_DEFAULT_LEARNING_INTERVAL 100
unsigned int interval; /* ms */
} fdb_notify;
+#define MLXSW_SP_MIN_AGEING_TIME 10
+#define MLXSW_SP_MAX_AGEING_TIME 1000000
#define MLXSW_SP_DEFAULT_AGEING_TIME 300
u32 ageing_time;
struct {
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index 7dbeafa65934..d4c4c2b5156c 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -232,8 +232,13 @@ static int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port,
unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000;
- if (switchdev_trans_ph_prepare(trans))
- return 0;
+ if (switchdev_trans_ph_prepare(trans)) {
+ if (ageing_time < MLXSW_SP_MIN_AGEING_TIME ||
+ ageing_time > MLXSW_SP_MAX_AGEING_TIME)
+ return -ERANGE;
+ else
+ return 0;
+ }
return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time);
}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index 46bbea8e023c..55007f1e6bbc 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -566,6 +566,7 @@ struct qlcnic_adapter_stats {
u64 tx_dma_map_error;
u64 spurious_intr;
u64 mac_filter_limit_overrun;
+ u64 mbx_spurious_intr;
};
/*
@@ -1099,7 +1100,7 @@ struct qlcnic_mailbox {
unsigned long status;
spinlock_t queue_lock; /* Mailbox queue lock */
spinlock_t aen_lock; /* Mailbox response/AEN lock */
- atomic_t rsp_status;
+ u32 rsp_status;
u32 num_cmds;
};
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
index 37a731be7d39..f9640d5ce6ba 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
@@ -491,7 +491,7 @@ irqreturn_t qlcnic_83xx_clear_legacy_intr(struct qlcnic_adapter *adapter)
static inline void qlcnic_83xx_notify_mbx_response(struct qlcnic_mailbox *mbx)
{
- atomic_set(&mbx->rsp_status, QLC_83XX_MBX_RESPONSE_ARRIVED);
+ mbx->rsp_status = QLC_83XX_MBX_RESPONSE_ARRIVED;
complete(&mbx->completion);
}
@@ -510,7 +510,7 @@ static void qlcnic_83xx_poll_process_aen(struct qlcnic_adapter *adapter)
if (event & QLCNIC_MBX_ASYNC_EVENT) {
__qlcnic_83xx_process_aen(adapter);
} else {
- if (atomic_read(&mbx->rsp_status) != rsp_status)
+ if (mbx->rsp_status != rsp_status)
qlcnic_83xx_notify_mbx_response(mbx);
}
out:
@@ -1023,7 +1023,7 @@ static void qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter)
if (event & QLCNIC_MBX_ASYNC_EVENT) {
__qlcnic_83xx_process_aen(adapter);
} else {
- if (atomic_read(&mbx->rsp_status) != rsp_status)
+ if (mbx->rsp_status != rsp_status)
qlcnic_83xx_notify_mbx_response(mbx);
}
}
@@ -2338,9 +2338,9 @@ static void qlcnic_83xx_handle_link_aen(struct qlcnic_adapter *adapter,
static irqreturn_t qlcnic_83xx_handle_aen(int irq, void *data)
{
+ u32 mask, resp, event, rsp_status = QLC_83XX_MBX_RESPONSE_ARRIVED;
struct qlcnic_adapter *adapter = data;
struct qlcnic_mailbox *mbx;
- u32 mask, resp, event;
unsigned long flags;
mbx = adapter->ahw->mailbox;
@@ -2350,10 +2350,14 @@ static irqreturn_t qlcnic_83xx_handle_aen(int irq, void *data)
goto out;
event = readl(QLCNIC_MBX_FW(adapter->ahw, 0));
- if (event & QLCNIC_MBX_ASYNC_EVENT)
+ if (event & QLCNIC_MBX_ASYNC_EVENT) {
__qlcnic_83xx_process_aen(adapter);
- else
- qlcnic_83xx_notify_mbx_response(mbx);
+ } else {
+ if (mbx->rsp_status != rsp_status)
+ qlcnic_83xx_notify_mbx_response(mbx);
+ else
+ adapter->stats.mbx_spurious_intr++;
+ }
out:
mask = QLCRDX(adapter->ahw, QLCNIC_DEF_INT_MASK);
@@ -4050,10 +4054,10 @@ static void qlcnic_83xx_mailbox_worker(struct work_struct *work)
struct qlcnic_adapter *adapter = mbx->adapter;
const struct qlcnic_mbx_ops *mbx_ops = mbx->ops;
struct device *dev = &adapter->pdev->dev;
- atomic_t *rsp_status = &mbx->rsp_status;
struct list_head *head = &mbx->cmd_q;
struct qlcnic_hardware_context *ahw;
struct qlcnic_cmd_args *cmd = NULL;
+ unsigned long flags;
ahw = adapter->ahw;
@@ -4063,7 +4067,9 @@ static void qlcnic_83xx_mailbox_worker(struct work_struct *work)
return;
}
- atomic_set(rsp_status, QLC_83XX_MBX_RESPONSE_WAIT);
+ spin_lock_irqsave(&mbx->aen_lock, flags);
+ mbx->rsp_status = QLC_83XX_MBX_RESPONSE_WAIT;
+ spin_unlock_irqrestore(&mbx->aen_lock, flags);
spin_lock(&mbx->queue_lock);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
index 494e8105adee..0a2318cad34d 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
@@ -59,7 +59,8 @@ static const struct qlcnic_stats qlcnic_gstrings_stats[] = {
QLC_OFF(stats.mac_filter_limit_overrun)},
{"spurious intr", QLC_SIZEOF(stats.spurious_intr),
QLC_OFF(stats.spurious_intr)},
-
+ {"mbx spurious intr", QLC_SIZEOF(stats.mbx_spurious_intr),
+ QLC_OFF(stats.mbx_spurious_intr)},
};
static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = {
diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
index 997976426799..b28e73ea2c25 100644
--- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c
+++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
@@ -1648,7 +1648,18 @@ static void ql_process_mac_rx_skb(struct ql_adapter *qdev,
return;
}
skb_reserve(new_skb, NET_IP_ALIGN);
+
+ pci_dma_sync_single_for_cpu(qdev->pdev,
+ dma_unmap_addr(sbq_desc, mapaddr),
+ dma_unmap_len(sbq_desc, maplen),
+ PCI_DMA_FROMDEVICE);
+
memcpy(skb_put(new_skb, length), skb->data, length);
+
+ pci_dma_sync_single_for_device(qdev->pdev,
+ dma_unmap_addr(sbq_desc, mapaddr),
+ dma_unmap_len(sbq_desc, maplen),
+ PCI_DMA_FROMDEVICE);
skb = new_skb;
/* Frame error, so drop the packet. */
diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c
index 689a4a5c8dcf..1ef03939d25f 100644
--- a/drivers/net/ethernet/qualcomm/qca_spi.c
+++ b/drivers/net/ethernet/qualcomm/qca_spi.c
@@ -811,7 +811,7 @@ qcaspi_netdev_setup(struct net_device *dev)
dev->netdev_ops = &qcaspi_netdev_ops;
qcaspi_set_ethtool_ops(dev);
dev->watchdog_timeo = QCASPI_TX_TIMEOUT;
- dev->flags = IFF_MULTICAST;
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
dev->tx_queue_len = 100;
qca = netdev_priv(dev);
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 6a8fc0f341ff..36fc9427418f 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -1185,11 +1185,8 @@ static void sh_eth_ring_format(struct net_device *ndev)
break;
sh_eth_set_receive_align(skb);
- /* RX descriptor */
- rxdesc = &mdp->rx_ring[i];
/* The size of the buffer is a multiple of 32 bytes. */
buf_len = ALIGN(mdp->rx_buf_sz, 32);
- rxdesc->len = cpu_to_edmac(mdp, buf_len << 16);
dma_addr = dma_map_single(&ndev->dev, skb->data, buf_len,
DMA_FROM_DEVICE);
if (dma_mapping_error(&ndev->dev, dma_addr)) {
@@ -1197,6 +1194,10 @@ static void sh_eth_ring_format(struct net_device *ndev)
break;
}
mdp->rx_skbuff[i] = skb;
+
+ /* RX descriptor */
+ rxdesc = &mdp->rx_ring[i];
+ rxdesc->len = cpu_to_edmac(mdp, buf_len << 16);
rxdesc->addr = cpu_to_edmac(mdp, dma_addr);
rxdesc->status = cpu_to_edmac(mdp, RD_RACT | RD_RFP);
@@ -1212,7 +1213,8 @@ static void sh_eth_ring_format(struct net_device *ndev)
mdp->dirty_rx = (u32) (i - mdp->num_rx_ring);
/* Mark the last entry as wrapping the ring. */
- rxdesc->status |= cpu_to_edmac(mdp, RD_RDLE);
+ if (rxdesc)
+ rxdesc->status |= cpu_to_edmac(mdp, RD_RDLE);
memset(mdp->tx_ring, 0, tx_ringsize);
diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c
index 52ec3d6e056a..2b34622a4bfe 100644
--- a/drivers/net/ethernet/rocker/rocker.c
+++ b/drivers/net/ethernet/rocker/rocker.c
@@ -239,6 +239,7 @@ struct rocker {
struct {
u64 id;
} hw;
+ unsigned long ageing_time;
spinlock_t cmd_ring_lock; /* for cmd ring accesses */
struct rocker_dma_ring_info cmd_ring;
struct rocker_dma_ring_info event_ring;
@@ -3704,7 +3705,7 @@ static void rocker_fdb_cleanup(unsigned long data)
struct rocker_port *rocker_port;
struct rocker_fdb_tbl_entry *entry;
struct hlist_node *tmp;
- unsigned long next_timer = jiffies + BR_MIN_AGEING_TIME;
+ unsigned long next_timer = jiffies + rocker->ageing_time;
unsigned long expires;
unsigned long lock_flags;
int flags = ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE |
@@ -4367,8 +4368,12 @@ static int rocker_port_bridge_ageing_time(struct rocker_port *rocker_port,
struct switchdev_trans *trans,
u32 ageing_time)
{
+ struct rocker *rocker = rocker_port->rocker;
+
if (!switchdev_trans_ph_prepare(trans)) {
rocker_port->ageing_time = clock_t_to_jiffies(ageing_time);
+ if (rocker_port->ageing_time < rocker->ageing_time)
+ rocker->ageing_time = rocker_port->ageing_time;
mod_timer(&rocker_port->rocker->fdb_cleanup_timer, jiffies);
}
@@ -5206,10 +5211,13 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_init_tbls;
}
+ rocker->ageing_time = BR_DEFAULT_AGEING_TIME;
setup_timer(&rocker->fdb_cleanup_timer, rocker_fdb_cleanup,
(unsigned long) rocker);
mod_timer(&rocker->fdb_cleanup_timer, jiffies);
+ rocker->ageing_time = BR_DEFAULT_AGEING_TIME;
+
err = rocker_probe_ports(rocker);
if (err) {
dev_err(&pdev->dev, "failed to probe ports\n");
diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c
index 696852eb23c3..7a3f990c1935 100644
--- a/drivers/net/irda/irtty-sir.c
+++ b/drivers/net/irda/irtty-sir.c
@@ -430,16 +430,6 @@ static int irtty_open(struct tty_struct *tty)
/* Module stuff handled via irda_ldisc.owner - Jean II */
- /* First make sure we're not already connected. */
- if (tty->disc_data != NULL) {
- priv = tty->disc_data;
- if (priv && priv->magic == IRTTY_MAGIC) {
- ret = -EEXIST;
- goto out;
- }
- tty->disc_data = NULL; /* ### */
- }
-
/* stop the underlying driver */
irtty_stop_receiver(tty, TRUE);
if (tty->ops->stop)
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 0fc521941c71..159a68782bec 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -760,6 +760,8 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
macvtap16_to_cpu(q, vnet_hdr.hdr_len) : GOODCOPY_LEN;
if (copylen > good_linear)
copylen = good_linear;
+ else if (copylen < ETH_HLEN)
+ copylen = ETH_HLEN;
linear = copylen;
i = *from;
iov_iter_advance(&i, copylen);
@@ -769,10 +771,11 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
if (!zerocopy) {
copylen = len;
- if (macvtap16_to_cpu(q, vnet_hdr.hdr_len) > good_linear)
+ linear = macvtap16_to_cpu(q, vnet_hdr.hdr_len);
+ if (linear > good_linear)
linear = good_linear;
- else
- linear = macvtap16_to_cpu(q, vnet_hdr.hdr_len);
+ else if (linear < ETH_HLEN)
+ linear = ETH_HLEN;
}
skb = macvtap_alloc_skb(&q->sk, MACVTAP_RESERVE, copylen,
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 9a863c6a6a33..174e06ec7c2f 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -567,7 +567,7 @@ static int get_filter(void __user *arg, struct sock_filter **p)
static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct ppp_file *pf = file->private_data;
+ struct ppp_file *pf;
struct ppp *ppp;
int err = -EFAULT, val, val2, i;
struct ppp_idle idle;
@@ -577,9 +577,14 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
void __user *argp = (void __user *)arg;
int __user *p = argp;
- if (!pf)
- return ppp_unattached_ioctl(current->nsproxy->net_ns,
- pf, file, cmd, arg);
+ mutex_lock(&ppp_mutex);
+
+ pf = file->private_data;
+ if (!pf) {
+ err = ppp_unattached_ioctl(current->nsproxy->net_ns,
+ pf, file, cmd, arg);
+ goto out;
+ }
if (cmd == PPPIOCDETACH) {
/*
@@ -594,7 +599,6 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
* this fd and reopening /dev/ppp.
*/
err = -EINVAL;
- mutex_lock(&ppp_mutex);
if (pf->kind == INTERFACE) {
ppp = PF_TO_PPP(pf);
rtnl_lock();
@@ -608,15 +612,13 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
} else
pr_warn("PPPIOCDETACH file->f_count=%ld\n",
atomic_long_read(&file->f_count));
- mutex_unlock(&ppp_mutex);
- return err;
+ goto out;
}
if (pf->kind == CHANNEL) {
struct channel *pch;
struct ppp_channel *chan;
- mutex_lock(&ppp_mutex);
pch = PF_TO_CHANNEL(pf);
switch (cmd) {
@@ -638,17 +640,16 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
err = chan->ops->ioctl(chan, cmd, arg);
up_read(&pch->chan_sem);
}
- mutex_unlock(&ppp_mutex);
- return err;
+ goto out;
}
if (pf->kind != INTERFACE) {
/* can't happen */
pr_err("PPP: not interface or channel??\n");
- return -EINVAL;
+ err = -EINVAL;
+ goto out;
}
- mutex_lock(&ppp_mutex);
ppp = PF_TO_PPP(pf);
switch (cmd) {
case PPPIOCSMRU:
@@ -823,7 +824,10 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
default:
err = -ENOTTY;
}
+
+out:
mutex_unlock(&ppp_mutex);
+
return err;
}
@@ -836,7 +840,6 @@ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
struct ppp_net *pn;
int __user *p = (int __user *)arg;
- mutex_lock(&ppp_mutex);
switch (cmd) {
case PPPIOCNEWUNIT:
/* Create a new ppp unit */
@@ -886,7 +889,7 @@ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
default:
err = -ENOTTY;
}
- mutex_unlock(&ppp_mutex);
+
return err;
}
@@ -2290,7 +2293,7 @@ int ppp_register_net_channel(struct net *net, struct ppp_channel *chan)
pch->ppp = NULL;
pch->chan = chan;
- pch->chan_net = net;
+ pch->chan_net = get_net(net);
chan->ppp = pch;
init_ppp_file(&pch->file, CHANNEL);
pch->file.hdrlen = chan->hdrlen;
@@ -2387,6 +2390,8 @@ ppp_unregister_channel(struct ppp_channel *chan)
spin_lock_bh(&pn->all_channels_lock);
list_del(&pch->list);
spin_unlock_bh(&pn->all_channels_lock);
+ put_net(pch->chan_net);
+ pch->chan_net = NULL;
pch->file.dead = 1;
wake_up_interruptible(&pch->file.rwait);
@@ -2803,6 +2808,7 @@ static struct ppp *ppp_create_interface(struct net *net, int unit,
out2:
mutex_unlock(&pn->all_ppp_mutex);
+ rtnl_unlock();
free_netdev(dev);
out1:
*retp = ret;
diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c
index 01f08a7751f7..e7034c55e796 100644
--- a/drivers/net/rionet.c
+++ b/drivers/net/rionet.c
@@ -280,7 +280,7 @@ static void rionet_outb_msg_event(struct rio_mport *mport, void *dev_id, int mbo
struct net_device *ndev = dev_id;
struct rionet_private *rnet = netdev_priv(ndev);
- spin_lock(&rnet->lock);
+ spin_lock(&rnet->tx_lock);
if (netif_msg_intr(rnet))
printk(KERN_INFO
@@ -299,7 +299,7 @@ static void rionet_outb_msg_event(struct rio_mport *mport, void *dev_id, int mbo
if (rnet->tx_cnt < RIONET_TX_RING_SIZE)
netif_wake_queue(ndev);
- spin_unlock(&rnet->lock);
+ spin_unlock(&rnet->tx_lock);
}
static int rionet_open(struct net_device *ndev)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 6d398f13e1e6..4b15d9ee5a54 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -621,7 +621,8 @@ static int tun_attach(struct tun_struct *tun, struct file *file, bool skip_filte
/* Re-attach the filter to persist device */
if (!skip_filter && (tun->filter_attached == true)) {
- err = sk_attach_filter(&tun->fprog, tfile->socket.sk);
+ err = __sk_attach_filter(&tun->fprog, tfile->socket.sk,
+ lockdep_rtnl_is_held());
if (!err)
goto out;
}
@@ -1000,7 +1001,6 @@ static void tun_net_init(struct net_device *dev)
/* Zero header length */
dev->type = ARPHRD_NONE;
dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
- dev->tx_queue_len = TUN_READQ_SIZE; /* We prefer our own queue length */
break;
case IFF_TAP:
@@ -1012,7 +1012,6 @@ static void tun_net_init(struct net_device *dev)
eth_hw_addr_random(dev);
- dev->tx_queue_len = TUN_READQ_SIZE; /* We prefer our own queue length */
break;
}
}
@@ -1467,6 +1466,8 @@ static void tun_setup(struct net_device *dev)
dev->ethtool_ops = &tun_ethtool_ops;
dev->destructor = tun_free_netdev;
+ /* We prefer our own queue length */
+ dev->tx_queue_len = TUN_READQ_SIZE;
}
/* Trivial set of netlink ops to allow deleting tun or tap
@@ -1808,7 +1809,7 @@ static void tun_detach_filter(struct tun_struct *tun, int n)
for (i = 0; i < n; i++) {
tfile = rtnl_dereference(tun->tfiles[i]);
- sk_detach_filter(tfile->socket.sk);
+ __sk_detach_filter(tfile->socket.sk, lockdep_rtnl_is_held());
}
tun->filter_attached = false;
@@ -1821,7 +1822,8 @@ static int tun_attach_filter(struct tun_struct *tun)
for (i = 0; i < tun->numqueues; i++) {
tfile = rtnl_dereference(tun->tfiles[i]);
- ret = sk_attach_filter(&tun->fprog, tfile->socket.sk);
+ ret = __sk_attach_filter(&tun->fprog, tfile->socket.sk,
+ lockdep_rtnl_is_held());
if (ret) {
tun_detach_filter(tun, i);
return ret;
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 3da70bf9936a..7cba2c3759df 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -160,6 +160,12 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
info->u = header.usb_cdc_union_desc;
info->header = header.usb_cdc_header_desc;
info->ether = header.usb_cdc_ether_desc;
+ if (!info->u) {
+ if (rndis)
+ goto skip;
+ else /* in that case a quirk is mandatory */
+ goto bad_desc;
+ }
/* we need a master/control interface (what we're
* probed with) and a slave/data interface; union
* descriptors sort this all out.
@@ -256,7 +262,7 @@ skip:
goto bad_desc;
}
- } else if (!info->header || !info->u || (!rndis && !info->ether)) {
+ } else if (!info->header || (!rndis && !info->ether)) {
dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n",
info->header ? "" : "header ",
info->u ? "" : "union ",
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index e8a1144c5a8b..8c2bb77db049 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -794,7 +794,11 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber;
- /* reset data interface */
+ /* Reset data interface. Some devices will not reset properly
+ * unless they are configured first. Toggle the altsetting to
+ * force a reset
+ */
+ usb_set_interface(dev->udev, iface_no, data_altsetting);
temp = usb_set_interface(dev->udev, iface_no, 0);
if (temp) {
dev_dbg(&intf->dev, "set interface failed\n");
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 982e0acd1a36..a34f491224c1 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -699,6 +699,7 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x19d2, 0x1426, 2)}, /* ZTE MF91 */
{QMI_FIXED_INTF(0x19d2, 0x1428, 2)}, /* Telewell TW-LTE 4G v2 */
{QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */
+ {QMI_FIXED_INTF(0x2001, 0x7e19, 4)}, /* D-Link DWM-221 B1 */
{QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)}, /* Sierra Wireless MC7700 */
{QMI_FIXED_INTF(0x114f, 0x68a2, 8)}, /* Sierra Wireless MC7750 */
{QMI_FIXED_INTF(0x1199, 0x68a2, 8)}, /* Sierra Wireless MC7710 in QMI mode */
@@ -718,8 +719,10 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x1199, 0x9061, 8)}, /* Sierra Wireless Modem */
{QMI_FIXED_INTF(0x1199, 0x9070, 8)}, /* Sierra Wireless MC74xx/EM74xx */
{QMI_FIXED_INTF(0x1199, 0x9070, 10)}, /* Sierra Wireless MC74xx/EM74xx */
- {QMI_FIXED_INTF(0x1199, 0x9071, 8)}, /* Sierra Wireless MC74xx/EM74xx */
- {QMI_FIXED_INTF(0x1199, 0x9071, 10)}, /* Sierra Wireless MC74xx/EM74xx */
+ {QMI_FIXED_INTF(0x1199, 0x9071, 8)}, /* Sierra Wireless MC74xx */
+ {QMI_FIXED_INTF(0x1199, 0x9071, 10)}, /* Sierra Wireless MC74xx */
+ {QMI_FIXED_INTF(0x1199, 0x9079, 8)}, /* Sierra Wireless EM74xx */
+ {QMI_FIXED_INTF(0x1199, 0x9079, 10)}, /* Sierra Wireless EM74xx */
{QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */
{QMI_FIXED_INTF(0x1bbb, 0x0203, 2)}, /* Alcatel L800MA */
{QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 0744bf2ef2d6..c2ea4e5666fb 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1766,6 +1766,13 @@ out3:
if (info->unbind)
info->unbind (dev, udev);
out1:
+ /* subdrivers must undo all they did in bind() if they
+ * fail it, but we may fail later and a deferred kevent
+ * may trigger an error resubmitting itself and, worse,
+ * schedule a timer. So we kill it all just in case.
+ */
+ cancel_work_sync(&dev->kevent);
+ del_timer_sync(&dev->delay);
free_netdev(net);
out:
return status;
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 0a242b200df4..903bda437839 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -114,20 +114,23 @@ static struct dst_ops vrf_dst_ops = {
#if IS_ENABLED(CONFIG_IPV6)
static bool check_ipv6_frame(const struct sk_buff *skb)
{
- const struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb->data;
- size_t hlen = sizeof(*ipv6h);
+ const struct ipv6hdr *ipv6h;
+ struct ipv6hdr _ipv6h;
bool rc = true;
- if (skb->len < hlen)
+ ipv6h = skb_header_pointer(skb, 0, sizeof(_ipv6h), &_ipv6h);
+ if (!ipv6h)
goto out;
if (ipv6h->nexthdr == NEXTHDR_ICMP) {
const struct icmp6hdr *icmph;
+ struct icmp6hdr _icmph;
- if (skb->len < hlen + sizeof(*icmph))
+ icmph = skb_header_pointer(skb, sizeof(_ipv6h),
+ sizeof(_icmph), &_icmph);
+ if (!icmph)
goto out;
- icmph = (struct icmp6hdr *)(skb->data + sizeof(*ipv6h));
switch (icmph->icmp6_type) {
case NDISC_ROUTER_SOLICITATION:
case NDISC_ROUTER_ADVERTISEMENT:
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index e0fcda4ddd55..3c0df70e2f53 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -1306,8 +1306,10 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
gbp = (struct vxlanhdr_gbp *)vxh;
md->gbp = ntohs(gbp->policy_id);
- if (tun_dst)
+ if (tun_dst) {
tun_dst->u.tun_info.key.tun_flags |= TUNNEL_VXLAN_OPT;
+ tun_dst->u.tun_info.options_len = sizeof(*md);
+ }
if (gbp->dont_learn)
md->gbp |= VXLAN_GBP_DONT_LEARN;
diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c
index 44541dbc5c28..69b994f3b8c5 100644
--- a/drivers/net/wan/farsync.c
+++ b/drivers/net/wan/farsync.c
@@ -2516,7 +2516,7 @@ fst_add_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->mem_start = card->phys_mem
+ BUF_OFFSET ( txBuffer[i][0][0]);
dev->mem_end = card->phys_mem
- + BUF_OFFSET ( txBuffer[i][NUM_TX_BUFFER][0]);
+ + BUF_OFFSET ( txBuffer[i][NUM_TX_BUFFER - 1][LEN_RX_BUFFER - 1]);
dev->base_addr = card->pci_conf;
dev->irq = card->irq;
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c
index cc81482c934d..113a43fca9cf 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom.c
@@ -403,10 +403,9 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah,
if (match) {
if (AR_SREV_9287(ah)) {
- /* FIXME: array overrun? */
for (i = 0; i < numXpdGains; i++) {
minPwrT4[i] = data_9287[idxL].pwrPdg[i][0];
- maxPwrT4[i] = data_9287[idxL].pwrPdg[i][4];
+ maxPwrT4[i] = data_9287[idxL].pwrPdg[i][intercepts - 1];
ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
data_9287[idxL].pwrPdg[i],
data_9287[idxL].vpdPdg[i],
@@ -416,7 +415,7 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah,
} else if (eeprom_4k) {
for (i = 0; i < numXpdGains; i++) {
minPwrT4[i] = data_4k[idxL].pwrPdg[i][0];
- maxPwrT4[i] = data_4k[idxL].pwrPdg[i][4];
+ maxPwrT4[i] = data_4k[idxL].pwrPdg[i][intercepts - 1];
ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
data_4k[idxL].pwrPdg[i],
data_4k[idxL].vpdPdg[i],
@@ -426,7 +425,7 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah,
} else {
for (i = 0; i < numXpdGains; i++) {
minPwrT4[i] = data_def[idxL].pwrPdg[i][0];
- maxPwrT4[i] = data_def[idxL].pwrPdg[i][4];
+ maxPwrT4[i] = data_def[idxL].pwrPdg[i][intercepts - 1];
ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
data_def[idxL].pwrPdg[i],
data_def[idxL].vpdPdg[i],
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
index d906fa13ba97..610c442c7ab2 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -106,7 +106,7 @@ static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant)
sizeof(tx_ant_cmd), &tx_ant_cmd);
}
-static void iwl_free_fw_paging(struct iwl_mvm *mvm)
+void iwl_free_fw_paging(struct iwl_mvm *mvm)
{
int i;
@@ -126,6 +126,8 @@ static void iwl_free_fw_paging(struct iwl_mvm *mvm)
get_order(mvm->fw_paging_db[i].fw_paging_size));
}
kfree(mvm->trans->paging_download_buf);
+ mvm->trans->paging_download_buf = NULL;
+
memset(mvm->fw_paging_db, 0, sizeof(mvm->fw_paging_db));
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index 4bde2d027dcd..244e26c26821 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -1190,6 +1190,9 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb);
+/* Paging */
+void iwl_free_fw_paging(struct iwl_mvm *mvm);
+
/* MVM debugfs */
#ifdef CONFIG_IWLWIFI_DEBUGFS
int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir);
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index 13c97f665ba8..c3adf2bcdc85 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -645,6 +645,8 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++)
kfree(mvm->nvm_sections[i].data);
+ iwl_free_fw_paging(mvm);
+
iwl_mvm_tof_clean(mvm);
ieee80211_free_hw(mvm->hw);
diff --git a/drivers/nfc/nq-nci.c b/drivers/nfc/nq-nci.c
index 918f8c82acdd..9b962a63c3d8 100644
--- a/drivers/nfc/nq-nci.c
+++ b/drivers/nfc/nq-nci.c
@@ -107,36 +107,14 @@ static void nqx_disable_irq(struct nqx_dev *nqx_dev)
spin_unlock_irqrestore(&nqx_dev->irq_enabled_lock, flags);
}
-static void nqx_enable_irq(struct nqx_dev *nqx_dev)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&nqx_dev->irq_enabled_lock, flags);
- if (!nqx_dev->irq_enabled) {
- nqx_dev->irq_enabled = true;
- enable_irq(nqx_dev->client->irq);
- }
- spin_unlock_irqrestore(&nqx_dev->irq_enabled_lock, flags);
-}
-
static irqreturn_t nqx_dev_irq_handler(int irq, void *dev_id)
{
struct nqx_dev *nqx_dev = dev_id;
unsigned long flags;
- int ret;
if (device_may_wakeup(&nqx_dev->client->dev))
pm_wakeup_event(&nqx_dev->client->dev, WAKEUP_SRC_TIMEOUT);
- ret = gpio_get_value(nqx_dev->irq_gpio);
- if (!ret) {
-#ifdef NFC_KERNEL_BU
- dev_info(&nqx_dev->client->dev,
- "nqx nfc : nqx_dev_irq_handler error = %d\n", ret);
-#endif
- return IRQ_HANDLED;
- }
-
nqx_disable_irq(nqx_dev);
spin_lock_irqsave(&nqx_dev->irq_enabled_lock, flags);
nqx_dev->count_irq++;
@@ -175,15 +153,24 @@ static ssize_t nfc_read(struct file *filp, char __user *buf,
ret = -EAGAIN;
goto err;
}
- if (!nqx_dev->irq_enabled) {
- enable_irq(nqx_dev->client->irq);
- nqx_dev->irq_enabled = true;
+ while (1) {
+ ret = 0;
+ if (!nqx_dev->irq_enabled) {
+ nqx_dev->irq_enabled = true;
+ enable_irq(nqx_dev->client->irq);
+ }
+ if (!gpio_get_value(nqx_dev->irq_gpio)) {
+ ret = wait_event_interruptible(nqx_dev->read_wq,
+ !nqx_dev->irq_enabled);
+ }
+ if (ret)
+ goto err;
+ nqx_disable_irq(nqx_dev);
+
+ if (gpio_get_value(nqx_dev->irq_gpio))
+ break;
+ dev_err_ratelimited(&nqx_dev->client->dev, "gpio is low, no need to read data\n");
}
- ret = wait_event_interruptible(nqx_dev->read_wq,
- gpio_get_value(nqx_dev->irq_gpio));
- if (ret)
- goto err;
- nqx_disable_irq(nqx_dev);
}
tmp = nqx_dev->kbuf;
@@ -393,7 +380,6 @@ int nfc_ioctl_power_states(struct file *filp, unsigned long arg)
/* hardware dependent delay */
msleep(100);
} else if (arg == 1) {
- nqx_enable_irq(nqx_dev);
dev_dbg(&nqx_dev->client->dev,
"gpio_set_value enable: %s: info: %p\n",
__func__, nqx_dev);
diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c
index 7e2c43f701bc..5f47356d6942 100644
--- a/drivers/nvdimm/bus.c
+++ b/drivers/nvdimm/bus.c
@@ -335,7 +335,7 @@ static const struct nd_cmd_desc __nd_cmd_dimm_descs[] = {
[ND_CMD_IMPLEMENTED] = { },
[ND_CMD_SMART] = {
.out_num = 2,
- .out_sizes = { 4, 8, },
+ .out_sizes = { 4, 128, },
},
[ND_CMD_SMART_THRESHOLD] = {
.out_num = 2,
@@ -513,10 +513,10 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
/* fail write commands (when read-only) */
if (read_only)
- switch (ioctl_cmd) {
- case ND_IOCTL_VENDOR:
- case ND_IOCTL_SET_CONFIG_DATA:
- case ND_IOCTL_ARS_START:
+ switch (cmd) {
+ case ND_CMD_VENDOR:
+ case ND_CMD_SET_CONFIG_DATA:
+ case ND_CMD_ARS_START:
dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n",
nvdimm ? nvdimm_cmd_name(cmd)
: nvdimm_bus_cmd_name(cmd));
diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c
index 71805a1aa0f3..9d3974591cd6 100644
--- a/drivers/nvdimm/pfn_devs.c
+++ b/drivers/nvdimm/pfn_devs.c
@@ -275,7 +275,7 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn)
} else {
/* from init we validate */
if (memcmp(nd_pfn->uuid, pfn_sb->uuid, 16) != 0)
- return -EINVAL;
+ return -ENODEV;
}
/*
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index edb1984201e9..7aafb5fb9336 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -179,6 +179,9 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
u16 orig_cmd;
struct pci_bus_region region, inverted_region;
+ if (dev->non_compliant_bars)
+ return 0;
+
mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
/* No printks while decoding is disabled! */
@@ -1174,6 +1177,7 @@ void pci_msi_setup_pci_dev(struct pci_dev *dev)
int pci_setup_device(struct pci_dev *dev)
{
u32 class;
+ u16 cmd;
u8 hdr_type;
int pos = 0;
struct pci_bus_region region;
@@ -1219,6 +1223,16 @@ int pci_setup_device(struct pci_dev *dev)
/* device class may be changed after fixup */
class = dev->class >> 8;
+ if (dev->non_compliant_bars) {
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
+ dev_info(&dev->dev, "device has non-compliant BARs; disabling IO/MEM decoding\n");
+ cmd &= ~PCI_COMMAND_IO;
+ cmd &= ~PCI_COMMAND_MEMORY;
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+ }
+
switch (dev->hdr_type) { /* header type */
case PCI_HEADER_TYPE_NORMAL: /* standard header */
if (class == PCI_CLASS_BRIDGE_PCI)
diff --git a/drivers/pcmcia/db1xxx_ss.c b/drivers/pcmcia/db1xxx_ss.c
index 4c2fa05b4589..944674ee3464 100644
--- a/drivers/pcmcia/db1xxx_ss.c
+++ b/drivers/pcmcia/db1xxx_ss.c
@@ -56,6 +56,7 @@ struct db1x_pcmcia_sock {
int stschg_irq; /* card-status-change irq */
int card_irq; /* card irq */
int eject_irq; /* db1200/pb1200 have these */
+ int insert_gpio; /* db1000 carddetect gpio */
#define BOARD_TYPE_DEFAULT 0 /* most boards */
#define BOARD_TYPE_DB1200 1 /* IRQs aren't gpios */
@@ -83,7 +84,7 @@ static int db1200_card_inserted(struct db1x_pcmcia_sock *sock)
/* carddetect gpio: low-active */
static int db1000_card_inserted(struct db1x_pcmcia_sock *sock)
{
- return !gpio_get_value(irq_to_gpio(sock->insert_irq));
+ return !gpio_get_value(sock->insert_gpio);
}
static int db1x_card_inserted(struct db1x_pcmcia_sock *sock)
@@ -457,9 +458,15 @@ static int db1x_pcmcia_socket_probe(struct platform_device *pdev)
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "card");
sock->card_irq = r ? r->start : 0;
- /* insert: irq which triggers on card insertion/ejection */
+ /* insert: irq which triggers on card insertion/ejection
+ * BIG FAT NOTE: on DB1000/1100/1500/1550 we pass a GPIO here!
+ */
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "insert");
sock->insert_irq = r ? r->start : -1;
+ if (sock->board_type == BOARD_TYPE_DEFAULT) {
+ sock->insert_gpio = r ? r->start : -1;
+ sock->insert_irq = r ? gpio_to_irq(r->start) : -1;
+ }
/* stschg: irq which trigger on card status change (optional) */
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "stschg");
diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
index 2e6ca69635aa..17dd8fe12b54 100644
--- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c
+++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
@@ -779,7 +779,7 @@ static int bcm2835_pctl_dt_node_to_map(struct pinctrl_dev *pctldev,
}
if (num_pulls) {
err = of_property_read_u32_index(np, "brcm,pull",
- (num_funcs > 1) ? i : 0, &pull);
+ (num_pulls > 1) ? i : 0, &pull);
if (err)
goto out;
err = bcm2835_pctl_dt_node_to_map_pull(pc, np, pin,
diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c
index a5bb93987378..1029aa7889b5 100644
--- a/drivers/pinctrl/freescale/pinctrl-imx.c
+++ b/drivers/pinctrl/freescale/pinctrl-imx.c
@@ -726,19 +726,18 @@ int imx_pinctrl_probe(struct platform_device *pdev,
if (of_property_read_bool(dev_np, "fsl,input-sel")) {
np = of_parse_phandle(dev_np, "fsl,input-sel", 0);
- if (np) {
- ipctl->input_sel_base = of_iomap(np, 0);
- if (IS_ERR(ipctl->input_sel_base)) {
- of_node_put(np);
- dev_err(&pdev->dev,
- "iomuxc input select base address not found\n");
- return PTR_ERR(ipctl->input_sel_base);
- }
- } else {
+ if (!np) {
dev_err(&pdev->dev, "iomuxc fsl,input-sel property not found\n");
return -EINVAL;
}
+
+ ipctl->input_sel_base = of_iomap(np, 0);
of_node_put(np);
+ if (!ipctl->input_sel_base) {
+ dev_err(&pdev->dev,
+ "iomuxc input select base address not found\n");
+ return -ENOMEM;
+ }
}
imx_pinctrl_desc.name = dev_name(&pdev->dev);
diff --git a/drivers/pinctrl/nomadik/pinctrl-nomadik.c b/drivers/pinctrl/nomadik/pinctrl-nomadik.c
index eebfae0c9b7c..f844b4ae7f79 100644
--- a/drivers/pinctrl/nomadik/pinctrl-nomadik.c
+++ b/drivers/pinctrl/nomadik/pinctrl-nomadik.c
@@ -995,7 +995,7 @@ static void nmk_gpio_dbg_show_one(struct seq_file *s,
int val;
if (pull)
- pullidx = data_out ? 1 : 2;
+ pullidx = data_out ? 2 : 1;
seq_printf(s, " gpio-%-3d (%-20.20s) in %s %s",
gpio,
diff --git a/drivers/pinctrl/pinctrl-pistachio.c b/drivers/pinctrl/pinctrl-pistachio.c
index 85c9046c690e..6b1a47f8c096 100644
--- a/drivers/pinctrl/pinctrl-pistachio.c
+++ b/drivers/pinctrl/pinctrl-pistachio.c
@@ -469,27 +469,27 @@ static const char * const pistachio_mips_pll_lock_groups[] = {
"mfio83",
};
-static const char * const pistachio_sys_pll_lock_groups[] = {
+static const char * const pistachio_audio_pll_lock_groups[] = {
"mfio84",
};
-static const char * const pistachio_wifi_pll_lock_groups[] = {
+static const char * const pistachio_rpu_v_pll_lock_groups[] = {
"mfio85",
};
-static const char * const pistachio_bt_pll_lock_groups[] = {
+static const char * const pistachio_rpu_l_pll_lock_groups[] = {
"mfio86",
};
-static const char * const pistachio_rpu_v_pll_lock_groups[] = {
+static const char * const pistachio_sys_pll_lock_groups[] = {
"mfio87",
};
-static const char * const pistachio_rpu_l_pll_lock_groups[] = {
+static const char * const pistachio_wifi_pll_lock_groups[] = {
"mfio88",
};
-static const char * const pistachio_audio_pll_lock_groups[] = {
+static const char * const pistachio_bt_pll_lock_groups[] = {
"mfio89",
};
@@ -559,12 +559,12 @@ enum pistachio_mux_option {
PISTACHIO_FUNCTION_DREQ4,
PISTACHIO_FUNCTION_DREQ5,
PISTACHIO_FUNCTION_MIPS_PLL_LOCK,
+ PISTACHIO_FUNCTION_AUDIO_PLL_LOCK,
+ PISTACHIO_FUNCTION_RPU_V_PLL_LOCK,
+ PISTACHIO_FUNCTION_RPU_L_PLL_LOCK,
PISTACHIO_FUNCTION_SYS_PLL_LOCK,
PISTACHIO_FUNCTION_WIFI_PLL_LOCK,
PISTACHIO_FUNCTION_BT_PLL_LOCK,
- PISTACHIO_FUNCTION_RPU_V_PLL_LOCK,
- PISTACHIO_FUNCTION_RPU_L_PLL_LOCK,
- PISTACHIO_FUNCTION_AUDIO_PLL_LOCK,
PISTACHIO_FUNCTION_DEBUG_RAW_CCA_IND,
PISTACHIO_FUNCTION_DEBUG_ED_SEC20_CCA_IND,
PISTACHIO_FUNCTION_DEBUG_ED_SEC40_CCA_IND,
@@ -620,12 +620,12 @@ static const struct pistachio_function pistachio_functions[] = {
FUNCTION(dreq4),
FUNCTION(dreq5),
FUNCTION(mips_pll_lock),
+ FUNCTION(audio_pll_lock),
+ FUNCTION(rpu_v_pll_lock),
+ FUNCTION(rpu_l_pll_lock),
FUNCTION(sys_pll_lock),
FUNCTION(wifi_pll_lock),
FUNCTION(bt_pll_lock),
- FUNCTION(rpu_v_pll_lock),
- FUNCTION(rpu_l_pll_lock),
- FUNCTION(audio_pll_lock),
FUNCTION(debug_raw_cca_ind),
FUNCTION(debug_ed_sec20_cca_ind),
FUNCTION(debug_ed_sec40_cca_ind),
diff --git a/drivers/pinctrl/qcom/pinctrl-msmfalcon.c b/drivers/pinctrl/qcom/pinctrl-msmfalcon.c
index 14abb75fffe0..45db409eb7c1 100644
--- a/drivers/pinctrl/qcom/pinctrl-msmfalcon.c
+++ b/drivers/pinctrl/qcom/pinctrl-msmfalcon.c
@@ -25,9 +25,9 @@
.ngroups = ARRAY_SIZE(fname##_groups), \
}
-#define SOUTH 0x00500000
-#define WEST 0x00100000
-#define EAST 0x00900000
+#define NORTH 0x00900000
+#define CENTER 0x00500000
+#define SOUTH 0x00100000
#define REG_SIZE 0x1000
#define PINGROUP(id, base, f1, f2, f3, f4, f5, f6, f7, f8, f9) \
{ \
@@ -204,9 +204,16 @@ static const struct pinctrl_pin_desc msmfalcon_pins[] = {
PINCTRL_PIN(108, "GPIO_108"),
PINCTRL_PIN(109, "GPIO_109"),
PINCTRL_PIN(110, "GPIO_110"),
- PINCTRL_PIN(111, "SDC2_CLK"),
- PINCTRL_PIN(112, "SDC2_CMD"),
- PINCTRL_PIN(113, "SDC2_DATA"),
+ PINCTRL_PIN(111, "GPIO_111"),
+ PINCTRL_PIN(112, "GPIO_112"),
+ PINCTRL_PIN(113, "GPIO_113"),
+ PINCTRL_PIN(114, "SDC1_CLK"),
+ PINCTRL_PIN(115, "SDC1_CMD"),
+ PINCTRL_PIN(116, "SDC1_DATA"),
+ PINCTRL_PIN(117, "SDC2_CLK"),
+ PINCTRL_PIN(118, "SDC2_CMD"),
+ PINCTRL_PIN(119, "SDC2_DATA"),
+ PINCTRL_PIN(120, "SDC1_RCLK"),
};
#define DECLARE_MSM_GPIO_PINS(pin) \
@@ -322,88 +329,101 @@ DECLARE_MSM_GPIO_PINS(107);
DECLARE_MSM_GPIO_PINS(108);
DECLARE_MSM_GPIO_PINS(109);
DECLARE_MSM_GPIO_PINS(110);
+DECLARE_MSM_GPIO_PINS(111);
+DECLARE_MSM_GPIO_PINS(112);
+DECLARE_MSM_GPIO_PINS(113);
-static const unsigned int sdc2_clk_pins[] = { 111 };
-static const unsigned int sdc2_cmd_pins[] = { 112 };
-static const unsigned int sdc2_data_pins[] = { 113 };
+static const unsigned int sdc1_clk_pins[] = { 114 };
+static const unsigned int sdc1_cmd_pins[] = { 115 };
+static const unsigned int sdc1_data_pins[] = { 116 };
+static const unsigned int sdc2_clk_pins[] = { 117 };
+static const unsigned int sdc2_cmd_pins[] = { 118 };
+static const unsigned int sdc2_data_pins[] = { 119 };
+static const unsigned int sdc1_rclk_pins[] = { 120 };
enum msmfalcon_functions {
msm_mux_blsp_spi1,
msm_mux_gpio,
+ msm_mux_blsp_uim1,
msm_mux_tgu_ch0,
- msm_mux_tgu_ch1,
+ msm_mux_qdss_gpio4,
+ msm_mux_atest_gpsadc1,
msm_mux_blsp_uart1,
+ msm_mux_SMB_STAT,
+ msm_mux_phase_flag14,
+ msm_mux_blsp_i2c2,
+ msm_mux_phase_flag31,
msm_mux_blsp_spi3,
msm_mux_wlan1_adc1,
msm_mux_atest_usb13,
- msm_mux_bimc_dte1,
- msm_mux_wlan1_adc0,
- msm_mux_atest_usb12,
- msm_mux_bimc_dte0,
+ msm_mux_tgu_ch1,
+ msm_mux_qdss_gpio5,
+ msm_mux_atest_gpsadc0,
msm_mux_blsp_i2c1,
- msm_mux_blsp_uim1,
msm_mux_ddr_bist,
msm_mux_atest_tsens2,
msm_mux_atest_usb1,
msm_mux_blsp_spi2,
- msm_mux_phase_flag3,
- msm_mux_phase_flag14,
- msm_mux_blsp_i2c2,
msm_mux_blsp_uim2,
- msm_mux_phase_flag31,
+ msm_mux_phase_flag3,
+ msm_mux_bimc_dte1,
+ msm_mux_wlan1_adc0,
+ msm_mux_atest_usb12,
+ msm_mux_bimc_dte0,
msm_mux_blsp_i2c3,
- msm_mux_atest_gpsadc1,
msm_mux_wlan2_adc1,
msm_mux_atest_usb11,
msm_mux_dbg_out,
- msm_mux_atest_gpsadc0,
msm_mux_wlan2_adc0,
msm_mux_atest_usb10,
+ msm_mux_RCM_MARKER,
msm_mux_blsp_spi4,
msm_mux_pri_mi2s,
msm_mux_phase_flag26,
- msm_mux_qdss_gpio4,
+ msm_mux_qdss_cti,
+ msm_mux_DP_HOT,
msm_mux_pri_mi2s_ws,
msm_mux_phase_flag27,
- msm_mux_qdss_gpio5,
msm_mux_blsp_i2c4,
msm_mux_phase_flag28,
msm_mux_blsp_uart5,
msm_mux_blsp_spi5,
+ msm_mux_blsp_uim5,
msm_mux_phase_flag5,
msm_mux_blsp_i2c5,
- msm_mux_blsp_uim5,
msm_mux_blsp_spi6,
msm_mux_blsp_uart2,
- msm_mux_qdss_cti,
- msm_mux_sec_mi2s,
- msm_mux_sndwire_clk,
- msm_mux_phase_flag17,
- msm_mux_vsense_clkout,
- msm_mux_sndwire_data,
- msm_mux_phase_flag18,
- msm_mux_blsp_i2c7,
- msm_mux_wsa_en1,
- msm_mux_phase_flag19,
+ msm_mux_blsp_uim6,
msm_mux_phase_flag11,
msm_mux_vsense_data0,
msm_mux_blsp_i2c6,
- msm_mux_blsp_uim6,
msm_mux_phase_flag12,
msm_mux_vsense_data1,
msm_mux_phase_flag13,
msm_mux_vsense_mode,
msm_mux_blsp_spi7,
msm_mux_BLSP_UART,
+ msm_mux_sec_mi2s,
+ msm_mux_sndwire_clk,
+ msm_mux_phase_flag17,
+ msm_mux_vsense_clkout,
+ msm_mux_sndwire_data,
+ msm_mux_phase_flag18,
+ msm_mux_WSA_SPKR,
+ msm_mux_blsp_i2c7,
+ msm_mux_phase_flag19,
msm_mux_vfr_1,
- msm_mux_wsa_en2,
msm_mux_phase_flag20,
+ msm_mux_NFC_INT,
msm_mux_blsp_spi,
msm_mux_m_voc,
msm_mux_phase_flag21,
+ msm_mux_NFC_EN,
msm_mux_phase_flag22,
+ msm_mux_NFC_DWL,
msm_mux_BLSP_I2C,
msm_mux_phase_flag23,
+ msm_mux_NFC_ESE,
msm_mux_pwr_modem,
msm_mux_phase_flag24,
msm_mux_qdss_gpio,
@@ -419,88 +439,92 @@ enum msmfalcon_functions {
msm_mux_qspi_data2,
msm_mux_jitter_bist,
msm_mux_qdss_gpio3,
+ msm_mux_qdss_gpio7,
+ msm_mux_FL_R3LED,
+ msm_mux_CCI_TIMER0,
+ msm_mux_FL_STROBE,
+ msm_mux_CCI_TIMER1,
+ msm_mux_CAM_LDO1,
+ msm_mux_mdss_vsync0,
+ msm_mux_mdss_vsync1,
+ msm_mux_mdss_vsync2,
+ msm_mux_mdss_vsync3,
+ msm_mux_qdss_gpio9,
+ msm_mux_CAM_IRQ,
+ msm_mux_atest_usb2,
msm_mux_cci_i2c,
msm_mux_pll_bypassnl,
msm_mux_atest_tsens,
+ msm_mux_atest_usb21,
msm_mux_pll_reset,
- msm_mux_qdss_gpio9,
- msm_mux_CAM_IRQ,
+ msm_mux_atest_usb23,
+ msm_mux_qdss_gpio6,
msm_mux_CCI_TIMER3,
msm_mux_CCI_ASYNC,
msm_mux_qspi_cs,
msm_mux_qdss_gpio10,
- msm_mux_CAM4_STANDBY,
+ msm_mux_CAM3_STANDBY,
msm_mux_CCI_TIMER4,
msm_mux_qdss_gpio11,
- msm_mux_bt_reset,
+ msm_mux_CAM_LDO2,
msm_mux_cci_async,
msm_mux_qdss_gpio12,
- msm_mux_CAM1_RST,
- msm_mux_qdss_gpio6,
- msm_mux_qdss_gpio7,
- msm_mux_FL_FRONT,
- msm_mux_CCI_TIMER0,
- msm_mux_qdss_gpio8,
- msm_mux_FL_STROBE,
- msm_mux_CCI_TIMER1,
- msm_mux_LASER_CE,
- msm_mux_mdss_vsync0,
- msm_mux_mdss_vsync1,
- msm_mux_mdss_vsync2,
- msm_mux_mdss_vsync3,
+ msm_mux_CAM0_RST,
msm_mux_qdss_gpio13,
- msm_mux_CAM2_RST,
+ msm_mux_CAM1_RST,
msm_mux_qspi_clk,
msm_mux_phase_flag30,
msm_mux_qdss_gpio14,
- msm_mux_CAM3_RST,
msm_mux_qspi_resetn,
msm_mux_phase_flag1,
msm_mux_qdss_gpio15,
- msm_mux_CAM1_STANDBY,
+ msm_mux_CAM0_STANDBY,
msm_mux_phase_flag2,
- msm_mux_CAM2_STANDBY,
+ msm_mux_CAM1_STANDBY,
msm_mux_phase_flag9,
- msm_mux_CAM3_STANDBY,
+ msm_mux_CAM2_STANDBY,
msm_mux_qspi_data3,
msm_mux_phase_flag15,
- msm_mux_CAM4_RST,
+ msm_mux_qdss_gpio8,
+ msm_mux_CAM3_RST,
msm_mux_CCI_TIMER2,
msm_mux_phase_flag16,
+ msm_mux_LCD0_RESET,
msm_mux_phase_flag6,
- msm_mux_RCM_MARKER2,
+ msm_mux_SD_CARD,
msm_mux_phase_flag29,
- msm_mux_SS_SWITCH,
+ msm_mux_DP_EN,
msm_mux_phase_flag25,
+ msm_mux_USBC_ORIENTATION,
msm_mux_phase_flag10,
+ msm_mux_atest_usb20,
msm_mux_gcc_gp1,
msm_mux_phase_flag4,
- msm_mux_USB_DIR,
+ msm_mux_atest_usb22,
msm_mux_USB_PHY,
msm_mux_gcc_gp2,
msm_mux_atest_char,
msm_mux_mdp_vsync,
msm_mux_gcc_gp3,
msm_mux_atest_char3,
- msm_mux_Lcd_mode,
- msm_mux_EDP_HOT,
+ msm_mux_FORCE_TOUCH,
msm_mux_cri_trng0,
msm_mux_atest_char2,
msm_mux_cri_trng1,
msm_mux_atest_char1,
+ msm_mux_AUDIO_USBC,
msm_mux_audio_ref,
msm_mux_MDP_VSYNC,
msm_mux_cri_trng,
msm_mux_atest_char0,
msm_mux_US_EURO,
- msm_mux_KEY_FOCUS,
- msm_mux_NAV_PPS,
+ msm_mux_LCD_BACKLIGHT,
msm_mux_blsp_spi8,
msm_mux_sp_cmu,
- msm_mux_SLT_PWR,
+ msm_mux_NAV_PPS,
+ msm_mux_GPS_TX,
msm_mux_adsp_ext,
msm_mux_TS_RESET,
- msm_mux_TS_INT,
msm_mux_ssc_irq,
msm_mux_isense_dbg,
msm_mux_phase_flag0,
@@ -508,8 +532,10 @@ enum msmfalcon_functions {
msm_mux_phase_flag8,
msm_mux_tsense_pwm1,
msm_mux_tsense_pwm2,
+ msm_mux_SENSOR_RST,
+ msm_mux_WMSS_RESETN,
msm_mux_HAPTICS_PWM,
- msm_mux_wmss_reset,
+ msm_mux_GPS_eLNA,
msm_mux_mss_lte,
msm_mux_uim2_data,
msm_mux_uim2_clk,
@@ -521,12 +547,12 @@ enum msmfalcon_functions {
msm_mux_uim1_present,
msm_mux_uim_batt,
msm_mux_pa_indicator,
- msm_mux_ssbi_gnss,
msm_mux_ldo_en,
msm_mux_ldo_update,
msm_mux_qlink_request,
msm_mux_qlink_enable,
msm_mux_prng_rosc,
+ msm_mux_LCD_PWR,
msm_mux_NA,
};
@@ -534,27 +560,44 @@ static const char * const blsp_spi1_groups[] = {
"gpio0", "gpio1", "gpio2", "gpio3", "gpio46",
};
static const char * const gpio_groups[] = {
- "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7",
- "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14",
- "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21",
- "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28",
- "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35",
- "gpio36", "gpio37", "gpio38", "gpio39", "gpio53", "gpio57", "gpio59",
- "gpio61", "gpio62", "gpio80", "gpio81", "gpio82", "gpio83", "gpio84",
- "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", "gpio91",
- "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98",
- "gpio99", "gpio100", "gpio101", "gpio102", "gpio103", "gpio104",
- "gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110",
+ "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio6", "gpio7", "gpio8",
+ "gpio9", "gpio10", "gpio11", "gpio14", "gpio15", "gpio16", "gpio17",
+ "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", "gpio23", "gpio24",
+ "gpio25", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36", "gpio37",
+ "gpio38", "gpio39", "gpio57", "gpio58", "gpio59", "gpio61", "gpio65",
+ "gpio81", "gpio82", "gpio83", "gpio84", "gpio85", "gpio86", "gpio87",
+ "gpio88", "gpio89", "gpio90", "gpio91", "gpio92", "gpio93", "gpio94",
+ "gpio95", "gpio96", "gpio97", "gpio98", "gpio99", "gpio100", "gpio101",
+ "gpio102", "gpio103", "gpio104", "gpio105", "gpio106", "gpio107",
+ "gpio108", "gpio109", "gpio110", "gpio111", "gpio112", "gpio113",
+};
+static const char * const blsp_uim1_groups[] = {
+ "gpio0", "gpio1",
};
static const char * const tgu_ch0_groups[] = {
"gpio0",
};
-static const char * const tgu_ch1_groups[] = {
- "gpio1",
+static const char * const qdss_gpio4_groups[] = {
+ "gpio0", "gpio36",
+};
+static const char * const atest_gpsadc1_groups[] = {
+ "gpio0",
};
static const char * const blsp_uart1_groups[] = {
"gpio0", "gpio1", "gpio2", "gpio3",
};
+static const char * const SMB_STAT_groups[] = {
+ "gpio5",
+};
+static const char * const phase_flag14_groups[] = {
+ "gpio5",
+};
+static const char * const blsp_i2c2_groups[] = {
+ "gpio6", "gpio7",
+};
+static const char * const phase_flag31_groups[] = {
+ "gpio6",
+};
static const char * const blsp_spi3_groups[] = {
"gpio8", "gpio9", "gpio10", "gpio11", "gpio30", "gpio65",
};
@@ -564,24 +607,18 @@ static const char * const wlan1_adc1_groups[] = {
static const char * const atest_usb13_groups[] = {
"gpio8",
};
-static const char * const bimc_dte1_groups[] = {
- "gpio8", "gpio10",
-};
-static const char * const wlan1_adc0_groups[] = {
- "gpio9",
+static const char * const tgu_ch1_groups[] = {
+ "gpio1",
};
-static const char * const atest_usb12_groups[] = {
- "gpio9",
+static const char * const qdss_gpio5_groups[] = {
+ "gpio1", "gpio37",
};
-static const char * const bimc_dte0_groups[] = {
- "gpio9", "gpio11",
+static const char * const atest_gpsadc0_groups[] = {
+ "gpio1",
};
static const char * const blsp_i2c1_groups[] = {
"gpio2", "gpio3",
};
-static const char * const blsp_uim1_groups[] = {
- "gpio2", "gpio3",
-};
static const char * const ddr_bist_groups[] = {
"gpio3", "gpio8", "gpio9", "gpio10",
};
@@ -594,27 +631,27 @@ static const char * const atest_usb1_groups[] = {
static const char * const blsp_spi2_groups[] = {
"gpio4", "gpio5", "gpio6", "gpio7",
};
+static const char * const blsp_uim2_groups[] = {
+ "gpio4", "gpio5",
+};
static const char * const phase_flag3_groups[] = {
"gpio4",
};
-static const char * const phase_flag14_groups[] = {
- "gpio5",
+static const char * const bimc_dte1_groups[] = {
+ "gpio8", "gpio10",
};
-static const char * const blsp_i2c2_groups[] = {
- "gpio6", "gpio7",
+static const char * const wlan1_adc0_groups[] = {
+ "gpio9",
};
-static const char * const blsp_uim2_groups[] = {
- "gpio6", "gpio7",
+static const char * const atest_usb12_groups[] = {
+ "gpio9",
};
-static const char * const phase_flag31_groups[] = {
- "gpio6",
+static const char * const bimc_dte0_groups[] = {
+ "gpio9", "gpio11",
};
static const char * const blsp_i2c3_groups[] = {
"gpio10", "gpio11",
};
-static const char * const atest_gpsadc1_groups[] = {
- "gpio10",
-};
static const char * const wlan2_adc1_groups[] = {
"gpio10",
};
@@ -624,15 +661,15 @@ static const char * const atest_usb11_groups[] = {
static const char * const dbg_out_groups[] = {
"gpio11",
};
-static const char * const atest_gpsadc0_groups[] = {
- "gpio11",
-};
static const char * const wlan2_adc0_groups[] = {
"gpio11",
};
static const char * const atest_usb10_groups[] = {
"gpio11",
};
+static const char * const RCM_MARKER_groups[] = {
+ "gpio12", "gpio13",
+};
static const char * const blsp_spi4_groups[] = {
"gpio12", "gpio13", "gpio14", "gpio15",
};
@@ -642,8 +679,12 @@ static const char * const pri_mi2s_groups[] = {
static const char * const phase_flag26_groups[] = {
"gpio12",
};
-static const char * const qdss_gpio4_groups[] = {
- "gpio12", "gpio36",
+static const char * const qdss_cti_groups[] = {
+ "gpio12", "gpio13", "gpio21", "gpio49", "gpio50", "gpio53", "gpio55",
+ "gpio66",
+};
+static const char * const DP_HOT_groups[] = {
+ "gpio13",
};
static const char * const pri_mi2s_ws_groups[] = {
"gpio13",
@@ -651,9 +692,6 @@ static const char * const pri_mi2s_ws_groups[] = {
static const char * const phase_flag27_groups[] = {
"gpio13",
};
-static const char * const qdss_gpio5_groups[] = {
- "gpio13", "gpio37",
-};
static const char * const blsp_i2c4_groups[] = {
"gpio14", "gpio15",
};
@@ -666,51 +704,23 @@ static const char * const blsp_uart5_groups[] = {
static const char * const blsp_spi5_groups[] = {
"gpio16", "gpio17", "gpio18", "gpio19",
};
+static const char * const blsp_uim5_groups[] = {
+ "gpio16", "gpio17",
+};
static const char * const phase_flag5_groups[] = {
"gpio17",
};
static const char * const blsp_i2c5_groups[] = {
"gpio18", "gpio19",
};
-static const char * const blsp_uim5_groups[] = {
- "gpio18", "gpio19",
-};
static const char * const blsp_spi6_groups[] = {
"gpio20", "gpio21", "gpio22", "gpio23",
};
static const char * const blsp_uart2_groups[] = {
"gpio20", "gpio21", "gpio22", "gpio23",
};
-static const char * const qdss_cti_groups[] = {
- "gpio20", "gpio21", "gpio24", "gpio25", "gpio26", "gpio49", "gpio50",
- "gpio61",
-};
-static const char * const sec_mi2s_groups[] = {
- "gpio24", "gpio25", "gpio26", "gpio27", "gpio62",
-};
-static const char * const sndwire_clk_groups[] = {
- "gpio24",
-};
-static const char * const phase_flag17_groups[] = {
- "gpio24",
-};
-static const char * const vsense_clkout_groups[] = {
- "gpio24",
-};
-static const char * const sndwire_data_groups[] = {
- "gpio25",
-};
-static const char * const phase_flag18_groups[] = {
- "gpio25",
-};
-static const char * const blsp_i2c7_groups[] = {
- "gpio26", "gpio27",
-};
-static const char * const wsa_en1_groups[] = {
- "gpio26",
-};
-static const char * const phase_flag19_groups[] = {
- "gpio26",
+static const char * const blsp_uim6_groups[] = {
+ "gpio20", "gpio21",
};
static const char * const phase_flag11_groups[] = {
"gpio21",
@@ -721,9 +731,6 @@ static const char * const vsense_data0_groups[] = {
static const char * const blsp_i2c6_groups[] = {
"gpio22", "gpio23",
};
-static const char * const blsp_uim6_groups[] = {
- "gpio22", "gpio23",
-};
static const char * const phase_flag12_groups[] = {
"gpio22",
};
@@ -743,15 +750,42 @@ static const char * const BLSP_UART_groups[] = {
"gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", "gpio30",
"gpio31",
};
-static const char * const vfr_1_groups[] = {
- "gpio27",
+static const char * const sec_mi2s_groups[] = {
+ "gpio24", "gpio25", "gpio26", "gpio27", "gpio62",
+};
+static const char * const sndwire_clk_groups[] = {
+ "gpio24",
+};
+static const char * const phase_flag17_groups[] = {
+ "gpio24",
+};
+static const char * const vsense_clkout_groups[] = {
+ "gpio24",
+};
+static const char * const sndwire_data_groups[] = {
+ "gpio25",
+};
+static const char * const phase_flag18_groups[] = {
+ "gpio25",
+};
+static const char * const WSA_SPKR_groups[] = {
+ "gpio26", "gpio27",
};
-static const char * const wsa_en2_groups[] = {
+static const char * const blsp_i2c7_groups[] = {
+ "gpio26", "gpio27",
+};
+static const char * const phase_flag19_groups[] = {
+ "gpio26",
+};
+static const char * const vfr_1_groups[] = {
"gpio27",
};
static const char * const phase_flag20_groups[] = {
"gpio27",
};
+static const char * const NFC_INT_groups[] = {
+ "gpio28",
+};
static const char * const blsp_spi_groups[] = {
"gpio28", "gpio29", "gpio30", "gpio31", "gpio40", "gpio41", "gpio44",
"gpio52",
@@ -762,15 +796,24 @@ static const char * const m_voc_groups[] = {
static const char * const phase_flag21_groups[] = {
"gpio28",
};
+static const char * const NFC_EN_groups[] = {
+ "gpio29",
+};
static const char * const phase_flag22_groups[] = {
"gpio29",
};
+static const char * const NFC_DWL_groups[] = {
+ "gpio30",
+};
static const char * const BLSP_I2C_groups[] = {
"gpio30", "gpio31", "gpio44", "gpio52",
};
static const char * const phase_flag23_groups[] = {
"gpio30",
};
+static const char * const NFC_ESE_groups[] = {
+ "gpio31",
+};
static const char * const pwr_modem_groups[] = {
"gpio31",
};
@@ -778,7 +821,7 @@ static const char * const phase_flag24_groups[] = {
"gpio31",
};
static const char * const qdss_gpio_groups[] = {
- "gpio31", "gpio41", "gpio68", "gpio69",
+ "gpio31", "gpio52", "gpio68", "gpio69",
};
static const char * const cam_mclk_groups[] = {
"gpio32", "gpio33", "gpio34", "gpio35",
@@ -787,7 +830,7 @@ static const char * const pwr_nav_groups[] = {
"gpio32",
};
static const char * const qdss_gpio0_groups[] = {
- "gpio32", "gpio62",
+ "gpio32", "gpio67",
};
static const char * const qspi_data0_groups[] = {
"gpio33",
@@ -814,7 +857,46 @@ static const char * const jitter_bist_groups[] = {
"gpio35",
};
static const char * const qdss_gpio3_groups[] = {
- "gpio35", "gpio65",
+ "gpio35", "gpio56",
+};
+static const char * const qdss_gpio7_groups[] = {
+ "gpio39", "gpio71",
+};
+static const char * const FL_R3LED_groups[] = {
+ "gpio40",
+};
+static const char * const CCI_TIMER0_groups[] = {
+ "gpio40",
+};
+static const char * const FL_STROBE_groups[] = {
+ "gpio41",
+};
+static const char * const CCI_TIMER1_groups[] = {
+ "gpio41",
+};
+static const char * const CAM_LDO1_groups[] = {
+ "gpio42",
+};
+static const char * const mdss_vsync0_groups[] = {
+ "gpio42",
+};
+static const char * const mdss_vsync1_groups[] = {
+ "gpio42",
+};
+static const char * const mdss_vsync2_groups[] = {
+ "gpio42",
+};
+static const char * const mdss_vsync3_groups[] = {
+ "gpio42",
+};
+static const char * const qdss_gpio9_groups[] = {
+ "gpio42", "gpio76",
+};
+static const char * const CAM_IRQ_groups[] = {
+ "gpio43",
+};
+static const char * const atest_usb2_groups[] = {
+ "gpio35",
};
static const char * const cci_i2c_groups[] = {
"gpio36", "gpio37", "gpio38", "gpio39",
@@ -825,14 +907,17 @@ static const char * const pll_bypassnl_groups[] = {
static const char * const atest_tsens_groups[] = {
"gpio36",
};
+static const char * const atest_usb21_groups[] = {
+ "gpio36",
+};
static const char * const pll_reset_groups[] = {
"gpio37",
};
-static const char * const qdss_gpio9_groups[] = {
- "gpio42", "gpio76",
+static const char * const atest_usb23_groups[] = {
+ "gpio37",
};
-static const char * const CAM_IRQ_groups[] = {
- "gpio43",
+static const char * const qdss_gpio6_groups[] = {
+ "gpio38", "gpio70",
};
static const char * const CCI_TIMER3_groups[] = {
"gpio43",
@@ -846,7 +931,7 @@ static const char * const qspi_cs_groups[] = {
static const char * const qdss_gpio10_groups[] = {
"gpio43", "gpio77",
};
-static const char * const CAM4_STANDBY_groups[] = {
+static const char * const CAM3_STANDBY_groups[] = {
"gpio44",
};
static const char * const CCI_TIMER4_groups[] = {
@@ -855,7 +940,7 @@ static const char * const CCI_TIMER4_groups[] = {
static const char * const qdss_gpio11_groups[] = {
"gpio44", "gpio79",
};
-static const char * const bt_reset_groups[] = {
+static const char * const CAM_LDO2_groups[] = {
"gpio45",
};
static const char * const cci_async_groups[] = {
@@ -864,49 +949,13 @@ static const char * const cci_async_groups[] = {
static const char * const qdss_gpio12_groups[] = {
"gpio45", "gpio80",
};
-static const char * const CAM1_RST_groups[] = {
+static const char * const CAM0_RST_groups[] = {
"gpio46",
};
-static const char * const qdss_gpio6_groups[] = {
- "gpio38", "gpio70",
-};
-static const char * const qdss_gpio7_groups[] = {
- "gpio39", "gpio71",
-};
-static const char * const FL_FRONT_groups[] = {
- "gpio40",
-};
-static const char * const CCI_TIMER0_groups[] = {
- "gpio40",
-};
-static const char * const qdss_gpio8_groups[] = {
- "gpio40", "gpio75",
-};
-static const char * const FL_STROBE_groups[] = {
- "gpio41",
-};
-static const char * const CCI_TIMER1_groups[] = {
- "gpio41",
-};
-static const char * const LASER_CE_groups[] = {
- "gpio42",
-};
-static const char * const mdss_vsync0_groups[] = {
- "gpio42",
-};
-static const char * const mdss_vsync1_groups[] = {
- "gpio42",
-};
-static const char * const mdss_vsync2_groups[] = {
- "gpio42",
-};
-static const char * const mdss_vsync3_groups[] = {
- "gpio42",
-};
static const char * const qdss_gpio13_groups[] = {
"gpio46", "gpio78",
};
-static const char * const CAM2_RST_groups[] = {
+static const char * const CAM1_RST_groups[] = {
"gpio47",
};
static const char * const qspi_clk_groups[] = {
@@ -918,9 +967,6 @@ static const char * const phase_flag30_groups[] = {
static const char * const qdss_gpio14_groups[] = {
"gpio47", "gpio72",
};
-static const char * const CAM3_RST_groups[] = {
- "gpio48",
-};
static const char * const qspi_resetn_groups[] = {
"gpio48",
};
@@ -930,19 +976,19 @@ static const char * const phase_flag1_groups[] = {
static const char * const qdss_gpio15_groups[] = {
"gpio48", "gpio73",
};
-static const char * const CAM1_STANDBY_groups[] = {
+static const char * const CAM0_STANDBY_groups[] = {
"gpio49",
};
static const char * const phase_flag2_groups[] = {
"gpio49",
};
-static const char * const CAM2_STANDBY_groups[] = {
+static const char * const CAM1_STANDBY_groups[] = {
"gpio50",
};
static const char * const phase_flag9_groups[] = {
"gpio50",
};
-static const char * const CAM3_STANDBY_groups[] = {
+static const char * const CAM2_STANDBY_groups[] = {
"gpio51",
};
static const char * const qspi_data3_groups[] = {
@@ -951,7 +997,10 @@ static const char * const qspi_data3_groups[] = {
static const char * const phase_flag15_groups[] = {
"gpio51",
};
-static const char * const CAM4_RST_groups[] = {
+static const char * const qdss_gpio8_groups[] = {
+ "gpio51", "gpio75",
+};
+static const char * const CAM3_RST_groups[] = {
"gpio52",
};
static const char * const CCI_TIMER2_groups[] = {
@@ -960,32 +1009,41 @@ static const char * const CCI_TIMER2_groups[] = {
static const char * const phase_flag16_groups[] = {
"gpio52",
};
+static const char * const LCD0_RESET_groups[] = {
+ "gpio53",
+};
static const char * const phase_flag6_groups[] = {
"gpio53",
};
-static const char * const RCM_MARKER2_groups[] = {
+static const char * const SD_CARD_groups[] = {
"gpio54",
};
static const char * const phase_flag29_groups[] = {
"gpio54",
};
-static const char * const SS_SWITCH_groups[] = {
- "gpio55", "gpio56",
+static const char * const DP_EN_groups[] = {
+ "gpio55",
};
static const char * const phase_flag25_groups[] = {
"gpio55",
};
+static const char * const USBC_ORIENTATION_groups[] = {
+ "gpio56",
+};
static const char * const phase_flag10_groups[] = {
"gpio56",
};
+static const char * const atest_usb20_groups[] = {
+ "gpio56",
+};
static const char * const gcc_gp1_groups[] = {
"gpio57", "gpio78",
};
static const char * const phase_flag4_groups[] = {
"gpio57",
};
-static const char * const USB_DIR_groups[] = {
- "gpio58",
+static const char * const atest_usb22_groups[] = {
+ "gpio57",
};
static const char * const USB_PHY_groups[] = {
"gpio58",
@@ -1005,11 +1063,8 @@ static const char * const gcc_gp3_groups[] = {
static const char * const atest_char3_groups[] = {
"gpio59",
};
-static const char * const Lcd_mode_groups[] = {
- "gpio60",
-};
-static const char * const EDP_HOT_groups[] = {
- "gpio60",
+static const char * const FORCE_TOUCH_groups[] = {
+ "gpio60", "gpio73",
};
static const char * const cri_trng0_groups[] = {
"gpio60",
@@ -1023,6 +1078,9 @@ static const char * const cri_trng1_groups[] = {
static const char * const atest_char1_groups[] = {
"gpio61",
};
+static const char * const AUDIO_USBC_groups[] = {
+ "gpio62",
+};
static const char * const audio_ref_groups[] = {
"gpio62",
};
@@ -1038,20 +1096,20 @@ static const char * const atest_char0_groups[] = {
static const char * const US_EURO_groups[] = {
"gpio63",
};
-static const char * const KEY_FOCUS_groups[] = {
+static const char * const LCD_BACKLIGHT_groups[] = {
"gpio64",
};
-static const char * const NAV_PPS_groups[] = {
- "gpio64", "gpio65", "gpio98", "gpio98",
-};
static const char * const blsp_spi8_groups[] = {
"gpio64", "gpio76",
};
static const char * const sp_cmu_groups[] = {
"gpio64",
};
-static const char * const SLT_PWR_groups[] = {
- "gpio65",
+static const char * const NAV_PPS_groups[] = {
+ "gpio65", "gpio65", "gpio80", "gpio80", "gpio98", "gpio98",
+};
+static const char * const GPS_TX_groups[] = {
+ "gpio65", "gpio80", "gpio98",
};
static const char * const adsp_ext_groups[] = {
"gpio65",
@@ -1059,12 +1117,9 @@ static const char * const adsp_ext_groups[] = {
static const char * const TS_RESET_groups[] = {
"gpio66",
};
-static const char * const TS_INT_groups[] = {
- "gpio67",
-};
static const char * const ssc_irq_groups[] = {
- "gpio68", "gpio69", "gpio70", "gpio71", "gpio72", "gpio73", "gpio74",
- "gpio75", "gpio76", "gpio77",
+ "gpio67", "gpio68", "gpio69", "gpio70", "gpio71", "gpio72", "gpio74",
+ "gpio75", "gpio76",
};
static const char * const isense_dbg_groups[] = {
"gpio68",
@@ -1084,12 +1139,18 @@ static const char * const tsense_pwm1_groups[] = {
static const char * const tsense_pwm2_groups[] = {
"gpio71",
};
-static const char * const HAPTICS_PWM_groups[] = {
+static const char * const SENSOR_RST_groups[] = {
+ "gpio77",
+};
+static const char * const WMSS_RESETN_groups[] = {
"gpio78",
};
-static const char * const wmss_reset_groups[] = {
+static const char * const HAPTICS_PWM_groups[] = {
"gpio79",
};
+static const char * const GPS_eLNA_groups[] = {
+ "gpio80",
+};
static const char * const mss_lte_groups[] = {
"gpio81", "gpio82",
};
@@ -1123,9 +1184,6 @@ static const char * const uim_batt_groups[] = {
static const char * const pa_indicator_groups[] = {
"gpio92",
};
-static const char * const ssbi_gnss_groups[] = {
- "gpio94",
-};
static const char * const ldo_en_groups[] = {
"gpio97",
};
@@ -1141,84 +1199,93 @@ static const char * const qlink_enable_groups[] = {
static const char * const prng_rosc_groups[] = {
"gpio102",
};
+static const char * const LCD_PWR_groups[] = {
+ "gpio113",
+};
static const struct msm_function msmfalcon_functions[] = {
FUNCTION(blsp_spi1),
FUNCTION(gpio),
+ FUNCTION(blsp_uim1),
FUNCTION(tgu_ch0),
- FUNCTION(tgu_ch1),
+ FUNCTION(qdss_gpio4),
+ FUNCTION(atest_gpsadc1),
FUNCTION(blsp_uart1),
+ FUNCTION(SMB_STAT),
+ FUNCTION(phase_flag14),
+ FUNCTION(blsp_i2c2),
+ FUNCTION(phase_flag31),
FUNCTION(blsp_spi3),
FUNCTION(wlan1_adc1),
FUNCTION(atest_usb13),
- FUNCTION(bimc_dte1),
- FUNCTION(wlan1_adc0),
- FUNCTION(atest_usb12),
- FUNCTION(bimc_dte0),
+ FUNCTION(tgu_ch1),
+ FUNCTION(qdss_gpio5),
+ FUNCTION(atest_gpsadc0),
FUNCTION(blsp_i2c1),
- FUNCTION(blsp_uim1),
FUNCTION(ddr_bist),
FUNCTION(atest_tsens2),
FUNCTION(atest_usb1),
FUNCTION(blsp_spi2),
- FUNCTION(phase_flag3),
- FUNCTION(phase_flag14),
- FUNCTION(blsp_i2c2),
FUNCTION(blsp_uim2),
- FUNCTION(phase_flag31),
+ FUNCTION(phase_flag3),
+ FUNCTION(bimc_dte1),
+ FUNCTION(wlan1_adc0),
+ FUNCTION(atest_usb12),
+ FUNCTION(bimc_dte0),
FUNCTION(blsp_i2c3),
- FUNCTION(atest_gpsadc1),
FUNCTION(wlan2_adc1),
FUNCTION(atest_usb11),
FUNCTION(dbg_out),
- FUNCTION(atest_gpsadc0),
FUNCTION(wlan2_adc0),
FUNCTION(atest_usb10),
+ FUNCTION(RCM_MARKER),
FUNCTION(blsp_spi4),
FUNCTION(pri_mi2s),
FUNCTION(phase_flag26),
- FUNCTION(qdss_gpio4),
+ FUNCTION(qdss_cti),
+ FUNCTION(DP_HOT),
FUNCTION(pri_mi2s_ws),
FUNCTION(phase_flag27),
- FUNCTION(qdss_gpio5),
FUNCTION(blsp_i2c4),
FUNCTION(phase_flag28),
FUNCTION(blsp_uart5),
FUNCTION(blsp_spi5),
+ FUNCTION(blsp_uim5),
FUNCTION(phase_flag5),
FUNCTION(blsp_i2c5),
- FUNCTION(blsp_uim5),
FUNCTION(blsp_spi6),
FUNCTION(blsp_uart2),
- FUNCTION(qdss_cti),
- FUNCTION(sec_mi2s),
- FUNCTION(sndwire_clk),
- FUNCTION(phase_flag17),
- FUNCTION(vsense_clkout),
- FUNCTION(sndwire_data),
- FUNCTION(phase_flag18),
- FUNCTION(blsp_i2c7),
- FUNCTION(wsa_en1),
- FUNCTION(phase_flag19),
+ FUNCTION(blsp_uim6),
FUNCTION(phase_flag11),
FUNCTION(vsense_data0),
FUNCTION(blsp_i2c6),
- FUNCTION(blsp_uim6),
FUNCTION(phase_flag12),
FUNCTION(vsense_data1),
FUNCTION(phase_flag13),
FUNCTION(vsense_mode),
FUNCTION(blsp_spi7),
FUNCTION(BLSP_UART),
+ FUNCTION(sec_mi2s),
+ FUNCTION(sndwire_clk),
+ FUNCTION(phase_flag17),
+ FUNCTION(vsense_clkout),
+ FUNCTION(sndwire_data),
+ FUNCTION(phase_flag18),
+ FUNCTION(WSA_SPKR),
+ FUNCTION(blsp_i2c7),
+ FUNCTION(phase_flag19),
FUNCTION(vfr_1),
- FUNCTION(wsa_en2),
FUNCTION(phase_flag20),
+ FUNCTION(NFC_INT),
FUNCTION(blsp_spi),
FUNCTION(m_voc),
FUNCTION(phase_flag21),
+ FUNCTION(NFC_EN),
FUNCTION(phase_flag22),
+ FUNCTION(NFC_DWL),
FUNCTION(BLSP_I2C),
FUNCTION(phase_flag23),
+ FUNCTION(NFC_ESE),
FUNCTION(pwr_modem),
FUNCTION(phase_flag24),
FUNCTION(qdss_gpio),
@@ -1234,88 +1301,92 @@ static const struct msm_function msmfalcon_functions[] = {
FUNCTION(qspi_data2),
FUNCTION(jitter_bist),
FUNCTION(qdss_gpio3),
+ FUNCTION(qdss_gpio7),
+ FUNCTION(FL_R3LED),
+ FUNCTION(CCI_TIMER0),
+ FUNCTION(FL_STROBE),
+ FUNCTION(CCI_TIMER1),
+ FUNCTION(CAM_LDO1),
+ FUNCTION(mdss_vsync0),
+ FUNCTION(mdss_vsync1),
+ FUNCTION(mdss_vsync2),
+ FUNCTION(mdss_vsync3),
+ FUNCTION(qdss_gpio9),
+ FUNCTION(CAM_IRQ),
+ FUNCTION(atest_usb2),
FUNCTION(cci_i2c),
FUNCTION(pll_bypassnl),
FUNCTION(atest_tsens),
+ FUNCTION(atest_usb21),
FUNCTION(pll_reset),
- FUNCTION(qdss_gpio9),
- FUNCTION(CAM_IRQ),
+ FUNCTION(atest_usb23),
+ FUNCTION(qdss_gpio6),
FUNCTION(CCI_TIMER3),
FUNCTION(CCI_ASYNC),
FUNCTION(qspi_cs),
FUNCTION(qdss_gpio10),
- FUNCTION(CAM4_STANDBY),
+ FUNCTION(CAM3_STANDBY),
FUNCTION(CCI_TIMER4),
FUNCTION(qdss_gpio11),
- FUNCTION(bt_reset),
+ FUNCTION(CAM_LDO2),
FUNCTION(cci_async),
FUNCTION(qdss_gpio12),
- FUNCTION(CAM1_RST),
- FUNCTION(qdss_gpio6),
- FUNCTION(qdss_gpio7),
- FUNCTION(FL_FRONT),
- FUNCTION(CCI_TIMER0),
- FUNCTION(qdss_gpio8),
- FUNCTION(FL_STROBE),
- FUNCTION(CCI_TIMER1),
- FUNCTION(LASER_CE),
- FUNCTION(mdss_vsync0),
- FUNCTION(mdss_vsync1),
- FUNCTION(mdss_vsync2),
- FUNCTION(mdss_vsync3),
+ FUNCTION(CAM0_RST),
FUNCTION(qdss_gpio13),
- FUNCTION(CAM2_RST),
+ FUNCTION(CAM1_RST),
FUNCTION(qspi_clk),
FUNCTION(phase_flag30),
FUNCTION(qdss_gpio14),
- FUNCTION(CAM3_RST),
FUNCTION(qspi_resetn),
FUNCTION(phase_flag1),
FUNCTION(qdss_gpio15),
- FUNCTION(CAM1_STANDBY),
+ FUNCTION(CAM0_STANDBY),
FUNCTION(phase_flag2),
- FUNCTION(CAM2_STANDBY),
+ FUNCTION(CAM1_STANDBY),
FUNCTION(phase_flag9),
- FUNCTION(CAM3_STANDBY),
+ FUNCTION(CAM2_STANDBY),
FUNCTION(qspi_data3),
FUNCTION(phase_flag15),
- FUNCTION(CAM4_RST),
+ FUNCTION(qdss_gpio8),
+ FUNCTION(CAM3_RST),
FUNCTION(CCI_TIMER2),
FUNCTION(phase_flag16),
+ FUNCTION(LCD0_RESET),
FUNCTION(phase_flag6),
- FUNCTION(RCM_MARKER2),
+ FUNCTION(SD_CARD),
FUNCTION(phase_flag29),
- FUNCTION(SS_SWITCH),
+ FUNCTION(DP_EN),
FUNCTION(phase_flag25),
+ FUNCTION(USBC_ORIENTATION),
FUNCTION(phase_flag10),
+ FUNCTION(atest_usb20),
FUNCTION(gcc_gp1),
FUNCTION(phase_flag4),
- FUNCTION(USB_DIR),
+ FUNCTION(atest_usb22),
FUNCTION(USB_PHY),
FUNCTION(gcc_gp2),
FUNCTION(atest_char),
FUNCTION(mdp_vsync),
FUNCTION(gcc_gp3),
FUNCTION(atest_char3),
- FUNCTION(Lcd_mode),
- FUNCTION(EDP_HOT),
+ FUNCTION(FORCE_TOUCH),
FUNCTION(cri_trng0),
FUNCTION(atest_char2),
FUNCTION(cri_trng1),
FUNCTION(atest_char1),
+ FUNCTION(AUDIO_USBC),
FUNCTION(audio_ref),
FUNCTION(MDP_VSYNC),
FUNCTION(cri_trng),
FUNCTION(atest_char0),
FUNCTION(US_EURO),
- FUNCTION(KEY_FOCUS),
- FUNCTION(NAV_PPS),
+ FUNCTION(LCD_BACKLIGHT),
FUNCTION(blsp_spi8),
FUNCTION(sp_cmu),
- FUNCTION(SLT_PWR),
+ FUNCTION(NAV_PPS),
+ FUNCTION(GPS_TX),
FUNCTION(adsp_ext),
FUNCTION(TS_RESET),
- FUNCTION(TS_INT),
FUNCTION(ssc_irq),
FUNCTION(isense_dbg),
FUNCTION(phase_flag0),
@@ -1323,8 +1394,10 @@ static const struct msm_function msmfalcon_functions[] = {
FUNCTION(phase_flag8),
FUNCTION(tsense_pwm1),
FUNCTION(tsense_pwm2),
+ FUNCTION(SENSOR_RST),
+ FUNCTION(WMSS_RESETN),
FUNCTION(HAPTICS_PWM),
- FUNCTION(wmss_reset),
+ FUNCTION(GPS_eLNA),
FUNCTION(mss_lte),
FUNCTION(uim2_data),
FUNCTION(uim2_clk),
@@ -1336,153 +1409,155 @@ static const struct msm_function msmfalcon_functions[] = {
FUNCTION(uim1_present),
FUNCTION(uim_batt),
FUNCTION(pa_indicator),
- FUNCTION(ssbi_gnss),
FUNCTION(ldo_en),
FUNCTION(ldo_update),
FUNCTION(qlink_request),
FUNCTION(qlink_enable),
FUNCTION(prng_rosc),
+ FUNCTION(LCD_PWR),
};
static const struct msm_pingroup msmfalcon_groups[] = {
- PINGROUP(0, SOUTH, blsp_spi1, blsp_uart1, tgu_ch0, NA, NA, NA, NA, NA,
- NA),
- PINGROUP(1, SOUTH, blsp_spi1, blsp_uart1, tgu_ch1, NA, NA, NA, NA, NA,
- NA),
- PINGROUP(2, SOUTH, blsp_spi1, blsp_uart1, blsp_i2c1, blsp_uim1, NA, NA,
- NA, NA, NA),
- PINGROUP(3, SOUTH, blsp_spi1, blsp_uart1, blsp_i2c1, blsp_uim1,
- ddr_bist, NA, atest_tsens2, atest_usb1, NA),
- PINGROUP(4, WEST, blsp_spi2, phase_flag3, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(5, WEST, blsp_spi2, phase_flag14, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(6, WEST, blsp_spi2, blsp_i2c2, blsp_uim2, phase_flag31, NA,
- NA, NA, NA, NA),
- PINGROUP(7, WEST, blsp_spi2, blsp_i2c2, blsp_uim2, NA, NA, NA, NA, NA,
- NA),
- PINGROUP(8, WEST, blsp_spi3, ddr_bist, NA, NA, wlan1_adc1, atest_usb13,
- bimc_dte1, NA, NA),
- PINGROUP(9, WEST, blsp_spi3, ddr_bist, NA, NA, wlan1_adc0, atest_usb12,
- bimc_dte0, NA, NA),
- PINGROUP(10, WEST, blsp_spi3, blsp_i2c3, ddr_bist, NA, atest_gpsadc1,
- wlan2_adc1, atest_usb11, bimc_dte1, NA),
- PINGROUP(11, WEST, blsp_spi3, blsp_i2c3, dbg_out, atest_gpsadc0,
- wlan2_adc0, atest_usb10, bimc_dte0, NA, NA),
- PINGROUP(12, SOUTH, blsp_spi4, pri_mi2s, phase_flag26, qdss_gpio4, NA,
+ PINGROUP(0, SOUTH, blsp_spi1, blsp_uart1, blsp_uim1, tgu_ch0, NA, NA,
+ qdss_gpio4, atest_gpsadc1, NA),
+ PINGROUP(1, SOUTH, blsp_spi1, blsp_uart1, blsp_uim1, tgu_ch1, NA, NA,
+ qdss_gpio5, atest_gpsadc0, NA),
+ PINGROUP(2, SOUTH, blsp_spi1, blsp_uart1, blsp_i2c1, NA, NA, NA, NA,
+ NA, NA),
+ PINGROUP(3, SOUTH, blsp_spi1, blsp_uart1, blsp_i2c1, ddr_bist, NA, NA,
+ atest_tsens2, atest_usb1, NA),
+ PINGROUP(4, NORTH, blsp_spi2, blsp_uim2, NA, phase_flag3, NA, NA, NA,
+ NA, NA),
+ PINGROUP(5, SOUTH, blsp_spi2, blsp_uim2, NA, phase_flag14, NA, NA, NA,
+ NA, NA),
+ PINGROUP(6, SOUTH, blsp_spi2, blsp_i2c2, NA, phase_flag31, NA, NA, NA,
+ NA, NA),
+ PINGROUP(7, SOUTH, blsp_spi2, blsp_i2c2, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(8, NORTH, blsp_spi3, ddr_bist, NA, NA, NA, wlan1_adc1,
+ atest_usb13, bimc_dte1, NA),
+ PINGROUP(9, NORTH, blsp_spi3, ddr_bist, NA, NA, NA, wlan1_adc0,
+ atest_usb12, bimc_dte0, NA),
+ PINGROUP(10, NORTH, blsp_spi3, blsp_i2c3, ddr_bist, NA, NA, wlan2_adc1,
+ atest_usb11, bimc_dte1, NA),
+ PINGROUP(11, NORTH, blsp_spi3, blsp_i2c3, NA, dbg_out, wlan2_adc0,
+ atest_usb10, bimc_dte0, NA, NA),
+ PINGROUP(12, NORTH, blsp_spi4, pri_mi2s, NA, phase_flag26, qdss_cti,
NA, NA, NA, NA),
- PINGROUP(13, SOUTH, blsp_spi4, pri_mi2s_ws, NA, phase_flag27,
- qdss_gpio5, NA, NA, NA, NA),
- PINGROUP(14, SOUTH, blsp_spi4, blsp_i2c4, pri_mi2s, phase_flag28, NA,
+ PINGROUP(13, NORTH, blsp_spi4, DP_HOT, pri_mi2s_ws, NA, NA,
+ phase_flag27, qdss_cti, NA, NA),
+ PINGROUP(14, NORTH, blsp_spi4, blsp_i2c4, pri_mi2s, NA, phase_flag28,
NA, NA, NA, NA),
- PINGROUP(15, SOUTH, blsp_spi4, blsp_i2c4, pri_mi2s, NA, NA, NA, NA, NA,
+ PINGROUP(15, NORTH, blsp_spi4, blsp_i2c4, pri_mi2s, NA, NA, NA, NA, NA,
NA),
- PINGROUP(16, WEST, blsp_uart5, blsp_spi5, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(17, WEST, blsp_uart5, blsp_spi5, phase_flag5, NA, NA, NA, NA,
+ PINGROUP(16, CENTER, blsp_uart5, blsp_spi5, blsp_uim5, NA, NA, NA, NA,
NA, NA),
- PINGROUP(18, WEST, blsp_uart5, blsp_spi5, blsp_i2c5, blsp_uim5, NA, NA,
- NA, NA, NA),
- PINGROUP(19, WEST, blsp_uart5, blsp_spi5, blsp_i2c5, blsp_uim5, NA, NA,
- NA, NA, NA),
- PINGROUP(20, WEST, blsp_spi6, blsp_uart2, NA, qdss_cti, NA, NA, NA, NA,
- NA),
- PINGROUP(21, WEST, blsp_spi6, blsp_uart2, phase_flag11, qdss_cti,
- vsense_data0, NA, NA, NA, NA),
- PINGROUP(22, WEST, blsp_spi6, blsp_uart2, blsp_i2c6, blsp_uim6,
+ PINGROUP(17, CENTER, blsp_uart5, blsp_spi5, blsp_uim5, NA, phase_flag5,
+ NA, NA, NA, NA),
+ PINGROUP(18, CENTER, blsp_uart5, blsp_spi5, blsp_i2c5, NA, NA, NA, NA,
+ NA, NA),
+ PINGROUP(19, CENTER, blsp_uart5, blsp_spi5, blsp_i2c5, NA, NA, NA, NA,
+ NA, NA),
+ PINGROUP(20, SOUTH, blsp_spi6, blsp_uart2, blsp_uim6, NA, NA, NA, NA,
+ NA, NA),
+ PINGROUP(21, SOUTH, blsp_spi6, blsp_uart2, blsp_uim6, NA, phase_flag11,
+ qdss_cti, vsense_data0, NA, NA),
+ PINGROUP(22, CENTER, blsp_spi6, blsp_uart2, blsp_i2c6, NA,
phase_flag12, vsense_data1, NA, NA, NA),
- PINGROUP(23, WEST, blsp_spi6, blsp_uart2, blsp_i2c6, blsp_uim6,
+ PINGROUP(23, CENTER, blsp_spi6, blsp_uart2, blsp_i2c6, NA,
phase_flag13, vsense_mode, NA, NA, NA),
- PINGROUP(24, WEST, blsp_spi7, BLSP_UART, sec_mi2s, sndwire_clk, NA,
- phase_flag17, qdss_cti, vsense_clkout, NA),
- PINGROUP(25, WEST, blsp_spi7, BLSP_UART, sec_mi2s, sndwire_data, NA,
- phase_flag18, qdss_cti, NA, NA),
- PINGROUP(26, WEST, blsp_spi7, BLSP_UART, blsp_i2c7, sec_mi2s, wsa_en1,
- phase_flag19, qdss_cti, NA, NA),
- PINGROUP(27, WEST, blsp_spi7, BLSP_UART, blsp_i2c7, vfr_1, sec_mi2s,
- wsa_en2, phase_flag20, NA, NA),
- PINGROUP(28, SOUTH, blsp_spi, BLSP_UART, m_voc, phase_flag21, NA, NA,
+ PINGROUP(24, NORTH, blsp_spi7, BLSP_UART, sec_mi2s, sndwire_clk, NA,
+ NA, phase_flag17, vsense_clkout, NA),
+ PINGROUP(25, NORTH, blsp_spi7, BLSP_UART, sec_mi2s, sndwire_data, NA,
+ NA, phase_flag18, NA, NA),
+ PINGROUP(26, NORTH, blsp_spi7, BLSP_UART, blsp_i2c7, sec_mi2s, NA,
+ phase_flag19, NA, NA, NA),
+ PINGROUP(27, NORTH, blsp_spi7, BLSP_UART, blsp_i2c7, vfr_1, sec_mi2s,
+ NA, phase_flag20, NA, NA),
+ PINGROUP(28, CENTER, blsp_spi, BLSP_UART, m_voc, NA, phase_flag21, NA,
NA, NA, NA),
- PINGROUP(29, SOUTH, blsp_spi, BLSP_UART, NA, phase_flag22, NA, NA, NA,
+ PINGROUP(29, CENTER, blsp_spi, BLSP_UART, NA, NA, phase_flag22, NA, NA,
NA, NA),
- PINGROUP(30, SOUTH, blsp_spi, BLSP_UART, BLSP_I2C, blsp_spi3,
- phase_flag23, NA, NA, NA, NA),
- PINGROUP(31, SOUTH, blsp_spi, BLSP_UART, BLSP_I2C, pwr_modem,
- phase_flag24, qdss_gpio, NA, NA, NA),
- PINGROUP(32, SOUTH, cam_mclk, pwr_nav, NA, qdss_gpio0, NA, NA, NA, NA,
+ PINGROUP(30, CENTER, blsp_spi, BLSP_UART, BLSP_I2C, blsp_spi3, NA,
+ phase_flag23, NA, NA, NA),
+ PINGROUP(31, CENTER, blsp_spi, BLSP_UART, BLSP_I2C, pwr_modem, NA,
+ phase_flag24, qdss_gpio, NA, NA),
+ PINGROUP(32, SOUTH, cam_mclk, pwr_nav, NA, NA, qdss_gpio0, NA, NA, NA,
NA),
- PINGROUP(33, SOUTH, cam_mclk, qspi_data0, pwr_crypto, NA, qdss_gpio1,
- NA, NA, NA, NA),
- PINGROUP(34, SOUTH, cam_mclk, qspi_data1, agera_pll, NA, qdss_gpio2,
- NA, NA, NA, NA),
- PINGROUP(35, SOUTH, cam_mclk, qspi_data2, jitter_bist, NA, qdss_gpio3,
- NA, NA, NA, NA),
- PINGROUP(36, SOUTH, cci_i2c, pll_bypassnl, agera_pll, NA, qdss_gpio4,
- atest_tsens, NA, NA, NA),
- PINGROUP(37, SOUTH, cci_i2c, pll_reset, NA, qdss_gpio5, NA, NA, NA, NA,
- NA),
- PINGROUP(38, SOUTH, cci_i2c, NA, qdss_gpio6, NA, NA, NA, NA, NA, NA),
- PINGROUP(39, SOUTH, cci_i2c, NA, qdss_gpio7, NA, NA, NA, NA, NA, NA),
- PINGROUP(40, SOUTH, CCI_TIMER0, NA, blsp_spi, NA, qdss_gpio8, NA, NA,
- NA, NA),
- PINGROUP(41, SOUTH, CCI_TIMER1, NA, blsp_spi, NA, qdss_gpio, NA, NA,
- NA, NA),
+ PINGROUP(33, SOUTH, cam_mclk, qspi_data0, pwr_crypto, NA, NA,
+ qdss_gpio1, NA, NA, NA),
+ PINGROUP(34, SOUTH, cam_mclk, qspi_data1, agera_pll, NA, NA,
+ qdss_gpio2, NA, NA, NA),
+ PINGROUP(35, SOUTH, cam_mclk, qspi_data2, jitter_bist, NA, NA,
+ qdss_gpio3, NA, atest_usb2, NA),
+ PINGROUP(36, SOUTH, cci_i2c, pll_bypassnl, agera_pll, NA, NA,
+ qdss_gpio4, atest_tsens, atest_usb21, NA),
+ PINGROUP(37, SOUTH, cci_i2c, pll_reset, NA, NA, qdss_gpio5,
+ atest_usb23, NA, NA, NA),
+ PINGROUP(38, SOUTH, cci_i2c, NA, NA, qdss_gpio6, NA, NA, NA, NA, NA),
+ PINGROUP(39, SOUTH, cci_i2c, NA, NA, qdss_gpio7, NA, NA, NA, NA, NA),
+ PINGROUP(40, SOUTH, CCI_TIMER0, NA, blsp_spi, NA, NA, NA, NA, NA, NA),
+ PINGROUP(41, SOUTH, CCI_TIMER1, NA, blsp_spi, NA, NA, NA, NA, NA, NA),
PINGROUP(42, SOUTH, mdss_vsync0, mdss_vsync1, mdss_vsync2, mdss_vsync3,
- NA, qdss_gpio9, NA, NA, NA),
- PINGROUP(43, SOUTH, CCI_TIMER3, CCI_ASYNC, qspi_cs, NA, qdss_gpio10,
- NA, NA, NA, NA),
- PINGROUP(44, SOUTH, CCI_TIMER4, CCI_ASYNC, blsp_spi, BLSP_I2C, NA,
- qdss_gpio11, NA, NA, NA),
- PINGROUP(45, SOUTH, cci_async, NA, qdss_gpio12, NA, NA, NA, NA, NA, NA),
- PINGROUP(46, SOUTH, blsp_spi1, NA, qdss_gpio13, NA, NA, NA, NA, NA, NA),
- PINGROUP(47, SOUTH, qspi_clk, phase_flag30, qdss_gpio14, NA, NA, NA,
- NA, NA, NA),
- PINGROUP(48, SOUTH, qspi_resetn, phase_flag1, qdss_gpio15, NA, NA, NA,
+ NA, NA, qdss_gpio9, NA, NA),
+ PINGROUP(43, SOUTH, CCI_TIMER3, CCI_ASYNC, qspi_cs, NA, NA,
+ qdss_gpio10, NA, NA, NA),
+ PINGROUP(44, SOUTH, CCI_TIMER4, CCI_ASYNC, blsp_spi, BLSP_I2C, NA, NA,
+ qdss_gpio11, NA, NA),
+ PINGROUP(45, SOUTH, cci_async, NA, NA, qdss_gpio12, NA, NA, NA, NA, NA),
+ PINGROUP(46, SOUTH, blsp_spi1, NA, NA, qdss_gpio13, NA, NA, NA, NA, NA),
+ PINGROUP(47, SOUTH, qspi_clk, NA, phase_flag30, qdss_gpio14, NA, NA,
NA, NA, NA),
- PINGROUP(49, SOUTH, phase_flag2, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(50, SOUTH, qspi_cs, phase_flag9, qdss_cti, NA, NA, NA, NA, NA,
+ PINGROUP(48, SOUTH, NA, phase_flag1, qdss_gpio15, NA, NA, NA, NA, NA,
NA),
- PINGROUP(51, SOUTH, qspi_data3, phase_flag15, NA, NA, NA, NA, NA, NA,
- NA),
- PINGROUP(52, EAST, CCI_TIMER2, blsp_spi, BLSP_I2C, phase_flag16, NA,
- NA, NA, NA, NA),
- PINGROUP(53, EAST, phase_flag6, NA, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(54, EAST, NA, phase_flag29, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(55, WEST, phase_flag25, NA, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(56, WEST, phase_flag10, NA, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(57, SOUTH, gcc_gp1, phase_flag4, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(58, SOUTH, USB_PHY, gcc_gp2, NA, atest_char, NA, NA, NA, NA,
+ PINGROUP(49, SOUTH, NA, phase_flag2, qdss_cti, NA, NA, NA, NA, NA, NA),
+ PINGROUP(50, SOUTH, qspi_cs, NA, phase_flag9, qdss_cti, NA, NA, NA, NA,
NA),
- PINGROUP(59, EAST, mdp_vsync, gcc_gp3, NA, atest_char3, NA, NA, NA, NA,
- NA),
- PINGROUP(60, EAST, EDP_HOT, cri_trng0, NA, atest_char2, NA, NA, NA, NA,
- NA),
- PINGROUP(61, EAST, pri_mi2s, cri_trng1, NA, qdss_cti, atest_char1, NA,
+ PINGROUP(51, SOUTH, qspi_data3, NA, phase_flag15, qdss_gpio8, NA, NA,
+ NA, NA, NA),
+ PINGROUP(52, SOUTH, CCI_TIMER2, blsp_spi, BLSP_I2C, NA, phase_flag16,
+ qdss_gpio, NA, NA, NA),
+ PINGROUP(53, NORTH, NA, phase_flag6, qdss_cti, NA, NA, NA, NA, NA, NA),
+ PINGROUP(54, NORTH, NA, NA, phase_flag29, NA, NA, NA, NA, NA, NA),
+ PINGROUP(55, SOUTH, NA, phase_flag25, qdss_cti, NA, NA, NA, NA, NA, NA),
+ PINGROUP(56, SOUTH, NA, phase_flag10, qdss_gpio3, NA, atest_usb20, NA,
NA, NA, NA),
- PINGROUP(62, SOUTH, sec_mi2s, audio_ref, MDP_VSYNC, cri_trng, NA,
- qdss_gpio0, atest_char0, NA, NA),
- PINGROUP(63, SOUTH, NA, NA, qdss_gpio1, NA, NA, NA, NA, NA, NA),
- PINGROUP(64, SOUTH, NAV_PPS, blsp_spi8, sp_cmu, NA, qdss_gpio2, NA, NA,
+ PINGROUP(57, SOUTH, gcc_gp1, NA, phase_flag4, atest_usb22, NA, NA, NA,
NA, NA),
- PINGROUP(65, SOUTH, NAV_PPS, blsp_spi3, NA, adsp_ext, NA, qdss_gpio3,
+ PINGROUP(58, SOUTH, USB_PHY, gcc_gp2, NA, NA, atest_char, NA, NA, NA,
+ NA),
+ PINGROUP(59, NORTH, mdp_vsync, gcc_gp3, NA, NA, atest_char3, NA, NA,
+ NA, NA),
+ PINGROUP(60, NORTH, cri_trng0, NA, NA, atest_char2, NA, NA, NA, NA, NA),
+ PINGROUP(61, NORTH, pri_mi2s, cri_trng1, NA, NA, atest_char1, NA, NA,
+ NA, NA),
+ PINGROUP(62, NORTH, sec_mi2s, audio_ref, MDP_VSYNC, cri_trng, NA, NA,
+ atest_char0, NA, NA),
+ PINGROUP(63, NORTH, NA, NA, NA, qdss_gpio1, NA, NA, NA, NA, NA),
+ PINGROUP(64, SOUTH, blsp_spi8, sp_cmu, NA, NA, qdss_gpio2, NA, NA, NA,
+ NA),
+ PINGROUP(65, SOUTH, NA, NAV_PPS, NAV_PPS, GPS_TX, blsp_spi3, adsp_ext,
NA, NA, NA),
- PINGROUP(66, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(67, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(68, SOUTH, isense_dbg, phase_flag0, qdss_gpio, NA, NA, NA, NA,
+ PINGROUP(66, NORTH, NA, NA, qdss_cti, NA, NA, NA, NA, NA, NA),
+ PINGROUP(67, NORTH, NA, NA, qdss_gpio0, NA, NA, NA, NA, NA, NA),
+ PINGROUP(68, NORTH, isense_dbg, NA, phase_flag0, qdss_gpio, NA, NA, NA,
NA, NA),
- PINGROUP(69, SOUTH, phase_flag7, qdss_gpio, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(70, SOUTH, phase_flag8, qdss_gpio6, NA, NA, NA, NA, NA, NA,
+ PINGROUP(69, NORTH, NA, phase_flag7, qdss_gpio, NA, NA, NA, NA, NA, NA),
+ PINGROUP(70, NORTH, NA, phase_flag8, qdss_gpio6, NA, NA, NA, NA, NA,
NA),
- PINGROUP(71, SOUTH, NA, qdss_gpio7, tsense_pwm1, tsense_pwm2, NA, NA,
+ PINGROUP(71, NORTH, NA, NA, qdss_gpio7, tsense_pwm1, tsense_pwm2, NA,
NA, NA, NA),
- PINGROUP(72, SOUTH, qdss_gpio14, NA, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(73, SOUTH, NA, qdss_gpio15, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(74, SOUTH, mdp_vsync, NA, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(75, WEST, NA, qdss_gpio8, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(76, WEST, blsp_spi8, NA, NA, qdss_gpio9, NA, NA, NA, NA, NA),
- PINGROUP(77, SOUTH, NA, qdss_gpio10, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(78, SOUTH, gcc_gp1, qdss_gpio13, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(79, SOUTH, NA, qdss_gpio11, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(80, SOUTH, NA, qdss_gpio12, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(81, SOUTH, mss_lte, gcc_gp2, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(82, SOUTH, mss_lte, NA, gcc_gp3, NA, NA, NA, NA, NA, NA),
+ PINGROUP(72, NORTH, NA, qdss_gpio14, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(73, NORTH, NA, NA, qdss_gpio15, NA, NA, NA, NA, NA, NA),
+ PINGROUP(74, NORTH, mdp_vsync, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(75, NORTH, NA, NA, qdss_gpio8, NA, NA, NA, NA, NA, NA),
+ PINGROUP(76, NORTH, blsp_spi8, NA, NA, NA, qdss_gpio9, NA, NA, NA, NA),
+ PINGROUP(77, NORTH, NA, NA, qdss_gpio10, NA, NA, NA, NA, NA, NA),
+ PINGROUP(78, NORTH, gcc_gp1, NA, qdss_gpio13, NA, NA, NA, NA, NA, NA),
+ PINGROUP(79, SOUTH, NA, NA, qdss_gpio11, NA, NA, NA, NA, NA, NA),
+ PINGROUP(80, SOUTH, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, qdss_gpio12, NA,
+ NA, NA),
+ PINGROUP(81, CENTER, mss_lte, gcc_gp2, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(82, CENTER, mss_lte, gcc_gp3, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(83, SOUTH, uim2_data, NA, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(84, SOUTH, uim2_clk, NA, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(85, SOUTH, uim2_reset, NA, NA, NA, NA, NA, NA, NA, NA),
@@ -1494,12 +1569,12 @@ static const struct msm_pingroup msmfalcon_groups[] = {
PINGROUP(91, SOUTH, uim_batt, NA, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(92, SOUTH, NA, NA, pa_indicator, NA, NA, NA, NA, NA, NA),
PINGROUP(93, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(94, SOUTH, NA, ssbi_gnss, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(94, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(95, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(96, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(97, SOUTH, NA, NA, ldo_en, NA, NA, NA, NA, NA, NA),
- PINGROUP(98, SOUTH, NA, NAV_PPS, NAV_PPS, ldo_update, NA, NA, NA, NA,
- NA),
+ PINGROUP(97, SOUTH, NA, ldo_en, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(98, SOUTH, NA, NAV_PPS, NAV_PPS, GPS_TX, ldo_update, NA, NA,
+ NA, NA),
PINGROUP(99, SOUTH, qlink_request, NA, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(100, SOUTH, qlink_enable, NA, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(101, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
@@ -1508,13 +1583,20 @@ static const struct msm_pingroup msmfalcon_groups[] = {
PINGROUP(104, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(105, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
PINGROUP(106, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(107, EAST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(108, EAST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(109, EAST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
- PINGROUP(110, EAST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
- SDC_QDSD_PINGROUP(sdc2_clk, 0x999000, 14, 6),
- SDC_QDSD_PINGROUP(sdc2_cmd, 0x999000, 11, 3),
- SDC_QDSD_PINGROUP(sdc2_data, 0x999000, 9, 0),
+ PINGROUP(107, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(108, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(109, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(110, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(111, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(112, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(113, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ SDC_QDSD_PINGROUP(sdc1_clk, 0x99a000, 13, 6),
+ SDC_QDSD_PINGROUP(sdc1_cmd, 0x99a000, 11, 3),
+ SDC_QDSD_PINGROUP(sdc1_data, 0x99a000, 9, 0),
+ SDC_QDSD_PINGROUP(sdc2_clk, 0x99b000, 14, 6),
+ SDC_QDSD_PINGROUP(sdc2_cmd, 0x99b000, 11, 3),
+ SDC_QDSD_PINGROUP(sdc2_data, 0x99b000, 9, 0),
+ SDC_QDSD_PINGROUP(sdc1_rclk, 0x99a000, 15, 0),
};
static const struct msm_pinctrl_soc_data msmfalcon_pinctrl = {
@@ -1524,7 +1606,7 @@ static const struct msm_pinctrl_soc_data msmfalcon_pinctrl = {
.nfunctions = ARRAY_SIZE(msmfalcon_functions),
.groups = msmfalcon_groups,
.ngroups = ARRAY_SIZE(msmfalcon_groups),
- .ngpios = 111,
+ .ngpios = 114,
};
static int msmfalcon_pinctrl_probe(struct platform_device *pdev)
diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c
index 181ea98a63b7..2b0d70217bbd 100644
--- a/drivers/pinctrl/sh-pfc/core.c
+++ b/drivers/pinctrl/sh-pfc/core.c
@@ -545,7 +545,9 @@ static int sh_pfc_probe(struct platform_device *pdev)
return ret;
}
- pinctrl_provide_dummies();
+ /* Enable dummy states for those platforms without pinctrl support */
+ if (!of_have_populated_dt())
+ pinctrl_provide_dummies();
ret = sh_pfc_init_ranges(pfc);
if (ret < 0)
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c
index 00265f0435a7..8b381d69df86 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c
@@ -485,6 +485,7 @@ static const struct sunxi_pinctrl_desc sun8i_a33_pinctrl_data = {
.pins = sun8i_a33_pins,
.npins = ARRAY_SIZE(sun8i_a33_pins),
.irq_banks = 2,
+ .irq_bank_base = 1,
};
static int sun8i_a33_pinctrl_probe(struct platform_device *pdev)
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
index dead97daca35..a4a5b504c532 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -578,7 +578,7 @@ static void sunxi_pinctrl_irq_release_resources(struct irq_data *d)
static int sunxi_pinctrl_irq_set_type(struct irq_data *d, unsigned int type)
{
struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
- u32 reg = sunxi_irq_cfg_reg(d->hwirq);
+ u32 reg = sunxi_irq_cfg_reg(d->hwirq, pctl->desc->irq_bank_base);
u8 index = sunxi_irq_cfg_offset(d->hwirq);
unsigned long flags;
u32 regval;
@@ -625,7 +625,8 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d, unsigned int type)
static void sunxi_pinctrl_irq_ack(struct irq_data *d)
{
struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
- u32 status_reg = sunxi_irq_status_reg(d->hwirq);
+ u32 status_reg = sunxi_irq_status_reg(d->hwirq,
+ pctl->desc->irq_bank_base);
u8 status_idx = sunxi_irq_status_offset(d->hwirq);
/* Clear the IRQ */
@@ -635,7 +636,7 @@ static void sunxi_pinctrl_irq_ack(struct irq_data *d)
static void sunxi_pinctrl_irq_mask(struct irq_data *d)
{
struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
- u32 reg = sunxi_irq_ctrl_reg(d->hwirq);
+ u32 reg = sunxi_irq_ctrl_reg(d->hwirq, pctl->desc->irq_bank_base);
u8 idx = sunxi_irq_ctrl_offset(d->hwirq);
unsigned long flags;
u32 val;
@@ -652,7 +653,7 @@ static void sunxi_pinctrl_irq_mask(struct irq_data *d)
static void sunxi_pinctrl_irq_unmask(struct irq_data *d)
{
struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
- u32 reg = sunxi_irq_ctrl_reg(d->hwirq);
+ u32 reg = sunxi_irq_ctrl_reg(d->hwirq, pctl->desc->irq_bank_base);
u8 idx = sunxi_irq_ctrl_offset(d->hwirq);
unsigned long flags;
u32 val;
@@ -744,7 +745,7 @@ static void sunxi_pinctrl_irq_handler(struct irq_desc *desc)
if (bank == pctl->desc->irq_banks)
return;
- reg = sunxi_irq_status_reg_from_bank(bank);
+ reg = sunxi_irq_status_reg_from_bank(bank, pctl->desc->irq_bank_base);
val = readl(pctl->membase + reg);
if (val) {
@@ -1023,9 +1024,11 @@ int sunxi_pinctrl_init(struct platform_device *pdev,
for (i = 0; i < pctl->desc->irq_banks; i++) {
/* Mask and clear all IRQs before registering a handler */
- writel(0, pctl->membase + sunxi_irq_ctrl_reg_from_bank(i));
+ writel(0, pctl->membase + sunxi_irq_ctrl_reg_from_bank(i,
+ pctl->desc->irq_bank_base));
writel(0xffffffff,
- pctl->membase + sunxi_irq_status_reg_from_bank(i));
+ pctl->membase + sunxi_irq_status_reg_from_bank(i,
+ pctl->desc->irq_bank_base));
irq_set_chained_handler_and_data(pctl->irq[i],
sunxi_pinctrl_irq_handler,
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
index e248e81a0f9e..0afce1ab12d0 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
@@ -97,6 +97,7 @@ struct sunxi_pinctrl_desc {
int npins;
unsigned pin_base;
unsigned irq_banks;
+ unsigned irq_bank_base;
bool irq_read_needs_mux;
};
@@ -233,12 +234,12 @@ static inline u32 sunxi_pull_offset(u16 pin)
return pin_num * PULL_PINS_BITS;
}
-static inline u32 sunxi_irq_cfg_reg(u16 irq)
+static inline u32 sunxi_irq_cfg_reg(u16 irq, unsigned bank_base)
{
u8 bank = irq / IRQ_PER_BANK;
u8 reg = (irq % IRQ_PER_BANK) / IRQ_CFG_IRQ_PER_REG * 0x04;
- return IRQ_CFG_REG + bank * IRQ_MEM_SIZE + reg;
+ return IRQ_CFG_REG + (bank_base + bank) * IRQ_MEM_SIZE + reg;
}
static inline u32 sunxi_irq_cfg_offset(u16 irq)
@@ -247,16 +248,16 @@ static inline u32 sunxi_irq_cfg_offset(u16 irq)
return irq_num * IRQ_CFG_IRQ_BITS;
}
-static inline u32 sunxi_irq_ctrl_reg_from_bank(u8 bank)
+static inline u32 sunxi_irq_ctrl_reg_from_bank(u8 bank, unsigned bank_base)
{
- return IRQ_CTRL_REG + bank * IRQ_MEM_SIZE;
+ return IRQ_CTRL_REG + (bank_base + bank) * IRQ_MEM_SIZE;
}
-static inline u32 sunxi_irq_ctrl_reg(u16 irq)
+static inline u32 sunxi_irq_ctrl_reg(u16 irq, unsigned bank_base)
{
u8 bank = irq / IRQ_PER_BANK;
- return sunxi_irq_ctrl_reg_from_bank(bank);
+ return sunxi_irq_ctrl_reg_from_bank(bank, bank_base);
}
static inline u32 sunxi_irq_ctrl_offset(u16 irq)
@@ -265,16 +266,16 @@ static inline u32 sunxi_irq_ctrl_offset(u16 irq)
return irq_num * IRQ_CTRL_IRQ_BITS;
}
-static inline u32 sunxi_irq_status_reg_from_bank(u8 bank)
+static inline u32 sunxi_irq_status_reg_from_bank(u8 bank, unsigned bank_base)
{
- return IRQ_STATUS_REG + bank * IRQ_MEM_SIZE;
+ return IRQ_STATUS_REG + (bank_base + bank) * IRQ_MEM_SIZE;
}
-static inline u32 sunxi_irq_status_reg(u16 irq)
+static inline u32 sunxi_irq_status_reg(u16 irq, unsigned bank_base)
{
u8 bank = irq / IRQ_PER_BANK;
- return sunxi_irq_status_reg_from_bank(bank);
+ return sunxi_irq_status_reg_from_bank(bank, bank_base);
}
static inline u32 sunxi_irq_status_offset(u16 irq)
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index 0069c07474d4..d2e31c3b0945 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -93,26 +93,6 @@ static void __gsi_config_gen_irq(int ee, uint32_t mask, uint32_t val)
GSI_EE_n_CNTXT_GSI_IRQ_EN_OFFS(ee));
}
-static void __gsi_config_inter_ee_ch_irq(int ee, uint32_t mask, uint32_t val)
-{
- uint32_t curr;
-
- curr = gsi_readl(gsi_ctx->base +
- GSI_INTER_EE_n_SRC_GSI_CH_IRQ_MSK_OFFS(ee));
- gsi_writel((curr & ~mask) | (val & mask), gsi_ctx->base +
- GSI_INTER_EE_n_SRC_GSI_CH_IRQ_MSK_OFFS(ee));
-}
-
-static void __gsi_config_inter_ee_evt_irq(int ee, uint32_t mask, uint32_t val)
-{
- uint32_t curr;
-
- curr = gsi_readl(gsi_ctx->base +
- GSI_INTER_EE_n_SRC_EV_CH_IRQ_MSK_OFFS(ee));
- gsi_writel((curr & ~mask) | (val & mask), gsi_ctx->base +
- GSI_INTER_EE_n_SRC_EV_CH_IRQ_MSK_OFFS(ee));
-}
-
static void gsi_handle_ch_ctrl(int ee)
{
uint32_t ch;
@@ -684,7 +664,10 @@ int gsi_register_device(struct gsi_per_props *props, unsigned long *dev_hdl)
/* only support 16 un-reserved + 7 reserved event virtual IDs */
gsi_ctx->evt_bmap = ~0x7E03FF;
- /* enable all interrupts but GSI_BREAK_POINT */
+ /*
+ * enable all interrupts but GSI_BREAK_POINT.
+ * Inter EE commands / interrupt are no supported.
+ */
__gsi_config_type_irq(props->ee, ~0, ~0);
__gsi_config_ch_irq(props->ee, ~0, ~0);
__gsi_config_evt_irq(props->ee, ~0, ~0);
@@ -692,8 +675,6 @@ int gsi_register_device(struct gsi_per_props *props, unsigned long *dev_hdl)
__gsi_config_glob_irq(props->ee, ~0, ~0);
__gsi_config_gen_irq(props->ee, ~0,
~GSI_EE_n_CNTXT_GSI_IRQ_CLR_GSI_BREAK_POINT_BMSK);
- __gsi_config_inter_ee_ch_irq(props->ee, ~0, ~0);
- __gsi_config_inter_ee_evt_irq(props->ee, ~0, ~0);
gsi_writel(props->intr, gsi_ctx->base +
GSI_EE_n_CNTXT_INTSET_OFFS(gsi_ctx->per.ee));
@@ -791,8 +772,6 @@ int gsi_deregister_device(unsigned long dev_hdl, bool force)
__gsi_config_ieob_irq(gsi_ctx->per.ee, ~0, 0);
__gsi_config_glob_irq(gsi_ctx->per.ee, ~0, 0);
__gsi_config_gen_irq(gsi_ctx->per.ee, ~0, 0);
- __gsi_config_inter_ee_ch_irq(gsi_ctx->per.ee, ~0, 0);
- __gsi_config_inter_ee_evt_irq(gsi_ctx->per.ee, ~0, 0);
devm_free_irq(gsi_ctx->dev, gsi_ctx->per.irq, gsi_ctx);
devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
diff --git a/drivers/platform/msm/gsi/gsi_dbg.c b/drivers/platform/msm/gsi/gsi_dbg.c
index bd8cdf3f9770..2ab8b79acc6d 100644
--- a/drivers/platform/msm/gsi/gsi_dbg.c
+++ b/drivers/platform/msm/gsi/gsi_dbg.c
@@ -653,11 +653,11 @@ static ssize_t gsi_rst_stats(struct file *file,
} else if (ch_id < 0 || ch_id >= GSI_MAX_CHAN ||
!gsi_ctx->chan[ch_id].allocated) {
goto error;
+ } else {
+ min = ch_id;
+ max = ch_id + 1;
}
- min = ch_id;
- max = ch_id + 1;
-
for (ch_id = min; ch_id < max; ch_id++)
memset(&gsi_ctx->chan[ch_id].stats, 0,
sizeof(gsi_ctx->chan[ch_id].stats));
diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c
index 208a4ce1e40e..09d1166e29a6 100644
--- a/drivers/platform/msm/ipa/ipa_api.c
+++ b/drivers/platform/msm/ipa/ipa_api.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/ipa_uc_offload.h>
#include "ipa_api.h"
#define DRV_NAME "ipa"
@@ -369,6 +370,24 @@ int ipa_reset_endpoint(u32 clnt_hdl)
}
EXPORT_SYMBOL(ipa_reset_endpoint);
+/**
+* ipa_disable_endpoint() - Disable an endpoint from IPA perspective
+* @clnt_hdl: [in] IPA client handle
+*
+* Returns: 0 on success, negative on failure
+*
+* Note: Should not be called from atomic context
+*/
+int ipa_disable_endpoint(u32 clnt_hdl)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_disable_endpoint, clnt_hdl);
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_disable_endpoint);
+
/**
* ipa_cfg_ep - IPA end-point configuration
@@ -2803,6 +2822,35 @@ void ipa_recycle_wan_skb(struct sk_buff *skb)
}
EXPORT_SYMBOL(ipa_recycle_wan_skb);
+/**
+ * ipa_setup_uc_ntn_pipes() - setup uc offload pipes
+ */
+int ipa_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *inp,
+ ipa_notify_cb notify, void *priv, u8 hdr_len,
+ struct ipa_ntn_conn_out_params *outp)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_setup_uc_ntn_pipes, inp,
+ notify, priv, hdr_len, outp);
+
+ return ret;
+}
+
+/**
+ * ipa_tear_down_uc_offload_pipes() - tear down uc offload pipes
+ */
+int ipa_tear_down_uc_offload_pipes(int ipa_ep_idx_ul,
+ int ipa_ep_idx_dl)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_tear_down_uc_offload_pipes, ipa_ep_idx_ul,
+ ipa_ep_idx_dl);
+
+ return ret;
+}
+
static const struct dev_pm_ops ipa_pm_ops = {
.suspend_noirq = ipa_ap_suspend,
.resume_noirq = ipa_ap_resume,
diff --git a/drivers/platform/msm/ipa/ipa_api.h b/drivers/platform/msm/ipa/ipa_api.h
index 862bdc475025..eab048323bd5 100644
--- a/drivers/platform/msm/ipa/ipa_api.h
+++ b/drivers/platform/msm/ipa/ipa_api.h
@@ -11,6 +11,7 @@
*/
#include <linux/ipa_mhi.h>
+#include <linux/ipa_uc_offload.h>
#include "ipa_common_i.h"
#ifndef _IPA_API_H_
@@ -26,6 +27,8 @@ struct ipa_api_controller {
int (*ipa_clear_endpoint_delay)(u32 clnt_hdl);
+ int (*ipa_disable_endpoint)(u32 clnt_hdl);
+
int (*ipa_cfg_ep)(u32 clnt_hdl, const struct ipa_ep_cfg *ipa_ep_cfg);
int (*ipa_cfg_ep_nat)(u32 clnt_hdl,
@@ -360,6 +363,12 @@ struct ipa_api_controller {
void (*ipa_recycle_wan_skb)(struct sk_buff *skb);
+ int (*ipa_setup_uc_ntn_pipes)(struct ipa_ntn_conn_in_params *in,
+ ipa_notify_cb notify, void *priv, u8 hdr_len,
+ struct ipa_ntn_conn_out_params *);
+
+ int (*ipa_tear_down_uc_offload_pipes)(int ipa_ep_idx_ul,
+ int ipa_ep_idx_dl);
};
#ifdef CONFIG_IPA
diff --git a/drivers/platform/msm/ipa/ipa_clients/Makefile b/drivers/platform/msm/ipa/ipa_clients/Makefile
index aac473f62751..61cef2d71960 100644
--- a/drivers/platform/msm/ipa/ipa_clients/Makefile
+++ b/drivers/platform/msm/ipa/ipa_clients/Makefile
@@ -1,2 +1,2 @@
-obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o ipa_mhi_client.o
-obj-$(CONFIG_IPA) += odu_bridge.o ipa_mhi_client.o \ No newline at end of file
+obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o
+obj-$(CONFIG_IPA) += odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c
new file mode 100644
index 000000000000..069f0a2e3fee
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c
@@ -0,0 +1,597 @@
+/* Copyright (c) 2015, 2016 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/ipa_uc_offload.h>
+#include <linux/msm_ipa.h>
+#include "../ipa_common_i.h"
+
+#define IPA_NTN_DMA_POOL_ALIGNMENT 8
+#define OFFLOAD_DRV_NAME "ipa_uc_offload"
+#define IPA_UC_OFFLOAD_DBG(fmt, args...) \
+ do { \
+ pr_debug(OFFLOAD_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+#define IPA_UC_OFFLOAD_LOW(fmt, args...) \
+ do { \
+ pr_debug(OFFLOAD_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+#define IPA_UC_OFFLOAD_ERR(fmt, args...) \
+ do { \
+ pr_err(OFFLOAD_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+#define IPA_UC_OFFLOAD_INFO(fmt, args...) \
+ do { \
+ pr_info(OFFLOAD_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+enum ipa_uc_offload_state {
+ IPA_UC_OFFLOAD_STATE_INVALID,
+ IPA_UC_OFFLOAD_STATE_INITIALIZED,
+ IPA_UC_OFFLOAD_STATE_UP,
+ IPA_UC_OFFLOAD_STATE_DOWN,
+};
+
+struct ipa_uc_offload_ctx {
+ enum ipa_uc_offload_proto proto;
+ enum ipa_uc_offload_state state;
+ void *priv;
+ u8 hdr_len;
+ u32 partial_hdr_hdl[IPA_IP_MAX];
+ char netdev_name[IPA_RESOURCE_NAME_MAX];
+ ipa_notify_cb notify;
+ struct completion ntn_completion;
+};
+
+static struct ipa_uc_offload_ctx *ipa_uc_offload_ctx[IPA_UC_MAX_PROT_SIZE];
+
+static int ipa_commit_partial_hdr(
+ struct ipa_ioc_add_hdr *hdr,
+ const char *netdev_name,
+ struct ipa_hdr_info *hdr_info)
+{
+ int i;
+
+ if (hdr == NULL || hdr_info == NULL) {
+ IPA_UC_OFFLOAD_ERR("Invalid input\n");
+ return -EINVAL;
+ }
+
+ hdr->commit = 1;
+ hdr->num_hdrs = 2;
+
+ snprintf(hdr->hdr[0].name, sizeof(hdr->hdr[0].name),
+ "%s_ipv4", netdev_name);
+ snprintf(hdr->hdr[1].name, sizeof(hdr->hdr[1].name),
+ "%s_ipv6", netdev_name);
+ for (i = IPA_IP_v4; i < IPA_IP_MAX; i++) {
+ hdr->hdr[i].hdr_len = hdr_info[i].hdr_len;
+ memcpy(hdr->hdr[i].hdr, hdr_info[i].hdr, hdr->hdr[i].hdr_len);
+ hdr->hdr[i].type = hdr_info[i].hdr_type;
+ hdr->hdr[i].is_partial = 1;
+ hdr->hdr[i].is_eth2_ofst_valid = 1;
+ hdr->hdr[i].eth2_ofst = hdr_info[i].dst_mac_addr_offset;
+ }
+
+ if (ipa_add_hdr(hdr)) {
+ IPA_UC_OFFLOAD_ERR("fail to add partial headers\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int ipa_uc_offload_ntn_reg_intf(
+ struct ipa_uc_offload_intf_params *inp,
+ struct ipa_uc_offload_out_params *outp,
+ struct ipa_uc_offload_ctx *ntn_ctx)
+{
+ struct ipa_ioc_add_hdr *hdr;
+ struct ipa_tx_intf tx;
+ struct ipa_rx_intf rx;
+ struct ipa_ioc_tx_intf_prop tx_prop[2];
+ struct ipa_ioc_rx_intf_prop rx_prop[2];
+ u32 len;
+ int ret = 0;
+
+ IPA_UC_OFFLOAD_DBG("register interface for netdev %s\n",
+ inp->netdev_name);
+
+ memcpy(ntn_ctx->netdev_name, inp->netdev_name, IPA_RESOURCE_NAME_MAX);
+ ntn_ctx->hdr_len = inp->hdr_info[0].hdr_len;
+ ntn_ctx->notify = inp->notify;
+ ntn_ctx->priv = inp->priv;
+
+ /* add partial header */
+ len = sizeof(struct ipa_ioc_add_hdr) + 2 * sizeof(struct ipa_hdr_add);
+ hdr = kzalloc(len, GFP_KERNEL);
+ if (hdr == NULL) {
+ IPA_UC_OFFLOAD_ERR("fail to alloc %d bytes\n", len);
+ return -ENOMEM;
+ }
+
+ if (ipa_commit_partial_hdr(hdr, ntn_ctx->netdev_name, inp->hdr_info)) {
+ IPA_UC_OFFLOAD_ERR("fail to commit partial headers\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ /* populate tx prop */
+ tx.num_props = 2;
+ tx.prop = tx_prop;
+
+ memset(tx_prop, 0, sizeof(tx_prop));
+ tx_prop[0].ip = IPA_IP_v4;
+ tx_prop[0].dst_pipe = IPA_CLIENT_ODU_TETH_CONS;
+ tx_prop[0].hdr_l2_type = inp->hdr_info[0].hdr_type;
+ memcpy(tx_prop[0].hdr_name, hdr->hdr[IPA_IP_v4].name,
+ sizeof(tx_prop[0].hdr_name));
+
+ tx_prop[1].ip = IPA_IP_v6;
+ tx_prop[1].dst_pipe = IPA_CLIENT_ODU_TETH_CONS;
+ tx_prop[1].hdr_l2_type = inp->hdr_info[1].hdr_type;
+ memcpy(tx_prop[1].hdr_name, hdr->hdr[IPA_IP_v6].name,
+ sizeof(tx_prop[1].hdr_name));
+
+ /* populate rx prop */
+ rx.num_props = 2;
+ rx.prop = rx_prop;
+
+ memset(rx_prop, 0, sizeof(rx_prop));
+ rx_prop[0].ip = IPA_IP_v4;
+ rx_prop[0].src_pipe = IPA_CLIENT_ODU_PROD;
+ rx_prop[0].hdr_l2_type = inp->hdr_info[0].hdr_type;
+ if (inp->is_meta_data_valid) {
+ rx_prop[0].attrib.attrib_mask |= IPA_FLT_META_DATA;
+ rx_prop[0].attrib.meta_data = inp->meta_data;
+ rx_prop[0].attrib.meta_data_mask = inp->meta_data_mask;
+ }
+
+ rx_prop[1].ip = IPA_IP_v6;
+ rx_prop[1].src_pipe = IPA_CLIENT_ODU_PROD;
+ rx_prop[1].hdr_l2_type = inp->hdr_info[1].hdr_type;
+ if (inp->is_meta_data_valid) {
+ rx_prop[1].attrib.attrib_mask |= IPA_FLT_META_DATA;
+ rx_prop[1].attrib.meta_data = inp->meta_data;
+ rx_prop[1].attrib.meta_data_mask = inp->meta_data_mask;
+ }
+
+ if (ipa_register_intf(inp->netdev_name, &tx, &rx)) {
+ IPA_UC_OFFLOAD_ERR("fail to add interface prop\n");
+ memset(ntn_ctx, 0, sizeof(*ntn_ctx));
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ ntn_ctx->partial_hdr_hdl[IPA_IP_v4] = hdr->hdr[IPA_IP_v4].hdr_hdl;
+ ntn_ctx->partial_hdr_hdl[IPA_IP_v6] = hdr->hdr[IPA_IP_v6].hdr_hdl;
+ init_completion(&ntn_ctx->ntn_completion);
+ ntn_ctx->state = IPA_UC_OFFLOAD_STATE_INITIALIZED;
+
+fail:
+ kfree(hdr);
+ return ret;
+}
+
+int ipa_uc_offload_reg_intf(
+ struct ipa_uc_offload_intf_params *inp,
+ struct ipa_uc_offload_out_params *outp)
+{
+ struct ipa_uc_offload_ctx *ctx;
+ int ret = 0;
+
+ if (inp == NULL || outp == NULL) {
+ IPA_UC_OFFLOAD_ERR("invalid params in=%p out=%p\n", inp, outp);
+ return -EINVAL;
+ }
+
+ if (inp->proto <= IPA_UC_INVALID ||
+ inp->proto >= IPA_UC_MAX_PROT_SIZE) {
+ IPA_UC_OFFLOAD_ERR("invalid proto %d\n", inp->proto);
+ return -EINVAL;
+ }
+
+ if (!ipa_uc_offload_ctx[inp->proto]) {
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (ctx == NULL) {
+ IPA_UC_OFFLOAD_ERR("fail to alloc uc offload ctx\n");
+ return -EFAULT;
+ }
+ ipa_uc_offload_ctx[inp->proto] = ctx;
+ ctx->proto = inp->proto;
+ } else
+ ctx = ipa_uc_offload_ctx[inp->proto];
+
+ if (ctx->state != IPA_UC_OFFLOAD_STATE_INVALID) {
+ IPA_UC_OFFLOAD_ERR("Already Initialized\n");
+ return -EINVAL;
+ }
+
+ if (ctx->proto == IPA_UC_NTN) {
+ ret = ipa_uc_offload_ntn_reg_intf(inp, outp, ctx);
+ if (!ret)
+ outp->clnt_hndl = IPA_UC_NTN;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_uc_offload_reg_intf);
+
+static int ipa_uc_ntn_cons_release(void)
+{
+ return 0;
+}
+
+static int ipa_uc_ntn_cons_request(void)
+{
+ int ret = 0;
+ struct ipa_uc_offload_ctx *ntn_ctx;
+
+ ntn_ctx = ipa_uc_offload_ctx[IPA_UC_NTN];
+ if (!ntn_ctx) {
+ IPA_UC_OFFLOAD_ERR("NTN is not initialized\n");
+ ret = -EFAULT;
+ } else if (ntn_ctx->state != IPA_UC_OFFLOAD_STATE_UP) {
+ IPA_UC_OFFLOAD_ERR("Invalid State: %d\n", ntn_ctx->state);
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
+
+static void ipa_uc_offload_rm_notify(void *user_data, enum ipa_rm_event event,
+ unsigned long data)
+{
+ struct ipa_uc_offload_ctx *offload_ctx;
+
+ offload_ctx = (struct ipa_uc_offload_ctx *)user_data;
+ if (!(offload_ctx && offload_ctx->proto > IPA_UC_INVALID &&
+ offload_ctx->proto < IPA_UC_MAX_PROT_SIZE)) {
+ IPA_UC_OFFLOAD_ERR("Invalid user data\n");
+ return;
+ }
+
+ if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_INITIALIZED)
+ IPA_UC_OFFLOAD_ERR("Invalid State: %d\n", offload_ctx->state);
+
+ switch (event) {
+ case IPA_RM_RESOURCE_GRANTED:
+ complete_all(&offload_ctx->ntn_completion);
+ break;
+
+ case IPA_RM_RESOURCE_RELEASED:
+ break;
+
+ default:
+ IPA_UC_OFFLOAD_ERR("Invalid RM Evt: %d", event);
+ break;
+ }
+}
+
+int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp,
+ struct ipa_ntn_conn_out_params *outp,
+ struct ipa_uc_offload_ctx *ntn_ctx)
+{
+ struct ipa_rm_create_params param;
+ int result = 0;
+
+ if (inp->dl.ring_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT ||
+ inp->dl.buff_pool_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT) {
+ IPA_UC_OFFLOAD_ERR("alignment failure on TX\n");
+ return -EINVAL;
+ }
+ if (inp->ul.ring_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT ||
+ inp->ul.buff_pool_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT) {
+ IPA_UC_OFFLOAD_ERR("alignment failure on RX\n");
+ return -EINVAL;
+ }
+
+ memset(&param, 0, sizeof(param));
+ param.name = IPA_RM_RESOURCE_ODU_ADAPT_PROD;
+ param.reg_params.user_data = ntn_ctx;
+ param.reg_params.notify_cb = ipa_uc_offload_rm_notify;
+ param.floor_voltage = IPA_VOLTAGE_SVS;
+ result = ipa_rm_create_resource(&param);
+ if (result) {
+ IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_PROD resource\n");
+ return -EFAULT;
+ }
+
+ memset(&param, 0, sizeof(param));
+ param.name = IPA_RM_RESOURCE_ODU_ADAPT_CONS;
+ param.request_resource = ipa_uc_ntn_cons_request;
+ param.release_resource = ipa_uc_ntn_cons_release;
+ result = ipa_rm_create_resource(&param);
+ if (result) {
+ IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_CONS resource\n");
+ goto fail_create_rm_cons;
+ }
+
+ if (ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
+ IPA_RM_RESOURCE_APPS_CONS)) {
+ IPA_UC_OFFLOAD_ERR("fail to add rm dependency\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ if (ipa_setup_uc_ntn_pipes(inp, ntn_ctx->notify,
+ ntn_ctx->priv, ntn_ctx->hdr_len, outp)) {
+ IPA_UC_OFFLOAD_ERR("fail to setup uc offload pipes\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ ntn_ctx->state = IPA_UC_OFFLOAD_STATE_UP;
+ result = ipa_rm_request_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
+ if (result == -EINPROGRESS) {
+ if (wait_for_completion_timeout(&ntn_ctx->ntn_completion,
+ 10*HZ) == 0) {
+ IPA_UC_OFFLOAD_ERR("ODU PROD resource req time out\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ } else if (result != 0) {
+ IPA_UC_OFFLOAD_ERR("fail to request resource\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS);
+fail_create_rm_cons:
+ ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
+
+ return result;
+}
+
+int ipa_uc_offload_conn_pipes(struct ipa_uc_offload_conn_in_params *inp,
+ struct ipa_uc_offload_conn_out_params *outp)
+{
+ int ret = 0;
+ struct ipa_uc_offload_ctx *offload_ctx;
+
+ if (!(inp && outp)) {
+ IPA_UC_OFFLOAD_ERR("bad parm. in=%p out=%p\n", inp, outp);
+ return -EINVAL;
+ }
+
+ if (inp->clnt_hndl <= IPA_UC_INVALID ||
+ inp->clnt_hndl >= IPA_UC_MAX_PROT_SIZE) {
+ IPA_UC_OFFLOAD_ERR("invalid client handle %d\n",
+ inp->clnt_hndl);
+ return -EINVAL;
+ }
+
+ offload_ctx = ipa_uc_offload_ctx[inp->clnt_hndl];
+ if (!offload_ctx) {
+ IPA_UC_OFFLOAD_ERR("Invalid Handle\n");
+ return -EINVAL;
+ }
+
+ if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_INITIALIZED) {
+ IPA_UC_OFFLOAD_ERR("Invalid state %d\n", offload_ctx->state);
+ return -EPERM;
+ }
+
+ switch (offload_ctx->proto) {
+ case IPA_UC_NTN:
+ ret = ipa_uc_ntn_conn_pipes(&inp->u.ntn, &outp->u.ntn,
+ offload_ctx);
+ break;
+
+ default:
+ IPA_UC_OFFLOAD_ERR("Invalid Proto :%d\n", offload_ctx->proto);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_uc_offload_conn_pipes);
+
+int ipa_set_perf_profile(struct ipa_perf_profile *profile)
+{
+ struct ipa_rm_perf_profile rm_profile;
+ enum ipa_rm_resource_name resource_name;
+
+ if (profile == NULL) {
+ IPA_UC_OFFLOAD_ERR("Invalid input\n");
+ return -EINVAL;
+ }
+
+ rm_profile.max_supported_bandwidth_mbps =
+ profile->max_supported_bw_mbps;
+
+ if (profile->client == IPA_CLIENT_ODU_PROD) {
+ resource_name = IPA_RM_RESOURCE_ODU_ADAPT_PROD;
+ } else if (profile->client == IPA_CLIENT_ODU_TETH_CONS) {
+ resource_name = IPA_RM_RESOURCE_ODU_ADAPT_CONS;
+ } else {
+ IPA_UC_OFFLOAD_ERR("not supported\n");
+ return -EINVAL;
+ }
+
+ if (ipa_rm_set_perf_profile(resource_name, &rm_profile)) {
+ IPA_UC_OFFLOAD_ERR("fail to setup rm perf profile\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ipa_set_perf_profile);
+
+static int ipa_uc_ntn_disconn_pipes(struct ipa_uc_offload_ctx *ntn_ctx)
+{
+ int ipa_ep_idx_ul, ipa_ep_idx_dl;
+
+ ntn_ctx->state = IPA_UC_OFFLOAD_STATE_DOWN;
+ if (ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
+ IPA_RM_RESOURCE_APPS_CONS)) {
+ IPA_UC_OFFLOAD_ERR("fail to delete rm dependency\n");
+ return -EFAULT;
+ }
+
+ if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD)) {
+ IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_PROD resource\n");
+ return -EFAULT;
+ }
+
+ if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS)) {
+ IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_CONS resource\n");
+ return -EFAULT;
+ }
+
+ ipa_ep_idx_ul = ipa_get_ep_mapping(IPA_CLIENT_ODU_PROD);
+ ipa_ep_idx_dl = ipa_get_ep_mapping(IPA_CLIENT_ODU_TETH_CONS);
+ if (ipa_tear_down_uc_offload_pipes(ipa_ep_idx_ul, ipa_ep_idx_dl)) {
+ IPA_UC_OFFLOAD_ERR("fail to tear down uc offload pipes\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int ipa_uc_offload_disconn_pipes(u32 clnt_hdl)
+{
+ struct ipa_uc_offload_ctx *offload_ctx;
+ int ret = 0;
+
+ if (clnt_hdl <= IPA_UC_INVALID ||
+ clnt_hdl >= IPA_UC_MAX_PROT_SIZE) {
+ IPA_UC_OFFLOAD_ERR("Invalid client handle %d\n", clnt_hdl);
+ return -EINVAL;
+ }
+
+ offload_ctx = ipa_uc_offload_ctx[clnt_hdl];
+ if (!offload_ctx) {
+ IPA_UC_OFFLOAD_ERR("Invalid client Handle\n");
+ return -EINVAL;
+ }
+
+ if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_UP) {
+ IPA_UC_OFFLOAD_ERR("Invalid state\n");
+ return -EINVAL;
+ }
+
+ switch (offload_ctx->proto) {
+ case IPA_UC_NTN:
+ ret = ipa_uc_ntn_disconn_pipes(offload_ctx);
+ break;
+
+ default:
+ IPA_UC_OFFLOAD_ERR("Invalid Proto :%d\n", clnt_hdl);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_uc_offload_disconn_pipes);
+
+static int ipa_uc_ntn_cleanup(struct ipa_uc_offload_ctx *ntn_ctx)
+{
+ int len, result = 0;
+ struct ipa_ioc_del_hdr *hdr;
+
+ len = sizeof(struct ipa_ioc_del_hdr) + 2 * sizeof(struct ipa_hdr_del);
+ hdr = kzalloc(len, GFP_KERNEL);
+ if (hdr == NULL) {
+ IPA_UC_OFFLOAD_ERR("fail to alloc %d bytes\n", len);
+ return -ENOMEM;
+ }
+
+ hdr->commit = 1;
+ hdr->num_hdls = 2;
+ hdr->hdl[0].hdl = ntn_ctx->partial_hdr_hdl[0];
+ hdr->hdl[1].hdl = ntn_ctx->partial_hdr_hdl[1];
+
+ if (ipa_del_hdr(hdr)) {
+ IPA_UC_OFFLOAD_ERR("fail to delete partial header\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ if (ipa_deregister_intf(ntn_ctx->netdev_name)) {
+ IPA_UC_OFFLOAD_ERR("fail to delete interface prop\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+fail:
+ kfree(hdr);
+ return result;
+}
+
+int ipa_uc_offload_cleanup(u32 clnt_hdl)
+{
+ struct ipa_uc_offload_ctx *offload_ctx;
+ int ret = 0;
+
+ if (clnt_hdl <= IPA_UC_INVALID ||
+ clnt_hdl >= IPA_UC_MAX_PROT_SIZE) {
+ IPA_UC_OFFLOAD_ERR("Invalid client handle %d\n", clnt_hdl);
+ return -EINVAL;
+ }
+
+ offload_ctx = ipa_uc_offload_ctx[clnt_hdl];
+ if (!offload_ctx) {
+ IPA_UC_OFFLOAD_ERR("Invalid client handle %d\n", clnt_hdl);
+ return -EINVAL;
+ }
+
+ if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_DOWN) {
+ IPA_UC_OFFLOAD_ERR("Invalid State %d\n", offload_ctx->state);
+ return -EINVAL;
+ }
+
+ switch (offload_ctx->proto) {
+ case IPA_UC_NTN:
+ ret = ipa_uc_ntn_cleanup(offload_ctx);
+ break;
+
+ default:
+ IPA_UC_OFFLOAD_ERR("Invalid Proto :%d\n", clnt_hdl);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (!ret) {
+ kfree(offload_ctx);
+ offload_ctx = NULL;
+ ipa_uc_offload_ctx[clnt_hdl] = NULL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(ipa_uc_offload_cleanup);
diff --git a/drivers/platform/msm/ipa/ipa_common_i.h b/drivers/platform/msm/ipa/ipa_common_i.h
index 060613281e4c..115348251d17 100644
--- a/drivers/platform/msm/ipa/ipa_common_i.h
+++ b/drivers/platform/msm/ipa/ipa_common_i.h
@@ -17,6 +17,7 @@
#define _IPA_COMMON_I_H_
#include <linux/ipc_logging.h>
#include <linux/ipa.h>
+#include <linux/ipa_uc_offload.h>
#define __FILENAME__ \
(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
@@ -342,6 +343,11 @@ int ipa_uc_state_check(void);
void ipa_get_holb(int ep_idx, struct ipa_ep_cfg_holb *holb);
void ipa_set_tag_process_before_gating(bool val);
bool ipa_has_open_aggr_frame(enum ipa_client_type client);
+int ipa_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
+ ipa_notify_cb notify, void *priv, u8 hdr_len,
+ struct ipa_ntn_conn_out_params *outp);
+
+int ipa_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl);
u8 *ipa_write_64(u64 w, u8 *dest);
u8 *ipa_write_32(u32 w, u8 *dest);
diff --git a/drivers/platform/msm/ipa/ipa_uc_offload_common_i.h b/drivers/platform/msm/ipa/ipa_uc_offload_common_i.h
new file mode 100644
index 000000000000..ae6cfc4fcd50
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_uc_offload_common_i.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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/ipa_mhi.h>
+#include <linux/ipa_qmi_service_v01.h>
+
+#ifndef _IPA_UC_OFFLOAD_COMMON_I_H_
+#define _IPA_UC_OFFLOAD_COMMON_I_H_
+
+int ipa_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
+ ipa_notify_cb notify, void *priv, u8 hdr_len,
+ struct ipa_ntn_conn_out_params *outp);
+int ipa_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl);
+
+#endif /* _IPA_UC_OFFLOAD_COMMON_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v2/Makefile b/drivers/platform/msm/ipa/ipa_v2/Makefile
index 435acbf1cab8..69b8a4c94461 100644
--- a/drivers/platform/msm/ipa/ipa_v2/Makefile
+++ b/drivers/platform/msm/ipa/ipa_v2/Makefile
@@ -1,6 +1,6 @@
obj-$(CONFIG_IPA) += ipat.o
ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o \
ipa_utils.o ipa_nat.o ipa_intf.o teth_bridge.o ipa_interrupts.o \
- ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o
+ ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o ipa_uc_ntn.o
obj-$(CONFIG_RMNET_IPA) += rmnet_ipa.o ipa_qmi_service_v01.o ipa_qmi_service.o rmnet_ipa_fd_ioctl.o
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index cb808bd2a8b7..fc3d9f355da6 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -65,6 +65,10 @@
#define IPA2_ACTIVE_CLIENT_LOG_TYPE_RESOURCE 2
#define IPA2_ACTIVE_CLIENT_LOG_TYPE_SPECIAL 3
+#define MAX_POLLING_ITERATION 40
+#define MIN_POLLING_ITERATION 1
+#define ONE_MSEC 1
+
#define IPA_AGGR_STR_IN_BYTES(str) \
(strnlen((str), IPA_AGGR_MAX_STR_LENGTH - 1) + 1)
@@ -3613,6 +3617,19 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p,
ipa_ctx->use_dma_zone = resource_p->use_dma_zone;
ipa_ctx->tethered_flow_control = resource_p->tethered_flow_control;
+ /* Setting up IPA RX Polling Timeout Seconds */
+ ipa_rx_timeout_min_max_calc(&ipa_ctx->ipa_rx_min_timeout_usec,
+ &ipa_ctx->ipa_rx_max_timeout_usec,
+ resource_p->ipa_rx_polling_sleep_msec);
+
+ /* Setting up ipa polling iteration */
+ if ((resource_p->ipa_polling_iteration >= MIN_POLLING_ITERATION)
+ && (resource_p->ipa_polling_iteration <= MAX_POLLING_ITERATION))
+ ipa_ctx->ipa_polling_iteration =
+ resource_p->ipa_polling_iteration;
+ else
+ ipa_ctx->ipa_polling_iteration = MAX_POLLING_ITERATION;
+
/* default aggregation parameters */
ipa_ctx->aggregation_type = IPA_MBIM_16;
ipa_ctx->aggregation_byte_limit = 1;
@@ -4035,6 +4052,12 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p,
else
IPADBG(":wdi init ok\n");
+ result = ipa_ntn_init();
+ if (result)
+ IPAERR(":ntn init failed (%d)\n", -result);
+ else
+ IPADBG(":ntn init ok\n");
+
ipa_ctx->q6_proxy_clk_vote_valid = true;
ipa_register_panic_hdlr();
@@ -4268,6 +4291,31 @@ static int get_ipa_dts_configuration(struct platform_device *pdev,
if (result)
ipa_drv_res->ee = 0;
+ /* Get IPA RX Polling Timeout Seconds */
+ result = of_property_read_u32(pdev->dev.of_node,
+ "qcom,rx-polling-sleep-ms",
+ &ipa_drv_res->ipa_rx_polling_sleep_msec);
+
+ if (result) {
+ ipa_drv_res->ipa_rx_polling_sleep_msec = ONE_MSEC;
+ IPADBG("using default polling timeout of 1MSec\n");
+ } else {
+ IPADBG(": found ipa_drv_res->ipa_rx_polling_sleep_sec = %d",
+ ipa_drv_res->ipa_rx_polling_sleep_msec);
+ }
+
+ /* Get IPA Polling Iteration */
+ result = of_property_read_u32(pdev->dev.of_node,
+ "qcom,ipa-polling-iteration",
+ &ipa_drv_res->ipa_polling_iteration);
+ if (result) {
+ ipa_drv_res->ipa_polling_iteration = MAX_POLLING_ITERATION;
+ IPADBG("using default polling iteration\n");
+ } else {
+ IPADBG(": found ipa_drv_res->ipa_polling_iteration = %d",
+ ipa_drv_res->ipa_polling_iteration);
+ }
+
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_client.c b/drivers/platform/msm/ipa/ipa_v2/ipa_client.c
index 64246ac4eec0..66e329a03df7 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_client.c
@@ -560,22 +560,30 @@ int ipa2_disconnect(u32 clnt_hdl)
if (!ep->keep_ipa_awake)
IPA_ACTIVE_CLIENTS_INC_EP(client_type);
- /* Set Disconnect in Progress flag. */
- spin_lock(&ipa_ctx->disconnect_lock);
- ep->disconnect_in_progress = true;
- spin_unlock(&ipa_ctx->disconnect_lock);
-
- /* Notify uc to stop monitoring holb on USB BAM Producer pipe. */
- if (IPA_CLIENT_IS_USB_CONS(ep->client)) {
- ipa_uc_monitor_holb(ep->client, false);
- IPADBG("Disabling holb monitor for client: %d\n", ep->client);
- }
+ /* For USB 2.0 controller, first the ep will be disabled.
+ * so this sequence is not needed again when disconnecting the pipe.
+ */
+ if (!ep->ep_disabled) {
+ /* Set Disconnect in Progress flag. */
+ spin_lock(&ipa_ctx->disconnect_lock);
+ ep->disconnect_in_progress = true;
+ spin_unlock(&ipa_ctx->disconnect_lock);
+
+ /* Notify uc to stop monitoring holb on USB BAM
+ * Producer pipe.
+ */
+ if (IPA_CLIENT_IS_USB_CONS(ep->client)) {
+ ipa_uc_monitor_holb(ep->client, false);
+ IPADBG("Disabling holb monitor for client: %d\n",
+ ep->client);
+ }
- result = ipa_disable_data_path(clnt_hdl);
- if (result) {
- IPAERR("disable data path failed res=%d clnt=%d.\n", result,
- clnt_hdl);
- return -EPERM;
+ result = ipa_disable_data_path(clnt_hdl);
+ if (result) {
+ IPAERR("disable data path failed res=%d clnt=%d.\n",
+ result, clnt_hdl);
+ return -EPERM;
+ }
}
result = sps_disconnect(ep->ep_hdl);
@@ -784,6 +792,82 @@ int ipa2_clear_endpoint_delay(u32 clnt_hdl)
}
/**
+ * ipa2_disable_endpoint() - low-level IPA client disable endpoint
+ * @clnt_hdl: [in] opaque client handle assigned by IPA to client
+ *
+ * Should be called by the driver of the peripheral that wants to
+ * disable the pipe from IPA in BAM-BAM mode.
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * Note: Should not be called from atomic context
+ */
+int ipa2_disable_endpoint(u32 clnt_hdl)
+{
+ int result;
+ struct ipa_ep_context *ep;
+ enum ipa_client_type client_type;
+ unsigned long bam;
+
+ if (unlikely(!ipa_ctx)) {
+ IPAERR("IPA driver was not initialized\n");
+ return -EINVAL;
+ }
+
+ if (clnt_hdl >= ipa_ctx->ipa_num_pipes ||
+ ipa_ctx->ep[clnt_hdl].valid == 0) {
+ IPAERR("bad parm.\n");
+ return -EINVAL;
+ }
+
+ ep = &ipa_ctx->ep[clnt_hdl];
+ client_type = ipa2_get_client_mapping(clnt_hdl);
+ IPA_ACTIVE_CLIENTS_INC_EP(client_type);
+
+ /* Set Disconnect in Progress flag. */
+ spin_lock(&ipa_ctx->disconnect_lock);
+ ep->disconnect_in_progress = true;
+ spin_unlock(&ipa_ctx->disconnect_lock);
+
+ /* Notify uc to stop monitoring holb on USB BAM Producer pipe. */
+ if (IPA_CLIENT_IS_USB_CONS(ep->client)) {
+ ipa_uc_monitor_holb(ep->client, false);
+ IPADBG("Disabling holb monitor for client: %d\n", ep->client);
+ }
+
+ result = ipa_disable_data_path(clnt_hdl);
+ if (result) {
+ IPAERR("disable data path failed res=%d clnt=%d.\n", result,
+ clnt_hdl);
+ goto fail;
+ }
+
+ if (IPA_CLIENT_IS_CONS(ep->client))
+ bam = ep->connect.source;
+ else
+ bam = ep->connect.destination;
+
+ result = sps_pipe_reset(bam, clnt_hdl);
+ if (result) {
+ IPAERR("SPS pipe reset failed.\n");
+ goto fail;
+ }
+
+ ep->ep_disabled = true;
+
+ IPA_ACTIVE_CLIENTS_DEC_EP(client_type);
+
+ IPADBG("client (ep: %d) disabled\n", clnt_hdl);
+
+ return 0;
+
+fail:
+ IPA_ACTIVE_CLIENTS_DEC_EP(client_type);
+ return -EPERM;
+}
+
+
+/**
* ipa_sps_connect_safe() - connect endpoint from BAM prespective
* @h: [in] sps pipe handle
* @connect: [in] sps connect parameters
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
index 566cb4d03c51..0eab77d27760 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
@@ -25,6 +25,12 @@
* IPA2_ACTIVE_CLIENTS_LOG_BUFFER_SIZE_LINES) \
+ IPA_MAX_MSG_LEN)
+#define RX_MIN_POLL_CNT "Rx Min Poll Count"
+#define RX_MAX_POLL_CNT "Rx Max Poll Count"
+#define MAX_COUNT_LENGTH 6
+#define MAX_POLLING_ITERATION 40
+#define MIN_POLLING_ITERATION 1
+
#define IPA_DUMP_STATUS_FIELD(f) \
pr_err(#f "=0x%x\n", status->f)
@@ -104,12 +110,16 @@ static struct dentry *dfile_ip6_flt;
static struct dentry *dfile_stats;
static struct dentry *dfile_wstats;
static struct dentry *dfile_wdi_stats;
+static struct dentry *dfile_ntn_stats;
static struct dentry *dfile_dbg_cnt;
static struct dentry *dfile_msg;
static struct dentry *dfile_ip4_nat;
static struct dentry *dfile_rm_stats;
static struct dentry *dfile_status_stats;
static struct dentry *dfile_active_clients;
+static struct dentry *dfile_ipa_rx_poll_timeout;
+static struct dentry *dfile_ipa_poll_iteration;
+
static char dbg_buff[IPA_MAX_MSG_LEN];
static char *active_clients_buf;
static s8 ep_reg_idx;
@@ -1091,6 +1101,110 @@ nxt_clnt_cons:
return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
}
+static ssize_t ipa_read_ntn(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+#define TX_STATS(y) \
+ ipa_ctx->uc_ntn_ctx.ntn_uc_stats_mmio->tx_ch_stats[0].y
+#define RX_STATS(y) \
+ ipa_ctx->uc_ntn_ctx.ntn_uc_stats_mmio->rx_ch_stats[0].y
+
+ struct IpaHwStatsNTNInfoData_t stats;
+ int nbytes;
+ int cnt = 0;
+
+ if (!ipa2_get_ntn_stats(&stats)) {
+ nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
+ "TX num_pkts_processed=%u\n"
+ "TX tail_ptr_val=%u\n"
+ "TX num_db_fired=%u\n"
+ "TX ringFull=%u\n"
+ "TX ringEmpty=%u\n"
+ "TX ringUsageHigh=%u\n"
+ "TX ringUsageLow=%u\n"
+ "TX RingUtilCount=%u\n"
+ "TX bamFifoFull=%u\n"
+ "TX bamFifoEmpty=%u\n"
+ "TX bamFifoUsageHigh=%u\n"
+ "TX bamFifoUsageLow=%u\n"
+ "TX bamUtilCount=%u\n"
+ "TX num_db=%u\n"
+ "TX num_unexpected_db=%u\n"
+ "TX num_bam_int_handled=%u\n"
+ "TX num_bam_int_in_non_running_state=%u\n"
+ "TX num_qmb_int_handled=%u\n"
+ "TX num_bam_int_handled_while_wait_for_bam=%u\n"
+ "TX num_bam_int_handled_while_not_in_bam=%u\n",
+ TX_STATS(num_pkts_processed),
+ TX_STATS(tail_ptr_val),
+ TX_STATS(num_db_fired),
+ TX_STATS(tx_comp_ring_stats.ringFull),
+ TX_STATS(tx_comp_ring_stats.ringEmpty),
+ TX_STATS(tx_comp_ring_stats.ringUsageHigh),
+ TX_STATS(tx_comp_ring_stats.ringUsageLow),
+ TX_STATS(tx_comp_ring_stats.RingUtilCount),
+ TX_STATS(bam_stats.bamFifoFull),
+ TX_STATS(bam_stats.bamFifoEmpty),
+ TX_STATS(bam_stats.bamFifoUsageHigh),
+ TX_STATS(bam_stats.bamFifoUsageLow),
+ TX_STATS(bam_stats.bamUtilCount),
+ TX_STATS(num_db),
+ TX_STATS(num_unexpected_db),
+ TX_STATS(num_bam_int_handled),
+ TX_STATS(num_bam_int_in_non_running_state),
+ TX_STATS(num_qmb_int_handled),
+ TX_STATS(num_bam_int_handled_while_wait_for_bam),
+ TX_STATS(num_bam_int_handled_while_not_in_bam));
+ cnt += nbytes;
+ nbytes = scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+ "RX max_outstanding_pkts=%u\n"
+ "RX num_pkts_processed=%u\n"
+ "RX rx_ring_rp_value=%u\n"
+ "RX ringFull=%u\n"
+ "RX ringEmpty=%u\n"
+ "RX ringUsageHigh=%u\n"
+ "RX ringUsageLow=%u\n"
+ "RX RingUtilCount=%u\n"
+ "RX bamFifoFull=%u\n"
+ "RX bamFifoEmpty=%u\n"
+ "RX bamFifoUsageHigh=%u\n"
+ "RX bamFifoUsageLow=%u\n"
+ "RX bamUtilCount=%u\n"
+ "RX num_bam_int_handled=%u\n"
+ "RX num_db=%u\n"
+ "RX num_unexpected_db=%u\n"
+ "RX num_pkts_in_dis_uninit_state=%u\n"
+ "num_ic_inj_vdev_change=%u\n"
+ "num_ic_inj_fw_desc_change=%u\n",
+ RX_STATS(max_outstanding_pkts),
+ RX_STATS(num_pkts_processed),
+ RX_STATS(rx_ring_rp_value),
+ RX_STATS(rx_ind_ring_stats.ringFull),
+ RX_STATS(rx_ind_ring_stats.ringEmpty),
+ RX_STATS(rx_ind_ring_stats.ringUsageHigh),
+ RX_STATS(rx_ind_ring_stats.ringUsageLow),
+ RX_STATS(rx_ind_ring_stats.RingUtilCount),
+ RX_STATS(bam_stats.bamFifoFull),
+ RX_STATS(bam_stats.bamFifoEmpty),
+ RX_STATS(bam_stats.bamFifoUsageHigh),
+ RX_STATS(bam_stats.bamFifoUsageLow),
+ RX_STATS(bam_stats.bamUtilCount),
+ RX_STATS(num_bam_int_handled),
+ RX_STATS(num_db),
+ RX_STATS(num_unexpected_db),
+ RX_STATS(num_pkts_in_dis_uninit_state),
+ RX_STATS(num_bam_int_handled_while_not_in_bam),
+ RX_STATS(num_bam_int_handled_while_in_bam_state));
+ cnt += nbytes;
+ } else {
+ nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
+ "Fail to read NTN stats\n");
+ cnt += nbytes;
+ }
+
+ return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
+}
+
static ssize_t ipa_read_wdi(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
@@ -1597,6 +1711,97 @@ static ssize_t ipa2_clear_active_clients_log(struct file *file,
return count;
}
+static ssize_t ipa_read_rx_polling_timeout(struct file *file,
+ char __user *ubuf, size_t count, loff_t *ppos)
+{
+ int min_cnt;
+ int max_cnt;
+
+ if (active_clients_buf == NULL) {
+ IPAERR("Active Clients buffer is not allocated");
+ return 0;
+ }
+ memset(active_clients_buf, 0, IPA_DBG_ACTIVE_CLIENTS_BUF_SIZE);
+ min_cnt = scnprintf(active_clients_buf,
+ IPA_DBG_ACTIVE_CLIENTS_BUF_SIZE,
+ "Rx Min Poll count = %u\n",
+ ipa_ctx->ipa_rx_min_timeout_usec);
+
+ max_cnt = scnprintf(active_clients_buf + min_cnt,
+ IPA_DBG_ACTIVE_CLIENTS_BUF_SIZE,
+ "Rx Max Poll count = %u\n",
+ ipa_ctx->ipa_rx_max_timeout_usec);
+
+ return simple_read_from_buffer(ubuf, count, ppos, active_clients_buf,
+ min_cnt + max_cnt);
+}
+
+static ssize_t ipa_write_rx_polling_timeout(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ s8 polltime = 0;
+
+ if (sizeof(dbg_buff) < count + 1)
+ return -EFAULT;
+
+ if (copy_from_user(dbg_buff, ubuf, count))
+ return -EFAULT;
+
+ dbg_buff[count] = '\0';
+
+ if (kstrtos8(dbg_buff, 0, &polltime))
+ return -EFAULT;
+
+ ipa_rx_timeout_min_max_calc(&ipa_ctx->ipa_rx_min_timeout_usec,
+ &ipa_ctx->ipa_rx_max_timeout_usec, polltime);
+ return count;
+}
+
+static ssize_t ipa_read_polling_iteration(struct file *file,
+ char __user *ubuf, size_t count, loff_t *ppos)
+{
+ int cnt;
+
+ if (active_clients_buf == NULL) {
+ IPAERR("Active Clients buffer is not allocated");
+ return 0;
+ }
+
+ memset(active_clients_buf, 0, IPA_DBG_ACTIVE_CLIENTS_BUF_SIZE);
+
+ cnt = scnprintf(active_clients_buf, IPA_DBG_ACTIVE_CLIENTS_BUF_SIZE,
+ "Polling Iteration count = %u\n",
+ ipa_ctx->ipa_polling_iteration);
+
+ return simple_read_from_buffer(ubuf, count, ppos, active_clients_buf,
+ cnt);
+}
+
+static ssize_t ipa_write_polling_iteration(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ s8 iteration_cnt = 0;
+
+ if (sizeof(dbg_buff) < count + 1)
+ return -EFAULT;
+
+ if (copy_from_user(dbg_buff, ubuf, count))
+ return -EFAULT;
+
+ dbg_buff[count] = '\0';
+
+ if (kstrtos8(dbg_buff, 0, &iteration_cnt))
+ return -EFAULT;
+
+ if ((iteration_cnt >= MIN_POLLING_ITERATION) &&
+ (iteration_cnt <= MAX_POLLING_ITERATION))
+ ipa_ctx->ipa_polling_iteration = iteration_cnt;
+ else
+ ipa_ctx->ipa_polling_iteration = MAX_POLLING_ITERATION;
+
+ return count;
+}
+
const struct file_operations ipa_gen_reg_ops = {
.read = ipa_read_gen_reg,
};
@@ -1645,6 +1850,10 @@ const struct file_operations ipa_wdi_ops = {
.read = ipa_read_wdi,
};
+const struct file_operations ipa_ntn_ops = {
+ .read = ipa_read_ntn,
+};
+
const struct file_operations ipa_msg_ops = {
.read = ipa_read_msg,
};
@@ -1671,6 +1880,16 @@ const struct file_operations ipa2_active_clients = {
.write = ipa2_clear_active_clients_log,
};
+const struct file_operations ipa_rx_poll_time_ops = {
+ .read = ipa_read_rx_polling_timeout,
+ .write = ipa_write_rx_polling_timeout,
+};
+
+const struct file_operations ipa_poll_iteration_ops = {
+ .read = ipa_read_polling_iteration,
+ .write = ipa_write_polling_iteration,
+};
+
void ipa_debugfs_init(void)
{
const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
@@ -1797,6 +2016,13 @@ void ipa_debugfs_init(void)
goto fail;
}
+ dfile_ntn_stats = debugfs_create_file("ntn", read_only_mode, dent, 0,
+ &ipa_ntn_ops);
+ if (!dfile_ntn_stats || IS_ERR(dfile_ntn_stats)) {
+ IPAERR("fail to create file for debug_fs ntn stats\n");
+ goto fail;
+ }
+
dfile_dbg_cnt = debugfs_create_file("dbg_cnt", read_write_mode, dent, 0,
&ipa_dbg_cnt_ops);
if (!dfile_dbg_cnt || IS_ERR(dfile_dbg_cnt)) {
@@ -1832,6 +2058,20 @@ void ipa_debugfs_init(void)
goto fail;
}
+ dfile_ipa_rx_poll_timeout = debugfs_create_file("ipa_rx_poll_time",
+ read_write_mode, dent, 0, &ipa_rx_poll_time_ops);
+ if (!dfile_ipa_rx_poll_timeout || IS_ERR(dfile_ipa_rx_poll_timeout)) {
+ IPAERR("fail to create file for debug_fs rx poll timeout\n");
+ goto fail;
+ }
+
+ dfile_ipa_poll_iteration = debugfs_create_file("ipa_poll_iteration",
+ read_write_mode, dent, 0, &ipa_poll_iteration_ops);
+ if (!dfile_ipa_poll_iteration || IS_ERR(dfile_ipa_poll_iteration)) {
+ IPAERR("fail to create file for debug_fs poll iteration\n");
+ goto fail;
+ }
+
file = debugfs_create_u32("enable_clock_scaling", read_write_mode,
dent, &ipa_ctx->enable_clock_scaling);
if (!file) {
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
index 1c93ac16d419..005508fdcdc1 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
@@ -20,8 +20,6 @@
#define IPA_LAST_DESC_CNT 0xFFFF
#define POLLING_INACTIVITY_RX 40
-#define POLLING_MIN_SLEEP_RX 1010
-#define POLLING_MAX_SLEEP_RX 1050
#define POLLING_INACTIVITY_TX 40
#define POLLING_MIN_SLEEP_TX 400
#define POLLING_MAX_SLEEP_TX 500
@@ -42,6 +40,8 @@
IPA_GENERIC_RX_BUFF_BASE_SZ) -\
IPA_GENERIC_RX_BUFF_BASE_SZ)
+#define IPA_RX_BUFF_CLIENT_HEADROOM 256
+
/* less 1 nominal MTU (1500 bytes) rounded to units of KB */
#define IPA_ADJUST_AGGR_BYTE_LIMIT(X) (((X) - IPA_MTU)/1000)
@@ -1045,8 +1045,8 @@ static void ipa_handle_rx(struct ipa_sys_context *sys)
if (cnt == 0) {
inactive_cycles++;
trace_idle_sleep_enter(sys->ep->client);
- usleep_range(POLLING_MIN_SLEEP_RX,
- POLLING_MAX_SLEEP_RX);
+ usleep_range(ipa_ctx->ipa_rx_min_timeout_usec,
+ ipa_ctx->ipa_rx_max_timeout_usec);
trace_idle_sleep_exit(sys->ep->client);
} else {
inactive_cycles = 0;
@@ -1059,7 +1059,7 @@ static void ipa_handle_rx(struct ipa_sys_context *sys)
if (sys->len == 0)
break;
- } while (inactive_cycles <= POLLING_INACTIVITY_RX);
+ } while (inactive_cycles <= ipa_ctx->ipa_polling_iteration);
trace_poll_to_intr(sys->ep->client);
ipa_rx_switch_to_intr_mode(sys);
@@ -2290,6 +2290,21 @@ static void ipa_cleanup_rx(struct ipa_sys_context *sys)
}
}
+static struct sk_buff *ipa_skb_copy_for_client(struct sk_buff *skb, int len)
+{
+ struct sk_buff *skb2 = NULL;
+
+ skb2 = __dev_alloc_skb(len + IPA_RX_BUFF_CLIENT_HEADROOM, GFP_KERNEL);
+ if (likely(skb2)) {
+ /* Set the data pointer */
+ skb_reserve(skb2, IPA_RX_BUFF_CLIENT_HEADROOM);
+ memcpy(skb2->data, skb->data, len);
+ skb2->len = len;
+ skb_set_tail_pointer(skb2, len);
+ }
+
+ return skb2;
+}
static int ipa_lan_rx_pyld_hdlr(struct sk_buff *skb,
struct ipa_sys_context *sys)
@@ -2486,7 +2501,8 @@ begin:
sys->drop_packet = true;
}
- skb2 = skb_clone(skb, GFP_KERNEL);
+ skb2 = ipa_skb_copy_for_client(skb,
+ status->pkt_len + IPA_PKT_STATUS_SIZE);
if (likely(skb2)) {
if (skb->len < len + IPA_PKT_STATUS_SIZE) {
IPADBG("SPL skb len %d len %d\n",
@@ -2529,7 +2545,7 @@ begin:
IPA_PKT_STATUS_SIZE);
}
} else {
- IPAERR("fail to clone\n");
+ IPAERR("fail to alloc skb\n");
if (skb->len < len) {
sys->prev_skb = NULL;
sys->len_rem = len - skb->len +
@@ -3171,7 +3187,7 @@ static int ipa_assign_policy_v2(struct ipa_sys_connect_params *in,
IPA_GENERIC_RX_BUFF_SZ(
ipa_adjust_ra_buff_base_sz(
in->ipa_ep_cfg.aggr.
- aggr_byte_limit));
+ aggr_byte_limit - IPA_HEADROOM));
in->ipa_ep_cfg.aggr.
aggr_byte_limit =
sys->rx_buff_sz < in->
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
index 790a0b41147e..62e026262663 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
@@ -581,7 +581,8 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx,
return 0;
bad_len:
- hdr_entry->ref_cnt--;
+ if (add_ref_hdr)
+ hdr_entry->ref_cnt--;
entry->cookie = 0;
kmem_cache_free(ipa_ctx->hdr_proc_ctx_cache, entry);
return -EPERM;
@@ -761,7 +762,7 @@ static int __ipa_del_hdr_proc_ctx(u32 proc_ctx_hdl, bool release_hdr)
}
if (release_hdr)
- __ipa_release_hdr(entry->hdr->id);
+ __ipa_del_hdr(entry->hdr->id);
/* move the offset entry to appropriate free list */
list_move(&entry->offset_entry->link,
@@ -1089,12 +1090,19 @@ int ipa2_reset_hdr(void)
&ipa_ctx->hdr_tbl.head_hdr_entry_list, link) {
/* do not remove the default header */
- if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME))
+ if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) {
+ if (entry->is_hdr_proc_ctx) {
+ mutex_unlock(&ipa_ctx->lock);
+ WARN_ON(1);
+ IPAERR("default header is proc ctx\n");
+ return -EFAULT;
+ }
continue;
+ }
if (ipa_id_find(entry->id) == NULL) {
- WARN_ON(1);
mutex_unlock(&ipa_ctx->lock);
+ WARN_ON(1);
return -EFAULT;
}
if (entry->is_hdr_proc_ctx) {
@@ -1147,8 +1155,8 @@ int ipa2_reset_hdr(void)
link) {
if (ipa_id_find(ctx_entry->id) == NULL) {
- WARN_ON(1);
mutex_unlock(&ipa_ctx->lock);
+ WARN_ON(1);
return -EFAULT;
}
list_del(&ctx_entry->link);
@@ -1311,8 +1319,8 @@ int ipa2_put_hdr(u32 hdr_hdl)
goto bail;
}
- if (entry == NULL || entry->cookie != IPA_COOKIE) {
- IPAERR("bad params\n");
+ if (entry->cookie != IPA_COOKIE) {
+ IPAERR("invalid header entry\n");
result = -EINVAL;
goto bail;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
index f94418efc927..5ea7a08b3135 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
@@ -26,12 +26,14 @@
#include <linux/platform_device.h>
#include <asm/dma-iommu.h>
#include <linux/iommu.h>
+#include <linux/ipa_uc_offload.h>
#include "ipa_hw_defs.h"
#include "ipa_ram_mmap.h"
#include "ipa_reg.h"
#include "ipa_qmi_service.h"
#include "../ipa_api.h"
#include "../ipa_common_i.h"
+#include "ipa_uc_offload_i.h"
#define DRV_NAME "ipa"
#define NAT_DEV_NAME "ipaNatTable"
@@ -242,7 +244,7 @@ struct ipa_rt_tbl {
* @is_partial: flag indicating if header table entry is partial
* @is_hdr_proc_ctx: false - hdr entry resides in hdr table,
* true - hdr entry resides in DDR and pointed to by proc ctx
- * @phys_base: physical address of entry in SRAM when is_hdr_proc_ctx is true,
+ * @phys_base: physical address of entry in DDR when is_hdr_proc_ctx is true,
* else 0
* @proc_ctx: processing context header
* @offset_entry: entry's offset
@@ -544,7 +546,7 @@ struct ipa_ep_context {
bool skip_ep_cfg;
bool keep_ipa_awake;
struct ipa_wlan_stats wstats;
- u32 wdi_state;
+ u32 uc_offload_state;
u32 rx_replenish_threshold;
bool disconnect_in_progress;
u32 qmi_request_sent;
@@ -553,6 +555,7 @@ struct ipa_ep_context {
bool switch_to_intr;
int inactive_cycles;
u32 eot_in_poll_err;
+ bool ep_disabled;
/* sys MUST be the last element of this struct */
struct ipa_sys_context *sys;
@@ -816,134 +819,6 @@ struct ipa_tag_completion {
struct ipa_controller;
/**
- * @brief Enum value determined based on the feature it
- * corresponds to
- * +----------------+----------------+
- * | 3 bits | 5 bits |
- * +----------------+----------------+
- * | HW_FEATURE | OPCODE |
- * +----------------+----------------+
- *
- */
-#define FEATURE_ENUM_VAL(feature, opcode) ((feature << 5) | opcode)
-#define EXTRACT_UC_FEATURE(value) (value >> 5)
-
-#define IPA_HW_NUM_FEATURES 0x8
-
-/**
- * enum ipa_hw_features - Values that represent the features supported in IPA HW
- * @IPA_HW_FEATURE_COMMON : Feature related to common operation of IPA HW
- * @IPA_HW_FEATURE_MHI : Feature related to MHI operation in IPA HW
- * @IPA_HW_FEATURE_WDI : Feature related to WDI operation in IPA HW
-*/
-enum ipa_hw_features {
- IPA_HW_FEATURE_COMMON = 0x0,
- IPA_HW_FEATURE_MHI = 0x1,
- IPA_HW_FEATURE_WDI = 0x3,
- IPA_HW_FEATURE_MAX = IPA_HW_NUM_FEATURES
-};
-
-/**
- * struct IpaHwSharedMemCommonMapping_t - Structure referring to the common
- * section in 128B shared memory located in offset zero of SW Partition in IPA
- * SRAM.
- * @cmdOp : CPU->HW command opcode. See IPA_CPU_2_HW_COMMANDS
- * @cmdParams : CPU->HW command parameter. The parameter filed can hold 32 bits
- * of parameters (immediate parameters) and point on structure in system memory
- * (in such case the address must be accessible for HW)
- * @responseOp : HW->CPU response opcode. See IPA_HW_2_CPU_RESPONSES
- * @responseParams : HW->CPU response parameter. The parameter filed can hold 32
- * bits of parameters (immediate parameters) and point on structure in system
- * memory
- * @eventOp : HW->CPU event opcode. See IPA_HW_2_CPU_EVENTS
- * @eventParams : HW->CPU event parameter. The parameter filed can hold 32 bits of
- * parameters (immediate parameters) and point on structure in system memory
- * @firstErrorAddress : Contains the address of first error-source on SNOC
- * @hwState : State of HW. The state carries information regarding the error type.
- * @warningCounter : The warnings counter. The counter carries information regarding
- * non fatal errors in HW
- * @interfaceVersionCommon : The Common interface version as reported by HW
- *
- * The shared memory is used for communication between IPA HW and CPU.
- */
-struct IpaHwSharedMemCommonMapping_t {
- u8 cmdOp;
- u8 reserved_01;
- u16 reserved_03_02;
- u32 cmdParams;
- u8 responseOp;
- u8 reserved_09;
- u16 reserved_0B_0A;
- u32 responseParams;
- u8 eventOp;
- u8 reserved_11;
- u16 reserved_13_12;
- u32 eventParams;
- u32 reserved_1B_18;
- u32 firstErrorAddress;
- u8 hwState;
- u8 warningCounter;
- u16 reserved_23_22;
- u16 interfaceVersionCommon;
- u16 reserved_27_26;
-} __packed;
-
-/**
- * union IpaHwFeatureInfoData_t - parameters for stats/config blob
- *
- * @offset : Location of a feature within the EventInfoData
- * @size : Size of the feature
- */
-union IpaHwFeatureInfoData_t {
- struct IpaHwFeatureInfoParams_t {
- u32 offset:16;
- u32 size:16;
- } __packed params;
- u32 raw32b;
-} __packed;
-
-/**
- * struct IpaHwEventInfoData_t - Structure holding the parameters for
- * statistics and config info
- *
- * @baseAddrOffset : Base Address Offset of the statistics or config
- * structure from IPA_WRAPPER_BASE
- * @IpaHwFeatureInfoData_t : Location and size of each feature within
- * the statistics or config structure
- *
- * @note Information about each feature in the featureInfo[]
- * array is populated at predefined indices per the IPA_HW_FEATURES
- * enum definition
- */
-struct IpaHwEventInfoData_t {
- u32 baseAddrOffset;
- union IpaHwFeatureInfoData_t featureInfo[IPA_HW_NUM_FEATURES];
-} __packed;
-
-/**
- * struct IpaHwEventLogInfoData_t - Structure holding the parameters for
- * IPA_HW_2_CPU_EVENT_LOG_INFO Event
- *
- * @featureMask : Mask indicating the features enabled in HW.
- * Refer IPA_HW_FEATURE_MASK
- * @circBuffBaseAddrOffset : Base Address Offset of the Circular Event
- * Log Buffer structure
- * @statsInfo : Statistics related information
- * @configInfo : Configuration related information
- *
- * @note The offset location of this structure from IPA_WRAPPER_BASE
- * will be provided as Event Params for the IPA_HW_2_CPU_EVENT_LOG_INFO
- * Event
- */
-struct IpaHwEventLogInfoData_t {
- u32 featureMask;
- u32 circBuffBaseAddrOffset;
- struct IpaHwEventInfoData_t statsInfo;
- struct IpaHwEventInfoData_t configInfo;
-
-} __packed;
-
-/**
* struct ipa_uc_hdlrs - IPA uC callback functions
* @ipa_uc_loaded_hdlr: Function handler when uC is loaded
* @ipa_uc_event_hdlr: Event handler function
@@ -1225,6 +1100,7 @@ struct ipa_context {
struct ipa_uc_ctx uc_ctx;
struct ipa_uc_wdi_ctx uc_wdi_ctx;
+ struct ipa_uc_ntn_ctx uc_ntn_ctx;
u32 wan_rx_ring_size;
bool skip_uc_pipe_reset;
bool smmu_present;
@@ -1244,6 +1120,9 @@ struct ipa_context {
/* M-release support to know client pipes */
struct ipacm_client_info ipacm_client[IPA_MAX_NUM_PIPES];
bool tethered_flow_control;
+ u32 ipa_rx_min_timeout_usec;
+ u32 ipa_rx_max_timeout_usec;
+ u32 ipa_polling_iteration;
};
/**
@@ -1295,6 +1174,8 @@ struct ipa_plat_drv_res {
bool skip_uc_pipe_reset;
bool use_dma_zone;
bool tethered_flow_control;
+ u32 ipa_rx_polling_sleep_msec;
+ u32 ipa_polling_iteration;
};
struct ipa_mem_partition {
@@ -1426,6 +1307,11 @@ int ipa2_reset_endpoint(u32 clnt_hdl);
int ipa2_clear_endpoint_delay(u32 clnt_hdl);
/*
+ * Disable ep
+ */
+int ipa2_disable_endpoint(u32 clnt_hdl);
+
+/*
* Configuration
*/
int ipa2_cfg_ep(u32 clnt_hdl, const struct ipa_ep_cfg *ipa_ep_cfg);
@@ -1593,6 +1479,11 @@ int ipa2_resume_wdi_pipe(u32 clnt_hdl);
int ipa2_suspend_wdi_pipe(u32 clnt_hdl);
int ipa2_get_wdi_stats(struct IpaHwStatsWDIInfoData_t *stats);
u16 ipa2_get_smem_restr_bytes(void);
+int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *inp,
+ ipa_notify_cb notify, void *priv, u8 hdr_len,
+ struct ipa_ntn_conn_out_params *outp);
+int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl);
+
/*
* To retrieve doorbell physical address of
* wlan pipes
@@ -1730,6 +1621,9 @@ void ipa_debugfs_init(void);
void ipa_debugfs_remove(void);
void ipa_dump_buff_internal(void *base, dma_addr_t phy_base, u32 size);
+
+void ipa_rx_timeout_min_max_calc(u32 *min, u32 *max, s8 time);
+
#ifdef IPA_DEBUG
#define IPA_DUMP_BUFF(base, phy_base, size) \
ipa_dump_buff_internal(base, phy_base, size)
@@ -1935,4 +1829,8 @@ int ipa_iommu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot);
int ipa2_rx_poll(u32 clnt_hdl, int budget);
void ipa2_recycle_wan_skb(struct sk_buff *skb);
+int ipa_ntn_init(void);
+int ipa2_get_ntn_stats(struct IpaHwStatsNTNInfoData_t *stats);
+int ipa2_register_ipa_ready_cb(void (*ipa_ready_cb)(void *),
+ void *user_data);
#endif /* _IPA_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c
index 7c10c4cee150..e8f25c9c23d3 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c
@@ -276,8 +276,6 @@ fail_ep_exists:
*/
int ipa2_disconnect_mhi_pipe(u32 clnt_hdl)
{
- struct ipa_ep_context *ep;
-
IPA_MHI_FUNC_ENTRY();
if (clnt_hdl >= ipa_ctx->ipa_num_pipes) {
@@ -290,7 +288,8 @@ int ipa2_disconnect_mhi_pipe(u32 clnt_hdl)
return -EINVAL;
}
- ep->valid = 0;
+ ipa_ctx->ep[clnt_hdl].valid = 0;
+
ipa_delete_dflt_flt_rules(clnt_hdl);
IPA_MHI_DBG("client (ep: %d) disconnected\n", clnt_hdl);
@@ -302,14 +301,13 @@ int ipa2_mhi_resume_channels_internal(enum ipa_client_type client,
bool LPTransitionRejected, bool brstmode_enabled,
union __packed gsi_channel_scratch ch_scratch, u8 index)
{
- int i;
int res;
IPA_MHI_FUNC_ENTRY();
res = ipa_uc_mhi_resume_channel(index, LPTransitionRejected);
if (res) {
- IPA_MHI_ERR("failed to suspend channel %d error %d\n",
- i, res);
+ IPA_MHI_ERR("failed to suspend channel %u error %d\n",
+ index, res);
return res;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
index 9d4704ded0c3..15476f38cf44 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
@@ -1008,6 +1008,10 @@ static int __ipa_add_rt_rule(enum ipa_ip_type ip, const char *name,
return 0;
ipa_insert_failed:
+ if (entry->hdr)
+ entry->hdr->ref_cnt--;
+ else if (entry->proc_ctx)
+ entry->proc_ctx->ref_cnt--;
list_del(&entry->link);
kmem_cache_free(ipa_ctx->rt_rule_cache, entry);
error:
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c
new file mode 100644
index 000000000000..08ed47f3cacf
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c
@@ -0,0 +1,438 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "ipa_i.h"
+
+#define IPA_UC_NTN_DB_PA_TX 0x79620DC
+#define IPA_UC_NTN_DB_PA_RX 0x79620D8
+
+static void ipa_uc_ntn_event_handler(
+ struct IpaHwSharedMemCommonMapping_t *uc_sram_mmio)
+{
+ union IpaHwNTNErrorEventData_t ntn_evt;
+
+ if (uc_sram_mmio->eventOp == IPA_HW_2_CPU_EVENT_NTN_ERROR) {
+ ntn_evt.raw32b = uc_sram_mmio->eventParams;
+ IPADBG("uC NTN evt errType=%u pipe=%d cherrType=%u\n",
+ ntn_evt.params.ntn_error_type,
+ ntn_evt.params.ipa_pipe_number,
+ ntn_evt.params.ntn_ch_err_type);
+ }
+}
+
+static void ipa_uc_ntn_event_log_info_handler(
+ struct IpaHwEventLogInfoData_t *uc_event_top_mmio)
+{
+ if ((uc_event_top_mmio->featureMask & (1 << IPA_HW_FEATURE_NTN)) == 0) {
+ IPAERR("NTN feature missing 0x%x\n",
+ uc_event_top_mmio->featureMask);
+ return;
+ }
+
+ if (uc_event_top_mmio->statsInfo.featureInfo[IPA_HW_FEATURE_NTN].
+ params.size != sizeof(struct IpaHwStatsNTNInfoData_t)) {
+ IPAERR("NTN stats sz invalid exp=%zu is=%u\n",
+ sizeof(struct IpaHwStatsNTNInfoData_t),
+ uc_event_top_mmio->statsInfo.
+ featureInfo[IPA_HW_FEATURE_NTN].params.size);
+ return;
+ }
+
+ ipa_ctx->uc_ntn_ctx.ntn_uc_stats_ofst = uc_event_top_mmio->
+ statsInfo.baseAddrOffset + uc_event_top_mmio->statsInfo.
+ featureInfo[IPA_HW_FEATURE_NTN].params.offset;
+ IPAERR("NTN stats ofst=0x%x\n", ipa_ctx->uc_ntn_ctx.ntn_uc_stats_ofst);
+ if (ipa_ctx->uc_ntn_ctx.ntn_uc_stats_ofst +
+ sizeof(struct IpaHwStatsNTNInfoData_t) >=
+ ipa_ctx->ctrl->ipa_reg_base_ofst +
+ IPA_SRAM_DIRECT_ACCESS_N_OFST_v2_0(0) +
+ ipa_ctx->smem_sz) {
+ IPAERR("uc_ntn_stats 0x%x outside SRAM\n",
+ ipa_ctx->uc_ntn_ctx.ntn_uc_stats_ofst);
+ return;
+ }
+
+ ipa_ctx->uc_ntn_ctx.ntn_uc_stats_mmio =
+ ioremap(ipa_ctx->ipa_wrapper_base +
+ ipa_ctx->uc_ntn_ctx.ntn_uc_stats_ofst,
+ sizeof(struct IpaHwStatsNTNInfoData_t));
+ if (!ipa_ctx->uc_ntn_ctx.ntn_uc_stats_mmio) {
+ IPAERR("fail to ioremap uc ntn stats\n");
+ return;
+ }
+}
+
+/**
+ * ipa2_get_wdi_stats() - Query WDI statistics from uc
+ * @stats: [inout] stats blob from client populated by driver
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * @note Cannot be called from atomic context
+ *
+ */
+int ipa2_get_ntn_stats(struct IpaHwStatsNTNInfoData_t *stats)
+{
+#define TX_STATS(y) stats->tx_ch_stats[0].y = \
+ ipa_ctx->uc_ntn_ctx.ntn_uc_stats_mmio->tx_ch_stats[0].y
+#define RX_STATS(y) stats->rx_ch_stats[0].y = \
+ ipa_ctx->uc_ntn_ctx.ntn_uc_stats_mmio->rx_ch_stats[0].y
+
+ if (unlikely(!ipa_ctx)) {
+ IPAERR("IPA driver was not initialized\n");
+ return -EINVAL;
+ }
+
+ if (!stats || !ipa_ctx->uc_ntn_ctx.ntn_uc_stats_mmio) {
+ IPAERR("bad parms stats=%p ntn_stats=%p\n",
+ stats,
+ ipa_ctx->uc_ntn_ctx.ntn_uc_stats_mmio);
+ return -EINVAL;
+ }
+
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+ TX_STATS(num_pkts_processed);
+ TX_STATS(tail_ptr_val);
+ TX_STATS(num_db_fired);
+ TX_STATS(tx_comp_ring_stats.ringFull);
+ TX_STATS(tx_comp_ring_stats.ringEmpty);
+ TX_STATS(tx_comp_ring_stats.ringUsageHigh);
+ TX_STATS(tx_comp_ring_stats.ringUsageLow);
+ TX_STATS(tx_comp_ring_stats.RingUtilCount);
+ TX_STATS(bam_stats.bamFifoFull);
+ TX_STATS(bam_stats.bamFifoEmpty);
+ TX_STATS(bam_stats.bamFifoUsageHigh);
+ TX_STATS(bam_stats.bamFifoUsageLow);
+ TX_STATS(bam_stats.bamUtilCount);
+ TX_STATS(num_db);
+ TX_STATS(num_unexpected_db);
+ TX_STATS(num_bam_int_handled);
+ TX_STATS(num_bam_int_in_non_running_state);
+ TX_STATS(num_qmb_int_handled);
+ TX_STATS(num_bam_int_handled_while_wait_for_bam);
+ TX_STATS(num_bam_int_handled_while_not_in_bam);
+
+ RX_STATS(max_outstanding_pkts);
+ RX_STATS(num_pkts_processed);
+ RX_STATS(rx_ring_rp_value);
+ RX_STATS(rx_ind_ring_stats.ringFull);
+ RX_STATS(rx_ind_ring_stats.ringEmpty);
+ RX_STATS(rx_ind_ring_stats.ringUsageHigh);
+ RX_STATS(rx_ind_ring_stats.ringUsageLow);
+ RX_STATS(rx_ind_ring_stats.RingUtilCount);
+ RX_STATS(bam_stats.bamFifoFull);
+ RX_STATS(bam_stats.bamFifoEmpty);
+ RX_STATS(bam_stats.bamFifoUsageHigh);
+ RX_STATS(bam_stats.bamFifoUsageLow);
+ RX_STATS(bam_stats.bamUtilCount);
+ RX_STATS(num_bam_int_handled);
+ RX_STATS(num_db);
+ RX_STATS(num_unexpected_db);
+ RX_STATS(num_pkts_in_dis_uninit_state);
+ RX_STATS(num_bam_int_handled_while_not_in_bam);
+ RX_STATS(num_bam_int_handled_while_in_bam_state);
+
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+
+ return 0;
+}
+
+int ipa2_register_ipa_ready_cb(void (*ipa_ready_cb)(void *), void *user_data)
+{
+ int ret;
+
+ ret = ipa2_uc_state_check();
+ if (ret) {
+ ipa_ctx->uc_ntn_ctx.uc_ready_cb = ipa_ready_cb;
+ ipa_ctx->uc_ntn_ctx.priv = user_data;
+ }
+
+ return -EEXIST;
+}
+
+static void ipa_uc_ntn_loaded_handler(void)
+{
+ if (!ipa_ctx) {
+ IPAERR("IPA ctx is null\n");
+ return;
+ }
+
+ if (ipa_ctx->uc_ntn_ctx.uc_ready_cb) {
+ ipa_ctx->uc_ntn_ctx.uc_ready_cb(
+ ipa_ctx->uc_ntn_ctx.priv);
+
+ ipa_ctx->uc_ntn_ctx.uc_ready_cb =
+ NULL;
+ ipa_ctx->uc_ntn_ctx.priv = NULL;
+ }
+}
+
+int ipa_ntn_init(void)
+{
+ struct ipa_uc_hdlrs uc_ntn_cbs = { 0 };
+
+ uc_ntn_cbs.ipa_uc_event_hdlr = ipa_uc_ntn_event_handler;
+ uc_ntn_cbs.ipa_uc_event_log_info_hdlr =
+ ipa_uc_ntn_event_log_info_handler;
+ uc_ntn_cbs.ipa_uc_loaded_hdlr =
+ ipa_uc_ntn_loaded_handler;
+
+ ipa_uc_register_handlers(IPA_HW_FEATURE_NTN, &uc_ntn_cbs);
+
+ return 0;
+}
+
+static int ipa2_uc_send_ntn_setup_pipe_cmd(
+ struct ipa_ntn_setup_info *ntn_info, u8 dir)
+{
+ int ipa_ep_idx;
+ int result = 0;
+ struct ipa_mem_buffer cmd;
+ struct IpaHwNtnSetUpCmdData_t *Ntn_params;
+ struct IpaHwOffloadSetUpCmdData_t *cmd_data;
+
+ if (ntn_info == NULL) {
+ IPAERR("invalid input\n");
+ return -EINVAL;
+ }
+
+ ipa_ep_idx = ipa_get_ep_mapping(ntn_info->client);
+ if (ipa_ep_idx == -1) {
+ IPAERR("fail to get ep idx.\n");
+ return -EFAULT;
+ }
+
+ IPADBG("client=%d ep=%d\n", ntn_info->client, ipa_ep_idx);
+
+ IPADBG("ring_base_pa = 0x%pa\n",
+ &ntn_info->ring_base_pa);
+ IPADBG("ntn_ring_size = %d\n", ntn_info->ntn_ring_size);
+ IPADBG("buff_pool_base_pa = 0x%pa\n", &ntn_info->buff_pool_base_pa);
+ IPADBG("num_buffers = %d\n", ntn_info->num_buffers);
+ IPADBG("data_buff_size = %d\n", ntn_info->data_buff_size);
+ IPADBG("tail_ptr_base_pa = 0x%pa\n", &ntn_info->ntn_reg_base_ptr_pa);
+
+ cmd.size = sizeof(*cmd_data);
+ cmd.base = dma_alloc_coherent(ipa_ctx->uc_pdev, cmd.size,
+ &cmd.phys_base, GFP_KERNEL);
+ if (cmd.base == NULL) {
+ IPAERR("fail to get DMA memory.\n");
+ return -ENOMEM;
+ }
+
+ cmd_data = (struct IpaHwOffloadSetUpCmdData_t *)cmd.base;
+ cmd_data->protocol = IPA_HW_FEATURE_NTN;
+
+ Ntn_params = &cmd_data->SetupCh_params.NtnSetupCh_params;
+ Ntn_params->ring_base_pa = ntn_info->ring_base_pa;
+ Ntn_params->buff_pool_base_pa = ntn_info->buff_pool_base_pa;
+ Ntn_params->ntn_ring_size = ntn_info->ntn_ring_size;
+ Ntn_params->num_buffers = ntn_info->num_buffers;
+ Ntn_params->ntn_reg_base_ptr_pa = ntn_info->ntn_reg_base_ptr_pa;
+ Ntn_params->data_buff_size = ntn_info->data_buff_size;
+ Ntn_params->ipa_pipe_number = ipa_ep_idx;
+ Ntn_params->dir = dir;
+
+ result = ipa_uc_send_cmd((u32)(cmd.phys_base),
+ IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP,
+ IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
+ false, 10*HZ);
+ if (result)
+ result = -EFAULT;
+
+ dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
+ return result;
+}
+
+/**
+ * ipa2_setup_uc_ntn_pipes() - setup uc offload pipes
+ */
+int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
+ ipa_notify_cb notify, void *priv, u8 hdr_len,
+ struct ipa_ntn_conn_out_params *outp)
+{
+ int ipa_ep_idx_ul, ipa_ep_idx_dl;
+ struct ipa_ep_context *ep_ul, *ep_dl;
+ int result = 0;
+
+ if (in == NULL) {
+ IPAERR("invalid input\n");
+ return -EINVAL;
+ }
+
+ ipa_ep_idx_ul = ipa_get_ep_mapping(in->ul.client);
+ ipa_ep_idx_dl = ipa_get_ep_mapping(in->dl.client);
+ if (ipa_ep_idx_ul == -1 || ipa_ep_idx_dl == -1) {
+ IPAERR("fail to alloc EP.\n");
+ return -EFAULT;
+ }
+
+ ep_ul = &ipa_ctx->ep[ipa_ep_idx_ul];
+ ep_dl = &ipa_ctx->ep[ipa_ep_idx_dl];
+
+ if (ep_ul->valid || ep_dl->valid) {
+ IPAERR("EP already allocated ul:%d dl:%d\n",
+ ep_ul->valid, ep_dl->valid);
+ return -EFAULT;
+ }
+
+ memset(ep_ul, 0, offsetof(struct ipa_ep_context, sys));
+ memset(ep_dl, 0, offsetof(struct ipa_ep_context, sys));
+
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+ /* setup ul ep cfg */
+ ep_ul->valid = 1;
+ ep_ul->client = in->ul.client;
+ result = ipa_enable_data_path(ipa_ep_idx_ul);
+ if (result) {
+ IPAERR("disable data path failed res=%d clnt=%d.\n", result,
+ ipa_ep_idx_ul);
+ return -EFAULT;
+ }
+ ep_ul->client_notify = notify;
+ ep_ul->priv = priv;
+
+ memset(&ep_ul->cfg, 0, sizeof(ep_ul->cfg));
+ ep_ul->cfg.nat.nat_en = IPA_SRC_NAT;
+ ep_ul->cfg.hdr.hdr_len = hdr_len;
+ ep_ul->cfg.mode.mode = IPA_BASIC;
+
+ if (ipa2_cfg_ep(ipa_ep_idx_ul, &ep_ul->cfg)) {
+ IPAERR("fail to setup ul pipe cfg\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ if (ipa2_uc_send_ntn_setup_pipe_cmd(&in->ul, IPA_NTN_RX_DIR)) {
+ IPAERR("fail to send cmd to uc for ul pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ ipa_install_dflt_flt_rules(ipa_ep_idx_ul);
+ outp->ul_uc_db_pa = IPA_UC_NTN_DB_PA_RX;
+ ep_ul->uc_offload_state |= IPA_UC_OFFLOAD_CONNECTED;
+ IPAERR("client %d (ep: %d) connected\n", in->ul.client,
+ ipa_ep_idx_ul);
+
+ /* setup dl ep cfg */
+ ep_dl->valid = 1;
+ ep_dl->client = in->dl.client;
+ result = ipa_enable_data_path(ipa_ep_idx_dl);
+ if (result) {
+ IPAERR("disable data path failed res=%d clnt=%d.\n", result,
+ ipa_ep_idx_dl);
+ result = -EFAULT;
+ goto fail;
+ }
+
+ memset(&ep_dl->cfg, 0, sizeof(ep_ul->cfg));
+ ep_dl->cfg.nat.nat_en = IPA_BYPASS_NAT;
+ ep_dl->cfg.hdr.hdr_len = hdr_len;
+ ep_dl->cfg.mode.mode = IPA_BASIC;
+
+ if (ipa2_cfg_ep(ipa_ep_idx_dl, &ep_dl->cfg)) {
+ IPAERR("fail to setup dl pipe cfg\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ if (ipa2_uc_send_ntn_setup_pipe_cmd(&in->dl, IPA_NTN_TX_DIR)) {
+ IPAERR("fail to send cmd to uc for dl pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ outp->dl_uc_db_pa = IPA_UC_NTN_DB_PA_TX;
+ ep_dl->uc_offload_state |= IPA_UC_OFFLOAD_CONNECTED;
+ IPAERR("client %d (ep: %d) connected\n", in->dl.client,
+ ipa_ep_idx_dl);
+
+fail:
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ return result;
+}
+
+/**
+ * ipa2_tear_down_uc_offload_pipes() - tear down uc offload pipes
+ */
+
+int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul,
+ int ipa_ep_idx_dl)
+{
+ struct ipa_mem_buffer cmd;
+ struct ipa_ep_context *ep_ul, *ep_dl;
+ struct IpaHwOffloadCommonChCmdData_t *cmd_data;
+ union IpaHwNtnCommonChCmdData_t *tear;
+ int result = 0;
+
+ IPADBG("ep_ul = %d\n", ipa_ep_idx_ul);
+ IPADBG("ep_dl = %d\n", ipa_ep_idx_dl);
+
+ ep_ul = &ipa_ctx->ep[ipa_ep_idx_ul];
+ ep_dl = &ipa_ctx->ep[ipa_ep_idx_dl];
+
+ if (ep_ul->uc_offload_state != IPA_UC_OFFLOAD_CONNECTED ||
+ ep_dl->uc_offload_state != IPA_UC_OFFLOAD_CONNECTED) {
+ IPAERR("channel bad state: ul %d dl %d\n",
+ ep_ul->uc_offload_state, ep_dl->uc_offload_state);
+ return -EFAULT;
+ }
+
+ cmd.size = sizeof(*cmd_data);
+ cmd.base = dma_alloc_coherent(ipa_ctx->uc_pdev, cmd.size,
+ &cmd.phys_base, GFP_KERNEL);
+ if (cmd.base == NULL) {
+ IPAERR("fail to get DMA memory.\n");
+ return -ENOMEM;
+ }
+
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+ /* teardown the UL pipe */
+ cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base;
+ cmd_data->protocol = IPA_HW_FEATURE_NTN;
+
+ tear = &cmd_data->CommonCh_params.NtnCommonCh_params;
+ tear->params.ipa_pipe_number = ipa_ep_idx_ul;
+ result = ipa_uc_send_cmd((u32)(cmd.phys_base),
+ IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
+ IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
+ false, 10*HZ);
+ if (result) {
+ IPAERR("fail to tear down ul pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ ipa_disable_data_path(ipa_ep_idx_ul);
+ ipa_delete_dflt_flt_rules(ipa_ep_idx_ul);
+ memset(&ipa_ctx->ep[ipa_ep_idx_ul], 0, sizeof(struct ipa_ep_context));
+ IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul);
+
+ /* teardown the DL pipe */
+ tear->params.ipa_pipe_number = ipa_ep_idx_dl;
+ result = ipa_uc_send_cmd((u32)(cmd.phys_base),
+ IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
+ IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
+ false, 10*HZ);
+ if (result) {
+ IPAERR("fail to tear down ul pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ ipa_disable_data_path(ipa_ep_idx_dl);
+ memset(&ipa_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa_ep_context));
+ IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl);
+
+fail:
+ dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ return result;
+}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h
new file mode 100644
index 000000000000..3bec471b4656
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h
@@ -0,0 +1,514 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _IPA_UC_OFFLOAD_I_H_
+#define _IPA_UC_OFFLOAD_I_H_
+
+#include <linux/ipa.h>
+#include "ipa_i.h"
+
+/*
+ * Neutrino protocol related data structures
+ */
+
+#define IPA_UC_MAX_NTN_TX_CHANNELS 1
+#define IPA_UC_MAX_NTN_RX_CHANNELS 1
+
+#define IPA_NTN_TX_DIR 1
+#define IPA_NTN_RX_DIR 2
+
+/**
+ * @brief Enum value determined based on the feature it
+ * corresponds to
+ * +----------------+----------------+
+ * | 3 bits | 5 bits |
+ * +----------------+----------------+
+ * | HW_FEATURE | OPCODE |
+ * +----------------+----------------+
+ *
+ */
+#define FEATURE_ENUM_VAL(feature, opcode) ((feature << 5) | opcode)
+#define EXTRACT_UC_FEATURE(value) (value >> 5)
+
+#define IPA_HW_NUM_FEATURES 0x8
+
+/**
+ * enum ipa_hw_features - Values that represent the features supported in IPA HW
+ * @IPA_HW_FEATURE_COMMON : Feature related to common operation of IPA HW
+ * @IPA_HW_FEATURE_MHI : Feature related to MHI operation in IPA HW
+ * @IPA_HW_FEATURE_WDI : Feature related to WDI operation in IPA HW
+ * @IPA_HW_FEATURE_NTN : Feature related to NTN operation in IPA HW
+ * @IPA_HW_FEATURE_OFFLOAD : Feature related to NTN operation in IPA HW
+*/
+enum ipa_hw_features {
+ IPA_HW_FEATURE_COMMON = 0x0,
+ IPA_HW_FEATURE_MHI = 0x1,
+ IPA_HW_FEATURE_WDI = 0x3,
+ IPA_HW_FEATURE_NTN = 0x4,
+ IPA_HW_FEATURE_OFFLOAD = 0x5,
+ IPA_HW_FEATURE_MAX = IPA_HW_NUM_FEATURES
+};
+
+/**
+ * struct IpaHwSharedMemCommonMapping_t - Structure referring to the common
+ * section in 128B shared memory located in offset zero of SW Partition in IPA
+ * SRAM.
+ * @cmdOp : CPU->HW command opcode. See IPA_CPU_2_HW_COMMANDS
+ * @cmdParams : CPU->HW command parameter. The parameter filed can hold 32 bits
+ * of parameters (immediate parameters) and point on structure in
+ * system memory (in such case the address must be accessible
+ * for HW)
+ * @responseOp : HW->CPU response opcode. See IPA_HW_2_CPU_RESPONSES
+ * @responseParams : HW->CPU response parameter. The parameter filed can hold
+ * 32 bits of parameters (immediate parameters) and point
+ * on structure in system memory
+ * @eventOp : HW->CPU event opcode. See IPA_HW_2_CPU_EVENTS
+ * @eventParams : HW->CPU event parameter. The parameter filed can hold 32 bits
+ * of parameters (immediate parameters) and point on
+ * structure in system memory
+ * @firstErrorAddress : Contains the address of first error-source on SNOC
+ * @hwState : State of HW. The state carries information regarding the error
+ * type.
+ * @warningCounter : The warnings counter. The counter carries information
+ * regarding non fatal errors in HW
+ * @interfaceVersionCommon : The Common interface version as reported by HW
+ *
+ * The shared memory is used for communication between IPA HW and CPU.
+ */
+struct IpaHwSharedMemCommonMapping_t {
+ u8 cmdOp;
+ u8 reserved_01;
+ u16 reserved_03_02;
+ u32 cmdParams;
+ u8 responseOp;
+ u8 reserved_09;
+ u16 reserved_0B_0A;
+ u32 responseParams;
+ u8 eventOp;
+ u8 reserved_11;
+ u16 reserved_13_12;
+ u32 eventParams;
+ u32 reserved_1B_18;
+ u32 firstErrorAddress;
+ u8 hwState;
+ u8 warningCounter;
+ u16 reserved_23_22;
+ u16 interfaceVersionCommon;
+ u16 reserved_27_26;
+} __packed;
+
+/**
+ * union IpaHwFeatureInfoData_t - parameters for stats/config blob
+ *
+ * @offset : Location of a feature within the EventInfoData
+ * @size : Size of the feature
+ */
+union IpaHwFeatureInfoData_t {
+ struct IpaHwFeatureInfoParams_t {
+ u32 offset:16;
+ u32 size:16;
+ } __packed params;
+ u32 raw32b;
+} __packed;
+
+/**
+ * struct IpaHwEventInfoData_t - Structure holding the parameters for
+ * statistics and config info
+ *
+ * @baseAddrOffset : Base Address Offset of the statistics or config
+ * structure from IPA_WRAPPER_BASE
+ * @IpaHwFeatureInfoData_t : Location and size of each feature within
+ * the statistics or config structure
+ *
+ * @note Information about each feature in the featureInfo[]
+ * array is populated at predefined indices per the IPA_HW_FEATURES
+ * enum definition
+ */
+struct IpaHwEventInfoData_t {
+ u32 baseAddrOffset;
+ union IpaHwFeatureInfoData_t featureInfo[IPA_HW_NUM_FEATURES];
+} __packed;
+
+/**
+ * struct IpaHwEventLogInfoData_t - Structure holding the parameters for
+ * IPA_HW_2_CPU_EVENT_LOG_INFO Event
+ *
+ * @featureMask : Mask indicating the features enabled in HW.
+ * Refer IPA_HW_FEATURE_MASK
+ * @circBuffBaseAddrOffset : Base Address Offset of the Circular Event
+ * Log Buffer structure
+ * @statsInfo : Statistics related information
+ * @configInfo : Configuration related information
+ *
+ * @note The offset location of this structure from IPA_WRAPPER_BASE
+ * will be provided as Event Params for the IPA_HW_2_CPU_EVENT_LOG_INFO
+ * Event
+ */
+struct IpaHwEventLogInfoData_t {
+ u32 featureMask;
+ u32 circBuffBaseAddrOffset;
+ struct IpaHwEventInfoData_t statsInfo;
+ struct IpaHwEventInfoData_t configInfo;
+
+} __packed;
+
+/**
+ * struct ipa_uc_ntn_ctx
+ * @ntn_uc_stats_ofst: Neutrino stats offset
+ * @ntn_uc_stats_mmio: Neutrino stats
+ * @priv: private data of client
+ * @uc_ready_cb: uc Ready cb
+ */
+struct ipa_uc_ntn_ctx {
+ u32 ntn_uc_stats_ofst;
+ struct IpaHwStatsNTNInfoData_t *ntn_uc_stats_mmio;
+ void *priv;
+ ipa_uc_ready_cb uc_ready_cb;
+};
+
+/**
+ * enum ipa_hw_2_cpu_ntn_events - Values that represent HW event
+ * to be sent to CPU
+ * @IPA_HW_2_CPU_EVENT_NTN_ERROR : Event to specify that HW
+ * detected an error in NTN
+ *
+ */
+enum ipa_hw_2_cpu_ntn_events {
+ IPA_HW_2_CPU_EVENT_NTN_ERROR =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_NTN, 0),
+};
+
+
+/**
+ * enum ipa_hw_ntn_errors - NTN specific error types.
+ * @IPA_HW_NTN_ERROR_NONE : No error persists
+ * @IPA_HW_NTN_CHANNEL_ERROR : Error is specific to channel
+ */
+enum ipa_hw_ntn_errors {
+ IPA_HW_NTN_ERROR_NONE = 0,
+ IPA_HW_NTN_CHANNEL_ERROR = 1
+};
+
+/**
+ * enum ipa_hw_ntn_channel_states - Values that represent NTN
+ * channel state machine.
+ * @IPA_HW_NTN_CHANNEL_STATE_INITED_DISABLED : Channel is
+ * initialized but disabled
+ * @IPA_HW_NTN_CHANNEL_STATE_RUNNING : Channel is running.
+ * Entered after SET_UP_COMMAND is processed successfully
+ * @IPA_HW_NTN_CHANNEL_STATE_ERROR : Channel is in error state
+ * @IPA_HW_NTN_CHANNEL_STATE_INVALID : Invalid state. Shall not
+ * be in use in operational scenario
+ *
+ * These states apply to both Tx and Rx paths. These do not reflect the
+ * sub-state the state machine may be in.
+ */
+enum ipa_hw_ntn_channel_states {
+ IPA_HW_NTN_CHANNEL_STATE_INITED_DISABLED = 1,
+ IPA_HW_NTN_CHANNEL_STATE_RUNNING = 2,
+ IPA_HW_NTN_CHANNEL_STATE_ERROR = 3,
+ IPA_HW_NTN_CHANNEL_STATE_INVALID = 0xFF
+};
+
+/**
+ * enum ipa_hw_ntn_channel_errors - List of NTN Channel error
+ * types. This is present in the event param
+ * @IPA_HW_NTN_CH_ERR_NONE: No error persists
+ * @IPA_HW_NTN_TX_FSM_ERROR: Error in the state machine
+ * transition
+ * @IPA_HW_NTN_TX_COMP_RE_FETCH_FAIL: Error while calculating
+ * num RE to bring
+ * @IPA_HW_NTN_RX_RING_WP_UPDATE_FAIL: Write pointer update
+ * failed in Rx ring
+ * @IPA_HW_NTN_RX_FSM_ERROR: Error in the state machine
+ * transition
+ * @IPA_HW_NTN_RX_CACHE_NON_EMPTY:
+ * @IPA_HW_NTN_CH_ERR_RESERVED:
+ *
+ * These states apply to both Tx and Rx paths. These do not
+ * reflect the sub-state the state machine may be in.
+ */
+enum ipa_hw_ntn_channel_errors {
+ IPA_HW_NTN_CH_ERR_NONE = 0,
+ IPA_HW_NTN_TX_RING_WP_UPDATE_FAIL = 1,
+ IPA_HW_NTN_TX_FSM_ERROR = 2,
+ IPA_HW_NTN_TX_COMP_RE_FETCH_FAIL = 3,
+ IPA_HW_NTN_RX_RING_WP_UPDATE_FAIL = 4,
+ IPA_HW_NTN_RX_FSM_ERROR = 5,
+ IPA_HW_NTN_RX_CACHE_NON_EMPTY = 6,
+ IPA_HW_NTN_CH_ERR_RESERVED = 0xFF
+};
+
+
+/**
+ * struct IpaHwNtnSetUpCmdData_t - Ntn setup command data
+ * @ring_base_pa: physical address of the base of the Tx/Rx NTN
+ * ring
+ * @buff_pool_base_pa: physical address of the base of the Tx/Rx
+ * buffer pool
+ * @ntn_ring_size: size of the Tx/Rx NTN ring
+ * @num_buffers: Rx/tx buffer pool size
+ * @ntn_reg_base_ptr_pa: physical address of the Tx/Rx NTN
+ * Ring's tail pointer
+ * @ipa_pipe_number: IPA pipe number that has to be used for the
+ * Tx/Rx path
+ * @dir: Tx/Rx Direction
+ * @data_buff_size: size of the each data buffer allocated in
+ * DDR
+ */
+struct IpaHwNtnSetUpCmdData_t {
+ u32 ring_base_pa;
+ u32 buff_pool_base_pa;
+ u16 ntn_ring_size;
+ u16 num_buffers;
+ u32 ntn_reg_base_ptr_pa;
+ u8 ipa_pipe_number;
+ u8 dir;
+ u16 data_buff_size;
+
+} __packed;
+
+/**
+ * struct IpaHwNtnCommonChCmdData_t - Structure holding the
+ * parameters for Ntn Tear down command data params
+ *
+ *@ipa_pipe_number: IPA pipe number. This could be Tx or an Rx pipe
+ */
+union IpaHwNtnCommonChCmdData_t {
+ struct IpaHwNtnCommonChCmdParams_t {
+ u32 ipa_pipe_number :8;
+ u32 reserved :24;
+ } __packed params;
+ uint32_t raw32b;
+} __packed;
+
+
+/**
+ * struct IpaHwNTNErrorEventData_t - Structure holding the
+ * IPA_HW_2_CPU_EVENT_NTN_ERROR event. The parameters are passed
+ * as immediate params in the shared memory
+ *
+ *@ntn_error_type: type of NTN error (IPA_HW_NTN_ERRORS)
+ *@ipa_pipe_number: IPA pipe number on which error has happened
+ * Applicable only if error type indicates channel error
+ *@ntn_ch_err_type: Information about the channel error (if
+ * available)
+ */
+union IpaHwNTNErrorEventData_t {
+ struct IpaHwNTNErrorEventParams_t {
+ u32 ntn_error_type :8;
+ u32 reserved :8;
+ u32 ipa_pipe_number :8;
+ u32 ntn_ch_err_type :8;
+ } __packed params;
+ uint32_t raw32b;
+} __packed;
+
+/**
+ * struct NTNRxInfoData_t - NTN Structure holding the
+ * Rx pipe information
+ *
+ *@max_outstanding_pkts: Number of outstanding packets in Rx
+ * Ring
+ *@num_pkts_processed: Number of packets processed - cumulative
+ *@rx_ring_rp_value: Read pointer last advertized to the WLAN FW
+ *
+ *@ntn_ch_err_type: Information about the channel error (if
+ * available)
+ *@rx_ind_ring_stats:
+ *@bam_stats:
+ *@num_bam_int_handled: Number of Bam Interrupts handled by FW
+ *@num_db: Number of times the doorbell was rung
+ *@num_unexpected_db: Number of unexpected doorbells
+ *@num_pkts_in_dis_uninit_state:
+ *@num_bam_int_handled_while_not_in_bam: Number of Bam
+ * Interrupts handled by FW
+ *@num_bam_int_handled_while_in_bam_state: Number of Bam
+ * Interrupts handled by FW
+ */
+struct NTNRxInfoData_t {
+ u32 max_outstanding_pkts;
+ u32 num_pkts_processed;
+ u32 rx_ring_rp_value;
+ struct IpaHwRingStats_t rx_ind_ring_stats;
+ struct IpaHwBamStats_t bam_stats;
+ u32 num_bam_int_handled;
+ u32 num_db;
+ u32 num_unexpected_db;
+ u32 num_pkts_in_dis_uninit_state;
+ u32 num_bam_int_handled_while_not_in_bam;
+ u32 num_bam_int_handled_while_in_bam_state;
+} __packed;
+
+
+/**
+ * struct NTNTxInfoData_t - Structure holding the NTN Tx channel
+ * Ensure that this is always word aligned
+ *
+ *@num_pkts_processed: Number of packets processed - cumulative
+ *@tail_ptr_val: Latest value of doorbell written to copy engine
+ *@num_db_fired: Number of DB from uC FW to Copy engine
+ *
+ *@tx_comp_ring_stats:
+ *@bam_stats:
+ *@num_db: Number of times the doorbell was rung
+ *@num_unexpected_db: Number of unexpected doorbells
+ *@num_bam_int_handled: Number of Bam Interrupts handled by FW
+ *@num_bam_int_in_non_running_state: Number of Bam interrupts
+ * while not in Running state
+ *@num_qmb_int_handled: Number of QMB interrupts handled
+ *@num_bam_int_handled_while_wait_for_bam: Number of times the
+ * Imm Cmd is injected due to fw_desc change
+ */
+struct NTNTxInfoData_t {
+ u32 num_pkts_processed;
+ u32 tail_ptr_val;
+ u32 num_db_fired;
+ struct IpaHwRingStats_t tx_comp_ring_stats;
+ struct IpaHwBamStats_t bam_stats;
+ u32 num_db;
+ u32 num_unexpected_db;
+ u32 num_bam_int_handled;
+ u32 num_bam_int_in_non_running_state;
+ u32 num_qmb_int_handled;
+ u32 num_bam_int_handled_while_wait_for_bam;
+ u32 num_bam_int_handled_while_not_in_bam;
+} __packed;
+
+
+/**
+ * struct IpaHwStatsNTNInfoData_t - Structure holding the NTN Tx
+ * channel Ensure that this is always word aligned
+ *
+ */
+struct IpaHwStatsNTNInfoData_t {
+ struct NTNRxInfoData_t rx_ch_stats[IPA_UC_MAX_NTN_RX_CHANNELS];
+ struct NTNTxInfoData_t tx_ch_stats[IPA_UC_MAX_NTN_TX_CHANNELS];
+} __packed;
+
+
+/*
+ * uC offload related data structures
+ */
+#define IPA_UC_OFFLOAD_CONNECTED BIT(0)
+#define IPA_UC_OFFLOAD_ENABLED BIT(1)
+#define IPA_UC_OFFLOAD_RESUMED BIT(2)
+
+/**
+ * enum ipa_cpu_2_hw_offload_commands - Values that represent
+ * the offload commands from CPU
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP : Command to set up
+ * Offload protocol's Tx/Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_RX_SET_UP : Command to tear down
+ * Offload protocol's Tx/ Rx Path
+ */
+enum ipa_cpu_2_hw_offload_commands {
+ IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 1),
+ IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
+};
+
+
+/**
+ * enum ipa_hw_offload_channel_states - Values that represent
+ * offload channel state machine.
+ * @IPA_HW_OFFLOAD_CHANNEL_STATE_INITED_DISABLED : Channel is initialized
+ * but disabled
+ * @IPA_HW_OFFLOAD_CHANNEL_STATE_RUNNING : Channel is running. Entered after
+ * SET_UP_COMMAND is processed successfully
+ * @IPA_HW_OFFLOAD_CHANNEL_STATE_ERROR : Channel is in error state
+ * @IPA_HW_OFFLOAD_CHANNEL_STATE_INVALID : Invalid state. Shall not be in use
+ * in operational scenario
+ *
+ * These states apply to both Tx and Rx paths. These do not
+ * reflect the sub-state the state machine may be in
+ */
+enum ipa_hw_offload_channel_states {
+ IPA_HW_OFFLOAD_CHANNEL_STATE_INITED_DISABLED = 1,
+ IPA_HW_OFFLOAD_CHANNEL_STATE_RUNNING = 2,
+ IPA_HW_OFFLOAD_CHANNEL_STATE_ERROR = 3,
+ IPA_HW_OFFLOAD_CHANNEL_STATE_INVALID = 0xFF
+};
+
+
+/**
+ * enum ipa_hw_2_cpu_cmd_resp_status - Values that represent
+ * offload related command response status to be sent to CPU.
+ */
+enum ipa_hw_2_cpu_offload_cmd_resp_status {
+ IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 0),
+ IPA_HW_2_CPU_OFFLOAD_MAX_TX_CHANNELS =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 1),
+ IPA_HW_2_CPU_OFFLOAD_TX_RING_OVERRUN_POSSIBILITY =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 2),
+ IPA_HW_2_CPU_OFFLOAD_TX_RING_SET_UP_FAILURE =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 3),
+ IPA_HW_2_CPU_OFFLOAD_TX_RING_PARAMS_UNALIGNED =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 4),
+ IPA_HW_2_CPU_OFFLOAD_UNKNOWN_TX_CHANNEL =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 5),
+ IPA_HW_2_CPU_OFFLOAD_TX_INVALID_FSM_TRANSITION =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 6),
+ IPA_HW_2_CPU_OFFLOAD_TX_FSM_TRANSITION_ERROR =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 7),
+ IPA_HW_2_CPU_OFFLOAD_MAX_RX_CHANNELS =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 8),
+ IPA_HW_2_CPU_OFFLOAD_RX_RING_PARAMS_UNALIGNED =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 9),
+ IPA_HW_2_CPU_OFFLOAD_RX_RING_SET_UP_FAILURE =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 10),
+ IPA_HW_2_CPU_OFFLOAD_UNKNOWN_RX_CHANNEL =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 11),
+ IPA_HW_2_CPU_OFFLOAD_RX_INVALID_FSM_TRANSITION =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 12),
+ IPA_HW_2_CPU_OFFLOAD_RX_FSM_TRANSITION_ERROR =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 13),
+ IPA_HW_2_CPU_OFFLOAD_RX_RING_OVERRUN_POSSIBILITY =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 14),
+};
+
+/**
+ * struct IpaHwSetUpCmd -
+ *
+ *
+ */
+union IpaHwSetUpCmd {
+ struct IpaHwNtnSetUpCmdData_t NtnSetupCh_params;
+} __packed;
+
+/**
+ * struct IpaHwOffloadSetUpCmdData_t -
+ *
+ *
+ */
+struct IpaHwOffloadSetUpCmdData_t {
+ u8 protocol;
+ union IpaHwSetUpCmd SetupCh_params;
+} __packed;
+
+/**
+ * struct IpaHwCommonChCmd - Structure holding the parameters
+ * for IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN
+ *
+ *
+ */
+union IpaHwCommonChCmd {
+ union IpaHwNtnCommonChCmdData_t NtnCommonCh_params;
+} __packed;
+
+struct IpaHwOffloadCommonChCmdData_t {
+ u8 protocol;
+ union IpaHwCommonChCmd CommonCh_params;
+} __packed;
+
+#endif /* _IPA_UC_OFFLOAD_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c
index a45b51ad7b7b..a1072638b281 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c
@@ -963,7 +963,7 @@ int ipa2_connect_wdi_pipe(struct ipa_wdi_in_params *in,
IPA_ACTIVE_CLIENTS_DEC_EP(in->sys.client);
dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
- ep->wdi_state |= IPA_WDI_CONNECTED;
+ ep->uc_offload_state |= IPA_WDI_CONNECTED;
IPADBG("client %d (ep: %d) connected\n", in->sys.client, ipa_ep_idx);
return 0;
@@ -1001,7 +1001,7 @@ int ipa2_disconnect_wdi_pipe(u32 clnt_hdl)
if (clnt_hdl >= ipa_ctx->ipa_num_pipes ||
ipa_ctx->ep[clnt_hdl].valid == 0) {
- IPAERR("bad parm.\n");
+ IPAERR("bad parm, %d\n", clnt_hdl);
return -EINVAL;
}
@@ -1013,8 +1013,8 @@ int ipa2_disconnect_wdi_pipe(u32 clnt_hdl)
ep = &ipa_ctx->ep[clnt_hdl];
- if (ep->wdi_state != IPA_WDI_CONNECTED) {
- IPAERR("WDI channel bad state %d\n", ep->wdi_state);
+ if (ep->uc_offload_state != IPA_WDI_CONNECTED) {
+ IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
return -EFAULT;
}
@@ -1067,7 +1067,7 @@ int ipa2_enable_wdi_pipe(u32 clnt_hdl)
if (clnt_hdl >= ipa_ctx->ipa_num_pipes ||
ipa_ctx->ep[clnt_hdl].valid == 0) {
- IPAERR("bad parm.\n");
+ IPAERR("bad parm, %d\n", clnt_hdl);
return -EINVAL;
}
@@ -1079,8 +1079,8 @@ int ipa2_enable_wdi_pipe(u32 clnt_hdl)
ep = &ipa_ctx->ep[clnt_hdl];
- if (ep->wdi_state != IPA_WDI_CONNECTED) {
- IPAERR("WDI channel bad state %d\n", ep->wdi_state);
+ if (ep->uc_offload_state != IPA_WDI_CONNECTED) {
+ IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
return -EFAULT;
}
@@ -1105,7 +1105,7 @@ int ipa2_enable_wdi_pipe(u32 clnt_hdl)
}
IPA_ACTIVE_CLIENTS_DEC_EP(ipa2_get_client_mapping(clnt_hdl));
- ep->wdi_state |= IPA_WDI_ENABLED;
+ ep->uc_offload_state |= IPA_WDI_ENABLED;
IPADBG("client (ep: %d) enabled\n", clnt_hdl);
uc_timeout:
@@ -1135,7 +1135,7 @@ int ipa2_disable_wdi_pipe(u32 clnt_hdl)
if (clnt_hdl >= ipa_ctx->ipa_num_pipes ||
ipa_ctx->ep[clnt_hdl].valid == 0) {
- IPAERR("bad parm.\n");
+ IPAERR("bad parm, %d\n", clnt_hdl);
return -EINVAL;
}
@@ -1147,8 +1147,8 @@ int ipa2_disable_wdi_pipe(u32 clnt_hdl)
ep = &ipa_ctx->ep[clnt_hdl];
- if (ep->wdi_state != (IPA_WDI_CONNECTED | IPA_WDI_ENABLED)) {
- IPAERR("WDI channel bad state %d\n", ep->wdi_state);
+ if (ep->uc_offload_state != (IPA_WDI_CONNECTED | IPA_WDI_ENABLED)) {
+ IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
return -EFAULT;
}
@@ -1206,7 +1206,7 @@ int ipa2_disable_wdi_pipe(u32 clnt_hdl)
}
IPA_ACTIVE_CLIENTS_DEC_EP(ipa2_get_client_mapping(clnt_hdl));
- ep->wdi_state &= ~IPA_WDI_ENABLED;
+ ep->uc_offload_state &= ~IPA_WDI_ENABLED;
IPADBG("client (ep: %d) disabled\n", clnt_hdl);
uc_timeout:
@@ -1235,7 +1235,7 @@ int ipa2_resume_wdi_pipe(u32 clnt_hdl)
if (clnt_hdl >= ipa_ctx->ipa_num_pipes ||
ipa_ctx->ep[clnt_hdl].valid == 0) {
- IPAERR("bad parm.\n");
+ IPAERR("bad parm, %d\n", clnt_hdl);
return -EINVAL;
}
@@ -1247,8 +1247,8 @@ int ipa2_resume_wdi_pipe(u32 clnt_hdl)
ep = &ipa_ctx->ep[clnt_hdl];
- if (ep->wdi_state != (IPA_WDI_CONNECTED | IPA_WDI_ENABLED)) {
- IPAERR("WDI channel bad state %d\n", ep->wdi_state);
+ if (ep->uc_offload_state != (IPA_WDI_CONNECTED | IPA_WDI_ENABLED)) {
+ IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
return -EFAULT;
}
@@ -1273,7 +1273,7 @@ int ipa2_resume_wdi_pipe(u32 clnt_hdl)
else
IPADBG("client (ep: %d) un-susp/delay\n", clnt_hdl);
- ep->wdi_state |= IPA_WDI_RESUMED;
+ ep->uc_offload_state |= IPA_WDI_RESUMED;
IPADBG("client (ep: %d) resumed\n", clnt_hdl);
uc_timeout:
@@ -1302,7 +1302,7 @@ int ipa2_suspend_wdi_pipe(u32 clnt_hdl)
if (clnt_hdl >= ipa_ctx->ipa_num_pipes ||
ipa_ctx->ep[clnt_hdl].valid == 0) {
- IPAERR("bad parm.\n");
+ IPAERR("bad parm, %d\n", clnt_hdl);
return -EINVAL;
}
@@ -1314,9 +1314,9 @@ int ipa2_suspend_wdi_pipe(u32 clnt_hdl)
ep = &ipa_ctx->ep[clnt_hdl];
- if (ep->wdi_state != (IPA_WDI_CONNECTED | IPA_WDI_ENABLED |
+ if (ep->uc_offload_state != (IPA_WDI_CONNECTED | IPA_WDI_ENABLED |
IPA_WDI_RESUMED)) {
- IPAERR("WDI channel bad state %d\n", ep->wdi_state);
+ IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
return -EFAULT;
}
@@ -1369,7 +1369,7 @@ int ipa2_suspend_wdi_pipe(u32 clnt_hdl)
ipa_ctx->tag_process_before_gating = true;
IPA_ACTIVE_CLIENTS_DEC_EP(ipa2_get_client_mapping(clnt_hdl));
- ep->wdi_state &= ~IPA_WDI_RESUMED;
+ ep->uc_offload_state &= ~IPA_WDI_RESUMED;
IPADBG("client (ep: %d) suspended\n", clnt_hdl);
uc_timeout:
@@ -1384,7 +1384,7 @@ int ipa_write_qmapid_wdi_pipe(u32 clnt_hdl, u8 qmap_id)
if (clnt_hdl >= ipa_ctx->ipa_num_pipes ||
ipa_ctx->ep[clnt_hdl].valid == 0) {
- IPAERR("bad parm.\n");
+ IPAERR("bad parm, %d\n", clnt_hdl);
return -EINVAL;
}
@@ -1396,8 +1396,8 @@ int ipa_write_qmapid_wdi_pipe(u32 clnt_hdl, u8 qmap_id)
ep = &ipa_ctx->ep[clnt_hdl];
- if (!(ep->wdi_state & IPA_WDI_CONNECTED)) {
- IPAERR("WDI channel bad state %d\n", ep->wdi_state);
+ if (!(ep->uc_offload_state & IPA_WDI_CONNECTED)) {
+ IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
return -EFAULT;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
index 0dd10743a01e..b627cd1fc833 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
@@ -43,6 +43,11 @@
#define IPA_AGGR_GRAN_MAX (32)
#define IPA_EOT_COAL_GRAN_MIN (1)
#define IPA_EOT_COAL_GRAN_MAX (16)
+#define MSEC 1000
+#define MIN_RX_POLL_TIME 1
+#define MAX_RX_POLL_TIME 5
+#define UPPER_CUTOFF 50
+#define LOWER_CUTOFF 10
#define IPA_DEFAULT_SYS_YELLOW_WM 32
@@ -3623,6 +3628,30 @@ void ipa_dump_buff_internal(void *base, dma_addr_t phy_base, u32 size)
}
/**
+ * void ipa_rx_timeout_min_max_calc() - calc min max timeout time of rx polling
+ * @time: time fom dtsi entry or from debugfs file system
+ * @min: rx polling min timeout
+ * @max: rx polling max timeout
+ * Maximum time could be of 10Msec allowed.
+ */
+void ipa_rx_timeout_min_max_calc(u32 *min, u32 *max, s8 time)
+{
+ if ((time >= MIN_RX_POLL_TIME) &&
+ (time <= MAX_RX_POLL_TIME)) {
+ *min = (time * MSEC) + LOWER_CUTOFF;
+ *max = (time * MSEC) + UPPER_CUTOFF;
+ } else {
+ /* Setting up the default min max time */
+ IPADBG("Setting up default rx polling timeout\n");
+ *min = (MIN_RX_POLL_TIME * MSEC) +
+ LOWER_CUTOFF;
+ *max = (MIN_RX_POLL_TIME * MSEC) +
+ UPPER_CUTOFF;
+ }
+ IPADBG("Rx polling timeout Min = %u len = %u\n", *min, *max);
+}
+
+/**
* ipa_pipe_mem_init() - initialize the pipe memory
* @start_ofst: start offset
* @size: size
@@ -4920,6 +4949,7 @@ int ipa2_bind_api_controller(enum ipa_hw_type ipa_hw_type,
api_ctrl->ipa_disconnect = ipa2_disconnect;
api_ctrl->ipa_reset_endpoint = ipa2_reset_endpoint;
api_ctrl->ipa_clear_endpoint_delay = ipa2_clear_endpoint_delay;
+ api_ctrl->ipa_disable_endpoint = ipa2_disable_endpoint;
api_ctrl->ipa_cfg_ep = ipa2_cfg_ep;
api_ctrl->ipa_cfg_ep_nat = ipa2_cfg_ep_nat;
api_ctrl->ipa_cfg_ep_hdr = ipa2_cfg_ep_hdr;
@@ -5054,6 +5084,7 @@ int ipa2_bind_api_controller(enum ipa_hw_type ipa_hw_type,
api_ctrl->ipa_get_dma_dev = ipa2_get_dma_dev;
api_ctrl->ipa_get_gsi_ep_info = ipa2_get_gsi_ep_info;
api_ctrl->ipa_stop_gsi_channel = ipa2_stop_gsi_channel;
+ api_ctrl->ipa_register_ipa_ready_cb = ipa2_register_ipa_ready_cb;
api_ctrl->ipa_inc_client_enable_clks = ipa2_inc_client_enable_clks;
api_ctrl->ipa_dec_client_disable_clks = ipa2_dec_client_disable_clks;
api_ctrl->ipa_inc_client_enable_clks_no_block =
@@ -5068,6 +5099,9 @@ int ipa2_bind_api_controller(enum ipa_hw_type ipa_hw_type,
api_ctrl->ipa_get_ipc_logbuf_low = ipa2_get_ipc_logbuf_low;
api_ctrl->ipa_rx_poll = ipa2_rx_poll;
api_ctrl->ipa_recycle_wan_skb = ipa2_recycle_wan_skb;
+ api_ctrl->ipa_setup_uc_ntn_pipes = ipa2_setup_uc_ntn_pipes;
+ api_ctrl->ipa_tear_down_uc_offload_pipes =
+ ipa2_tear_down_uc_offload_pipes;
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index 50e820992f29..2420dd78b4c0 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -1144,14 +1144,16 @@ static void apps_ipa_tx_complete_notify(void *priv,
struct net_device *dev = (struct net_device *)priv;
struct wwan_private *wwan_ptr;
- if (evt != IPA_WRITE_DONE) {
- IPAWANDBG("unsupported event on Tx callback\n");
+ if (dev != ipa_netdevs[0]) {
+ IPAWANDBG("Received pre-SSR packet completion\n");
+ dev_kfree_skb_any(skb);
return;
}
- if (dev != ipa_netdevs[0]) {
- IPAWANDBG("Received pre-SSR packet completion\n");
+ if (evt != IPA_WRITE_DONE) {
+ IPAWANERR("unsupported evt on Tx callback, Drop the packet\n");
dev_kfree_skb_any(skb);
+ dev->stats.tx_dropped++;
return;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/Makefile b/drivers/platform/msm/ipa/ipa_v3/Makefile
index 9653dd6d27f2..a4faaea715a8 100644
--- a/drivers/platform/msm/ipa/ipa_v3/Makefile
+++ b/drivers/platform/msm/ipa/ipa_v3/Makefile
@@ -3,6 +3,6 @@ obj-$(CONFIG_IPA3) += ipahal/
obj-$(CONFIG_IPA3) += ipat.o
ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o \
ipa_utils.o ipa_nat.o ipa_intf.o teth_bridge.o ipa_interrupts.o \
- ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o
+ ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o ipa_uc_ntn.o
obj-$(CONFIG_RMNET_IPA3) += rmnet_ipa.o ipa_qmi_service_v01.o ipa_qmi_service.o rmnet_ipa_fd_ioctl.o
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index c553be1ad717..4db07bad7d93 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -37,6 +37,7 @@
#include <linux/hashtable.h>
#include <linux/hash.h>
#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/smem.h>
#define IPA_SUBSYSTEM_NAME "ipa_fws"
#include "ipa_i.h"
#include "../ipa_rm_i.h"
@@ -75,6 +76,17 @@
#define IPA3_ACTIVE_CLIENT_LOG_TYPE_RESOURCE 2
#define IPA3_ACTIVE_CLIENT_LOG_TYPE_SPECIAL 3
+#define IPA_SMEM_SIZE (8 * 1024)
+
+/* round addresses for closes page per SMMU requirements */
+#define IPA_SMMU_ROUND_TO_PAGE(iova, pa, size, iova_p, pa_p, size_p) \
+ do { \
+ (iova_p) = rounddown((iova), PAGE_SIZE); \
+ (pa_p) = rounddown((pa), PAGE_SIZE); \
+ (size_p) = roundup((size) + (pa) - (pa_p), PAGE_SIZE); \
+ } while (0)
+
+
/* The relative location in /lib/firmware where the FWs will reside */
#define IPA_FWS_PATH "ipa/ipa_fws.elf"
@@ -1887,44 +1899,43 @@ static int ipa3_q6_clean_q6_tables(void)
if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v4, IPA_RULE_HASHABLE)) {
IPAERR("failed to clean q6 flt tbls (v4/hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v6, IPA_RULE_HASHABLE)) {
IPAERR("failed to clean q6 flt tbls (v6/hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v4, IPA_RULE_NON_HASHABLE)) {
IPAERR("failed to clean q6 flt tbls (v4/non-hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v6, IPA_RULE_NON_HASHABLE)) {
IPAERR("failed to clean q6 flt tbls (v6/non-hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v4, IPA_RULE_HASHABLE)) {
IPAERR("failed to clean q6 rt tbls (v4/hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v6, IPA_RULE_HASHABLE)) {
IPAERR("failed to clean q6 rt tbls (v6/hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v4, IPA_RULE_NON_HASHABLE)) {
IPAERR("failed to clean q6 rt tbls (v4/non-hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v6, IPA_RULE_NON_HASHABLE)) {
IPAERR("failed to clean q6 rt tbls (v6/non-hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
/* Flush rules cache */
desc = kzalloc(sizeof(struct ipa3_desc), GFP_KERNEL);
if (!desc) {
IPAERR("failed to allocate memory\n");
- retval = -ENOMEM;
- goto bail_dma;
+ return -ENOMEM;
}
flush.v4_flt = true;
@@ -1941,6 +1952,7 @@ static int ipa3_q6_clean_q6_tables(void)
&reg_write_cmd, false);
if (!cmd_pyld) {
IPAERR("fail construct register_write imm cmd\n");
+ retval = -EFAULT;
goto bail_desc;
}
desc->opcode =
@@ -1957,9 +1969,9 @@ static int ipa3_q6_clean_q6_tables(void)
}
ipahal_destroy_imm_cmd(cmd_pyld);
+
bail_desc:
kfree(desc);
-bail_dma:
IPADBG("Done - retval = %d\n", retval);
return retval;
}
@@ -3702,6 +3714,12 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p,
else
IPADBG(":wdi init ok\n");
+ result = ipa3_ntn_init();
+ if (result)
+ IPAERR(":ntn init failed (%d)\n", -result);
+ else
+ IPADBG(":ntn init ok\n");
+
ipa3_register_panic_hdlr();
ipa3_ctx->q6_proxy_clk_vote_valid = true;
@@ -4813,6 +4831,10 @@ static int ipa_smmu_ap_cb_probe(struct device *dev)
int fast = 1;
int bypass = 1;
u32 iova_ap_mapping[2];
+ u32 add_map_size;
+ const u32 *add_map;
+ void *smem_addr;
+ int i;
IPADBG("AP CB probe: sub pdev=%p\n", dev);
@@ -4902,6 +4924,55 @@ static int ipa_smmu_ap_cb_probe(struct device *dev)
return result;
}
+ add_map = of_get_property(dev->of_node,
+ "qcom,additional-mapping", &add_map_size);
+ if (add_map) {
+ /* mapping size is an array of 3-tuple of u32 */
+ if (add_map_size % (3 * sizeof(u32))) {
+ IPAERR("wrong additional mapping format\n");
+ cb->valid = false;
+ return -EFAULT;
+ }
+
+ /* iterate of each entry of the additional mapping array */
+ for (i = 0; i < add_map_size / sizeof(u32); i += 3) {
+ u32 iova = be32_to_cpu(add_map[i]);
+ u32 pa = be32_to_cpu(add_map[i + 1]);
+ u32 size = be32_to_cpu(add_map[i + 2]);
+ unsigned long iova_p;
+ phys_addr_t pa_p;
+ u32 size_p;
+
+ IPA_SMMU_ROUND_TO_PAGE(iova, pa, size,
+ iova_p, pa_p, size_p);
+ IPADBG("mapping 0x%lx to 0x%pa size %d\n",
+ iova_p, &pa_p, size_p);
+ ipa3_iommu_map(cb->mapping->domain,
+ iova_p, pa_p, size_p,
+ IOMMU_READ | IOMMU_WRITE | IOMMU_DEVICE);
+ }
+ }
+
+ /* map SMEM memory for IPA table accesses */
+ smem_addr = smem_alloc(SMEM_IPA_FILTER_TABLE, IPA_SMEM_SIZE,
+ SMEM_MODEM, 0);
+ if (smem_addr) {
+ phys_addr_t iova = smem_virt_to_phys(smem_addr);
+ phys_addr_t pa = iova;
+ unsigned long iova_p;
+ phys_addr_t pa_p;
+ u32 size_p;
+
+ IPA_SMMU_ROUND_TO_PAGE(iova, pa, IPA_SMEM_SIZE,
+ iova_p, pa_p, size_p);
+ IPADBG("mapping 0x%lx to 0x%pa size %d\n",
+ iova_p, &pa_p, size_p);
+ ipa3_iommu_map(cb->mapping->domain,
+ iova_p, pa_p, size_p,
+ IOMMU_READ | IOMMU_WRITE | IOMMU_DEVICE);
+ }
+
+
smmu_info.present = true;
if (!ipa3_bus_scale_table)
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
index 0319c5c78b0d..c3c5ae38ec14 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
@@ -95,6 +95,7 @@ static struct dentry *dfile_ip6_flt_hw;
static struct dentry *dfile_stats;
static struct dentry *dfile_wstats;
static struct dentry *dfile_wdi_stats;
+static struct dentry *dfile_ntn_stats;
static struct dentry *dfile_dbg_cnt;
static struct dentry *dfile_msg;
static struct dentry *dfile_ip4_nat;
@@ -1184,6 +1185,110 @@ nxt_clnt_cons:
return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
}
+static ssize_t ipa3_read_ntn(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+#define TX_STATS(y) \
+ ipa3_ctx->uc_ntn_ctx.ntn_uc_stats_mmio->tx_ch_stats[0].y
+#define RX_STATS(y) \
+ ipa3_ctx->uc_ntn_ctx.ntn_uc_stats_mmio->rx_ch_stats[0].y
+
+ struct Ipa3HwStatsNTNInfoData_t stats;
+ int nbytes;
+ int cnt = 0;
+
+ if (!ipa3_get_ntn_stats(&stats)) {
+ nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
+ "TX num_pkts_processed=%u\n"
+ "TX tail_ptr_val=%u\n"
+ "TX num_db_fired=%u\n"
+ "TX ringFull=%u\n"
+ "TX ringEmpty=%u\n"
+ "TX ringUsageHigh=%u\n"
+ "TX ringUsageLow=%u\n"
+ "TX RingUtilCount=%u\n"
+ "TX bamFifoFull=%u\n"
+ "TX bamFifoEmpty=%u\n"
+ "TX bamFifoUsageHigh=%u\n"
+ "TX bamFifoUsageLow=%u\n"
+ "TX bamUtilCount=%u\n"
+ "TX num_db=%u\n"
+ "TX num_unexpected_db=%u\n"
+ "TX num_bam_int_handled=%u\n"
+ "TX num_bam_int_in_non_running_state=%u\n"
+ "TX num_qmb_int_handled=%u\n"
+ "TX num_bam_int_handled_while_wait_for_bam=%u\n"
+ "TX num_bam_int_handled_while_not_in_bam=%u\n",
+ TX_STATS(num_pkts_processed),
+ TX_STATS(tail_ptr_val),
+ TX_STATS(num_db_fired),
+ TX_STATS(tx_comp_ring_stats.ringFull),
+ TX_STATS(tx_comp_ring_stats.ringEmpty),
+ TX_STATS(tx_comp_ring_stats.ringUsageHigh),
+ TX_STATS(tx_comp_ring_stats.ringUsageLow),
+ TX_STATS(tx_comp_ring_stats.RingUtilCount),
+ TX_STATS(bam_stats.bamFifoFull),
+ TX_STATS(bam_stats.bamFifoEmpty),
+ TX_STATS(bam_stats.bamFifoUsageHigh),
+ TX_STATS(bam_stats.bamFifoUsageLow),
+ TX_STATS(bam_stats.bamUtilCount),
+ TX_STATS(num_db),
+ TX_STATS(num_unexpected_db),
+ TX_STATS(num_bam_int_handled),
+ TX_STATS(num_bam_int_in_non_running_state),
+ TX_STATS(num_qmb_int_handled),
+ TX_STATS(num_bam_int_handled_while_wait_for_bam),
+ TX_STATS(num_bam_int_handled_while_not_in_bam));
+ cnt += nbytes;
+ nbytes = scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+ "RX max_outstanding_pkts=%u\n"
+ "RX num_pkts_processed=%u\n"
+ "RX rx_ring_rp_value=%u\n"
+ "RX ringFull=%u\n"
+ "RX ringEmpty=%u\n"
+ "RX ringUsageHigh=%u\n"
+ "RX ringUsageLow=%u\n"
+ "RX RingUtilCount=%u\n"
+ "RX bamFifoFull=%u\n"
+ "RX bamFifoEmpty=%u\n"
+ "RX bamFifoUsageHigh=%u\n"
+ "RX bamFifoUsageLow=%u\n"
+ "RX bamUtilCount=%u\n"
+ "RX num_bam_int_handled=%u\n"
+ "RX num_db=%u\n"
+ "RX num_unexpected_db=%u\n"
+ "RX num_pkts_in_dis_uninit_state=%u\n"
+ "num_ic_inj_vdev_change=%u\n"
+ "num_ic_inj_fw_desc_change=%u\n",
+ RX_STATS(max_outstanding_pkts),
+ RX_STATS(num_pkts_processed),
+ RX_STATS(rx_ring_rp_value),
+ RX_STATS(rx_ind_ring_stats.ringFull),
+ RX_STATS(rx_ind_ring_stats.ringEmpty),
+ RX_STATS(rx_ind_ring_stats.ringUsageHigh),
+ RX_STATS(rx_ind_ring_stats.ringUsageLow),
+ RX_STATS(rx_ind_ring_stats.RingUtilCount),
+ RX_STATS(bam_stats.bamFifoFull),
+ RX_STATS(bam_stats.bamFifoEmpty),
+ RX_STATS(bam_stats.bamFifoUsageHigh),
+ RX_STATS(bam_stats.bamFifoUsageLow),
+ RX_STATS(bam_stats.bamUtilCount),
+ RX_STATS(num_bam_int_handled),
+ RX_STATS(num_db),
+ RX_STATS(num_unexpected_db),
+ RX_STATS(num_pkts_in_dis_uninit_state),
+ RX_STATS(num_bam_int_handled_while_not_in_bam),
+ RX_STATS(num_bam_int_handled_while_in_bam_state));
+ cnt += nbytes;
+ } else {
+ nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
+ "Fail to read NTN stats\n");
+ cnt += nbytes;
+ }
+
+ return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
+}
+
static ssize_t ipa3_read_wdi(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
@@ -1747,6 +1852,10 @@ const struct file_operations ipa3_wdi_ops = {
.read = ipa3_read_wdi,
};
+const struct file_operations ipa3_ntn_ops = {
+ .read = ipa3_read_ntn,
+};
+
const struct file_operations ipa3_msg_ops = {
.read = ipa3_read_msg,
};
@@ -1931,6 +2040,13 @@ void ipa3_debugfs_init(void)
goto fail;
}
+ dfile_ntn_stats = debugfs_create_file("ntn", read_only_mode, dent, 0,
+ &ipa3_ntn_ops);
+ if (!dfile_ntn_stats || IS_ERR(dfile_ntn_stats)) {
+ IPAERR("fail to create file for debug_fs ntn stats\n");
+ goto fail;
+ }
+
dfile_dbg_cnt = debugfs_create_file("dbg_cnt", read_write_mode, dent, 0,
&ipa3_dbg_cnt_ops);
if (!dfile_dbg_cnt || IS_ERR(dfile_dbg_cnt)) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
index 029647213531..11da023c9d6a 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
@@ -418,7 +418,8 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx,
return 0;
bad_len:
- hdr_entry->ref_cnt--;
+ if (add_ref_hdr)
+ hdr_entry->ref_cnt--;
entry->cookie = 0;
kmem_cache_free(ipa3_ctx->hdr_proc_ctx_cache, entry);
return -EPERM;
@@ -589,7 +590,7 @@ static int __ipa3_del_hdr_proc_ctx(u32 proc_ctx_hdl, bool release_hdr)
}
if (release_hdr)
- __ipa3_release_hdr(entry->hdr->id);
+ __ipa3_del_hdr(entry->hdr->id);
/* move the offset entry to appropriate free list */
list_move(&entry->offset_entry->link,
@@ -893,12 +894,19 @@ int ipa3_reset_hdr(void)
&ipa3_ctx->hdr_tbl.head_hdr_entry_list, link) {
/* do not remove the default header */
- if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME))
+ if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) {
+ if (entry->is_hdr_proc_ctx) {
+ IPAERR("default header is proc ctx\n");
+ mutex_unlock(&ipa3_ctx->lock);
+ WARN_ON(1);
+ return -EFAULT;
+ }
continue;
+ }
if (ipa3_id_find(entry->id) == NULL) {
- WARN_ON(1);
mutex_unlock(&ipa3_ctx->lock);
+ WARN_ON(1);
return -EFAULT;
}
if (entry->is_hdr_proc_ctx) {
@@ -951,8 +959,8 @@ int ipa3_reset_hdr(void)
link) {
if (ipa3_id_find(ctx_entry->id) == NULL) {
- WARN_ON(1);
mutex_unlock(&ipa3_ctx->lock);
+ WARN_ON(1);
return -EFAULT;
}
list_del(&ctx_entry->link);
@@ -1115,8 +1123,8 @@ int ipa3_put_hdr(u32 hdr_hdl)
goto bail;
}
- if (entry == NULL || entry->cookie != IPA_COOKIE) {
- IPAERR("bad params\n");
+ if (entry->cookie != IPA_COOKIE) {
+ IPAERR("invalid header entry\n");
result = -EINVAL;
goto bail;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index cce05cf31b3c..806510ea8867 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -35,6 +35,7 @@
#include "ipahal/ipahal.h"
#include "ipahal/ipahal_fltrt.h"
#include "../ipa_common_i.h"
+#include "ipa_uc_offload_i.h"
#define DRV_NAME "ipa"
#define NAT_DEV_NAME "ipaNatTable"
@@ -271,7 +272,7 @@ struct ipa3_rt_tbl {
* @is_partial: flag indicating if header table entry is partial
* @is_hdr_proc_ctx: false - hdr entry resides in hdr table,
* true - hdr entry resides in DDR and pointed to by proc ctx
- * @phys_base: physical address of entry in SRAM when is_hdr_proc_ctx is true,
+ * @phys_base: physical address of entry in DDR when is_hdr_proc_ctx is true,
* else 0
* @proc_ctx: processing context header
* @offset_entry: entry's offset
@@ -546,7 +547,7 @@ struct ipa3_ep_context {
bool skip_ep_cfg;
bool keep_ipa_awake;
struct ipa3_wlan_stats wstats;
- u32 wdi_state;
+ u32 uc_offload_state;
bool disconnect_in_progress;
u32 qmi_request_sent;
bool napi_enabled;
@@ -869,200 +870,6 @@ struct ipa3_tag_completion {
struct ipa3_controller;
/**
- * @brief Enum value determined based on the feature it
- * corresponds to
- * +----------------+----------------+
- * | 3 bits | 5 bits |
- * +----------------+----------------+
- * | HW_FEATURE | OPCODE |
- * +----------------+----------------+
- *
- */
-#define FEATURE_ENUM_VAL(feature, opcode) ((feature << 5) | opcode)
-#define EXTRACT_UC_FEATURE(value) (value >> 5)
-
-#define IPA_HW_NUM_FEATURES 0x8
-
-/**
- * enum ipa3_hw_features - Values that represent the features supported in IPA HW
- * @IPA_HW_FEATURE_COMMON : Feature related to common operation of IPA HW
- * @IPA_HW_FEATURE_MHI : Feature related to MHI operation in IPA HW
- * @IPA_HW_FEATURE_POWER_COLLAPSE: Feature related to IPA Power collapse
- * @IPA_HW_FEATURE_WDI : Feature related to WDI operation in IPA HW
- * @IPA_HW_FEATURE_ZIP: Feature related to CMP/DCMP operation in IPA HW
-*/
-enum ipa3_hw_features {
- IPA_HW_FEATURE_COMMON = 0x0,
- IPA_HW_FEATURE_MHI = 0x1,
- IPA_HW_FEATURE_POWER_COLLAPSE = 0x2,
- IPA_HW_FEATURE_WDI = 0x3,
- IPA_HW_FEATURE_ZIP = 0x4,
- IPA_HW_FEATURE_MAX = IPA_HW_NUM_FEATURES
-};
-
-/**
- * enum ipa3_hw_2_cpu_events - Values that represent HW event to be sent to CPU.
- * @IPA_HW_2_CPU_EVENT_NO_OP : No event present
- * @IPA_HW_2_CPU_EVENT_ERROR : Event specify a system error is detected by the
- * device
- * @IPA_HW_2_CPU_EVENT_LOG_INFO : Event providing logging specific information
- */
-enum ipa3_hw_2_cpu_events {
- IPA_HW_2_CPU_EVENT_NO_OP =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 0),
- IPA_HW_2_CPU_EVENT_ERROR =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 1),
- IPA_HW_2_CPU_EVENT_LOG_INFO =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 2),
-};
-
-/**
- * enum ipa3_hw_errors - Common error types.
- * @IPA_HW_ERROR_NONE : No error persists
- * @IPA_HW_INVALID_DOORBELL_ERROR : Invalid data read from doorbell
- * @IPA_HW_DMA_ERROR : Unexpected DMA error
- * @IPA_HW_FATAL_SYSTEM_ERROR : HW has crashed and requires reset.
- * @IPA_HW_INVALID_OPCODE : Invalid opcode sent
- * @IPA_HW_INVALID_PARAMS : Invalid params for the requested command
- * @IPA_HW_GSI_CH_NOT_EMPTY_FAILURE : GSI channel emptiness validation failed
- */
-enum ipa3_hw_errors {
- IPA_HW_ERROR_NONE =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 0),
- IPA_HW_INVALID_DOORBELL_ERROR =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 1),
- IPA_HW_DMA_ERROR =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 2),
- IPA_HW_FATAL_SYSTEM_ERROR =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 3),
- IPA_HW_INVALID_OPCODE =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 4),
- IPA_HW_INVALID_PARAMS =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 5),
- IPA_HW_CONS_DISABLE_CMD_GSI_STOP_FAILURE =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 6),
- IPA_HW_PROD_DISABLE_CMD_GSI_STOP_FAILURE =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 7),
- IPA_HW_GSI_CH_NOT_EMPTY_FAILURE =
- FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 8)
-};
-
-/**
- * struct IpaHwSharedMemCommonMapping_t - Structure referring to the common
- * section in 128B shared memory located in offset zero of SW Partition in IPA
- * SRAM.
- * @cmdOp : CPU->HW command opcode. See IPA_CPU_2_HW_COMMANDS
- * @cmdParams : CPU->HW command parameter lower 32bit.
- * @cmdParams_hi : CPU->HW command parameter higher 32bit.
- * of parameters (immediate parameters) and point on structure in system memory
- * (in such case the address must be accessible for HW)
- * @responseOp : HW->CPU response opcode. See IPA_HW_2_CPU_RESPONSES
- * @responseParams : HW->CPU response parameter. The parameter filed can hold 32
- * bits of parameters (immediate parameters) and point on structure in system
- * memory
- * @eventOp : HW->CPU event opcode. See IPA_HW_2_CPU_EVENTS
- * @eventParams : HW->CPU event parameter. The parameter filed can hold 32 bits of
- * parameters (immediate parameters) and point on structure in system memory
- * @firstErrorAddress : Contains the address of first error-source on SNOC
- * @hwState : State of HW. The state carries information regarding the error type.
- * @warningCounter : The warnings counter. The counter carries information regarding
- * non fatal errors in HW
- * @interfaceVersionCommon : The Common interface version as reported by HW
- *
- * The shared memory is used for communication between IPA HW and CPU.
- */
-struct IpaHwSharedMemCommonMapping_t {
- u8 cmdOp;
- u8 reserved_01;
- u16 reserved_03_02;
- u32 cmdParams;
- u32 cmdParams_hi;
- u8 responseOp;
- u8 reserved_0D;
- u16 reserved_0F_0E;
- u32 responseParams;
- u8 eventOp;
- u8 reserved_15;
- u16 reserved_17_16;
- u32 eventParams;
- u32 firstErrorAddress;
- u8 hwState;
- u8 warningCounter;
- u16 reserved_23_22;
- u16 interfaceVersionCommon;
- u16 reserved_27_26;
-} __packed;
-
-/**
- * union IpaHwFeatureInfoData_t - parameters for stats/config blob
- *
- * @offset : Location of a feature within the EventInfoData
- * @size : Size of the feature
- */
-union IpaHwFeatureInfoData_t {
- struct IpaHwFeatureInfoParams_t {
- u32 offset:16;
- u32 size:16;
- } __packed params;
- u32 raw32b;
-} __packed;
-
-/**
- * union IpaHwErrorEventData_t - HW->CPU Common Events
- * @errorType : Entered when a system error is detected by the HW. Type of
- * error is specified by IPA_HW_ERRORS
- * @reserved : Reserved
- */
-union IpaHwErrorEventData_t {
- struct IpaHwErrorEventParams_t {
- u32 errorType:8;
- u32 reserved:24;
- } __packed params;
- u32 raw32b;
-} __packed;
-
-/**
- * struct IpaHwEventInfoData_t - Structure holding the parameters for
- * statistics and config info
- *
- * @baseAddrOffset : Base Address Offset of the statistics or config
- * structure from IPA_WRAPPER_BASE
- * @IpaHwFeatureInfoData_t : Location and size of each feature within
- * the statistics or config structure
- *
- * @note Information about each feature in the featureInfo[]
- * array is populated at predefined indices per the IPA_HW_FEATURES
- * enum definition
- */
-struct IpaHwEventInfoData_t {
- u32 baseAddrOffset;
- union IpaHwFeatureInfoData_t featureInfo[IPA_HW_NUM_FEATURES];
-} __packed;
-
-/**
- * struct IpaHwEventLogInfoData_t - Structure holding the parameters for
- * IPA_HW_2_CPU_EVENT_LOG_INFO Event
- *
- * @featureMask : Mask indicating the features enabled in HW.
- * Refer IPA_HW_FEATURE_MASK
- * @circBuffBaseAddrOffset : Base Address Offset of the Circular Event
- * Log Buffer structure
- * @statsInfo : Statistics related information
- * @configInfo : Configuration related information
- *
- * @note The offset location of this structure from IPA_WRAPPER_BASE
- * will be provided as Event Params for the IPA_HW_2_CPU_EVENT_LOG_INFO
- * Event
- */
-struct IpaHwEventLogInfoData_t {
- u32 featureMask;
- u32 circBuffBaseAddrOffset;
- struct IpaHwEventInfoData_t statsInfo;
- struct IpaHwEventInfoData_t configInfo;
-
-} __packed;
-
-/**
* struct ipa3_uc_hdlrs - IPA uC callback functions
* @ipa_uc_loaded_hdlr: Function handler when uC is loaded
* @ipa_uc_event_hdlr: Event handler function
@@ -1393,6 +1200,7 @@ struct ipa3_context {
struct ipa3_uc_ctx uc_ctx;
struct ipa3_uc_wdi_ctx uc_wdi_ctx;
+ struct ipa3_uc_ntn_ctx uc_ntn_ctx;
u32 wan_rx_ring_size;
bool skip_uc_pipe_reset;
enum ipa_transport_type transport_prototype;
@@ -1854,6 +1662,11 @@ int ipa3_resume_wdi_pipe(u32 clnt_hdl);
int ipa3_suspend_wdi_pipe(u32 clnt_hdl);
int ipa3_get_wdi_stats(struct IpaHwStatsWDIInfoData_t *stats);
u16 ipa3_get_smem_restr_bytes(void);
+int ipa3_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
+ ipa_notify_cb notify, void *priv, u8 hdr_len,
+ struct ipa_ntn_conn_out_params *outp);
+int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl);
+
/*
* To retrieve doorbell physical address of
* wlan pipes
@@ -2197,4 +2010,6 @@ void ipa3_recycle_wan_skb(struct sk_buff *skb);
int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map);
int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr,
u32 size, bool map);
+int ipa3_ntn_init(void);
+int ipa3_get_ntn_stats(struct Ipa3HwStatsNTNInfoData_t *stats);
#endif /* _IPA3_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
index 14e2f1f4c510..e83c249ad425 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
@@ -549,11 +549,6 @@ int ipa3_mhi_resume_channels_internal(enum ipa_client_type client,
return res;
}
}
- if (res) {
- IPA_MHI_ERR("failed to resume channel error %d\n",
- res);
- return res;
- }
res = gsi_start_channel(ep->gsi_chan_hdl);
if (res) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
index 138db3dbde84..b06e33a8258a 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
@@ -957,6 +957,10 @@ static int __ipa_finish_rt_rule_add(struct ipa3_rt_entry *entry, u32 *rule_hdl,
return 0;
ipa_insert_failed:
+ if (entry->hdr)
+ entry->hdr->ref_cnt--;
+ else if (entry->proc_ctx)
+ entry->proc_ctx->ref_cnt--;
idr_remove(&tbl->rule_ids, entry->rule_id);
list_del(&entry->link);
kmem_cache_free(ipa3_ctx->rt_rule_cache, entry);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c
new file mode 100644
index 000000000000..7b891843028d
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c
@@ -0,0 +1,410 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "ipa_i.h"
+
+#define IPA_UC_NTN_DB_PA_TX 0x79620DC
+#define IPA_UC_NTN_DB_PA_RX 0x79620D8
+
+static void ipa3_uc_ntn_event_handler(struct IpaHwSharedMemCommonMapping_t
+ *uc_sram_mmio)
+
+{
+ union Ipa3HwNTNErrorEventData_t ntn_evt;
+
+ if (uc_sram_mmio->eventOp ==
+ IPA_HW_2_CPU_EVENT_NTN_ERROR) {
+ ntn_evt.raw32b = uc_sram_mmio->eventParams;
+ IPADBG("uC NTN evt errType=%u pipe=%d cherrType=%u\n",
+ ntn_evt.params.ntn_error_type,
+ ntn_evt.params.ipa_pipe_number,
+ ntn_evt.params.ntn_ch_err_type);
+ }
+}
+
+static void ipa3_uc_ntn_event_log_info_handler(
+struct IpaHwEventLogInfoData_t *uc_event_top_mmio)
+
+{
+ if ((uc_event_top_mmio->featureMask & (1 << IPA_HW_FEATURE_NTN)) == 0) {
+ IPAERR("NTN feature missing 0x%x\n",
+ uc_event_top_mmio->featureMask);
+ return;
+ }
+
+ if (uc_event_top_mmio->statsInfo.featureInfo[IPA_HW_FEATURE_NTN].
+ params.size != sizeof(struct Ipa3HwStatsNTNInfoData_t)) {
+ IPAERR("NTN stats sz invalid exp=%zu is=%u\n",
+ sizeof(struct Ipa3HwStatsNTNInfoData_t),
+ uc_event_top_mmio->statsInfo.
+ featureInfo[IPA_HW_FEATURE_NTN].params.size);
+ return;
+ }
+
+ ipa3_ctx->uc_ntn_ctx.ntn_uc_stats_ofst = uc_event_top_mmio->
+ statsInfo.baseAddrOffset + uc_event_top_mmio->statsInfo.
+ featureInfo[IPA_HW_FEATURE_NTN].params.offset;
+ IPAERR("NTN stats ofst=0x%x\n", ipa3_ctx->uc_ntn_ctx.ntn_uc_stats_ofst);
+ if (ipa3_ctx->uc_ntn_ctx.ntn_uc_stats_ofst +
+ sizeof(struct Ipa3HwStatsNTNInfoData_t) >=
+ ipa3_ctx->ctrl->ipa_reg_base_ofst +
+ ipahal_get_reg_n_ofst(IPA_SRAM_DIRECT_ACCESS_n, 0) +
+ ipa3_ctx->smem_sz) {
+ IPAERR("uc_ntn_stats 0x%x outside SRAM\n",
+ ipa3_ctx->uc_ntn_ctx.ntn_uc_stats_ofst);
+ return;
+ }
+
+ ipa3_ctx->uc_ntn_ctx.ntn_uc_stats_mmio =
+ ioremap(ipa3_ctx->ipa_wrapper_base +
+ ipa3_ctx->uc_ntn_ctx.ntn_uc_stats_ofst,
+ sizeof(struct Ipa3HwStatsNTNInfoData_t));
+ if (!ipa3_ctx->uc_ntn_ctx.ntn_uc_stats_mmio) {
+ IPAERR("fail to ioremap uc ntn stats\n");
+ return;
+ }
+}
+
+/**
+ * ipa2_get_wdi_stats() - Query WDI statistics from uc
+ * @stats: [inout] stats blob from client populated by driver
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * @note Cannot be called from atomic context
+ *
+ */
+int ipa3_get_ntn_stats(struct Ipa3HwStatsNTNInfoData_t *stats)
+{
+#define TX_STATS(y) stats->tx_ch_stats[0].y = \
+ ipa3_ctx->uc_ntn_ctx.ntn_uc_stats_mmio->tx_ch_stats[0].y
+#define RX_STATS(y) stats->rx_ch_stats[0].y = \
+ ipa3_ctx->uc_ntn_ctx.ntn_uc_stats_mmio->rx_ch_stats[0].y
+
+ if (unlikely(!ipa3_ctx)) {
+ IPAERR("IPA driver was not initialized\n");
+ return -EINVAL;
+ }
+
+ if (!stats || !ipa3_ctx->uc_ntn_ctx.ntn_uc_stats_mmio) {
+ IPAERR("bad parms stats=%p ntn_stats=%p\n",
+ stats,
+ ipa3_ctx->uc_ntn_ctx.ntn_uc_stats_mmio);
+ return -EINVAL;
+ }
+
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+ TX_STATS(num_pkts_processed);
+ TX_STATS(tail_ptr_val);
+ TX_STATS(num_db_fired);
+ TX_STATS(tx_comp_ring_stats.ringFull);
+ TX_STATS(tx_comp_ring_stats.ringEmpty);
+ TX_STATS(tx_comp_ring_stats.ringUsageHigh);
+ TX_STATS(tx_comp_ring_stats.ringUsageLow);
+ TX_STATS(tx_comp_ring_stats.RingUtilCount);
+ TX_STATS(bam_stats.bamFifoFull);
+ TX_STATS(bam_stats.bamFifoEmpty);
+ TX_STATS(bam_stats.bamFifoUsageHigh);
+ TX_STATS(bam_stats.bamFifoUsageLow);
+ TX_STATS(bam_stats.bamUtilCount);
+ TX_STATS(num_db);
+ TX_STATS(num_unexpected_db);
+ TX_STATS(num_bam_int_handled);
+ TX_STATS(num_bam_int_in_non_running_state);
+ TX_STATS(num_qmb_int_handled);
+ TX_STATS(num_bam_int_handled_while_wait_for_bam);
+ TX_STATS(num_bam_int_handled_while_not_in_bam);
+
+ RX_STATS(max_outstanding_pkts);
+ RX_STATS(num_pkts_processed);
+ RX_STATS(rx_ring_rp_value);
+ RX_STATS(rx_ind_ring_stats.ringFull);
+ RX_STATS(rx_ind_ring_stats.ringEmpty);
+ RX_STATS(rx_ind_ring_stats.ringUsageHigh);
+ RX_STATS(rx_ind_ring_stats.ringUsageLow);
+ RX_STATS(rx_ind_ring_stats.RingUtilCount);
+ RX_STATS(bam_stats.bamFifoFull);
+ RX_STATS(bam_stats.bamFifoEmpty);
+ RX_STATS(bam_stats.bamFifoUsageHigh);
+ RX_STATS(bam_stats.bamFifoUsageLow);
+ RX_STATS(bam_stats.bamUtilCount);
+ RX_STATS(num_bam_int_handled);
+ RX_STATS(num_db);
+ RX_STATS(num_unexpected_db);
+ RX_STATS(num_pkts_in_dis_uninit_state);
+ RX_STATS(num_bam_int_handled_while_not_in_bam);
+ RX_STATS(num_bam_int_handled_while_in_bam_state);
+
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+
+ return 0;
+}
+
+int ipa3_ntn_init(void)
+{
+ struct ipa3_uc_hdlrs uc_ntn_cbs = { 0 };
+
+ uc_ntn_cbs.ipa_uc_event_hdlr = ipa3_uc_ntn_event_handler;
+ uc_ntn_cbs.ipa_uc_event_log_info_hdlr =
+ ipa3_uc_ntn_event_log_info_handler;
+
+ ipa3_uc_register_handlers(IPA_HW_FEATURE_NTN, &uc_ntn_cbs);
+
+ return 0;
+}
+
+static int ipa3_uc_send_ntn_setup_pipe_cmd(
+ struct ipa_ntn_setup_info *ntn_info, u8 dir)
+{
+ int ipa_ep_idx;
+ int result = 0;
+ struct ipa_mem_buffer cmd;
+ struct Ipa3HwNtnSetUpCmdData_t *Ntn_params;
+ struct IpaHwOffloadSetUpCmdData_t *cmd_data;
+
+ if (ntn_info == NULL) {
+ IPAERR("invalid input\n");
+ return -EINVAL;
+ }
+
+ ipa_ep_idx = ipa_get_ep_mapping(ntn_info->client);
+ if (ipa_ep_idx == -1) {
+ IPAERR("fail to get ep idx.\n");
+ return -EFAULT;
+ }
+
+ IPADBG("client=%d ep=%d\n", ntn_info->client, ipa_ep_idx);
+
+ IPADBG("ring_base_pa = 0x%pa\n",
+ &ntn_info->ring_base_pa);
+ IPADBG("ntn_ring_size = %d\n", ntn_info->ntn_ring_size);
+ IPADBG("buff_pool_base_pa = 0x%pa\n", &ntn_info->buff_pool_base_pa);
+ IPADBG("num_buffers = %d\n", ntn_info->num_buffers);
+ IPADBG("data_buff_size = %d\n", ntn_info->data_buff_size);
+ IPADBG("tail_ptr_base_pa = 0x%pa\n", &ntn_info->ntn_reg_base_ptr_pa);
+
+ cmd.size = sizeof(*cmd_data);
+ cmd.base = dma_alloc_coherent(ipa3_ctx->uc_pdev, cmd.size,
+ &cmd.phys_base, GFP_KERNEL);
+ if (cmd.base == NULL) {
+ IPAERR("fail to get DMA memory.\n");
+ return -ENOMEM;
+ }
+
+ cmd_data = (struct IpaHwOffloadSetUpCmdData_t *)cmd.base;
+ cmd_data->protocol = IPA_HW_FEATURE_NTN;
+
+ Ntn_params = &cmd_data->SetupCh_params.NtnSetupCh_params;
+ Ntn_params->ring_base_pa = ntn_info->ring_base_pa;
+ Ntn_params->buff_pool_base_pa = ntn_info->buff_pool_base_pa;
+ Ntn_params->ntn_ring_size = ntn_info->ntn_ring_size;
+ Ntn_params->num_buffers = ntn_info->num_buffers;
+ Ntn_params->ntn_reg_base_ptr_pa = ntn_info->ntn_reg_base_ptr_pa;
+ Ntn_params->data_buff_size = ntn_info->data_buff_size;
+ Ntn_params->ipa_pipe_number = ipa_ep_idx;
+ Ntn_params->dir = dir;
+
+ result = ipa3_uc_send_cmd((u32)(cmd.phys_base),
+ IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP,
+ IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
+ false, 10*HZ);
+ if (result)
+ result = -EFAULT;
+
+ dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
+ return result;
+}
+
+/**
+ * ipa3_setup_uc_ntn_pipes() - setup uc offload pipes
+ */
+int ipa3_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
+ ipa_notify_cb notify, void *priv, u8 hdr_len,
+ struct ipa_ntn_conn_out_params *outp)
+{
+ struct ipa3_ep_context *ep_ul;
+ struct ipa3_ep_context *ep_dl;
+ int ipa_ep_idx_ul;
+ int ipa_ep_idx_dl;
+ int result = 0;
+
+ if (in == NULL) {
+ IPAERR("invalid input\n");
+ return -EINVAL;
+ }
+
+ ipa_ep_idx_ul = ipa_get_ep_mapping(in->ul.client);
+ ipa_ep_idx_dl = ipa_get_ep_mapping(in->dl.client);
+ if (ipa_ep_idx_ul == -1 || ipa_ep_idx_dl == -1) {
+ IPAERR("fail to alloc EP.\n");
+ return -EFAULT;
+ }
+
+ ep_ul = &ipa3_ctx->ep[ipa_ep_idx_ul];
+ ep_dl = &ipa3_ctx->ep[ipa_ep_idx_dl];
+
+ if (ep_ul->valid || ep_dl->valid) {
+ IPAERR("EP already allocated.\n");
+ return -EFAULT;
+ }
+
+ memset(ep_ul, 0, offsetof(struct ipa3_ep_context, sys));
+ memset(ep_dl, 0, offsetof(struct ipa3_ep_context, sys));
+
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+ /* setup ul ep cfg */
+ ep_ul->valid = 1;
+ ep_ul->client = in->ul.client;
+ result = ipa3_enable_data_path(ipa_ep_idx_ul);
+ if (result) {
+ IPAERR("disable data path failed res=%d clnt=%d.\n", result,
+ ipa_ep_idx_ul);
+ return -EFAULT;
+ }
+ ep_ul->client_notify = notify;
+ ep_ul->priv = priv;
+
+ memset(&ep_ul->cfg, 0, sizeof(ep_ul->cfg));
+ ep_ul->cfg.nat.nat_en = IPA_SRC_NAT;
+ ep_ul->cfg.hdr.hdr_len = hdr_len;
+ ep_ul->cfg.mode.mode = IPA_BASIC;
+
+ if (ipa3_cfg_ep(ipa_ep_idx_ul, &ep_ul->cfg)) {
+ IPAERR("fail to setup ul pipe cfg\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ if (ipa3_uc_send_ntn_setup_pipe_cmd(&in->ul, IPA_NTN_RX_DIR)) {
+ IPAERR("fail to send cmd to uc for ul pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ ipa3_install_dflt_flt_rules(ipa_ep_idx_ul);
+ outp->ul_uc_db_pa = IPA_UC_NTN_DB_PA_RX;
+ ep_ul->uc_offload_state |= IPA_UC_OFFLOAD_CONNECTED;
+ IPADBG("client %d (ep: %d) connected\n", in->ul.client,
+ ipa_ep_idx_ul);
+
+ /* setup dl ep cfg */
+ ep_dl->valid = 1;
+ ep_dl->client = in->dl.client;
+ result = ipa3_enable_data_path(ipa_ep_idx_dl);
+ if (result) {
+ IPAERR("disable data path failed res=%d clnt=%d.\n", result,
+ ipa_ep_idx_dl);
+ result = -EFAULT;
+ goto fail;
+ }
+
+ memset(&ep_dl->cfg, 0, sizeof(ep_ul->cfg));
+ ep_dl->cfg.nat.nat_en = IPA_BYPASS_NAT;
+ ep_dl->cfg.hdr.hdr_len = hdr_len;
+ ep_dl->cfg.mode.mode = IPA_BASIC;
+
+ if (ipa3_cfg_ep(ipa_ep_idx_dl, &ep_dl->cfg)) {
+ IPAERR("fail to setup dl pipe cfg\n");
+ result = -EFAULT;
+ goto fail;
+ }
+
+ if (ipa3_uc_send_ntn_setup_pipe_cmd(&in->dl, IPA_NTN_TX_DIR)) {
+ IPAERR("fail to send cmd to uc for dl pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ outp->dl_uc_db_pa = IPA_UC_NTN_DB_PA_TX;
+ ep_dl->uc_offload_state |= IPA_UC_OFFLOAD_CONNECTED;
+ IPADBG("client %d (ep: %d) connected\n", in->dl.client,
+ ipa_ep_idx_dl);
+
+fail:
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ return result;
+}
+
+/**
+ * ipa3_tear_down_uc_offload_pipes() - tear down uc offload pipes
+ */
+
+int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul,
+ int ipa_ep_idx_dl)
+{
+ struct ipa_mem_buffer cmd;
+ struct ipa3_ep_context *ep_ul, *ep_dl;
+ struct IpaHwOffloadCommonChCmdData_t *cmd_data;
+ union Ipa3HwNtnCommonChCmdData_t *tear;
+ int result = 0;
+
+ IPADBG("ep_ul = %d\n", ipa_ep_idx_ul);
+ IPADBG("ep_dl = %d\n", ipa_ep_idx_dl);
+
+ ep_ul = &ipa3_ctx->ep[ipa_ep_idx_ul];
+ ep_dl = &ipa3_ctx->ep[ipa_ep_idx_dl];
+
+ if (ep_ul->uc_offload_state != IPA_UC_OFFLOAD_CONNECTED ||
+ ep_dl->uc_offload_state != IPA_UC_OFFLOAD_CONNECTED) {
+ IPAERR("channel bad state: ul %d dl %d\n",
+ ep_ul->uc_offload_state, ep_dl->uc_offload_state);
+ return -EFAULT;
+ }
+
+ cmd.size = sizeof(*cmd_data);
+ cmd.base = dma_alloc_coherent(ipa3_ctx->uc_pdev, cmd.size,
+ &cmd.phys_base, GFP_KERNEL);
+ if (cmd.base == NULL) {
+ IPAERR("fail to get DMA memory.\n");
+ return -ENOMEM;
+ }
+
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+ /* teardown the UL pipe */
+ cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base;
+ cmd_data->protocol = IPA_HW_FEATURE_NTN;
+
+ tear = &cmd_data->CommonCh_params.NtnCommonCh_params;
+ tear->params.ipa_pipe_number = ipa_ep_idx_ul;
+ result = ipa3_uc_send_cmd((u32)(cmd.phys_base),
+ IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
+ IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
+ false, 10*HZ);
+ if (result) {
+ IPAERR("fail to tear down ul pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ ipa3_disable_data_path(ipa_ep_idx_ul);
+ ipa3_delete_dflt_flt_rules(ipa_ep_idx_ul);
+ memset(&ipa3_ctx->ep[ipa_ep_idx_ul], 0, sizeof(struct ipa3_ep_context));
+ IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul);
+
+ /* teardown the DL pipe */
+ tear->params.ipa_pipe_number = ipa_ep_idx_dl;
+ result = ipa3_uc_send_cmd((u32)(cmd.phys_base),
+ IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
+ IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
+ false, 10*HZ);
+ if (result) {
+ IPAERR("fail to tear down ul pipe\n");
+ result = -EFAULT;
+ goto fail;
+ }
+ ipa3_disable_data_path(ipa_ep_idx_dl);
+ memset(&ipa3_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa3_ep_context));
+ IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl);
+
+fail:
+ dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ return result;
+}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_offload_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_offload_i.h
new file mode 100644
index 000000000000..946fc7e31fb9
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_offload_i.h
@@ -0,0 +1,580 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _IPA_UC_OFFLOAD_I_H_
+#define _IPA_UC_OFFLOAD_I_H_
+
+#include <linux/ipa.h>
+#include "ipa_i.h"
+
+/*
+ * Neutrino protocol related data structures
+ */
+
+#define IPA_UC_MAX_NTN_TX_CHANNELS 1
+#define IPA_UC_MAX_NTN_RX_CHANNELS 1
+
+#define IPA_NTN_TX_DIR 1
+#define IPA_NTN_RX_DIR 2
+
+/**
+ * @brief Enum value determined based on the feature it
+ * corresponds to
+ * +----------------+----------------+
+ * | 3 bits | 5 bits |
+ * +----------------+----------------+
+ * | HW_FEATURE | OPCODE |
+ * +----------------+----------------+
+ *
+ */
+#define FEATURE_ENUM_VAL(feature, opcode) ((feature << 5) | opcode)
+#define EXTRACT_UC_FEATURE(value) (value >> 5)
+
+#define IPA_HW_NUM_FEATURES 0x8
+
+/**
+ * enum ipa3_hw_features - Values that represent the features supported
+ * in IPA HW
+ * @IPA_HW_FEATURE_COMMON : Feature related to common operation of IPA HW
+ * @IPA_HW_FEATURE_MHI : Feature related to MHI operation in IPA HW
+ * @IPA_HW_FEATURE_POWER_COLLAPSE: Feature related to IPA Power collapse
+ * @IPA_HW_FEATURE_WDI : Feature related to WDI operation in IPA HW
+ * @IPA_HW_FEATURE_ZIP: Feature related to CMP/DCMP operation in IPA HW
+ * @IPA_HW_FEATURE_NTN : Feature related to NTN operation in IPA HW
+ * @IPA_HW_FEATURE_OFFLOAD : Feature related to NTN operation in IPA HW
+*/
+enum ipa3_hw_features {
+ IPA_HW_FEATURE_COMMON = 0x0,
+ IPA_HW_FEATURE_MHI = 0x1,
+ IPA_HW_FEATURE_POWER_COLLAPSE = 0x2,
+ IPA_HW_FEATURE_WDI = 0x3,
+ IPA_HW_FEATURE_ZIP = 0x4,
+ IPA_HW_FEATURE_NTN = 0x5,
+ IPA_HW_FEATURE_OFFLOAD = 0x6,
+ IPA_HW_FEATURE_MAX = IPA_HW_NUM_FEATURES
+};
+
+/**
+ * enum ipa3_hw_2_cpu_events - Values that represent HW event to be sent to CPU.
+ * @IPA_HW_2_CPU_EVENT_NO_OP : No event present
+ * @IPA_HW_2_CPU_EVENT_ERROR : Event specify a system error is detected by the
+ * device
+ * @IPA_HW_2_CPU_EVENT_LOG_INFO : Event providing logging specific information
+ */
+enum ipa3_hw_2_cpu_events {
+ IPA_HW_2_CPU_EVENT_NO_OP =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 0),
+ IPA_HW_2_CPU_EVENT_ERROR =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 1),
+ IPA_HW_2_CPU_EVENT_LOG_INFO =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 2),
+};
+
+/**
+ * enum ipa3_hw_errors - Common error types.
+ * @IPA_HW_ERROR_NONE : No error persists
+ * @IPA_HW_INVALID_DOORBELL_ERROR : Invalid data read from doorbell
+ * @IPA_HW_DMA_ERROR : Unexpected DMA error
+ * @IPA_HW_FATAL_SYSTEM_ERROR : HW has crashed and requires reset.
+ * @IPA_HW_INVALID_OPCODE : Invalid opcode sent
+ * @IPA_HW_INVALID_PARAMS : Invalid params for the requested command
+ * @IPA_HW_GSI_CH_NOT_EMPTY_FAILURE : GSI channel emptiness validation failed
+ */
+enum ipa3_hw_errors {
+ IPA_HW_ERROR_NONE =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 0),
+ IPA_HW_INVALID_DOORBELL_ERROR =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 1),
+ IPA_HW_DMA_ERROR =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 2),
+ IPA_HW_FATAL_SYSTEM_ERROR =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 3),
+ IPA_HW_INVALID_OPCODE =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 4),
+ IPA_HW_INVALID_PARAMS =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 5),
+ IPA_HW_CONS_DISABLE_CMD_GSI_STOP_FAILURE =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 6),
+ IPA_HW_PROD_DISABLE_CMD_GSI_STOP_FAILURE =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 7),
+ IPA_HW_GSI_CH_NOT_EMPTY_FAILURE =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 8)
+};
+
+/**
+ * struct IpaHwSharedMemCommonMapping_t - Structure referring to the common
+ * section in 128B shared memory located in offset zero of SW Partition in IPA
+ * SRAM.
+ * @cmdOp : CPU->HW command opcode. See IPA_CPU_2_HW_COMMANDS
+ * @cmdParams : CPU->HW command parameter lower 32bit.
+ * @cmdParams_hi : CPU->HW command parameter higher 32bit.
+ * of parameters (immediate parameters) and point on structure in system memory
+ * (in such case the address must be accessible for HW)
+ * @responseOp : HW->CPU response opcode. See IPA_HW_2_CPU_RESPONSES
+ * @responseParams : HW->CPU response parameter. The parameter filed can hold 32
+ * bits of parameters (immediate parameters) and point on structure in system
+ * memory
+ * @eventOp : HW->CPU event opcode. See IPA_HW_2_CPU_EVENTS
+ * @eventParams : HW->CPU event parameter. The parameter filed can hold 32
+ * bits of parameters (immediate parameters) and point on
+ * structure in system memory
+ * @firstErrorAddress : Contains the address of first error-source on SNOC
+ * @hwState : State of HW. The state carries information regarding the
+ * error type.
+ * @warningCounter : The warnings counter. The counter carries information
+ * regarding non fatal errors in HW
+ * @interfaceVersionCommon : The Common interface version as reported by HW
+ *
+ * The shared memory is used for communication between IPA HW and CPU.
+ */
+struct IpaHwSharedMemCommonMapping_t {
+ u8 cmdOp;
+ u8 reserved_01;
+ u16 reserved_03_02;
+ u32 cmdParams;
+ u32 cmdParams_hi;
+ u8 responseOp;
+ u8 reserved_0D;
+ u16 reserved_0F_0E;
+ u32 responseParams;
+ u8 eventOp;
+ u8 reserved_15;
+ u16 reserved_17_16;
+ u32 eventParams;
+ u32 firstErrorAddress;
+ u8 hwState;
+ u8 warningCounter;
+ u16 reserved_23_22;
+ u16 interfaceVersionCommon;
+ u16 reserved_27_26;
+} __packed;
+
+/**
+ * union Ipa3HwFeatureInfoData_t - parameters for stats/config blob
+ *
+ * @offset : Location of a feature within the EventInfoData
+ * @size : Size of the feature
+ */
+union Ipa3HwFeatureInfoData_t {
+ struct IpaHwFeatureInfoParams_t {
+ u32 offset:16;
+ u32 size:16;
+ } __packed params;
+ u32 raw32b;
+} __packed;
+
+/**
+ * union IpaHwErrorEventData_t - HW->CPU Common Events
+ * @errorType : Entered when a system error is detected by the HW. Type of
+ * error is specified by IPA_HW_ERRORS
+ * @reserved : Reserved
+ */
+union IpaHwErrorEventData_t {
+ struct IpaHwErrorEventParams_t {
+ u32 errorType:8;
+ u32 reserved:24;
+ } __packed params;
+ u32 raw32b;
+} __packed;
+
+/**
+ * struct Ipa3HwEventInfoData_t - Structure holding the parameters for
+ * statistics and config info
+ *
+ * @baseAddrOffset : Base Address Offset of the statistics or config
+ * structure from IPA_WRAPPER_BASE
+ * @Ipa3HwFeatureInfoData_t : Location and size of each feature within
+ * the statistics or config structure
+ *
+ * @note Information about each feature in the featureInfo[]
+ * array is populated at predefined indices per the IPA_HW_FEATURES
+ * enum definition
+ */
+struct Ipa3HwEventInfoData_t {
+ u32 baseAddrOffset;
+ union Ipa3HwFeatureInfoData_t featureInfo[IPA_HW_NUM_FEATURES];
+} __packed;
+
+/**
+ * struct IpaHwEventLogInfoData_t - Structure holding the parameters for
+ * IPA_HW_2_CPU_EVENT_LOG_INFO Event
+ *
+ * @featureMask : Mask indicating the features enabled in HW.
+ * Refer IPA_HW_FEATURE_MASK
+ * @circBuffBaseAddrOffset : Base Address Offset of the Circular Event
+ * Log Buffer structure
+ * @statsInfo : Statistics related information
+ * @configInfo : Configuration related information
+ *
+ * @note The offset location of this structure from IPA_WRAPPER_BASE
+ * will be provided as Event Params for the IPA_HW_2_CPU_EVENT_LOG_INFO
+ * Event
+ */
+struct IpaHwEventLogInfoData_t {
+ u32 featureMask;
+ u32 circBuffBaseAddrOffset;
+ struct Ipa3HwEventInfoData_t statsInfo;
+ struct Ipa3HwEventInfoData_t configInfo;
+
+} __packed;
+
+/**
+ * struct ipa3_uc_ntn_ctx
+ * @ntn_uc_stats_ofst: Neutrino stats offset
+ * @ntn_uc_stats_mmio: Neutrino stats
+ * @priv: private data of client
+ * @uc_ready_cb: uc Ready cb
+ */
+struct ipa3_uc_ntn_ctx {
+ u32 ntn_uc_stats_ofst;
+ struct Ipa3HwStatsNTNInfoData_t *ntn_uc_stats_mmio;
+ void *priv;
+ ipa_uc_ready_cb uc_ready_cb;
+};
+
+/**
+ * enum ipa3_hw_2_cpu_ntn_events - Values that represent HW event
+ * to be sent to CPU
+ * @IPA_HW_2_CPU_EVENT_NTN_ERROR : Event to specify that HW
+ * detected an error in NTN
+ *
+ */
+enum ipa3_hw_2_cpu_ntn_events {
+ IPA_HW_2_CPU_EVENT_NTN_ERROR =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_NTN, 0),
+};
+
+
+/**
+ * enum ipa3_hw_ntn_errors - NTN specific error types.
+ * @IPA_HW_NTN_ERROR_NONE : No error persists
+ * @IPA_HW_NTN_CHANNEL_ERROR : Error is specific to channel
+ */
+enum ipa3_hw_ntn_errors {
+ IPA_HW_NTN_ERROR_NONE = 0,
+ IPA_HW_NTN_CHANNEL_ERROR = 1
+};
+
+/**
+ * enum ipa3_hw_ntn_channel_states - Values that represent NTN
+ * channel state machine.
+ * @IPA_HW_NTN_CHANNEL_STATE_INITED_DISABLED : Channel is
+ * initialized but disabled
+ * @IPA_HW_NTN_CHANNEL_STATE_RUNNING : Channel is running.
+ * Entered after SET_UP_COMMAND is processed successfully
+ * @IPA_HW_NTN_CHANNEL_STATE_ERROR : Channel is in error state
+ * @IPA_HW_NTN_CHANNEL_STATE_INVALID : Invalid state. Shall not
+ * be in use in operational scenario
+ *
+ * These states apply to both Tx and Rx paths. These do not reflect the
+ * sub-state the state machine may be in.
+ */
+enum ipa3_hw_ntn_channel_states {
+ IPA_HW_NTN_CHANNEL_STATE_INITED_DISABLED = 1,
+ IPA_HW_NTN_CHANNEL_STATE_RUNNING = 2,
+ IPA_HW_NTN_CHANNEL_STATE_ERROR = 3,
+ IPA_HW_NTN_CHANNEL_STATE_INVALID = 0xFF
+};
+
+/**
+ * enum ipa3_hw_ntn_channel_errors - List of NTN Channel error
+ * types. This is present in the event param
+ * @IPA_HW_NTN_CH_ERR_NONE: No error persists
+ * @IPA_HW_NTN_TX_FSM_ERROR: Error in the state machine
+ * transition
+ * @IPA_HW_NTN_TX_COMP_RE_FETCH_FAIL: Error while calculating
+ * num RE to bring
+ * @IPA_HW_NTN_RX_RING_WP_UPDATE_FAIL: Write pointer update
+ * failed in Rx ring
+ * @IPA_HW_NTN_RX_FSM_ERROR: Error in the state machine
+ * transition
+ * @IPA_HW_NTN_RX_CACHE_NON_EMPTY:
+ * @IPA_HW_NTN_CH_ERR_RESERVED:
+ *
+ * These states apply to both Tx and Rx paths. These do not
+ * reflect the sub-state the state machine may be in.
+ */
+enum ipa3_hw_ntn_channel_errors {
+ IPA_HW_NTN_CH_ERR_NONE = 0,
+ IPA_HW_NTN_TX_RING_WP_UPDATE_FAIL = 1,
+ IPA_HW_NTN_TX_FSM_ERROR = 2,
+ IPA_HW_NTN_TX_COMP_RE_FETCH_FAIL = 3,
+ IPA_HW_NTN_RX_RING_WP_UPDATE_FAIL = 4,
+ IPA_HW_NTN_RX_FSM_ERROR = 5,
+ IPA_HW_NTN_RX_CACHE_NON_EMPTY = 6,
+ IPA_HW_NTN_CH_ERR_RESERVED = 0xFF
+};
+
+
+/**
+ * struct Ipa3HwNtnSetUpCmdData_t - Ntn setup command data
+ * @ring_base_pa: physical address of the base of the Tx/Rx NTN
+ * ring
+ * @buff_pool_base_pa: physical address of the base of the Tx/Rx
+ * buffer pool
+ * @ntn_ring_size: size of the Tx/Rx NTN ring
+ * @num_buffers: Rx/tx buffer pool size
+ * @ntn_reg_base_ptr_pa: physical address of the Tx/Rx NTN
+ * Ring's tail pointer
+ * @ipa_pipe_number: IPA pipe number that has to be used for the
+ * Tx/Rx path
+ * @dir: Tx/Rx Direction
+ * @data_buff_size: size of the each data buffer allocated in
+ * DDR
+ */
+struct Ipa3HwNtnSetUpCmdData_t {
+ u32 ring_base_pa;
+ u32 buff_pool_base_pa;
+ u16 ntn_ring_size;
+ u16 num_buffers;
+ u32 ntn_reg_base_ptr_pa;
+ u8 ipa_pipe_number;
+ u8 dir;
+ u16 data_buff_size;
+
+} __packed;
+
+/**
+ * struct Ipa3HwNtnCommonChCmdData_t - Structure holding the
+ * parameters for Ntn Tear down command data params
+ *
+ *@ipa_pipe_number: IPA pipe number. This could be Tx or an Rx pipe
+ */
+union Ipa3HwNtnCommonChCmdData_t {
+ struct IpaHwNtnCommonChCmdParams_t {
+ u32 ipa_pipe_number :8;
+ u32 reserved :24;
+ } __packed params;
+ uint32_t raw32b;
+} __packed;
+
+
+/**
+ * struct Ipa3HwNTNErrorEventData_t - Structure holding the
+ * IPA_HW_2_CPU_EVENT_NTN_ERROR event. The parameters are passed
+ * as immediate params in the shared memory
+ *
+ *@ntn_error_type: type of NTN error (ipa3_hw_ntn_errors)
+ *@ipa_pipe_number: IPA pipe number on which error has happened
+ * Applicable only if error type indicates channel error
+ *@ntn_ch_err_type: Information about the channel error (if
+ * available)
+ */
+union Ipa3HwNTNErrorEventData_t {
+ struct IpaHwNTNErrorEventParams_t {
+ u32 ntn_error_type :8;
+ u32 reserved :8;
+ u32 ipa_pipe_number :8;
+ u32 ntn_ch_err_type :8;
+ } __packed params;
+ uint32_t raw32b;
+} __packed;
+
+/**
+ * struct NTN3RxInfoData_t - NTN Structure holding the Rx pipe
+ * information
+ *
+ *@max_outstanding_pkts: Number of outstanding packets in Rx
+ * Ring
+ *@num_pkts_processed: Number of packets processed - cumulative
+ *@rx_ring_rp_value: Read pointer last advertized to the WLAN FW
+ *
+ *@ntn_ch_err_type: Information about the channel error (if
+ * available)
+ *@rx_ind_ring_stats:
+ *@bam_stats:
+ *@num_bam_int_handled: Number of Bam Interrupts handled by FW
+ *@num_db: Number of times the doorbell was rung
+ *@num_unexpected_db: Number of unexpected doorbells
+ *@num_pkts_in_dis_uninit_state:
+ *@num_bam_int_handled_while_not_in_bam: Number of Bam
+ * Interrupts handled by FW
+ *@num_bam_int_handled_while_in_bam_state: Number of Bam
+ * Interrupts handled by FW
+ */
+struct NTN3RxInfoData_t {
+ u32 max_outstanding_pkts;
+ u32 num_pkts_processed;
+ u32 rx_ring_rp_value;
+ struct IpaHwRingStats_t rx_ind_ring_stats;
+ struct IpaHwBamStats_t bam_stats;
+ u32 num_bam_int_handled;
+ u32 num_db;
+ u32 num_unexpected_db;
+ u32 num_pkts_in_dis_uninit_state;
+ u32 num_bam_int_handled_while_not_in_bam;
+ u32 num_bam_int_handled_while_in_bam_state;
+} __packed;
+
+
+/**
+ * struct NTNTxInfoData_t - Structure holding the NTN Tx channel
+ * Ensure that this is always word aligned
+ *
+ *@num_pkts_processed: Number of packets processed - cumulative
+ *@tail_ptr_val: Latest value of doorbell written to copy engine
+ *@num_db_fired: Number of DB from uC FW to Copy engine
+ *
+ *@tx_comp_ring_stats:
+ *@bam_stats:
+ *@num_db: Number of times the doorbell was rung
+ *@num_unexpected_db: Number of unexpected doorbells
+ *@num_bam_int_handled: Number of Bam Interrupts handled by FW
+ *@num_bam_int_in_non_running_state: Number of Bam interrupts
+ * while not in Running state
+ *@num_qmb_int_handled: Number of QMB interrupts handled
+ *@num_bam_int_handled_while_wait_for_bam: Number of times the
+ * Imm Cmd is injected due to fw_desc change
+ */
+struct NTNTxInfoData_t {
+ u32 num_pkts_processed;
+ u32 tail_ptr_val;
+ u32 num_db_fired;
+ struct IpaHwRingStats_t tx_comp_ring_stats;
+ struct IpaHwBamStats_t bam_stats;
+ u32 num_db;
+ u32 num_unexpected_db;
+ u32 num_bam_int_handled;
+ u32 num_bam_int_in_non_running_state;
+ u32 num_qmb_int_handled;
+ u32 num_bam_int_handled_while_wait_for_bam;
+ u32 num_bam_int_handled_while_not_in_bam;
+} __packed;
+
+
+/**
+ * struct Ipa3HwStatsNTNInfoData_t - Structure holding the NTN Tx
+ * channel Ensure that this is always word aligned
+ *
+ */
+struct Ipa3HwStatsNTNInfoData_t {
+ struct NTN3RxInfoData_t rx_ch_stats[IPA_UC_MAX_NTN_RX_CHANNELS];
+ struct NTNTxInfoData_t tx_ch_stats[IPA_UC_MAX_NTN_TX_CHANNELS];
+} __packed;
+
+
+/*
+ * uC offload related data structures
+ */
+#define IPA_UC_OFFLOAD_CONNECTED BIT(0)
+#define IPA_UC_OFFLOAD_ENABLED BIT(1)
+#define IPA_UC_OFFLOAD_RESUMED BIT(2)
+
+/**
+ * enum ipa_cpu_2_hw_offload_commands - Values that represent
+ * the offload commands from CPU
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP : Command to set up
+ * Offload protocol's Tx/Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_RX_SET_UP : Command to tear down
+ * Offload protocol's Tx/ Rx Path
+ */
+enum ipa_cpu_2_hw_offload_commands {
+ IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 1),
+ IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
+};
+
+
+/**
+ * enum ipa3_hw_offload_channel_states - Values that represent
+ * offload channel state machine.
+ * @IPA_HW_OFFLOAD_CHANNEL_STATE_INITED_DISABLED : Channel is
+ * initialized but disabled
+ * @IPA_HW_OFFLOAD_CHANNEL_STATE_RUNNING : Channel is running.
+ * Entered after SET_UP_COMMAND is processed successfully
+ * @IPA_HW_OFFLOAD_CHANNEL_STATE_ERROR : Channel is in error state
+ * @IPA_HW_OFFLOAD_CHANNEL_STATE_INVALID : Invalid state. Shall not
+ * be in use in operational scenario
+ *
+ * These states apply to both Tx and Rx paths. These do not
+ * reflect the sub-state the state machine may be in
+ */
+enum ipa3_hw_offload_channel_states {
+ IPA_HW_OFFLOAD_CHANNEL_STATE_INITED_DISABLED = 1,
+ IPA_HW_OFFLOAD_CHANNEL_STATE_RUNNING = 2,
+ IPA_HW_OFFLOAD_CHANNEL_STATE_ERROR = 3,
+ IPA_HW_OFFLOAD_CHANNEL_STATE_INVALID = 0xFF
+};
+
+
+/**
+ * enum ipa3_hw_2_cpu_cmd_resp_status - Values that represent
+ * offload related command response status to be sent to CPU.
+ */
+enum ipa3_hw_2_cpu_offload_cmd_resp_status {
+ IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 0),
+ IPA_HW_2_CPU_OFFLOAD_MAX_TX_CHANNELS =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 1),
+ IPA_HW_2_CPU_OFFLOAD_TX_RING_OVERRUN_POSSIBILITY =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 2),
+ IPA_HW_2_CPU_OFFLOAD_TX_RING_SET_UP_FAILURE =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 3),
+ IPA_HW_2_CPU_OFFLOAD_TX_RING_PARAMS_UNALIGNED =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 4),
+ IPA_HW_2_CPU_OFFLOAD_UNKNOWN_TX_CHANNEL =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 5),
+ IPA_HW_2_CPU_OFFLOAD_TX_INVALID_FSM_TRANSITION =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 6),
+ IPA_HW_2_CPU_OFFLOAD_TX_FSM_TRANSITION_ERROR =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 7),
+ IPA_HW_2_CPU_OFFLOAD_MAX_RX_CHANNELS =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 8),
+ IPA_HW_2_CPU_OFFLOAD_RX_RING_PARAMS_UNALIGNED =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 9),
+ IPA_HW_2_CPU_OFFLOAD_RX_RING_SET_UP_FAILURE =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 10),
+ IPA_HW_2_CPU_OFFLOAD_UNKNOWN_RX_CHANNEL =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 11),
+ IPA_HW_2_CPU_OFFLOAD_RX_INVALID_FSM_TRANSITION =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 12),
+ IPA_HW_2_CPU_OFFLOAD_RX_FSM_TRANSITION_ERROR =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 13),
+ IPA_HW_2_CPU_OFFLOAD_RX_RING_OVERRUN_POSSIBILITY =
+ FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 14),
+};
+
+/**
+ * struct IpaHwSetUpCmd -
+ *
+ *
+ */
+union IpaHwSetUpCmd {
+ struct Ipa3HwNtnSetUpCmdData_t NtnSetupCh_params;
+} __packed;
+
+/**
+ * struct IpaHwOffloadSetUpCmdData_t -
+ *
+ *
+ */
+struct IpaHwOffloadSetUpCmdData_t {
+ u8 protocol;
+ union IpaHwSetUpCmd SetupCh_params;
+} __packed;
+
+/**
+ * struct IpaHwCommonChCmd - Structure holding the parameters
+ * for IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN
+ *
+ *
+ */
+union IpaHwCommonChCmd {
+ union Ipa3HwNtnCommonChCmdData_t NtnCommonCh_params;
+} __packed;
+
+struct IpaHwOffloadCommonChCmdData_t {
+ u8 protocol;
+ union IpaHwCommonChCmd CommonCh_params;
+} __packed;
+
+#endif /* _IPA_UC_OFFLOAD_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
index 1caccddf5834..e0f32bdcbb3d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
@@ -1190,7 +1190,7 @@ int ipa3_connect_wdi_pipe(struct ipa_wdi_in_params *in,
IPA_ACTIVE_CLIENTS_DEC_EP(in->sys.client);
dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
- ep->wdi_state |= IPA_WDI_CONNECTED;
+ ep->uc_offload_state |= IPA_WDI_CONNECTED;
IPADBG("client %d (ep: %d) connected\n", in->sys.client, ipa_ep_idx);
return 0;
@@ -1222,7 +1222,7 @@ int ipa3_disconnect_wdi_pipe(u32 clnt_hdl)
if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
ipa3_ctx->ep[clnt_hdl].valid == 0) {
- IPAERR("bad parm.\n");
+ IPAERR("bad parm, %d\n", clnt_hdl);
return -EINVAL;
}
@@ -1234,8 +1234,8 @@ int ipa3_disconnect_wdi_pipe(u32 clnt_hdl)
ep = &ipa3_ctx->ep[clnt_hdl];
- if (ep->wdi_state != IPA_WDI_CONNECTED) {
- IPAERR("WDI channel bad state %d\n", ep->wdi_state);
+ if (ep->uc_offload_state != IPA_WDI_CONNECTED) {
+ IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
return -EFAULT;
}
@@ -1283,7 +1283,7 @@ int ipa3_enable_wdi_pipe(u32 clnt_hdl)
if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
ipa3_ctx->ep[clnt_hdl].valid == 0) {
- IPAERR("bad parm.\n");
+ IPAERR("bad parm, %d\n", clnt_hdl);
return -EINVAL;
}
@@ -1295,8 +1295,8 @@ int ipa3_enable_wdi_pipe(u32 clnt_hdl)
ep = &ipa3_ctx->ep[clnt_hdl];
- if (ep->wdi_state != IPA_WDI_CONNECTED) {
- IPAERR("WDI channel bad state %d\n", ep->wdi_state);
+ if (ep->uc_offload_state != IPA_WDI_CONNECTED) {
+ IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
return -EFAULT;
}
IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl));
@@ -1319,7 +1319,7 @@ int ipa3_enable_wdi_pipe(u32 clnt_hdl)
result = ipa3_cfg_ep_holb(clnt_hdl, &holb_cfg);
}
IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl));
- ep->wdi_state |= IPA_WDI_ENABLED;
+ ep->uc_offload_state |= IPA_WDI_ENABLED;
IPADBG("client (ep: %d) enabled\n", clnt_hdl);
uc_timeout:
@@ -1345,7 +1345,7 @@ int ipa3_disable_wdi_pipe(u32 clnt_hdl)
if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
ipa3_ctx->ep[clnt_hdl].valid == 0) {
- IPAERR("bad parm.\n");
+ IPAERR("bad parm, %d\n", clnt_hdl);
return -EINVAL;
}
@@ -1379,8 +1379,8 @@ int ipa3_disable_wdi_pipe(u32 clnt_hdl)
ep = &ipa3_ctx->ep[clnt_hdl];
- if (ep->wdi_state != (IPA_WDI_CONNECTED | IPA_WDI_ENABLED)) {
- IPAERR("WDI channel bad state %d\n", ep->wdi_state);
+ if (ep->uc_offload_state != (IPA_WDI_CONNECTED | IPA_WDI_ENABLED)) {
+ IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
return -EFAULT;
}
IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl));
@@ -1436,7 +1436,7 @@ int ipa3_disable_wdi_pipe(u32 clnt_hdl)
ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
}
IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl));
- ep->wdi_state &= ~IPA_WDI_ENABLED;
+ ep->uc_offload_state &= ~IPA_WDI_ENABLED;
IPADBG("client (ep: %d) disabled\n", clnt_hdl);
uc_timeout:
@@ -1460,7 +1460,7 @@ int ipa3_resume_wdi_pipe(u32 clnt_hdl)
if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
ipa3_ctx->ep[clnt_hdl].valid == 0) {
- IPAERR("bad parm.\n");
+ IPAERR("bad parm, %d\n", clnt_hdl);
return -EINVAL;
}
@@ -1472,8 +1472,8 @@ int ipa3_resume_wdi_pipe(u32 clnt_hdl)
ep = &ipa3_ctx->ep[clnt_hdl];
- if (ep->wdi_state != (IPA_WDI_CONNECTED | IPA_WDI_ENABLED)) {
- IPAERR("WDI channel bad state %d\n", ep->wdi_state);
+ if (ep->uc_offload_state != (IPA_WDI_CONNECTED | IPA_WDI_ENABLED)) {
+ IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
return -EFAULT;
}
IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl));
@@ -1497,7 +1497,7 @@ int ipa3_resume_wdi_pipe(u32 clnt_hdl)
else
IPADBG("client (ep: %d) un-susp/delay\n", clnt_hdl);
- ep->wdi_state |= IPA_WDI_RESUMED;
+ ep->uc_offload_state |= IPA_WDI_RESUMED;
IPADBG("client (ep: %d) resumed\n", clnt_hdl);
uc_timeout:
@@ -1521,7 +1521,7 @@ int ipa3_suspend_wdi_pipe(u32 clnt_hdl)
if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
ipa3_ctx->ep[clnt_hdl].valid == 0) {
- IPAERR("bad parm.\n");
+ IPAERR("bad parm, %d\n", clnt_hdl);
return -EINVAL;
}
@@ -1533,9 +1533,9 @@ int ipa3_suspend_wdi_pipe(u32 clnt_hdl)
ep = &ipa3_ctx->ep[clnt_hdl];
- if (ep->wdi_state != (IPA_WDI_CONNECTED | IPA_WDI_ENABLED |
+ if (ep->uc_offload_state != (IPA_WDI_CONNECTED | IPA_WDI_ENABLED |
IPA_WDI_RESUMED)) {
- IPAERR("WDI channel bad state %d\n", ep->wdi_state);
+ IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
return -EFAULT;
}
@@ -1588,7 +1588,7 @@ int ipa3_suspend_wdi_pipe(u32 clnt_hdl)
ipa3_ctx->tag_process_before_gating = true;
IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl));
- ep->wdi_state &= ~IPA_WDI_RESUMED;
+ ep->uc_offload_state &= ~IPA_WDI_RESUMED;
IPADBG("client (ep: %d) suspended\n", clnt_hdl);
uc_timeout:
@@ -1603,7 +1603,7 @@ int ipa3_write_qmapid_wdi_pipe(u32 clnt_hdl, u8 qmap_id)
if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
ipa3_ctx->ep[clnt_hdl].valid == 0) {
- IPAERR("bad parm.\n");
+ IPAERR("bad parm, %d\n", clnt_hdl);
return -EINVAL;
}
@@ -1615,8 +1615,8 @@ int ipa3_write_qmapid_wdi_pipe(u32 clnt_hdl, u8 qmap_id)
ep = &ipa3_ctx->ep[clnt_hdl];
- if (!(ep->wdi_state & IPA_WDI_CONNECTED)) {
- IPAERR("WDI channel bad state %d\n", ep->wdi_state);
+ if (!(ep->uc_offload_state & IPA_WDI_CONNECTED)) {
+ IPAERR("WDI channel bad state %d\n", ep->uc_offload_state);
return -EFAULT;
}
IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl));
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index 21b8cac11d2b..395cf62c9728 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -40,7 +40,8 @@
#define IPA_TAG_SLEEP_MIN_USEC (1000)
#define IPA_TAG_SLEEP_MAX_USEC (2000)
#define IPA_FORCE_CLOSE_TAG_PROCESS_TIMEOUT (10 * HZ)
-#define IPA_BCR_REG_VAL (0x00000001)
+#define IPA_BCR_REG_VAL_v3_0 (0x00000001)
+#define IPA_BCR_REG_VAL_v3_5 (0x0000003B)
#define IPA_AGGR_GRAN_MIN (1)
#define IPA_AGGR_GRAN_MAX (32)
#define IPA_EOT_COAL_GRAN_MIN (1)
@@ -848,14 +849,28 @@ void ipa3_cfg_qsb(void)
int ipa3_init_hw(void)
{
u32 ipa_version = 0;
+ u32 val;
/* Read IPA version and make sure we have access to the registers */
ipa_version = ipahal_read_reg(IPA_VERSION);
if (ipa_version == 0)
return -EFAULT;
- /* using old BCR configuration(IPAv2.6)*/
- ipahal_write_reg(IPA_BCR, IPA_BCR_REG_VAL);
+ switch (ipa3_ctx->ipa_hw_type) {
+ case IPA_HW_v3_0:
+ case IPA_HW_v3_1:
+ val = IPA_BCR_REG_VAL_v3_0;
+ break;
+ case IPA_HW_v3_5:
+ case IPA_HW_v3_5_1:
+ val = IPA_BCR_REG_VAL_v3_5;
+ break;
+ default:
+ IPAERR("unknown HW type in dts\n");
+ return -EFAULT;
+ }
+
+ ipahal_write_reg(IPA_BCR, val);
ipa3_cfg_qsb();
@@ -3029,6 +3044,7 @@ int ipa3_bind_api_controller(enum ipa_hw_type ipa_hw_type,
api_ctrl->ipa_disconnect = ipa3_disconnect;
api_ctrl->ipa_reset_endpoint = ipa3_reset_endpoint;
api_ctrl->ipa_clear_endpoint_delay = ipa3_clear_endpoint_delay;
+ api_ctrl->ipa_disable_endpoint = NULL;
api_ctrl->ipa_cfg_ep = ipa3_cfg_ep;
api_ctrl->ipa_cfg_ep_nat = ipa3_cfg_ep_nat;
api_ctrl->ipa_cfg_ep_hdr = ipa3_cfg_ep_hdr;
@@ -3178,6 +3194,9 @@ int ipa3_bind_api_controller(enum ipa_hw_type ipa_hw_type,
api_ctrl->ipa_get_ipc_logbuf_low = ipa3_get_ipc_logbuf_low;
api_ctrl->ipa_rx_poll = ipa3_rx_poll;
api_ctrl->ipa_recycle_wan_skb = ipa3_recycle_wan_skb;
+ api_ctrl->ipa_setup_uc_ntn_pipes = ipa3_setup_uc_ntn_pipes;
+ api_ctrl->ipa_tear_down_uc_offload_pipes =
+ ipa3_tear_down_uc_offload_pipes;
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
index 4f6097c6da35..6c4d14b093c3 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
@@ -1215,7 +1215,10 @@ int ipahal_cp_proc_ctx_to_hw_buff(enum ipa_hdr_proc_type type,
(!phys_base && !hdr_base_addr) ||
!hdr_base_addr ||
((is_hdr_proc_ctx == false) && !offset_entry)) {
- IPAHAL_ERR("failed on validating params");
+ IPAHAL_ERR(
+ "invalid input: hdr_len:%u phys_base:%pad hdr_base_addr:%u is_hdr_proc_ctx:%d offset_entry:%pK\n"
+ , hdr_len, &phys_base, hdr_base_addr
+ , is_hdr_proc_ctx, offset_entry);
return -EINVAL;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index a4eab02cb571..aebdaab3ac77 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -1157,14 +1157,16 @@ static void apps_ipa_tx_complete_notify(void *priv,
struct net_device *dev = (struct net_device *)priv;
struct ipa3_wwan_private *wwan_ptr;
- if (evt != IPA_WRITE_DONE) {
- IPAWANDBG("unsupported event on Tx callback\n");
+ if (dev != IPA_NETDEV()) {
+ IPAWANDBG("Received pre-SSR packet completion\n");
+ dev_kfree_skb_any(skb);
return;
}
- if (dev != IPA_NETDEV()) {
- IPAWANDBG("Received pre-SSR packet completion\n");
+ if (evt != IPA_WRITE_DONE) {
+ IPAWANERR("unsupported evt on Tx callback, Drop the packet\n");
dev_kfree_skb_any(skb);
+ dev->stats.tx_dropped++;
return;
}
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index d78ee151c9e4..be3bc2f4edd4 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -865,6 +865,20 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
},
},
{
+ .ident = "Lenovo ideapad Y700-15ISK",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-15ISK"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad Y700 Touch-15ISK",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700 Touch-15ISK"),
+ },
+ },
+ {
.ident = "Lenovo ideapad Y700-17ISK",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 61f611296ad6..a45a5d103040 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -265,6 +265,8 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(typec_power_role),
POWER_SUPPLY_ATTR(pd_allowed),
POWER_SUPPLY_ATTR(pd_active),
+ POWER_SUPPLY_ATTR(charger_temp),
+ POWER_SUPPLY_ATTR(charger_temp_max),
/* Local extensions of type int64_t */
POWER_SUPPLY_ATTR(charge_counter_ext),
/* Properties of type `const char *' */
diff --git a/drivers/power/qcom-charger/qpnp-qnovo.c b/drivers/power/qcom-charger/qpnp-qnovo.c
index d50188a5efbf..2418b112d670 100644
--- a/drivers/power/qcom-charger/qpnp-qnovo.c
+++ b/drivers/power/qcom-charger/qpnp-qnovo.c
@@ -153,7 +153,7 @@ struct qnovo {
struct work_struct status_change_work;
int fv_uV_request;
int fcc_uA_request;
- struct votable *fcc_votable;
+ struct votable *fcc_max_votable;
struct votable *fv_votable;
};
@@ -243,8 +243,9 @@ static int qnovo_disable_cb(struct votable *votable, void *data, int disable,
vote(chip->fv_votable, QNOVO_VOTER, false, 0);
}
if (chip->fcc_uA_request != -EINVAL) {
- if (chip->fcc_votable)
- vote(chip->fcc_votable, QNOVO_VOTER, false, 0);
+ if (chip->fcc_max_votable)
+ vote(chip->fcc_max_votable, QNOVO_VOTER,
+ false, 0);
}
}
@@ -265,10 +266,10 @@ static int qnovo_disable_cb(struct votable *votable, void *data, int disable,
true, chip->fv_uV_request);
}
if (chip->fcc_uA_request != -EINVAL) {
- if (!chip->fcc_votable)
- chip->fcc_votable = find_votable("FCC");
- if (chip->fcc_votable)
- vote(chip->fcc_votable, QNOVO_VOTER,
+ if (!chip->fcc_max_votable)
+ chip->fcc_max_votable = find_votable("FCC_MAX");
+ if (chip->fcc_max_votable)
+ vote(chip->fcc_max_votable, QNOVO_VOTER,
true, chip->fcc_uA_request);
}
}
diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c
index 541e40aeb91a..83cb87f94665 100644
--- a/drivers/power/qcom-charger/qpnp-smb2.c
+++ b/drivers/power/qcom-charger/qpnp-smb2.c
@@ -25,9 +25,6 @@
#include "smb-lib.h"
#include "pmic-voter.h"
-#define SMB2_DEFAULT_FCC_UA 3000000
-#define SMB2_DEFAULT_FV_UV 4350000
-#define SMB2_DEFAULT_ICL_UA 3000000
#define SMB2_DEFAULT_WPWR_UW 8000000
static struct smb_params v1_params = {
@@ -146,7 +143,7 @@ static int smb2_parse_dt(struct smb2 *chip)
{
struct smb_charger *chg = &chip->chg;
struct device_node *node = chg->dev->of_node;
- int rc;
+ int rc, byte_len;
if (!node) {
pr_err("device tree node missing\n");
@@ -159,28 +156,47 @@ static int smb2_parse_dt(struct smb2 *chip)
rc = of_property_read_u32(node,
"qcom,fcc-max-ua", &chip->dt.fcc_ua);
if (rc < 0)
- chip->dt.fcc_ua = SMB2_DEFAULT_FCC_UA;
+ chip->dt.fcc_ua = -EINVAL;
rc = of_property_read_u32(node,
"qcom,fv-max-uv", &chip->dt.fv_uv);
if (rc < 0)
- chip->dt.fv_uv = SMB2_DEFAULT_FV_UV;
+ chip->dt.fv_uv = -EINVAL;
rc = of_property_read_u32(node,
"qcom,usb-icl-ua", &chip->dt.usb_icl_ua);
if (rc < 0)
- chip->dt.usb_icl_ua = SMB2_DEFAULT_ICL_UA;
+ chip->dt.usb_icl_ua = -EINVAL;
rc = of_property_read_u32(node,
"qcom,dc-icl-ua", &chip->dt.dc_icl_ua);
if (rc < 0)
- chip->dt.dc_icl_ua = SMB2_DEFAULT_ICL_UA;
+ chip->dt.dc_icl_ua = -EINVAL;
rc = of_property_read_u32(node,
"qcom,wipower-max-uw", &chip->dt.wipower_max_uw);
if (rc < 0)
chip->dt.wipower_max_uw = SMB2_DEFAULT_WPWR_UW;
+ if (of_find_property(node, "qcom,thermal-mitigation", &byte_len)) {
+ chg->thermal_mitigation = devm_kzalloc(chg->dev, byte_len,
+ GFP_KERNEL);
+
+ if (chg->thermal_mitigation == NULL)
+ return -ENOMEM;
+
+ chg->thermal_levels = byte_len / sizeof(u32);
+ rc = of_property_read_u32_array(node,
+ "qcom,thermal-mitigation",
+ chg->thermal_mitigation,
+ chg->thermal_levels);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't read threm limits rc = %d\n", rc);
+ return rc;
+ }
+ }
+
return 0;
}
@@ -350,6 +366,105 @@ static int smb2_init_usb_psy(struct smb2 *chip)
}
/*************************
+ * DC PSY REGISTRATION *
+ *************************/
+
+static enum power_supply_property smb2_dc_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static int smb2_dc_get_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct smb2 *chip = power_supply_get_drvdata(psy);
+ struct smb_charger *chg = &chip->chg;
+ int rc = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ rc = smblib_get_prop_dc_present(chg, val);
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ rc = smblib_get_prop_dc_online(chg, val);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ rc = smblib_get_prop_dc_current_max(chg, val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int smb2_dc_set_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct smb2 *chip = power_supply_get_drvdata(psy);
+ struct smb_charger *chg = &chip->chg;
+ int rc = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ rc = smblib_set_prop_dc_current_max(chg, val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int smb2_dc_prop_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ int rc;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ rc = 1;
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+
+ return rc;
+}
+
+static const struct power_supply_desc dc_psy_desc = {
+ .name = "dc",
+ .type = POWER_SUPPLY_TYPE_WIPOWER,
+ .properties = smb2_dc_props,
+ .num_properties = ARRAY_SIZE(smb2_dc_props),
+ .get_property = smb2_dc_get_prop,
+ .set_property = smb2_dc_set_prop,
+ .property_is_writeable = smb2_dc_prop_is_writeable,
+};
+
+static int smb2_init_dc_psy(struct smb2 *chip)
+{
+ struct power_supply_config dc_cfg = {};
+ struct smb_charger *chg = &chip->chg;
+
+ dc_cfg.drv_data = chip;
+ dc_cfg.of_node = chg->dev->of_node;
+ chg->dc_psy = devm_power_supply_register(chg->dev,
+ &dc_psy_desc,
+ &dc_cfg);
+ if (IS_ERR(chg->dc_psy)) {
+ pr_err("Couldn't register USB power supply\n");
+ return PTR_ERR(chg->dc_psy);
+ }
+
+ return 0;
+}
+
+/*************************
* BATT PSY REGISTRATION *
*************************/
@@ -360,6 +475,7 @@ static enum power_supply_property smb2_batt_props[] = {
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
};
static int smb2_batt_get_prop(struct power_supply *psy,
@@ -387,9 +503,11 @@ static int smb2_batt_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_CAPACITY:
smblib_get_prop_batt_capacity(chg, val);
break;
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ smblib_get_prop_system_temp_level(chg, val);
+ break;
default:
- pr_err("batt power supply prop %d not supported\n",
- psp);
+ pr_err("batt power supply prop %d not supported\n", psp);
return -EINVAL;
}
@@ -400,17 +518,21 @@ static int smb2_batt_set_prop(struct power_supply *psy,
enum power_supply_property prop,
const union power_supply_propval *val)
{
+ int rc = 0;
struct smb_charger *chg = power_supply_get_drvdata(psy);
switch (prop) {
case POWER_SUPPLY_PROP_INPUT_SUSPEND:
- smblib_set_prop_input_suspend(chg, val);
+ rc = smblib_set_prop_input_suspend(chg, val);
+ break;
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ rc = smblib_set_prop_system_temp_level(chg, val);
break;
default:
- return -EINVAL;
+ rc = -EINVAL;
}
- return 0;
+ return rc;
}
static int smb2_batt_prop_is_writeable(struct power_supply *psy,
@@ -418,6 +540,7 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy,
{
switch (psp) {
case POWER_SUPPLY_PROP_INPUT_SUSPEND:
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
return 1;
default:
break;
@@ -599,6 +722,20 @@ static int smb2_init_hw(struct smb2 *chip)
struct smb_charger *chg = &chip->chg;
int rc;
+ if (chip->dt.fcc_ua < 0)
+ smblib_get_charge_param(chg, &chg->param.fcc, &chip->dt.fcc_ua);
+
+ if (chip->dt.fv_uv < 0)
+ smblib_get_charge_param(chg, &chg->param.fv, &chip->dt.fv_uv);
+
+ if (chip->dt.usb_icl_ua < 0)
+ smblib_get_charge_param(chg, &chg->param.usb_icl,
+ &chip->dt.usb_icl_ua);
+
+ if (chip->dt.dc_icl_ua < 0)
+ smblib_get_charge_param(chg, &chg->param.dc_icl,
+ &chip->dt.dc_icl_ua);
+
/* votes must be cast before configuring software control */
vote(chg->pl_disable_votable,
USBIN_ICL_VOTER, true, 0);
@@ -608,7 +745,7 @@ static int smb2_init_hw(struct smb2 *chip)
DEFAULT_VOTER, chip->dt.suspend_input, 0);
vote(chg->dc_suspend_votable,
DEFAULT_VOTER, chip->dt.suspend_input, 0);
- vote(chg->fcc_votable,
+ vote(chg->fcc_max_votable,
DEFAULT_VOTER, true, chip->dt.fcc_ua);
vote(chg->fv_votable,
DEFAULT_VOTER, true, chip->dt.fv_uv);
@@ -617,17 +754,21 @@ static int smb2_init_hw(struct smb2 *chip)
vote(chg->dc_icl_votable,
DEFAULT_VOTER, true, chip->dt.dc_icl_ua);
- /* configure charge enable for software control; active high */
+ /*
+ * Configure charge enable for software control; active high, and end
+ * the charge cycle while the battery is OV.
+ */
rc = smblib_masked_write(chg, CHGR_CFG2_REG,
- CHG_EN_POLARITY_BIT | CHG_EN_SRC_BIT, 0);
+ CHG_EN_POLARITY_BIT |
+ CHG_EN_SRC_BIT |
+ BAT_OV_ECC_BIT, BAT_OV_ECC_BIT);
if (rc < 0) {
- dev_err(chg->dev,
- "Couldn't configure charge enable source rc=%d\n", rc);
+ dev_err(chg->dev, "Couldn't configure charger rc=%d\n", rc);
return rc;
}
/* enable the charging path */
- rc = smblib_enable_charging(chg, true);
+ rc = vote(chg->chg_disable_votable, DEFAULT_VOTER, false, 0);
if (rc < 0) {
dev_err(chg->dev, "Couldn't enable charging rc=%d\n", rc);
return rc;
@@ -635,11 +776,10 @@ static int smb2_init_hw(struct smb2 *chip)
/*
* trigger the usb-typec-change interrupt only when the CC state
- * changes, or there was a VBUS error
+ * changes
*/
rc = smblib_write(chg, TYPE_C_INTRPT_ENB_REG,
- TYPEC_CCSTATE_CHANGE_INT_EN_BIT
- | TYPEC_VBUS_ERROR_INT_EN_BIT);
+ TYPEC_CCSTATE_CHANGE_INT_EN_BIT);
if (rc < 0) {
dev_err(chg->dev,
"Couldn't configure Type-C interrupts rc=%d\n", rc);
@@ -904,6 +1044,12 @@ static int smb2_probe(struct platform_device *pdev)
goto cleanup;
}
+ rc = smb2_init_dc_psy(chip);
+ if (rc < 0) {
+ pr_err("Couldn't initialize dc psy rc=%d\n", rc);
+ goto cleanup;
+ }
+
rc = smb2_init_usb_psy(chip);
if (rc < 0) {
pr_err("Couldn't initialize usb psy rc=%d\n", rc);
diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c
index dd3ec1eb51e3..8fe882e078f0 100644
--- a/drivers/power/qcom-charger/smb-lib.c
+++ b/drivers/power/qcom-charger/smb-lib.c
@@ -210,22 +210,6 @@ static const struct apsd_result *smblib_get_apsd_result(struct smb_charger *chg)
* REGISTER SETTERS *
********************/
-int smblib_enable_charging(struct smb_charger *chg, bool enable)
-{
- int rc = 0;
-
- rc = smblib_masked_write(chg, CHARGING_ENABLE_CMD_REG,
- CHARGING_ENABLE_CMD_BIT,
- enable ? CHARGING_ENABLE_CMD_BIT : 0);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't %s charging rc=%d\n",
- enable ? "enable" : "disable", rc);
- return rc;
- }
-
- return rc;
-}
-
int smblib_set_charge_param(struct smb_charger *chg,
struct smb_chg_param *param, int val_u)
{
@@ -416,6 +400,14 @@ static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data,
return smblib_set_dc_suspend(chg, suspend);
}
+static int smblib_fcc_max_vote_callback(struct votable *votable, void *data,
+ int fcc_ua, const char *client)
+{
+ struct smb_charger *chg = data;
+
+ return vote(chg->fcc_votable, FCC_MAX_RESULT, true, fcc_ua);
+}
+
static int smblib_fcc_vote_callback(struct votable *votable, void *data,
int fcc_ua, const char *client)
{
@@ -606,6 +598,23 @@ static int smblib_pl_disable_vote_callback(struct votable *votable, void *data,
return 0;
}
+static int smblib_chg_disable_vote_callback(struct votable *votable, void *data,
+ int chg_disable, const char *client)
+{
+ struct smb_charger *chg = data;
+ int rc;
+
+ rc = smblib_masked_write(chg, CHARGING_ENABLE_CMD_REG,
+ CHARGING_ENABLE_CMD_BIT,
+ chg_disable ? 0 : CHARGING_ENABLE_CMD_BIT);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't %s charging rc=%d\n",
+ chg_disable ? "disable" : "enable", rc);
+ return rc;
+ }
+
+ return 0;
+}
/*****************
* OTG REGULATOR *
*****************/
@@ -849,6 +858,13 @@ done:
return rc;
}
+int smblib_get_prop_system_temp_level(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ val->intval = chg->system_temp_level;
+ return 0;
+}
+
/***********************
* BATTERY PSY SETTERS *
***********************/
@@ -876,6 +892,101 @@ int smblib_set_prop_input_suspend(struct smb_charger *chg,
return rc;
}
+int smblib_set_prop_system_temp_level(struct smb_charger *chg,
+ const union power_supply_propval *val)
+{
+ if (val->intval < 0)
+ return -EINVAL;
+
+ if (chg->thermal_levels <= 0)
+ return -EINVAL;
+
+ if (val->intval > chg->thermal_levels)
+ return -EINVAL;
+
+ chg->system_temp_level = val->intval;
+ if (chg->system_temp_level == chg->thermal_levels)
+ return vote(chg->chg_disable_votable, THERMAL_DAEMON, true, 0);
+
+ vote(chg->chg_disable_votable, THERMAL_DAEMON, false, 0);
+ if (chg->system_temp_level == 0)
+ return vote(chg->fcc_votable, THERMAL_DAEMON, false, 0);
+
+ vote(chg->fcc_votable, THERMAL_DAEMON, true,
+ chg->thermal_mitigation[chg->system_temp_level]);
+ return 0;
+}
+
+/*******************
+ * DC PSY GETTERS *
+ *******************/
+
+int smblib_get_prop_dc_present(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc = 0;
+ u8 stat;
+
+ rc = smblib_read(chg, DC_INT_RT_STS_REG, &stat);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't read DC_INT_RT_STS_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+ smblib_dbg(chg, PR_REGISTER, "DC_INT_RT_STS_REG = 0x%02x\n",
+ stat);
+
+ val->intval = (bool)(stat & DCIN_PLUGIN_RT_STS_BIT);
+
+ return rc;
+}
+
+int smblib_get_prop_dc_online(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc = 0;
+ u8 stat;
+
+ if (get_client_vote(chg->dc_suspend_votable, USER_VOTER)) {
+ val->intval = false;
+ return rc;
+ }
+
+ rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't read POWER_PATH_STATUS rc=%d\n",
+ rc);
+ return rc;
+ }
+ smblib_dbg(chg, PR_REGISTER, "POWER_PATH_STATUS = 0x%02x\n",
+ stat);
+
+ val->intval = (stat & USE_DCIN_BIT) &&
+ (stat & VALID_INPUT_POWER_SOURCE_BIT);
+
+ return rc;
+}
+
+int smblib_get_prop_dc_current_max(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ val->intval = get_effective_result_locked(chg->dc_icl_votable);
+ return 0;
+}
+
+/*******************
+ * USB PSY SETTERS *
+ * *****************/
+
+int smblib_set_prop_dc_current_max(struct smb_charger *chg,
+ const union power_supply_propval *val)
+{
+ int rc;
+
+ rc = vote(chg->dc_icl_votable, USER_VOTER, true, val->intval);
+ return rc;
+}
+
/*******************
* USB PSY GETTERS *
*******************/
@@ -1694,7 +1805,15 @@ int smblib_create_votables(struct smb_charger *chg)
return rc;
}
- chg->fcc_votable = create_votable("FCC", VOTE_MAX,
+ chg->fcc_max_votable = create_votable("FCC_MAX", VOTE_MAX,
+ smblib_fcc_max_vote_callback,
+ chg);
+ if (IS_ERR(chg->fcc_max_votable)) {
+ rc = PTR_ERR(chg->fcc_max_votable);
+ return rc;
+ }
+
+ chg->fcc_votable = create_votable("FCC", VOTE_MIN,
smblib_fcc_vote_callback,
chg);
if (IS_ERR(chg->fcc_votable)) {
@@ -1749,6 +1868,14 @@ int smblib_create_votables(struct smb_charger *chg)
return rc;
}
+ chg->chg_disable_votable = create_votable("CHG_DISABLE", VOTE_SET_ANY,
+ smblib_chg_disable_vote_callback,
+ chg);
+ if (IS_ERR(chg->chg_disable_votable)) {
+ rc = PTR_ERR(chg->chg_disable_votable);
+ return rc;
+ }
+
return rc;
}
@@ -1796,6 +1923,7 @@ int smblib_deinit(struct smb_charger *chg)
{
destroy_votable(chg->usb_suspend_votable);
destroy_votable(chg->dc_suspend_votable);
+ destroy_votable(chg->fcc_max_votable);
destroy_votable(chg->fcc_votable);
destroy_votable(chg->fv_votable);
destroy_votable(chg->usb_icl_votable);
diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h
index 2e35e1e3b174..1521fdb3fccf 100644
--- a/drivers/power/qcom-charger/smb-lib.h
+++ b/drivers/power/qcom-charger/smb-lib.h
@@ -31,6 +31,8 @@ enum print_reason {
#define CHG_STATE_VOTER "CHG_STATE_VOTER"
#define TYPEC_SRC_VOTER "TYPEC_SRC_VOTER"
#define TAPER_END_VOTER "TAPER_END_VOTER"
+#define FCC_MAX_RESULT "FCC_MAX_RESULT"
+#define THERMAL_DAEMON "THERMAL_DAEMON"
enum smb_mode {
PARALLEL_MASTER = 0,
@@ -93,6 +95,7 @@ struct smb_charger {
/* power supplies */
struct power_supply *batt_psy;
struct power_supply *usb_psy;
+ struct power_supply *dc_psy;
struct power_supply_desc usb_psy_desc;
/* parallel charging */
@@ -106,6 +109,7 @@ struct smb_charger {
/* votables */
struct votable *usb_suspend_votable;
struct votable *dc_suspend_votable;
+ struct votable *fcc_max_votable;
struct votable *fcc_votable;
struct votable *fv_votable;
struct votable *usb_icl_votable;
@@ -113,6 +117,7 @@ struct smb_charger {
struct votable *pd_allowed_votable;
struct votable *awake_votable;
struct votable *pl_disable_votable;
+ struct votable *chg_disable_votable;
/* work */
struct work_struct pl_detect_work;
@@ -125,6 +130,10 @@ struct smb_charger {
int voltage_max_uv;
bool pd_active;
bool vbus_present;
+
+ int system_temp_level;
+ int thermal_levels;
+ int *thermal_mitigation;
};
int smblib_read(struct smb_charger *chg, u16 addr, u8 *val);
@@ -171,8 +180,22 @@ int smblib_get_prop_batt_charge_type(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_batt_health(struct smb_charger *chg,
union power_supply_propval *val);
+int smblib_get_prop_system_temp_level(struct smb_charger *chg,
+ union power_supply_propval *val);
+
int smblib_set_prop_input_suspend(struct smb_charger *chg,
const union power_supply_propval *val);
+int smblib_set_prop_system_temp_level(struct smb_charger *chg,
+ const union power_supply_propval *val);
+
+int smblib_get_prop_dc_present(struct smb_charger *chg,
+ union power_supply_propval *val);
+int smblib_get_prop_dc_online(struct smb_charger *chg,
+ union power_supply_propval *val);
+int smblib_get_prop_dc_current_max(struct smb_charger *chg,
+ union power_supply_propval *val);
+int smblib_set_prop_dc_current_max(struct smb_charger *chg,
+ const union power_supply_propval *val);
int smblib_get_prop_usb_present(struct smb_charger *chg,
union power_supply_propval *val);
diff --git a/drivers/power/qcom-charger/smb-reg.h b/drivers/power/qcom-charger/smb-reg.h
index 5af01c229f01..b03e8a7e0403 100644
--- a/drivers/power/qcom-charger/smb-reg.h
+++ b/drivers/power/qcom-charger/smb-reg.h
@@ -641,6 +641,9 @@ enum {
#define WIPWR_RANGE_STATUS_REG (DCIN_BASE + 0x08)
#define WIPWR_RANGE_STATUS_MASK GENMASK(4, 0)
+#define DC_INT_RT_STS_REG (DCIN_BASE + 0x10)
+#define DCIN_PLUGIN_RT_STS_BIT BIT(4)
+
/* DCIN Interrupt Bits */
#define WIPWR_VOLTAGE_RANGE_RT_STS_BIT BIT(7)
#define DCIN_ICL_CHANGE_RT_STS_BIT BIT(6)
diff --git a/drivers/power/qcom-charger/smb138x-charger.c b/drivers/power/qcom-charger/smb138x-charger.c
index 9a6baff27dac..11d936762e3c 100644
--- a/drivers/power/qcom-charger/smb138x-charger.c
+++ b/drivers/power/qcom-charger/smb138x-charger.c
@@ -564,7 +564,7 @@ static int smb138x_init_hw(struct smb138x *chip)
}
/* enable the charging path */
- rc = smblib_enable_charging(chg, true);
+ rc = vote(chg->chg_disable_votable, DEFAULT_VOTER, false, 0);
if (rc < 0) {
dev_err(chg->dev, "Couldn't enable charging rc=%d\n", rc);
return rc;
@@ -857,7 +857,9 @@ static int smb138x_slave_probe(struct smb138x *chip)
}
/* enable the charging path */
- rc = smblib_enable_charging(chg, true);
+ rc = smblib_masked_write(chg, CHARGING_ENABLE_CMD_REG,
+ CHARGING_ENABLE_CMD_BIT,
+ CHARGING_ENABLE_CMD_BIT);
if (rc < 0) {
dev_err(chg->dev, "Couldn't enable charging rc=%d\n", rc);
return rc;
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index eb515721dfde..d49d8606da15 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -134,24 +134,24 @@ static bool have_full_constraints(void)
return has_full_constraints || of_have_populated_dt();
}
+static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev)
+{
+ if (rdev && rdev->supply)
+ return rdev->supply->rdev;
+
+ return NULL;
+}
+
/**
* regulator_lock_supply - lock a regulator and its supplies
* @rdev: regulator source
*/
static void regulator_lock_supply(struct regulator_dev *rdev)
{
- struct regulator *supply;
- int i = 0;
-
- while (1) {
- mutex_lock_nested(&rdev->mutex, i++);
- supply = rdev->supply;
-
- if (!rdev->supply)
- return;
+ int i;
- rdev = supply->rdev;
- }
+ for (i = 0; rdev->supply; rdev = rdev_get_supply(rdev), i++)
+ mutex_lock_nested(&rdev->mutex, i);
}
/**
diff --git a/drivers/regulator/cpr3-mmss-regulator.c b/drivers/regulator/cpr3-mmss-regulator.c
index e5055708a871..232bcf8fcf31 100644
--- a/drivers/regulator/cpr3-mmss-regulator.c
+++ b/drivers/regulator/cpr3-mmss-regulator.c
@@ -50,6 +50,9 @@
* @limitation: CPR limitation select fuse parameter value
* @aging_init_quot_diff: Initial quotient difference between CPR aging
* min and max sensors measured at time of manufacturing
+ * @force_highest_corner: Flag indicating that all corners must operate
+ * at the voltage of the highest corner. This is
+ * applicable to MSMCOBALT only.
*
* This struct holds the values for all of the fuses read from memory.
*/
@@ -60,6 +63,7 @@ struct cpr3_msm8996_mmss_fuses {
u64 cpr_fusing_rev;
u64 limitation;
u64 aging_init_quot_diff;
+ u64 force_highest_corner;
};
/* Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 */
@@ -158,6 +162,12 @@ msmcobalt_mmss_offset_voltage_param[MSM8996_MMSS_FUSE_CORNERS][2] = {
{{65, 44, 47}, {} },
};
+static const struct cpr3_fuse_param
+msmcobalt_cpr_force_highest_corner_param[] = {
+ {100, 45, 45},
+ {},
+};
+
#define MSM8996PRO_SOC_ID 4
#define MSMCOBALT_SOC_ID 5
@@ -243,6 +253,12 @@ enum msmcobalt_cpr_partial_binning {
MSMCOBALT_CPR_PARTIAL_BINNING_SAFE_CORNER = 0xE,
};
+/*
+ * The partial binning open-loop voltage fuse values only apply to the lowest
+ * two fuse corners (0 and 1, i.e. MinSVS and SVS).
+ */
+#define MSMCOBALT_CPR_PARTIAL_BINNING_MAX_FUSE_CORNER 1
+
/**
* cpr3_msm8996_mmss_read_fuse_data() - load MMSS specific fuse parameter values
* @vreg: Pointer to the CPR3 regulator
@@ -338,6 +354,19 @@ static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg)
}
if (vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID) {
+ rc = cpr3_read_fuse_param(base,
+ msmcobalt_cpr_force_highest_corner_param,
+ &fuse->force_highest_corner);
+ if (rc) {
+ cpr3_err(vreg, "Unable to read CPR force highest corner fuse, rc=%d\n",
+ rc);
+ return rc;
+ }
+ if (fuse->force_highest_corner)
+ cpr3_info(vreg, "Fusing requires all operation at the highest corner\n");
+ }
+
+ if (vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID) {
combo_max = CPR3_MSMCOBALT_MMSS_FUSE_COMBO_COUNT;
vreg->fuse_combo = fuse->cpr_fusing_rev;
} else if (vreg->thread->ctrl->soc_revision == MSM8996PRO_SOC_ID) {
@@ -738,7 +767,8 @@ static int cpr3_msm8996_mmss_calculate_open_loop_voltages(
*/
if (is_msmcobalt &&
(volt_init == MSMCOBALT_CPR_PARTIAL_BINNING_NEXT_CORNER ||
- volt_init == MSMCOBALT_CPR_PARTIAL_BINNING_SAFE_CORNER))
+ volt_init == MSMCOBALT_CPR_PARTIAL_BINNING_SAFE_CORNER) &&
+ i <= MSMCOBALT_CPR_PARTIAL_BINNING_MAX_FUSE_CORNER)
volt_init = MSM8996_MMSS_MIN_VOLTAGE_FUSE_VAL;
fuse_volt[i] = cpr3_convert_open_loop_voltage_fuse(ref_volt[i],
@@ -849,19 +879,43 @@ static int cpr3_msmcobalt_partial_binning_override(struct cpr3_regulator *vreg)
u32 proc_freq;
struct cpr3_corner *corner;
struct cpr3_corner *safe_corner;
- int i, j, low, high, safe_fuse_corner;
+ int i, j, low, high, safe_fuse_corner, max_fuse_corner;
if (vreg->thread->ctrl->soc_revision != MSMCOBALT_SOC_ID)
return 0;
- /* Loop over all fuse corners except for the highest one. */
- for (i = 0; i < vreg->fuse_corner_count - 1; i++) {
+ /* Handle the force highest corner fuse. */
+ if (fuse->force_highest_corner) {
+ cpr3_info(vreg, "overriding CPR parameters for corners 0 to %d with quotients and voltages of corner %d\n",
+ vreg->corner_count - 2, vreg->corner_count - 1);
+ corner = &vreg->corner[vreg->corner_count - 1];
+ for (i = 0; i < vreg->corner_count - 1; i++) {
+ proc_freq = vreg->corner[i].proc_freq;
+ vreg->corner[i] = *corner;
+ vreg->corner[i].proc_freq = proc_freq;
+ }
+
+ /*
+ * Return since the potential partial binning fuse values are
+ * superceded by the force highest corner fuse value.
+ */
+ return 0;
+ }
+
+ /*
+ * Allow up to the max corner which can be fused with partial
+ * binning values.
+ */
+ max_fuse_corner = min(MSMCOBALT_CPR_PARTIAL_BINNING_MAX_FUSE_CORNER,
+ vreg->fuse_corner_count - 2);
+
+ for (i = 0; i <= max_fuse_corner; i++) {
/* Determine which higher corners to override with (if any). */
if (fuse->init_voltage[i] != next
&& fuse->init_voltage[i] != safe)
continue;
- for (j = i + 1; j < vreg->fuse_corner_count - 1; j++)
+ for (j = i + 1; j <= max_fuse_corner; j++)
if (fuse->init_voltage[j] != next
&& fuse->init_voltage[j] != safe)
break;
diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h
index 074878b55a0b..d044f3f273be 100644
--- a/drivers/scsi/aacraid/aacraid.h
+++ b/drivers/scsi/aacraid/aacraid.h
@@ -944,6 +944,7 @@ struct fib {
*/
struct list_head fiblink;
void *data;
+ u32 vector_no;
struct hw_fib *hw_fib_va; /* Actual shared object */
dma_addr_t hw_fib_pa; /* physical address of hw_fib*/
};
@@ -2113,6 +2114,7 @@ static inline unsigned int cap_to_cyls(sector_t capacity, unsigned divisor)
int aac_acquire_irq(struct aac_dev *dev);
void aac_free_irq(struct aac_dev *dev);
const char *aac_driverinfo(struct Scsi_Host *);
+void aac_fib_vector_assign(struct aac_dev *dev);
struct fib *aac_fib_alloc(struct aac_dev *dev);
int aac_fib_setup(struct aac_dev *dev);
void aac_fib_map_free(struct aac_dev *dev);
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
index a1f90fe849c9..4cbf54928640 100644
--- a/drivers/scsi/aacraid/commsup.c
+++ b/drivers/scsi/aacraid/commsup.c
@@ -83,13 +83,38 @@ static int fib_map_alloc(struct aac_dev *dev)
void aac_fib_map_free(struct aac_dev *dev)
{
- pci_free_consistent(dev->pdev,
- dev->max_fib_size * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB),
- dev->hw_fib_va, dev->hw_fib_pa);
+ if (dev->hw_fib_va && dev->max_fib_size) {
+ pci_free_consistent(dev->pdev,
+ (dev->max_fib_size *
+ (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB)),
+ dev->hw_fib_va, dev->hw_fib_pa);
+ }
dev->hw_fib_va = NULL;
dev->hw_fib_pa = 0;
}
+void aac_fib_vector_assign(struct aac_dev *dev)
+{
+ u32 i = 0;
+ u32 vector = 1;
+ struct fib *fibptr = NULL;
+
+ for (i = 0, fibptr = &dev->fibs[i];
+ i < (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB);
+ i++, fibptr++) {
+ if ((dev->max_msix == 1) ||
+ (i > ((dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB - 1)
+ - dev->vector_cap))) {
+ fibptr->vector_no = 0;
+ } else {
+ fibptr->vector_no = vector;
+ vector++;
+ if (vector == dev->max_msix)
+ vector = 1;
+ }
+ }
+}
+
/**
* aac_fib_setup - setup the fibs
* @dev: Adapter to set up
@@ -151,6 +176,12 @@ int aac_fib_setup(struct aac_dev * dev)
hw_fib_pa = hw_fib_pa +
dev->max_fib_size + sizeof(struct aac_fib_xporthdr);
}
+
+ /*
+ *Assign vector numbers to fibs
+ */
+ aac_fib_vector_assign(dev);
+
/*
* Add the fib chain to the free list
*/
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index 3b6e5c67e853..aa6eccb8940b 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -1404,8 +1404,18 @@ static int aac_acquire_resources(struct aac_dev *dev)
aac_adapter_enable_int(dev);
- if (!dev->sync_mode)
+ /*max msix may change after EEH
+ * Re-assign vectors to fibs
+ */
+ aac_fib_vector_assign(dev);
+
+ if (!dev->sync_mode) {
+ /* After EEH recovery or suspend resume, max_msix count
+ * may change, therfore updating in init as well.
+ */
aac_adapter_start(dev);
+ dev->init->Sa_MSIXVectors = cpu_to_le32(dev->max_msix);
+ }
return 0;
error_iounmap:
diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c
index 2aa34ea8ceb1..bc0203f3d243 100644
--- a/drivers/scsi/aacraid/src.c
+++ b/drivers/scsi/aacraid/src.c
@@ -156,8 +156,8 @@ static irqreturn_t aac_src_intr_message(int irq, void *dev_id)
break;
if (dev->msi_enabled && dev->max_msix > 1)
atomic_dec(&dev->rrq_outstanding[vector_no]);
- aac_intr_normal(dev, handle-1, 0, isFastResponse, NULL);
dev->host_rrq[index++] = 0;
+ aac_intr_normal(dev, handle-1, 0, isFastResponse, NULL);
if (index == (vector_no + 1) * dev->vector_cap)
index = vector_no * dev->vector_cap;
dev->host_rrq_idx[vector_no] = index;
@@ -452,36 +452,20 @@ static int aac_src_deliver_message(struct fib *fib)
#endif
u16 hdr_size = le16_to_cpu(fib->hw_fib_va->header.Size);
+ u16 vector_no;
atomic_inc(&q->numpending);
if (dev->msi_enabled && fib->hw_fib_va->header.Command != AifRequest &&
dev->max_msix > 1) {
- u_int16_t vector_no, first_choice = 0xffff;
-
- vector_no = dev->fibs_pushed_no % dev->max_msix;
- do {
- vector_no += 1;
- if (vector_no == dev->max_msix)
- vector_no = 1;
- if (atomic_read(&dev->rrq_outstanding[vector_no]) <
- dev->vector_cap)
- break;
- if (0xffff == first_choice)
- first_choice = vector_no;
- else if (vector_no == first_choice)
- break;
- } while (1);
- if (vector_no == first_choice)
- vector_no = 0;
- atomic_inc(&dev->rrq_outstanding[vector_no]);
- if (dev->fibs_pushed_no == 0xffffffff)
- dev->fibs_pushed_no = 0;
- else
- dev->fibs_pushed_no++;
+ vector_no = fib->vector_no;
fib->hw_fib_va->header.Handle += (vector_no << 16);
+ } else {
+ vector_no = 0;
}
+ atomic_inc(&dev->rrq_outstanding[vector_no]);
+
if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE2) {
/* Calculate the amount to the fibsize bits */
fibsize = (hdr_size + 127) / 128 - 1;
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c
index b846a4683562..fc6a83188c1e 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_osm.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c
@@ -1336,6 +1336,7 @@ ahc_platform_set_tags(struct ahc_softc *ahc, struct scsi_device *sdev,
case AHC_DEV_Q_TAGGED:
scsi_change_queue_depth(sdev,
dev->openings + dev->active);
+ break;
default:
/*
* We allow the OS to queue 2 untagged transactions to
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index fe0c5143f8e6..758f76e88704 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -4470,6 +4470,7 @@ put_shost:
scsi_host_put(phba->shost);
free_kset:
iscsi_boot_destroy_kset(phba->boot_kset);
+ phba->boot_kset = NULL;
return -ENOMEM;
}
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index 536cd5a80422..43ac62623bf2 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -4003,13 +4003,17 @@ static ssize_t ipr_store_update_fw(struct device *dev,
struct ipr_sglist *sglist;
char fname[100];
char *src;
- int len, result, dnld_size;
+ char *endline;
+ int result, dnld_size;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- len = snprintf(fname, 99, "%s", buf);
- fname[len-1] = '\0';
+ snprintf(fname, sizeof(fname), "%s", buf);
+
+ endline = strchr(fname, '\n');
+ if (endline)
+ *endline = '\0';
if (request_firmware(&fw_entry, fname, &ioa_cfg->pdev->dev)) {
dev_err(&ioa_cfg->pdev->dev, "Firmware file %s not found\n", fname);
diff --git a/drivers/scsi/scsi_common.c b/drivers/scsi/scsi_common.c
index c126966130ab..ce79de822e46 100644
--- a/drivers/scsi/scsi_common.c
+++ b/drivers/scsi/scsi_common.c
@@ -278,8 +278,16 @@ int scsi_set_sense_information(u8 *buf, int buf_len, u64 info)
ucp[3] = 0;
put_unaligned_be64(info, &ucp[4]);
} else if ((buf[0] & 0x7f) == 0x70) {
- buf[0] |= 0x80;
- put_unaligned_be64(info, &buf[3]);
+ /*
+ * Only set the 'VALID' bit if we can represent the value
+ * correctly; otherwise just fill out the lower bytes and
+ * clear the 'VALID' flag.
+ */
+ if (info <= 0xffffffffUL)
+ buf[0] |= 0x80;
+ else
+ buf[0] &= 0x7f;
+ put_unaligned_be32((u32)info, &buf[3]);
}
return 0;
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 6d6606e1568a..5d81bcc1dc75 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -673,7 +673,7 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode)
*/
if (sdkp->lbprz) {
q->limits.discard_alignment = 0;
- q->limits.discard_granularity = 1;
+ q->limits.discard_granularity = logical_block_size;
} else {
q->limits.discard_alignment = sdkp->unmap_alignment *
logical_block_size;
@@ -1300,18 +1300,19 @@ static int sd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
struct scsi_disk *sdkp = scsi_disk(bdev->bd_disk);
struct scsi_device *sdp = sdkp->device;
struct Scsi_Host *host = sdp->host;
+ sector_t capacity = logical_to_sectors(sdp, sdkp->capacity);
int diskinfo[4];
/* default to most commonly used values */
- diskinfo[0] = 0x40; /* 1 << 6 */
- diskinfo[1] = 0x20; /* 1 << 5 */
- diskinfo[2] = sdkp->capacity >> 11;
-
+ diskinfo[0] = 0x40; /* 1 << 6 */
+ diskinfo[1] = 0x20; /* 1 << 5 */
+ diskinfo[2] = capacity >> 11;
+
/* override with calculated, extended default, or driver values */
if (host->hostt->bios_param)
- host->hostt->bios_param(sdp, bdev, sdkp->capacity, diskinfo);
+ host->hostt->bios_param(sdp, bdev, capacity, diskinfo);
else
- scsicam_bios_param(bdev, sdkp->capacity, diskinfo);
+ scsicam_bios_param(bdev, capacity, diskinfo);
geo->heads = diskinfo[0];
geo->sectors = diskinfo[1];
@@ -2281,14 +2282,6 @@ got_data:
if (sdkp->capacity > 0xffffffff)
sdp->use_16_for_rw = 1;
- /* Rescale capacity to 512-byte units */
- if (sector_size == 4096)
- sdkp->capacity <<= 3;
- else if (sector_size == 2048)
- sdkp->capacity <<= 2;
- else if (sector_size == 1024)
- sdkp->capacity <<= 1;
-
blk_queue_physical_block_size(sdp->request_queue,
sdkp->physical_block_size);
sdkp->device->sector_size = sector_size;
@@ -2739,11 +2732,6 @@ static int sd_try_extended_inquiry(struct scsi_device *sdp)
return 0;
}
-static inline u32 logical_to_sectors(struct scsi_device *sdev, u32 blocks)
-{
- return blocks << (ilog2(sdev->sector_size) - 9);
-}
-
/**
* sd_revalidate_disk - called the first time a new disk is seen,
* performs disk spin up, read_capacity, etc.
@@ -2827,7 +2815,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
/* Combine with controller limits */
q->limits.max_sectors = min(rw_max, queue_max_hw_sectors(q));
- set_capacity(disk, sdkp->capacity);
+ set_capacity(disk, logical_to_sectors(sdp, sdkp->capacity));
sd_config_write_same(sdkp);
kfree(buffer);
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
index 5f2a84aff29f..654630bb7d0e 100644
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -65,7 +65,7 @@ struct scsi_disk {
struct device dev;
struct gendisk *disk;
atomic_t openers;
- sector_t capacity; /* size in 512-byte sectors */
+ sector_t capacity; /* size in logical blocks */
u32 max_xfer_blocks;
u32 opt_xfer_blocks;
u32 max_ws_blocks;
@@ -146,6 +146,11 @@ static inline int scsi_medium_access_command(struct scsi_cmnd *scmd)
return 0;
}
+static inline sector_t logical_to_sectors(struct scsi_device *sdev, sector_t blocks)
+{
+ return blocks << (ilog2(sdev->sector_size) - 9);
+}
+
/*
* A DIF-capable target device can be formatted with different
* protection schemes. Currently 0 through 3 are defined:
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index ab184cd4d773..77b2da269d6e 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -652,7 +652,8 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
else
hp->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV : SG_DXFER_NONE;
hp->dxfer_len = mxsize;
- if (hp->dxfer_direction == SG_DXFER_TO_DEV)
+ if ((hp->dxfer_direction == SG_DXFER_TO_DEV) ||
+ (hp->dxfer_direction == SG_DXFER_TO_FROM_DEV))
hp->dxferp = (char __user *)buf + cmd_size;
else
hp->dxferp = NULL;
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index 3fba42ad9fb8..0f636cc4c809 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -889,8 +889,9 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb,
do_work = true;
process_err_fn = storvsc_remove_lun;
break;
- case (SRB_STATUS_ABORTED | SRB_STATUS_AUTOSENSE_VALID):
- if ((asc == 0x2a) && (ascq == 0x9)) {
+ case SRB_STATUS_ABORTED:
+ if (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID &&
+ (asc == 0x2a) && (ascq == 0x9)) {
do_work = true;
process_err_fn = storvsc_device_scan;
/*
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 3e167f4c0f42..4d406c51d884 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -4092,8 +4092,9 @@ int ufshcd_uic_hibern8_exit(struct ufs_hba *hba)
ufshcd_update_error_stats(hba, UFS_ERR_HIBERN8_EXIT);
dev_err(hba->dev, "%s: hibern8 exit failed. ret = %d",
__func__, ret);
+ ret = ufshcd_link_recovery(hba);
/* Unable to recover the link, so no point proceeding */
- if (ufshcd_link_recovery(hba))
+ if (ret)
BUG();
} else {
dev_dbg(hba->dev, "%s: Hibern8 Exit at %lld us", __func__,
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 98fce5e5c06a..2e6672326a77 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -299,6 +299,26 @@ static void slim_reinit_tx_msgq(struct msm_slim_ctrl *dev)
}
}
+static int ngd_check_hw_status(struct msm_slim_ctrl *dev)
+{
+ void __iomem *ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver);
+ u32 laddr = readl_relaxed(ngd + NGD_STATUS);
+ int ret = 0;
+
+ /* Lost logical addr due to noise */
+ if (!(laddr & NGD_LADDR)) {
+ SLIM_WARN(dev, "NGD lost LADDR: status:0x%x\n", laddr);
+ ret = ngd_slim_power_up(dev, false);
+
+ if (ret) {
+ SLIM_WARN(dev, "slim resume ret:%d, state:%d\n",
+ ret, dev->state);
+ ret = -EREMOTEIO;
+ }
+ }
+ return ret;
+}
+
static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
{
DECLARE_COMPLETION_ONSTACK(done);
@@ -351,7 +371,6 @@ static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
/* If txn is tried when controller is down, wait for ADSP to boot */
if (!report_sat) {
-
if (dev->state == MSM_CTRL_DOWN) {
u8 mc = (u8)txn->mc;
int timeout;
@@ -418,6 +437,12 @@ static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
msm_slim_put_ctrl(dev);
return -EREMOTEIO;
}
+ ret = ngd_check_hw_status(dev);
+ if (ret) {
+ mutex_unlock(&dev->tx_lock);
+ msm_slim_put_ctrl(dev);
+ return ret;
+ }
}
if (txn->mt == SLIM_MSG_MT_CORE &&
@@ -737,6 +762,14 @@ static int ngd_bulk_wr(struct slim_controller *ctrl, u8 la, u8 mt, u8 mc,
}
mutex_lock(&dev->tx_lock);
}
+
+ ret = ngd_check_hw_status(dev);
+ if (ret) {
+ mutex_unlock(&dev->tx_lock);
+ msm_slim_put_ctrl(dev);
+ return ret;
+ }
+
if (dev->use_tx_msgqs != MSM_MSGQ_ENABLED) {
SLIM_WARN(dev, "bulk wr not supported");
ret = -EPROTONOSUPPORT;
@@ -1080,6 +1113,7 @@ static void ngd_slim_setup(struct msm_slim_ctrl *dev)
} else {
if (dev->use_rx_msgqs == MSM_MSGQ_DISABLED)
goto setup_tx_msg_path;
+
if ((dev->use_rx_msgqs == MSM_MSGQ_ENABLED) &&
(cfg & NGD_CFG_RX_MSGQ_EN))
goto setup_tx_msg_path;
@@ -1185,7 +1219,7 @@ static void ngd_slim_rx(struct msm_slim_ctrl *dev, u8 *buf)
static int ngd_slim_power_up(struct msm_slim_ctrl *dev, bool mdm_restart)
{
void __iomem *ngd;
- int timeout, ret = 0;
+ int timeout, retries = 0, ret = 0;
enum msm_ctrl_state cur_state = dev->state;
u32 laddr;
u32 rx_msgq;
@@ -1203,16 +1237,24 @@ static int ngd_slim_power_up(struct msm_slim_ctrl *dev, bool mdm_restart)
}
}
+hw_init_retry:
/* No need to vote if contorller is not in low power mode */
if (!mdm_restart &&
(cur_state == MSM_CTRL_DOWN || cur_state == MSM_CTRL_ASLEEP)) {
ret = msm_slim_qmi_power_request(dev, true);
if (ret) {
- SLIM_ERR(dev, "SLIM QMI power request failed:%d\n",
- ret);
+ SLIM_WARN(dev, "SLIM power req failed:%d, retry:%d\n",
+ ret, retries);
+ msm_slim_qmi_power_request(dev, false);
+ if (retries < INIT_MX_RETRIES) {
+ retries++;
+ goto hw_init_retry;
+ }
return ret;
}
}
+ retries = 0;
+
if (!dev->ver) {
dev->ver = readl_relaxed(dev->base);
/* Version info in 16 MSbits */
@@ -1276,6 +1318,7 @@ static int ngd_slim_power_up(struct msm_slim_ctrl *dev, bool mdm_restart)
dev->state = MSM_CTRL_DOWN;
}
+capability_retry:
/*
* ADSP power collapse case (OR SSR), where HW was reset
* BAM programming will happen when capability message is received
@@ -1296,7 +1339,16 @@ static int ngd_slim_power_up(struct msm_slim_ctrl *dev, bool mdm_restart)
timeout = wait_for_completion_timeout(&dev->reconf, HZ);
if (!timeout) {
- SLIM_WARN(dev, "capability exchange timed-out\n");
+ u32 cfg = readl_relaxed(dev->base +
+ NGD_BASE(dev->ctrl.nr, dev->ver));
+ laddr = readl_relaxed(ngd + NGD_STATUS);
+ SLIM_WARN(dev,
+ "slim capability time-out:%d, stat:0x%x,cfg:0x%x\n",
+ retries, laddr, cfg);
+ if (retries < INIT_MX_RETRIES) {
+ retries++;
+ goto capability_retry;
+ }
return -ETIMEDOUT;
}
/* mutliple transactions waiting on slimbus to power up? */
@@ -1388,12 +1440,11 @@ capability_retry:
SLIM_INFO(dev,
"SLIM SAT: capability exchange successful\n");
- if (prev_state >= MSM_CTRL_ASLEEP)
- complete(&dev->reconf);
- else
+ if (prev_state < MSM_CTRL_ASLEEP)
SLIM_WARN(dev,
- "SLIM: unexpected capability, state:%d\n",
+ "capability due to noise, state:%d\n",
prev_state);
+ complete(&dev->reconf);
/* ADSP SSR, send device_up notifications */
if (prev_state == MSM_CTRL_DOWN)
complete(&dev->qmi.slave_notify);
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 7859d1e79e39..fc0a8d23f573 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -67,7 +67,7 @@
#define SLIM_MSG_ASM_FIRST_WORD(l, mt, mc, dt, ad) \
((l) | ((mt) << 5) | ((mc) << 8) | ((dt) << 15) | ((ad) << 16))
-#define INIT_MX_RETRIES 10
+#define INIT_MX_RETRIES 3
#define DEF_RETRY_MS 10
#define MSM_CONCUR_MSG 8
#define SAT_CONCUR_MSG 8
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 45dc329a776e..c45cbfa8a786 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -104,6 +104,16 @@ config MSM_GLINK_SMEM_NATIVE_XPRT
transport to only connecting with entities internal to the
System-on-Chip.
+config MSM_GLINK_SPI_XPRT
+ depends on MSM_GLINK
+ tristate "Generic Link (G-Link) SPI Transport"
+ help
+ G-Link SPI Transport is a Transport plug-in developed over SPI
+ bus. This transport plug-in performs marshaling of G-Link
+ commands & data to the appropriate SPI bus wire format and
+ allows for G-Link communication with remote subsystems that are
+ external to the System-on-Chip.
+
config MSM_SPCOM
depends on MSM_GLINK
bool "Secure Processor Communication over GLINK"
@@ -374,6 +384,15 @@ config QCOM_WATCHDOG_V2
deadlocks. It does not run during the bootup process, so it will
not catch any early lockups.
+config QCOM_IRQ_HELPER
+ bool "QCOM Irq Helper"
+ help
+ This enables the irq helper module. It exposes two APIs
+ int irq_blacklist_on(void) and int irq_blacklist_off(void)
+ to other kernel module.
+ These two apis will be used to control the black list used
+ by the irq balancer.
+
config QCOM_MEMORY_DUMP
bool "Qualcomm Memory Dump Support"
help
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index adbf2dc7a166..269b72c68b68 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_MSM_GLINK) += glink.o glink_debugfs.o glink_ssr.o
obj-$(CONFIG_MSM_GLINK_LOOPBACK_SERVER) += glink_loopback_server.o
obj-$(CONFIG_MSM_GLINK_SMD_XPRT) += glink_smd_xprt.o
obj-$(CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT)+= glink_smem_native_xprt.o
+obj-$(CONFIG_MSM_GLINK_SPI_XPRT) += glink_spi_xprt.o
obj-$(CONFIG_MSM_SMEM_LOGGING) += smem_log.o
obj-$(CONFIG_MSM_SYSMON_GLINK_COMM) += sysmon-glink.o sysmon-qmi.o
obj-$(CONFIG_ARCH_MSM8996) += kryo-l2-accessors.o
@@ -67,6 +68,7 @@ obj-$(CONFIG_QCOM_MEMORY_DUMP_V2) += memory_dump_v2.o
obj-$(CONFIG_QCOM_DCC) += dcc.o
obj-$(CONFIG_QCOM_WATCHDOG_V2) += watchdog_v2.o
obj-$(CONFIG_QCOM_COMMON_LOG) += common_log.o
+obj-$(CONFIG_QCOM_IRQ_HELPER) += irq-helper.o
obj-$(CONFIG_TRACER_PKT) += tracer_pkt.o
obj-$(CONFIG_ICNSS) += icnss.o wlan_firmware_service_v01.o
obj-$(CONFIG_SOC_BUS) += socinfo.o
diff --git a/drivers/soc/qcom/core_ctl_helper.c b/drivers/soc/qcom/core_ctl_helper.c
index 3dde30d29a1c..88201412128e 100644
--- a/drivers/soc/qcom/core_ctl_helper.c
+++ b/drivers/soc/qcom/core_ctl_helper.c
@@ -72,22 +72,28 @@ EXPORT_SYMBOL(core_ctl_find_cpu_device);
int __ref core_ctl_online_core(unsigned int cpu)
{
- int ret;
+ int ret = -EINVAL;
+ struct device *dev = get_cpu_device(cpu);
- lock_device_hotplug();
- ret = device_online(get_cpu_device(cpu));
- unlock_device_hotplug();
+ if (dev) {
+ lock_device_hotplug();
+ ret = device_online(dev);
+ unlock_device_hotplug();
+ }
return ret;
}
EXPORT_SYMBOL(core_ctl_online_core);
int __ref core_ctl_offline_core(unsigned int cpu)
{
- int ret;
+ int ret = -EINVAL;
+ struct device *dev = get_cpu_device(cpu);
- lock_device_hotplug();
- ret = device_offline(get_cpu_device(cpu));
- unlock_device_hotplug();
+ if (dev) {
+ lock_device_hotplug();
+ ret = device_offline(dev);
+ unlock_device_hotplug();
+ }
return ret;
}
EXPORT_SYMBOL(core_ctl_offline_core);
diff --git a/drivers/soc/qcom/gladiator_erp_v2.c b/drivers/soc/qcom/gladiator_erp_v2.c
index 20bb97f1fb16..25c7bd77ae96 100644
--- a/drivers/soc/qcom/gladiator_erp_v2.c
+++ b/drivers/soc/qcom/gladiator_erp_v2.c
@@ -758,7 +758,7 @@ static int gladiator_erp_pm_callback(struct notifier_block *nb,
static int gladiator_erp_v2_probe(struct platform_device *pdev)
{
- int ret;
+ int ret = -1;
struct msm_gladiator_data *msm_gld_data;
msm_gld_data = devm_kzalloc(&pdev->dev,
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index 464fe17158cf..57e58a57fab7 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -372,10 +372,10 @@ static struct channel_ctx *ch_name_to_ch_ctx_create(
const char *name);
static void ch_push_remote_rx_intent(struct channel_ctx *ctx, size_t size,
- uint32_t riid);
+ uint32_t riid, void *cookie);
static int ch_pop_remote_rx_intent(struct channel_ctx *ctx, size_t size,
- uint32_t *riid_ptr, size_t *intent_size);
+ uint32_t *riid_ptr, size_t *intent_size, void **cookie);
static struct glink_core_rx_intent *ch_push_local_rx_intent(
struct channel_ctx *ctx, const void *pkt_priv, size_t size);
@@ -1139,11 +1139,12 @@ bool ch_check_duplicate_riid(struct channel_ctx *ctx, int riid)
* @ctx: Local channel context
* @size: Size of Intent
* @riid_ptr: Pointer to return value of remote intent ID
+ * @cookie: Transport-specific cookie to return
*
* This functions searches for an RX intent that is >= to the requested size.
*/
int ch_pop_remote_rx_intent(struct channel_ctx *ctx, size_t size,
- uint32_t *riid_ptr, size_t *intent_size)
+ uint32_t *riid_ptr, size_t *intent_size, void **cookie)
{
struct glink_core_rx_intent *intent;
struct glink_core_rx_intent *intent_tmp;
@@ -1177,6 +1178,7 @@ int ch_pop_remote_rx_intent(struct channel_ctx *ctx, size_t size,
intent->intent_size);
*riid_ptr = intent->id;
*intent_size = intent->intent_size;
+ *cookie = intent->cookie;
kfree(intent);
spin_unlock_irqrestore(
&ctx->rmt_rx_intent_lst_lock_lhc2, flags);
@@ -1192,11 +1194,12 @@ int ch_pop_remote_rx_intent(struct channel_ctx *ctx, size_t size,
* @ctx: Local channel context
* @size: Size of Intent
* @riid: Remote intent ID
+ * @cookie: Transport-specific cookie to cache
*
* This functions adds a remote RX intent to the remote RX intent list.
*/
void ch_push_remote_rx_intent(struct channel_ctx *ctx, size_t size,
- uint32_t riid)
+ uint32_t riid, void *cookie)
{
struct glink_core_rx_intent *intent;
unsigned long flags;
@@ -1225,6 +1228,7 @@ void ch_push_remote_rx_intent(struct channel_ctx *ctx, size_t size,
}
intent->id = riid;
intent->intent_size = size;
+ intent->cookie = cookie;
spin_lock_irqsave(&ctx->rmt_rx_intent_lst_lock_lhc2, flags);
list_add_tail(&intent->list, &ctx->rmt_rx_intent_list);
@@ -2794,6 +2798,7 @@ static int glink_tx_common(void *handle, void *pkt_priv,
bool is_atomic =
tx_flags & (GLINK_TX_SINGLE_THREADED | GLINK_TX_ATOMIC);
unsigned long flags;
+ void *cookie = NULL;
if (!size)
return -EINVAL;
@@ -2826,7 +2831,7 @@ static int glink_tx_common(void *handle, void *pkt_priv,
}
/* find matching rx intent (first-fit algorithm for now) */
- if (ch_pop_remote_rx_intent(ctx, size, &riid, &intent_size)) {
+ if (ch_pop_remote_rx_intent(ctx, size, &riid, &intent_size, &cookie)) {
if (!(tx_flags & GLINK_TX_REQ_INTENT)) {
/* no rx intent available */
GLINK_ERR_CH(ctx,
@@ -2856,7 +2861,7 @@ static int glink_tx_common(void *handle, void *pkt_priv,
}
while (ch_pop_remote_rx_intent(ctx, size, &riid,
- &intent_size)) {
+ &intent_size, &cookie)) {
rwref_get(&ctx->ch_state_lhb2);
rwref_read_put(&ctx->ch_state_lhb2);
if (is_atomic) {
@@ -2928,7 +2933,7 @@ static int glink_tx_common(void *handle, void *pkt_priv,
is_atomic ? GFP_ATOMIC : GFP_KERNEL);
if (!tx_info) {
GLINK_ERR_CH(ctx, "%s: No memory for allocation\n", __func__);
- ch_push_remote_rx_intent(ctx, intent_size, riid);
+ ch_push_remote_rx_intent(ctx, intent_size, riid, cookie);
rwref_read_put(&ctx->ch_state_lhb2);
return -ENOMEM;
}
@@ -2946,6 +2951,7 @@ static int glink_tx_common(void *handle, void *pkt_priv,
tx_info->vprovider = vbuf_provider;
tx_info->pprovider = pbuf_provider;
tx_info->intent_size = intent_size;
+ tx_info->cookie = cookie;
/* schedule packet for transmit */
if ((tx_flags & GLINK_TX_SINGLE_THREADED) &&
@@ -3577,6 +3583,10 @@ int glink_xprt_name_to_id(const char *name, uint16_t *id)
*id = SMEM_XPRT_ID;
return 0;
}
+ if (!strcmp(name, "spi")) {
+ *id = SPIV2_XPRT_ID;
+ return 0;
+ }
if (!strcmp(name, "smd_trans")) {
*id = SMD_TRANS_XPRT_ID;
return 0;
@@ -4844,7 +4854,35 @@ static void glink_core_remote_rx_intent_put(struct glink_transport_if *if_ptr,
return;
}
- ch_push_remote_rx_intent(ctx, size, riid);
+ ch_push_remote_rx_intent(ctx, size, riid, NULL);
+ rwref_put(&ctx->ch_state_lhb2);
+}
+
+/**
+ * glink_core_remote_rx_intent_put_cookie() - Receive remove intent
+ *
+ * @if_ptr: Pointer to transport instance
+ * @rcid: Remote Channel ID
+ * @riid: Remote Intent ID
+ * @size: Size of the remote intent ID
+ * @cookie: Transport-specific cookie to cache
+ */
+static void glink_core_remote_rx_intent_put_cookie(
+ struct glink_transport_if *if_ptr,
+ uint32_t rcid, uint32_t riid, size_t size, void *cookie)
+{
+ struct channel_ctx *ctx;
+
+ ctx = xprt_rcid_to_ch_ctx_get(if_ptr->glink_core_priv, rcid);
+ if (!ctx) {
+ /* unknown rcid received - this shouldn't happen */
+ GLINK_ERR_XPRT(if_ptr->glink_core_priv,
+ "%s: invalid rcid received %u\n", __func__,
+ (unsigned)rcid);
+ return;
+ }
+
+ ch_push_remote_rx_intent(ctx, size, riid, cookie);
rwref_put(&ctx->ch_state_lhb2);
}
@@ -5050,6 +5088,7 @@ void glink_core_rx_cmd_tx_done(struct glink_transport_if *if_ptr,
struct glink_core_tx_pkt *tx_pkt;
unsigned long flags;
size_t intent_size;
+ void *cookie;
ctx = xprt_rcid_to_ch_ctx_get(if_ptr->glink_core_priv, rcid);
if (!ctx) {
@@ -5082,11 +5121,12 @@ void glink_core_rx_cmd_tx_done(struct glink_transport_if *if_ptr,
ctx->notify_tx_done(ctx, ctx->user_priv, tx_pkt->pkt_priv,
tx_pkt->data ? tx_pkt->data : tx_pkt->iovec);
intent_size = tx_pkt->intent_size;
+ cookie = tx_pkt->cookie;
ch_remove_tx_pending_remote_done(ctx, tx_pkt);
spin_unlock_irqrestore(&ctx->tx_lists_lock_lhc3, flags);
if (reuse)
- ch_push_remote_rx_intent(ctx, intent_size, riid);
+ ch_push_remote_rx_intent(ctx, intent_size, riid, cookie);
rwref_put(&ctx->ch_state_lhb2);
}
@@ -5525,6 +5565,8 @@ static struct glink_core_if core_impl = {
.rx_get_pkt_ctx = glink_core_rx_get_pkt_ctx,
.rx_put_pkt_ctx = glink_core_rx_put_pkt_ctx,
.rx_cmd_remote_rx_intent_put = glink_core_remote_rx_intent_put,
+ .rx_cmd_remote_rx_intent_put_cookie =
+ glink_core_remote_rx_intent_put_cookie,
.rx_cmd_remote_rx_intent_req = glink_core_rx_cmd_remote_rx_intent_req,
.rx_cmd_rx_intent_req_ack = glink_core_rx_cmd_rx_intent_req_ack,
.rx_cmd_tx_done = glink_core_rx_cmd_tx_done,
diff --git a/drivers/soc/qcom/glink_core_if.h b/drivers/soc/qcom/glink_core_if.h
index 93c59d9c4aa1..14113305a50e 100644
--- a/drivers/soc/qcom/glink_core_if.h
+++ b/drivers/soc/qcom/glink_core_if.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -64,6 +64,7 @@ struct glink_core_version {
* iovec: Pointer to vector buffer if the transport passes a vector buffer
* vprovider: Virtual address-space buffer provider for a vector buffer
* pprovider: Physical address-space buffer provider for a vector buffer
+ * cookie: Private transport specific cookie
* pkt_priv: G-Link core owned packet-private data
* list: G-Link core owned list node
* bounce_buf: Pointer to the temporary/internal bounce buffer
@@ -78,6 +79,7 @@ struct glink_core_rx_intent {
void *iovec;
void * (*vprovider)(void *iovec, size_t offset, size_t *size);
void * (*pprovider)(void *iovec, size_t offset, size_t *size);
+ void *cookie;
/* G-Link-Core-owned elements - please ignore */
struct list_head list;
@@ -151,6 +153,9 @@ struct glink_core_if {
struct glink_core_rx_intent *intent_ptr, bool complete);
void (*rx_cmd_remote_rx_intent_put)(struct glink_transport_if *if_ptr,
uint32_t rcid, uint32_t riid, size_t size);
+ void (*rx_cmd_remote_rx_intent_put_cookie)(
+ struct glink_transport_if *if_ptr, uint32_t rcid,
+ uint32_t riid, size_t size, void *cookie);
void (*rx_cmd_tx_done)(struct glink_transport_if *if_ptr, uint32_t rcid,
uint32_t riid, bool reuse);
void (*rx_cmd_remote_rx_intent_req)(struct glink_transport_if *if_ptr,
diff --git a/drivers/soc/qcom/glink_spi_xprt.c b/drivers/soc/qcom/glink_spi_xprt.c
new file mode 100644
index 000000000000..6c91ac54821d
--- /dev/null
+++ b/drivers/soc/qcom/glink_spi_xprt.c
@@ -0,0 +1,2192 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/spinlock.h>
+#include <linux/srcu.h>
+#include <linux/wait.h>
+#include <linux/component.h>
+#include <soc/qcom/tracer_pkt.h>
+#include <sound/wcd-dsp-mgr.h>
+#include <sound/wcd-spi.h>
+#include "glink_core_if.h"
+#include "glink_private.h"
+#include "glink_xprt_if.h"
+
+#define XPRT_NAME "spi"
+#define FIFO_ALIGNMENT 16
+#define FIFO_FULL_RESERVE 8
+#define TX_BLOCKED_CMD_RESERVE 16
+#define TRACER_PKT_FEATURE BIT(2)
+#define DEFAULT_FIFO_SIZE 1024
+#define SHORT_PKT_SIZE 16
+#define XPRT_ALIGNMENT 4
+
+#define MAX_INACTIVE_CYCLES 50
+#define POLL_INTERVAL_US 500
+
+#define ACTIVE_TX BIT(0)
+#define ACTIVE_RX BIT(1)
+
+#define ID_MASK 0xFFFFFF
+/**
+ * enum command_types - definition of the types of commands sent/received
+ * @VERSION_CMD: Version and feature set supported
+ * @VERSION_ACK_CMD: Response for @VERSION_CMD
+ * @OPEN_CMD: Open a channel
+ * @CLOSE_CMD: Close a channel
+ * @OPEN_ACK_CMD: Response to @OPEN_CMD
+ * @CLOSE_ACK_CMD: Response for @CLOSE_CMD
+ * @RX_INTENT_CMD: RX intent for a channel is queued
+ * @RX_DONE_CMD: Use of RX intent for a channel is complete
+ * @RX_DONE_W_REUSE_CMD: Same as @RX_DONE but also reuse the used intent
+ * @RX_INTENT_REQ_CMD: Request to have RX intent queued
+ * @RX_INTENT_REQ_ACK_CMD: Response for @RX_INTENT_REQ_CMD
+ * @TX_DATA_CMD: Start of a data transfer
+ * @TX_DATA_CONT_CMD: Continuation or end of a data transfer
+ * @READ_NOTIF_CMD: Request for a notification when this cmd is read
+ * @SIGNALS_CMD: Sideband signals
+ * @TRACER_PKT_CMD: Start of a Tracer Packet Command
+ * @TRACER_PKT_CONT_CMD: Continuation or end of a Tracer Packet Command
+ * @TX_SHORT_DATA_CMD: Transmit short packets
+ */
+enum command_types {
+ VERSION_CMD,
+ VERSION_ACK_CMD,
+ OPEN_CMD,
+ CLOSE_CMD,
+ OPEN_ACK_CMD,
+ CLOSE_ACK_CMD,
+ RX_INTENT_CMD,
+ RX_DONE_CMD,
+ RX_DONE_W_REUSE_CMD,
+ RX_INTENT_REQ_CMD,
+ RX_INTENT_REQ_ACK_CMD,
+ TX_DATA_CMD,
+ TX_DATA_CONT_CMD,
+ READ_NOTIF_CMD,
+ SIGNALS_CMD,
+ TRACER_PKT_CMD,
+ TRACER_PKT_CONT_CMD,
+ TX_SHORT_DATA_CMD,
+};
+
+/**
+ * struct glink_cmpnt - Component to cache WDSP component and its operations
+ * @master_dev: Device structure corresponding to WDSP device.
+ * @master_ops: Operations supported by the WDSP device.
+ */
+struct glink_cmpnt {
+ struct device *master_dev;
+ struct wdsp_mgr_ops *master_ops;
+};
+
+/**
+ * struct edge_info - local information for managing a single complete edge
+ * @xprt_if: The transport interface registered with the
+ * glink core associated with this edge.
+ * @xprt_cfg: The transport configuration for the glink core
+ * assocaited with this edge.
+ * @subsys_name: Name of the remote subsystem in the edge.
+ * @spi_dev: Pointer to the connectingSPI Device.
+ * @fifo_size: Size of the FIFO at the remote end.
+ * @tx_fifo_start: Base Address of the TX FIFO.
+ * @tx_fifo_end: End Address of the TX FIFO.
+ * @rx_fifo_start: Base Address of the RX FIFO.
+ * @rx_fifo_end: End Address of the RX FIFO.
+ * @tx_fifo_read_reg_addr: Address of the TX FIFO Read Index Register.
+ * @tx_fifo_write_reg_addr: Address of the TX FIFO Write Index Register.
+ * @rx_fifo_read_reg_addr: Address of the RX FIFO Read Index Register.
+ * @rx_fifo_write_reg_addr: Address of the RX FIFO Write Index Register.
+ * @kwork: Work to be executed when receiving data.
+ * @kworker: Handle to the entity processing @kwork.
+ * @task: Handle to the task context that runs @kworker.
+ * @use_ref: Active users of this transport grab a
+ * reference. Used for SSR synchronization.
+ * @in_ssr: Signals if this transport is in ssr.
+ * @write_lock: Lock to serialize write/tx operation.
+ * @tx_blocked_queue: Queue of entities waiting for the remote side to
+ * signal the resumption of TX.
+ * @tx_resume_needed: A tx resume signal needs to be sent to the glink
+ * core.
+ * @tx_blocked_signal_sent: Flag to indicate the flush signal has already
+ * been sent, and a response is pending from the
+ * remote side. Protected by @write_lock.
+ * @num_pw_states: Size of @ramp_time_us.
+ * @ramp_time_us: Array of ramp times in microseconds where array
+ * index position represents a power state.
+ * @activity_flag: Flag indicating active TX and RX.
+ * @activity_lock: Lock to synchronize access to activity flag.
+ * @cmpnt: Component to interface with the remote device.
+ */
+struct edge_info {
+ struct list_head list;
+ struct glink_transport_if xprt_if;
+ struct glink_core_transport_cfg xprt_cfg;
+ char subsys_name[GLINK_NAME_SIZE];
+ struct spi_device *spi_dev;
+
+ uint32_t fifo_size;
+ uint32_t tx_fifo_start;
+ uint32_t tx_fifo_end;
+ uint32_t rx_fifo_start;
+ uint32_t rx_fifo_end;
+ unsigned int tx_fifo_read_reg_addr;
+ unsigned int tx_fifo_write_reg_addr;
+ unsigned int rx_fifo_read_reg_addr;
+ unsigned int rx_fifo_write_reg_addr;
+
+ struct kthread_work kwork;
+ struct kthread_worker kworker;
+ struct task_struct *task;
+ struct srcu_struct use_ref;
+ bool in_ssr;
+ struct mutex write_lock;
+ wait_queue_head_t tx_blocked_queue;
+ bool tx_resume_needed;
+ bool tx_blocked_signal_sent;
+
+ uint32_t num_pw_states;
+ unsigned long *ramp_time_us;
+
+ uint32_t activity_flag;
+ spinlock_t activity_lock;
+
+ struct glink_cmpnt cmpnt;
+};
+
+static uint32_t negotiate_features_v1(struct glink_transport_if *if_ptr,
+ const struct glink_core_version *version,
+ uint32_t features);
+static DEFINE_SPINLOCK(edge_infos_lock);
+static LIST_HEAD(edge_infos);
+static struct glink_core_version versions[] = {
+ {1, TRACER_PKT_FEATURE, negotiate_features_v1},
+};
+
+/**
+ * negotiate_features_v1() - determine what features of a version can be used
+ * @if_ptr: The transport for which features are negotiated for.
+ * @version: The version negotiated.
+ * @features: The set of requested features.
+ *
+ * Return: What set of the requested features can be supported.
+ */
+static uint32_t negotiate_features_v1(struct glink_transport_if *if_ptr,
+ const struct glink_core_version *version,
+ uint32_t features)
+{
+ return features & version->features;
+}
+
+/**
+ * wdsp_suspend() - Vote for the WDSP device suspend
+ * @cmpnt: Component to identify the WDSP device.
+ *
+ * Return: 0 on success, standard Linux error codes on failure.
+ */
+static int wdsp_suspend(struct glink_cmpnt *cmpnt)
+{
+ if (cmpnt && cmpnt->master_dev &&
+ cmpnt->master_ops && cmpnt->master_ops->suspend)
+ return cmpnt->master_ops->suspend(cmpnt->master_dev);
+ else
+ return -EINVAL;
+}
+
+/**
+ * wdsp_resume() - Vote for the WDSP device resume
+ * @cmpnt: Component to identify the WDSP device.
+ *
+ * Return: 0 on success, standard Linux error codes on failure.
+ */
+static int wdsp_resume(struct glink_cmpnt *cmpnt)
+{
+ if (cmpnt && cmpnt->master_dev &&
+ cmpnt->master_ops && cmpnt->master_ops->resume)
+ return cmpnt->master_ops->resume(cmpnt->master_dev);
+ else
+ return -EINVAL;
+}
+
+/**
+ * glink_spi_xprt_set_poll_mode() - Set the transport to polling mode
+ * @einfo: Edge information corresponding to the transport.
+ *
+ * This helper function indicates the start of RX polling. This will
+ * prevent the system from suspending and keeps polling for RX for a
+ * pre-defined duration.
+ */
+static void glink_spi_xprt_set_poll_mode(struct edge_info *einfo)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&einfo->activity_lock, flags);
+ einfo->activity_flag |= ACTIVE_RX;
+ spin_unlock_irqrestore(&einfo->activity_lock, flags);
+ if (!strcmp(einfo->xprt_cfg.edge, "wdsp"))
+ wdsp_resume(&einfo->cmpnt);
+}
+
+/**
+ * glink_spi_xprt_set_irq_mode() - Set the transport to IRQ mode
+ * @einfo: Edge information corresponding to the transport.
+ *
+ * This helper indicates the end of RX polling. This will allow the
+ * system to suspend and new RX data can be handled only through an IRQ.
+ */
+static void glink_spi_xprt_set_irq_mode(struct edge_info *einfo)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&einfo->activity_lock, flags);
+ einfo->activity_flag &= ~ACTIVE_RX;
+ spin_unlock_irqrestore(&einfo->activity_lock, flags);
+}
+
+/**
+ * glink_spi_xprt_rx_data() - Receive data over SPI bus
+ * @einfo: Edge from which the data has to be received.
+ * @src: Source Address of the RX data.
+ * @dst: Address of the destination RX buffer.
+ * @size: Size of the RX data.
+ *
+ * This function is used to receive data or command as a byte stream from
+ * the remote subsystem over the SPI bus.
+ *
+ * Return: 0 on success, standard Linux error codes on failure.
+ */
+static int glink_spi_xprt_rx_data(struct edge_info *einfo, void *src,
+ void *dst, uint32_t size)
+{
+ struct wcd_spi_msg spi_msg;
+
+ memset(&spi_msg, 0, sizeof(spi_msg));
+ spi_msg.data = dst;
+ spi_msg.remote_addr = (uint32_t)(size_t)src;
+ spi_msg.len = (size_t)size;
+ return wcd_spi_data_read(einfo->spi_dev, &spi_msg);
+}
+
+/**
+ * glink_spi_xprt_tx_data() - Transmit data over SPI bus
+ * @einfo: Edge from which the data has to be received.
+ * @src: Address of the TX buffer.
+ * @dst: Destination Address of the TX Date.
+ * @size: Size of the TX data.
+ *
+ * This function is used to transmit data or command as a byte stream to
+ * the remote subsystem over the SPI bus.
+ *
+ * Return: 0 on success, standard Linux error codes on failure.
+ */
+static int glink_spi_xprt_tx_data(struct edge_info *einfo, void *src,
+ void *dst, uint32_t size)
+{
+ struct wcd_spi_msg spi_msg;
+
+ memset(&spi_msg, 0, sizeof(spi_msg));
+ spi_msg.data = src;
+ spi_msg.remote_addr = (uint32_t)(size_t)dst;
+ spi_msg.len = (size_t)size;
+ return wcd_spi_data_write(einfo->spi_dev, &spi_msg);
+}
+
+/**
+ * glink_spi_xprt_reg_read() - Read the TX/RX FIFO Read/Write Index registers
+ * @einfo: Edge from which the registers have to be read.
+ * @reg_addr: Address of the register to be read.
+ * @data: Buffer into which the register data has to be read.
+ *
+ * Return: 0 on success, standard Linux error codes on failure.
+ */
+static int glink_spi_xprt_reg_read(struct edge_info *einfo, u32 reg_addr,
+ uint32_t *data)
+{
+ int rc;
+
+ rc = glink_spi_xprt_rx_data(einfo, (void *)(unsigned long)reg_addr,
+ data, sizeof(*data));
+ if (!rc)
+ *data = *data & ID_MASK;
+ return rc;
+}
+
+/**
+ * glink_spi_xprt_reg_write() - Write the TX/RX FIFO Read/Write Index registers
+ * @einfo: Edge to which the registers have to be written.
+ * @reg_addr: Address of the registers to be written.
+ * @data: Data to be written to the registers.
+ *
+ * Return: 0 on success, standard Linux error codes on failure.
+ */
+static int glink_spi_xprt_reg_write(struct edge_info *einfo, u32 reg_addr,
+ uint32_t data)
+{
+ return glink_spi_xprt_tx_data(einfo, &data,
+ (void *)(unsigned long)reg_addr, sizeof(data));
+}
+
+/**
+ * glink_spi_xprt_write_avail() - Available Write Space in the remote side
+ * @einfo: Edge information corresponding to the remote side.
+ *
+ * This function reads the TX FIFO Read & Write Index registers from the
+ * remote subsystem and calculate the available write space.
+ *
+ * Return: 0 on error, available write space on success.
+ */
+static int glink_spi_xprt_write_avail(struct edge_info *einfo)
+{
+ uint32_t read_id;
+ uint32_t write_id;
+ int write_avail;
+ int ret;
+
+ ret = glink_spi_xprt_reg_read(einfo, einfo->tx_fifo_read_reg_addr,
+ &read_id);
+ if (ret < 0) {
+ pr_err("%s: Error %d reading %s tx_fifo_read_reg_addr %d\n",
+ __func__, ret, einfo->xprt_cfg.edge,
+ einfo->tx_fifo_read_reg_addr);
+ return 0;
+ }
+
+ ret = glink_spi_xprt_reg_read(einfo, einfo->tx_fifo_write_reg_addr,
+ &write_id);
+ if (ret < 0) {
+ pr_err("%s: Error %d reading %s tx_fifo_write_reg_addr %d\n",
+ __func__, ret, einfo->xprt_cfg.edge,
+ einfo->tx_fifo_write_reg_addr);
+ return 0;
+ }
+
+ if (!read_id || !write_id)
+ return 0;
+
+ if (unlikely(!einfo->tx_fifo_start))
+ einfo->tx_fifo_start = write_id;
+
+ if (read_id > write_id)
+ write_avail = read_id - write_id;
+ else
+ write_avail = einfo->fifo_size - (write_id - read_id);
+
+ if (write_avail < FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE)
+ write_avail = 0;
+ else
+ write_avail -= FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE;
+
+ return write_avail;
+}
+
+/**
+ * glink_spi_xprt_read_avail() - Available Read Data from the remote side
+ * @einfo: Edge information corresponding to the remote side.
+ *
+ * This function reads the RX FIFO Read & Write Index registers from the
+ * remote subsystem and calculate the available read data size.
+ *
+ * Return: 0 on error, available read data on success.
+ */
+static int glink_spi_xprt_read_avail(struct edge_info *einfo)
+{
+ uint32_t read_id;
+ uint32_t write_id;
+ int read_avail;
+ int ret;
+
+ ret = glink_spi_xprt_reg_read(einfo, einfo->rx_fifo_read_reg_addr,
+ &read_id);
+ if (ret < 0) {
+ pr_err("%s: Error %d reading %s rx_fifo_read_reg_addr %d\n",
+ __func__, ret, einfo->xprt_cfg.edge,
+ einfo->rx_fifo_read_reg_addr);
+ return 0;
+ }
+
+ ret = glink_spi_xprt_reg_read(einfo, einfo->rx_fifo_write_reg_addr,
+ &write_id);
+ if (ret < 0) {
+ pr_err("%s: Error %d reading %s rx_fifo_write_reg_addr %d\n",
+ __func__, ret, einfo->xprt_cfg.edge,
+ einfo->rx_fifo_write_reg_addr);
+ return 0;
+ }
+
+ if (!read_id || !write_id)
+ return 0;
+
+ if (unlikely(!einfo->rx_fifo_start))
+ einfo->rx_fifo_start = read_id;
+
+ if (read_id <= write_id)
+ read_avail = write_id - read_id;
+ else
+ read_avail = einfo->fifo_size - (read_id - write_id);
+ return read_avail;
+}
+
+/**
+ * glink_spi_xprt_rx_cmd() - Receive G-Link commands
+ * @einfo: Edge information corresponding to the remote side.
+ * @dst: Destination buffer where the commands have to be read into.
+ * @size: Size of the data to be read.
+ *
+ * This function is used to receive the commands from the RX FIFO. This
+ * function updates the RX FIFO Read Index after reading the data.
+ *
+ * Return: 0 on success, standard Linux error codes on error.
+ */
+static int glink_spi_xprt_rx_cmd(struct edge_info *einfo, void *dst,
+ uint32_t size)
+{
+ uint32_t read_id;
+ uint32_t size_to_read = size;
+ uint32_t offset = 0;
+ int ret;
+
+ ret = glink_spi_xprt_reg_read(einfo, einfo->rx_fifo_read_reg_addr,
+ &read_id);
+ if (ret < 0) {
+ pr_err("%s: Error %d reading %s rx_fifo_read_reg_addr %d\n",
+ __func__, ret, einfo->xprt_cfg.edge,
+ einfo->rx_fifo_read_reg_addr);
+ return ret;
+ }
+
+ do {
+ if ((read_id + size_to_read) >=
+ (einfo->rx_fifo_start + einfo->fifo_size))
+ size_to_read = einfo->rx_fifo_start + einfo->fifo_size
+ - read_id;
+ ret = glink_spi_xprt_rx_data(einfo, (void *)(size_t)read_id,
+ dst + offset, size_to_read);
+ if (ret < 0) {
+ pr_err("%s: Error %d reading data\n", __func__, ret);
+ return ret;
+ }
+ read_id += size_to_read;
+ offset += size_to_read;
+ if (read_id >= (einfo->rx_fifo_start + einfo->fifo_size))
+ read_id = einfo->rx_fifo_start;
+ size_to_read = size - offset;
+ } while (size_to_read);
+
+ ret = glink_spi_xprt_reg_write(einfo, einfo->rx_fifo_read_reg_addr,
+ read_id);
+ if (ret < 0)
+ pr_err("%s: Error %d writing %s rx_fifo_read_reg_addr %d\n",
+ __func__, ret, einfo->xprt_cfg.edge,
+ einfo->rx_fifo_read_reg_addr);
+ return ret;
+}
+
+/**
+ * glink_spi_xprt_tx_cmd_safe() - Transmit G-Link commands
+ * @einfo: Edge information corresponding to the remote subsystem.
+ * @src: Source buffer containing the G-Link command.
+ * @size: Size of the command to transmit.
+ *
+ * This function is used to transmit the G-Link commands. This function
+ * must be called with einfo->write_lock locked.
+ *
+ * Return: 0 on success, standard Linux error codes on error.
+ */
+static int glink_spi_xprt_tx_cmd_safe(struct edge_info *einfo, void *src,
+ uint32_t size)
+{
+ uint32_t write_id;
+ uint32_t size_to_write = size;
+ uint32_t offset = 0;
+ int ret;
+
+ ret = glink_spi_xprt_reg_read(einfo, einfo->tx_fifo_write_reg_addr,
+ &write_id);
+ if (ret < 0) {
+ pr_err("%s: Error %d reading %s tx_fifo_write_reg_addr %d\n",
+ __func__, ret, einfo->xprt_cfg.edge,
+ einfo->tx_fifo_write_reg_addr);
+ return ret;
+ }
+
+ do {
+ if ((write_id + size_to_write) >=
+ (einfo->tx_fifo_start + einfo->fifo_size))
+ size_to_write = einfo->tx_fifo_start + einfo->fifo_size
+ - write_id;
+ ret = glink_spi_xprt_tx_data(einfo, src + offset,
+ (void *)(size_t)write_id, size_to_write);
+ if (ret < 0) {
+ pr_err("%s: Error %d writing data\n", __func__, ret);
+ return ret;
+ }
+ write_id += size_to_write;
+ offset += size_to_write;
+ if (write_id >= (einfo->tx_fifo_start + einfo->fifo_size))
+ write_id = einfo->tx_fifo_start;
+ size_to_write = size - offset;
+ } while (size_to_write);
+
+ ret = glink_spi_xprt_reg_write(einfo, einfo->tx_fifo_write_reg_addr,
+ write_id);
+ if (ret < 0)
+ pr_err("%s: Error %d writing %s tx_fifo_write_reg_addr %d\n",
+ __func__, ret, einfo->xprt_cfg.edge,
+ einfo->tx_fifo_write_reg_addr);
+ return ret;
+}
+
+/**
+ * send_tx_blocked_signal() - Send flow control request message
+ * @einfo: Edge information corresponding to the remote subsystem.
+ *
+ * This function is used to send a message to the remote subsystem indicating
+ * that the local subsystem is waiting for the write space. The remote
+ * subsystem on receiving this message will send a resume tx message.
+ */
+static void send_tx_blocked_signal(struct edge_info *einfo)
+{
+ struct read_notif_request {
+ uint16_t cmd;
+ uint16_t reserved;
+ uint32_t reserved2;
+ uint64_t reserved3;
+ };
+ struct read_notif_request read_notif_req = {0};
+
+ read_notif_req.cmd = READ_NOTIF_CMD;
+
+ if (!einfo->tx_blocked_signal_sent) {
+ einfo->tx_blocked_signal_sent = true;
+ glink_spi_xprt_tx_cmd_safe(einfo, &read_notif_req,
+ sizeof(read_notif_req));
+ }
+}
+
+/**
+ * glink_spi_xprt_tx_cmd() - Transmit G-Link commands
+ * @einfo: Edge information corresponding to the remote subsystem.
+ * @src: Source buffer containing the G-Link command.
+ * @size: Size of the command to transmit.
+ *
+ * This function is used to transmit the G-Link commands. This function
+ * might sleep if the space is not available to transmit the command.
+ *
+ * Return: 0 on success, standard Linux error codes on error.
+ */
+static int glink_spi_xprt_tx_cmd(struct edge_info *einfo, void *src,
+ uint32_t size)
+{
+ int ret;
+ DEFINE_WAIT(wait);
+
+ mutex_lock(&einfo->write_lock);
+ while (glink_spi_xprt_write_avail(einfo) < size) {
+ send_tx_blocked_signal(einfo);
+ prepare_to_wait(&einfo->tx_blocked_queue, &wait,
+ TASK_UNINTERRUPTIBLE);
+ if (glink_spi_xprt_write_avail(einfo) < size &&
+ !einfo->in_ssr) {
+ mutex_unlock(&einfo->write_lock);
+ schedule();
+ mutex_lock(&einfo->write_lock);
+ }
+ finish_wait(&einfo->tx_blocked_queue, &wait);
+ if (einfo->in_ssr) {
+ mutex_unlock(&einfo->write_lock);
+ return -EFAULT;
+ }
+ }
+ ret = glink_spi_xprt_tx_cmd_safe(einfo, src, size);
+ mutex_unlock(&einfo->write_lock);
+ return ret;
+}
+
+/**
+ * process_rx_data() - process received data from an edge
+ * @einfo: The edge the data is received on.
+ * @cmd_id: ID to specify the type of data.
+ * @rcid: The remote channel id associated with the data.
+ * @intend_id: The intent the data should be put in.
+ * @src: Address of the source buffer from which the data
+ * is read.
+ * @frag_size: Size of the data fragment to read.
+ * @size_remaining: Size of data left to be read in this packet.
+ */
+static void process_rx_data(struct edge_info *einfo, uint16_t cmd_id,
+ uint32_t rcid, uint32_t intent_id, void *src,
+ uint32_t frag_size, uint32_t size_remaining)
+{
+ struct glink_core_rx_intent *intent;
+ int rc = 0;
+
+ intent = einfo->xprt_if.glink_core_if_ptr->rx_get_pkt_ctx(
+ &einfo->xprt_if, rcid, intent_id);
+ if (intent == NULL) {
+ GLINK_ERR("%s: no intent for ch %d liid %d\n", __func__, rcid,
+ intent_id);
+ return;
+ } else if (intent->data == NULL) {
+ GLINK_ERR("%s: intent for ch %d liid %d has no data buff\n",
+ __func__, rcid, intent_id);
+ return;
+ } else if (intent->intent_size - intent->write_offset < frag_size ||
+ intent->write_offset + size_remaining > intent->intent_size) {
+ GLINK_ERR("%s: rx data size:%d and remaining:%d %s %d %s:%d\n",
+ __func__, frag_size, size_remaining,
+ "will overflow ch", rcid, "intent", intent_id);
+ return;
+ }
+
+ if (cmd_id == TX_SHORT_DATA_CMD)
+ memcpy(intent->data + intent->write_offset, src, frag_size);
+ else
+ rc = glink_spi_xprt_rx_data(einfo, src,
+ intent->data + intent->write_offset, frag_size);
+ if (rc < 0) {
+ GLINK_ERR("%s: Error %d receiving data %d:%d:%d:%d\n",
+ __func__, rc, rcid, intent_id, frag_size,
+ size_remaining);
+ size_remaining += frag_size;
+ } else {
+ intent->write_offset += frag_size;
+ intent->pkt_size += frag_size;
+
+ if (unlikely((cmd_id == TRACER_PKT_CMD ||
+ cmd_id == TRACER_PKT_CONT_CMD) && !size_remaining)) {
+ tracer_pkt_log_event(intent->data, GLINK_XPRT_RX);
+ intent->tracer_pkt = true;
+ }
+ }
+ einfo->xprt_if.glink_core_if_ptr->rx_put_pkt_ctx(&einfo->xprt_if,
+ rcid, intent, size_remaining ? false : true);
+}
+
+/**
+ * process_rx_cmd() - Process incoming G-Link commands
+ * @einfo: Edge information corresponding to the remote subsystem.
+ * @rx_data: Buffer which contains the G-Link commands to be processed.
+ * @rx_size: Size of the buffer containing the series of G-Link commands.
+ *
+ * This function is used to parse and process a series of G-Link commands
+ * received in a buffer.
+ */
+static void process_rx_cmd(struct edge_info *einfo,
+ void *rx_data, int rx_size)
+{
+ struct command {
+ uint16_t id;
+ uint16_t param1;
+ uint32_t param2;
+ uint32_t param3;
+ uint32_t param4;
+ };
+ struct intent_desc {
+ uint32_t size;
+ uint32_t id;
+ uint64_t addr;
+ };
+ struct rx_desc {
+ uint32_t size;
+ uint32_t size_left;
+ uint64_t addr;
+ };
+ struct rx_short_data_desc {
+ unsigned char data[SHORT_PKT_SIZE];
+ };
+ struct command *cmd;
+ struct intent_desc *intents;
+ struct rx_desc *rx_descp;
+ struct rx_short_data_desc *rx_sd_descp;
+ int offset = 0;
+ int rcu_id;
+ uint16_t rcid;
+ uint16_t name_len;
+ uint16_t prio;
+ char *name;
+ bool granted;
+ int i;
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return;
+ }
+
+ while (offset < rx_size) {
+ cmd = (struct command *)(rx_data + offset);
+ offset += sizeof(*cmd);
+ switch (cmd->id) {
+ case VERSION_CMD:
+ if (cmd->param3)
+ einfo->fifo_size = cmd->param3;
+ einfo->xprt_if.glink_core_if_ptr->rx_cmd_version(
+ &einfo->xprt_if, cmd->param1, cmd->param2);
+ break;
+
+ case VERSION_ACK_CMD:
+ einfo->xprt_if.glink_core_if_ptr->rx_cmd_version_ack(
+ &einfo->xprt_if, cmd->param1, cmd->param2);
+ break;
+
+ case OPEN_CMD:
+ rcid = cmd->param1;
+ name_len = (uint16_t)(cmd->param2 & 0xFFFF);
+ prio = (uint16_t)((cmd->param2 & 0xFFFF0000) >> 16);
+ name = (char *)(rx_data + offset);
+ offset += ALIGN(name_len, FIFO_ALIGNMENT);
+ einfo->xprt_if.glink_core_if_ptr->rx_cmd_ch_remote_open(
+ &einfo->xprt_if, rcid, name, prio);
+ break;
+
+ case CLOSE_CMD:
+ einfo->xprt_if.glink_core_if_ptr->
+ rx_cmd_ch_remote_close(
+ &einfo->xprt_if, cmd->param1);
+ break;
+
+ case OPEN_ACK_CMD:
+ prio = (uint16_t)(cmd->param2 & 0xFFFF);
+ einfo->xprt_if.glink_core_if_ptr->rx_cmd_ch_open_ack(
+ &einfo->xprt_if, cmd->param1, prio);
+ break;
+
+ case CLOSE_ACK_CMD:
+ einfo->xprt_if.glink_core_if_ptr->rx_cmd_ch_close_ack(
+ &einfo->xprt_if, cmd->param1);
+ break;
+
+ case RX_INTENT_CMD:
+ for (i = 0; i < cmd->param2; i++) {
+ intents = (struct intent_desc *)
+ (rx_data + offset);
+ offset += sizeof(*intents);
+ einfo->xprt_if.glink_core_if_ptr->
+ rx_cmd_remote_rx_intent_put_cookie(
+ &einfo->xprt_if, cmd->param1,
+ intents->id, intents->size,
+ (void *)(intents->addr));
+ }
+ break;
+
+ case RX_DONE_CMD:
+ einfo->xprt_if.glink_core_if_ptr->rx_cmd_tx_done(
+ &einfo->xprt_if, cmd->param1, cmd->param2,
+ false);
+ break;
+
+ case RX_INTENT_REQ_CMD:
+ einfo->xprt_if.glink_core_if_ptr->
+ rx_cmd_remote_rx_intent_req(
+ &einfo->xprt_if, cmd->param1,
+ cmd->param2);
+ break;
+
+ case RX_INTENT_REQ_ACK_CMD:
+ granted = cmd->param2 == 1 ? true : false;
+ einfo->xprt_if.glink_core_if_ptr->
+ rx_cmd_rx_intent_req_ack(&einfo->xprt_if,
+ cmd->param1, granted);
+ break;
+
+ case TX_DATA_CMD:
+ case TX_DATA_CONT_CMD:
+ case TRACER_PKT_CMD:
+ case TRACER_PKT_CONT_CMD:
+ rx_descp = (struct rx_desc *)(rx_data + offset);
+ offset += sizeof(*rx_descp);
+ process_rx_data(einfo, cmd->id, cmd->param1,
+ cmd->param2, (void *)rx_descp->addr,
+ rx_descp->size, rx_descp->size_left);
+ break;
+
+ case TX_SHORT_DATA_CMD:
+ rx_sd_descp = (struct rx_short_data_desc *)
+ (rx_data + offset);
+ offset += sizeof(*rx_sd_descp);
+ process_rx_data(einfo, cmd->id, cmd->param1,
+ cmd->param2, (void *)rx_sd_descp->data,
+ cmd->param3, cmd->param4);
+ break;
+
+ case READ_NOTIF_CMD:
+ break;
+
+ case SIGNALS_CMD:
+ einfo->xprt_if.glink_core_if_ptr->rx_cmd_remote_sigs(
+ &einfo->xprt_if, cmd->param1, cmd->param2);
+ break;
+
+ case RX_DONE_W_REUSE_CMD:
+ einfo->xprt_if.glink_core_if_ptr->rx_cmd_tx_done(
+ &einfo->xprt_if, cmd->param1,
+ cmd->param2, true);
+ break;
+
+ default:
+ pr_err("Unrecognized command: %d\n", cmd->id);
+ break;
+ }
+ }
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
+ * __rx_worker() - Receive commands on a specific edge
+ * @einfo: Edge to process commands on.
+ *
+ * This function checks the size of data to be received, allocates the
+ * buffer for that data and reads the data from the remote subsytem
+ * into that buffer. This function then calls the process_rx_cmd() to
+ * parse the received G-Link command sequence. This function will also
+ * poll for the data for a predefined duration for performance reasons.
+ */
+static void __rx_worker(struct edge_info *einfo)
+{
+ uint32_t inactive_cycles = 0;
+ int rx_avail, rc;
+ void *rx_data;
+ int rcu_id;
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (unlikely(!einfo->rx_fifo_start)) {
+ rx_avail = glink_spi_xprt_read_avail(einfo);
+ if (!rx_avail) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return;
+ }
+ einfo->in_ssr = false;
+ einfo->xprt_if.glink_core_if_ptr->link_up(&einfo->xprt_if);
+ }
+
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return;
+ }
+
+ glink_spi_xprt_set_poll_mode(einfo);
+ while (inactive_cycles < MAX_INACTIVE_CYCLES) {
+ if (einfo->tx_resume_needed &&
+ glink_spi_xprt_write_avail(einfo)) {
+ einfo->tx_resume_needed = false;
+ einfo->xprt_if.glink_core_if_ptr->tx_resume(
+ &einfo->xprt_if);
+ }
+ mutex_lock(&einfo->write_lock);
+ if (einfo->tx_blocked_signal_sent) {
+ wake_up_all(&einfo->tx_blocked_queue);
+ einfo->tx_blocked_signal_sent = false;
+ }
+ mutex_unlock(&einfo->write_lock);
+
+ rx_avail = glink_spi_xprt_read_avail(einfo);
+ if (!rx_avail) {
+ usleep_range(POLL_INTERVAL_US, POLL_INTERVAL_US + 50);
+ inactive_cycles++;
+ continue;
+ }
+ inactive_cycles = 0;
+
+ rx_data = kzalloc(rx_avail, GFP_KERNEL);
+ if (!rx_data)
+ break;
+
+ rc = glink_spi_xprt_rx_cmd(einfo, rx_data, rx_avail);
+ if (rc < 0) {
+ GLINK_ERR("%s: Error %d receiving data\n",
+ __func__, rc);
+ kfree(rx_data);
+ break;
+ }
+ process_rx_cmd(einfo, rx_data, rx_avail);
+ kfree(rx_data);
+ }
+ glink_spi_xprt_set_irq_mode(einfo);
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
+ * rx_worker() - Worker function to process received commands
+ * @work: kwork associated with the edge to process commands on.
+ */
+static void rx_worker(struct kthread_work *work)
+{
+ struct edge_info *einfo;
+
+ einfo = container_of(work, struct edge_info, kwork);
+ __rx_worker(einfo);
+};
+
+/**
+ * tx_cmd_version() - Convert a version cmd to wire format and transmit
+ * @if_ptr: The transport to transmit on.
+ * @version: The version number to encode.
+ * @features: The features information to encode.
+ */
+static void tx_cmd_version(struct glink_transport_if *if_ptr, uint32_t version,
+ uint32_t features)
+{
+ struct command {
+ uint16_t id;
+ uint16_t version;
+ uint32_t features;
+ uint32_t fifo_size;
+ uint32_t reserved;
+ };
+ struct command cmd;
+ struct edge_info *einfo;
+ int rcu_id;
+
+ memset(&cmd, 0, sizeof(cmd));
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return;
+ }
+
+ cmd.id = VERSION_CMD;
+ cmd.version = version;
+ cmd.features = features;
+
+ glink_spi_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
+ * tx_cmd_version_ack() - Convert a version ack cmd to wire format and transmit
+ * @if_ptr: The transport to transmit on.
+ * @version: The version number to encode.
+ * @features: The features information to encode.
+ */
+static void tx_cmd_version_ack(struct glink_transport_if *if_ptr,
+ uint32_t version,
+ uint32_t features)
+{
+ struct command {
+ uint16_t id;
+ uint16_t version;
+ uint32_t features;
+ uint32_t fifo_size;
+ uint32_t reserved;
+ };
+ struct command cmd;
+ struct edge_info *einfo;
+ int rcu_id;
+
+ memset(&cmd, 0, sizeof(cmd));
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return;
+ }
+
+ cmd.id = VERSION_ACK_CMD;
+ cmd.version = version;
+ cmd.features = features;
+
+ glink_spi_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
+ * set_version() - Activate a negotiated version and feature set
+ * @if_ptr: The transport to configure.
+ * @version: The version to use.
+ * @features: The features to use.
+ *
+ * Return: The supported capabilities of the transport.
+ */
+static uint32_t set_version(struct glink_transport_if *if_ptr, uint32_t version,
+ uint32_t features)
+{
+ struct edge_info *einfo;
+ uint32_t ret;
+ int rcu_id;
+
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return 0;
+ }
+
+ ret = GCAP_SIGNALS;
+ if (features & TRACER_PKT_FEATURE)
+ ret |= GCAP_TRACER_PKT;
+
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return ret;
+}
+
+/**
+ * tx_cmd_ch_open() - Convert a channel open cmd to wire format and transmit
+ * @if_ptr: The transport to transmit on.
+ * @lcid: The local channel id to encode.
+ * @name: The channel name to encode.
+ * @req_xprt: The transport the core would like to migrate this channel to.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int tx_cmd_ch_open(struct glink_transport_if *if_ptr, uint32_t lcid,
+ const char *name, uint16_t req_xprt)
+{
+ struct command {
+ uint16_t id;
+ uint16_t lcid;
+ uint16_t length;
+ uint16_t req_xprt;
+ uint64_t reserved;
+ };
+ struct command cmd;
+ struct edge_info *einfo;
+ uint32_t buf_size;
+ void *buf;
+ int rcu_id;
+
+ memset(&cmd, 0, sizeof(cmd));
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return -EFAULT;
+ }
+
+ cmd.id = OPEN_CMD;
+ cmd.lcid = lcid;
+ cmd.length = (uint16_t)(strlen(name) + 1);
+ cmd.req_xprt = req_xprt;
+
+ buf_size = ALIGN(sizeof(cmd) + cmd.length, FIFO_ALIGNMENT);
+
+ buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!buf) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return -ENOMEM;
+ }
+
+ memcpy(buf, &cmd, sizeof(cmd));
+ memcpy(buf + sizeof(cmd), name, cmd.length);
+
+ glink_spi_xprt_tx_cmd(einfo, buf, buf_size);
+
+ kfree(buf);
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return 0;
+}
+
+/**
+ * tx_cmd_ch_close() - Convert a channel close cmd to wire format and transmit
+ * @if_ptr: The transport to transmit on.
+ * @lcid: The local channel id to encode.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int tx_cmd_ch_close(struct glink_transport_if *if_ptr, uint32_t lcid)
+{
+ struct command {
+ uint16_t id;
+ uint16_t lcid;
+ uint32_t reserved1;
+ uint64_t reserved2;
+ };
+ struct command cmd;
+ struct edge_info *einfo;
+ int rcu_id;
+
+ memset(&cmd, 0, sizeof(cmd));
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return -EFAULT;
+ }
+
+ cmd.id = CLOSE_CMD;
+ cmd.lcid = lcid;
+
+ glink_spi_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return 0;
+}
+
+/**
+ * tx_cmd_ch_remote_open_ack() - Convert a channel open ack cmd to wire format
+ * and transmit
+ * @if_ptr: The transport to transmit on.
+ * @rcid: The remote channel id to encode.
+ * @xprt_resp: The response to a transport migration request.
+ */
+static void tx_cmd_ch_remote_open_ack(struct glink_transport_if *if_ptr,
+ uint32_t rcid, uint16_t xprt_resp)
+{
+ struct command {
+ uint16_t id;
+ uint16_t rcid;
+ uint16_t reserved1;
+ uint16_t xprt_resp;
+ uint64_t reserved2;
+ };
+ struct command cmd;
+ struct edge_info *einfo;
+ int rcu_id;
+
+ memset(&cmd, 0, sizeof(cmd));
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return;
+ }
+
+ cmd.id = OPEN_ACK_CMD;
+ cmd.rcid = rcid;
+ cmd.xprt_resp = xprt_resp;
+
+ glink_spi_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
+ * tx_cmd_ch_remote_close_ack() - Convert a channel close ack cmd to wire format
+ * and transmit
+ * @if_ptr: The transport to transmit on.
+ * @rcid: The remote channel id to encode.
+ */
+static void tx_cmd_ch_remote_close_ack(struct glink_transport_if *if_ptr,
+ uint32_t rcid)
+{
+ struct command {
+ uint16_t id;
+ uint16_t rcid;
+ uint32_t reserved1;
+ uint64_t reserved2;
+ };
+ struct command cmd;
+ struct edge_info *einfo;
+ int rcu_id;
+
+ memset(&cmd, 0, sizeof(cmd));
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return;
+ }
+
+ cmd.id = CLOSE_ACK_CMD;
+ cmd.rcid = rcid;
+
+ glink_spi_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
+ * ssr() - Process a subsystem restart notification of a transport
+ * @if_ptr: The transport to restart
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int ssr(struct glink_transport_if *if_ptr)
+{
+ struct edge_info *einfo;
+
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+ einfo->in_ssr = true;
+ wake_up_all(&einfo->tx_blocked_queue);
+
+ synchronize_srcu(&einfo->use_ref);
+ einfo->tx_resume_needed = false;
+ einfo->tx_blocked_signal_sent = false;
+ einfo->tx_fifo_start = 0;
+ einfo->rx_fifo_start = 0;
+ einfo->fifo_size = DEFAULT_FIFO_SIZE;
+ einfo->xprt_if.glink_core_if_ptr->link_down(&einfo->xprt_if);
+
+ return 0;
+}
+
+/**
+ * allocate_rx_intent() - Allocate/reserve space for RX Intent
+ * @if_ptr: The transport the intent is associated with.
+ * @size: size of intent.
+ * @intent: Pointer to the intent structure.
+ *
+ * Assign "data" with the buffer created, since the transport creates
+ * a linear buffer and "iovec" with the "intent" itself, so that
+ * the data can be passed to a client that receives only vector buffer.
+ * Note that returning NULL for the pointer is valid (it means that space has
+ * been reserved, but the actual pointer will be provided later).
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int allocate_rx_intent(struct glink_transport_if *if_ptr, size_t size,
+ struct glink_core_rx_intent *intent)
+{
+ void *t;
+
+ t = kzalloc(size, GFP_KERNEL);
+ if (!t)
+ return -ENOMEM;
+
+ intent->data = t;
+ intent->iovec = (void *)intent;
+ intent->vprovider = rx_linear_vbuf_provider;
+ intent->pprovider = NULL;
+ return 0;
+}
+
+/**
+ * deallocate_rx_intent() - Deallocate space created for RX Intent
+ * @if_ptr: The transport the intent is associated with.
+ * @intent: Pointer to the intent structure.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int deallocate_rx_intent(struct glink_transport_if *if_ptr,
+ struct glink_core_rx_intent *intent)
+{
+ if (!intent || !intent->data)
+ return -EINVAL;
+
+ kfree(intent->data);
+ intent->data = NULL;
+ intent->iovec = NULL;
+ intent->vprovider = NULL;
+ return 0;
+}
+
+/**
+ * tx_cmd_local_rx_intent() - Convert an rx intent cmd to wire format and
+ * transmit
+ * @if_ptr: The transport to transmit on.
+ * @lcid: The local channel id to encode.
+ * @size: The intent size to encode.
+ * @liid: The local intent id to encode.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int tx_cmd_local_rx_intent(struct glink_transport_if *if_ptr,
+ uint32_t lcid, size_t size, uint32_t liid)
+{
+ struct command {
+ uint16_t id;
+ uint16_t lcid;
+ uint32_t count;
+ uint64_t reserved;
+ uint32_t size;
+ uint32_t liid;
+ uint64_t addr;
+ };
+ struct command cmd;
+ struct edge_info *einfo;
+ int rcu_id;
+
+ if (size > UINT_MAX) {
+ pr_err("%s: size %zu is too large to encode\n", __func__, size);
+ return -EMSGSIZE;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return -EFAULT;
+ }
+
+ cmd.id = RX_INTENT_CMD;
+ cmd.lcid = lcid;
+ cmd.count = 1;
+ cmd.size = size;
+ cmd.liid = liid;
+
+ glink_spi_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return 0;
+}
+
+/**
+ * tx_cmd_local_rx_done() - Convert an rx done cmd to wire format and transmit
+ * @if_ptr: The transport to transmit on.
+ * @lcid: The local channel id to encode.
+ * @liid: The local intent id to encode.
+ * @reuse: Reuse the consumed intent.
+ */
+static void tx_cmd_local_rx_done(struct glink_transport_if *if_ptr,
+ uint32_t lcid, uint32_t liid, bool reuse)
+{
+ struct command {
+ uint16_t id;
+ uint16_t lcid;
+ uint32_t liid;
+ uint64_t reserved;
+ };
+ struct command cmd;
+ struct edge_info *einfo;
+ int rcu_id;
+
+ memset(&cmd, 0, sizeof(cmd));
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return;
+ }
+
+ cmd.id = reuse ? RX_DONE_W_REUSE_CMD : RX_DONE_CMD;
+ cmd.lcid = lcid;
+ cmd.liid = liid;
+
+ glink_spi_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
+ * tx_cmd_rx_intent_req() - Convert an rx intent request cmd to wire format and
+ * transmit
+ * @if_ptr: The transport to transmit on.
+ * @lcid: The local channel id to encode.
+ * @size: The requested intent size to encode.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int tx_cmd_rx_intent_req(struct glink_transport_if *if_ptr,
+ uint32_t lcid, size_t size)
+{
+ struct command {
+ uint16_t id;
+ uint16_t lcid;
+ uint32_t size;
+ uint64_t reserved;
+ };
+ struct command cmd;
+ struct edge_info *einfo;
+ int rcu_id;
+
+ if (size > UINT_MAX) {
+ pr_err("%s: size %zu is too large to encode\n", __func__, size);
+ return -EMSGSIZE;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return -EFAULT;
+ }
+
+ cmd.id = RX_INTENT_REQ_CMD,
+ cmd.lcid = lcid;
+ cmd.size = size;
+
+ glink_spi_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return 0;
+}
+
+/**
+ * tx_cmd_rx_intent_req_ack() - Convert an rx intent request ack cmd to wire
+ * format and transmit
+ * @if_ptr: The transport to transmit on.
+ * @lcid: The local channel id to encode.
+ * @granted: The request response to encode.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int tx_cmd_remote_rx_intent_req_ack(struct glink_transport_if *if_ptr,
+ uint32_t lcid, bool granted)
+{
+ struct command {
+ uint16_t id;
+ uint16_t lcid;
+ uint32_t response;
+ uint64_t reserved;
+ };
+ struct command cmd;
+ struct edge_info *einfo;
+ int rcu_id;
+
+ memset(&cmd, 0, sizeof(cmd));
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return -EFAULT;
+ }
+
+ cmd.id = RX_INTENT_REQ_ACK_CMD,
+ cmd.lcid = lcid;
+ if (granted)
+ cmd.response = 1;
+ else
+ cmd.response = 0;
+
+ glink_spi_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return 0;
+}
+
+/**
+ * tx_cmd_set_sigs() - Convert a signals ack cmd to wire format and transmit
+ * @if_ptr: The transport to transmit on.
+ * @lcid: The local channel id to encode.
+ * @sigs: The signals to encode.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int tx_cmd_set_sigs(struct glink_transport_if *if_ptr, uint32_t lcid,
+ uint32_t sigs)
+{
+ struct command {
+ uint16_t id;
+ uint16_t lcid;
+ uint32_t sigs;
+ uint64_t reserved;
+ };
+ struct command cmd;
+ struct edge_info *einfo;
+ int rcu_id;
+
+ memset(&cmd, 0, sizeof(cmd));
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return -EFAULT;
+ }
+
+ cmd.id = SIGNALS_CMD,
+ cmd.lcid = lcid;
+ cmd.sigs = sigs;
+
+ glink_spi_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return 0;
+}
+
+/**
+ * tx_data() - convert a data/tracer_pkt to wire format and transmit
+ * @if_ptr: The transport to transmit on.
+ * @cmd_id: The command ID to transmit.
+ * @lcid: The local channel id to encode.
+ * @pctx: The data to encode.
+ *
+ * Return: Number of bytes written or standard Linux error code.
+ */
+static int tx_data(struct glink_transport_if *if_ptr, uint16_t cmd_id,
+ uint32_t lcid, struct glink_core_tx_pkt *pctx)
+{
+ struct command {
+ uint16_t id;
+ uint16_t lcid;
+ uint32_t riid;
+ uint64_t reserved;
+ uint32_t size;
+ uint32_t size_left;
+ uint64_t addr;
+ };
+ struct command cmd;
+ struct edge_info *einfo;
+ uint32_t size;
+ void *data_start, *dst = NULL;
+ size_t tx_size = 0;
+ int rcu_id;
+
+ if (pctx->size < pctx->size_remaining) {
+ GLINK_ERR("%s: size remaining exceeds size. Resetting.\n",
+ __func__);
+ pctx->size_remaining = pctx->size;
+ }
+ if (!pctx->size_remaining)
+ return 0;
+
+ memset(&cmd, 0, sizeof(cmd));
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return -EFAULT;
+ }
+
+ if (cmd_id == TX_DATA_CMD) {
+ if (pctx->size_remaining == pctx->size)
+ cmd.id = TX_DATA_CMD;
+ else
+ cmd.id = TX_DATA_CONT_CMD;
+ } else {
+ if (pctx->size_remaining == pctx->size)
+ cmd.id = TRACER_PKT_CMD;
+ else
+ cmd.id = TRACER_PKT_CONT_CMD;
+ }
+ cmd.lcid = lcid;
+ cmd.riid = pctx->riid;
+ data_start = get_tx_vaddr(pctx, pctx->size - pctx->size_remaining,
+ &tx_size);
+ if (unlikely(!data_start)) {
+ GLINK_ERR("%s: invalid data_start\n", __func__);
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return -EINVAL;
+ }
+ if (tx_size & (XPRT_ALIGNMENT - 1))
+ tx_size = ALIGN(tx_size - SHORT_PKT_SIZE, XPRT_ALIGNMENT);
+ if (likely(pctx->cookie))
+ dst = pctx->cookie + (pctx->size - pctx->size_remaining);
+
+ mutex_lock(&einfo->write_lock);
+ size = glink_spi_xprt_write_avail(einfo);
+ /* Need enough space to write the command */
+ if (size <= sizeof(cmd)) {
+ einfo->tx_resume_needed = true;
+ mutex_unlock(&einfo->write_lock);
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return -EAGAIN;
+ }
+ cmd.addr = 0;
+ cmd.size = tx_size;
+ pctx->size_remaining -= tx_size;
+ cmd.size_left = pctx->size_remaining;
+ if (cmd.id == TRACER_PKT_CMD)
+ tracer_pkt_log_event((void *)(pctx->data), GLINK_XPRT_TX);
+
+ if (!strcmp(einfo->xprt_cfg.edge, "wdsp"))
+ wdsp_resume(&einfo->cmpnt);
+ glink_spi_xprt_tx_data(einfo, data_start, dst, tx_size);
+ glink_spi_xprt_tx_cmd_safe(einfo, &cmd, sizeof(cmd));
+ GLINK_DBG("%s %s: lcid[%u] riid[%u] cmd[%d], size[%d], size_left[%d]\n",
+ "<SPI>", __func__, cmd.lcid, cmd.riid, cmd.id, cmd.size,
+ cmd.size_left);
+ mutex_unlock(&einfo->write_lock);
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return cmd.size;
+}
+
+/**
+ * tx_short_data() - Tansmit a short packet in band along with command
+ * @if_ptr: The transport to transmit on.
+ * @cmd_id: The command ID to transmit.
+ * @lcid: The local channel id to encode.
+ * @pctx: The data to encode.
+ *
+ * Return: Number of bytes written or standard Linux error code.
+ */
+static int tx_short_data(struct glink_transport_if *if_ptr,
+ uint32_t lcid, struct glink_core_tx_pkt *pctx)
+{
+ struct command {
+ uint16_t id;
+ uint16_t lcid;
+ uint32_t riid;
+ uint32_t size;
+ uint32_t size_left;
+ unsigned char data[SHORT_PKT_SIZE];
+ };
+ struct command cmd;
+ struct edge_info *einfo;
+ uint32_t size;
+ void *data_start;
+ size_t tx_size = 0;
+ int rcu_id;
+
+ if (pctx->size < pctx->size_remaining) {
+ GLINK_ERR("%s: size remaining exceeds size. Resetting.\n",
+ __func__);
+ pctx->size_remaining = pctx->size;
+ }
+ if (!pctx->size_remaining)
+ return 0;
+
+ memset(&cmd, 0, sizeof(cmd));
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return -EFAULT;
+ }
+
+ cmd.id = TX_SHORT_DATA_CMD;
+ cmd.lcid = lcid;
+ cmd.riid = pctx->riid;
+ data_start = get_tx_vaddr(pctx, pctx->size - pctx->size_remaining,
+ &tx_size);
+ if (unlikely(!data_start || tx_size > SHORT_PKT_SIZE)) {
+ GLINK_ERR("%s: invalid data_start %p or tx_size %zu\n",
+ __func__, data_start, tx_size);
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&einfo->write_lock);
+ size = glink_spi_xprt_write_avail(einfo);
+ /* Need enough space to write the command */
+ if (size <= sizeof(cmd)) {
+ einfo->tx_resume_needed = true;
+ mutex_unlock(&einfo->write_lock);
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return -EAGAIN;
+ }
+ cmd.size = tx_size;
+ pctx->size_remaining -= tx_size;
+ cmd.size_left = pctx->size_remaining;
+ memcpy(cmd.data, data_start, tx_size);
+ if (!strcmp(einfo->xprt_cfg.edge, "wdsp"))
+ wdsp_resume(&einfo->cmpnt);
+ glink_spi_xprt_tx_cmd_safe(einfo, &cmd, sizeof(cmd));
+ GLINK_DBG("%s %s: lcid[%u] riid[%u] cmd[%d], size[%d], size_left[%d]\n",
+ "<SPI>", __func__, cmd.lcid, cmd.riid, cmd.id, cmd.size,
+ cmd.size_left);
+ mutex_unlock(&einfo->write_lock);
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return cmd.size;
+}
+
+/**
+ * tx() - convert a data transmit cmd to wire format and transmit
+ * @if_ptr: The transport to transmit on.
+ * @lcid: The local channel id to encode.
+ * @pctx: The data to encode.
+ *
+ * Return: Number of bytes written or standard Linux error code.
+ */
+static int tx(struct glink_transport_if *if_ptr, uint32_t lcid,
+ struct glink_core_tx_pkt *pctx)
+{
+ if (pctx->size_remaining <= SHORT_PKT_SIZE)
+ return tx_short_data(if_ptr, lcid, pctx);
+ return tx_data(if_ptr, TX_DATA_CMD, lcid, pctx);
+}
+
+/**
+ * tx_cmd_tracer_pkt() - convert a tracer packet cmd to wire format and transmit
+ * @if_ptr: The transport to transmit on.
+ * @lcid: The local channel id to encode.
+ * @pctx: The data to encode.
+ *
+ * Return: Number of bytes written or standard Linux error code.
+ */
+static int tx_cmd_tracer_pkt(struct glink_transport_if *if_ptr, uint32_t lcid,
+ struct glink_core_tx_pkt *pctx)
+{
+ return tx_data(if_ptr, TRACER_PKT_CMD, lcid, pctx);
+}
+
+/**
+ * int wait_link_down() - Check status of read/write indices
+ * @if_ptr: The transport to check
+ *
+ * Return: 1 if indices are all zero, 0 otherwise
+ */
+static int wait_link_down(struct glink_transport_if *if_ptr)
+{
+ return 0;
+}
+
+/**
+ * get_power_vote_ramp_time() - Get the ramp time required for the power
+ * votes to be applied
+ * @if_ptr: The transport interface on which power voting is requested.
+ * @state: The power state for which ramp time is required.
+ *
+ * Return: The ramp time specific to the power state, standard error otherwise.
+ */
+static unsigned long get_power_vote_ramp_time(
+ struct glink_transport_if *if_ptr, uint32_t state)
+{
+ return 0;
+}
+
+/**
+ * power_vote() - Update the power votes to meet qos requirement
+ * @if_ptr: The transport interface on which power voting is requested.
+ * @state: The power state for which the voting should be done.
+ *
+ * Return: 0 on Success, standard error otherwise.
+ */
+static int power_vote(struct glink_transport_if *if_ptr, uint32_t state)
+{
+ unsigned long flags;
+ struct edge_info *einfo;
+
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+ spin_lock_irqsave(&einfo->activity_lock, flags);
+ einfo->activity_flag |= ACTIVE_TX;
+ spin_unlock_irqrestore(&einfo->activity_lock, flags);
+ return 0;
+}
+
+/**
+ * power_unvote() - Remove the all the power votes
+ * @if_ptr: The transport interface on which power voting is requested.
+ *
+ * Return: 0 on Success, standard error otherwise.
+ */
+static int power_unvote(struct glink_transport_if *if_ptr)
+{
+ unsigned long flags;
+ struct edge_info *einfo;
+
+ einfo = container_of(if_ptr, struct edge_info, xprt_if);
+ spin_lock_irqsave(&einfo->activity_lock, flags);
+ einfo->activity_flag &= ~ACTIVE_TX;
+ spin_unlock_irqrestore(&einfo->activity_lock, flags);
+ return 0;
+}
+
+static int glink_wdsp_cmpnt_init(struct device *dev, void *priv_data)
+{
+ return 0;
+}
+
+static int glink_wdsp_cmpnt_deinit(struct device *dev, void *priv_data)
+{
+ return 0;
+}
+
+static int glink_wdsp_cmpnt_event_handler(struct device *dev,
+ void *priv_data, enum wdsp_event_type event, void *data)
+{
+ struct edge_info *einfo = dev_get_drvdata(dev);
+ struct glink_cmpnt *cmpnt = &einfo->cmpnt;
+ struct device *sdev;
+ struct spi_device *spi_dev;
+
+ switch (event) {
+ case WDSP_EVENT_PRE_BOOTUP:
+ if (cmpnt && cmpnt->master_dev &&
+ cmpnt->master_ops &&
+ cmpnt->master_ops->get_dev_for_cmpnt)
+ sdev = cmpnt->master_ops->get_dev_for_cmpnt(
+ cmpnt->master_dev, WDSP_CMPNT_TRANSPORT);
+ else
+ sdev = NULL;
+
+ if (!sdev) {
+ dev_err(dev, "%s: Failed to get transport device\n",
+ __func__);
+ break;
+ }
+
+ spi_dev = to_spi_device(sdev);
+ einfo->spi_dev = spi_dev;
+ break;
+ case WDSP_EVENT_IPC1_INTR:
+ queue_kthread_work(&einfo->kworker, &einfo->kwork);
+ break;
+ default:
+ pr_debug("%s: unhandled event %d", __func__, event);
+ break;
+ }
+
+ return 0;
+}
+
+/* glink_wdsp_cmpnt_ops - Callback operations registered wtih WDSP framework */
+static struct wdsp_cmpnt_ops glink_wdsp_cmpnt_ops = {
+ .init = glink_wdsp_cmpnt_init,
+ .deinit = glink_wdsp_cmpnt_deinit,
+ .event_handler = glink_wdsp_cmpnt_event_handler,
+};
+
+static int glink_component_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct edge_info *einfo = dev_get_drvdata(dev);
+ struct glink_cmpnt *cmpnt = &einfo->cmpnt;
+ int ret = 0;
+
+ cmpnt->master_dev = master;
+ cmpnt->master_ops = data;
+
+ if (cmpnt->master_ops && cmpnt->master_ops->register_cmpnt_ops)
+ ret = cmpnt->master_ops->register_cmpnt_ops(master, dev, einfo,
+ &glink_wdsp_cmpnt_ops);
+ else
+ ret = -EINVAL;
+
+ if (ret)
+ dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n",
+ __func__, ret);
+ return ret;
+}
+
+static void glink_component_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct edge_info *einfo = dev_get_drvdata(dev);
+ struct glink_cmpnt *cmpnt = &einfo->cmpnt;
+
+ cmpnt->master_dev = NULL;
+ cmpnt->master_ops = NULL;
+}
+
+static const struct component_ops glink_component_ops = {
+ .bind = glink_component_bind,
+ .unbind = glink_component_unbind,
+};
+
+/**
+ * init_xprt_if() - Initialize the xprt_if for an edge
+ * @einfo: The edge to initialize.
+ */
+static void init_xprt_if(struct edge_info *einfo)
+{
+ einfo->xprt_if.tx_cmd_version = tx_cmd_version;
+ einfo->xprt_if.tx_cmd_version_ack = tx_cmd_version_ack;
+ einfo->xprt_if.set_version = set_version;
+ einfo->xprt_if.tx_cmd_ch_open = tx_cmd_ch_open;
+ einfo->xprt_if.tx_cmd_ch_close = tx_cmd_ch_close;
+ einfo->xprt_if.tx_cmd_ch_remote_open_ack = tx_cmd_ch_remote_open_ack;
+ einfo->xprt_if.tx_cmd_ch_remote_close_ack = tx_cmd_ch_remote_close_ack;
+ einfo->xprt_if.ssr = ssr;
+ einfo->xprt_if.allocate_rx_intent = allocate_rx_intent;
+ einfo->xprt_if.deallocate_rx_intent = deallocate_rx_intent;
+ einfo->xprt_if.tx_cmd_local_rx_intent = tx_cmd_local_rx_intent;
+ einfo->xprt_if.tx_cmd_local_rx_done = tx_cmd_local_rx_done;
+ einfo->xprt_if.tx = tx;
+ einfo->xprt_if.tx_cmd_rx_intent_req = tx_cmd_rx_intent_req;
+ einfo->xprt_if.tx_cmd_remote_rx_intent_req_ack =
+ tx_cmd_remote_rx_intent_req_ack;
+ einfo->xprt_if.tx_cmd_set_sigs = tx_cmd_set_sigs;
+ einfo->xprt_if.wait_link_down = wait_link_down;
+ einfo->xprt_if.tx_cmd_tracer_pkt = tx_cmd_tracer_pkt;
+ einfo->xprt_if.get_power_vote_ramp_time = get_power_vote_ramp_time;
+ einfo->xprt_if.power_vote = power_vote;
+ einfo->xprt_if.power_unvote = power_unvote;
+}
+
+/**
+ * init_xprt_cfg() - Initialize the xprt_cfg for an edge
+ * @einfo: The edge to initialize.
+ * @name: The name of the remote side this edge communicates to.
+ */
+static void init_xprt_cfg(struct edge_info *einfo, const char *name)
+{
+ einfo->xprt_cfg.name = XPRT_NAME;
+ einfo->xprt_cfg.edge = name;
+ einfo->xprt_cfg.versions = versions;
+ einfo->xprt_cfg.versions_entries = ARRAY_SIZE(versions);
+ einfo->xprt_cfg.max_cid = SZ_64K;
+ einfo->xprt_cfg.max_iid = SZ_2G;
+}
+
+/**
+ * parse_qos_dt_params() - Parse the power states from DT
+ * @dev: Reference to the platform device for a specific edge.
+ * @einfo: Edge information for the edge probe function is called.
+ *
+ * Return: 0 on success, standard error code otherwise.
+ */
+static int parse_qos_dt_params(struct device_node *node,
+ struct edge_info *einfo)
+{
+ int rc;
+ int i;
+ char *key;
+ uint32_t *arr32;
+ uint32_t num_states;
+
+ key = "qcom,ramp-time";
+ if (!of_find_property(node, key, &num_states))
+ return -ENODEV;
+
+ num_states /= sizeof(uint32_t);
+
+ einfo->num_pw_states = num_states;
+
+ arr32 = kmalloc_array(num_states, sizeof(uint32_t), GFP_KERNEL);
+ if (!arr32)
+ return -ENOMEM;
+
+ einfo->ramp_time_us = kmalloc_array(num_states, sizeof(unsigned long),
+ GFP_KERNEL);
+ if (!einfo->ramp_time_us) {
+ rc = -ENOMEM;
+ goto mem_alloc_fail;
+ }
+
+ rc = of_property_read_u32_array(node, key, arr32, num_states);
+ if (rc) {
+ rc = -ENODEV;
+ goto invalid_key;
+ }
+ for (i = 0; i < num_states; i++)
+ einfo->ramp_time_us[i] = arr32[i];
+
+ kfree(arr32);
+ return 0;
+
+invalid_key:
+ kfree(einfo->ramp_time_us);
+mem_alloc_fail:
+ kfree(arr32);
+ return rc;
+}
+
+/**
+ * parse_qos_dt_params() - Parse any remote FIFO configuration
+ * @node: Reference to the platform device for a specific edge.
+ * @einfo: Edge information for the edge probe function is called.
+ *
+ * Return: 0 on success, standard error code otherwise.
+ */
+static int parse_remote_fifo_cfg(struct device_node *node,
+ struct edge_info *einfo)
+{
+ int rc;
+ char *key;
+
+ key = "qcom,out-read-idx-reg";
+ rc = of_property_read_u32(node, key, &einfo->tx_fifo_read_reg_addr);
+ if (rc)
+ goto key_error;
+
+ key = "qcom,out-write-idx-reg";
+ rc = of_property_read_u32(node, key, &einfo->tx_fifo_write_reg_addr);
+ if (rc)
+ goto key_error;
+
+ key = "qcom,in-read-idx-reg";
+ rc = of_property_read_u32(node, key, &einfo->rx_fifo_read_reg_addr);
+ if (rc)
+ goto key_error;
+
+ key = "qcom,in-write-idx-reg";
+ rc = of_property_read_u32(node, key, &einfo->rx_fifo_write_reg_addr);
+ if (rc)
+ goto key_error;
+ return 0;
+
+key_error:
+ pr_err("%s: Error %d parsing key %s\n", __func__, rc, key);
+ return rc;
+}
+
+static int glink_spi_probe(struct platform_device *pdev)
+{
+ struct device_node *node;
+ struct device_node *phandle_node;
+ struct edge_info *einfo;
+ int rc;
+ char *key;
+ const char *subsys_name;
+ unsigned long flags;
+
+ node = pdev->dev.of_node;
+
+ einfo = kzalloc(sizeof(*einfo), GFP_KERNEL);
+ if (!einfo) {
+ rc = -ENOMEM;
+ goto edge_info_alloc_fail;
+ }
+
+ key = "label";
+ subsys_name = of_get_property(node, key, NULL);
+ if (!subsys_name) {
+ pr_err("%s: missing key %s\n", __func__, key);
+ rc = -ENODEV;
+ goto missing_key;
+ }
+ strlcpy(einfo->subsys_name, subsys_name, sizeof(einfo->subsys_name));
+
+ init_xprt_cfg(einfo, subsys_name);
+ init_xprt_if(einfo);
+
+ einfo->in_ssr = true;
+ einfo->fifo_size = DEFAULT_FIFO_SIZE;
+ init_kthread_work(&einfo->kwork, rx_worker);
+ init_kthread_worker(&einfo->kworker);
+ init_srcu_struct(&einfo->use_ref);
+ mutex_init(&einfo->write_lock);
+ init_waitqueue_head(&einfo->tx_blocked_queue);
+ spin_lock_init(&einfo->activity_lock);
+
+ spin_lock_irqsave(&edge_infos_lock, flags);
+ list_add_tail(&einfo->list, &edge_infos);
+ spin_unlock_irqrestore(&edge_infos_lock, flags);
+
+ einfo->task = kthread_run(kthread_worker_fn, &einfo->kworker,
+ "spi_%s", subsys_name);
+ if (IS_ERR(einfo->task)) {
+ rc = PTR_ERR(einfo->task);
+ pr_err("%s: kthread run failed %d\n", __func__, rc);
+ goto kthread_fail;
+ }
+
+ key = "qcom,remote-fifo-config";
+ phandle_node = of_parse_phandle(node, key, 0);
+ if (phandle_node)
+ parse_remote_fifo_cfg(phandle_node, einfo);
+
+ key = "qcom,qos-config";
+ phandle_node = of_parse_phandle(node, key, 0);
+ if (phandle_node && !(of_get_glink_core_qos_cfg(phandle_node,
+ &einfo->xprt_cfg)))
+ parse_qos_dt_params(node, einfo);
+
+ rc = glink_core_register_transport(&einfo->xprt_if, &einfo->xprt_cfg);
+ if (rc == -EPROBE_DEFER)
+ goto reg_xprt_fail;
+ if (rc) {
+ pr_err("%s: glink core register transport failed: %d\n",
+ __func__, rc);
+ goto reg_xprt_fail;
+ }
+
+ dev_set_drvdata(&pdev->dev, einfo);
+ if (!strcmp(einfo->xprt_cfg.edge, "wdsp")) {
+ rc = component_add(&pdev->dev, &glink_component_ops);
+ if (rc) {
+ pr_err("%s: component_add failed, err = %d\n",
+ __func__, rc);
+ rc = -ENODEV;
+ goto reg_cmpnt_fail;
+ }
+ }
+ return 0;
+
+reg_cmpnt_fail:
+ dev_set_drvdata(&pdev->dev, NULL);
+ glink_core_unregister_transport(&einfo->xprt_if);
+reg_xprt_fail:
+ flush_kthread_worker(&einfo->kworker);
+ kthread_stop(einfo->task);
+ einfo->task = NULL;
+kthread_fail:
+ spin_lock_irqsave(&edge_infos_lock, flags);
+ list_del(&einfo->list);
+ spin_unlock_irqrestore(&edge_infos_lock, flags);
+missing_key:
+ kfree(einfo);
+edge_info_alloc_fail:
+ return rc;
+}
+
+static int glink_spi_remove(struct platform_device *pdev)
+{
+ struct edge_info *einfo;
+ unsigned long flags;
+
+ einfo = (struct edge_info *)dev_get_drvdata(&pdev->dev);
+ glink_core_unregister_transport(&einfo->xprt_if);
+ flush_kthread_worker(&einfo->kworker);
+ kthread_stop(einfo->task);
+ einfo->task = NULL;
+ spin_lock_irqsave(&edge_infos_lock, flags);
+ list_del(&einfo->list);
+ spin_unlock_irqrestore(&edge_infos_lock, flags);
+ kfree(einfo);
+ return 0;
+}
+
+static int glink_spi_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int glink_spi_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ unsigned long flags;
+ struct edge_info *einfo;
+ bool suspend;
+ int rc = -EBUSY;
+
+ einfo = (struct edge_info *)dev_get_drvdata(&pdev->dev);
+ if (strcmp(einfo->xprt_cfg.edge, "wdsp"))
+ return 0;
+
+ spin_lock_irqsave(&einfo->activity_lock, flags);
+ suspend = !(einfo->activity_flag);
+ spin_unlock_irqrestore(&einfo->activity_lock, flags);
+ if (suspend)
+ rc = wdsp_suspend(&einfo->cmpnt);
+ if (rc < 0)
+ pr_err("%s: Could not suspend activity_flag %d, rc %d\n",
+ __func__, einfo->activity_flag, rc);
+ return rc;
+}
+
+static const struct of_device_id spi_match_table[] = {
+ { .compatible = "qcom,glink-spi-xprt" },
+ {},
+};
+
+static struct platform_driver glink_spi_driver = {
+ .probe = glink_spi_probe,
+ .remove = glink_spi_remove,
+ .resume = glink_spi_resume,
+ .suspend = glink_spi_suspend,
+ .driver = {
+ .name = "msm_glink_spi_xprt",
+ .owner = THIS_MODULE,
+ .of_match_table = spi_match_table,
+ },
+};
+
+static int __init glink_spi_xprt_init(void)
+{
+ int rc;
+
+ rc = platform_driver_register(&glink_spi_driver);
+ if (rc)
+ pr_err("%s: glink_spi register failed %d\n", __func__, rc);
+
+ return rc;
+}
+module_init(glink_spi_xprt_init);
+
+static void __exit glink_spi_xprt_exit(void)
+{
+ platform_driver_unregister(&glink_spi_driver);
+}
+module_exit(glink_spi_xprt_exit);
+
+MODULE_DESCRIPTION("MSM G-Link SPI Transport");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/glink_xprt_if.h b/drivers/soc/qcom/glink_xprt_if.h
index 6242e867fe72..f4d5a3b303db 100644
--- a/drivers/soc/qcom/glink_xprt_if.h
+++ b/drivers/soc/qcom/glink_xprt_if.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -28,6 +28,7 @@ enum buf_type {
enum xprt_ids {
SMEM_XPRT_ID = 100,
+ SPIV2_XPRT_ID = SMEM_XPRT_ID,
SMD_TRANS_XPRT_ID = 200,
LLOOP_XPRT_ID = 300,
MOCK_XPRT_HIGH_ID = 390,
@@ -56,6 +57,7 @@ enum xprt_ids {
* @iovec: Pointer to the vector buffer packet.
* @vprovider: Packet-specific virtual buffer provider function.
* @pprovider: Packet-specific physical buffer provider function.
+ * @cookie: Transport-specific cookie
* @pkt_ref: Active references to the packet.
*/
struct glink_core_tx_pkt {
@@ -73,6 +75,7 @@ struct glink_core_tx_pkt {
void *iovec;
void * (*vprovider)(void *iovec, size_t offset, size_t *size);
void * (*pprovider)(void *iovec, size_t offset, size_t *size);
+ void *cookie;
struct rwref_lock pkt_ref;
};
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index ea25ed5d0611..e8a9751fa266 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -1580,6 +1580,14 @@ int icnss_get_soc_info(struct icnss_soc_info *info)
info->v_addr = penv->mem_base_va;
info->p_addr = penv->mem_base_pa;
+ info->chip_id = penv->chip_info.chip_id;
+ info->chip_family = penv->chip_info.chip_family;
+ info->board_id = penv->board_info.board_id;
+ info->soc_id = penv->soc_info.soc_id;
+ info->fw_version = penv->fw_version_info.fw_version;
+ strlcpy(info->fw_build_timestamp,
+ penv->fw_version_info.fw_build_timestamp,
+ QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1);
return 0;
}
diff --git a/drivers/soc/qcom/irq-helper.c b/drivers/soc/qcom/irq-helper.c
new file mode 100644
index 000000000000..2bb71464d165
--- /dev/null
+++ b/drivers/soc/qcom/irq-helper.c
@@ -0,0 +1,180 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/cpu.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <soc/qcom/irq-helper.h>
+
+struct irq_helper {
+ bool enable;
+ bool deploy;
+ uint32_t count;
+ struct kobject kobj;
+ /* spinlock to protect reference count variable 'count' */
+ spinlock_t lock;
+};
+
+struct irq_helper_attr {
+ struct attribute attr;
+ ssize_t (*show)(struct kobject *kobj, struct attribute *attr,
+ char *buf);
+ size_t (*store)(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count);
+};
+
+#define IRQ_HELPER_ATTR(_name, _mode, _show, _store) \
+ struct irq_helper_attr irq_helper_##_name = \
+ __ATTR(_name, _mode, _show, _store)
+
+#define to_irq_helper(kobj) \
+ container_of(kobj, struct irq_helper, kobj)
+
+#define to_irq_helper_attr(_attr) \
+ container_of(_attr, struct irq_helper_attr, attr)
+
+static ssize_t attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct irq_helper_attr *irq_attr = to_irq_helper_attr(attr);
+ ssize_t ret = -EIO;
+
+ if (irq_attr->show)
+ ret = irq_attr->show(kobj, attr, buf);
+
+ return ret;
+}
+
+static const struct sysfs_ops irq_helper_sysfs_ops = {
+ .show = attr_show,
+};
+
+static struct kobj_type irq_helper_ktype = {
+ .sysfs_ops = &irq_helper_sysfs_ops,
+};
+
+static ssize_t show_deploy(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct irq_helper *irq = to_irq_helper(kobj);
+
+ return snprintf(buf, sizeof(irq->deploy), "%u\n", irq->deploy);
+}
+IRQ_HELPER_ATTR(irq_blacklist_on, 0444, show_deploy, NULL);
+
+static struct irq_helper *irq_h;
+
+int irq_blacklist_on(void)
+{
+ bool flag = false;
+
+ if (!irq_h) {
+ pr_err("%s: init function is not called", __func__);
+ return -EPERM;
+ }
+ if (!irq_h->enable) {
+ pr_err("%s: enable bit is not set up", __func__);
+ return -EPERM;
+ }
+ spin_lock(&irq_h->lock);
+ irq_h->count++;
+ if (!irq_h->deploy) {
+ irq_h->deploy = true;
+ flag = true;
+ }
+ spin_unlock(&irq_h->lock);
+ if (flag)
+ sysfs_notify(&irq_h->kobj, NULL, "irq_blacklist_on");
+ return 0;
+}
+EXPORT_SYMBOL(irq_blacklist_on);
+
+int irq_blacklist_off(void)
+{
+ bool flag = false;
+
+ if (!irq_h) {
+ pr_err("%s: init function is not called", __func__);
+ return -EPERM;
+ }
+ if (!irq_h->enable) {
+ pr_err("%s: enable bit is not set up", __func__);
+ return -EPERM;
+ }
+ spin_lock(&irq_h->lock);
+ if (irq_h->count == 0) {
+ pr_err("%s: ref-count is 0, cannot call irq blacklist off.",
+ __func__);
+ spin_unlock(&irq_h->lock);
+ return -EPERM;
+ }
+ irq_h->count--;
+ if (irq_h->count == 0) {
+ irq_h->deploy = false;
+ flag = true;
+ }
+ spin_unlock(&irq_h->lock);
+
+ if (flag)
+ sysfs_notify(&irq_h->kobj, NULL, "irq_blacklist_on");
+ return 0;
+}
+EXPORT_SYMBOL(irq_blacklist_off);
+
+static int __init irq_helper_init(void)
+{
+ int ret;
+
+ irq_h = kzalloc(sizeof(struct irq_helper), GFP_KERNEL);
+ if (!irq_h)
+ return -ENOMEM;
+
+ ret = kobject_init_and_add(&irq_h->kobj, &irq_helper_ktype,
+ kernel_kobj, "%s", "irq_helper");
+ if (ret) {
+ pr_err("%s:Error in creation kobject_add\n", __func__);
+ goto out_free_irq;
+ }
+
+ ret = sysfs_create_file(&irq_h->kobj,
+ &irq_helper_irq_blacklist_on.attr);
+ if (ret) {
+ pr_err("%s:Error in sysfs_create_file\n", __func__);
+ goto out_put_kobj;
+ }
+
+ spin_lock_init(&irq_h->lock);
+ irq_h->count = 0;
+ irq_h->enable = true;
+ return 0;
+out_put_kobj:
+ kobject_put(&irq_h->kobj);
+out_free_irq:
+ kfree(irq_h);
+ return ret;
+}
+module_init(irq_helper_init);
+
+static void __exit irq_helper_exit(void)
+{
+ sysfs_remove_file(&irq_h->kobj, &irq_helper_irq_blacklist_on.attr);
+ kobject_del(&irq_h->kobj);
+ kobject_put(&irq_h->kobj);
+ kfree(irq_h);
+}
+module_exit(irq_helper_exit);
+MODULE_DESCRIPTION("IRQ Helper APIs");
diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c
index e163dd79b8b9..b055234326b6 100644
--- a/drivers/soc/qcom/peripheral-loader.c
+++ b/drivers/soc/qcom/peripheral-loader.c
@@ -189,8 +189,8 @@ int pil_assign_mem_to_subsys(struct pil_desc *desc, phys_addr_t addr,
ret = hyp_assign_phys(addr, size, srcVM, 1, destVM, destVMperm, 1);
if (ret)
- pil_err(desc, "%s: failed for %pa address of size %zx - subsys VMid %d\n",
- __func__, &addr, size, desc->subsys_vmid);
+ pil_err(desc, "%s: failed for %pa address of size %zx - subsys VMid %d rc:%d\n",
+ __func__, &addr, size, desc->subsys_vmid, ret);
return ret;
}
EXPORT_SYMBOL(pil_assign_mem_to_subsys);
@@ -205,8 +205,8 @@ int pil_assign_mem_to_linux(struct pil_desc *desc, phys_addr_t addr,
ret = hyp_assign_phys(addr, size, srcVM, 1, destVM, destVMperm, 1);
if (ret)
- panic("%s: failed for %pa address of size %zx - subsys VMid %d. Fatal error.\n",
- __func__, &addr, size, desc->subsys_vmid);
+ panic("%s: failed for %pa address of size %zx - subsys VMid %d rc:%d\n",
+ __func__, &addr, size, desc->subsys_vmid, ret);
return ret;
}
@@ -222,8 +222,8 @@ int pil_assign_mem_to_subsys_and_linux(struct pil_desc *desc,
ret = hyp_assign_phys(addr, size, srcVM, 1, destVM, destVMperm, 2);
if (ret)
- pil_err(desc, "%s: failed for %pa address of size %zx - subsys VMid %d\n",
- __func__, &addr, size, desc->subsys_vmid);
+ pil_err(desc, "%s: failed for %pa address of size %zx - subsys VMid %d rc:%d\n",
+ __func__, &addr, size, desc->subsys_vmid, ret);
return ret;
}
@@ -642,8 +642,8 @@ static int pil_load_seg(struct pil_desc *desc, struct pil_seg *seg)
seg->filesz, desc->map_fw_mem,
desc->unmap_fw_mem, map_data);
if (ret < 0) {
- pil_err(desc, "Failed to locate blob %s or blob is too big.\n",
- fw_name);
+ pil_err(desc, "Failed to locate blob %s or blob is too big(rc:%d)\n",
+ fw_name, ret);
return ret;
}
@@ -679,7 +679,8 @@ static int pil_load_seg(struct pil_desc *desc, struct pil_seg *seg)
if (desc->ops->verify_blob) {
ret = desc->ops->verify_blob(desc, seg->paddr, seg->sz);
if (ret)
- pil_err(desc, "Blob%u failed verification\n", num);
+ pil_err(desc, "Blob%u failed verification(rc:%d)\n",
+ num, ret);
}
return ret;
@@ -754,7 +755,7 @@ int pil_boot(struct pil_desc *desc)
snprintf(fw_name, sizeof(fw_name), "%s.mdt", desc->fw_name);
ret = request_firmware(&fw, fw_name, desc->dev);
if (ret) {
- pil_err(desc, "Failed to locate %s\n", fw_name);
+ pil_err(desc, "Failed to locate %s(rc:%d)\n", fw_name, ret);
goto out;
}
@@ -792,14 +793,14 @@ int pil_boot(struct pil_desc *desc)
desc->priv->unvoted_flag = 0;
ret = pil_proxy_vote(desc);
if (ret) {
- pil_err(desc, "Failed to proxy vote\n");
+ pil_err(desc, "Failed to proxy vote(rc:%d)\n", ret);
goto release_fw;
}
if (desc->ops->init_image)
ret = desc->ops->init_image(desc, fw->data, fw->size);
if (ret) {
- pil_err(desc, "Invalid firmware metadata\n");
+ pil_err(desc, "Initializing image failed(rc:%d)\n", ret);
goto err_boot;
}
@@ -807,7 +808,7 @@ int pil_boot(struct pil_desc *desc)
ret = desc->ops->mem_setup(desc, priv->region_start,
priv->region_end - priv->region_start);
if (ret) {
- pil_err(desc, "Memory setup error\n");
+ pil_err(desc, "Memory setup error(rc:%d)\n", ret);
goto err_deinit_image;
}
@@ -852,7 +853,7 @@ int pil_boot(struct pil_desc *desc)
ret = desc->ops->auth_and_reset(desc);
if (ret) {
- pil_err(desc, "Failed to bring out of reset\n");
+ pil_err(desc, "Failed to bring out of reset(rc:%d)\n", ret);
goto err_auth_and_reset;
}
pil_info(desc, "Brought out of reset\n");
diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c
index 16c62240ec0a..3873a34c60fb 100644
--- a/drivers/soc/qcom/pil-msa.c
+++ b/drivers/soc/qcom/pil-msa.c
@@ -123,7 +123,8 @@ static int pil_mss_power_up(struct q6v5_data *drv)
if (drv->vreg) {
ret = regulator_enable(drv->vreg);
if (ret)
- dev_err(drv->desc.dev, "Failed to enable modem regulator.\n");
+ dev_err(drv->desc.dev, "Failed to enable modem regulator(rc:%d)\n",
+ ret);
}
if (drv->cxrail_bhs) {
@@ -245,7 +246,7 @@ static int pil_msa_wait_for_mba_ready(struct q6v5_data *drv)
ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status,
status != 0, POLL_INTERVAL_US, val);
if (ret) {
- dev_err(dev, "PBL boot timed out\n");
+ dev_err(dev, "PBL boot timed out (rc:%d)\n", ret);
return ret;
}
if (status != STATUS_PBL_SUCCESS) {
@@ -257,7 +258,7 @@ static int pil_msa_wait_for_mba_ready(struct q6v5_data *drv)
ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
status != 0, POLL_INTERVAL_US, val);
if (ret) {
- dev_err(dev, "MBA boot timed out\n");
+ dev_err(dev, "MBA boot timed out (rc:%d)\n", ret);
return ret;
}
if (status != STATUS_XPU_UNLOCKED &&
@@ -299,7 +300,8 @@ int pil_mss_shutdown(struct pil_desc *pil)
if (!ret)
assert_clamps(pil);
else
- dev_err(pil->dev, "error turning ON AHB clock\n");
+ dev_err(pil->dev, "error turning ON AHB clock(rc:%d)\n",
+ ret);
}
ret = pil_mss_restart_reg(drv, 1);
@@ -328,7 +330,8 @@ int __pil_mss_deinit_image(struct pil_desc *pil, bool err_path)
status == STATUS_MBA_UNLOCKED || status < 0,
POLL_INTERVAL_US, val);
if (ret)
- dev_err(pil->dev, "MBA region unlock timed out\n");
+ dev_err(pil->dev, "MBA region unlock timed out(rc:%d)\n",
+ ret);
else if (status < 0)
dev_err(pil->dev, "MBA unlock returned err status: %d\n",
status);
@@ -367,19 +370,20 @@ int pil_mss_make_proxy_votes(struct pil_desc *pil)
ret = of_property_read_u32(pil->dev->of_node, "vdd_mx-uV", &uv);
if (ret) {
- dev_err(pil->dev, "missing vdd_mx-uV property\n");
+ dev_err(pil->dev, "missing vdd_mx-uV property(rc:%d)\n", ret);
return ret;
}
ret = regulator_set_voltage(drv->vreg_mx, uv, INT_MAX);
if (ret) {
- dev_err(pil->dev, "Failed to request vreg_mx voltage\n");
+ dev_err(pil->dev, "Failed to request vreg_mx voltage(rc:%d)\n",
+ ret);
return ret;
}
ret = regulator_enable(drv->vreg_mx);
if (ret) {
- dev_err(pil->dev, "Failed to enable vreg_mx\n");
+ dev_err(pil->dev, "Failed to enable vreg_mx(rc:%d)\n", ret);
regulator_set_voltage(drv->vreg_mx, 0, INT_MAX);
return ret;
}
@@ -540,8 +544,8 @@ int pil_mss_reset_load_mba(struct pil_desc *pil)
fw_name_p = drv->non_elf_image ? fw_name_legacy : fw_name;
ret = request_firmware(&fw, fw_name_p, pil->dev);
if (ret) {
- dev_err(pil->dev, "Failed to locate %s\n",
- fw_name_p);
+ dev_err(pil->dev, "Failed to locate %s (rc:%d)\n",
+ fw_name_p, ret);
return ret;
}
@@ -611,14 +615,15 @@ int pil_mss_reset_load_mba(struct pil_desc *pil)
ret = pil_assign_mem_to_subsys(pil, drv->mba_dp_phys,
drv->mba_dp_size);
if (ret) {
- pr_err("scm_call to unprotect MBA and DP mem failed\n");
+ pr_err("scm_call to unprotect MBA and DP mem failed(rc:%d)\n",
+ ret);
goto err_mba_data;
}
}
ret = pil_mss_reset(pil);
if (ret) {
- dev_err(pil->dev, "MBA boot failed.\n");
+ dev_err(pil->dev, "MBA boot failed(rc:%d)\n", ret);
goto err_mss_reset;
}
@@ -673,7 +678,8 @@ static int pil_msa_auth_modem_mdt(struct pil_desc *pil, const u8 *metadata,
ret = pil_assign_mem_to_subsys(pil, mdata_phys,
ALIGN(size, SZ_4K));
if (ret) {
- pr_err("scm_call to unprotect modem metadata mem failed\n");
+ pr_err("scm_call to unprotect modem metadata mem failed(rc:%d)\n",
+ ret);
dma_free_attrs(&drv->mba_mem_dev, size, mdata_virt,
mdata_phys, &attrs);
goto fail;
@@ -690,7 +696,8 @@ static int pil_msa_auth_modem_mdt(struct pil_desc *pil, const u8 *metadata,
status == STATUS_META_DATA_AUTH_SUCCESS || status < 0,
POLL_INTERVAL_US, val);
if (ret) {
- dev_err(pil->dev, "MBA authentication of headers timed out\n");
+ dev_err(pil->dev, "MBA authentication of headers timed out(rc:%d)\n",
+ ret);
} else if (status < 0) {
dev_err(pil->dev, "MBA returned error %d for headers\n",
status);
@@ -771,7 +778,8 @@ static int pil_msa_mba_auth(struct pil_desc *pil)
ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
status == STATUS_AUTH_COMPLETE || status < 0, 50, val);
if (ret) {
- dev_err(pil->dev, "MBA authentication of image timed out\n");
+ dev_err(pil->dev, "MBA authentication of image timed out(rc:%d)\n",
+ ret);
} else if (status < 0) {
dev_err(pil->dev, "MBA returned error %d for image\n", status);
ret = -EINVAL;
diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c
index 5c0c1ffa8951..af9cd189cf6d 100644
--- a/drivers/soc/qcom/pil-q6v5-mss.c
+++ b/drivers/soc/qcom/pil-q6v5-mss.c
@@ -291,11 +291,13 @@ static int pil_mss_loadable_init(struct modem_data *drv,
ret = regulator_set_voltage(q6->vreg, VDD_MSS_UV,
MAX_VDD_MSS_UV);
if (ret)
- dev_err(&pdev->dev, "Failed to set vreg voltage.\n");
+ dev_err(&pdev->dev, "Failed to set vreg voltage(rc:%d)\n",
+ ret);
ret = regulator_set_load(q6->vreg, 100000);
if (ret < 0) {
- dev_err(&pdev->dev, "Failed to set vreg mode.\n");
+ dev_err(&pdev->dev, "Failed to set vreg mode(rc:%d)\n",
+ ret);
return ret;
}
}
@@ -330,7 +332,7 @@ static int pil_mss_loadable_init(struct modem_data *drv,
ret = of_property_read_u32(pdev->dev.of_node,
"qcom,pas-id", &drv->pas_id);
if (ret)
- dev_warn(&pdev->dev, "Failed to find the pas_id.\n");
+ dev_info(&pdev->dev, "No pas_id found.\n");
drv->subsys_desc.pil_mss_memsetup =
of_property_read_bool(pdev->dev.of_node, "qcom,pil-mss-memsetup");
diff --git a/drivers/soc/qcom/pil-q6v5.c b/drivers/soc/qcom/pil-q6v5.c
index 3dcfb5abdb23..f8895e8a7b3d 100644
--- a/drivers/soc/qcom/pil-q6v5.c
+++ b/drivers/soc/qcom/pil-q6v5.c
@@ -91,50 +91,53 @@ int pil_q6v5_make_proxy_votes(struct pil_desc *pil)
ret = of_property_read_u32(pil->dev->of_node, "vdd_cx-voltage", &uv);
if (ret) {
- dev_err(pil->dev, "missing vdd_cx-voltage property\n");
+ dev_err(pil->dev, "missing vdd_cx-voltage property(rc:%d)\n",
+ ret);
return ret;
}
ret = clk_prepare_enable(drv->xo);
if (ret) {
- dev_err(pil->dev, "Failed to vote for XO\n");
+ dev_err(pil->dev, "Failed to vote for XO(rc:%d)\n", ret);
goto out;
}
ret = clk_prepare_enable(drv->pnoc_clk);
if (ret) {
- dev_err(pil->dev, "Failed to vote for pnoc\n");
+ dev_err(pil->dev, "Failed to vote for pnoc(rc:%d)\n", ret);
goto err_pnoc_vote;
}
ret = clk_prepare_enable(drv->qdss_clk);
if (ret) {
- dev_err(pil->dev, "Failed to vote for qdss\n");
+ dev_err(pil->dev, "Failed to vote for qdss(rc:%d)\n", ret);
goto err_qdss_vote;
}
ret = regulator_set_voltage(drv->vreg_cx, uv, INT_MAX);
if (ret) {
- dev_err(pil->dev, "Failed to request vdd_cx voltage.\n");
+ dev_err(pil->dev, "Failed to request vdd_cx voltage(rc:%d)\n",
+ ret);
goto err_cx_voltage;
}
ret = regulator_set_load(drv->vreg_cx, 100000);
if (ret < 0) {
- dev_err(pil->dev, "Failed to set vdd_cx mode.\n");
+ dev_err(pil->dev, "Failed to set vdd_cx mode(rc:%d)\n", ret);
goto err_cx_mode;
}
ret = regulator_enable(drv->vreg_cx);
if (ret) {
- dev_err(pil->dev, "Failed to vote for vdd_cx\n");
+ dev_err(pil->dev, "Failed to vote for vdd_cx(rc:%d)\n", ret);
goto err_cx_enable;
}
if (drv->vreg_pll) {
ret = regulator_enable(drv->vreg_pll);
if (ret) {
- dev_err(pil->dev, "Failed to vote for vdd_pll\n");
+ dev_err(pil->dev, "Failed to vote for vdd_pll(rc:%d)\n",
+ ret);
goto err_vreg_pll;
}
}
@@ -165,7 +168,8 @@ void pil_q6v5_remove_proxy_votes(struct pil_desc *pil)
ret = of_property_read_u32(pil->dev->of_node, "vdd_cx-voltage", &uv);
if (ret) {
- dev_err(pil->dev, "missing vdd_cx-voltage property\n");
+ dev_err(pil->dev, "missing vdd_cx-voltage property(rc:%d)\n",
+ ret);
return;
}
@@ -708,13 +712,15 @@ struct q6v5_data *pil_q6v5_init(struct platform_device *pdev)
ret = regulator_set_voltage(drv->vreg_pll, vdd_pll,
vdd_pll);
if (ret) {
- dev_err(&pdev->dev, "Failed to set vdd_pll voltage.\n");
+ dev_err(&pdev->dev, "Failed to set vdd_pll voltage(rc:%d)\n",
+ ret);
return ERR_PTR(ret);
}
ret = regulator_set_load(drv->vreg_pll, 10000);
if (ret < 0) {
- dev_err(&pdev->dev, "Failed to set vdd_pll mode.\n");
+ dev_err(&pdev->dev, "Failed to set vdd_pll mode(rc:%d)\n",
+ ret);
return ERR_PTR(ret);
}
} else
diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
index 4f29923e054c..23e32214756a 100644
--- a/drivers/soc/qcom/socinfo.c
+++ b/drivers/soc/qcom/socinfo.c
@@ -528,10 +528,11 @@ static struct msm_soc_info cpu_of_id[] = {
[270] = {MSM_CPU_8929, "MSM8229"},
[271] = {MSM_CPU_8929, "APQ8029"},
- /* Cobalt ID */
+ /* Cobalt IDs */
[292] = {MSM_CPU_COBALT, "MSMCOBALT"},
+ [319] = {MSM_CPU_COBALT, "APQCOBALT"},
- /* Cobalt ID */
+ /* Hamster ID */
[306] = {MSM_CPU_HAMSTER, "MSMHAMSTER"},
/* falcon ID */
@@ -655,13 +656,14 @@ static uint32_t socinfo_get_foundry_id(void)
: 0;
}
-static uint32_t socinfo_get_serial_number(void)
+uint32_t socinfo_get_serial_number(void)
{
return socinfo ?
(socinfo_format >= SOCINFO_VERSION(0, 10) ?
socinfo->v0_10.serial_number : 0)
: 0;
}
+EXPORT_SYMBOL(socinfo_get_serial_number);
static uint32_t socinfo_get_chip_family(void)
{
@@ -1205,6 +1207,10 @@ static void * __init setup_dummy_socinfo(void)
dummy_socinfo.id = 317;
strlcpy(dummy_socinfo.build_id, "msmfalcon - ",
sizeof(dummy_socinfo.build_id));
+ } else if (early_machine_is_apqcobalt()) {
+ dummy_socinfo.id = 319;
+ strlcpy(dummy_socinfo.build_id, "apqcobalt - ",
+ sizeof(dummy_socinfo.build_id));
}
strlcat(dummy_socinfo.build_id, "Dummy socinfo",
diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c
index e3e43eee3608..56ca6835fc12 100644
--- a/drivers/soc/qcom/subsys-pil-tz.c
+++ b/drivers/soc/qcom/subsys-pil-tz.c
@@ -339,7 +339,8 @@ static int of_read_regs(struct device *dev, struct reg_info **regs_ref,
rc = of_property_read_u32_array(dev->of_node, reg_uV_uA_name,
vdd_uV_uA, len);
if (rc) {
- dev_err(dev, "Failed to read uV/uA values\n");
+ dev_err(dev, "Failed to read uV/uA values(rc:%d)\n",
+ rc);
return rc;
}
@@ -423,7 +424,8 @@ static int enable_regulators(struct pil_tz_data *d, struct device *dev,
rc = regulator_set_voltage(regs[i].reg,
regs[i].uV, INT_MAX);
if (rc) {
- dev_err(dev, "Failed to request voltage.\n");
+ dev_err(dev, "Failed to request voltage(rc:%d)\n",
+ rc);
goto err_voltage;
}
}
@@ -432,7 +434,8 @@ static int enable_regulators(struct pil_tz_data *d, struct device *dev,
rc = regulator_set_load(regs[i].reg,
regs[i].uA);
if (rc < 0) {
- dev_err(dev, "Failed to set regulator mode\n");
+ dev_err(dev, "Failed to set regulator mode(rc:%d)\n",
+ rc);
goto err_mode;
}
}
@@ -442,7 +445,7 @@ static int enable_regulators(struct pil_tz_data *d, struct device *dev,
rc = regulator_enable(regs[i].reg);
if (rc) {
- dev_err(dev, "Regulator enable failed\n");
+ dev_err(dev, "Regulator enable failed(rc:%d)\n", rc);
goto err_enable;
}
}
@@ -499,7 +502,7 @@ static int prepare_enable_clocks(struct device *dev, struct clk **clks,
for (i = 0; i < clk_count; i++) {
rc = clk_prepare_enable(clks[i]);
if (rc) {
- dev_err(dev, "Clock enable failed\n");
+ dev_err(dev, "Clock enable failed(rc:%d)\n", rc);
goto err;
}
}
@@ -541,7 +544,8 @@ static int pil_make_proxy_vote(struct pil_desc *pil)
if (d->bus_client) {
rc = msm_bus_scale_client_update_request(d->bus_client, 1);
if (rc) {
- dev_err(pil->dev, "bandwidth request failed\n");
+ dev_err(pil->dev, "bandwidth request failed(rc:%d)\n",
+ rc);
goto err_bw;
}
} else
@@ -995,7 +999,8 @@ static int pil_tz_driver_probe(struct platform_device *pdev)
rc = of_property_read_u32(pdev->dev.of_node, "qcom,smem-id",
&d->smem_id);
if (rc) {
- dev_err(&pdev->dev, "Failed to get the smem_id.\n");
+ dev_err(&pdev->dev, "Failed to get the smem_id(rc:%d)\n",
+ rc);
return rc;
}
}
@@ -1019,7 +1024,8 @@ static int pil_tz_driver_probe(struct platform_device *pdev)
rc = of_property_read_u32(pdev->dev.of_node, "qcom,pas-id",
&d->pas_id);
if (rc) {
- dev_err(&pdev->dev, "Failed to find the pas_id.\n");
+ dev_err(&pdev->dev, "Failed to find the pas_id(rc:%d)\n",
+ rc);
return rc;
}
scm_pas_init(MSM_BUS_MASTER_CRYPTO_CORE0);
diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c
index 32041c17d88f..0ed8a6533e00 100644
--- a/drivers/soc/qcom/subsystem_restart.c
+++ b/drivers/soc/qcom/subsystem_restart.c
@@ -268,7 +268,8 @@ static ssize_t firmware_name_store(struct device *dev,
pr_info("Changing subsys fw_name to %s\n", buf);
mutex_lock(&track->lock);
- strlcpy(subsys->desc->fw_name, buf, count + 1);
+ strlcpy(subsys->desc->fw_name, buf,
+ min(count + 1, sizeof(subsys->desc->fw_name)));
mutex_unlock(&track->lock);
return orig_count;
}
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index 5e1fd988b22c..b02e48185355 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -97,6 +97,17 @@ enum pmic_arb_cmd_op_code {
/* interrupt enable bit */
#define SPMI_PIC_ACC_ENABLE_BIT BIT(0)
+#define HWIRQ(slave_id, periph_id, irq_id, apid) \
+ ((((slave_id) & 0xF) << 28) | \
+ (((periph_id) & 0xFF) << 20) | \
+ (((irq_id) & 0x7) << 16) | \
+ (((apid) & 0x1FF) << 0))
+
+#define HWIRQ_SID(hwirq) (((hwirq) >> 28) & 0xF)
+#define HWIRQ_PER(hwirq) (((hwirq) >> 20) & 0xFF)
+#define HWIRQ_IRQ(hwirq) (((hwirq) >> 16) & 0x7)
+#define HWIRQ_APID(hwirq) (((hwirq) >> 0) & 0x1FF)
+
struct pmic_arb_ver_ops;
struct apid_data {
@@ -172,7 +183,7 @@ struct spmi_pmic_arb {
struct pmic_arb_ver_ops {
const char *ver_str;
int (*ppid_to_apid)(struct spmi_pmic_arb *pa, u8 sid, u16 addr,
- u8 *apid);
+ u16 *apid);
int (*mode)(struct spmi_pmic_arb *dev, u8 sid, u16 addr,
mode_t *mode);
/* spmi commands (read_cmd, write_cmd, cmd) functionality */
@@ -181,10 +192,10 @@ struct pmic_arb_ver_ops {
u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc);
int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid);
/* Interrupts controller functionality (offset of PIC registers) */
- u32 (*owner_acc_status)(u8 m, u8 n);
- u32 (*acc_enable)(u8 n);
- u32 (*irq_status)(u8 n);
- u32 (*irq_clear)(u8 n);
+ u32 (*owner_acc_status)(u8 m, u16 n);
+ u32 (*acc_enable)(u16 n);
+ u32 (*irq_status)(u16 n);
+ u32 (*irq_clear)(u16 n);
};
static inline void pmic_arb_base_write(struct spmi_pmic_arb *pa,
@@ -466,8 +477,8 @@ static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf,
size_t len)
{
struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d);
- u8 sid = d->hwirq >> 24;
- u8 per = d->hwirq >> 16;
+ u8 sid = HWIRQ_SID(d->hwirq);
+ u8 per = HWIRQ_PER(d->hwirq);
if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid,
(per << 8) + reg, buf, len))
@@ -479,8 +490,8 @@ static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf,
static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len)
{
struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d);
- u8 sid = d->hwirq >> 24;
- u8 per = d->hwirq >> 16;
+ u8 sid = HWIRQ_SID(d->hwirq);
+ u8 per = HWIRQ_PER(d->hwirq);
if (pmic_arb_read_cmd(pa->spmic, SPMI_CMD_EXT_READL, sid,
(per << 8) + reg, buf, len))
@@ -489,7 +500,7 @@ static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len)
d->irq);
}
-static void cleanup_irq(struct spmi_pmic_arb *pa, u8 apid, int id)
+static void cleanup_irq(struct spmi_pmic_arb *pa, u16 apid, int id)
{
u16 ppid = pa->apid_data[apid].ppid;
u8 sid = ppid >> 8;
@@ -514,20 +525,19 @@ static void cleanup_irq(struct spmi_pmic_arb *pa, u8 apid, int id)
irq_mask, ppid);
}
-static void periph_interrupt(struct spmi_pmic_arb *pa, u8 apid)
+static void periph_interrupt(struct spmi_pmic_arb *pa, u16 apid)
{
unsigned int irq;
u32 status;
int id;
+ u8 sid = (pa->apid_data[apid].ppid >> 8) & 0xF;
+ u8 per = pa->apid_data[apid].ppid & 0xFF;
status = readl_relaxed(pa->intr + pa->ver_ops->irq_status(apid));
while (status) {
id = ffs(status) - 1;
status &= ~BIT(id);
- irq = irq_find_mapping(pa->domain,
- pa->apid_data[apid].ppid << 16
- | id << 8
- | apid);
+ irq = irq_find_mapping(pa->domain, HWIRQ(sid, per, id, apid));
if (irq == 0) {
cleanup_irq(pa, apid, id);
continue;
@@ -568,8 +578,8 @@ static void pmic_arb_chained_irq(struct irq_desc *desc)
static void qpnpint_irq_ack(struct irq_data *d)
{
struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d);
- u8 irq = d->hwirq >> 8;
- u8 apid = d->hwirq;
+ u8 irq = HWIRQ_IRQ(d->hwirq);
+ u16 apid = HWIRQ_APID(d->hwirq);
u8 data;
writel_relaxed(BIT(irq), pa->intr + pa->ver_ops->irq_clear(apid));
@@ -580,7 +590,7 @@ static void qpnpint_irq_ack(struct irq_data *d)
static void qpnpint_irq_mask(struct irq_data *d)
{
- u8 irq = d->hwirq >> 8;
+ u8 irq = HWIRQ_IRQ(d->hwirq);
u8 data = BIT(irq);
qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1);
@@ -589,8 +599,8 @@ static void qpnpint_irq_mask(struct irq_data *d)
static void qpnpint_irq_unmask(struct irq_data *d)
{
struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d);
- u8 irq = d->hwirq >> 8;
- u8 apid = d->hwirq;
+ u8 irq = HWIRQ_IRQ(d->hwirq);
+ u16 apid = HWIRQ_APID(d->hwirq);
u8 buf[2];
writel_relaxed(SPMI_PIC_ACC_ENABLE_BIT,
@@ -612,7 +622,7 @@ static void qpnpint_irq_unmask(struct irq_data *d)
static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
{
struct spmi_pmic_arb_qpnpint_type type;
- u8 irq = d->hwirq >> 8;
+ u8 irq = HWIRQ_IRQ(d->hwirq);
u8 bit_mask_irq = BIT(irq);
qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type));
@@ -649,7 +659,7 @@ static int qpnpint_get_irqchip_state(struct irq_data *d,
enum irqchip_irq_state which,
bool *state)
{
- u8 irq = d->hwirq >> 8;
+ u8 irq = HWIRQ_IRQ(d->hwirq);
u8 status = 0;
if (which != IRQCHIP_STATE_LINE_LEVEL)
@@ -681,7 +691,7 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
{
struct spmi_pmic_arb *pa = d->host_data;
int rc;
- u8 apid;
+ u16 apid;
dev_dbg(&pa->spmic->dev,
"intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
@@ -709,10 +719,7 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
if (apid < pa->min_apid)
pa->min_apid = apid;
- *out_hwirq = (intspec[0] & 0xF) << 24
- | (intspec[1] & 0xFF) << 16
- | (intspec[2] & 0x7) << 8
- | apid;
+ *out_hwirq = HWIRQ(intspec[0], intspec[1], intspec[2], apid);
*out_type = intspec[3] & IRQ_TYPE_SENSE_MASK;
dev_dbg(&pa->spmic->dev, "out_hwirq = %lu\n", *out_hwirq);
@@ -735,7 +742,7 @@ static int qpnpint_irq_domain_map(struct irq_domain *d,
}
static int
-pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u8 *apid)
+pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid)
{
u16 ppid = sid << 8 | ((addr >> 8) & 0xFF);
u32 *mapping_table = pa->mapping_table;
@@ -834,7 +841,7 @@ static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pa, u16 ppid)
}
static int
-pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u8 *apid)
+pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid)
{
u16 ppid = (sid << 8) | (addr >> 8);
u16 apid_valid;
@@ -852,7 +859,7 @@ pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u8 *apid)
static int
pmic_arb_mode_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, mode_t *mode)
{
- u8 apid;
+ u16 apid;
int rc;
rc = pmic_arb_ppid_to_apid_v2(pa, sid, addr, &apid);
@@ -871,7 +878,7 @@ pmic_arb_mode_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, mode_t *mode)
static int
pmic_arb_offset_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u32 *offset)
{
- u8 apid;
+ u16 apid;
int rc;
rc = pmic_arb_ppid_to_apid_v2(pa, sid, addr, &apid);
@@ -892,47 +899,47 @@ static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc)
return (opc << 27) | ((addr & 0xff) << 4) | (bc & 0x7);
}
-static u32 pmic_arb_owner_acc_status_v1(u8 m, u8 n)
+static u32 pmic_arb_owner_acc_status_v1(u8 m, u16 n)
{
return 0x20 * m + 0x4 * n;
}
-static u32 pmic_arb_owner_acc_status_v2(u8 m, u8 n)
+static u32 pmic_arb_owner_acc_status_v2(u8 m, u16 n)
{
return 0x100000 + 0x1000 * m + 0x4 * n;
}
-static u32 pmic_arb_owner_acc_status_v3(u8 m, u8 n)
+static u32 pmic_arb_owner_acc_status_v3(u8 m, u16 n)
{
return 0x200000 + 0x1000 * m + 0x4 * n;
}
-static u32 pmic_arb_acc_enable_v1(u8 n)
+static u32 pmic_arb_acc_enable_v1(u16 n)
{
return 0x200 + 0x4 * n;
}
-static u32 pmic_arb_acc_enable_v2(u8 n)
+static u32 pmic_arb_acc_enable_v2(u16 n)
{
return 0x1000 * n;
}
-static u32 pmic_arb_irq_status_v1(u8 n)
+static u32 pmic_arb_irq_status_v1(u16 n)
{
return 0x600 + 0x4 * n;
}
-static u32 pmic_arb_irq_status_v2(u8 n)
+static u32 pmic_arb_irq_status_v2(u16 n)
{
return 0x4 + 0x1000 * n;
}
-static u32 pmic_arb_irq_clear_v1(u8 n)
+static u32 pmic_arb_irq_clear_v1(u16 n)
{
return 0xA00 + 0x4 * n;
}
-static u32 pmic_arb_irq_clear_v2(u8 n)
+static u32 pmic_arb_irq_clear_v2(u16 n)
{
return 0x8 + 0x1000 * n;
}
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
index e9237202e79f..a8c8e120c348 100755
--- a/drivers/staging/android/ion/ion.c
+++ b/drivers/staging/android/ion/ion.c
@@ -257,9 +257,10 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
* memory coming from the heaps is ready for dma, ie if it has a
* cached mapping that mapping has been invalidated
*/
- for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i)
+ for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) {
sg_dma_address(sg) = sg_phys(sg);
-
+ sg_dma_len(sg) = sg->length;
+ }
mutex_lock(&dev->buffer_lock);
ion_buffer_add(dev, buffer);
mutex_unlock(&dev->buffer_lock);
diff --git a/drivers/staging/android/ion/ion_test.c b/drivers/staging/android/ion/ion_test.c
index b8dcf5a26cc4..58d46893e5ff 100644
--- a/drivers/staging/android/ion/ion_test.c
+++ b/drivers/staging/android/ion/ion_test.c
@@ -285,8 +285,8 @@ static int __init ion_test_init(void)
{
ion_test_pdev = platform_device_register_simple("ion-test",
-1, NULL, 0);
- if (!ion_test_pdev)
- return -ENODEV;
+ if (IS_ERR(ion_test_pdev))
+ return PTR_ERR(ion_test_pdev);
return platform_driver_probe(&ion_test_platform_driver, ion_test_probe);
}
diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c
index 6cc304a4c59b..27fbf1a81097 100644
--- a/drivers/staging/comedi/drivers/ni_mio_common.c
+++ b/drivers/staging/comedi/drivers/ni_mio_common.c
@@ -246,24 +246,24 @@ static void ni_writel(struct comedi_device *dev, uint32_t data, int reg)
{
if (dev->mmio)
writel(data, dev->mmio + reg);
-
- outl(data, dev->iobase + reg);
+ else
+ outl(data, dev->iobase + reg);
}
static void ni_writew(struct comedi_device *dev, uint16_t data, int reg)
{
if (dev->mmio)
writew(data, dev->mmio + reg);
-
- outw(data, dev->iobase + reg);
+ else
+ outw(data, dev->iobase + reg);
}
static void ni_writeb(struct comedi_device *dev, uint8_t data, int reg)
{
if (dev->mmio)
writeb(data, dev->mmio + reg);
-
- outb(data, dev->iobase + reg);
+ else
+ outb(data, dev->iobase + reg);
}
static uint32_t ni_readl(struct comedi_device *dev, int reg)
diff --git a/drivers/staging/comedi/drivers/ni_tiocmd.c b/drivers/staging/comedi/drivers/ni_tiocmd.c
index 437f723bb34d..823e47910004 100644
--- a/drivers/staging/comedi/drivers/ni_tiocmd.c
+++ b/drivers/staging/comedi/drivers/ni_tiocmd.c
@@ -92,7 +92,7 @@ static int ni_tio_input_inttrig(struct comedi_device *dev,
unsigned long flags;
int ret = 0;
- if (trig_num != cmd->start_src)
+ if (trig_num != cmd->start_arg)
return -EINVAL;
spin_lock_irqsave(&counter->lock, flags);
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 94f4ffac723f..d151bc3d6971 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -2618,8 +2618,6 @@ void target_wait_for_sess_cmds(struct se_session *se_sess)
list_for_each_entry_safe(se_cmd, tmp_cmd,
&se_sess->sess_wait_list, se_cmd_list) {
- list_del_init(&se_cmd->se_cmd_list);
-
pr_debug("Waiting for se_cmd: %p t_state: %d, fabric state:"
" %d\n", se_cmd, se_cmd->t_state,
se_cmd->se_tfo->get_cmd_state(se_cmd));
diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c
index 73d7435d2eb8..97ab02dfc753 100644
--- a/drivers/thermal/msm-tsens.c
+++ b/drivers/thermal/msm-tsens.c
@@ -831,6 +831,7 @@ struct tsens_tm_device {
bool prev_reading_avail;
bool calibration_less_mode;
bool tsens_local_init;
+ bool gain_offset_programmed;
int tsens_factor;
uint32_t tsens_num_sensor;
int tsens_irq;
@@ -5341,17 +5342,25 @@ static int get_device_tree_data(struct platform_device *pdev,
return -ENODEV;
}
- tsens_slope_data = devm_kzalloc(&pdev->dev,
+ /* TSENS calibration region */
+ tmdev->res_calib_mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "tsens_eeprom_physical");
+ if (!tmdev->res_calib_mem) {
+ pr_debug("Using controller programmed gain and offset\n");
+ tmdev->gain_offset_programmed = true;
+ } else {
+ tsens_slope_data = devm_kzalloc(&pdev->dev,
tsens_num_sensors * sizeof(u32), GFP_KERNEL);
- if (!tsens_slope_data)
- return -ENOMEM;
+ if (!tsens_slope_data)
+ return -ENOMEM;
- rc = of_property_read_u32_array(of_node,
- "qcom,slope", tsens_slope_data, tsens_num_sensors);
- if (rc) {
- dev_err(&pdev->dev, "invalid or missing property: tsens-slope\n");
- return rc;
- };
+ rc = of_property_read_u32_array(of_node,
+ "qcom,slope", tsens_slope_data, tsens_num_sensors);
+ if (rc) {
+ dev_err(&pdev->dev, "missing property: tsens-slope\n");
+ return rc;
+ };
+ }
if (!of_match_node(tsens_match, of_node)) {
pr_err("Need to read SoC specific fuse map\n");
@@ -5364,9 +5373,13 @@ static int get_device_tree_data(struct platform_device *pdev,
return -ENODEV;
}
- for (i = 0; i < tsens_num_sensors; i++)
- tmdev->sensor[i].slope_mul_tsens_factor = tsens_slope_data[i];
- tmdev->tsens_factor = TSENS_SLOPE_FACTOR;
+ if (!tmdev->gain_offset_programmed) {
+ for (i = 0; i < tsens_num_sensors; i++)
+ tmdev->sensor[i].slope_mul_tsens_factor =
+ tsens_slope_data[i];
+ tmdev->tsens_factor = TSENS_SLOPE_FACTOR;
+ }
+
tmdev->tsens_num_sensor = tsens_num_sensors;
tmdev->calibration_less_mode = of_property_read_bool(of_node,
"qcom,calibration-less-mode");
@@ -5536,24 +5549,17 @@ static int get_device_tree_data(struct platform_device *pdev,
goto fail_unmap_tsens_region;
}
- /* TSENS calibration region */
- tmdev->res_calib_mem = platform_get_resource_byname(pdev,
- IORESOURCE_MEM, "tsens_eeprom_physical");
- if (!tmdev->res_calib_mem) {
- pr_err("Could not get qfprom physical address resource\n");
- rc = -EINVAL;
- goto fail_unmap_tsens;
- }
-
- tmdev->calib_len = tmdev->res_calib_mem->end -
+ if (!tmdev->gain_offset_programmed) {
+ tmdev->calib_len = tmdev->res_calib_mem->end -
tmdev->res_calib_mem->start + 1;
- tmdev->tsens_calib_addr = ioremap(tmdev->res_calib_mem->start,
+ tmdev->tsens_calib_addr = ioremap(tmdev->res_calib_mem->start,
tmdev->calib_len);
- if (!tmdev->tsens_calib_addr) {
- pr_err("Failed to IO map EEPROM registers.\n");
- rc = -EINVAL;
- goto fail_unmap_tsens;
+ if (!tmdev->tsens_calib_addr) {
+ pr_err("Failed to IO map EEPROM registers.\n");
+ rc = -EINVAL;
+ goto fail_unmap_tsens;
+ }
}
return 0;
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index df8c82aa2dd9..e03d3b41c25b 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -39,7 +39,6 @@
#include <linux/kthread.h>
#include <net/netlink.h>
#include <net/genetlink.h>
-#include <linux/suspend.h>
#define CREATE_TRACE_POINTS
#include <trace/events/thermal.h>
@@ -64,8 +63,6 @@ static LIST_HEAD(thermal_governor_list);
static DEFINE_MUTEX(thermal_list_lock);
static DEFINE_MUTEX(thermal_governor_lock);
-static atomic_t in_suspend;
-
static struct thermal_governor *def_governor;
static struct thermal_governor *__find_governor(const char *name)
@@ -851,6 +848,10 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
{
enum thermal_trip_type type;
+ /* Ignore disabled trip points */
+ if (test_bit(trip, &tz->trips_disabled))
+ return;
+
tz->ops->get_trip_type(tz, trip, &type);
if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT ||
@@ -957,9 +958,6 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
{
int count;
- if (atomic_read(&in_suspend))
- return;
-
if (!tz->ops->get_temp)
return;
@@ -2247,6 +2245,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
{
struct thermal_zone_device *tz;
enum thermal_trip_type trip_type;
+ int trip_temp;
int result;
int count;
int passive = 0;
@@ -2318,9 +2317,15 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
goto unregister;
for (count = 0; count < trips; count++) {
- tz->ops->get_trip_type(tz, count, &trip_type);
+ if (tz->ops->get_trip_type(tz, count, &trip_type))
+ set_bit(count, &tz->trips_disabled);
if (trip_type == THERMAL_TRIP_PASSIVE)
passive = 1;
+ if (tz->ops->get_trip_temp(tz, count, &trip_temp))
+ set_bit(count, &tz->trips_disabled);
+ /* Check for bogus trip points */
+ if (trip_temp == 0)
+ set_bit(count, &tz->trips_disabled);
}
if (!passive) {
@@ -2630,36 +2635,6 @@ static void thermal_unregister_governors(void)
thermal_gov_power_allocator_unregister();
}
-static int thermal_pm_notify(struct notifier_block *nb,
- unsigned long mode, void *_unused)
-{
- struct thermal_zone_device *tz;
-
- switch (mode) {
- case PM_HIBERNATION_PREPARE:
- case PM_RESTORE_PREPARE:
- case PM_SUSPEND_PREPARE:
- atomic_set(&in_suspend, 1);
- break;
- case PM_POST_HIBERNATION:
- case PM_POST_RESTORE:
- case PM_POST_SUSPEND:
- atomic_set(&in_suspend, 0);
- list_for_each_entry(tz, &thermal_tz_list, node) {
- thermal_zone_device_reset(tz);
- thermal_zone_device_update(tz);
- }
- break;
- default:
- break;
- }
- return 0;
-}
-
-static struct notifier_block thermal_pm_nb = {
- .notifier_call = thermal_pm_notify,
-};
-
static int __init thermal_init(void)
{
int result;
@@ -2680,11 +2655,6 @@ static int __init thermal_init(void)
if (result)
goto exit_netlink;
- result = register_pm_notifier(&thermal_pm_nb);
- if (result)
- pr_warn("Thermal: Can not register suspend notifier, return %d\n",
- result);
-
return 0;
exit_netlink:
@@ -2704,7 +2674,6 @@ error:
static void __exit thermal_exit(void)
{
- unregister_pm_notifier(&thermal_pm_nb);
of_thermal_destroy_zones();
genetlink_exit();
class_unregister(&thermal_class);
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 52d82d2ac726..56ccbcefdd85 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -713,22 +713,16 @@ static int size_fifo(struct uart_8250_port *up)
*/
static unsigned int autoconfig_read_divisor_id(struct uart_8250_port *p)
{
- unsigned char old_dll, old_dlm, old_lcr;
- unsigned int id;
+ unsigned char old_lcr;
+ unsigned int id, old_dl;
old_lcr = serial_in(p, UART_LCR);
serial_out(p, UART_LCR, UART_LCR_CONF_MODE_A);
+ old_dl = serial_dl_read(p);
+ serial_dl_write(p, 0);
+ id = serial_dl_read(p);
+ serial_dl_write(p, old_dl);
- old_dll = serial_in(p, UART_DLL);
- old_dlm = serial_in(p, UART_DLM);
-
- serial_out(p, UART_DLL, 0);
- serial_out(p, UART_DLM, 0);
-
- id = serial_in(p, UART_DLL) | serial_in(p, UART_DLM) << 8;
-
- serial_out(p, UART_DLL, old_dll);
- serial_out(p, UART_DLM, old_dlm);
serial_out(p, UART_LCR, old_lcr);
return id;
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 6843711774b2..d4ece0e56954 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -70,9 +70,11 @@
#define UART_SPS_CONS_PERIPHERAL 0
#define UART_SPS_PROD_PERIPHERAL 1
-#define IPC_MSM_HS_LOG_PAGES 5
+#define IPC_MSM_HS_LOG_STATE_PAGES 2
+#define IPC_MSM_HS_LOG_USER_PAGES 2
+#define IPC_MSM_HS_LOG_DATA_PAGES 3
#define UART_DMA_DESC_NR 8
-#define BUF_DUMP_SIZE 20
+#define BUF_DUMP_SIZE 32
/* If the debug_mask gets set to FATAL_LEV,
* a fatal error has happened and further IPC logging
@@ -121,6 +123,11 @@ enum {
} \
} while (0)
+#define LOG_USR_MSG(ctx, x...) do { \
+ if (ctx) \
+ ipc_log_string(ctx, x); \
+} while (0)
+
/*
* There are 3 different kind of UART Core available on MSM.
* High Speed UART (i.e. Legacy HSUART), GSBI based HSUART
@@ -164,6 +171,7 @@ struct msm_hs_tx {
struct task_struct *task;
struct msm_hs_sps_ep_conn_data cons;
struct timer_list tx_timeout_timer;
+ void *ipc_tx_ctxt;
};
struct msm_hs_rx {
@@ -181,6 +189,7 @@ struct msm_hs_rx {
unsigned long pending_flag;
int rx_inx;
struct sps_iovec iovec[UART_DMA_DESC_NR]; /* track descriptors */
+ void *ipc_rx_ctxt;
};
enum buffer_states {
NONE_PENDING = 0x0,
@@ -214,7 +223,7 @@ struct msm_hs_port {
struct clk *pclk;
struct msm_hs_tx tx;
struct msm_hs_rx rx;
- atomic_t clk_count;
+ atomic_t resource_count;
struct msm_hs_wakeup wakeup;
struct dentry *loopback_dir;
@@ -248,6 +257,7 @@ struct msm_hs_port {
bool obs; /* out of band sleep flag */
atomic_t client_req_state;
void *ipc_msm_hs_log_ctxt;
+ void *ipc_msm_hs_pwr_ctxt;
int ipc_debug_mask;
};
@@ -315,7 +325,7 @@ static int msm_hs_ioctl(struct uart_port *uport, unsigned int cmd,
break;
}
default: {
- MSM_HS_DBG("%s():Unknown cmd specified: cmd=%d\n", __func__,
+ MSM_HS_INFO("%s():Unknown cmd specified: cmd=%d\n", __func__,
cmd);
ret = -ENOIOCTLCMD;
break;
@@ -380,7 +390,7 @@ static void msm_hs_clk_bus_unvote(struct msm_hs_port *msm_uport)
static void msm_hs_resource_unvote(struct msm_hs_port *msm_uport)
{
struct uart_port *uport = &(msm_uport->uport);
- int rc = atomic_read(&msm_uport->clk_count);
+ int rc = atomic_read(&msm_uport->resource_count);
MSM_HS_DBG("%s(): power usage count %d", __func__, rc);
if (rc <= 0) {
@@ -388,7 +398,7 @@ static void msm_hs_resource_unvote(struct msm_hs_port *msm_uport)
WARN_ON(1);
return;
}
- atomic_dec(&msm_uport->clk_count);
+ atomic_dec(&msm_uport->resource_count);
pm_runtime_mark_last_busy(uport->dev);
pm_runtime_put_autosuspend(uport->dev);
}
@@ -400,12 +410,12 @@ static void msm_hs_resource_vote(struct msm_hs_port *msm_uport)
struct uart_port *uport = &(msm_uport->uport);
ret = pm_runtime_get_sync(uport->dev);
if (ret < 0 || msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
- MSM_HS_WARN("%s(): %p runtime PM callback not invoked(%d)",
- __func__, uport->dev, ret);
+ MSM_HS_WARN("%s:%s runtime callback not invoked ret:%d st:%d",
+ __func__, dev_name(uport->dev), ret,
+ msm_uport->pm_state);
msm_hs_pm_resume(uport->dev);
}
-
- atomic_inc(&msm_uport->clk_count);
+ atomic_inc(&msm_uport->resource_count);
}
/* Check if the uport line number matches with user id stored in pdata.
@@ -567,23 +577,21 @@ static int sps_rx_disconnect(struct sps_pipe *sps_pipe_handler)
return sps_disconnect(sps_pipe_handler);
}
-static void hex_dump_ipc(struct msm_hs_port *msm_uport,
- char *prefix, char *string, int size)
+static void hex_dump_ipc(struct msm_hs_port *msm_uport, void *ipc_ctx,
+ char *prefix, char *string, u64 addr, int size)
+
{
- unsigned char linebuf[512];
- unsigned char firstbuf[40], lastbuf[40];
+ char buf[(BUF_DUMP_SIZE * 3) + 2];
+ int len = 0;
- if ((msm_uport->ipc_debug_mask != DBG_LEV) && (size > BUF_DUMP_SIZE)) {
- hex_dump_to_buffer(string, 10, 16, 1,
- firstbuf, sizeof(firstbuf), 1);
- hex_dump_to_buffer(string + (size - 10), 10, 16, 1,
- lastbuf, sizeof(lastbuf), 1);
- MSM_HS_INFO("%s : %s...%s", prefix, firstbuf, lastbuf);
- } else {
- hex_dump_to_buffer(string, size, 16, 1,
- linebuf, sizeof(linebuf), 1);
- MSM_HS_INFO("%s : %s", prefix, linebuf);
- }
+ len = min(size, BUF_DUMP_SIZE);
+ /*
+ * Print upto 32 data bytes, 32 bytes per line, 1 byte at a time and
+ * don't include the ASCII text at the end of the buffer.
+ */
+ hex_dump_to_buffer(string, len, 32, 1, buf, sizeof(buf), false);
+ ipc_log_string(ipc_ctx, "%s[0x%.10x:%d] : %s", prefix,
+ (unsigned int)addr, size, buf);
}
/*
@@ -594,8 +602,8 @@ static void dump_uart_hs_registers(struct msm_hs_port *msm_uport)
struct uart_port *uport = &(msm_uport->uport);
if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
- MSM_HS_INFO("%s:Failed clocks are off, clk_count %d",
- __func__, atomic_read(&msm_uport->clk_count));
+ MSM_HS_INFO("%s:Failed clocks are off, resource_count %d",
+ __func__, atomic_read(&msm_uport->resource_count));
return;
}
@@ -757,8 +765,10 @@ static int msm_hs_spsconnect_tx(struct msm_hs_port *msm_uport)
unsigned long flags;
unsigned int data;
- if (tx->flush != FLUSH_SHUTDOWN)
+ if (tx->flush != FLUSH_SHUTDOWN) {
+ MSM_HS_ERR("%s:Invalid flush state:%d\n", __func__, tx->flush);
return 0;
+ }
/* Establish connection between peripheral and memory endpoint */
ret = sps_connect(sps_pipe_handle, sps_config);
@@ -1100,7 +1110,6 @@ static void msm_hs_set_termios(struct uart_port *uport,
mutex_lock(&msm_uport->mtx);
msm_hs_write(uport, UART_DM_IMR, 0);
- MSM_HS_DBG("Entering %s\n", __func__);
msm_hs_disable_flow_control(uport, true);
/*
@@ -1214,10 +1223,10 @@ static void msm_hs_set_termios(struct uart_port *uport,
msm_uport->flow_control = true;
}
msm_hs_write(uport, UART_DM_MR1, data);
+ MSM_HS_INFO("%s: Cflags 0x%x Baud %u\n", __func__, c_cflag, bps);
mutex_unlock(&msm_uport->mtx);
- MSM_HS_DBG("Exit %s\n", __func__);
msm_hs_resource_unvote(msm_uport);
}
@@ -1400,9 +1409,6 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport)
if (tx_count > left)
tx_count = left;
- MSM_HS_INFO("%s(): [UART_TX]<%d>\n", __func__, tx_count);
- hex_dump_ipc(msm_uport, "HSUART write: ",
- &tx_buf->buf[tx_buf->tail], tx_count);
src_addr = tx->dma_base + tx_buf->tail;
/* Mask the src_addr to align on a cache
@@ -1415,6 +1421,8 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport)
tx->tx_count = tx_count;
+ hex_dump_ipc(msm_uport, tx->ipc_tx_ctxt, "Tx",
+ &tx_buf->buf[tx_buf->tail], (u64)src_addr, tx_count);
sps_pipe_handle = tx->cons.pipe_handle;
/* Queue transfer request to SPS */
ret = sps_transfer_one(sps_pipe_handle, src_addr, tx_count,
@@ -1717,12 +1725,11 @@ static void msm_serial_hs_rx_work(struct kthread_work *work)
goto out;
rx_count = msm_uport->rx.iovec[msm_uport->rx.rx_inx].size;
-
- MSM_HS_INFO("%s():[UART_RX]<%d>\n", __func__, rx_count);
- hex_dump_ipc(msm_uport, "HSUART Read: ",
- (msm_uport->rx.buffer +
- (msm_uport->rx.rx_inx * UARTDM_RX_BUF_SIZE)),
- rx_count);
+ hex_dump_ipc(msm_uport, rx->ipc_rx_ctxt, "Rx",
+ (msm_uport->rx.buffer +
+ (msm_uport->rx.rx_inx * UARTDM_RX_BUF_SIZE)),
+ msm_uport->rx.iovec[msm_uport->rx.rx_inx].addr,
+ rx_count);
/*
* We are in a spin locked context, spin lock taken at
@@ -1733,7 +1740,7 @@ static void msm_serial_hs_rx_work(struct kthread_work *work)
&msm_uport->rx.pending_flag) &&
!test_bit(msm_uport->rx.rx_inx,
&msm_uport->rx.queued_flag))
- MSM_HS_ERR("RX INX not set");
+ MSM_HS_ERR("%s: RX INX not set", __func__);
else if (test_bit(msm_uport->rx.rx_inx,
&msm_uport->rx.pending_flag) &&
!test_bit(msm_uport->rx.rx_inx,
@@ -1748,14 +1755,14 @@ static void msm_serial_hs_rx_work(struct kthread_work *work)
rx_count);
if (retval != rx_count) {
- MSM_HS_DBG("%s(): ret %d rx_count %d",
+ MSM_HS_INFO("%s(): ret %d rx_count %d",
__func__, retval, rx_count);
msm_uport->rx.buffer_pending |=
CHARS_NORMAL | retval << 5 |
(rx_count - retval) << 16;
}
} else
- MSM_HS_ERR("Error in inx %d",
+ MSM_HS_ERR("%s: Error in inx %d", __func__,
msm_uport->rx.rx_inx);
}
@@ -1778,7 +1785,7 @@ static void msm_serial_hs_rx_work(struct kthread_work *work)
}
out:
if (msm_uport->rx.buffer_pending) {
- MSM_HS_WARN("tty buffer exhausted. Stalling\n");
+ MSM_HS_WARN("%s: tty buffer exhausted. Stalling\n", __func__);
schedule_delayed_work(&msm_uport->rx.flip_insert_work
, msecs_to_jiffies(RETRY_TIMEOUT));
}
@@ -1796,7 +1803,7 @@ static void msm_hs_start_tx_locked(struct uart_port *uport)
/* Bail if transfer in progress */
if (tx->flush < FLUSH_STOP || tx->dma_in_flight) {
- MSM_HS_DBG("%s(): retry, flush %d, dma_in_flight %d\n",
+ MSM_HS_INFO("%s(): retry, flush %d, dma_in_flight %d\n",
__func__, tx->flush, tx->dma_in_flight);
return;
}
@@ -1826,11 +1833,9 @@ static void msm_hs_sps_tx_callback(struct sps_event_notify *notify)
notify->data.transfer.iovec.addr);
msm_uport->notify = *notify;
- MSM_HS_DBG("%s: ev_id=%d, addr=0x%pa, size=0x%x, flags=0x%x, line=%d\n",
- __func__, notify->event_id, &addr,
- notify->data.transfer.iovec.size,
- notify->data.transfer.iovec.flags,
- msm_uport->uport.line);
+ MSM_HS_INFO("tx_cb: addr=0x%pa, size=0x%x, flags=0x%x\n",
+ &addr, notify->data.transfer.iovec.size,
+ notify->data.transfer.iovec.flags);
del_timer(&msm_uport->tx.tx_timeout_timer);
MSM_HS_DBG("%s(): Queue kthread work", __func__);
@@ -1931,9 +1936,8 @@ static void msm_hs_sps_rx_callback(struct sps_event_notify *notify)
uport = &(msm_uport->uport);
msm_uport->notify = *notify;
- MSM_HS_DBG("\n%s: sps ev_id=%d, addr=0x%pa, size=0x%x, flags=0x%x\n",
- __func__, notify->event_id, &addr,
- notify->data.transfer.iovec.size,
+ MSM_HS_INFO("rx_cb: addr=0x%pa, size=0x%x, flags=0x%x\n",
+ &addr, notify->data.transfer.iovec.size,
notify->data.transfer.iovec.flags);
spin_lock_irqsave(&uport->lock, flags);
@@ -1985,13 +1989,13 @@ void msm_hs_set_mctrl_locked(struct uart_port *uport,
unsigned int set_rts;
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- MSM_HS_DBG("%s()", __func__);
if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
MSM_HS_WARN("%s(): Clocks are off\n", __func__);
return;
}
/* RTS is active low */
set_rts = TIOCM_RTS & mctrl ? 0 : 1;
+ MSM_HS_INFO("%s: set_rts %d\n", __func__, set_rts);
if (set_rts)
msm_hs_disable_flow_control(uport, false);
@@ -2186,7 +2190,7 @@ static struct msm_hs_port *msm_hs_get_hs_port(int port_index)
return NULL;
}
-void toggle_wakeup_interrupt(struct msm_hs_port *msm_uport)
+void enable_wakeup_interrupt(struct msm_hs_port *msm_uport)
{
unsigned long flags;
struct uart_port *uport = &(msm_uport->uport);
@@ -2197,7 +2201,6 @@ void toggle_wakeup_interrupt(struct msm_hs_port *msm_uport)
return;
if (!(msm_uport->wakeup.enabled)) {
- MSM_HS_DBG("%s(): Enable Wakeup IRQ", __func__);
enable_irq(msm_uport->wakeup.irq);
disable_irq(uport->irq);
spin_lock_irqsave(&uport->lock, flags);
@@ -2205,12 +2208,28 @@ void toggle_wakeup_interrupt(struct msm_hs_port *msm_uport)
msm_uport->wakeup.enabled = true;
spin_unlock_irqrestore(&uport->lock, flags);
} else {
+ MSM_HS_WARN("%s:Wake up IRQ already enabled", __func__);
+ }
+}
+
+void disable_wakeup_interrupt(struct msm_hs_port *msm_uport)
+{
+ unsigned long flags;
+ struct uart_port *uport = &(msm_uport->uport);
+
+ if (!is_use_low_power_wakeup(msm_uport))
+ return;
+ if (msm_uport->wakeup.freed)
+ return;
+
+ if (msm_uport->wakeup.enabled) {
disable_irq_nosync(msm_uport->wakeup.irq);
enable_irq(uport->irq);
spin_lock_irqsave(&uport->lock, flags);
msm_uport->wakeup.enabled = false;
spin_unlock_irqrestore(&uport->lock, flags);
- MSM_HS_DBG("%s(): Disable Wakeup IRQ", __func__);
+ } else {
+ MSM_HS_WARN("%s:Wake up IRQ already disabled", __func__);
}
}
@@ -2267,6 +2286,7 @@ int msm_hs_request_clock_off(struct uart_port *uport)
{
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
int ret = 0;
+ int client_count = 0;
mutex_lock(&msm_uport->mtx);
/*
@@ -2293,8 +2313,10 @@ int msm_hs_request_clock_off(struct uart_port *uport)
atomic_set(&msm_uport->client_req_state, 1);
msm_hs_resource_unvote(msm_uport);
atomic_dec(&msm_uport->client_count);
- MSM_HS_INFO("%s():DISABLE UART CLOCK: ioc %d\n",
- __func__, atomic_read(&msm_uport->client_count));
+ client_count = atomic_read(&msm_uport->client_count);
+ LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+ "%s: Client_Count %d\n", __func__,
+ client_count);
exit_request_clock_off:
return ret;
}
@@ -2323,8 +2345,9 @@ int msm_hs_request_clock_on(struct uart_port *uport)
msm_hs_resource_vote(UARTDM_TO_MSM(uport));
atomic_inc(&msm_uport->client_count);
client_count = atomic_read(&msm_uport->client_count);
- MSM_HS_INFO("%s():ENABLE UART CLOCK: ioc %d\n",
- __func__, client_count);
+ LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+ "%s: Client_Count %d\n", __func__,
+ client_count);
/* Clear the flag */
if (msm_uport->obs)
@@ -2342,11 +2365,8 @@ static irqreturn_t msm_hs_wakeup_isr(int irq, void *dev)
struct uart_port *uport = &msm_uport->uport;
struct tty_struct *tty = NULL;
- msm_hs_resource_vote(msm_uport);
spin_lock_irqsave(&uport->lock, flags);
- MSM_HS_DBG("%s(): ignore %d\n", __func__,
- msm_uport->wakeup.ignore);
if (msm_uport->wakeup.ignore)
msm_uport->wakeup.ignore = 0;
else
@@ -2362,13 +2382,15 @@ static irqreturn_t msm_hs_wakeup_isr(int irq, void *dev)
tty_insert_flip_char(tty->port,
msm_uport->wakeup.rx_to_inject,
TTY_NORMAL);
- MSM_HS_DBG("%s(): Inject 0x%x", __func__,
- msm_uport->wakeup.rx_to_inject);
+ hex_dump_ipc(msm_uport, msm_uport->rx.ipc_rx_ctxt,
+ "Rx Inject",
+ &msm_uport->wakeup.rx_to_inject, 0, 1);
+ MSM_HS_INFO("Wakeup ISR.Ignore%d\n",
+ msm_uport->wakeup.ignore);
}
}
spin_unlock_irqrestore(&uport->lock, flags);
- msm_hs_resource_unvote(msm_uport);
if (wakeup && msm_uport->wakeup.inject_rx)
tty_flip_buffer_push(tty->port);
@@ -2396,7 +2418,7 @@ static void msm_hs_unconfig_uart_gpios(struct uart_port *uport)
ret = pinctrl_select_state(msm_uport->pinctrl,
msm_uport->gpio_state_suspend);
if (ret)
- MSM_HS_ERR("%s(): Failed to pinctrl set_state",
+ MSM_HS_ERR("%s():Failed to pinctrl set_state",
__func__);
} else if (pdata) {
if (gpio_is_valid(pdata->uart_tx_gpio))
@@ -2674,6 +2696,8 @@ static int msm_hs_startup(struct uart_port *uport)
spin_lock_irqsave(&uport->lock, flags);
atomic_set(&msm_uport->client_count, 0);
atomic_set(&msm_uport->client_req_state, 0);
+ LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+ "%s: Client_Count 0\n", __func__);
msm_hs_start_rx_locked(uport);
spin_unlock_irqrestore(&uport->lock, flags);
@@ -3092,17 +3116,19 @@ static void msm_hs_pm_suspend(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
int ret;
+ int client_count = 0;
if (!msm_uport)
goto err_suspend;
mutex_lock(&msm_uport->mtx);
+ client_count = atomic_read(&msm_uport->client_count);
/* For OBS, don't use wakeup interrupt, set gpio to suspended state */
if (msm_uport->obs) {
ret = pinctrl_select_state(msm_uport->pinctrl,
msm_uport->gpio_state_suspend);
if (ret)
- MSM_HS_ERR("%s(): Error selecting suspend state",
+ MSM_HS_ERR("%s():Error selecting pinctrl suspend state",
__func__);
}
@@ -3111,8 +3137,10 @@ static void msm_hs_pm_suspend(struct device *dev)
obs_manage_irq(msm_uport, false);
msm_hs_clk_bus_unvote(msm_uport);
if (!atomic_read(&msm_uport->client_req_state))
- toggle_wakeup_interrupt(msm_uport);
- MSM_HS_DBG("%s(): return suspend\n", __func__);
+ enable_wakeup_interrupt(msm_uport);
+ LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+ "%s: PM State Suspended client_count %d\n", __func__,
+ client_count);
mutex_unlock(&msm_uport->mtx);
return;
err_suspend:
@@ -3124,17 +3152,26 @@ static int msm_hs_pm_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
- int ret;
+ int ret = 0;
+ int client_count = 0;
- if (!msm_uport)
- goto err_resume;
+ if (!msm_uport) {
+ dev_err(dev, "%s:Invalid uport\n", __func__);
+ return -ENODEV;
+ }
mutex_lock(&msm_uport->mtx);
+ client_count = atomic_read(&msm_uport->client_count);
if (msm_uport->pm_state == MSM_HS_PM_ACTIVE)
goto exit_pm_resume;
if (!atomic_read(&msm_uport->client_req_state))
- toggle_wakeup_interrupt(msm_uport);
- msm_hs_clk_bus_vote(msm_uport);
+ disable_wakeup_interrupt(msm_uport);
+ ret = msm_hs_clk_bus_vote(msm_uport);
+ if (ret) {
+ MSM_HS_ERR("%s:Failed clock vote %d\n", __func__, ret);
+ dev_err(dev, "%s:Failed clock vote %d\n", __func__, ret);
+ goto exit_pm_resume;
+ }
obs_manage_irq(msm_uport, true);
msm_uport->pm_state = MSM_HS_PM_ACTIVE;
msm_hs_resource_on(msm_uport);
@@ -3144,17 +3181,15 @@ static int msm_hs_pm_resume(struct device *dev)
ret = pinctrl_select_state(msm_uport->pinctrl,
msm_uport->gpio_state_active);
if (ret)
- MSM_HS_ERR("%s(): Error selecting active state",
+ MSM_HS_ERR("%s():Error selecting active state",
__func__);
}
- MSM_HS_DBG("%s(): return resume\n", __func__);
+ LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+ "%s:PM State:Active client_count %d\n", __func__, client_count);
exit_pm_resume:
mutex_unlock(&msm_uport->mtx);
- return 0;
-err_resume:
- pr_err("%s(): invalid uport", __func__);
- return 0;
+ return ret;
}
#ifdef CONFIG_PM
@@ -3174,20 +3209,20 @@ static int msm_hs_pm_sys_suspend_noirq(struct device *dev)
* If there is an active clk request or an impending userspace request
* fail the suspend callback.
*/
- clk_cnt = atomic_read(&msm_uport->clk_count);
+ clk_cnt = atomic_read(&msm_uport->resource_count);
client_count = atomic_read(&msm_uport->client_count);
- if (clk_cnt || (pm_runtime_enabled(dev) &&
- !pm_runtime_suspended(dev))) {
- MSM_HS_WARN("%s:Fail Suspend.clk_cnt:%d,clnt_count:%d,RPM:%d\n",
- __func__, clk_cnt, client_count,
- dev->power.runtime_status);
+ if (msm_uport->pm_state == MSM_HS_PM_ACTIVE) {
+ MSM_HS_WARN("%s:Fail Suspend.clk_cnt:%d,clnt_count:%d\n",
+ __func__, clk_cnt, client_count);
ret = -EBUSY;
goto exit_suspend_noirq;
}
prev_pwr_state = msm_uport->pm_state;
msm_uport->pm_state = MSM_HS_PM_SYS_SUSPENDED;
- MSM_HS_DBG("%s(): suspending", __func__);
+ LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+ "%s:PM State:Sys-Suspended client_count %d\n", __func__,
+ client_count);
exit_suspend_noirq:
mutex_unlock(&msm_uport->mtx);
return ret;
@@ -3207,9 +3242,10 @@ static int msm_hs_pm_sys_resume_noirq(struct device *dev)
*/
mutex_lock(&msm_uport->mtx);
- MSM_HS_DBG("%s(): system resume", __func__);
if (msm_uport->pm_state == MSM_HS_PM_SYS_SUSPENDED)
msm_uport->pm_state = MSM_HS_PM_SUSPENDED;
+ LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+ "%s:PM State: Suspended\n", __func__);
mutex_unlock(&msm_uport->mtx);
return 0;
}
@@ -3257,6 +3293,7 @@ static int msm_hs_probe(struct platform_device *pdev)
int core_irqres, bam_irqres, wakeup_irqres;
struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
unsigned long data;
+ char name[30];
if (pdev->dev.of_node) {
dev_dbg(&pdev->dev, "device tree enabled\n");
@@ -3350,11 +3387,13 @@ static int msm_hs_probe(struct platform_device *pdev)
iounmap(uport->membase);
return -ENOMEM;
}
+
+ memset(name, 0, sizeof(name));
+ scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev),
+ "_state");
msm_uport->ipc_msm_hs_log_ctxt =
- ipc_log_context_create(IPC_MSM_HS_LOG_PAGES,
- dev_name(msm_uport->uport.dev), 0);
- pr_debug("%s: Device name is %s\n", __func__,
- dev_name(msm_uport->uport.dev));
+ ipc_log_context_create(IPC_MSM_HS_LOG_STATE_PAGES,
+ name, 0);
if (!msm_uport->ipc_msm_hs_log_ctxt) {
dev_err(&pdev->dev, "%s: error creating logging context",
__func__);
@@ -3439,6 +3478,36 @@ static int msm_hs_probe(struct platform_device *pdev)
msm_uport->tx.flush = FLUSH_SHUTDOWN;
msm_uport->rx.flush = FLUSH_SHUTDOWN;
+ memset(name, 0, sizeof(name));
+ scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev),
+ "_tx");
+ msm_uport->tx.ipc_tx_ctxt =
+ ipc_log_context_create(IPC_MSM_HS_LOG_DATA_PAGES, name, 0);
+ if (!msm_uport->tx.ipc_tx_ctxt)
+ dev_err(&pdev->dev, "%s: error creating tx logging context",
+ __func__);
+
+ memset(name, 0, sizeof(name));
+ scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev),
+ "_rx");
+ msm_uport->rx.ipc_rx_ctxt = ipc_log_context_create(
+ IPC_MSM_HS_LOG_DATA_PAGES, name, 0);
+ if (!msm_uport->rx.ipc_rx_ctxt)
+ dev_err(&pdev->dev, "%s: error creating rx logging context",
+ __func__);
+
+ memset(name, 0, sizeof(name));
+ scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev),
+ "_pwr");
+ msm_uport->ipc_msm_hs_pwr_ctxt = ipc_log_context_create(
+ IPC_MSM_HS_LOG_USER_PAGES, name, 0);
+ if (!msm_uport->ipc_msm_hs_pwr_ctxt)
+ dev_err(&pdev->dev, "%s: error creating usr logging context",
+ __func__);
+
+ uport->irq = core_irqres;
+ msm_uport->bam_irq = bam_irqres;
+
clk_set_rate(msm_uport->clk, msm_uport->uport.uartclk);
msm_hs_clk_bus_vote(msm_uport);
ret = uartdm_init_port(uport);
@@ -3622,9 +3691,9 @@ static void msm_hs_shutdown(struct uart_port *uport)
UART_XMIT_SIZE, DMA_TO_DEVICE);
msm_hs_resource_unvote(msm_uport);
- rc = atomic_read(&msm_uport->clk_count);
+ rc = atomic_read(&msm_uport->resource_count);
if (rc) {
- atomic_set(&msm_uport->clk_count, 1);
+ atomic_set(&msm_uport->resource_count, 1);
MSM_HS_WARN("%s(): removing extra vote\n", __func__);
msm_hs_resource_unvote(msm_uport);
}
@@ -3635,6 +3704,8 @@ static void msm_hs_shutdown(struct uart_port *uport)
if (atomic_read(&msm_uport->client_count)) {
MSM_HS_WARN("%s: Client vote on, forcing to 0\n", __func__);
atomic_set(&msm_uport->client_count, 0);
+ LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+ "%s: Client_Count 0\n", __func__);
}
msm_hs_unconfig_uart_gpios(uport);
MSM_HS_INFO("%s:UART port closed successfully\n", __func__);
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index fa4e23930614..d37fdcc3143c 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1114,6 +1114,9 @@ static int acm_probe(struct usb_interface *intf,
if (quirks == NO_UNION_NORMAL) {
data_interface = usb_ifnum_to_if(usb_dev, 1);
control_interface = usb_ifnum_to_if(usb_dev, 0);
+ /* we would crash */
+ if (!data_interface || !control_interface)
+ return -ENODEV;
goto skip_normal_probe;
}
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 56593a9a8726..2057d91d8336 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -502,11 +502,15 @@ static int usb_unbind_interface(struct device *dev)
int usb_driver_claim_interface(struct usb_driver *driver,
struct usb_interface *iface, void *priv)
{
- struct device *dev = &iface->dev;
+ struct device *dev;
struct usb_device *udev;
int retval = 0;
int lpm_disable_error;
+ if (!iface)
+ return -ENODEV;
+
+ dev = &iface->dev;
if (dev->driver)
return -EBUSY;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 1560f3f3e756..84df093639ac 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -4277,7 +4277,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
{
struct usb_device *hdev = hub->hdev;
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
- int i, j, retval;
+ int retries, operations, retval, i;
unsigned delay = HUB_SHORT_RESET_TIME;
enum usb_device_speed oldspeed = udev->speed;
const char *speed;
@@ -4379,7 +4379,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
* first 8 bytes of the device descriptor to get the ep0 maxpacket
* value.
*/
- for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
+ for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {
bool did_new_scheme = false;
if (use_new_scheme(udev, retry_counter)) {
@@ -4406,7 +4406,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
* 255 is for WUSB devices, we actually need to use
* 512 (WUSB1.0[4.8.1]).
*/
- for (j = 0; j < 3; ++j) {
+ for (operations = 0; operations < 3; ++operations) {
buf->bMaxPacketSize0 = 0;
r = usb_control_msg(udev, usb_rcvaddr0pipe(),
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
@@ -4426,7 +4426,13 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
r = -EPROTO;
break;
}
- if (r == 0)
+ /*
+ * Some devices time out if they are powered on
+ * when already connected. They need a second
+ * reset. But only on the first attempt,
+ * lest we get into a time out/reset loop
+ */
+ if (r == 0 || (r == -ETIMEDOUT && retries == 0))
break;
}
udev->descriptor.bMaxPacketSize0 =
@@ -4458,7 +4464,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
* authorization will assign the final address.
*/
if (udev->wusb == 0) {
- for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
+ for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {
retval = hub_set_address(udev, devnum);
if (retval >= 0)
break;
@@ -5386,6 +5392,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
}
bos = udev->bos;
+ udev->bos = NULL;
for (i = 0; i < SET_CONFIG_TRIES; ++i) {
@@ -5478,11 +5485,8 @@ done:
usb_set_usb2_hardware_lpm(udev, 1);
usb_unlocked_enable_lpm(udev);
usb_enable_ltm(udev);
- /* release the new BOS descriptor allocated by hub_port_init() */
- if (udev->bos != bos) {
- usb_release_bos_descriptor(udev);
- udev->bos = bos;
- }
+ usb_release_bos_descriptor(udev);
+ udev->bos = bos;
return 0;
re_enumerate:
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 4635edf0189b..4d35de1c14c5 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -92,6 +92,13 @@ MODULE_PARM_DESC(cpu_to_affin, "affin usb irq to this cpu");
#define PIPE3_PHYSTATUS_SW BIT(3)
#define PIPE_UTMI_CLK_DIS BIT(8)
+#define HS_PHY_CTRL_REG (QSCRATCH_REG_OFFSET + 0x10)
+#define UTMI_OTG_VBUS_VALID BIT(20)
+#define SW_SESSVLD_SEL BIT(28)
+
+#define SS_PHY_CTRL_REG (QSCRATCH_REG_OFFSET + 0x30)
+#define LANE0_PWR_PRESENT BIT(24)
+
/* GSI related registers */
#define GSI_TRB_ADDR_BIT_53_MASK (1 << 21)
#define GSI_TRB_ADDR_BIT_55_MASK (1 << 23)
@@ -3090,6 +3097,25 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on)
return 0;
}
+static void dwc3_override_vbus_status(struct dwc3_msm *mdwc, bool vbus_present)
+{
+ struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
+
+ /* Update OTG VBUS Valid from HSPHY to controller */
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG,
+ vbus_present ? UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL :
+ UTMI_OTG_VBUS_VALID,
+ vbus_present ? UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL : 0);
+
+ /* Update only if Super Speed is supported */
+ if (dwc->maximum_speed == USB_SPEED_SUPER) {
+ /* Update VBUS Valid from SSPHY to controller */
+ dwc3_msm_write_readback(mdwc->base, SS_PHY_CTRL_REG,
+ LANE0_PWR_PRESENT,
+ vbus_present ? LANE0_PWR_PRESENT : 0);
+ }
+}
+
/**
* dwc3_otg_start_peripheral - bind/unbind the peripheral controller.
*
@@ -3110,6 +3136,7 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on)
dev_dbg(mdwc->dev, "%s: turn on gadget %s\n",
__func__, dwc->gadget.name);
+ dwc3_override_vbus_status(mdwc, true);
usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH);
usb_phy_notify_connect(mdwc->ss_phy, USB_SPEED_SUPER);
@@ -3125,6 +3152,7 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on)
usb_gadget_vbus_disconnect(&dwc->gadget);
usb_phy_notify_disconnect(mdwc->hs_phy, USB_SPEED_HIGH);
usb_phy_notify_disconnect(mdwc->ss_phy, USB_SPEED_SUPER);
+ dwc3_override_vbus_status(mdwc, false);
dwc3_usb3_phy_suspend(dwc, false);
}
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 3639890c0dc7..5db4fe9e3cdf 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1738,7 +1738,10 @@ void unregister_gadget_item(struct config_item *item)
{
struct gadget_info *gi = to_gadget_info(item);
+ /* to protect race with gadget_dev_desc_UDC_store*/
+ mutex_lock(&gi->lock);
unregister_gadget(gi);
+ mutex_unlock(&gi->lock);
}
EXPORT_SYMBOL_GPL(unregister_gadget_item);
diff --git a/drivers/usb/gadget/function/f_cdev.c b/drivers/usb/gadget/function/f_cdev.c
index 0a9a3afd72dd..b22ea656367e 100644
--- a/drivers/usb/gadget/function/f_cdev.c
+++ b/drivers/usb/gadget/function/f_cdev.c
@@ -529,6 +529,14 @@ static int usb_cser_notify(struct f_cdev *port, u8 type, u16 value,
const unsigned len = sizeof(*notify) + length;
void *buf;
int status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (!port->is_connected) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ pr_debug("%s: port disconnected\n", __func__);
+ return -ENODEV;
+ }
req = port->port_usb.notify_req;
port->port_usb.notify_req = NULL;
@@ -544,7 +552,9 @@ static int usb_cser_notify(struct f_cdev *port, u8 type, u16 value,
notify->wValue = cpu_to_le16(value);
notify->wIndex = cpu_to_le16(port->port_usb.data_id);
notify->wLength = cpu_to_le16(length);
+ /* 2 byte data copy */
memcpy(buf, data, length);
+ spin_unlock_irqrestore(&port->port_lock, flags);
status = usb_ep_queue(ep, req, GFP_ATOMIC);
if (status < 0) {
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 48f987d77e91..c5fd3ce3ed9a 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -24,6 +24,7 @@
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/uio.h>
+#include <linux/ipc_logging.h>
#include <asm/unaligned.h>
#include <linux/usb/composite.h>
@@ -41,6 +42,15 @@
#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
+#define NUM_PAGES 10 /* # of pages for ipc logging */
+
+static void *ffs_ipc_log;
+#define ffs_log(fmt, ...) do { \
+ ipc_log_string(ffs_ipc_log, "%s: " fmt, __func__, \
+ ##__VA_ARGS__); \
+ pr_debug(fmt, ##__VA_ARGS__); \
+} while (0)
+
/* Reference counter handling */
static void ffs_data_get(struct ffs_data *ffs);
static void ffs_data_put(struct ffs_data *ffs);
@@ -214,6 +224,9 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
spin_unlock_irq(&ffs->ev.waitq.lock);
+ ffs_log("enter: state %d setup_state %d flags %lu", ffs->state,
+ ffs->setup_state, ffs->flags);
+
req->buf = data;
req->length = len;
@@ -238,11 +251,18 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
}
ffs->setup_state = FFS_NO_SETUP;
+
+ ffs_log("exit: state %d setup_state %d flags %lu", ffs->state,
+ ffs->setup_state, ffs->flags);
+
return req->status ? req->status : req->actual;
}
static int __ffs_ep0_stall(struct ffs_data *ffs)
{
+ ffs_log("state %d setup_state %d flags %lu can_stall %d", ffs->state,
+ ffs->setup_state, ffs->flags, ffs->ev.can_stall);
+
if (ffs->ev.can_stall) {
pr_vdebug("ep0 stall\n");
usb_ep_set_halt(ffs->gadget->ep0);
@@ -263,6 +283,9 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
ENTER();
+ ffs_log("enter:len %zu state %d setup_state %d flags %lu", len,
+ ffs->state, ffs->setup_state, ffs->flags);
+
/* Fast check if setup was canceled */
if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
return -EIDRM;
@@ -391,6 +414,9 @@ done_spin:
break;
}
+ ffs_log("exit:ret %zu state %d setup_state %d flags %lu", ret,
+ ffs->state, ffs->setup_state, ffs->flags);
+
mutex_unlock(&ffs->mutex);
return ret;
}
@@ -424,6 +450,10 @@ static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
ffs->ev.count * sizeof *ffs->ev.types);
spin_unlock_irq(&ffs->ev.waitq.lock);
+
+ ffs_log("state %d setup_state %d flags %lu #evt %zu", ffs->state,
+ ffs->setup_state, ffs->flags, n);
+
mutex_unlock(&ffs->mutex);
return unlikely(copy_to_user(buf, events, size)) ? -EFAULT : size;
@@ -439,6 +469,9 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
ENTER();
+ ffs_log("enter:len %zu state %d setup_state %d flags %lu", len,
+ ffs->state, ffs->setup_state, ffs->flags);
+
/* Fast check if setup was canceled */
if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
return -EIDRM;
@@ -527,8 +560,12 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
spin_unlock_irq(&ffs->ev.waitq.lock);
done_mutex:
+ ffs_log("exit:ret %d state %d setup_state %d flags %lu", ret,
+ ffs->state, ffs->setup_state, ffs->flags);
+
mutex_unlock(&ffs->mutex);
kfree(data);
+
return ret;
}
@@ -538,6 +575,9 @@ static int ffs_ep0_open(struct inode *inode, struct file *file)
ENTER();
+ ffs_log("state %d setup_state %d flags %lu opened %d", ffs->state,
+ ffs->setup_state, ffs->flags, atomic_read(&ffs->opened));
+
if (unlikely(ffs->state == FFS_CLOSING))
return -EBUSY;
@@ -557,6 +597,9 @@ static int ffs_ep0_release(struct inode *inode, struct file *file)
ENTER();
+ ffs_log("state %d setup_state %d flags %lu opened %d", ffs->state,
+ ffs->setup_state, ffs->flags, atomic_read(&ffs->opened));
+
ffs_data_closed(ffs);
return 0;
@@ -570,6 +613,9 @@ static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
ENTER();
+ ffs_log("state %d setup_state %d flags %lu opened %d", ffs->state,
+ ffs->setup_state, ffs->flags, atomic_read(&ffs->opened));
+
if (code == FUNCTIONFS_INTERFACE_REVMAP) {
struct ffs_function *func = ffs->func;
ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV;
@@ -588,6 +634,9 @@ static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait)
unsigned int mask = POLLWRNORM;
int ret;
+ ffs_log("enter:state %d setup_state %d flags %lu opened %d", ffs->state,
+ ffs->setup_state, ffs->flags, atomic_read(&ffs->opened));
+
poll_wait(file, &ffs->ev.waitq, wait);
ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
@@ -618,6 +667,8 @@ static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait)
break;
}
+ ffs_log("exit: mask %u", mask);
+
mutex_unlock(&ffs->mutex);
return mask;
@@ -648,6 +699,7 @@ static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
ep->status = req->status ? req->status : req->actual;
/* Set is_busy false to indicate completion of last request */
ep->is_busy = false;
+ ffs_log("ep status %d for req %p", ep->status, req);
complete(req->context);
}
}
@@ -659,6 +711,8 @@ static void ffs_user_copy_worker(struct work_struct *work)
int ret = io_data->req->status ? io_data->req->status :
io_data->req->actual;
+ ffs_log("enter: ret %d", ret);
+
if (io_data->read && ret > 0) {
use_mm(io_data->mm);
ret = copy_to_iter(io_data->buf, ret, &io_data->data);
@@ -680,6 +734,8 @@ static void ffs_user_copy_worker(struct work_struct *work)
kfree(io_data->to_free);
kfree(io_data->buf);
kfree(io_data);
+
+ ffs_log("exit");
}
static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
@@ -689,8 +745,12 @@ static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
ENTER();
+ ffs_log("enter");
+
INIT_WORK(&io_data->work, ffs_user_copy_worker);
schedule_work(&io_data->work);
+
+ ffs_log("exit");
}
static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
@@ -701,6 +761,9 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
ssize_t ret, data_len = -EINVAL;
int halt;
+ ffs_log("enter: epfile name %s epfile err %d", epfile->name,
+ atomic_read(&epfile->error));
+
smp_mb__before_atomic();
if (atomic_read(&epfile->error))
return -ENODEV;
@@ -927,6 +990,9 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
}
mutex_unlock(&epfile->mutex);
+
+ ffs_log("exit:ret %zu", ret);
+
return ret;
error_lock:
@@ -934,6 +1000,9 @@ error_lock:
mutex_unlock(&epfile->mutex);
error:
kfree(data);
+
+ ffs_log("exit: ret %zu", ret);
+
return ret;
}
@@ -944,6 +1013,9 @@ ffs_epfile_open(struct inode *inode, struct file *file)
ENTER();
+ ffs_log("enter:state %d setup_state %d flag %lu", epfile->ffs->state,
+ epfile->ffs->setup_state, epfile->ffs->flags);
+
if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
return -ENODEV;
@@ -962,6 +1034,9 @@ ffs_epfile_open(struct inode *inode, struct file *file)
smp_mb__before_atomic();
atomic_set(&epfile->error, 0);
+ ffs_log("exit:state %d setup_state %d flag %lu", epfile->ffs->state,
+ epfile->ffs->setup_state, epfile->ffs->flags);
+
return 0;
}
@@ -973,6 +1048,9 @@ static int ffs_aio_cancel(struct kiocb *kiocb)
ENTER();
+ ffs_log("enter:state %d setup_state %d flag %lu", epfile->ffs->state,
+ epfile->ffs->setup_state, epfile->ffs->flags);
+
spin_lock_irq(&epfile->ffs->eps_lock);
if (likely(io_data && io_data->ep && io_data->req))
@@ -982,6 +1060,8 @@ static int ffs_aio_cancel(struct kiocb *kiocb)
spin_unlock_irq(&epfile->ffs->eps_lock);
+ ffs_log("exit: value %d", value);
+
return value;
}
@@ -992,6 +1072,8 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from)
ENTER();
+ ffs_log("enter");
+
if (!is_sync_kiocb(kiocb)) {
p = kmalloc(sizeof(io_data), GFP_KERNEL);
if (unlikely(!p))
@@ -1018,6 +1100,9 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from)
kfree(p);
else
*from = p->data;
+
+ ffs_log("exit");
+
return res;
}
@@ -1028,6 +1113,8 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to)
ENTER();
+ ffs_log("enter");
+
if (!is_sync_kiocb(kiocb)) {
p = kmalloc(sizeof(io_data), GFP_KERNEL);
if (unlikely(!p))
@@ -1066,6 +1153,9 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to)
} else {
*to = p->data;
}
+
+ ffs_log("enter");
+
return res;
}
@@ -1076,12 +1166,17 @@ ffs_epfile_release(struct inode *inode, struct file *file)
ENTER();
+ ffs_log("enter:state %d setup_state %d flag %lu", epfile->ffs->state,
+ epfile->ffs->setup_state, epfile->ffs->flags);
+
smp_mb__before_atomic();
atomic_set(&epfile->opened, 0);
atomic_set(&epfile->error, 1);
ffs_data_closed(epfile->ffs);
file->private_data = NULL;
+ ffs_log("exit");
+
return 0;
}
@@ -1093,6 +1188,9 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
ENTER();
+ ffs_log("enter:state %d setup_state %d flag %lu", epfile->ffs->state,
+ epfile->ffs->setup_state, epfile->ffs->flags);
+
if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
return -ENODEV;
@@ -1143,6 +1241,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
}
spin_unlock_irq(&epfile->ffs->eps_lock);
+ ffs_log("exit:ret %d", ret);
+
return ret;
}
@@ -1174,6 +1274,8 @@ ffs_sb_make_inode(struct super_block *sb, void *data,
ENTER();
+ ffs_log("enter");
+
inode = new_inode(sb);
if (likely(inode)) {
@@ -1193,6 +1295,8 @@ ffs_sb_make_inode(struct super_block *sb, void *data,
inode->i_op = iops;
}
+ ffs_log("exit");
+
return inode;
}
@@ -1207,6 +1311,8 @@ static struct dentry *ffs_sb_create_file(struct super_block *sb,
ENTER();
+ ffs_log("enter");
+
dentry = d_alloc_name(sb->s_root, name);
if (unlikely(!dentry))
return NULL;
@@ -1218,6 +1324,9 @@ static struct dentry *ffs_sb_create_file(struct super_block *sb,
}
d_add(dentry, inode);
+
+ ffs_log("exit");
+
return dentry;
}
@@ -1243,6 +1352,8 @@ static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
ENTER();
+ ffs_log("enter");
+
ffs->sb = sb;
data->ffs_data = NULL;
sb->s_fs_info = ffs;
@@ -1267,6 +1378,8 @@ static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
&ffs_ep0_operations)))
return -ENOMEM;
+ ffs_log("exit");
+
return 0;
}
@@ -1274,6 +1387,8 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
{
ENTER();
+ ffs_log("enter");
+
if (!opts || !*opts)
return 0;
@@ -1356,6 +1471,8 @@ invalid:
opts = comma + 1;
}
+ ffs_log("exit");
+
return 0;
}
@@ -1381,6 +1498,8 @@ ffs_fs_mount(struct file_system_type *t, int flags,
ENTER();
+ ffs_log("enter");
+
ret = ffs_fs_parse_opts(&data, opts);
if (unlikely(ret < 0))
return ERR_PTR(ret);
@@ -1410,6 +1529,9 @@ ffs_fs_mount(struct file_system_type *t, int flags,
ffs_release_dev(data.ffs_data);
ffs_data_put(data.ffs_data);
}
+
+ ffs_log("exit");
+
return rv;
}
@@ -1418,12 +1540,16 @@ ffs_fs_kill_sb(struct super_block *sb)
{
ENTER();
+ ffs_log("enter");
+
kill_litter_super(sb);
if (sb->s_fs_info) {
ffs_release_dev(sb->s_fs_info);
ffs_data_closed(sb->s_fs_info);
ffs_data_put(sb->s_fs_info);
}
+
+ ffs_log("exit");
}
static struct file_system_type ffs_fs_type = {
@@ -1449,6 +1575,8 @@ static int functionfs_init(void)
else
pr_err("failed registering file system (%d)\n", ret);
+ ffs_ipc_log = ipc_log_context_create(NUM_PAGES, "f_fs", 0);
+
return ret;
}
@@ -1470,14 +1598,21 @@ static void ffs_data_get(struct ffs_data *ffs)
{
ENTER();
+ ffs_log("enter");
+
smp_mb__before_atomic();
atomic_inc(&ffs->ref);
+
+ ffs_log("exit");
}
static void ffs_data_opened(struct ffs_data *ffs)
{
ENTER();
+ ffs_log("enter: state %d setup_state %d flag %lu opened %d", ffs->state,
+ ffs->setup_state, ffs->flags, atomic_read(&ffs->opened));
+
smp_mb__before_atomic();
atomic_inc(&ffs->ref);
if (atomic_add_return(1, &ffs->opened) == 1 &&
@@ -1485,12 +1620,17 @@ static void ffs_data_opened(struct ffs_data *ffs)
ffs->state = FFS_CLOSING;
ffs_data_reset(ffs);
}
+
+ ffs_log("exit: state %d setup_state %d flag %lu", ffs->state,
+ ffs->setup_state, ffs->flags);
}
static void ffs_data_put(struct ffs_data *ffs)
{
ENTER();
+ ffs_log("enter");
+
smp_mb__before_atomic();
if (unlikely(atomic_dec_and_test(&ffs->ref))) {
pr_info("%s(): freeing\n", __func__);
@@ -1500,12 +1640,17 @@ static void ffs_data_put(struct ffs_data *ffs)
kfree(ffs->dev_name);
kfree(ffs);
}
+
+ ffs_log("exit");
}
static void ffs_data_closed(struct ffs_data *ffs)
{
ENTER();
+ ffs_log("enter: state %d setup_state %d flag %lu opened %d", ffs->state,
+ ffs->setup_state, ffs->flags, atomic_read(&ffs->opened));
+
smp_mb__before_atomic();
if (atomic_dec_and_test(&ffs->opened)) {
if (ffs->no_disconnect) {
@@ -1529,6 +1674,9 @@ static void ffs_data_closed(struct ffs_data *ffs)
ffs_data_reset(ffs);
}
+ ffs_log("exit: state %d setup_state %d flag %lu", ffs->state,
+ ffs->setup_state, ffs->flags);
+
ffs_data_put(ffs);
}
@@ -1540,6 +1688,8 @@ static struct ffs_data *ffs_data_new(void)
ENTER();
+ ffs_log("enter");
+
atomic_set(&ffs->ref, 1);
atomic_set(&ffs->opened, 0);
ffs->state = FFS_READ_DESCRIPTORS;
@@ -1553,6 +1703,8 @@ static struct ffs_data *ffs_data_new(void)
/* XXX REVISIT need to update it in some places, or do we? */
ffs->ev.can_stall = 1;
+ ffs_log("exit");
+
return ffs;
}
@@ -1560,6 +1712,9 @@ static void ffs_data_clear(struct ffs_data *ffs)
{
ENTER();
+ ffs_log("enter: state %d setup_state %d flag %lu", ffs->state,
+ ffs->setup_state, ffs->flags);
+
pr_debug("%s: ffs->gadget= %p, ffs->flags= %lu\n",
__func__, ffs->gadget, ffs->flags);
ffs_closed(ffs);
@@ -1578,12 +1733,18 @@ static void ffs_data_clear(struct ffs_data *ffs)
kfree(ffs->raw_descs_data);
kfree(ffs->raw_strings);
kfree(ffs->stringtabs);
+
+ ffs_log("exit: state %d setup_state %d flag %lu", ffs->state,
+ ffs->setup_state, ffs->flags);
}
static void ffs_data_reset(struct ffs_data *ffs)
{
ENTER();
+ ffs_log("enter: state %d setup_state %d flag %lu", ffs->state,
+ ffs->setup_state, ffs->flags);
+
ffs_data_clear(ffs);
ffs->epfiles = NULL;
@@ -1606,6 +1767,9 @@ static void ffs_data_reset(struct ffs_data *ffs)
ffs->state = FFS_READ_DESCRIPTORS;
ffs->setup_state = FFS_NO_SETUP;
ffs->flags = 0;
+
+ ffs_log("exit: state %d setup_state %d flag %lu", ffs->state,
+ ffs->setup_state, ffs->flags);
}
@@ -1616,6 +1780,9 @@ static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
ENTER();
+ ffs_log("enter: state %d setup_state %d flag %lu", ffs->state,
+ ffs->setup_state, ffs->flags);
+
if (WARN_ON(ffs->state != FFS_ACTIVE
|| test_and_set_bit(FFS_FL_BOUND, &ffs->flags)))
return -EBADFD;
@@ -1641,6 +1808,10 @@ static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
}
ffs->gadget = cdev->gadget;
+
+ ffs_log("exit: state %d setup_state %d flag %lu gadget %p\n",
+ ffs->state, ffs->setup_state, ffs->flags, ffs->gadget);
+
ffs_data_get(ffs);
return 0;
}
@@ -1654,6 +1825,8 @@ static void functionfs_unbind(struct ffs_data *ffs)
ffs->ep0req = NULL;
ffs->gadget = NULL;
clear_bit(FFS_FL_BOUND, &ffs->flags);
+ ffs_log("state %d setup_state %d flag %lu gadget %p\n",
+ ffs->state, ffs->setup_state, ffs->flags, ffs->gadget);
ffs_data_put(ffs);
}
}
@@ -1665,6 +1838,9 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
ENTER();
+ ffs_log("enter: state %d setup_state %d flag %lu", ffs->state,
+ ffs->setup_state, ffs->flags);
+
count = ffs->eps_count;
epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL);
if (!epfiles)
@@ -1690,6 +1866,10 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
}
ffs->epfiles = epfiles;
+
+ ffs_log("exit: epfile name %s state %d setup_state %d flag %lu",
+ epfile->name, ffs->state, ffs->setup_state, ffs->flags);
+
return 0;
}
@@ -1699,6 +1879,8 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
ENTER();
+ ffs_log("enter: epfilename %s", epfile->name);
+
for (; count; --count, ++epfile) {
BUG_ON(mutex_is_locked(&epfile->mutex) ||
waitqueue_active(&epfile->wait));
@@ -1710,6 +1892,8 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
}
kfree(epfiles);
+
+ ffs_log("exit");
}
static void ffs_func_eps_disable(struct ffs_function *func)
@@ -1719,6 +1903,9 @@ static void ffs_func_eps_disable(struct ffs_function *func)
unsigned count = func->ffs->eps_count;
unsigned long flags;
+ ffs_log("enter: state %d setup_state %d flag %lu", func->ffs->state,
+ func->ffs->setup_state, func->ffs->flags);
+
spin_lock_irqsave(&func->ffs->eps_lock, flags);
do {
@@ -1738,6 +1925,8 @@ static void ffs_func_eps_disable(struct ffs_function *func)
}
} while (--count);
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+
+ ffs_log("exit");
}
static int ffs_func_eps_enable(struct ffs_function *func)
@@ -1749,6 +1938,9 @@ static int ffs_func_eps_enable(struct ffs_function *func)
unsigned long flags;
int ret = 0;
+ ffs_log("enter: state %d setup_state %d flag %lu", func->ffs->state,
+ func->ffs->setup_state, func->ffs->flags);
+
spin_lock_irqsave(&func->ffs->eps_lock, flags);
do {
struct usb_endpoint_descriptor *ds;
@@ -1786,6 +1978,7 @@ static int ffs_func_eps_enable(struct ffs_function *func)
epfile->ep = ep;
epfile->in = usb_endpoint_dir_in(ds);
epfile->isoc = usb_endpoint_xfer_isoc(ds);
+ ffs_log("usb_ep_enable %s", ep->ep->name);
} else {
break;
}
@@ -1797,6 +1990,8 @@ static int ffs_func_eps_enable(struct ffs_function *func)
} while (--count);
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+ ffs_log("exit: ret %d", ret);
+
return ret;
}
@@ -1837,6 +2032,8 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len,
ENTER();
+ ffs_log("enter: len %u", len);
+
/* At least two bytes are required: length and type */
if (len < 2) {
pr_vdebug("descriptor too short\n");
@@ -1953,6 +2150,8 @@ inv_length:
#undef __entity_check_STRING
#undef __entity_check_ENDPOINT
+ ffs_log("exit: desc type %d length %d", _ds->bDescriptorType, length);
+
return length;
}
@@ -1964,6 +2163,8 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
ENTER();
+ ffs_log("enter: len %u", len);
+
for (;;) {
int ret;
@@ -1991,6 +2192,8 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
data += ret;
++num;
}
+
+ ffs_log("exit: len %u", len);
}
static int __ffs_data_do_entity(enum ffs_entity_type type,
@@ -2002,6 +2205,8 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
ENTER();
+ ffs_log("enter: type %u", type);
+
switch (type) {
case FFS_DESCRIPTOR:
break;
@@ -2040,6 +2245,8 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
break;
}
+ ffs_log("exit");
+
return 0;
}
@@ -2049,6 +2256,8 @@ static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type,
u16 bcd_version = le16_to_cpu(desc->bcdVersion);
u16 w_index = le16_to_cpu(desc->wIndex);
+ ffs_log("enter");
+
if (bcd_version != 1) {
pr_vdebug("unsupported os descriptors version: %d",
bcd_version);
@@ -2066,6 +2275,8 @@ static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type,
return -EINVAL;
}
+ ffs_log("exit: size of desc %lu", sizeof(*desc));
+
return sizeof(*desc);
}
@@ -2085,6 +2296,8 @@ static int __must_check ffs_do_single_os_desc(char *data, unsigned len,
ENTER();
+ ffs_log("enter: len %u os desc type %d", len, type);
+
/* loop over all ext compat/ext prop descriptors */
while (feature_count--) {
ret = entity(type, h, data, len, priv);
@@ -2095,6 +2308,9 @@ static int __must_check ffs_do_single_os_desc(char *data, unsigned len,
data += ret;
len -= ret;
}
+
+ ffs_log("exit");
+
return _len - len;
}
@@ -2108,6 +2324,8 @@ static int __must_check ffs_do_os_descs(unsigned count,
ENTER();
+ ffs_log("enter: len %u", len);
+
for (num = 0; num < count; ++num) {
int ret;
enum ffs_os_desc_type type;
@@ -2157,6 +2375,9 @@ static int __must_check ffs_do_os_descs(unsigned count,
len -= ret;
data += ret;
}
+
+ ffs_log("exit");
+
return _len - len;
}
@@ -2172,6 +2393,8 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
ENTER();
+ ffs_log("enter: len %u", len);
+
switch (type) {
case FFS_OS_DESC_EXT_COMPAT: {
struct usb_ext_compat_desc *d = data;
@@ -2226,6 +2449,9 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
pr_vdebug("unknown descriptor: %d\n", type);
return -EINVAL;
}
+
+ ffs_log("exit");
+
return length;
}
@@ -2239,6 +2465,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
ENTER();
+ ffs_log("enter: len %zu", len);
+
if (get_unaligned_le32(data + 4) != len)
goto error;
@@ -2349,10 +2577,13 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
ffs->ss_descs_count = counts[2];
ffs->ms_os_descs_count = os_descs_count;
+ ffs_log("exit");
+
return 0;
error:
kfree(_data);
+ ffs_log("exit: ret %d", ret);
return ret;
}
@@ -2366,6 +2597,8 @@ static int __ffs_data_got_strings(struct ffs_data *ffs,
ENTER();
+ ffs_log("enter: len %zu", len);
+
if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
get_unaligned_le32(data + 4) != len))
goto error;
@@ -2480,12 +2713,14 @@ static int __ffs_data_got_strings(struct ffs_data *ffs,
ffs->stringtabs = stringtabs;
ffs->raw_strings = _data;
+ ffs_log("exit");
return 0;
error_free:
kfree(stringtabs);
error:
kfree(_data);
+ ffs_log("exit: -EINVAL");
return -EINVAL;
}
@@ -2498,6 +2733,9 @@ static void __ffs_event_add(struct ffs_data *ffs,
enum usb_functionfs_event_type rem_type1, rem_type2 = type;
int neg = 0;
+ ffs_log("enter: type %d state %d setup_state %d flag %lu", type,
+ ffs->state, ffs->setup_state, ffs->flags);
+
/*
* Abort any unhandled setup
*
@@ -2557,6 +2795,9 @@ static void __ffs_event_add(struct ffs_data *ffs,
wake_up_locked(&ffs->ev.waitq);
if (ffs->ffs_eventfd)
eventfd_signal(ffs->ffs_eventfd, 1);
+
+ ffs_log("exit: state %d setup_state %d flag %lu", ffs->state,
+ ffs->setup_state, ffs->flags);
}
static void ffs_event_add(struct ffs_data *ffs,
@@ -2591,6 +2832,8 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
int idx;
static const char *speed_names[] = { "full", "high", "super" };
+ ffs_log("enter");
+
if (type != FFS_DESCRIPTOR)
return 0;
@@ -2666,6 +2909,8 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
}
ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength);
+ ffs_log("exit");
+
return 0;
}
@@ -2677,6 +2922,8 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
unsigned idx;
u8 newValue;
+ ffs_log("enter: type %d", type);
+
switch (type) {
default:
case FFS_DESCRIPTOR:
@@ -2721,6 +2968,9 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
pr_vdebug("%02x -> %02x\n", *valuep, newValue);
*valuep = newValue;
+
+ ffs_log("exit: newValue %d", newValue);
+
return 0;
}
@@ -2731,6 +2981,8 @@ static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
struct ffs_function *func = priv;
u8 length = 0;
+ ffs_log("enter: type %d", type);
+
switch (type) {
case FFS_OS_DESC_EXT_COMPAT: {
struct usb_ext_compat_desc *desc = data;
@@ -2800,6 +3052,8 @@ static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
pr_vdebug("unknown descriptor: %d\n", type);
}
+ ffs_log("exit");
+
return length;
}
@@ -2813,6 +3067,8 @@ static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
ENTER();
+ ffs_log("enter");
+
/*
* Legacy gadget triggers binding in functionfs_ready_callback,
* which already uses locking; taking the same lock here would
@@ -2847,6 +3103,8 @@ static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
ffs_opts->refcnt++;
func->function.strings = func->ffs->stringtabs;
+ ffs_log("exit");
+
return ffs_opts;
}
@@ -2889,6 +3147,9 @@ static int _ffs_func_bind(struct usb_configuration *c,
ENTER();
+ ffs_log("enter: state %d setup_state %d flag %lu", ffs->state,
+ ffs->setup_state, ffs->flags);
+
/* Has descriptors only for speeds gadget does not support */
if (unlikely(!(full | high | super)))
return -ENOTSUPP;
@@ -3006,10 +3267,15 @@ static int _ffs_func_bind(struct usb_configuration *c,
/* And we're done */
ffs_event_add(ffs, FUNCTIONFS_BIND);
+
+ ffs_log("exit: state %d setup_state %d flag %lu", ffs->state,
+ ffs->setup_state, ffs->flags);
+
return 0;
error:
/* XXX Do we need to release all claimed endpoints here? */
+ ffs_log("exit: ret %d", ret);
return ret;
}
@@ -3020,6 +3286,8 @@ static int ffs_func_bind(struct usb_configuration *c,
struct ffs_function *func = ffs_func_from_usb(f);
int ret;
+ ffs_log("enter");
+
if (IS_ERR(ffs_opts))
return PTR_ERR(ffs_opts);
@@ -3027,6 +3295,8 @@ static int ffs_func_bind(struct usb_configuration *c,
if (ret && !--ffs_opts->refcnt)
functionfs_unbind(func->ffs);
+ ffs_log("exit: ret %d", ret);
+
return ret;
}
@@ -3037,7 +3307,12 @@ static void ffs_reset_work(struct work_struct *work)
{
struct ffs_data *ffs = container_of(work,
struct ffs_data, reset_work);
+
+ ffs_log("enter");
+
ffs_data_reset(ffs);
+
+ ffs_log("exit");
}
static int ffs_func_set_alt(struct usb_function *f,
@@ -3047,6 +3322,8 @@ static int ffs_func_set_alt(struct usb_function *f,
struct ffs_data *ffs = func->ffs;
int ret = 0, intf;
+ ffs_log("enter");
+
if (alt != (unsigned)-1) {
intf = ffs_func_revmap_intf(func, interface);
if (unlikely(intf < 0))
@@ -3082,6 +3359,8 @@ static int ffs_func_set_alt(struct usb_function *f,
usb_gadget_autopm_get_async(ffs->gadget);
}
+ ffs_log("exit: ret %d", ret);
+
return ret;
}
@@ -3090,9 +3369,13 @@ static void ffs_func_disable(struct usb_function *f)
struct ffs_function *func = ffs_func_from_usb(f);
struct ffs_data *ffs = func->ffs;
+ ffs_log("enter");
+
ffs_func_set_alt(f, 0, (unsigned)-1);
/* matching put to allow LPM on disconnect */
usb_gadget_autopm_put_async(ffs->gadget);
+
+ ffs_log("exit");
}
static int ffs_func_setup(struct usb_function *f,
@@ -3105,6 +3388,8 @@ static int ffs_func_setup(struct usb_function *f,
ENTER();
+ ffs_log("enter");
+
pr_vdebug("creq->bRequestType = %02x\n", creq->bRequestType);
pr_vdebug("creq->bRequest = %02x\n", creq->bRequest);
pr_vdebug("creq->wValue = %04x\n", le16_to_cpu(creq->wValue));
@@ -3148,19 +3433,31 @@ static int ffs_func_setup(struct usb_function *f,
__ffs_event_add(ffs, FUNCTIONFS_SETUP);
spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
+ ffs_log("exit");
+
return 0;
}
static void ffs_func_suspend(struct usb_function *f)
{
ENTER();
+
+ ffs_log("enter");
+
ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_SUSPEND);
+
+ ffs_log("exit");
}
static void ffs_func_resume(struct usb_function *f)
{
ENTER();
+
+ ffs_log("enter");
+
ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_RESUME);
+
+ ffs_log("exit");
}
@@ -3177,11 +3474,15 @@ static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
short *nums = func->interfaces_nums;
unsigned count = func->ffs->interfaces_count;
+ ffs_log("enter");
+
for (; count; --count, ++nums) {
if (*nums >= 0 && *nums == intf)
return nums - func->interfaces_nums;
}
+ ffs_log("exit");
+
return -EDOM;
}
@@ -3194,6 +3495,8 @@ static struct ffs_dev *_ffs_do_find_dev(const char *name)
{
struct ffs_dev *dev;
+ ffs_log("enter");
+
list_for_each_entry(dev, &ffs_devices, entry) {
if (!dev->name || !name)
continue;
@@ -3201,6 +3504,8 @@ static struct ffs_dev *_ffs_do_find_dev(const char *name)
return dev;
}
+ ffs_log("exit");
+
return NULL;
}
@@ -3211,12 +3516,16 @@ static struct ffs_dev *_ffs_get_single_dev(void)
{
struct ffs_dev *dev;
+ ffs_log("enter");
+
if (list_is_singular(&ffs_devices)) {
dev = list_first_entry(&ffs_devices, struct ffs_dev, entry);
if (dev->single)
return dev;
}
+ ffs_log("exit");
+
return NULL;
}
@@ -3227,11 +3536,17 @@ static struct ffs_dev *_ffs_find_dev(const char *name)
{
struct ffs_dev *dev;
+ ffs_log("enter");
+
dev = _ffs_get_single_dev();
if (dev)
return dev;
- return _ffs_do_find_dev(name);
+ dev = _ffs_do_find_dev(name);
+
+ ffs_log("exit");
+
+ return dev;
}
/* Configfs support *********************************************************/
@@ -3353,6 +3668,10 @@ static void ffs_func_unbind(struct usb_configuration *c,
unsigned long flags;
ENTER();
+
+ ffs_log("enter: state %d setup_state %d flag %lu", ffs->state,
+ ffs->setup_state, ffs->flags);
+
if (ffs->func == func) {
ffs_func_eps_disable(func);
ffs->func = NULL;
@@ -3383,6 +3702,9 @@ static void ffs_func_unbind(struct usb_configuration *c,
func->interfaces_nums = NULL;
ffs_event_add(ffs, FUNCTIONFS_UNBIND);
+
+ ffs_log("exit: state %d setup_state %d flag %lu", ffs->state,
+ ffs->setup_state, ffs->flags);
}
static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
@@ -3445,12 +3767,16 @@ static int _ffs_name_dev(struct ffs_dev *dev, const char *name)
{
struct ffs_dev *existing;
+ ffs_log("enter");
+
existing = _ffs_do_find_dev(name);
if (existing)
return -EBUSY;
dev->name = name;
+ ffs_log("exit");
+
return 0;
}
@@ -3461,10 +3787,14 @@ int ffs_name_dev(struct ffs_dev *dev, const char *name)
{
int ret;
+ ffs_log("enter");
+
ffs_dev_lock();
ret = _ffs_name_dev(dev, name);
ffs_dev_unlock();
+ ffs_log("exit");
+
return ret;
}
EXPORT_SYMBOL_GPL(ffs_name_dev);
@@ -3473,6 +3803,8 @@ int ffs_single_dev(struct ffs_dev *dev)
{
int ret;
+ ffs_log("enter");
+
ret = 0;
ffs_dev_lock();
@@ -3482,6 +3814,9 @@ int ffs_single_dev(struct ffs_dev *dev)
dev->single = true;
ffs_dev_unlock();
+
+ ffs_log("exit");
+
return ret;
}
EXPORT_SYMBOL_GPL(ffs_single_dev);
@@ -3491,12 +3826,17 @@ EXPORT_SYMBOL_GPL(ffs_single_dev);
*/
static void _ffs_free_dev(struct ffs_dev *dev)
{
+
+ ffs_log("enter");
+
list_del(&dev->entry);
if (dev->name_allocated)
kfree(dev->name);
kfree(dev);
if (list_empty(&ffs_devices))
functionfs_cleanup();
+
+ ffs_log("exit");
}
static void *ffs_acquire_dev(const char *dev_name)
@@ -3504,6 +3844,9 @@ static void *ffs_acquire_dev(const char *dev_name)
struct ffs_dev *ffs_dev;
ENTER();
+
+ ffs_log("enter");
+
ffs_dev_lock();
ffs_dev = _ffs_find_dev(dev_name);
@@ -3518,6 +3861,9 @@ static void *ffs_acquire_dev(const char *dev_name)
ffs_dev->mounted = true;
ffs_dev_unlock();
+
+ ffs_log("exit");
+
return ffs_dev;
}
@@ -3526,6 +3872,9 @@ static void ffs_release_dev(struct ffs_data *ffs_data)
struct ffs_dev *ffs_dev;
ENTER();
+
+ ffs_log("enter");
+
ffs_dev_lock();
ffs_dev = ffs_data->private_data;
@@ -3537,6 +3886,8 @@ static void ffs_release_dev(struct ffs_data *ffs_data)
}
ffs_dev_unlock();
+
+ ffs_log("exit");
}
static int ffs_ready(struct ffs_data *ffs)
@@ -3545,6 +3896,9 @@ static int ffs_ready(struct ffs_data *ffs)
int ret = 0;
ENTER();
+
+ ffs_log("enter");
+
ffs_dev_lock();
ffs_obj = ffs->private_data;
@@ -3569,6 +3923,9 @@ static int ffs_ready(struct ffs_data *ffs)
set_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags);
done:
ffs_dev_unlock();
+
+ ffs_log("exit");
+
return ret;
}
@@ -3578,11 +3935,16 @@ static void ffs_closed(struct ffs_data *ffs)
struct f_fs_opts *opts;
ENTER();
+
+ ffs_log("enter");
+
ffs_dev_lock();
ffs_obj = ffs->private_data;
- if (!ffs_obj)
+ if (!ffs_obj) {
+ ffs_dev_unlock();
goto done;
+ }
ffs_obj->desc_ready = false;
@@ -3590,20 +3952,29 @@ static void ffs_closed(struct ffs_data *ffs)
ffs_obj->ffs_closed_callback)
ffs_obj->ffs_closed_callback(ffs);
- if (ffs_obj->opts)
+ if (ffs_obj->opts) {
opts = ffs_obj->opts;
- else
+ } else {
+ ffs_dev_unlock();
goto done;
+ }
smp_mb__before_atomic();
if (opts->no_configfs || !opts->func_inst.group.cg_item.ci_parent
- || !atomic_read(&opts->func_inst.group.cg_item.ci_kref.refcount))
+ || !atomic_read(&opts->func_inst.group.cg_item.ci_kref.refcount)) {
+ ffs_dev_unlock();
goto done;
+ }
+
+ ffs_dev_unlock();
- unregister_gadget_item(ffs_obj->opts->
+ if (test_bit(FFS_FL_BOUND, &ffs->flags)) {
+ unregister_gadget_item(opts->
func_inst.group.cg_item.ci_parent->ci_parent);
+ ffs_log("unreg gadget done");
+ }
done:
- ffs_dev_unlock();
+ ffs_log("exit");
}
/* Misc helper functions ****************************************************/
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index 5612645d7237..a629723d19cb 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -39,6 +39,8 @@ static struct workqueue_struct *ipa_usb_wq;
static void ipa_disconnect_handler(struct gsi_data_port *d_port);
static int gsi_ctrl_send_notification(struct f_gsi *gsi,
enum gsi_ctrl_notify_state);
+static int gsi_alloc_trb_buffer(struct f_gsi *gsi);
+static void gsi_free_trb_buffer(struct f_gsi *gsi);
void post_event(struct gsi_data_port *port, u8 event)
{
@@ -474,6 +476,9 @@ static void ipa_disconnect_work_handler(struct gsi_data_port *d_port)
if (gsi->d_port.out_ep)
usb_gsi_ep_op(gsi->d_port.out_ep, NULL, GSI_EP_OP_FREE_TRBS);
+
+ /* free buffers allocated with each TRB */
+ gsi_free_trb_buffer(gsi);
}
static int ipa_suspend_work_handler(struct gsi_data_port *d_port)
@@ -547,6 +552,7 @@ static void ipa_work_handler(struct work_struct *w)
struct usb_gadget *gadget = d_port->gadget;
struct device *dev;
struct device *gad_dev;
+ struct f_gsi *gsi;
event = read_event(d_port);
@@ -566,31 +572,27 @@ static void ipa_work_handler(struct work_struct *w)
return;
}
+ gsi = d_port_to_gsi(d_port);
+
switch (d_port->sm_state) {
case STATE_UNINITIALIZED:
break;
case STATE_INITIALIZED:
if (event == EVT_CONNECT_IN_PROGRESS) {
+ usb_gadget_autopm_get(d_port->gadget);
+ log_event_dbg("%s: get = %d", __func__,
+ atomic_read(&gad_dev->power.usage_count));
+ /* allocate buffers used with each TRB */
+ ret = gsi_alloc_trb_buffer(gsi);
+ if (ret) {
+ log_event_err("%s: gsi_alloc_trb_failed\n",
+ __func__);
+ break;
+ }
ipa_connect_channels(d_port);
d_port->sm_state = STATE_CONNECT_IN_PROGRESS;
log_event_dbg("%s: ST_INIT_EVT_CONN_IN_PROG",
__func__);
- } else if (event == EVT_HOST_READY) {
- /*
- * When in a composition such as RNDIS + ADB,
- * RNDIS host sends a GEN_CURRENT_PACKET_FILTER msg
- * to enable/disable flow control eg. during RNDIS
- * adaptor disable/enable from device manager.
- * In the case of the msg to disable flow control,
- * connect IPA channels and enable data path.
- * EVT_HOST_READY is posted to the state machine
- * in the handler for this msg.
- */
- ipa_connect_channels(d_port);
- ipa_data_path_enable(d_port);
- d_port->sm_state = STATE_CONNECTED;
- log_event_dbg("%s: ST_INIT_EVT_HOST_READY",
- __func__);
}
break;
case STATE_CONNECT_IN_PROGRESS:
@@ -648,6 +650,7 @@ static void ipa_work_handler(struct work_struct *w)
&gad_dev->power.usage_count));
} else if (event == EVT_SUSPEND) {
if (peek_event(d_port) == EVT_DISCONNECTED) {
+ read_event(d_port);
ipa_disconnect_work_handler(d_port);
d_port->sm_state = STATE_INITIALIZED;
usb_gadget_autopm_put_async(d_port->gadget);
@@ -718,19 +721,12 @@ static void ipa_work_handler(struct work_struct *w)
case STATE_SUSPENDED:
if (event == EVT_RESUMED) {
+ usb_gadget_autopm_get(d_port->gadget);
+ log_event_dbg("%s: ST_SUS_EVT_RES", __func__);
+ log_event_dbg("%s: get = %d", __func__,
+ atomic_read(&gad_dev->power.usage_count));
ipa_resume_work_handler(d_port);
d_port->sm_state = STATE_CONNECTED;
- /*
- * Increment usage count here to disallow gadget
- * parent suspend. This counter will decrement
- * after IPA handshake is done in disconnect work
- * (due to cable disconnect) or in suspended state.
- */
- usb_gadget_autopm_get_noresume(d_port->gadget);
- log_event_dbg("%s: ST_SUS_EVT_RES", __func__);
- log_event_dbg("%s: get_nores2 = %d", __func__,
- atomic_read(
- &gad_dev->power.usage_count));
} else if (event == EVT_DISCONNECTED) {
ipa_disconnect_work_handler(d_port);
d_port->sm_state = STATE_INITIALIZED;
@@ -1714,6 +1710,92 @@ static int gsi_get_alt(struct usb_function *f, unsigned intf)
return -EINVAL;
}
+static int gsi_alloc_trb_buffer(struct f_gsi *gsi)
+{
+ u32 len_in = 0, len_out = 0;
+ int ret = 0;
+
+ log_event_dbg("allocate trb's buffer\n");
+
+ if (gsi->d_port.in_ep && !gsi->d_port.in_request.buf_base_addr) {
+ log_event_dbg("IN: num_bufs:=%zu, buf_len=%zu\n",
+ gsi->d_port.in_request.num_bufs,
+ gsi->d_port.in_request.buf_len);
+
+ len_in = gsi->d_port.in_request.buf_len *
+ gsi->d_port.in_request.num_bufs;
+ gsi->d_port.in_request.buf_base_addr =
+ dma_zalloc_coherent(gsi->d_port.gadget->dev.parent,
+ len_in, &gsi->d_port.in_request.dma, GFP_KERNEL);
+ if (!gsi->d_port.in_request.buf_base_addr) {
+ dev_err(&gsi->d_port.gadget->dev,
+ "IN buf_base_addr allocate failed %s\n",
+ gsi->function.name);
+ ret = -ENOMEM;
+ goto fail1;
+ }
+ }
+
+ if (gsi->d_port.out_ep && !gsi->d_port.out_request.buf_base_addr) {
+ log_event_dbg("OUT: num_bufs:=%zu, buf_len=%zu\n",
+ gsi->d_port.out_request.num_bufs,
+ gsi->d_port.out_request.buf_len);
+
+ len_out = gsi->d_port.out_request.buf_len *
+ gsi->d_port.out_request.num_bufs;
+ gsi->d_port.out_request.buf_base_addr =
+ dma_zalloc_coherent(gsi->d_port.gadget->dev.parent,
+ len_out, &gsi->d_port.out_request.dma, GFP_KERNEL);
+ if (!gsi->d_port.out_request.buf_base_addr) {
+ dev_err(&gsi->d_port.gadget->dev,
+ "OUT buf_base_addr allocate failed %s\n",
+ gsi->function.name);
+ ret = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ log_event_dbg("finished allocating trb's buffer\n");
+ return ret;
+
+fail:
+ if (len_in && gsi->d_port.in_request.buf_base_addr) {
+ dma_free_coherent(gsi->d_port.gadget->dev.parent, len_in,
+ gsi->d_port.in_request.buf_base_addr,
+ gsi->d_port.in_request.dma);
+ gsi->d_port.in_request.buf_base_addr = NULL;
+ }
+fail1:
+ return ret;
+}
+
+static void gsi_free_trb_buffer(struct f_gsi *gsi)
+{
+ u32 len;
+
+ log_event_dbg("freeing trb's buffer\n");
+
+ if (gsi->d_port.out_ep &&
+ gsi->d_port.out_request.buf_base_addr) {
+ len = gsi->d_port.out_request.buf_len *
+ gsi->d_port.out_request.num_bufs;
+ dma_free_coherent(gsi->d_port.gadget->dev.parent, len,
+ gsi->d_port.out_request.buf_base_addr,
+ gsi->d_port.out_request.dma);
+ gsi->d_port.out_request.buf_base_addr = NULL;
+ }
+
+ if (gsi->d_port.in_ep &&
+ gsi->d_port.in_request.buf_base_addr) {
+ len = gsi->d_port.in_request.buf_len *
+ gsi->d_port.in_request.num_bufs;
+ dma_free_coherent(gsi->d_port.gadget->dev.parent, len,
+ gsi->d_port.in_request.buf_base_addr,
+ gsi->d_port.in_request.dma);
+ gsi->d_port.in_request.buf_base_addr = NULL;
+ }
+}
+
static int gsi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_gsi *gsi = func_to_gsi(f);
@@ -1827,14 +1909,14 @@ static int gsi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (gsi->prot_id == IPA_USB_ECM)
gsi->d_port.cdc_filter = DEFAULT_FILTER;
+ post_event(&gsi->d_port, EVT_CONNECT_IN_PROGRESS);
/*
- * Increment usage count upon cable connect. Decrement
- * after IPA disconnect is done in disconnect work
- * (due to cable disconnect) or in suspend work.
+ * For RNDIS the event is posted from the flow control
+ * handler which is invoked when the host sends the
+ * GEN_CURRENT_PACKET_FILTER message.
*/
- usb_gadget_autopm_get_noresume(gsi->d_port.gadget);
-
- post_event(&gsi->d_port, EVT_CONNECT_IN_PROGRESS);
+ if (gsi->prot_id != IPA_USB_RNDIS)
+ post_event(&gsi->d_port, EVT_HOST_READY);
queue_work(gsi->d_port.ipa_usb_wq,
&gsi->d_port.usb_ipa_w);
}
@@ -2042,7 +2124,6 @@ static int gsi_update_function_bind_params(struct f_gsi *gsi,
struct usb_ep *ep;
struct usb_cdc_notification *event;
struct usb_function *f = &gsi->function;
- u32 len = 0;
int status;
/* maybe allocate device-global string IDs */
@@ -2162,37 +2243,9 @@ skip_string_id_alloc:
gsi->d_port.in_request.buf_len = info->in_req_buf_len;
gsi->d_port.in_request.num_bufs = info->in_req_num_buf;
- len = gsi->d_port.in_request.buf_len * gsi->d_port.in_request.num_bufs;
- dev_dbg(&cdev->gadget->dev, "%zu %zu\n", gsi->d_port.in_request.buf_len,
- gsi->d_port.in_request.num_bufs);
- gsi->d_port.in_request.buf_base_addr =
- dma_zalloc_coherent(cdev->gadget->dev.parent, len,
- &gsi->d_port.in_request.dma, GFP_KERNEL);
- if (!gsi->d_port.in_request.buf_base_addr) {
- dev_err(&cdev->gadget->dev,
- "IN buf_base_addr allocate failed %s\n",
- gsi->function.name);
- goto fail;
- }
-
if (gsi->d_port.out_ep) {
gsi->d_port.out_request.buf_len = info->out_req_buf_len;
gsi->d_port.out_request.num_bufs = info->out_req_num_buf;
- len =
- gsi->d_port.out_request.buf_len *
- gsi->d_port.out_request.num_bufs;
- dev_dbg(&cdev->gadget->dev, "%zu %zu\n",
- gsi->d_port.out_request.buf_len,
- gsi->d_port.out_request.num_bufs);
- gsi->d_port.out_request.buf_base_addr =
- dma_zalloc_coherent(cdev->gadget->dev.parent, len,
- &gsi->d_port.out_request.dma, GFP_KERNEL);
- if (!gsi->d_port.out_request.buf_base_addr) {
- dev_err(&cdev->gadget->dev,
- "OUT buf_base_addr allocate failed %s\n",
- gsi->function.name);
- goto fail;
- }
}
/* Initialize event queue */
@@ -2263,14 +2316,6 @@ fail:
gsi->d_port.out_ep->driver_data = NULL;
if (gsi->d_port.in_ep && gsi->d_port.in_ep->desc)
gsi->d_port.in_ep->driver_data = NULL;
- if (len && gsi->d_port.in_request.buf_base_addr)
- dma_free_coherent(cdev->gadget->dev.parent, len,
- gsi->d_port.in_request.buf_base_addr,
- gsi->d_port.in_request.dma);
- if (len && gsi->d_port.out_request.buf_base_addr)
- dma_free_coherent(cdev->gadget->dev.parent, len,
- gsi->d_port.out_request.buf_base_addr,
- gsi->d_port.out_request.dma);
log_event_err("%s: bind failed for %s", __func__, f->name);
return -ENOMEM;
}
@@ -2564,8 +2609,6 @@ fail:
static void gsi_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_gsi *gsi = func_to_gsi(f);
- struct usb_composite_dev *cdev = c->cdev;
- u32 len;
/*
* Use drain_workqueue to accomplish below conditions:
@@ -2600,19 +2643,7 @@ static void gsi_unbind(struct usb_configuration *c, struct usb_function *f)
if (gsi->c_port.notify) {
kfree(gsi->c_port.notify_req->buf);
usb_ep_free_request(gsi->c_port.notify, gsi->c_port.notify_req);
-
- len =
- gsi->d_port.out_request.buf_len *
- gsi->d_port.out_request.num_bufs;
- dma_free_coherent(&cdev->gadget->dev, len,
- gsi->d_port.out_request.buf_base_addr,
- gsi->d_port.out_request.dma);
}
-
- len = gsi->d_port.in_request.buf_len * gsi->d_port.in_request.num_bufs;
- dma_free_coherent(&cdev->gadget->dev, len,
- gsi->d_port.in_request.buf_base_addr,
- gsi->d_port.in_request.dma);
}
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index c6bfd13f6c92..1950e87b4219 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -787,6 +787,12 @@ static int iowarrior_probe(struct usb_interface *interface,
iface_desc = interface->cur_altsetting;
dev->product_id = le16_to_cpu(udev->descriptor.idProduct);
+ if (iface_desc->desc.bNumEndpoints < 1) {
+ dev_err(&interface->dev, "Invalid number of endpoints\n");
+ retval = -EINVAL;
+ goto error;
+ }
+
/* set up the endpoint information */
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c
index 5a29fc96f940..f8121eb4f63a 100644
--- a/drivers/usb/phy/phy-msm-qusb-v2.c
+++ b/drivers/usb/phy/phy-msm-qusb-v2.c
@@ -54,10 +54,6 @@
#define QUSB2PHY_PORT_TUNE2 0x240
-#define HS_PHY_CTRL_REG 0x10
-#define UTMI_OTG_VBUS_VALID BIT(20)
-#define SW_SESSVLD_SEL BIT(28)
-
#define QUSB2PHY_1P8_VOL_MIN 1800000 /* uV */
#define QUSB2PHY_1P8_VOL_MAX 1800000 /* uV */
#define QUSB2PHY_1P8_HPM_LOAD 30000 /* uA */
@@ -76,7 +72,6 @@ MODULE_PARM_DESC(phy_tune2, "QUSB PHY v2 TUNE2");
struct qusb_phy {
struct usb_phy phy;
void __iomem *base;
- void __iomem *qscratch_base;
void __iomem *tune2_efuse_reg;
struct clk *ref_clk_src;
@@ -625,25 +620,6 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
return 0;
}
-static void qusb_write_readback(void *base, u32 offset,
- const u32 mask, u32 val)
-{
- u32 write_val, tmp = readl_relaxed(base + offset);
-
- tmp &= ~mask; /* retain other bits */
- write_val = tmp | val;
-
- writel_relaxed(write_val, base + offset);
-
- /* Read back to see if val was written */
- tmp = readl_relaxed(base + offset);
- tmp &= mask; /* clear other bits */
-
- if (tmp != val)
- pr_err("%s: write: %x to QSCRATCH: %x FAILED\n",
- __func__, val, offset);
-}
-
static int qusb_phy_notify_connect(struct usb_phy *phy,
enum usb_device_speed speed)
{
@@ -651,21 +627,11 @@ static int qusb_phy_notify_connect(struct usb_phy *phy,
qphy->cable_connected = true;
- dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected);
-
if (qphy->qusb_phy_host_init_seq && qphy->phy.flags & PHY_HOST_MODE)
qusb_phy_host_init(phy);
- /* Set OTG VBUS Valid from HSPHY to controller */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- UTMI_OTG_VBUS_VALID,
- UTMI_OTG_VBUS_VALID);
-
- /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- SW_SESSVLD_SEL, SW_SESSVLD_SEL);
-
- dev_dbg(phy->dev, "QUSB2 phy connect notification\n");
+ dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n",
+ qphy->cable_connected);
return 0;
}
@@ -676,17 +642,8 @@ static int qusb_phy_notify_disconnect(struct usb_phy *phy,
qphy->cable_connected = false;
- dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected);
-
- /* Set OTG VBUS Valid from HSPHY to controller */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- UTMI_OTG_VBUS_VALID, 0);
-
- /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- SW_SESSVLD_SEL, 0);
-
- dev_dbg(phy->dev, "QUSB2 phy disconnect notification\n");
+ dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n",
+ qphy->cable_connected);
return 0;
}
@@ -768,16 +725,6 @@ static int qusb_phy_probe(struct platform_device *pdev)
return PTR_ERR(qphy->base);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "qscratch_base");
- if (res) {
- qphy->qscratch_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(qphy->qscratch_base)) {
- dev_dbg(dev, "couldn't ioremap qscratch_base\n");
- qphy->qscratch_base = NULL;
- }
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"emu_phy_base");
if (res) {
qphy->emu_phy_base = devm_ioremap_resource(dev, res);
@@ -852,7 +799,7 @@ static int qusb_phy_probe(struct platform_device *pdev)
}
of_property_read_u32_array(dev->of_node,
- "qcom,qemu-init-seq",
+ "qcom,emu-init-seq",
qphy->emu_init_seq,
qphy->emu_init_seq_len);
} else {
@@ -977,11 +924,8 @@ static int qusb_phy_probe(struct platform_device *pdev)
qphy->phy.set_suspend = qusb_phy_set_suspend;
qphy->phy.shutdown = qusb_phy_shutdown;
qphy->phy.type = USB_PHY_TYPE_USB2;
-
- if (qphy->qscratch_base) {
- qphy->phy.notify_connect = qusb_phy_notify_connect;
- qphy->phy.notify_disconnect = qusb_phy_notify_disconnect;
- }
+ qphy->phy.notify_connect = qusb_phy_notify_connect;
+ qphy->phy.notify_disconnect = qusb_phy_notify_disconnect;
ret = usb_add_phy_dev(&qphy->phy);
if (ret)
diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c
index 1eb0c8d6b62f..5ec08098d197 100644
--- a/drivers/usb/phy/phy-msm-qusb.c
+++ b/drivers/usb/phy/phy-msm-qusb.c
@@ -88,9 +88,6 @@
#define LINESTATE_DP BIT(0)
#define LINESTATE_DM BIT(1)
-#define HS_PHY_CTRL_REG 0x10
-#define UTMI_OTG_VBUS_VALID BIT(20)
-#define SW_SESSVLD_SEL BIT(28)
#define QUSB2PHY_1P8_VOL_MIN 1800000 /* uV */
#define QUSB2PHY_1P8_VOL_MAX 1800000 /* uV */
@@ -109,7 +106,6 @@ MODULE_PARM_DESC(tune2, "QUSB PHY TUNE2");
struct qusb_phy {
struct usb_phy phy;
void __iomem *base;
- void __iomem *qscratch_base;
void __iomem *tune2_efuse_reg;
void __iomem *ref_clk_base;
@@ -686,24 +682,6 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
return 0;
}
-static void qusb_write_readback(void *base, u32 offset,
- const u32 mask, u32 val)
-{
- u32 write_val, tmp = readl_relaxed(base + offset);
- tmp &= ~mask; /* retain other bits */
- write_val = tmp | val;
-
- writel_relaxed(write_val, base + offset);
-
- /* Read back to see if val was written */
- tmp = readl_relaxed(base + offset);
- tmp &= mask; /* clear other bits */
-
- if (tmp != val)
- pr_err("%s: write: %x to QSCRATCH: %x FAILED\n",
- __func__, val, offset);
-}
-
static int qusb_phy_notify_connect(struct usb_phy *phy,
enum usb_device_speed speed)
{
@@ -711,18 +689,8 @@ static int qusb_phy_notify_connect(struct usb_phy *phy,
qphy->cable_connected = true;
- dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected);
-
- /* Set OTG VBUS Valid from HSPHY to controller */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- UTMI_OTG_VBUS_VALID,
- UTMI_OTG_VBUS_VALID);
-
- /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- SW_SESSVLD_SEL, SW_SESSVLD_SEL);
-
- dev_dbg(phy->dev, "QUSB2 phy connect notification\n");
+ dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n",
+ qphy->cable_connected);
return 0;
}
@@ -733,17 +701,8 @@ static int qusb_phy_notify_disconnect(struct usb_phy *phy,
qphy->cable_connected = false;
- dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected);
-
- /* Set OTG VBUS Valid from HSPHY to controller */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- UTMI_OTG_VBUS_VALID, 0);
-
- /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */
- qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG,
- SW_SESSVLD_SEL, 0);
-
- dev_dbg(phy->dev, "QUSB2 phy disconnect notification\n");
+ dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n",
+ qphy->cable_connected);
return 0;
}
@@ -827,16 +786,6 @@ static int qusb_phy_probe(struct platform_device *pdev)
return PTR_ERR(qphy->base);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "qscratch_base");
- if (res) {
- qphy->qscratch_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(qphy->qscratch_base)) {
- dev_dbg(dev, "couldn't ioremap qscratch_base\n");
- qphy->qscratch_base = NULL;
- }
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"emu_phy_base");
if (res) {
qphy->emu_phy_base = devm_ioremap_resource(dev, res);
@@ -929,7 +878,7 @@ static int qusb_phy_probe(struct platform_device *pdev)
}
of_property_read_u32_array(dev->of_node,
- "qcom,qemu-init-seq",
+ "qcom,emu-init-seq",
qphy->emu_init_seq,
qphy->emu_init_seq_len);
} else {
@@ -1051,11 +1000,8 @@ static int qusb_phy_probe(struct platform_device *pdev)
qphy->phy.set_suspend = qusb_phy_set_suspend;
qphy->phy.shutdown = qusb_phy_shutdown;
qphy->phy.type = USB_PHY_TYPE_USB2;
-
- if (qphy->qscratch_base) {
- qphy->phy.notify_connect = qusb_phy_notify_connect;
- qphy->phy.notify_disconnect = qusb_phy_notify_disconnect;
- }
+ qphy->phy.notify_connect = qusb_phy_notify_connect;
+ qphy->phy.notify_disconnect = qusb_phy_notify_disconnect;
/*
* On some platforms multiple QUSB PHYs are available. If QUSB PHY is
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index c0f5c652d272..f1893e08e51a 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -190,7 +190,8 @@ static int usbhsf_pkt_handler(struct usbhs_pipe *pipe, int type)
goto __usbhs_pkt_handler_end;
}
- ret = func(pkt, &is_done);
+ if (likely(func))
+ ret = func(pkt, &is_done);
if (is_done)
__usbhsf_pkt_del(pkt);
@@ -889,6 +890,7 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
pkt->trans = len;
+ usbhsf_tx_irq_ctrl(pipe, 0);
INIT_WORK(&pkt->work, xfer_work);
schedule_work(&pkt->work);
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index 8f7a78e70975..fa14198daf77 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -158,10 +158,14 @@ static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
struct usbhs_pipe *pipe = pkt->pipe;
struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe);
struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt);
+ unsigned long flags;
ureq->req.actual = pkt->actual;
- usbhsg_queue_pop(uep, ureq, 0);
+ usbhs_lock(priv, flags);
+ if (uep)
+ __usbhsg_queue_pop(uep, ureq, 0);
+ usbhs_unlock(priv, flags);
}
static void usbhsg_queue_push(struct usbhsg_uep *uep,
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 7a76fe4c2f9e..bdc0f2f24f19 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -164,6 +164,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x18EF, 0xE025) }, /* ELV Marble Sound Board 1 */
{ USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */
{ USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */
+ { USB_DEVICE(0x1901, 0x0194) }, /* GE Healthcare Remote Alarm Box */
{ USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */
{ USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */
{ USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index 01bf53392819..244acb1299a9 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -447,6 +447,11 @@ static int cypress_generic_port_probe(struct usb_serial_port *port)
struct usb_serial *serial = port->serial;
struct cypress_private *priv;
+ if (!port->interrupt_out_urb || !port->interrupt_in_urb) {
+ dev_err(&port->dev, "required endpoint is missing\n");
+ return -ENODEV;
+ }
+
priv = kzalloc(sizeof(struct cypress_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -606,12 +611,6 @@ static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port)
cypress_set_termios(tty, port, &priv->tmp_termios);
/* setup the port and start reading from the device */
- if (!port->interrupt_in_urb) {
- dev_err(&port->dev, "%s - interrupt_in_urb is empty!\n",
- __func__);
- return -1;
- }
-
usb_fill_int_urb(port->interrupt_in_urb, serial->dev,
usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress),
port->interrupt_in_urb->transfer_buffer,
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 12b0e67473ba..3df7b7ec178e 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -1251,8 +1251,27 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num)
static int digi_startup(struct usb_serial *serial)
{
+ struct device *dev = &serial->interface->dev;
struct digi_serial *serial_priv;
int ret;
+ int i;
+
+ /* check whether the device has the expected number of endpoints */
+ if (serial->num_port_pointers < serial->type->num_ports + 1) {
+ dev_err(dev, "OOB endpoints missing\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < serial->type->num_ports + 1 ; i++) {
+ if (!serial->port[i]->read_urb) {
+ dev_err(dev, "bulk-in endpoint missing\n");
+ return -ENODEV;
+ }
+ if (!serial->port[i]->write_urb) {
+ dev_err(dev, "bulk-out endpoint missing\n");
+ return -ENODEV;
+ }
+ }
serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL);
if (!serial_priv)
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 8c660ae401d8..b61f12160d37 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1004,6 +1004,10 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(FTDI_VID, CHETCO_SEASMART_DISPLAY_PID) },
{ USB_DEVICE(FTDI_VID, CHETCO_SEASMART_LITE_PID) },
{ USB_DEVICE(FTDI_VID, CHETCO_SEASMART_ANALOG_PID) },
+ /* ICP DAS I-756xU devices */
+ { USB_DEVICE(ICPDAS_VID, ICPDAS_I7560U_PID) },
+ { USB_DEVICE(ICPDAS_VID, ICPDAS_I7561U_PID) },
+ { USB_DEVICE(ICPDAS_VID, ICPDAS_I7563U_PID) },
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index a84df2513994..c5d6c1e73e8e 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -872,6 +872,14 @@
#define NOVITUS_BONO_E_PID 0x6010
/*
+ * ICPDAS I-756*U devices
+ */
+#define ICPDAS_VID 0x1b5c
+#define ICPDAS_I7560U_PID 0x0103
+#define ICPDAS_I7561U_PID 0x0104
+#define ICPDAS_I7563U_PID 0x0105
+
+/*
* RT Systems programming cables for various ham radios
*/
#define RTSYSTEMS_VID 0x2100 /* Vendor ID */
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index fd707d6a10e2..89726f702202 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -376,14 +376,21 @@ static void mct_u232_msr_to_state(struct usb_serial_port *port,
static int mct_u232_port_probe(struct usb_serial_port *port)
{
+ struct usb_serial *serial = port->serial;
struct mct_u232_private *priv;
+ /* check first to simplify error handling */
+ if (!serial->port[1] || !serial->port[1]->interrupt_in_urb) {
+ dev_err(&port->dev, "expected endpoint missing\n");
+ return -ENODEV;
+ }
+
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* Use second interrupt-in endpoint for reading. */
- priv->read_urb = port->serial->port[1]->interrupt_in_urb;
+ priv->read_urb = serial->port[1]->interrupt_in_urb;
priv->read_urb->context = port;
spin_lock_init(&priv->lock);
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 348e19834b83..c6f497f16526 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -1818,6 +1818,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d02, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x02, 0x01) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) },
+ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */
{ USB_DEVICE_INTERFACE_CLASS(0x2020, 0x4000, 0xff) }, /* OLICARD300 - MT6225 */
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 5c66d3f7a6d0..9baf081174ce 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -2,7 +2,7 @@
* USB Attached SCSI
* Note that this is not the same as the USB Mass Storage driver
*
- * Copyright Hans de Goede <hdegoede@redhat.com> for Red Hat, Inc. 2013 - 2014
+ * Copyright Hans de Goede <hdegoede@redhat.com> for Red Hat, Inc. 2013 - 2016
* Copyright Matthew Wilcox for Intel Corp, 2010
* Copyright Sarah Sharp for Intel Corp, 2010
*
@@ -757,6 +757,17 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd)
return SUCCESS;
}
+static int uas_target_alloc(struct scsi_target *starget)
+{
+ struct uas_dev_info *devinfo = (struct uas_dev_info *)
+ dev_to_shost(starget->dev.parent)->hostdata;
+
+ if (devinfo->flags & US_FL_NO_REPORT_LUNS)
+ starget->no_report_luns = 1;
+
+ return 0;
+}
+
static int uas_slave_alloc(struct scsi_device *sdev)
{
struct uas_dev_info *devinfo =
@@ -800,7 +811,6 @@ static int uas_slave_configure(struct scsi_device *sdev)
if (devinfo->flags & US_FL_BROKEN_FUA)
sdev->broken_fua = 1;
- scsi_change_queue_depth(sdev, devinfo->qdepth - 2);
return 0;
}
@@ -808,11 +818,12 @@ static struct scsi_host_template uas_host_template = {
.module = THIS_MODULE,
.name = "uas",
.queuecommand = uas_queuecommand,
+ .target_alloc = uas_target_alloc,
.slave_alloc = uas_slave_alloc,
.slave_configure = uas_slave_configure,
.eh_abort_handler = uas_eh_abort_handler,
.eh_bus_reset_handler = uas_eh_bus_reset_handler,
- .can_queue = 65536, /* Is there a limit on the _host_ ? */
+ .can_queue = MAX_CMNDS,
.this_id = -1,
.sg_tablesize = SG_NONE,
.skip_settle_delay = 1,
@@ -932,6 +943,12 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
if (result)
goto set_alt0;
+ /*
+ * 1 tag is reserved for untagged commands +
+ * 1 tag to avoid off by one errors in some bridge firmwares
+ */
+ shost->can_queue = devinfo->qdepth - 2;
+
usb_set_intfdata(intf, shost);
result = scsi_add_host(shost, &intf->dev);
if (result)
diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h
index ccc113e83d88..53341a77d89f 100644
--- a/drivers/usb/storage/unusual_uas.h
+++ b/drivers/usb/storage/unusual_uas.h
@@ -64,6 +64,13 @@ UNUSUAL_DEV(0x0bc2, 0x3312, 0x0000, 0x9999,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_ATA_1X),
+/* Reported-by: David Webb <djw@noc.ac.uk> */
+UNUSUAL_DEV(0x0bc2, 0x331a, 0x0000, 0x9999,
+ "Seagate",
+ "Expansion Desk",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_REPORT_LUNS),
+
/* Reported-by: Hans de Goede <hdegoede@redhat.com> */
UNUSUAL_DEV(0x0bc2, 0x3320, 0x0000, 0x9999,
"Seagate",
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 43576ed31ccd..9de988a0f856 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -482,7 +482,7 @@ void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags)
US_FL_NO_READ_DISC_INFO | US_FL_NO_READ_CAPACITY_16 |
US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE |
US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES |
- US_FL_MAX_SECTORS_240);
+ US_FL_MAX_SECTORS_240 | US_FL_NO_REPORT_LUNS);
p = quirks;
while (*p) {
@@ -532,6 +532,9 @@ void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags)
case 'i':
f |= US_FL_IGNORE_DEVICE;
break;
+ case 'j':
+ f |= US_FL_NO_REPORT_LUNS;
+ break;
case 'l':
f |= US_FL_NOT_LOCKABLE;
break;
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index 88950e9cb2aa..b601cafba6fd 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -176,6 +176,7 @@ enum mdss_hw_capabilities {
MDSS_CAPS_10_BIT_SUPPORTED,
MDSS_CAPS_CWB_SUPPORTED,
MDSS_CAPS_MDP_VOTE_CLK_NOT_SUPPORTED,
+ MDSS_CAPS_AVR_SUPPORTED,
MDSS_CAPS_MAX,
};
diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c
index e391a5aaa45d..e883f045967d 100644
--- a/drivers/video/fbdev/msm/mdss_compat_utils.c
+++ b/drivers/video/fbdev/msm/mdss_compat_utils.c
@@ -299,7 +299,7 @@ static int __compat_atomic_commit(struct fb_info *info, unsigned int cmd,
struct mdp_layer_commit commit;
struct mdp_layer_commit32 commit32;
u32 layer_count;
- struct mdp_input_layer *layer_list = NULL, *layer;
+ struct mdp_input_layer *layer_list = NULL;
struct mdp_input_layer32 *layer_list32 = NULL;
struct mdp_output_layer *output_layer = NULL;
@@ -370,8 +370,8 @@ static int __compat_atomic_commit(struct fb_info *info, unsigned int cmd,
argp, layer_count);
for (i = 0; i < layer_count; i++) {
- kfree(layer[i].scale);
- mdss_mdp_free_layer_pp_info(&layer[i]);
+ kfree(layer_list[i].scale);
+ mdss_mdp_free_layer_pp_info(&layer_list[i]);
}
kfree(layer_list);
layer_list_err:
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index 06e5502910b2..4285a14e7f35 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -1747,6 +1747,9 @@ static void __mdss_dsi_update_video_mode_total(struct mdss_panel_data *pdata,
return;
}
+ if (ctrl_pdata->timing_db_mode)
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x1e8, 0x1);
+
vsync_period =
mdss_panel_get_vtotal(&pdata->panel_info);
hsync_period =
@@ -1756,23 +1759,13 @@ static void __mdss_dsi_update_video_mode_total(struct mdss_panel_data *pdata,
new_dsi_v_total =
((vsync_period - 1) << 16) | (hsync_period - 1);
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C,
- (current_dsi_v_total | 0x8000000));
- if (new_dsi_v_total & 0x8000000) {
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C,
- new_dsi_v_total);
- } else {
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C,
- (new_dsi_v_total | 0x8000000));
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C,
- (new_dsi_v_total & 0x7ffffff));
- }
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C, new_dsi_v_total);
if (ctrl_pdata->timing_db_mode)
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x1e4, 0x1);
- pr_debug("%s new_fps:%d vsync:%d hsync:%d frame_rate:%d\n",
- __func__, new_fps, vsync_period, hsync_period,
+ pr_debug("%s new_fps:%d new_vtotal:0x%X cur_vtotal:0x%X frame_rate:%d\n",
+ __func__, new_fps, new_dsi_v_total, current_dsi_v_total,
ctrl_pdata->panel_data.panel_info.mipi.frame_rate);
ctrl_pdata->panel_data.panel_info.current_fps = new_fps;
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index 9548ea471385..4bd705bdc05f 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -389,8 +389,10 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable)
}
gpio_set_value((ctrl_pdata->rst_gpio), 0);
gpio_free(ctrl_pdata->rst_gpio);
- if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio))
+ if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) {
+ gpio_set_value(ctrl_pdata->lcd_mode_sel_gpio, 0);
gpio_free(ctrl_pdata->lcd_mode_sel_gpio);
+ }
}
exit:
@@ -1967,7 +1969,6 @@ static void mdss_dsi_parse_dfps_config(struct device_node *pan_node,
pinfo->dfps_update = DFPS_SUSPEND_RESUME_MODE;
pr_debug("default dfps mode: suspend/resume\n");
}
- mdss_dsi_set_refresh_rate_range(pan_node, pinfo);
} else {
pinfo->dynamic_fps = false;
pr_debug("dfps update mode not configured: disable\n");
@@ -2528,6 +2529,8 @@ static int mdss_panel_parse_dt(struct device_node *np,
mdss_dsi_parse_dfps_config(np, ctrl_pdata);
+ mdss_dsi_set_refresh_rate_range(np, pinfo);
+
pinfo->is_dba_panel = of_property_read_bool(np,
"qcom,dba-panel");
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index 43e8373f18df..b2c0c78d3f2b 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -552,13 +552,19 @@ static ssize_t mdss_fb_get_panel_info(struct device *dev,
struct msm_fb_data_type *mfd = fbi->par;
struct mdss_panel_info *pinfo = mfd->panel_info;
int ret;
+ bool dfps_porch_mode = false;
+
+ if (pinfo->dfps_update == DFPS_IMMEDIATE_PORCH_UPDATE_MODE_HFP ||
+ pinfo->dfps_update == DFPS_IMMEDIATE_PORCH_UPDATE_MODE_VFP)
+ dfps_porch_mode = true;
ret = scnprintf(buf, PAGE_SIZE,
"pu_en=%d\nxstart=%d\nwalign=%d\nystart=%d\nhalign=%d\n"
"min_w=%d\nmin_h=%d\nroi_merge=%d\ndyn_fps_en=%d\n"
"min_fps=%d\nmax_fps=%d\npanel_name=%s\n"
"primary_panel=%d\nis_pluggable=%d\ndisplay_id=%s\n"
- "is_cec_supported=%d\nis_pingpong_split=%d\n",
+ "is_cec_supported=%d\nis_pingpong_split=%d\n"
+ "dfps_porch_mode=%d\n",
pinfo->partial_update_enabled,
pinfo->roi_alignment.xstart_pix_align,
pinfo->roi_alignment.width_pix_align,
@@ -570,7 +576,8 @@ static ssize_t mdss_fb_get_panel_info(struct device *dev,
pinfo->dynamic_fps, pinfo->min_fps, pinfo->max_fps,
pinfo->panel_name, pinfo->is_prim_panel,
pinfo->is_pluggable, pinfo->display_id,
- pinfo->is_cec_supported, is_pingpong_split(mfd));
+ pinfo->is_cec_supported, is_pingpong_split(mfd),
+ dfps_porch_mode);
return ret;
}
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
index 6a6cdc8b502c..4f1435d006b2 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
@@ -216,7 +216,7 @@ static int hdmi_edid_reset_parser(struct hdmi_edid_ctrl *edid_ctrl)
/* reset HDR related data */
edid_ctrl->hdr_supported = false;
edid_ctrl->hdr_data.eotf = 0;
- edid_ctrl->hdr_data.descriptor = 0;
+ edid_ctrl->hdr_data.metadata_type_one = false;
edid_ctrl->hdr_data.max_luminance = 0;
edid_ctrl->hdr_data.avg_luminance = 0;
edid_ctrl->hdr_data.min_luminance = 0;
@@ -794,7 +794,7 @@ static ssize_t hdmi_edid_sysfs_rda_hdr_data(struct device *dev,
ret = scnprintf(buf, PAGE_SIZE, "%d, %u, %u, %u, %u, %u\n",
edid_ctrl->hdr_supported,
edid_ctrl->hdr_data.eotf,
- edid_ctrl->hdr_data.descriptor,
+ edid_ctrl->hdr_data.metadata_type_one,
edid_ctrl->hdr_data.max_luminance,
edid_ctrl->hdr_data.avg_luminance,
edid_ctrl->hdr_data.min_luminance);
@@ -964,8 +964,8 @@ static void hdmi_edid_parse_hdrdb(struct hdmi_edid_ctrl *edid_ctrl,
/* Byte 3: Electro-Optical Transfer Functions */
edid_ctrl->hdr_data.eotf = data_block[2] & 0x3F;
- /* Byte 4: Static Metadata Descriptors */
- edid_ctrl->hdr_data.descriptor = data_block[3] & 0x1;
+ /* Byte 4: Static Metadata Descriptor Type 1 */
+ edid_ctrl->hdr_data.metadata_type_one = (data_block[3] & 0x1) & BIT(0);
/* Byte 5: Desired Content Maximum Luminance */
if (hdmi_edid_is_luminance_value_present(len, MAXIMUM_LUMINANCE))
@@ -2458,16 +2458,16 @@ u8 hdmi_edid_get_deep_color(void *input)
* Return: HDR data.
*/
void hdmi_edid_get_hdr_data(void *input,
- struct hdmi_edid_hdr_data *hdr_data)
+ struct hdmi_edid_hdr_data **hdr_data)
{
struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
- if (!edid_ctrl || !hdr_data) {
+ if (!edid_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
return;
}
- hdr_data = &edid_ctrl->hdr_data;
+ *hdr_data = &edid_ctrl->hdr_data;
}
bool hdmi_edid_is_s3d_mode_supported(void *input, u32 video_mode, u32 s3d_mode)
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h
index c818f3fc0d19..ce6cecbb2e03 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h
@@ -31,14 +31,14 @@ struct hdmi_edid_init_data {
/*
* struct hdmi_edid_hdr_data - HDR Static Metadata
* @eotf: Electro-Optical Transfer Function
- * @descriptor: Static Metadata Descriptor
+ * @metadata_type_one: Static Metadata Type 1 support
* @max_luminance: Desired Content Maximum Luminance
* @avg_luminance: Desired Content Frame-average Luminance
* @min_luminance: Desired Content Minimum Luminance
*/
struct hdmi_edid_hdr_data {
u32 eotf;
- u32 descriptor;
+ bool metadata_type_one;
u32 max_luminance;
u32 avg_luminance;
u32 min_luminance;
@@ -61,6 +61,6 @@ bool hdmi_edid_is_s3d_mode_supported(void *input,
u32 video_mode, u32 s3d_mode);
u8 hdmi_edid_get_deep_color(void *edid_ctrl);
void hdmi_edid_get_hdr_data(void *edid_ctrl,
- struct hdmi_edid_hdr_data *hdr_data);
+ struct hdmi_edid_hdr_data **hdr_data);
#endif /* __HDMI_EDID_H__ */
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c
index fb59d0b03afe..9ce47ccb5e09 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c
@@ -1041,6 +1041,7 @@ void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data)
register_data.hdcp_ctx = &ctrl->lib_ctx;
register_data.client_ops = &client_ops;
register_data.txmtr_ops = &txmtr_ops;
+ register_data.device_type = HDCP_TXMTR_HDMI;
register_data.client_ctx = ctrl;
register_data.tethered = ctrl->tethered;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
index 3d773371713d..10e7a2d1a940 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
@@ -79,6 +79,8 @@
#define HDMI_TX_MAX_FPS 120000
#define HDMI_TX_VERSION_403 0x40000003 /* msmcobalt */
+#define HDMI_GET_MSB(x) (x >> 8)
+#define HDMI_GET_LSB(x) (x & 0xff)
/* Enable HDCP by default */
static bool hdcp_feature_on = true;
@@ -111,6 +113,9 @@ static int hdmi_tx_enable_power(struct hdmi_tx_ctrl *hdmi_ctrl,
enum hdmi_tx_power_module_type module, int enable);
static int hdmi_tx_setup_tmds_clk_rate(struct hdmi_tx_ctrl *hdmi_ctrl);
static void hdmi_tx_fps_work(struct work_struct *work);
+static int hdmi_tx_pinctrl_set_state(struct hdmi_tx_ctrl *hdmi_ctrl,
+ enum hdmi_tx_power_module_type module, bool active);
+static void hdmi_panel_set_hdr_infoframe(struct hdmi_tx_ctrl *hdmi_ctrl);
static struct mdss_hw hdmi_tx_hw = {
.hw_ndx = MDSS_HW_HDMI,
@@ -285,6 +290,29 @@ static inline bool hdmi_tx_is_hdcp_enabled(struct hdmi_tx_ctrl *hdmi_ctrl)
hdmi_ctrl->hdcp_ops;
}
+/*
+ * The sink must support at least one electro-optical transfer function for
+ * HDMI controller to sendi the dynamic range and mastering infoframe.
+ */
+static inline bool hdmi_tx_is_hdr_supported(struct hdmi_tx_ctrl *hdmi_ctrl)
+{
+ struct hdmi_edid_hdr_data *hdr_data;
+
+ hdmi_edid_get_hdr_data(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID), &hdr_data);
+
+ return (hdr_data->eotf & BIT(0)) || (hdr_data->eotf & BIT(1)) ||
+ (hdr_data->eotf & BIT(2));
+}
+
+static inline bool hdmi_tx_metadata_type_one(struct hdmi_tx_ctrl *hdmi_ctrl)
+{
+ struct hdmi_edid_hdr_data *hdr_data;
+
+ hdmi_edid_get_hdr_data(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID), &hdr_data);
+
+ return hdr_data->metadata_type_one;
+}
+
static inline bool hdmi_tx_dc_support(struct hdmi_tx_ctrl *hdmi_ctrl)
{
return hdmi_ctrl->dc_feature_on && hdmi_ctrl->dc_support &&
@@ -478,25 +506,30 @@ void *hdmi_get_featuredata_from_sysfs_dev(struct device *device,
} /* hdmi_tx_get_featuredata_from_sysfs_dev */
EXPORT_SYMBOL(hdmi_get_featuredata_from_sysfs_dev);
-static int hdmi_tx_config_5v(struct hdmi_tx_ctrl *hdmi_ctrl, bool enable)
+static int hdmi_tx_config_5v(struct hdmi_tx_ctrl *ctrl, bool enable)
{
- struct dss_module_power *pd = NULL;
int ret = 0;
+ struct dss_module_power *pd = NULL;
- if (!hdmi_ctrl) {
- DEV_ERR("%s: invalid input\n", __func__);
+ if (!ctrl) {
+ DEV_ERR("%s: Invalid HDMI ctrl\n", __func__);
ret = -EINVAL;
goto end;
}
- pd = &hdmi_ctrl->pdata.power_data[HDMI_TX_HPD_PM];
- if (!pd || !pd->gpio_config) {
- DEV_ERR("%s: Error: invalid power data\n", __func__);
- ret = -EINVAL;
- goto end;
+ if (ctrl->hdmi_tx_version >= HDMI_TX_VERSION_403)
+ ret = hdmi_tx_pinctrl_set_state(ctrl, HDMI_TX_HPD_PM, enable);
+ else {
+ pd = &ctrl->pdata.power_data[HDMI_TX_HPD_PM];
+ if (!pd || !pd->gpio_config) {
+ DEV_ERR("%s: Invalid power data\n", __func__);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ gpio_set_value(pd->gpio_config->gpio, enable);
}
- gpio_set_value(pd->gpio_config->gpio, enable);
end:
return ret;
}
@@ -1220,12 +1253,6 @@ static ssize_t hdmi_tx_sysfs_wta_5v(struct device *dev,
}
mutex_lock(&hdmi_ctrl->tx_lock);
- pd = &hdmi_ctrl->pdata.power_data[HDMI_TX_HPD_PM];
- if (!pd || !pd->gpio_config) {
- DEV_ERR("%s: Error: invalid power data\n", __func__);
- ret = -EINVAL;
- goto end;
- }
ret = kstrtoint(buf, 10, &read);
if (ret) {
@@ -1245,6 +1272,72 @@ end:
return ret;
}
+static ssize_t hdmi_tx_sysfs_wta_hdr_stream(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ u32 const hdr_param_count = 13;
+ struct hdmi_tx_ctrl *ctrl = NULL;
+
+ ctrl = hdmi_tx_get_drvdata_from_sysfs_dev(dev);
+ if (!ctrl) {
+ pr_err("%s: invalid input\n", __func__);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (!hdmi_tx_is_hdr_supported(ctrl)) {
+ pr_err("%s: Sink does not support HDR\n", __func__);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u %u",
+ &ctrl->hdr_data.eotf,
+ &ctrl->hdr_data.display_primaries_x[0],
+ &ctrl->hdr_data.display_primaries_y[0],
+ &ctrl->hdr_data.display_primaries_x[1],
+ &ctrl->hdr_data.display_primaries_y[1],
+ &ctrl->hdr_data.display_primaries_x[2],
+ &ctrl->hdr_data.display_primaries_y[2],
+ &ctrl->hdr_data.white_point_x,
+ &ctrl->hdr_data.white_point_y,
+ &ctrl->hdr_data.max_luminance,
+ &ctrl->hdr_data.min_luminance,
+ &ctrl->hdr_data.max_content_light_level,
+ &ctrl->hdr_data.max_average_light_level)
+ != hdr_param_count) {
+ pr_err("%s: Invalid HDR stream data\n", __func__);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ pr_debug("%s: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ __func__,
+ ctrl->hdr_data.eotf,
+ ctrl->hdr_data.display_primaries_x[0],
+ ctrl->hdr_data.display_primaries_y[0],
+ ctrl->hdr_data.display_primaries_x[1],
+ ctrl->hdr_data.display_primaries_y[1],
+ ctrl->hdr_data.display_primaries_x[2],
+ ctrl->hdr_data.display_primaries_y[2]);
+
+ pr_debug("%s: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ __func__,
+ ctrl->hdr_data.white_point_x,
+ ctrl->hdr_data.white_point_y,
+ ctrl->hdr_data.max_luminance,
+ ctrl->hdr_data.min_luminance,
+ ctrl->hdr_data.max_content_light_level,
+ ctrl->hdr_data.max_average_light_level);
+
+ hdmi_panel_set_hdr_infoframe(ctrl);
+
+ ret = strnlen(buf, PAGE_SIZE);
+end:
+ return ret;
+}
+
static DEVICE_ATTR(connected, S_IRUGO, hdmi_tx_sysfs_rda_connected, NULL);
static DEVICE_ATTR(hdmi_audio_cb, S_IWUSR, NULL, hdmi_tx_sysfs_wta_audio_cb);
static DEVICE_ATTR(hot_plug, S_IWUSR, NULL, hdmi_tx_sysfs_wta_hot_plug);
@@ -1265,6 +1358,7 @@ static DEVICE_ATTR(avi_cn0_1, S_IWUSR, NULL, hdmi_tx_sysfs_wta_avi_cn_bits);
static DEVICE_ATTR(s3d_mode, S_IRUGO | S_IWUSR, hdmi_tx_sysfs_rda_s3d_mode,
hdmi_tx_sysfs_wta_s3d_mode);
static DEVICE_ATTR(5v, S_IWUSR, NULL, hdmi_tx_sysfs_wta_5v);
+static DEVICE_ATTR(hdr_stream, S_IWUSR, NULL, hdmi_tx_sysfs_wta_hdr_stream);
static struct attribute *hdmi_tx_fs_attrs[] = {
&dev_attr_connected.attr,
@@ -1280,6 +1374,7 @@ static struct attribute *hdmi_tx_fs_attrs[] = {
&dev_attr_avi_cn0_1.attr,
&dev_attr_s3d_mode.attr,
&dev_attr_5v.attr,
+ &dev_attr_hdr_stream.attr,
NULL,
};
static struct attribute_group hdmi_tx_fs_attrs_group = {
@@ -2209,7 +2304,7 @@ static int hdmi_tx_check_capability(struct hdmi_tx_ctrl *hdmi_ctrl)
DEV_DBG("%s: Features <HDMI:%s, HDCP:%s, Deep Color:%s>\n", __func__,
hdmi_disabled ? "OFF" : "ON", hdcp_disabled ? "OFF" : "ON",
- hdmi_ctrl->dc_feature_on ? "OFF" : "ON");
+ !hdmi_ctrl->dc_feature_on ? "OFF" : "ON");
if (hdmi_disabled) {
DEV_ERR("%s: HDMI disabled\n", __func__);
@@ -2635,6 +2730,102 @@ static void hdmi_tx_phy_reset(struct hdmi_tx_ctrl *hdmi_ctrl)
DSS_REG_W_ND(io, HDMI_PHY_CTRL, val | SW_RESET_PLL);
} /* hdmi_tx_phy_reset */
+static void hdmi_panel_set_hdr_infoframe(struct hdmi_tx_ctrl *ctrl)
+{
+ u32 packet_payload = 0;
+ u32 packet_header = 0;
+ u32 packet_control = 0;
+ u32 const type_code = 0x87;
+ u32 const version = 0x01;
+ u32 const length = 0x1a;
+ u32 const descriptor_id = 0x00;
+ struct dss_io_data *io = NULL;
+
+ if (!ctrl) {
+ pr_err("%s: invalid input\n", __func__);
+ return;
+ }
+
+ if (!hdmi_tx_is_hdr_supported(ctrl)) {
+ pr_err("%s: Sink does not support HDR\n", __func__);
+ return;
+ }
+
+ io = &ctrl->pdata.io[HDMI_TX_CORE_IO];
+ if (!io->base) {
+ pr_err("%s: core io not inititalized\n", __func__);
+ return;
+ }
+
+ /* Setup Packet header and payload */
+ packet_header = type_code | (version << 8) | (length << 16);
+ DSS_REG_W(io, HDMI_GENERIC0_HDR, packet_header);
+
+ packet_payload = (ctrl->hdr_data.eotf << 8);
+ if (hdmi_tx_metadata_type_one(ctrl)) {
+ packet_payload |= (descriptor_id << 16)
+ | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_x[0])
+ << 24);
+ DSS_REG_W(io, HDMI_GENERIC0_0, packet_payload);
+ } else {
+ pr_debug("%s: Metadata Type 1 not supported\n", __func__);
+ DSS_REG_W(io, HDMI_GENERIC0_0, packet_payload);
+ goto enable_packet_control;
+ }
+
+ packet_payload =
+ (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_x[0]))
+ | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_y[0]) << 8)
+ | (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_y[0]) << 16)
+ | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_x[1]) << 24);
+ DSS_REG_W(io, HDMI_GENERIC0_1, packet_payload);
+
+ packet_payload =
+ (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_x[1]))
+ | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_y[1]) << 8)
+ | (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_y[1]) << 16)
+ | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_x[2]) << 24);
+ DSS_REG_W(io, HDMI_GENERIC0_2, packet_payload);
+
+ packet_payload =
+ (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_x[2]))
+ | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_y[2]) << 8)
+ | (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_y[2]) << 16)
+ | (HDMI_GET_LSB(ctrl->hdr_data.white_point_x) << 24);
+ DSS_REG_W(io, HDMI_GENERIC0_3, packet_payload);
+
+ packet_payload =
+ (HDMI_GET_MSB(ctrl->hdr_data.white_point_x))
+ | (HDMI_GET_LSB(ctrl->hdr_data.white_point_y) << 8)
+ | (HDMI_GET_MSB(ctrl->hdr_data.white_point_y) << 16)
+ | (HDMI_GET_LSB(ctrl->hdr_data.max_luminance) << 24);
+ DSS_REG_W(io, HDMI_GENERIC0_4, packet_payload);
+
+ packet_payload =
+ (HDMI_GET_MSB(ctrl->hdr_data.max_luminance))
+ | (HDMI_GET_LSB(ctrl->hdr_data.min_luminance) << 8)
+ | (HDMI_GET_MSB(ctrl->hdr_data.min_luminance) << 16)
+ | (HDMI_GET_LSB(ctrl->hdr_data.max_content_light_level) << 24);
+ DSS_REG_W(io, HDMI_GENERIC0_5, packet_payload);
+
+ packet_payload =
+ (HDMI_GET_MSB(ctrl->hdr_data.max_content_light_level))
+ | (HDMI_GET_LSB(ctrl->hdr_data.max_average_light_level) << 8)
+ | (HDMI_GET_MSB(ctrl->hdr_data.max_average_light_level) << 16);
+ DSS_REG_W(io, HDMI_GENERIC0_6, packet_payload);
+
+enable_packet_control:
+ /*
+ * GENERIC0_LINE | GENERIC0_CONT | GENERIC0_SEND
+ * Setup HDMI TX generic packet control
+ * Enable this packet to transmit every frame
+ * Enable HDMI TX engine to transmit Generic packet 1
+ */
+ packet_control = DSS_REG_R_ND(io, HDMI_GEN_PKT_CTRL);
+ packet_control |= BIT(0) | BIT(1) | BIT(2) | BIT(16);
+ DSS_REG_W(io, HDMI_GEN_PKT_CTRL, packet_control);
+}
+
static int hdmi_tx_audio_info_setup(struct platform_device *pdev,
struct msm_ext_disp_audio_setup_params *params)
{
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.h b/drivers/video/fbdev/msm/mdss_hdmi_tx.h
index 462edac31c09..1c306df70c7e 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.h
@@ -20,6 +20,7 @@
#include "mdss_hdmi_audio.h"
#define MAX_SWITCH_NAME_SIZE 5
+#define HDR_PRIMARIES_COUNT 3
enum hdmi_tx_io_type {
HDMI_TX_CORE_IO,
@@ -61,6 +62,30 @@ struct hdmi_tx_pinctrl {
struct hdmi_tx_ctrl;
typedef int (*hdmi_tx_evt_handler) (struct hdmi_tx_ctrl *);
+/*
+ * struct hdmi_tx_hdr_stream - HDR video stream characteristics
+ * @eotf: Electro-Optical Transfer Function
+ * @display_primaries_x: display primaries data for x-coordinate
+ * @display_primaries_y: display primaries data for y-coordinate
+ * @white_point_x: white point data for x-coordinate
+ * @white_point_y: white point data for y-coordinate
+ * @max_luminance: content maximum luminance
+ * @min_luminance: content minimum luminance
+ * @max_content_light_level: content maximum light level
+ * @max_average_light_level: content average light level
+ */
+struct hdmi_tx_hdr_stream_data {
+ u32 eotf;
+ u32 display_primaries_x[HDR_PRIMARIES_COUNT];
+ u32 display_primaries_y[HDR_PRIMARIES_COUNT];
+ u32 white_point_x;
+ u32 white_point_y;
+ u32 max_luminance;
+ u32 min_luminance;
+ u32 max_content_light_level;
+ u32 max_average_light_level;
+};
+
struct hdmi_tx_ctrl {
struct platform_device *pdev;
struct hdmi_tx_platform_data pdata;
@@ -88,6 +113,7 @@ struct hdmi_tx_ctrl {
struct hdmi_panel_ops panel_ops;
struct msm_ext_disp_audio_setup_params audio_params;
struct work_struct fps_work;
+ struct hdmi_tx_hdr_stream_data hdr_data;
spinlock_t hpd_state_lock;
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index 5a355f226179..518b84fbad51 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -1950,6 +1950,7 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
mdss_set_quirk(mdata, MDSS_QUIRK_SRC_SPLIT_ALWAYS);
mdata->has_wb_ubwc = true;
set_bit(MDSS_CAPS_10_BIT_SUPPORTED, mdata->mdss_caps_map);
+ set_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map);
break;
default:
mdata->max_target_zorder = 4; /* excluding base layer */
@@ -2489,6 +2490,8 @@ ssize_t mdss_mdp_show_capabilities(struct device *dev,
SPRINT(" separate_rotator");
if (test_bit(MDSS_CAPS_CWB_SUPPORTED, mdata->mdss_caps_map))
SPRINT(" concurrent_writeback");
+ if (test_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map))
+ SPRINT(" avr");
SPRINT("\n");
#undef SPRINT
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index da5e7bb8a343..da60570c7085 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -303,6 +303,11 @@ enum mdp_wb_blk_caps {
MDSS_MDP_WB_UBWC = BIT(3),
};
+enum mdss_mdp_avr_mode {
+ MDSS_MDP_AVR_CONTINUOUS = 0,
+ MDSS_MDP_AVR_ONE_SHOT,
+};
+
/**
* enum perf_calc_vote_mode - enum to decide if mdss_mdp_get_bw_vote_mode
* function needs an extra efficiency factor.
@@ -391,6 +396,7 @@ struct mdss_mdp_ctl_intfs_ops {
/* to update lineptr, [1..yres] - enable, 0 - disable */
int (*update_lineptr)(struct mdss_mdp_ctl *ctl, bool enable);
+ int (*avr_ctrl_fnc)(struct mdss_mdp_ctl *);
};
struct mdss_mdp_cwb {
@@ -406,6 +412,11 @@ struct mdss_mdp_cwb {
struct work_struct cwb_work;
};
+struct mdss_mdp_avr_info {
+ bool avr_enabled;
+ int avr_mode;
+};
+
struct mdss_mdp_ctl {
u32 num;
char __iomem *base;
@@ -513,6 +524,7 @@ struct mdss_mdp_ctl {
/* dynamic resolution switch during cont-splash handoff */
bool switch_with_handoff;
+ struct mdss_mdp_avr_info avr_info;
};
struct mdss_mdp_mixer {
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index 3f2de6915c7e..2218e9c4ac81 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -5528,6 +5528,26 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
sctl = mdss_mdp_get_split_ctl(ctl);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
+ if (ctl->ops.avr_ctrl_fnc) {
+ ret = ctl->ops.avr_ctrl_fnc(ctl);
+ if (ret) {
+ pr_err("error configuring avr ctrl registers ctl=%d err=%d\n",
+ ctl->num, ret);
+ mutex_unlock(&ctl->lock);
+ return ret;
+ }
+ }
+
+ if (sctl && sctl->ops.avr_ctrl_fnc) {
+ ret = sctl->ops.avr_ctrl_fnc(sctl);
+ if (ret) {
+ pr_err("error configuring avr ctrl registers sctl=%d err=%d\n",
+ sctl->num, ret);
+ mutex_unlock(&ctl->lock);
+ return ret;
+ }
+ }
+
mutex_lock(&ctl->flush_lock);
/*
diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
index 74ab902f6e8e..de868bcd8f6f 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
@@ -688,6 +688,11 @@ enum mdss_mpd_intf_index {
#define MDSS_MDP_REG_INTF_PROG_LINE_INTR_CONF 0x250
#define MDSS_MDP_REG_INTF_VBLANK_END_CONF 0x264
+#define MDSS_MDP_REG_INTF_AVR_CONTROL 0x270
+#define MDSS_MDP_REG_INTF_AVR_MODE 0x274
+#define MDSS_MDP_REG_INTF_AVR_TRIGGER 0x278
+#define MDSS_MDP_REG_INTF_AVR_VTOTAL 0x27C
+
#define MDSS_MDP_REG_INTF_FRAME_LINE_COUNT_EN 0x0A8
#define MDSS_MDP_REG_INTF_FRAME_COUNT 0x0AC
#define MDSS_MDP_REG_INTF_LINE_COUNT 0x0B0
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
index d1ced303b059..72fc20d97f44 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
@@ -403,6 +403,76 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
}
}
+static void mdss_mdp_video_avr_vtotal_setup(struct mdss_mdp_ctl *ctl,
+ struct intf_timing_params *p,
+ struct mdss_mdp_video_ctx *ctx)
+{
+ struct mdss_data_type *mdata = ctl->mdata;
+
+ if (test_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map)) {
+ struct mdss_panel_data *pdata = ctl->panel_data;
+ u32 hsync_period = p->hsync_pulse_width + p->h_back_porch +
+ p->width + p->h_front_porch;
+ u32 vsync_period = p->vsync_pulse_width + p->v_back_porch +
+ p->height + p->v_front_porch;
+ u32 min_fps = pdata->panel_info.min_fps;
+ u32 diff_fps = abs(pdata->panel_info.default_fps - min_fps);
+ u32 vtotal = mdss_panel_get_vtotal(&pdata->panel_info);
+
+ int add_porches = mult_frac(vtotal, diff_fps, min_fps);
+
+ u32 vsync_period_slow = vsync_period + add_porches;
+ u32 avr_vtotal = vsync_period_slow * hsync_period;
+
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_AVR_VTOTAL, avr_vtotal);
+
+ MDSS_XLOG(min_fps, vsync_period, vsync_period_slow, avr_vtotal);
+ }
+}
+
+static int mdss_mdp_video_avr_trigger_setup(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_mdp_video_ctx *ctx = NULL;
+ struct mdss_data_type *mdata = ctl->mdata;
+
+ ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX];
+ if (!ctx || !ctx->ref_cnt) {
+ pr_err("invalid master ctx\n");
+ return -EINVAL;
+ }
+
+ if (!ctl->is_master)
+ return 0;
+
+ if (ctl->avr_info.avr_enabled &&
+ test_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map))
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_AVR_TRIGGER, 1);
+
+ return 0;
+}
+
+static void mdss_mdp_video_avr_ctrl_setup(struct mdss_mdp_video_ctx *ctx,
+ struct mdss_mdp_avr_info *avr_info, bool is_master)
+{
+ u32 avr_ctrl = 0;
+ u32 avr_mode = 0;
+
+ avr_ctrl = avr_info->avr_enabled;
+ avr_mode = avr_info->avr_mode;
+
+ /* Enable avr_vsync_clear_en bit to clear avr in next vsync */
+ if (avr_mode == MDSS_MDP_AVR_ONE_SHOT)
+ avr_mode |= (1 << 8);
+
+ if (is_master)
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_AVR_CONTROL, avr_ctrl);
+
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_AVR_MODE, avr_mode);
+
+ pr_debug("intf:%d avr_mode:%x avr_ctrl:%x\n",
+ ctx->intf_num, avr_mode, avr_ctrl);
+}
+
static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl,
struct intf_timing_params *p,
struct mdss_mdp_video_ctx *ctx)
@@ -1185,16 +1255,7 @@ static int mdss_mdp_video_vfp_fps_update(struct mdss_mdp_video_ctx *ctx,
new_vsync_period_f0 = (vsync_period * hsync_period);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0,
- current_vsync_period_f0 | 0x800000);
- if (new_vsync_period_f0 & 0x800000) {
- mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0,
new_vsync_period_f0);
- } else {
- mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0,
- new_vsync_period_f0 | 0x800000);
- mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0,
- new_vsync_period_f0 & 0x7fffff);
- }
pr_debug("if:%d vtotal:%d htotal:%d f0:0x%x nw_f0:0x%x\n",
ctx->intf_num, vsync_period, hsync_period,
@@ -1404,6 +1465,11 @@ static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl, int new_fps)
}
/*
+ * Make sure controller setting committed
+ */
+ wmb();
+
+ /*
* MDP INTF registers support DB on targets
* starting from MDP v1.5.
*/
@@ -1530,6 +1596,12 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
CTL_INTF_EVENT_FLAG_DEFAULT);
}
+ rc = mdss_mdp_video_avr_trigger_setup(ctl);
+ if (rc) {
+ pr_err("avr trigger setup failed\n");
+ return rc;
+ }
+
if (mdss_mdp_is_lineptr_supported(ctl))
mdss_mdp_video_lineptr_ctrl(ctl, true);
@@ -1673,7 +1745,9 @@ static void mdss_mdp_fetch_start_config(struct mdss_mdp_video_ctx *ctx,
h_total = mdss_panel_get_htotal(pinfo, true);
fetch_start = (v_total - pinfo->prg_fet) * h_total + 1;
- fetch_enable = BIT(31);
+
+ fetch_enable = mdp_video_read(ctx, MDSS_MDP_REG_INTF_CONFIG);
+ fetch_enable |= BIT(31);
if (pinfo->dynamic_fps && (pinfo->dfps_update ==
DFPS_IMMEDIATE_CLK_UPDATE_MODE))
@@ -1886,6 +1960,8 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl,
mdss_mdp_handoff_programmable_fetch(ctl, ctx);
}
+ mdss_mdp_video_avr_vtotal_setup(ctl, itp, ctx);
+
mdss_mdp_disable_prefill(ctl);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_PANEL_FORMAT, ctl->dst_format);
@@ -2124,6 +2200,29 @@ static int mdss_mdp_video_early_wake_up(struct mdss_mdp_ctl *ctl)
return 0;
}
+static int mdss_mdp_video_avr_ctrl(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_mdp_video_ctx *ctx = NULL, *sctx = NULL;
+
+ ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX];
+ if (!ctx || !ctx->ref_cnt) {
+ pr_err("invalid master ctx\n");
+ return -EINVAL;
+ }
+ mdss_mdp_video_avr_ctrl_setup(ctx, &ctl->avr_info, ctl->is_master);
+
+ if (is_pingpong_split(ctl->mfd)) {
+ sctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[SLAVE_CTX];
+ if (!sctx || !sctx->ref_cnt) {
+ pr_err("invalid slave ctx\n");
+ return -EINVAL;
+ }
+ mdss_mdp_video_avr_ctrl_setup(sctx, &ctl->avr_info, false);
+ }
+
+ return 0;
+}
+
int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl)
{
int intfs_num, ret = 0;
@@ -2144,6 +2243,7 @@ int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl)
ctl->ops.config_fps_fnc = mdss_mdp_video_config_fps;
ctl->ops.early_wake_up_fnc = mdss_mdp_video_early_wake_up;
ctl->ops.update_lineptr = mdss_mdp_video_lineptr_ctrl;
+ ctl->ops.avr_ctrl_fnc = mdss_mdp_video_avr_ctrl;
return 0;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index 0ae420724d61..35bd0932f321 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_layer.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c
@@ -288,6 +288,57 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd,
return ret;
}
+static int mdss_mdp_avr_validate(struct msm_fb_data_type *mfd,
+ struct mdp_layer_commit_v1 *commit)
+{
+ struct mdss_data_type *mdata = mfd_to_mdata(mfd);
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
+ int req = 0;
+ struct mdss_panel_info *pinfo = NULL;
+
+ if (!ctl || !mdata || !commit) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ if (!(commit->flags & MDP_COMMIT_AVR_EN))
+ return 0;
+
+ pinfo = &ctl->panel_data->panel_info;
+
+ if (!test_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map) ||
+ (pinfo->max_fps == pinfo->min_fps)) {
+ pr_err("AVR not supported\n");
+ return -ENODEV;
+ }
+
+ if (pinfo->dynamic_fps &&
+ !(pinfo->dfps_update == DFPS_IMMEDIATE_PORCH_UPDATE_MODE_HFP ||
+ pinfo->dfps_update == DFPS_IMMEDIATE_PORCH_UPDATE_MODE_VFP)) {
+ pr_err("Dynamic fps and AVR cannot coexists\n");
+ return -EINVAL;
+ }
+
+ if (!ctl->is_video_mode) {
+ pr_err("AVR not supported in command mode\n");
+ return -EINVAL;
+ }
+
+ return req;
+}
+
+static void __update_avr_info(struct mdss_mdp_ctl *ctl,
+ struct mdp_layer_commit_v1 *commit)
+{
+ if (commit->flags & MDP_COMMIT_AVR_EN)
+ ctl->avr_info.avr_enabled = true;
+
+ ctl->avr_info.avr_mode = MDSS_MDP_AVR_CONTINUOUS;
+
+ if (commit->flags & MDP_COMMIT_AVR_ONE_SHOT_MODE)
+ ctl->avr_info.avr_mode = MDSS_MDP_AVR_ONE_SHOT;
+}
+
/*
* __layer_needs_src_split() - check needs source split configuration
* @layer: input layer
@@ -2247,13 +2298,13 @@ int mdss_mdp_layer_pre_commit(struct msm_fb_data_type *mfd,
struct mdss_overlay_private *mdp5_data;
struct mdss_mdp_data *src_data[MDSS_MDP_MAX_SSPP];
struct mdss_mdp_validate_info_t *validate_info_list;
+ struct mdss_mdp_ctl *sctl = NULL;
mdp5_data = mfd_to_mdp5_data(mfd);
if (!mdp5_data || !mdp5_data->ctl)
return -EINVAL;
-
if (commit->output_layer) {
ret = __is_cwb_requested(commit->output_layer->flags);
if (IS_ERR_VALUE(ret)) {
@@ -2267,6 +2318,18 @@ int mdss_mdp_layer_pre_commit(struct msm_fb_data_type *mfd,
}
}
+ ret = mdss_mdp_avr_validate(mfd, commit);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("AVR validate failed\n");
+ return -EINVAL;
+ }
+
+ __update_avr_info(mdp5_data->ctl, commit);
+
+ sctl = mdss_mdp_get_split_ctl(mdp5_data->ctl);
+ if (sctl)
+ __update_avr_info(sctl, commit);
+
layer_list = commit->input_layers;
/* handle null commit */
@@ -2428,6 +2491,12 @@ int mdss_mdp_layer_atomic_validate(struct msm_fb_data_type *mfd,
}
}
+ rc = mdss_mdp_avr_validate(mfd, commit);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("AVR validate failed\n");
+ return -EINVAL;
+ }
+
return __validate_layers(mfd, file, commit);
}
diff --git a/drivers/video/fbdev/msm/mdss_panel.c b/drivers/video/fbdev/msm/mdss_panel.c
index 97025b3a9c23..61911810b2c0 100644
--- a/drivers/video/fbdev/msm/mdss_panel.c
+++ b/drivers/video/fbdev/msm/mdss_panel.c
@@ -644,6 +644,7 @@ void mdss_panel_info_from_timing(struct mdss_panel_timing *pt,
pinfo->dsc_enc_total = pt->dsc_enc_total;
pinfo->fbc = pt->fbc;
pinfo->compression_mode = pt->compression_mode;
+ pinfo->default_fps = pinfo->mipi.frame_rate;
pinfo->roi_alignment = pt->roi_alignment;
pinfo->te = pt->te;
diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c
index 8e5cf194cc0b..4469202eaa8e 100644
--- a/drivers/virtio/virtio_pci_modern.c
+++ b/drivers/virtio/virtio_pci_modern.c
@@ -17,6 +17,7 @@
*
*/
+#include <linux/delay.h>
#define VIRTIO_PCI_NO_LEGACY
#include "virtio_pci_common.h"
@@ -271,9 +272,13 @@ static void vp_reset(struct virtio_device *vdev)
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
/* 0 status means a reset. */
vp_iowrite8(0, &vp_dev->common->device_status);
- /* Flush out the status write, and flush in device writes,
- * including MSI-X interrupts, if any. */
- vp_ioread8(&vp_dev->common->device_status);
+ /* After writing 0 to device_status, the driver MUST wait for a read of
+ * device_status to return 0 before reinitializing the device.
+ * This will flush out the status write, and flush in device writes,
+ * including MSI-X interrupts, if any.
+ */
+ while (vp_ioread8(&vp_dev->common->device_status))
+ msleep(1);
/* Flush pending VQ/configuration callbacks. */
vp_synchronize_vectors(vdev);
}
diff --git a/drivers/watchdog/rc32434_wdt.c b/drivers/watchdog/rc32434_wdt.c
index 71e78ef4b736..3a75f3b53452 100644
--- a/drivers/watchdog/rc32434_wdt.c
+++ b/drivers/watchdog/rc32434_wdt.c
@@ -237,7 +237,7 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd,
return -EINVAL;
/* Fall through */
case WDIOC_GETTIMEOUT:
- return copy_to_user(argp, &timeout, sizeof(int));
+ return copy_to_user(argp, &timeout, sizeof(int)) ? -EFAULT : 0;
default:
return -ENOTTY;
}
diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c
index 524c22146429..44367783f07a 100644
--- a/drivers/xen/events/events_base.c
+++ b/drivers/xen/events/events_base.c
@@ -484,9 +484,19 @@ static void eoi_pirq(struct irq_data *data)
struct physdev_eoi eoi = { .irq = pirq_from_irq(data->irq) };
int rc = 0;
- irq_move_irq(data);
+ if (!VALID_EVTCHN(evtchn))
+ return;
- if (VALID_EVTCHN(evtchn))
+ if (unlikely(irqd_is_setaffinity_pending(data))) {
+ int masked = test_and_set_mask(evtchn);
+
+ clear_evtchn(evtchn);
+
+ irq_move_masked_irq(data);
+
+ if (!masked)
+ unmask_evtchn(evtchn);
+ } else
clear_evtchn(evtchn);
if (pirq_needs_eoi(data->irq)) {
@@ -1357,9 +1367,19 @@ static void ack_dynirq(struct irq_data *data)
{
int evtchn = evtchn_from_irq(data->irq);
- irq_move_irq(data);
+ if (!VALID_EVTCHN(evtchn))
+ return;
- if (VALID_EVTCHN(evtchn))
+ if (unlikely(irqd_is_setaffinity_pending(data))) {
+ int masked = test_and_set_mask(evtchn);
+
+ clear_evtchn(evtchn);
+
+ irq_move_masked_irq(data);
+
+ if (!masked)
+ unmask_evtchn(evtchn);
+ } else
clear_evtchn(evtchn);
}
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 0f09526aa7d9..5e5db3687e34 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1885,7 +1885,7 @@ static int start_ordered_ops(struct inode *inode, loff_t start, loff_t end)
*/
int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
{
- struct dentry *dentry = file->f_path.dentry;
+ struct dentry *dentry = file_dentry(file);
struct inode *inode = d_inode(dentry);
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_trans_handle *trans;
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 323e12cc9d2f..0e044d7ee721 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -4406,6 +4406,127 @@ static int btrfs_log_trailing_hole(struct btrfs_trans_handle *trans,
return ret;
}
+/*
+ * When we are logging a new inode X, check if it doesn't have a reference that
+ * matches the reference from some other inode Y created in a past transaction
+ * and that was renamed in the current transaction. If we don't do this, then at
+ * log replay time we can lose inode Y (and all its files if it's a directory):
+ *
+ * mkdir /mnt/x
+ * echo "hello world" > /mnt/x/foobar
+ * sync
+ * mv /mnt/x /mnt/y
+ * mkdir /mnt/x # or touch /mnt/x
+ * xfs_io -c fsync /mnt/x
+ * <power fail>
+ * mount fs, trigger log replay
+ *
+ * After the log replay procedure, we would lose the first directory and all its
+ * files (file foobar).
+ * For the case where inode Y is not a directory we simply end up losing it:
+ *
+ * echo "123" > /mnt/foo
+ * sync
+ * mv /mnt/foo /mnt/bar
+ * echo "abc" > /mnt/foo
+ * xfs_io -c fsync /mnt/foo
+ * <power fail>
+ *
+ * We also need this for cases where a snapshot entry is replaced by some other
+ * entry (file or directory) otherwise we end up with an unreplayable log due to
+ * attempts to delete the snapshot entry (entry of type BTRFS_ROOT_ITEM_KEY) as
+ * if it were a regular entry:
+ *
+ * mkdir /mnt/x
+ * btrfs subvolume snapshot /mnt /mnt/x/snap
+ * btrfs subvolume delete /mnt/x/snap
+ * rmdir /mnt/x
+ * mkdir /mnt/x
+ * fsync /mnt/x or fsync some new file inside it
+ * <power fail>
+ *
+ * The snapshot delete, rmdir of x, mkdir of a new x and the fsync all happen in
+ * the same transaction.
+ */
+static int btrfs_check_ref_name_override(struct extent_buffer *eb,
+ const int slot,
+ const struct btrfs_key *key,
+ struct inode *inode)
+{
+ int ret;
+ struct btrfs_path *search_path;
+ char *name = NULL;
+ u32 name_len = 0;
+ u32 item_size = btrfs_item_size_nr(eb, slot);
+ u32 cur_offset = 0;
+ unsigned long ptr = btrfs_item_ptr_offset(eb, slot);
+
+ search_path = btrfs_alloc_path();
+ if (!search_path)
+ return -ENOMEM;
+ search_path->search_commit_root = 1;
+ search_path->skip_locking = 1;
+
+ while (cur_offset < item_size) {
+ u64 parent;
+ u32 this_name_len;
+ u32 this_len;
+ unsigned long name_ptr;
+ struct btrfs_dir_item *di;
+
+ if (key->type == BTRFS_INODE_REF_KEY) {
+ struct btrfs_inode_ref *iref;
+
+ iref = (struct btrfs_inode_ref *)(ptr + cur_offset);
+ parent = key->offset;
+ this_name_len = btrfs_inode_ref_name_len(eb, iref);
+ name_ptr = (unsigned long)(iref + 1);
+ this_len = sizeof(*iref) + this_name_len;
+ } else {
+ struct btrfs_inode_extref *extref;
+
+ extref = (struct btrfs_inode_extref *)(ptr +
+ cur_offset);
+ parent = btrfs_inode_extref_parent(eb, extref);
+ this_name_len = btrfs_inode_extref_name_len(eb, extref);
+ name_ptr = (unsigned long)&extref->name;
+ this_len = sizeof(*extref) + this_name_len;
+ }
+
+ if (this_name_len > name_len) {
+ char *new_name;
+
+ new_name = krealloc(name, this_name_len, GFP_NOFS);
+ if (!new_name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ name_len = this_name_len;
+ name = new_name;
+ }
+
+ read_extent_buffer(eb, name, name_ptr, this_name_len);
+ di = btrfs_lookup_dir_item(NULL, BTRFS_I(inode)->root,
+ search_path, parent,
+ name, this_name_len, 0);
+ if (di && !IS_ERR(di)) {
+ ret = 1;
+ goto out;
+ } else if (IS_ERR(di)) {
+ ret = PTR_ERR(di);
+ goto out;
+ }
+ btrfs_release_path(search_path);
+
+ cur_offset += this_len;
+ }
+ ret = 0;
+out:
+ btrfs_free_path(search_path);
+ kfree(name);
+ return ret;
+}
+
/* log a single inode in the tree log.
* At least one parent directory for this inode must exist in the tree
* or be logged already.
@@ -4578,6 +4699,22 @@ again:
if (min_key.type == BTRFS_INODE_ITEM_KEY)
need_log_inode_item = false;
+ if ((min_key.type == BTRFS_INODE_REF_KEY ||
+ min_key.type == BTRFS_INODE_EXTREF_KEY) &&
+ BTRFS_I(inode)->generation == trans->transid) {
+ ret = btrfs_check_ref_name_override(path->nodes[0],
+ path->slots[0],
+ &min_key, inode);
+ if (ret < 0) {
+ err = ret;
+ goto out_unlock;
+ } else if (ret > 0) {
+ err = 1;
+ btrfs_set_log_full_commit(root->fs_info, trans);
+ goto out_unlock;
+ }
+ }
+
/* Skip xattrs, we log them later with btrfs_log_all_xattrs() */
if (min_key.type == BTRFS_XATTR_ITEM_KEY) {
if (ins_nr == 0)
diff --git a/fs/coredump.c b/fs/coredump.c
index 1777331eee76..dfc87c5f5a54 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -32,6 +32,9 @@
#include <linux/pipe_fs_i.h>
#include <linux/oom.h>
#include <linux/compat.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/path.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
@@ -627,6 +630,8 @@ void do_coredump(const siginfo_t *siginfo)
}
} else {
struct inode *inode;
+ int open_flags = O_CREAT | O_RDWR | O_NOFOLLOW |
+ O_LARGEFILE | O_EXCL;
if (cprm.limit < binfmt->min_coredump)
goto fail_unlock;
@@ -665,10 +670,27 @@ void do_coredump(const siginfo_t *siginfo)
* what matters is that at least one of the two processes
* writes its coredump successfully, not which one.
*/
- cprm.file = filp_open(cn.corename,
- O_CREAT | 2 | O_NOFOLLOW |
- O_LARGEFILE | O_EXCL,
- 0600);
+ if (need_suid_safe) {
+ /*
+ * Using user namespaces, normal user tasks can change
+ * their current->fs->root to point to arbitrary
+ * directories. Since the intention of the "only dump
+ * with a fully qualified path" rule is to control where
+ * coredumps may be placed using root privileges,
+ * current->fs->root must not be used. Instead, use the
+ * root directory of init_task.
+ */
+ struct path root;
+
+ task_lock(&init_task);
+ get_fs_root(init_task.fs, &root);
+ task_unlock(&init_task);
+ cprm.file = file_open_root(root.dentry, root.mnt,
+ cn.corename, open_flags, 0600);
+ path_put(&root);
+ } else {
+ cprm.file = filp_open(cn.corename, open_flags, 0600);
+ }
if (IS_ERR(cprm.file))
goto fail_unlock;
diff --git a/fs/dcache.c b/fs/dcache.c
index 877bcbbd03ff..18effa378f97 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1666,7 +1666,8 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op)
DCACHE_OP_REVALIDATE |
DCACHE_OP_WEAK_REVALIDATE |
DCACHE_OP_DELETE |
- DCACHE_OP_SELECT_INODE));
+ DCACHE_OP_SELECT_INODE |
+ DCACHE_OP_REAL));
dentry->d_op = op;
if (!op)
return;
@@ -1684,6 +1685,8 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op)
dentry->d_flags |= DCACHE_OP_PRUNE;
if (op->d_select_inode)
dentry->d_flags |= DCACHE_OP_SELECT_INODE;
+ if (op->d_real)
+ dentry->d_flags |= DCACHE_OP_REAL;
}
EXPORT_SYMBOL(d_set_d_op);
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 5cf6d8be48dd..786cb51cab56 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -850,6 +850,29 @@ do { \
#include "extents_status.h"
/*
+ * Lock subclasses for i_data_sem in the ext4_inode_info structure.
+ *
+ * These are needed to avoid lockdep false positives when we need to
+ * allocate blocks to the quota inode during ext4_map_blocks(), while
+ * holding i_data_sem for a normal (non-quota) inode. Since we don't
+ * do quota tracking for the quota inode, this avoids deadlock (as
+ * well as infinite recursion, since it isn't turtles all the way
+ * down...)
+ *
+ * I_DATA_SEM_NORMAL - Used for most inodes
+ * I_DATA_SEM_OTHER - Used by move_inode.c for the second normal inode
+ * where the second inode has larger inode number
+ * than the first
+ * I_DATA_SEM_QUOTA - Used for quota inodes only
+ */
+enum {
+ I_DATA_SEM_NORMAL = 0,
+ I_DATA_SEM_OTHER,
+ I_DATA_SEM_QUOTA,
+};
+
+
+/*
* fourth extended file system inode data in memory
*/
struct ext4_inode_info {
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index e032a0423e35..9bdbf98240a0 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -60,10 +60,10 @@ ext4_double_down_write_data_sem(struct inode *first, struct inode *second)
{
if (first < second) {
down_write(&EXT4_I(first)->i_data_sem);
- down_write_nested(&EXT4_I(second)->i_data_sem, SINGLE_DEPTH_NESTING);
+ down_write_nested(&EXT4_I(second)->i_data_sem, I_DATA_SEM_OTHER);
} else {
down_write(&EXT4_I(second)->i_data_sem);
- down_write_nested(&EXT4_I(first)->i_data_sem, SINGLE_DEPTH_NESTING);
+ down_write_nested(&EXT4_I(first)->i_data_sem, I_DATA_SEM_OTHER);
}
}
@@ -483,6 +483,13 @@ mext_check_arguments(struct inode *orig_inode,
return -EBUSY;
}
+ if (IS_NOQUOTA(orig_inode) || IS_NOQUOTA(donor_inode)) {
+ ext4_debug("ext4 move extent: The argument files should "
+ "not be quota files [ino:orig %lu, donor %lu]\n",
+ orig_inode->i_ino, donor_inode->i_ino);
+ return -EBUSY;
+ }
+
/* Ext4 move extent supports only extent based file */
if (!(ext4_test_inode_flag(orig_inode, EXT4_INODE_EXTENTS))) {
ext4_debug("ext4 move extent: orig file is not extents "
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index c9ab67da6e5a..ba1cf0bf2f81 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1292,9 +1292,9 @@ static int set_qf_name(struct super_block *sb, int qtype, substring_t *args)
return -1;
}
if (ext4_has_feature_quota(sb)) {
- ext4_msg(sb, KERN_ERR, "Cannot set journaled quota options "
- "when QUOTA feature is enabled");
- return -1;
+ ext4_msg(sb, KERN_INFO, "Journaled quota options "
+ "ignored when QUOTA feature is enabled");
+ return 1;
}
qname = match_strdup(args);
if (!qname) {
@@ -1657,10 +1657,10 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
return -1;
}
if (ext4_has_feature_quota(sb)) {
- ext4_msg(sb, KERN_ERR,
- "Cannot set journaled quota options "
+ ext4_msg(sb, KERN_INFO,
+ "Quota format mount options ignored "
"when QUOTA feature is enabled");
- return -1;
+ return 1;
}
sbi->s_jquota_fmt = m->mount_opt;
#endif
@@ -1721,11 +1721,11 @@ static int parse_options(char *options, struct super_block *sb,
#ifdef CONFIG_QUOTA
if (ext4_has_feature_quota(sb) &&
(test_opt(sb, USRQUOTA) || test_opt(sb, GRPQUOTA))) {
- ext4_msg(sb, KERN_ERR, "Cannot set quota options when QUOTA "
- "feature is enabled");
- return 0;
- }
- if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
+ ext4_msg(sb, KERN_INFO, "Quota feature enabled, usrquota and grpquota "
+ "mount options ignored.");
+ clear_opt(sb, USRQUOTA);
+ clear_opt(sb, GRPQUOTA);
+ } else if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
if (test_opt(sb, USRQUOTA) && sbi->s_qf_names[USRQUOTA])
clear_opt(sb, USRQUOTA);
@@ -4936,6 +4936,20 @@ static int ext4_quota_on_mount(struct super_block *sb, int type)
EXT4_SB(sb)->s_jquota_fmt, type);
}
+static void lockdep_set_quota_inode(struct inode *inode, int subclass)
+{
+ struct ext4_inode_info *ei = EXT4_I(inode);
+
+ /* The first argument of lockdep_set_subclass has to be
+ * *exactly* the same as the argument to init_rwsem() --- in
+ * this case, in init_once() --- or lockdep gets unhappy
+ * because the name of the lock is set using the
+ * stringification of the argument to init_rwsem().
+ */
+ (void) ei; /* shut up clang warning if !CONFIG_LOCKDEP */
+ lockdep_set_subclass(&ei->i_data_sem, subclass);
+}
+
/*
* Standard function to be called on quota_on
*/
@@ -4975,8 +4989,12 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id,
if (err)
return err;
}
-
- return dquot_quota_on(sb, type, format_id, path);
+ lockdep_set_quota_inode(path->dentry->d_inode, I_DATA_SEM_QUOTA);
+ err = dquot_quota_on(sb, type, format_id, path);
+ if (err)
+ lockdep_set_quota_inode(path->dentry->d_inode,
+ I_DATA_SEM_NORMAL);
+ return err;
}
static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
@@ -5002,8 +5020,11 @@ static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
/* Don't account quota for quota files to avoid recursion */
qf_inode->i_flags |= S_NOQUOTA;
+ lockdep_set_quota_inode(qf_inode, I_DATA_SEM_QUOTA);
err = dquot_enable(qf_inode, type, format_id, flags);
iput(qf_inode);
+ if (err)
+ lockdep_set_quota_inode(qf_inode, I_DATA_SEM_NORMAL);
return err;
}
diff --git a/fs/fhandle.c b/fs/fhandle.c
index d59712dfa3e7..ca3c3dd01789 100644
--- a/fs/fhandle.c
+++ b/fs/fhandle.c
@@ -228,7 +228,7 @@ long do_handle_open(int mountdirfd,
path_put(&path);
return fd;
}
- file = file_open_root(path.dentry, path.mnt, "", open_flag);
+ file = file_open_root(path.dentry, path.mnt, "", open_flag, 0);
if (IS_ERR(file)) {
put_unused_fd(fd);
retval = PTR_ERR(file);
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index bf8c78920bee..de11206dda63 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -281,13 +281,15 @@ locked_inode_to_wb_and_lock_list(struct inode *inode)
wb_get(wb);
spin_unlock(&inode->i_lock);
spin_lock(&wb->list_lock);
- wb_put(wb); /* not gonna deref it anymore */
/* i_wb may have changed inbetween, can't use inode_to_wb() */
- if (likely(wb == inode->i_wb))
- return wb; /* @inode already has ref */
+ if (likely(wb == inode->i_wb)) {
+ wb_put(wb); /* @inode already has ref */
+ return wb;
+ }
spin_unlock(&wb->list_lock);
+ wb_put(wb);
cpu_relax();
spin_lock(&inode->i_lock);
}
@@ -1339,10 +1341,10 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
* we go e.g. from filesystem. Flusher thread uses __writeback_single_inode()
* and does more profound writeback list handling in writeback_sb_inodes().
*/
-static int
-writeback_single_inode(struct inode *inode, struct bdi_writeback *wb,
- struct writeback_control *wbc)
+static int writeback_single_inode(struct inode *inode,
+ struct writeback_control *wbc)
{
+ struct bdi_writeback *wb;
int ret = 0;
spin_lock(&inode->i_lock);
@@ -1380,7 +1382,8 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb,
ret = __writeback_single_inode(inode, wbc);
wbc_detach_inode(wbc);
- spin_lock(&wb->list_lock);
+
+ wb = inode_to_wb_and_lock_list(inode);
spin_lock(&inode->i_lock);
/*
* If inode is clean, remove it from writeback lists. Otherwise don't
@@ -1455,6 +1458,7 @@ static long writeback_sb_inodes(struct super_block *sb,
while (!list_empty(&wb->b_io)) {
struct inode *inode = wb_inode(wb->b_io.prev);
+ struct bdi_writeback *tmp_wb;
if (inode->i_sb != sb) {
if (work->sb) {
@@ -1545,15 +1549,23 @@ static long writeback_sb_inodes(struct super_block *sb,
cond_resched();
}
-
- spin_lock(&wb->list_lock);
+ /*
+ * Requeue @inode if still dirty. Be careful as @inode may
+ * have been switched to another wb in the meantime.
+ */
+ tmp_wb = inode_to_wb_and_lock_list(inode);
spin_lock(&inode->i_lock);
if (!(inode->i_state & I_DIRTY_ALL))
wrote++;
- requeue_inode(inode, wb, &wbc);
+ requeue_inode(inode, tmp_wb, &wbc);
inode_sync_complete(inode);
spin_unlock(&inode->i_lock);
+ if (unlikely(tmp_wb != wb)) {
+ spin_unlock(&tmp_wb->list_lock);
+ spin_lock(&wb->list_lock);
+ }
+
/*
* bail out to wb_writeback() often enough to check
* background threshold and other termination conditions.
@@ -2340,7 +2352,6 @@ EXPORT_SYMBOL(sync_inodes_sb);
*/
int write_inode_now(struct inode *inode, int sync)
{
- struct bdi_writeback *wb = &inode_to_bdi(inode)->wb;
struct writeback_control wbc = {
.nr_to_write = LONG_MAX,
.sync_mode = sync ? WB_SYNC_ALL : WB_SYNC_NONE,
@@ -2352,7 +2363,7 @@ int write_inode_now(struct inode *inode, int sync)
wbc.nr_to_write = 0;
might_sleep();
- return writeback_single_inode(inode, wb, &wbc);
+ return writeback_single_inode(inode, &wbc);
}
EXPORT_SYMBOL(write_inode_now);
@@ -2369,7 +2380,7 @@ EXPORT_SYMBOL(write_inode_now);
*/
int sync_inode(struct inode *inode, struct writeback_control *wbc)
{
- return writeback_single_inode(inode, &inode_to_bdi(inode)->wb, wbc);
+ return writeback_single_inode(inode, wbc);
}
EXPORT_SYMBOL(sync_inode);
diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c
index 8e3ee1936c7e..c5b6b7165489 100644
--- a/fs/fuse/cuse.c
+++ b/fs/fuse/cuse.c
@@ -90,7 +90,7 @@ static struct list_head *cuse_conntbl_head(dev_t devt)
static ssize_t cuse_read_iter(struct kiocb *kiocb, struct iov_iter *to)
{
- struct fuse_io_priv io = { .async = 0, .file = kiocb->ki_filp };
+ struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(kiocb->ki_filp);
loff_t pos = 0;
return fuse_direct_io(&io, to, &pos, FUSE_DIO_CUSE);
@@ -98,7 +98,7 @@ static ssize_t cuse_read_iter(struct kiocb *kiocb, struct iov_iter *to)
static ssize_t cuse_write_iter(struct kiocb *kiocb, struct iov_iter *from)
{
- struct fuse_io_priv io = { .async = 0, .file = kiocb->ki_filp };
+ struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(kiocb->ki_filp);
loff_t pos = 0;
/*
* No locking or generic_write_checks(), the server is
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 8ff09059a91d..461dcf5e4526 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -546,6 +546,11 @@ static void fuse_release_user_pages(struct fuse_req *req, int write)
}
}
+static void fuse_io_release(struct kref *kref)
+{
+ kfree(container_of(kref, struct fuse_io_priv, refcnt));
+}
+
static ssize_t fuse_get_res_by_io(struct fuse_io_priv *io)
{
if (io->err)
@@ -603,8 +608,9 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos)
}
io->iocb->ki_complete(io->iocb, res, 0);
- kfree(io);
}
+
+ kref_put(&io->refcnt, fuse_io_release);
}
static void fuse_aio_complete_req(struct fuse_conn *fc, struct fuse_req *req)
@@ -631,6 +637,7 @@ static size_t fuse_async_req_send(struct fuse_conn *fc, struct fuse_req *req,
size_t num_bytes, struct fuse_io_priv *io)
{
spin_lock(&io->lock);
+ kref_get(&io->refcnt);
io->size += num_bytes;
io->reqs++;
spin_unlock(&io->lock);
@@ -709,7 +716,7 @@ static void fuse_short_read(struct fuse_req *req, struct inode *inode,
static int fuse_do_readpage(struct file *file, struct page *page)
{
- struct fuse_io_priv io = { .async = 0, .file = file };
+ struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(file);
struct inode *inode = page->mapping->host;
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_req *req;
@@ -1009,7 +1016,7 @@ static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file,
size_t res;
unsigned offset;
unsigned i;
- struct fuse_io_priv io = { .async = 0, .file = file };
+ struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(file);
for (i = 0; i < req->num_pages; i++)
fuse_wait_on_page_writeback(inode, req->pages[i]->index);
@@ -1430,7 +1437,7 @@ static ssize_t __fuse_direct_read(struct fuse_io_priv *io,
static ssize_t fuse_direct_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
- struct fuse_io_priv io = { .async = 0, .file = iocb->ki_filp };
+ struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb->ki_filp);
return __fuse_direct_read(&io, to, &iocb->ki_pos);
}
@@ -1438,7 +1445,7 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
- struct fuse_io_priv io = { .async = 0, .file = file };
+ struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(file);
ssize_t res;
if (is_bad_inode(inode))
@@ -2824,6 +2831,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t offset)
loff_t i_size;
size_t count = iov_iter_count(iter);
struct fuse_io_priv *io;
+ bool is_sync = is_sync_kiocb(iocb);
pos = offset;
inode = file->f_mapping->host;
@@ -2844,6 +2852,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t offset)
if (!io)
return -ENOMEM;
spin_lock_init(&io->lock);
+ kref_init(&io->refcnt);
io->reqs = 1;
io->bytes = -1;
io->size = 0;
@@ -2863,12 +2872,18 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t offset)
* to wait on real async I/O requests, so we must submit this request
* synchronously.
*/
- if (!is_sync_kiocb(iocb) && (offset + count > i_size) &&
+ if (!is_sync && (offset + count > i_size) &&
iov_iter_rw(iter) == WRITE)
io->async = false;
- if (io->async && is_sync_kiocb(iocb))
+ if (io->async && is_sync) {
+ /*
+ * Additional reference to keep io around after
+ * calling fuse_aio_complete()
+ */
+ kref_get(&io->refcnt);
io->done = &wait;
+ }
if (iov_iter_rw(iter) == WRITE) {
ret = fuse_direct_io(io, iter, &pos, FUSE_DIO_WRITE);
@@ -2881,14 +2896,14 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t offset)
fuse_aio_complete(io, ret < 0 ? ret : 0, -1);
/* we have a non-extending, async request, so return */
- if (!is_sync_kiocb(iocb))
+ if (!is_sync)
return -EIOCBQUEUED;
wait_for_completion(&wait);
ret = fuse_get_res_by_io(io);
}
- kfree(io);
+ kref_put(&io->refcnt, fuse_io_release);
if (iov_iter_rw(iter) == WRITE) {
if (ret > 0)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 8db84062741d..0cbeea6ee831 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -22,6 +22,7 @@
#include <linux/rbtree.h>
#include <linux/poll.h>
#include <linux/workqueue.h>
+#include <linux/kref.h>
/** Max number of pages that can be used in a single read request */
#define FUSE_MAX_PAGES_PER_REQ 32
@@ -248,6 +249,7 @@ struct fuse_args {
/** The request IO state (for asynchronous processing) */
struct fuse_io_priv {
+ struct kref refcnt;
int async;
spinlock_t lock;
unsigned reqs;
@@ -261,6 +263,13 @@ struct fuse_io_priv {
struct completion *done;
};
+#define FUSE_IO_PRIV_SYNC(f) \
+{ \
+ .refcnt = { ATOMIC_INIT(1) }, \
+ .async = 0, \
+ .file = f, \
+}
+
/**
* Request flags
*
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index 81e622681c82..624a57a9c4aa 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -1408,11 +1408,12 @@ out:
/**
* jbd2_mark_journal_empty() - Mark on disk journal as empty.
* @journal: The journal to update.
+ * @write_op: With which operation should we write the journal sb
*
* Update a journal's dynamic superblock fields to show that journal is empty.
* Write updated superblock to disk waiting for IO to complete.
*/
-static void jbd2_mark_journal_empty(journal_t *journal)
+static void jbd2_mark_journal_empty(journal_t *journal, int write_op)
{
journal_superblock_t *sb = journal->j_superblock;
@@ -1430,7 +1431,7 @@ static void jbd2_mark_journal_empty(journal_t *journal)
sb->s_start = cpu_to_be32(0);
read_unlock(&journal->j_state_lock);
- jbd2_write_superblock(journal, WRITE_FUA);
+ jbd2_write_superblock(journal, write_op);
/* Log is no longer empty */
write_lock(&journal->j_state_lock);
@@ -1716,7 +1717,13 @@ int jbd2_journal_destroy(journal_t *journal)
if (journal->j_sb_buffer) {
if (!is_journal_aborted(journal)) {
mutex_lock(&journal->j_checkpoint_mutex);
- jbd2_mark_journal_empty(journal);
+
+ write_lock(&journal->j_state_lock);
+ journal->j_tail_sequence =
+ ++journal->j_transaction_sequence;
+ write_unlock(&journal->j_state_lock);
+
+ jbd2_mark_journal_empty(journal, WRITE_FLUSH_FUA);
mutex_unlock(&journal->j_checkpoint_mutex);
} else
err = -EIO;
@@ -1975,7 +1982,7 @@ int jbd2_journal_flush(journal_t *journal)
* the magic code for a fully-recovered superblock. Any future
* commits of data to the journal will restore the current
* s_start value. */
- jbd2_mark_journal_empty(journal);
+ jbd2_mark_journal_empty(journal, WRITE_FUA);
mutex_unlock(&journal->j_checkpoint_mutex);
write_lock(&journal->j_state_lock);
J_ASSERT(!journal->j_running_transaction);
@@ -2021,7 +2028,7 @@ int jbd2_journal_wipe(journal_t *journal, int write)
if (write) {
/* Lock to make assertions happy... */
mutex_lock(&journal->j_checkpoint_mutex);
- jbd2_mark_journal_empty(journal);
+ jbd2_mark_journal_empty(journal, WRITE_FUA);
mutex_unlock(&journal->j_checkpoint_mutex);
}
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index ce5a21861074..5fc2162afb67 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -377,7 +377,7 @@ int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc,
again:
timestamp = jiffies;
gencount = nfs_inc_attr_generation_counter();
- error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, entry->cookie, pages,
+ error = NFS_PROTO(inode)->readdir(file_dentry(file), cred, entry->cookie, pages,
NFS_SERVER(inode)->dtsize, desc->plus);
if (error < 0) {
/* We requested READDIRPLUS, but the server doesn't grok it */
@@ -560,7 +560,7 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
count++;
if (desc->plus != 0)
- nfs_prime_dcache(desc->file->f_path.dentry, entry);
+ nfs_prime_dcache(file_dentry(desc->file), entry);
status = nfs_readdir_add_to_array(entry, page);
if (status != 0)
@@ -864,7 +864,7 @@ static bool nfs_dir_mapping_need_revalidate(struct inode *dir)
*/
static int nfs_readdir(struct file *file, struct dir_context *ctx)
{
- struct dentry *dentry = file->f_path.dentry;
+ struct dentry *dentry = file_dentry(file);
struct inode *inode = d_inode(dentry);
nfs_readdir_descriptor_t my_desc,
*desc = &my_desc;
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 3e2071a177fd..f714b98cfd74 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -927,7 +927,7 @@ int nfs_open(struct inode *inode, struct file *filp)
{
struct nfs_open_context *ctx;
- ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
+ ctx = alloc_nfs_open_context(file_dentry(filp), filp->f_mode);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
nfs_file_set_open_context(filp, ctx);
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index db9b5fea5b3e..679e003818b1 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -26,7 +26,7 @@ static int
nfs4_file_open(struct inode *inode, struct file *filp)
{
struct nfs_open_context *ctx;
- struct dentry *dentry = filp->f_path.dentry;
+ struct dentry *dentry = file_dentry(filp);
struct dentry *parent = NULL;
struct inode *dir;
unsigned openflags = filp->f_flags;
@@ -57,7 +57,7 @@ nfs4_file_open(struct inode *inode, struct file *filp)
parent = dget_parent(dentry);
dir = d_inode(parent);
- ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
+ ctx = alloc_nfs_open_context(file_dentry(filp), filp->f_mode);
err = PTR_ERR(ctx);
if (IS_ERR(ctx))
goto out;
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index a9f096c7e99f..7d5351cd67fb 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -877,6 +877,7 @@ nfsd4_secinfo(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
&exp, &dentry);
if (err)
return err;
+ fh_unlock(&cstate->current_fh);
if (d_really_is_negative(dentry)) {
exp_put(exp);
err = nfserr_noent;
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 51c9e9ca39a4..12935209deca 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -1072,8 +1072,9 @@ nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename
READ_BUF(4);
rename->rn_snamelen = be32_to_cpup(p++);
- READ_BUF(rename->rn_snamelen + 4);
+ READ_BUF(rename->rn_snamelen);
SAVEMEM(rename->rn_sname, rename->rn_snamelen);
+ READ_BUF(4);
rename->rn_tnamelen = be32_to_cpup(p++);
READ_BUF(rename->rn_tnamelen);
SAVEMEM(rename->rn_tname, rename->rn_tnamelen);
@@ -1155,13 +1156,14 @@ nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclient
READ_BUF(8);
setclientid->se_callback_prog = be32_to_cpup(p++);
setclientid->se_callback_netid_len = be32_to_cpup(p++);
-
- READ_BUF(setclientid->se_callback_netid_len + 4);
+ READ_BUF(setclientid->se_callback_netid_len);
SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len);
+ READ_BUF(4);
setclientid->se_callback_addr_len = be32_to_cpup(p++);
- READ_BUF(setclientid->se_callback_addr_len + 4);
+ READ_BUF(setclientid->se_callback_addr_len);
SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len);
+ READ_BUF(4);
setclientid->se_callback_ident = be32_to_cpup(p++);
DECODE_TAIL;
@@ -1815,8 +1817,9 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
READ_BUF(4);
argp->taglen = be32_to_cpup(p++);
- READ_BUF(argp->taglen + 8);
+ READ_BUF(argp->taglen);
SAVEMEM(argp->tag, argp->taglen);
+ READ_BUF(8);
argp->minorversion = be32_to_cpup(p++);
argp->opcnt = be32_to_cpup(p++);
max_reply += 4 + (XDR_QUADLEN(argp->taglen) << 2);
diff --git a/fs/ocfs2/dlm/dlmconvert.c b/fs/ocfs2/dlm/dlmconvert.c
index e36d63ff1783..f90931335c6b 100644
--- a/fs/ocfs2/dlm/dlmconvert.c
+++ b/fs/ocfs2/dlm/dlmconvert.c
@@ -262,6 +262,7 @@ enum dlm_status dlmconvert_remote(struct dlm_ctxt *dlm,
struct dlm_lock *lock, int flags, int type)
{
enum dlm_status status;
+ u8 old_owner = res->owner;
mlog(0, "type=%d, convert_type=%d, busy=%d\n", lock->ml.type,
lock->ml.convert_type, res->state & DLM_LOCK_RES_IN_PROGRESS);
@@ -287,6 +288,19 @@ enum dlm_status dlmconvert_remote(struct dlm_ctxt *dlm,
status = DLM_DENIED;
goto bail;
}
+
+ if (lock->ml.type == type && lock->ml.convert_type == LKM_IVMODE) {
+ mlog(0, "last convert request returned DLM_RECOVERING, but "
+ "owner has already queued and sent ast to me. res %.*s, "
+ "(cookie=%u:%llu, type=%d, conv=%d)\n",
+ res->lockname.len, res->lockname.name,
+ dlm_get_lock_cookie_node(be64_to_cpu(lock->ml.cookie)),
+ dlm_get_lock_cookie_seq(be64_to_cpu(lock->ml.cookie)),
+ lock->ml.type, lock->ml.convert_type);
+ status = DLM_NORMAL;
+ goto bail;
+ }
+
res->state |= DLM_LOCK_RES_IN_PROGRESS;
/* move lock to local convert queue */
/* do not alter lock refcount. switching lists. */
@@ -316,11 +330,19 @@ enum dlm_status dlmconvert_remote(struct dlm_ctxt *dlm,
spin_lock(&res->spinlock);
res->state &= ~DLM_LOCK_RES_IN_PROGRESS;
lock->convert_pending = 0;
- /* if it failed, move it back to granted queue */
+ /* if it failed, move it back to granted queue.
+ * if master returns DLM_NORMAL and then down before sending ast,
+ * it may have already been moved to granted queue, reset to
+ * DLM_RECOVERING and retry convert */
if (status != DLM_NORMAL) {
if (status != DLM_NOTQUEUED)
dlm_error(status);
dlm_revert_pending_convert(res, lock);
+ } else if ((res->state & DLM_LOCK_RES_RECOVERING) ||
+ (old_owner != res->owner)) {
+ mlog(0, "res %.*s is in recovering or has been recovered.\n",
+ res->lockname.len, res->lockname.name);
+ status = DLM_RECOVERING;
}
bail:
spin_unlock(&res->spinlock);
diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c
index 42f0cae93a0a..4a338803e7e9 100644
--- a/fs/ocfs2/dlm/dlmrecovery.c
+++ b/fs/ocfs2/dlm/dlmrecovery.c
@@ -2064,7 +2064,6 @@ void dlm_move_lockres_to_recovery_list(struct dlm_ctxt *dlm,
dlm_lock_get(lock);
if (lock->convert_pending) {
/* move converting lock back to granted */
- BUG_ON(i != DLM_CONVERTING_LIST);
mlog(0, "node died with convert pending "
"on %.*s. move back to granted list.\n",
res->lockname.len, res->lockname.name);
diff --git a/fs/open.c b/fs/open.c
index b6f1e96a7c0b..6a24f988d253 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -995,14 +995,12 @@ struct file *filp_open(const char *filename, int flags, umode_t mode)
EXPORT_SYMBOL(filp_open);
struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt,
- const char *filename, int flags)
+ const char *filename, int flags, umode_t mode)
{
struct open_flags op;
- int err = build_open_flags(flags, 0, &op);
+ int err = build_open_flags(flags, mode, &op);
if (err)
return ERR_PTR(err);
- if (flags & O_CREAT)
- return ERR_PTR(-EINVAL);
return do_file_open_root(dentry, mnt, filename, &op);
}
EXPORT_SYMBOL(file_open_root);
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 000b2ed05c29..a1acc6004a91 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -276,6 +276,37 @@ static void ovl_dentry_release(struct dentry *dentry)
}
}
+static struct dentry *ovl_d_real(struct dentry *dentry, struct inode *inode)
+{
+ struct dentry *real;
+
+ if (d_is_dir(dentry)) {
+ if (!inode || inode == d_inode(dentry))
+ return dentry;
+ goto bug;
+ }
+
+ real = ovl_dentry_upper(dentry);
+ if (real && (!inode || inode == d_inode(real)))
+ return real;
+
+ real = ovl_dentry_lower(dentry);
+ if (!real)
+ goto bug;
+
+ if (!inode || inode == d_inode(real))
+ return real;
+
+ /* Handle recursion */
+ if (real->d_flags & DCACHE_OP_REAL)
+ return real->d_op->d_real(real, inode);
+
+bug:
+ WARN(1, "ovl_d_real(%pd4, %s:%lu\n): real dentry not found\n", dentry,
+ inode ? inode->i_sb->s_id : "NULL", inode ? inode->i_ino : 0);
+ return dentry;
+}
+
static int ovl_dentry_revalidate(struct dentry *dentry, unsigned int flags)
{
struct ovl_entry *oe = dentry->d_fsdata;
@@ -320,11 +351,13 @@ static int ovl_dentry_weak_revalidate(struct dentry *dentry, unsigned int flags)
static const struct dentry_operations ovl_dentry_operations = {
.d_release = ovl_dentry_release,
.d_select_inode = ovl_d_select_inode,
+ .d_real = ovl_d_real,
};
static const struct dentry_operations ovl_reval_dentry_operations = {
.d_release = ovl_dentry_release,
.d_select_inode = ovl_d_select_inode,
+ .d_real = ovl_d_real,
.d_revalidate = ovl_dentry_revalidate,
.d_weak_revalidate = ovl_dentry_weak_revalidate,
};
diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c
index 8ebd9a334085..87645955990d 100644
--- a/fs/proc_namespace.c
+++ b/fs/proc_namespace.c
@@ -197,6 +197,8 @@ static int show_vfsstat(struct seq_file *m, struct vfsmount *mnt)
if (sb->s_op->show_devname) {
seq_puts(m, "device ");
err = sb->s_op->show_devname(m, mnt_path.dentry);
+ if (err)
+ goto out;
} else {
if (r->mnt_devname) {
seq_puts(m, "device ");
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index ef0d64b2a6d9..353ff31dcee1 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -1398,7 +1398,7 @@ static int dquot_active(const struct inode *inode)
static int __dquot_initialize(struct inode *inode, int type)
{
int cnt, init_needed = 0;
- struct dquot **dquots, *got[MAXQUOTAS];
+ struct dquot **dquots, *got[MAXQUOTAS] = {};
struct super_block *sb = inode->i_sb;
qsize_t rsv;
int ret = 0;
@@ -1415,7 +1415,6 @@ static int __dquot_initialize(struct inode *inode, int type)
int rc;
struct dquot *dquot;
- got[cnt] = NULL;
if (type != -1 && cnt != type)
continue;
/*
diff --git a/fs/splice.c b/fs/splice.c
index 4cf700d50b40..0f77e9682857 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -185,6 +185,9 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
unsigned int spd_pages = spd->nr_pages;
int ret, do_wakeup, page_nr;
+ if (!spd_pages)
+ return 0;
+
ret = 0;
do_wakeup = 0;
page_nr = 0;
diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c
index 0ef7c2ed3f8a..4fa14820e2e2 100644
--- a/fs/xfs/xfs_attr_list.c
+++ b/fs/xfs/xfs_attr_list.c
@@ -202,8 +202,10 @@ xfs_attr_shortform_list(xfs_attr_list_context_t *context)
sbp->namelen,
sbp->valuelen,
&sbp->name[sbp->namelen]);
- if (error)
+ if (error) {
+ kmem_free(sbuf);
return error;
+ }
if (context->seen_enough)
break;
cursor->offset++;
@@ -454,14 +456,13 @@ xfs_attr3_leaf_list_int(
args.rmtblkcnt = xfs_attr3_rmt_blocks(
args.dp->i_mount, valuelen);
retval = xfs_attr_rmtval_get(&args);
- if (retval)
- return retval;
- retval = context->put_listent(context,
- entry->flags,
- name_rmt->name,
- (int)name_rmt->namelen,
- valuelen,
- args.value);
+ if (!retval)
+ retval = context->put_listent(context,
+ entry->flags,
+ name_rmt->name,
+ (int)name_rmt->namelen,
+ valuelen,
+ args.value);
kmem_free(args.value);
} else {
retval = context->put_listent(context,
diff --git a/include/asm-generic/bitops/lock.h b/include/asm-generic/bitops/lock.h
index c30266e94806..8ef0ccbf8167 100644
--- a/include/asm-generic/bitops/lock.h
+++ b/include/asm-generic/bitops/lock.h
@@ -29,16 +29,16 @@ do { \
* @nr: the bit to set
* @addr: the address to start counting from
*
- * This operation is like clear_bit_unlock, however it is not atomic.
- * It does provide release barrier semantics so it can be used to unlock
- * a bit lock, however it would only be used if no other CPU can modify
- * any bits in the memory until the lock is released (a good example is
- * if the bit lock itself protects access to the other bits in the word).
+ * A weaker form of clear_bit_unlock() as used by __bit_lock_unlock(). If all
+ * the bits in the word are protected by this lock some archs can use weaker
+ * ops to safely unlock.
+ *
+ * See for example x86's implementation.
*/
#define __clear_bit_unlock(nr, addr) \
do { \
- smp_mb(); \
- __clear_bit(nr, addr); \
+ smp_mb__before_atomic(); \
+ clear_bit(nr, addr); \
} while (0)
#endif /* _ASM_GENERIC_BITOPS_LOCK_H_ */
diff --git a/include/asm-generic/preempt.h b/include/asm-generic/preempt.h
index 5d8ffa3e6f8c..c1cde3577551 100644
--- a/include/asm-generic/preempt.h
+++ b/include/asm-generic/preempt.h
@@ -7,10 +7,10 @@
static __always_inline int preempt_count(void)
{
- return current_thread_info()->preempt_count;
+ return READ_ONCE(current_thread_info()->preempt_count);
}
-static __always_inline int *preempt_count_ptr(void)
+static __always_inline volatile int *preempt_count_ptr(void)
{
return &current_thread_info()->preempt_count;
}
diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
index a702c042b716..e63d3a513e67 100644
--- a/include/linux/cgroup-defs.h
+++ b/include/linux/cgroup-defs.h
@@ -216,6 +216,9 @@ struct css_set {
/* all css_task_iters currently walking this cset */
struct list_head task_iters;
+ /* dead and being drained, ignore for migration */
+ bool dead;
+
/* For RCU-protected deletion */
struct rcu_head rcu_head;
};
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 22ab246feed3..eeae401a2412 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -199,7 +199,7 @@
#define unreachable() __builtin_unreachable()
/* Mark a function definition as prohibited from being cloned. */
-#define __noclone __attribute__((__noclone__))
+#define __noclone __attribute__((__noclone__, __optimize__("no-tracer")))
#endif /* GCC_VERSION >= 40500 */
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 8a2e009c8a5a..f513dd855cb2 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -161,6 +161,7 @@ struct dentry_operations {
struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool);
struct inode *(*d_select_inode)(struct dentry *, unsigned);
+ struct dentry *(*d_real)(struct dentry *, struct inode *);
} ____cacheline_aligned;
/*
@@ -227,6 +228,7 @@ struct dentry_operations {
#define DCACHE_MAY_FREE 0x00800000
#define DCACHE_FALLTHRU 0x01000000 /* Fall through to lower layer */
#define DCACHE_OP_SELECT_INODE 0x02000000 /* Unioned entry: dcache op selects inode */
+#define DCACHE_OP_REAL 0x08000000
extern seqlock_t rename_lock;
@@ -582,4 +584,12 @@ static inline struct dentry *d_backing_dentry(struct dentry *upper)
return upper;
}
+static inline struct dentry *d_real(struct dentry *dentry)
+{
+ if (unlikely(dentry->d_flags & DCACHE_OP_REAL))
+ return dentry->d_op->d_real(dentry, NULL);
+ else
+ return dentry;
+}
+
#endif /* __LINUX_DCACHE_H */
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 2151ebfe0902..010b5ad2ac5d 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -124,6 +124,8 @@ struct dm_dev {
char name[16];
};
+dev_t dm_get_dev_t(const char *path);
+
/*
* Constructors should call these functions to ensure destination devices
* are opened/closed correctly.
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 5972ffe5719a..5110d4211866 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -446,8 +446,12 @@ int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog,
void bpf_prog_destroy(struct bpf_prog *fp);
int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
+int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk,
+ bool locked);
int sk_attach_bpf(u32 ufd, struct sock *sk);
int sk_detach_filter(struct sock *sk);
+int __sk_detach_filter(struct sock *sk, bool locked);
+
int sk_get_filter(struct sock *sk, struct sock_filter __user *filter,
unsigned int len);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 522b582e61c7..cc2796b2486f 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1214,6 +1214,16 @@ static inline struct inode *file_inode(const struct file *f)
return f->f_inode;
}
+static inline struct dentry *file_dentry(const struct file *file)
+{
+ struct dentry *dentry = file->f_path.dentry;
+
+ if (unlikely(dentry->d_flags & DCACHE_OP_REAL))
+ return dentry->d_op->d_real(dentry, file_inode(file));
+ else
+ return dentry;
+}
+
static inline int locks_lock_file_wait(struct file *filp, struct file_lock *fl)
{
return locks_lock_inode_wait(file_inode(filp), fl);
@@ -2224,7 +2234,7 @@ extern long do_sys_open(int dfd, const char __user *filename, int flags,
extern struct file *file_open_name(struct filename *, int, umode_t);
extern struct file *filp_open(const char *, int, umode_t);
extern struct file *file_open_root(struct dentry *, struct vfsmount *,
- const char *, int);
+ const char *, int, umode_t);
extern struct file * dentry_open(const struct path *, int, const struct cred *);
extern int filp_close(struct file *, fl_owner_t id);
diff --git a/include/linux/hdcp_qseecom.h b/include/linux/hdcp_qseecom.h
index d9835193961e..26e97700fc73 100644
--- a/include/linux/hdcp_qseecom.h
+++ b/include/linux/hdcp_qseecom.h
@@ -108,9 +108,15 @@ struct hdcp_client_ops {
int (*wakeup)(struct hdmi_hdcp_wakeup_data *data);
};
+enum hdcp_device_type {
+ HDCP_TXMTR_HDMI = 0x8001,
+ HDCP_TXMTR_DP = 0x8002
+};
+
struct hdcp_register_data {
struct hdcp_client_ops *client_ops;
struct hdcp_txmtr_ops *txmtr_ops;
+ enum hdcp_device_type device_type;
void *client_ctx;
void **hdcp_ctx;
bool tethered;
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index a338a688ee4a..dcb89e3515db 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -46,10 +46,6 @@ struct br_ip_list {
#define BR_LEARNING_SYNC BIT(9)
#define BR_PROXYARP_WIFI BIT(10)
-/* values as per ieee8021QBridgeFdbAgingTime */
-#define BR_MIN_AGEING_TIME (10 * HZ)
-#define BR_MAX_AGEING_TIME (1000000 * HZ)
-
#define BR_DEFAULT_AGEING_TIME (300 * HZ)
extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *));
diff --git a/include/linux/input/ft5x06_ts.h b/include/linux/input/ft5x06_ts.h
index b2fb3c4a56ae..a9577b62cb07 100644
--- a/include/linux/input/ft5x06_ts.h
+++ b/include/linux/input/ft5x06_ts.h
@@ -24,8 +24,8 @@ struct ft5x06_ts_platform_data {
u32 y_max;
u32 irq_gpio;
u32 reset_gpio;
- int (*power_init) (bool);
- int (*power_on) (bool);
+ int (*power_init)(bool);
+ int (*power_on)(bool);
};
#endif
diff --git a/include/linux/ipa.h b/include/linux/ipa.h
index d152057af385..5f85508353c9 100644
--- a/include/linux/ipa.h
+++ b/include/linux/ipa.h
@@ -1096,6 +1096,11 @@ int ipa_reset_endpoint(u32 clnt_hdl);
int ipa_clear_endpoint_delay(u32 clnt_hdl);
/*
+ * Disable ep
+ */
+int ipa_disable_endpoint(u32 clnt_hdl);
+
+/*
* Configuration
*/
int ipa_cfg_ep(u32 clnt_hdl, const struct ipa_ep_cfg *ipa_ep_cfg);
@@ -1462,6 +1467,14 @@ static inline int ipa_clear_endpoint_delay(u32 clnt_hdl)
}
/*
+ * Disable ep
+ */
+static inline int ipa_disable_endpoint(u32 clnt_hdl)
+{
+ return -EPERM;
+}
+
+/*
* Configuration
*/
static inline int ipa_cfg_ep(u32 clnt_hdl,
diff --git a/include/linux/ipa_uc_offload.h b/include/linux/ipa_uc_offload.h
new file mode 100644
index 000000000000..0277e87a2570
--- /dev/null
+++ b/include/linux/ipa_uc_offload.h
@@ -0,0 +1,259 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _IPA_UC_OFFLOAD_H_
+#define _IPA_UC_OFFLOAD_H_
+
+#include <linux/ipa.h>
+
+/**
+ * enum ipa_uc_offload_proto
+ * Protocol type: either WDI or Neutrino
+ *
+ * @IPA_UC_WDI: wdi Protocol
+ * @IPA_UC_NTN: Neutrino Protocol
+ */
+enum ipa_uc_offload_proto {
+ IPA_UC_INVALID = 0,
+ IPA_UC_WDI = 1,
+ IPA_UC_NTN = 2,
+ IPA_UC_MAX_PROT_SIZE
+};
+
+/**
+ * struct ipa_hdr_info - Header to install on IPA HW
+ *
+ * @hdr: header to install on IPA HW
+ * @hdr_len: length of header
+ * @dst_mac_addr_offset: destination mac address offset
+ * @hdr_type: layer two header type
+ */
+struct ipa_hdr_info {
+ u8 *hdr;
+ u8 hdr_len;
+ u8 dst_mac_addr_offset;
+ enum ipa_hdr_l2_type hdr_type;
+};
+
+/**
+ * struct ipa_uc_offload_intf_params - parameters for uC offload
+ * interface registration
+ *
+ * @netdev_name: network interface name
+ * @notify: callback for exception/embedded packets
+ * @priv: callback cookie
+ * @hdr_info: header information
+ * @meta_data: meta data if any
+ * @meta_data_mask: meta data mask
+ * @proto: uC offload protocol type
+ * @alt_dst_pipe: alternate routing output pipe
+ */
+struct ipa_uc_offload_intf_params {
+ const char *netdev_name;
+ ipa_notify_cb notify;
+ void *priv;
+ struct ipa_hdr_info hdr_info[IPA_IP_MAX];
+ u8 is_meta_data_valid;
+ u32 meta_data;
+ u32 meta_data_mask;
+ enum ipa_uc_offload_proto proto;
+ enum ipa_client_type alt_dst_pipe;
+};
+
+/**
+ * struct ipa_ntn_setup_info - NTN TX/Rx configuration
+ * @client: type of "client" (IPA_CLIENT_ODU#_PROD/CONS)
+ * @ring_base_pa: physical address of the base of the Tx/Rx ring
+ * @ntn_ring_size: size of the Tx/Rx ring (in terms of elements)
+ * @buff_pool_base_pa: physical address of the base of the Tx/Rx
+ * buffer pool
+ * @num_buffers: Rx/Tx buffer pool size (in terms of elements)
+ * @data_buff_size: size of the each data buffer allocated in DDR
+ * @ntn_reg_base_ptr_pa: physical address of the Tx/Rx NTN Ring's
+ * tail pointer
+ */
+struct ipa_ntn_setup_info {
+ enum ipa_client_type client;
+ phys_addr_t ring_base_pa;
+ u32 ntn_ring_size;
+
+ phys_addr_t buff_pool_base_pa;
+ u32 num_buffers;
+ u32 data_buff_size;
+
+ phys_addr_t ntn_reg_base_ptr_pa;
+};
+
+/**
+ * struct ipa_uc_offload_out_params - out parameters for uC offload
+ *
+ * @clnt_hndl: Handle that client need to pass during
+ * further operations
+ */
+struct ipa_uc_offload_out_params {
+ u32 clnt_hndl;
+};
+
+/**
+ * struct ipa_ntn_conn_in_params - NTN TX/Rx connect parameters
+ * @ul: parameters to connect UL pipe(from Neutrino to IPA)
+ * @dl: parameters to connect DL pipe(from IPA to Neutrino)
+ */
+struct ipa_ntn_conn_in_params {
+ struct ipa_ntn_setup_info ul;
+ struct ipa_ntn_setup_info dl;
+};
+
+/**
+ * struct ipa_ntn_conn_out_params - information provided
+ * to uC offload client
+ * @ul_uc_db_pa: physical address of IPA uc doorbell for UL
+ * @dl_uc_db_pa: physical address of IPA uc doorbell for DL
+ * @clnt_hdl: opaque handle assigned to offload client
+ */
+struct ipa_ntn_conn_out_params {
+ phys_addr_t ul_uc_db_pa;
+ phys_addr_t dl_uc_db_pa;
+};
+
+/**
+ * struct ipa_uc_offload_conn_in_params - information provided by
+ * uC offload client
+ * @clnt_hndl: Handle that return as part of reg interface
+ * @proto: Protocol to use for offload data path
+ * @ntn: uC RX/Tx configuration info
+ */
+struct ipa_uc_offload_conn_in_params {
+ u32 clnt_hndl;
+ union {
+ struct ipa_ntn_conn_in_params ntn;
+ } u;
+};
+
+/**
+ * struct ipa_uc_offload_conn_out_params - information provided
+ * to uC offload client
+ * @ul_uc_db_pa: physical address of IPA uc doorbell for UL
+ * @dl_uc_db_pa: physical address of IPA uc doorbell for DL
+ * @clnt_hdl: opaque handle assigned to offload client
+ */
+struct ipa_uc_offload_conn_out_params {
+ union {
+ struct ipa_ntn_conn_out_params ntn;
+ } u;
+};
+
+/**
+ * struct ipa_perf_profile - To set BandWidth profile
+ *
+ * @client: type of "client" (IPA_CLIENT_ODU#_PROD/CONS)
+ * @max_supported_bw_mbps: maximum bandwidth needed (in Mbps)
+ */
+struct ipa_perf_profile {
+ enum ipa_client_type client;
+ u32 max_supported_bw_mbps;
+};
+
+#if defined CONFIG_IPA || defined CONFIG_IPA3
+
+/**
+ * ipa_uc_offload_reg_intf - Client should call this function to
+ * init uC offload data path
+ *
+ * @init: [in] initialization parameters
+ *
+ * Note: Should not be called from atomic context and only
+ * after checking IPA readiness using ipa_register_ipa_ready_cb()
+ *
+ * @Return 0 on success, negative on failure
+ */
+int ipa_uc_offload_reg_intf(
+ struct ipa_uc_offload_intf_params *in,
+ struct ipa_uc_offload_out_params *out);
+
+/**
+ * ipa_uc_offload_cleanup - Client Driver should call this
+ * function before unload and after disconnect
+ *
+ * @Return 0 on success, negative on failure
+ */
+int ipa_uc_offload_cleanup(u32 clnt_hdl);
+
+/**
+ * ipa_uc_offload_conn_pipes - Client should call this
+ * function to connect uC pipe for offload data path
+ *
+ * @in: [in] input parameters from client
+ * @out: [out] output params to client
+ *
+ * Note: Should not be called from atomic context and only
+ * after checking IPA readiness using ipa_register_ipa_ready_cb()
+ *
+ * @Return 0 on success, negative on failure
+ */
+int ipa_uc_offload_conn_pipes(struct ipa_uc_offload_conn_in_params *in,
+ struct ipa_uc_offload_conn_out_params *out);
+
+/**
+ * ipa_uc_offload_disconn_pipes() - Client should call this
+ * function to disconnect uC pipe to disable offload data path
+ * @clnt_hdl: [in] opaque client handle assigned by IPA to client
+ *
+ * Note: Should not be called from atomic context
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_uc_offload_disconn_pipes(u32 clnt_hdl);
+
+/**
+ * ipa_set_perf_profile() - Client should call this function to
+ * set IPA clock Band Width based on data rates
+ * @profile: [in] BandWidth profile to use
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_set_perf_profile(struct ipa_perf_profile *profile);
+
+#else /* (CONFIG_IPA || CONFIG_IPA3) */
+
+static inline int ipa_uc_offload_reg_intf(
+ struct ipa_uc_offload_intf_params *in,
+ struct ipa_uc_offload_out_params *out)
+{
+ return -EPERM;
+}
+
+static inline int ipa_uC_offload_cleanup(u32 clnt_hdl)
+{
+ return -EPERM;
+}
+
+static inline int ipa_uc_offload_conn_pipes(
+ struct ipa_uc_offload_conn_in_params *in,
+ struct ipa_uc_offload_conn_out_params *out)
+{
+ return -EPERM;
+}
+
+static inline int ipa_uc_offload_disconn_pipes(u32 clnt_hdl)
+{
+ return -EPERM;
+}
+
+static inline int ipa_set_perf_profile(struct ipa_perf_profile *profile)
+{
+ return -EPERM;
+}
+
+#endif /* CONFIG_IPA3 */
+
+#endif /* _IPA_UC_OFFLOAD_H_ */
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 1eaf3d81f5c1..2955e672391d 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -607,7 +607,7 @@ do { \
#define do_trace_printk(fmt, args...) \
do { \
- static const char *trace_printk_fmt \
+ static const char *trace_printk_fmt __used \
__attribute__((section("__trace_printk_fmt"))) = \
__builtin_constant_p(fmt) ? fmt : NULL; \
\
@@ -651,7 +651,7 @@ int __trace_printk(unsigned long ip, const char *fmt, ...);
*/
#define trace_puts(str) ({ \
- static const char *trace_printk_fmt \
+ static const char *trace_printk_fmt __used \
__attribute__((section("__trace_printk_fmt"))) = \
__builtin_constant_p(str) ? str : NULL; \
\
@@ -673,7 +673,7 @@ extern void trace_dump_stack(int skip);
#define ftrace_vprintk(fmt, vargs) \
do { \
if (__builtin_constant_p(fmt)) { \
- static const char *trace_printk_fmt \
+ static const char *trace_printk_fmt __used \
__attribute__((section("__trace_printk_fmt"))) = \
__builtin_constant_p(fmt) ? fmt : NULL; \
\
diff --git a/include/linux/leds-qpnp-flash-v2.h b/include/linux/leds-qpnp-flash-v2.h
index 47fd0699a9c1..1ae77e2e277b 100644
--- a/include/linux/leds-qpnp-flash-v2.h
+++ b/include/linux/leds-qpnp-flash-v2.h
@@ -14,11 +14,21 @@
#define __LEDS_QPNP_FLASH_V2_H
#include <linux/leds.h>
-#include "leds.h"
+#include <linux/notifier.h>
-#define ENABLE_REGULATOR BIT(0)
-#define QUERY_MAX_CURRENT BIT(1)
+enum flash_led_irq_type {
+ LED_FAULT_IRQ = BIT(0),
+ MITIGATION_IRQ = BIT(1),
+ FLASH_TIMER_EXP_IRQ = BIT(2),
+ ALL_RAMP_DOWN_DONE_IRQ = BIT(3),
+ ALL_RAMP_UP_DONE_IRQ = BIT(4),
+ LED3_RAMP_UP_DONE_IRQ = BIT(5),
+ LED2_RAMP_UP_DONE_IRQ = BIT(6),
+ LED1_RAMP_UP_DONE_IRQ = BIT(7),
+ INVALID_IRQ = BIT(8),
+};
-int qpnp_flash_led_prepare(struct led_classdev *led_cdev, int options);
+int qpnp_flash_led_register_irq_notifier(struct notifier_block *nb);
+int qpnp_flash_led_unregister_irq_notifier(struct notifier_block *nb);
#endif
diff --git a/include/linux/leds-qpnp-flash.h b/include/linux/leds-qpnp-flash.h
new file mode 100644
index 000000000000..55867e78bba6
--- /dev/null
+++ b/include/linux/leds-qpnp-flash.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LEDS_QPNP_FLASH_H
+#define __LEDS_QPNP_FLASH_H
+
+#include <linux/leds.h>
+
+#define ENABLE_REGULATOR BIT(0)
+#define QUERY_MAX_CURRENT BIT(1)
+
+int qpnp_flash_led_prepare(struct led_trigger *trig, int options);
+
+#endif
diff --git a/include/linux/mfd/wcd934x/registers.h b/include/linux/mfd/wcd934x/registers.h
index 871bf6a778b1..085e16d66bc4 100644
--- a/include/linux/mfd/wcd934x/registers.h
+++ b/include/linux/mfd/wcd934x/registers.h
@@ -33,6 +33,7 @@ enum {
WCD934X_PAGE_13,
WCD934X_PAGE_14,
WCD934X_PAGE_15,
+ WCD934X_PAGE_0x50,
WCD934X_PAGE_0X80,
};
diff --git a/include/linux/mfd/wcd9xxx/pdata.h b/include/linux/mfd/wcd9xxx/pdata.h
index 52277f26b5a4..7bf2bff2f173 100755
--- a/include/linux/mfd/wcd9xxx/pdata.h
+++ b/include/linux/mfd/wcd9xxx/pdata.h
@@ -189,6 +189,7 @@ struct wcd9xxx_pdata {
u32 mclk_rate;
u32 dmic_sample_rate;
u32 mad_dmic_sample_rate;
+ u32 ecpp_dmic_sample_rate;
u32 dmic_clk_drv;
u16 use_pinctrl;
};
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index b222d86b501f..acf302ef2f01 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -265,6 +265,7 @@ struct header_ops {
void (*cache_update)(struct hh_cache *hh,
const struct net_device *dev,
const unsigned char *haddr);
+ bool (*validate)(const char *ll_header, unsigned int len);
};
/* These flag bits are private to the generic network queueing
@@ -1398,8 +1399,7 @@ enum netdev_priv_flags {
* @dma: DMA channel
* @mtu: Interface MTU value
* @type: Interface hardware type
- * @hard_header_len: Hardware header length, which means that this is the
- * minimum size of a packet.
+ * @hard_header_len: Maximum hardware header length.
*
* @needed_headroom: Extra headroom the hardware may need, but not in all
* cases can this be guaranteed
@@ -2493,6 +2493,24 @@ static inline int dev_parse_header(const struct sk_buff *skb,
return dev->header_ops->parse(skb, haddr);
}
+/* ll_header must have at least hard_header_len allocated */
+static inline bool dev_validate_header(const struct net_device *dev,
+ char *ll_header, int len)
+{
+ if (likely(len >= dev->hard_header_len))
+ return true;
+
+ if (capable(CAP_SYS_RAWIO)) {
+ memset(ll_header + len, 0, dev->hard_header_len - len);
+ return true;
+ }
+
+ if (dev->header_ops && dev->header_ops->validate)
+ return dev->header_ops->validate(ll_header, len);
+
+ return false;
+}
+
typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, int len);
int register_gifconf(unsigned int family, gifconf_func_t *gifconf);
static inline int unregister_gifconf(unsigned int family)
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 6ae25aae88fd..e89c7ee7e803 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -359,6 +359,7 @@ struct pci_dev {
unsigned int io_window_1k:1; /* Intel P2P bridge 1K I/O windows */
unsigned int irq_managed:1;
unsigned int has_secondary_link:1;
+ unsigned int non_compliant_bars:1; /* broken BARs; ignore them */
pci_dev_flags_t dev_flags;
atomic_t enable_cnt; /* pci_enable_device has been called */
@@ -988,23 +989,6 @@ static inline int pci_is_managed(struct pci_dev *pdev)
return pdev->is_managed;
}
-static inline void pci_set_managed_irq(struct pci_dev *pdev, unsigned int irq)
-{
- pdev->irq = irq;
- pdev->irq_managed = 1;
-}
-
-static inline void pci_reset_managed_irq(struct pci_dev *pdev)
-{
- pdev->irq = 0;
- pdev->irq_managed = 0;
-}
-
-static inline bool pci_has_managed_irq(struct pci_dev *pdev)
-{
- return pdev->irq_managed && pdev->irq > 0;
-}
-
void pci_disable_device(struct pci_dev *dev);
extern unsigned int pcibios_max_latency;
diff --git a/include/linux/platform_data/asoc-s3c.h b/include/linux/platform_data/asoc-s3c.h
index 5e0bc779e6c5..33f88b4479e4 100644
--- a/include/linux/platform_data/asoc-s3c.h
+++ b/include/linux/platform_data/asoc-s3c.h
@@ -39,6 +39,10 @@ struct samsung_i2s {
*/
struct s3c_audio_pdata {
int (*cfg_gpio)(struct platform_device *);
+ void *dma_playback;
+ void *dma_capture;
+ void *dma_play_sec;
+ void *dma_capture_mic;
union {
struct samsung_i2s i2s;
} type;
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index 9a2e50337af9..cccaf4a29e9f 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -34,6 +34,8 @@ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp);
int dev_pm_opp_get_opp_count(struct device *dev);
unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev);
+unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev);
+unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev);
struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev);
struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
@@ -55,6 +57,14 @@ int dev_pm_opp_enable(struct device *dev, unsigned long freq);
int dev_pm_opp_disable(struct device *dev, unsigned long freq);
struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev);
+int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
+ unsigned int count);
+void dev_pm_opp_put_supported_hw(struct device *dev);
+int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
+void dev_pm_opp_put_prop_name(struct device *dev);
+int dev_pm_opp_set_regulator(struct device *dev, const char *name);
+void dev_pm_opp_put_regulator(struct device *dev);
+int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
#else
static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
{
@@ -81,6 +91,16 @@ static inline unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev)
return 0;
}
+static inline unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)
+{
+ return 0;
+}
+
+static inline unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev)
+{
+ return 0;
+}
+
static inline struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev)
{
return NULL;
@@ -129,6 +149,35 @@ static inline struct srcu_notifier_head *dev_pm_opp_get_notifier(
{
return ERR_PTR(-EINVAL);
}
+
+static inline int dev_pm_opp_set_supported_hw(struct device *dev,
+ const u32 *versions,
+ unsigned int count)
+{
+ return -EINVAL;
+}
+
+static inline void dev_pm_opp_put_supported_hw(struct device *dev) {}
+
+static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
+{
+ return -EINVAL;
+}
+
+static inline void dev_pm_opp_put_prop_name(struct device *dev) {}
+
+static inline int dev_pm_opp_set_regulator(struct device *dev, const char *name)
+{
+ return -EINVAL;
+}
+
+static inline void dev_pm_opp_put_regulator(struct device *dev) {}
+
+static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
+{
+ return -EINVAL;
+}
+
#endif /* CONFIG_PM_OPP */
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index b081a56e250f..7d1e374e176c 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -214,6 +214,8 @@ enum power_supply_property {
POWER_SUPPLY_PROP_TYPEC_POWER_ROLE,
POWER_SUPPLY_PROP_PD_ALLOWED,
POWER_SUPPLY_PROP_PD_ACTIVE,
+ POWER_SUPPLY_PROP_CHARGER_TEMP,
+ POWER_SUPPLY_PROP_CHARGER_TEMP_MAX,
/* Local extensions of type int64_t */
POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
/* Properties of type `const char *' */
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 75f136a22a5e..4fde61804191 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -1908,6 +1908,30 @@ static inline void skb_reserve(struct sk_buff *skb, int len)
skb->tail += len;
}
+/**
+ * skb_tailroom_reserve - adjust reserved_tailroom
+ * @skb: buffer to alter
+ * @mtu: maximum amount of headlen permitted
+ * @needed_tailroom: minimum amount of reserved_tailroom
+ *
+ * Set reserved_tailroom so that headlen can be as large as possible but
+ * not larger than mtu and tailroom cannot be smaller than
+ * needed_tailroom.
+ * The required headroom should already have been reserved before using
+ * this function.
+ */
+static inline void skb_tailroom_reserve(struct sk_buff *skb, unsigned int mtu,
+ unsigned int needed_tailroom)
+{
+ SKB_LINEAR_ASSERT(skb);
+ if (mtu < skb_tailroom(skb) - needed_tailroom)
+ /* use at most mtu */
+ skb->reserved_tailroom = skb_tailroom(skb) - mtu;
+ else
+ /* use up to all available space */
+ skb->reserved_tailroom = needed_tailroom;
+}
+
#define ENCAP_TYPE_ETHER 0
#define ENCAP_TYPE_IPPROTO 1
@@ -2724,6 +2748,23 @@ static inline void skb_postpull_rcsum(struct sk_buff *skb,
unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len);
+static inline void skb_postpush_rcsum(struct sk_buff *skb,
+ const void *start, unsigned int len)
+{
+ /* For performing the reverse operation to skb_postpull_rcsum(),
+ * we can instead of ...
+ *
+ * skb->csum = csum_add(skb->csum, csum_partial(start, len, 0));
+ *
+ * ... just use this equivalent version here to save a few
+ * instructions. Feeding csum of 0 in csum_partial() and later
+ * on adding skb->csum is equivalent to feed skb->csum in the
+ * first place.
+ */
+ if (skb->ip_summed == CHECKSUM_COMPLETE)
+ skb->csum = csum_partial(start, len, skb->csum);
+}
+
/**
* pskb_trim_rcsum - trim received skb and update checksum
* @skb: buffer to trim
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index a81516c611c4..b90f8f5c663d 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -190,6 +190,7 @@ struct sensor_info {
* @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis
* @devdata: private pointer for device private data
* @trips: number of trip points the thermal zone supports
+ * @trips_disabled; bitmap for disabled trips
* @passive_delay: number of milliseconds to wait between polls when
* performing passive cooling.
* @polling_delay: number of milliseconds to wait between polls when
@@ -225,6 +226,7 @@ struct thermal_zone_device {
struct thermal_attr *trip_hyst_attrs;
void *devdata;
int trips;
+ unsigned long trips_disabled; /* bitmap for disabled trips */
int passive_delay;
int polling_delay;
int temperature;
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 6b6e811f4575..3bf03b6b52e9 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -594,7 +594,7 @@ static inline int tty_ldisc_receive_buf(struct tty_ldisc *ld, unsigned char *p,
count = ld->ops->receive_buf2(ld->tty, p, f, count);
else {
count = min_t(int, count, ld->tty->receive_room);
- if (count)
+ if (count && ld->ops->receive_buf)
ld->ops->receive_buf(ld->tty, p, f, count);
}
return count;
diff --git a/include/linux/uid_stat.h b/include/linux/uid_stat.h
deleted file mode 100644
index 6bd6c4e52d17..000000000000
--- a/include/linux/uid_stat.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* include/linux/uid_stat.h
- *
- * Copyright (C) 2008-2009 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __uid_stat_h
-#define __uid_stat_h
-
-/* Contains definitions for resource tracking per uid. */
-
-#ifdef CONFIG_UID_STAT
-int uid_stat_tcp_snd(uid_t uid, int size);
-int uid_stat_tcp_rcv(uid_t uid, int size);
-#else
-#define uid_stat_tcp_snd(uid, size) do {} while (0);
-#define uid_stat_tcp_rcv(uid, size) do {} while (0);
-#endif
-
-#endif /* _LINUX_UID_STAT_H */
diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h
index 7f5f78bd15ad..245f57dbbb61 100644
--- a/include/linux/usb_usual.h
+++ b/include/linux/usb_usual.h
@@ -79,6 +79,8 @@
/* Cannot handle MI_REPORT_SUPPORTED_OPERATION_CODES */ \
US_FLAG(MAX_SECTORS_240, 0x08000000) \
/* Sets max_sectors to 240 */ \
+ US_FLAG(NO_REPORT_LUNS, 0x10000000) \
+ /* Cannot handle REPORT_LUNS */ \
#define US_FLAG(name, value) US_FL_##name = value ,
enum { US_DO_ALL_FLAGS };
diff --git a/include/net/activity_stats.h b/include/net/activity_stats.h
deleted file mode 100644
index 10e4c1506eeb..000000000000
--- a/include/net/activity_stats.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- *
- * Author: Mike Chan (mike@android.com)
- */
-
-#ifndef __activity_stats_h
-#define __activity_stats_h
-
-#ifdef CONFIG_NET_ACTIVITY_STATS
-void activity_stats_update(void);
-#else
-#define activity_stats_update(void) {}
-#endif
-
-#endif /* _NET_ACTIVITY_STATS_H */
diff --git a/include/net/bonding.h b/include/net/bonding.h
index c1740a2794a3..93abe5f6188d 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -214,6 +214,7 @@ struct bonding {
* ALB mode (6) - to sync the use and modifications of its hash table
*/
spinlock_t mode_lock;
+ spinlock_t stats_lock;
u8 send_peer_notif;
u8 igmp_retrans;
#ifdef CONFIG_PROC_FS
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 63568caf0de8..e0b0d2b12b88 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -3042,6 +3042,24 @@ struct wiphy_vendor_command {
};
/**
+ * struct wiphy_iftype_ext_capab - extended capabilities per interface type
+ * @iftype: interface type
+ * @extended_capabilities: extended capabilities supported by the driver,
+ * additional capabilities might be supported by userspace; these are the
+ * 802.11 extended capabilities ("Extended Capabilities element") and are
+ * in the same format as in the information element. See IEEE Std
+ * 802.11-2012 8.4.2.29 for the defined fields.
+ * @extended_capabilities_mask: mask of the valid values
+ * @extended_capabilities_len: length of the extended capabilities
+ */
+struct wiphy_iftype_ext_capab {
+ enum nl80211_iftype iftype;
+ const u8 *extended_capabilities;
+ const u8 *extended_capabilities_mask;
+ u8 extended_capabilities_len;
+};
+
+/**
* struct wiphy - wireless hardware description
* @reg_notifier: the driver's regulatory notification callback,
* note that if your driver uses wiphy_apply_custom_regulatory()
@@ -3158,9 +3176,14 @@ struct wiphy_vendor_command {
* additional capabilities might be supported by userspace; these are
* the 802.11 extended capabilities ("Extended Capabilities element")
* and are in the same format as in the information element. See
- * 802.11-2012 8.4.2.29 for the defined fields.
+ * 802.11-2012 8.4.2.29 for the defined fields. These are the default
+ * extended capabilities to be used if the capabilities are not specified
+ * for a specific interface type in iftype_ext_capab.
* @extended_capabilities_mask: mask of the valid values
* @extended_capabilities_len: length of the extended capabilities
+ * @iftype_ext_capab: array of extended capabilities per interface type
+ * @num_iftype_ext_capab: number of interface types for which extended
+ * capabilities are specified separately.
* @coalesce: packet coalescing support information
*
* @vendor_commands: array of vendor commands supported by the hardware
@@ -3257,6 +3280,9 @@ struct wiphy {
const u8 *extended_capabilities, *extended_capabilities_mask;
u8 extended_capabilities_len;
+ const struct wiphy_iftype_ext_capab *iftype_ext_capab;
+ unsigned int num_iftype_ext_capab;
+
/* If multiple wiphys are registered and you're handed e.g.
* a regular netdev with assigned ieee80211_ptr, you won't
* know whether it points to a wiphy your driver has registered
diff --git a/include/soc/qcom/camera2.h b/include/soc/qcom/camera2.h
index 6e1aac3ff05a..bf9db17e6981 100644
--- a/include/soc/qcom/camera2.h
+++ b/include/soc/qcom/camera2.h
@@ -147,6 +147,7 @@ struct msm_camera_sensor_board_info {
const char *eeprom_name;
const char *actuator_name;
const char *ois_name;
+ const char *flash_name;
struct msm_camera_slave_info *slave_info;
struct msm_camera_csi_lane_params *csi_lane_params;
struct msm_camera_sensor_strobe_flash_data *strobe_flash_data;
diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h
index 6c3ec33cc613..6275e4536bc0 100644
--- a/include/soc/qcom/icnss.h
+++ b/include/soc/qcom/icnss.h
@@ -15,6 +15,7 @@
#include <linux/interrupt.h>
#define ICNSS_MAX_IRQ_REGISTRATIONS 12
+#define ICNSS_MAX_TIMESTAMP_LEN 32
struct icnss_driver_ops {
char *name;
@@ -79,7 +80,12 @@ enum icnss_driver_mode {
struct icnss_soc_info {
void __iomem *v_addr;
phys_addr_t p_addr;
- u32 version;
+ uint32_t chip_id;
+ uint32_t chip_family;
+ uint32_t board_id;
+ uint32_t soc_id;
+ uint32_t fw_version;
+ char fw_build_timestamp[ICNSS_MAX_TIMESTAMP_LEN + 1];
};
extern int icnss_register_driver(struct icnss_driver_ops *driver);
diff --git a/include/soc/qcom/irq-helper.h b/include/soc/qcom/irq-helper.h
new file mode 100644
index 000000000000..d992fb6f470a
--- /dev/null
+++ b/include/soc/qcom/irq-helper.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SOC_QCOM_IRQ_HELPER_H
+#define __SOC_QCOM_IRQ_HELPER_H
+
+int irq_blacklist_on(void);
+int irq_blacklist_off(void);
+
+#endif
+
diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h
index 39527ed83fa7..82672bba7c17 100644
--- a/include/soc/qcom/socinfo.h
+++ b/include/soc/qcom/socinfo.h
@@ -90,6 +90,8 @@
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8929")
#define early_machine_is_msmcobalt() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmcobalt")
+#define early_machine_is_apqcobalt() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,apqcobalt")
#define early_machine_is_msmhamster() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmhamster")
#define early_machine_is_msmfalcon() \
@@ -128,6 +130,7 @@
#define early_machine_is_msm8976() 0
#define early_machine_is_msm8929() 0
#define early_machine_is_msmcobalt() 0
+#define early_machine_is_apqcobalt() 0
#define early_machine_is_msmhamster() 0
#define early_machine_is_msmfalcon() 0
#endif
@@ -221,6 +224,7 @@ char *socinfo_get_build_id(void);
uint32_t socinfo_get_platform_type(void);
uint32_t socinfo_get_platform_subtype(void);
uint32_t socinfo_get_platform_version(void);
+uint32_t socinfo_get_serial_number(void);
enum pmic_model socinfo_get_pmic_model(void);
uint32_t socinfo_get_pmic_die_revision(void);
int __init socinfo_init(void) __must_check;
diff --git a/include/sound/wcd-dsp-mgr.h b/include/sound/wcd-dsp-mgr.h
new file mode 100644
index 000000000000..5adcbcf660ba
--- /dev/null
+++ b/include/sound/wcd-dsp-mgr.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD_DSP_MGR_H__
+#define __WCD_DSP_MGR_H__
+
+#include <linux/types.h>
+
+/*
+ * These enums correspond to the component types
+ * that wcd-dsp-manager driver will use. The order
+ * of the enums specifies the order in which the
+ * manager driver will perform the sequencing.
+ * Changing this will cause the sequencing order
+ * to be changed as well.
+ */
+enum wdsp_cmpnt_type {
+ /* Component to control the DSP */
+ WDSP_CMPNT_CONTROL = 0,
+ /* Component to perform data transfer to/from DSP */
+ WDSP_CMPNT_TRANSPORT,
+ /* Component that performs high level IPC */
+ WDSP_CMPNT_IPC,
+
+ WDSP_CMPNT_TYPE_MAX,
+};
+
+enum wdsp_event_type {
+ /* Image download related */
+ WDSP_EVENT_PRE_DLOAD_CODE,
+ WDSP_EVENT_DLOAD_SECTION,
+ WDSP_EVENT_POST_DLOAD_CODE,
+ WDSP_EVENT_PRE_DLOAD_DATA,
+ WDSP_EVENT_POST_DLOAD_DATA,
+ WDSP_EVENT_DLOAD_FAILED,
+
+ /* DSP boot related */
+ WDSP_EVENT_PRE_BOOTUP,
+ WDSP_EVENT_DO_BOOT,
+ WDSP_EVENT_POST_BOOTUP,
+ WDSP_EVENT_PRE_SHUTDOWN,
+ WDSP_EVENT_DO_SHUTDOWN,
+ WDSP_EVENT_POST_SHUTDOWN,
+
+ /* IRQ handling related */
+ WDSP_EVENT_IPC1_INTR,
+
+ /* Suspend/Resume related */
+ WDSP_EVENT_SUSPEND,
+ WDSP_EVENT_RESUME,
+};
+
+enum wdsp_intr {
+ WDSP_IPC1_INTR,
+};
+
+/*
+ * wdsp_cmpnt_ops: ops/function callbacks for components
+ * @init: called by manager driver, component is expected
+ * to initialize itself in this callback
+ * @deinit: called by manager driver, component should
+ * de-initialize itself in this callback
+ * @event_handler: Event handler for each component, called
+ * by the manager as per sequence
+ */
+struct wdsp_cmpnt_ops {
+ int (*init)(struct device *, void *priv_data);
+ int (*deinit)(struct device *, void *priv_data);
+ int (*event_handler)(struct device *, void *priv_data,
+ enum wdsp_event_type, void *data);
+};
+
+struct wdsp_img_section {
+ u32 addr;
+ size_t size;
+ u8 *data;
+};
+
+/*
+ * wdsp_ops: ops/function callbacks for manager driver
+ * @register_cmpnt_ops: components will use this to register
+ * their own ops to manager driver
+ * @get_dev_for_cmpnt: components can use this to get handle
+ * to struct device * of any other component
+ * @intr_handler: callback to notify manager driver that interrupt
+ * has occurred.
+ * @vote_for_dsp: notifies manager that dsp should be booted up
+ * @suspend: notifies manager that one component wants to suspend.
+ * Manager will make sure to suspend all components in order
+ * @resume: notifies manager that one component wants to resume.
+ * Manager will make sure to resume all components in order
+ */
+
+struct wdsp_mgr_ops {
+ int (*register_cmpnt_ops)(struct device *wdsp_dev,
+ struct device *cdev,
+ void *priv_data,
+ struct wdsp_cmpnt_ops *ops);
+ struct device *(*get_dev_for_cmpnt)(struct device *wdsp_dev,
+ enum wdsp_cmpnt_type type);
+ int (*intr_handler)(struct device *wdsp_dev,
+ enum wdsp_intr intr);
+ int (*vote_for_dsp)(struct device *wdsp_dev, bool vote);
+ int (*suspend)(struct device *wdsp_dev);
+ int (*resume)(struct device *wdsp_dev);
+};
+
+#endif /* end of __WCD_DSP_MGR_H__ */
diff --git a/include/sound/wcd-spi.h b/include/sound/wcd-spi.h
new file mode 100644
index 000000000000..1fff58d727a1
--- /dev/null
+++ b/include/sound/wcd-spi.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD_SPI_H__
+#define __WCD_SPI_H__
+
+struct wcd_spi_msg {
+ /*
+ * Caller's buffer pointer that holds data to
+ * be transmitted in case of data_write and
+ * data to be copied to in case of data_read.
+ */
+ void *data;
+
+ /* Length of data to write/read */
+ size_t len;
+
+ /*
+ * Address in remote memory to write to
+ * or read from.
+ */
+ u32 remote_addr;
+
+ /* Bitmask of flags, currently unused */
+ u32 flags;
+};
+
+#ifdef CONFIG_SND_SOC_WCD_SPI
+
+int wcd_spi_data_write(struct spi_device *spi, struct wcd_spi_msg *msg);
+int wcd_spi_data_read(struct spi_device *spi, struct wcd_spi_msg *msg);
+
+#else
+
+int wcd_spi_data_write(struct spi_device *spi, struct wcd_spi_msg *msg)
+{
+ return -ENODEV;
+}
+
+int wcd_spi_data_read(struct spi_device *spi, struct wcd_spi_msg *msg)
+{
+ return -ENODEV;
+}
+
+#endif /* End of CONFIG_SND_SOC_WCD_SPI */
+
+#endif /* End of __WCD_SPI_H__ */
diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h
index 54a02107a06e..60ebda8be9cb 100644
--- a/include/uapi/linux/msm_ipa.h
+++ b/include/uapi/linux/msm_ipa.h
@@ -434,6 +434,9 @@ enum ipa_rm_resource_name {
* @IPA_HW_v2_6: IPA hardware version 2.6
* @IPA_HW_v2_6L: IPA hardware version 2.6L
* @IPA_HW_v3_0: IPA hardware version 3.0
+ * @IPA_HW_v3_1: IPA hardware version 3.1
+ * @IPA_HW_v3_5: IPA hardware version 3.5
+ * @IPA_HW_v3_5_1: IPA hardware version 3.5.1
*/
enum ipa_hw_type {
IPA_HW_None = 0,
@@ -446,6 +449,8 @@ enum ipa_hw_type {
IPA_HW_v2_6L = 6,
IPA_HW_v3_0 = 10,
IPA_HW_v3_1 = 11,
+ IPA_HW_v3_5 = 12,
+ IPA_HW_v3_5_1 = 13,
IPA_HW_MAX
};
diff --git a/include/uapi/linux/msm_mdp_ext.h b/include/uapi/linux/msm_mdp_ext.h
index 8472224f33a6..811d8b4e1994 100644
--- a/include/uapi/linux/msm_mdp_ext.h
+++ b/include/uapi/linux/msm_mdp_ext.h
@@ -138,6 +138,15 @@ VALIDATE/COMMIT FLAG CONFIGURATION
*/
#define MDP_COMMIT_SYNC_FENCE_WAIT 0x04
+/* Flag to enable AVR(Adaptive variable refresh) feature. */
+#define MDP_COMMIT_AVR_EN 0x08
+
+/*
+ * Flag to select one shot mode when AVR feature is enabled.
+ * Default mode is continuous mode.
+ */
+#define MDP_COMMIT_AVR_ONE_SHOT_MODE 0x10
+
/* Flag to enable concurrent writeback for the frame */
#define MDP_COMMIT_CWB_EN 0x800
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 25627f584405..b5323800eeb5 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1764,8 +1764,9 @@ enum nl80211_commands {
* over all channels.
*
* @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a
- * scheduled scan (or a WoWLAN net-detect scan) is started, u32
- * in seconds.
+ * scheduled scan is started. Or the delay before a WoWLAN
+ * net-detect scan is started, counting from the moment the
+ * system is suspended. This value is a u32, in seconds.
* @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
* is operating in an indoor environment.
@@ -1782,11 +1783,26 @@ enum nl80211_commands {
* thus it must not specify the number of iterations, only the interval
* between scans. The scan plans are executed sequentially.
* Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan.
- *
* @NL80211_ATTR_PBSS: flag attribute. If set it means operate
* in a PBSS. Specified in %NL80211_CMD_CONNECT to request
* connecting to a PCP, and in %NL80211_CMD_START_AP to start
* a PCP instead of AP. Relevant for DMG networks only.
+ * @NL80211_ATTR_BSS_SELECT: nested attribute for driver supporting the
+ * BSS selection feature. When used with %NL80211_CMD_GET_WIPHY it contains
+ * attributes according &enum nl80211_bss_select_attr to indicate what
+ * BSS selection behaviours are supported. When used with %NL80211_CMD_CONNECT
+ * it contains the behaviour-specific attribute containing the parameters for
+ * BSS selection to be done by driver and/or firmware.
+ *
+ * @NL80211_ATTR_STA_SUPPORT_P2P_PS: whether P2P PS mechanism supported
+ * or not. u8, one of the values of &enum nl80211_sta_p2p_ps_status
+ *
+ * @NL80211_ATTR_PAD: attribute used for padding for 64-bit alignment
+ *
+ * @NL80211_ATTR_IFTYPE_EXT_CAPA: Nested attribute of the following attributes:
+ * %NL80211_ATTR_IFTYPE, %NL80211_ATTR_EXT_CAPA,
+ * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities per
+ * interface type.
*
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
@@ -2164,6 +2180,14 @@ enum nl80211_attrs {
NL80211_ATTR_PBSS,
+ NL80211_ATTR_BSS_SELECT,
+
+ NL80211_ATTR_STA_SUPPORT_P2P_PS,
+
+ NL80211_ATTR_PAD,
+
+ NL80211_ATTR_IFTYPE_EXT_CAPA,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index fe19c7596f8c..643c68f4c449 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -812,6 +812,15 @@ enum v4l2_mpeg_vidc_extradata {
#define V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI \
V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI
V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI = 27,
+#define V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO \
+ V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO
+ V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO = 28,
+#define V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY \
+ V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY
+ V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY = 29,
+#define V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE \
+ V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE
+ V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE = 30,
};
#define V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL (V4L2_CID_MPEG_MSM_VIDC_BASE + 26)
@@ -1173,6 +1182,23 @@ enum v4l2_mpeg_vidc_video_h264_transform_8x8 {
V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE = 1,
};
+#define V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 94)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 95)
+
+enum v4l2_cid_mpeg_vidc_video_full_range {
+ V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE_DISABLE = 0,
+ V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE_ENABLE = 1,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 96)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 97)
+
/* Camera class control IDs */
#define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900)
diff --git a/include/uapi/media/msm_vidc.h b/include/uapi/media/msm_vidc.h
index 40cd867a9b7b..b259bdef8a93 100644
--- a/include/uapi/media/msm_vidc.h
+++ b/include/uapi/media/msm_vidc.h
@@ -55,6 +55,18 @@ struct msm_vidc_mpeg2_seqdisp_payload {
unsigned int disp_height;
};
+struct msm_vidc_vc1_seqdisp_payload {
+ unsigned int prog_seg_format;
+ unsigned int uv_sampl_fmt;
+ unsigned int color_format;
+ unsigned int color_primaries;
+ unsigned int transfer_char;
+ unsigned int matrix_coeffs;
+ unsigned int aspect_ratio;
+ unsigned int aspect_horiz;
+ unsigned int aspect_vert;
+};
+
struct msm_vidc_input_crop_payload {
unsigned int size;
unsigned int version;
@@ -154,6 +166,13 @@ struct msm_vidc_yuv_stats_payload {
unsigned int frame_difference;
};
+struct msm_vidc_vpx_colorspace_payload {
+ unsigned int color_space;
+ unsigned int yuv_range_flag;
+ unsigned int sumsampling_x;
+ unsigned int sumsampling_y;
+};
+
struct msm_vidc_roi_qp_payload {
int upper_qp_offset;
int lower_qp_offset;
@@ -176,6 +195,23 @@ struct msm_vidc_content_light_level_sei_payload {
unsigned int nMaxPicAverageLight;
};
+struct msm_vidc_vui_display_info_payload {
+ unsigned int video_signal_present_flag;
+ unsigned int video_format;
+ unsigned int bit_depth_y;
+ unsigned int bit_depth_c;
+ unsigned int video_full_range_flag;
+ unsigned int color_description_present_flag;
+ unsigned int color_primaries;
+ unsigned int transfer_characteristics;
+ unsigned int matrix_coefficients;
+ unsigned int chroma_location_info_present_flag;
+ unsigned int chroma_format_idc;
+ unsigned int separate_color_plane_flag;
+ unsigned int chroma_sample_loc_type_top_field;
+ unsigned int chroma_sample_loc_type_bottom_field;
+};
+
enum msm_vidc_extradata_type {
MSM_VIDC_EXTRADATA_NONE = 0x00000000,
MSM_VIDC_EXTRADATA_MB_QUANTIZATION = 0x00000001,
@@ -199,11 +235,17 @@ enum msm_vidc_extradata_type {
#define MSM_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI \
MSM_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI
MSM_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI = 0x00000016,
+#define MSM_VIDC_EXTRADATA_PQ_INFO \
+ MSM_VIDC_EXTRADATA_PQ_INFO
+ MSM_VIDC_EXTRADATA_PQ_INFO = 0x00000017,
MSM_VIDC_EXTRADATA_INPUT_CROP = 0x0700000E,
#define MSM_VIDC_EXTRADATA_OUTPUT_CROP \
MSM_VIDC_EXTRADATA_OUTPUT_CROP
MSM_VIDC_EXTRADATA_OUTPUT_CROP = 0x0700000F,
MSM_VIDC_EXTRADATA_DIGITAL_ZOOM = 0x07000010,
+#define MSM_VIDC_EXTRADATA_VPX_COLORSPACE_INFO \
+ MSM_VIDC_EXTRADATA_VPX_COLORSPACE_INFO
+ MSM_VIDC_EXTRADATA_VPX_COLORSPACE_INFO = 0x070000011,
MSM_VIDC_EXTRADATA_MULTISLICE_INFO = 0x7F100000,
MSM_VIDC_EXTRADATA_NUM_CONCEALED_MB = 0x7F100001,
MSM_VIDC_EXTRADATA_INDEX = 0x7F100002,
@@ -211,6 +253,9 @@ enum msm_vidc_extradata_type {
MSM_VIDC_EXTRADATA_METADATA_LTR = 0x7F100004,
MSM_VIDC_EXTRADATA_METADATA_FILLER = 0x7FE00002,
MSM_VIDC_EXTRADATA_METADATA_MBI = 0x7F100005,
+#define MSM_VIDC_EXTRADATA_VUI_DISPLAY_INFO \
+ MSM_VIDC_EXTRADATA_VUI_DISPLAY_INFO
+ MSM_VIDC_EXTRADATA_VUI_DISPLAY_INFO = 0x7F100006,
MSM_VIDC_EXTRADATA_YUVSTATS_INFO = 0x7F100007,
};
enum msm_vidc_interlace_type {
@@ -240,12 +285,90 @@ enum msm_vidc_userdata_type {
MSM_VIDC_USERDATA_TYPE_BOTTOM_FIELD = 0x3,
};
+/* See colour_primaries of ISO/IEC 14496 for significance */
+enum msm_vidc_h264_color_primaries_values {
+ MSM_VIDC_RESERVED_1 = 0,
+ MSM_VIDC_BT709_5 = 1,
+ MSM_VIDC_UNSPECIFIED = 2,
+ MSM_VIDC_RESERVED_2 = 3,
+ MSM_VIDC_BT470_6_M = 4,
+ MSM_VIDC_BT601_6_625 = 5,
+ MSM_VIDC_BT470_6_BG = MSM_VIDC_BT601_6_625,
+ MSM_VIDC_BT601_6_525 = 6,
+ MSM_VIDC_SMPTE_240M = 7,
+ MSM_VIDC_GENERIC_FILM = 8,
+ MSM_VIDC_BT2020 = 9,
+};
+
+enum msm_vidc_vp9_color_primaries_values {
+ MSM_VIDC_CS_UNKNOWN,
+ MSM_VIDC_CS_BT_601,
+ MSM_VIDC_CS_BT_709,
+ MSM_VIDC_CS_SMPTE_170,
+ MSM_VIDC_CS_SMPTE_240,
+ MSM_VIDC_CS_BT_2020,
+ MSM_VIDC_CS_RESERVED,
+ MSM_VIDC_CS_RGB,
+};
+
+enum msm_vidc_h264_matrix_coeff_values {
+ MSM_VIDC_MATRIX_RGB = 0,
+ MSM_VIDC_MATRIX_BT_709_5 = 1,
+ MSM_VIDC_MATRIX_UNSPECIFIED = 2,
+ MSM_VIDC_MATRIX_RESERVED = 3,
+ MSM_VIDC_MATRIX_FCC_47 = 4,
+ MSM_VIDC_MATRIX_601_6_625 = 5,
+ MSM_VIDC_MATRIX_BT470_BG = MSM_VIDC_MATRIX_601_6_625,
+ MSM_VIDC_MATRIX_601_6_525 = 6,
+ MSM_VIDC_MATRIX_SMPTE_170M = MSM_VIDC_MATRIX_601_6_525,
+ MSM_VIDC_MATRIX_SMPTE_240M = 7,
+ MSM_VIDC_MATRIX_Y_CG_CO = 8,
+ MSM_VIDC_MATRIX_BT_2020 = 9,
+ MSM_VIDC_MATRIX_BT_2020_CONST = 10,
+};
+
+enum msm_vidc_h264_transfer_chars_values {
+ MSM_VIDC_TRANSFER_RESERVED_1 = 0,
+ MSM_VIDC_TRANSFER_BT709_5 = 1,
+ MSM_VIDC_TRANSFER_UNSPECIFIED = 2,
+ MSM_VIDC_TRANSFER_RESERVED_2 = 3,
+ MSM_VIDC_TRANSFER_BT_470_6_M = 4,
+ MSM_VIDC_TRANSFER_BT_470_6_BG = 5,
+ MSM_VIDC_TRANSFER_601_6_625 = 6,
+ MSM_VIDC_TRANSFER_601_6_525 = MSM_VIDC_TRANSFER_601_6_625,
+ MSM_VIDC_TRANSFER_SMPTE_240M = 7,
+ MSM_VIDC_TRANSFER_LINEAR = 8,
+ MSM_VIDC_TRANSFER_LOG_100_1 = 9,
+ MSM_VIDC_TRANSFER_LOG_100_SQRT10_1 = 10,
+ MSM_VIDC_TRANSFER_IEC_61966 = 11,
+ MSM_VIDC_TRANSFER_BT_1361 = 12,
+ MSM_VIDC_TRANSFER_SRGB = 13,
+ MSM_VIDC_TRANSFER_BT_2020_10 = 14,
+ MSM_VIDC_TRANSFER_BT_2020_12 = 15,
+};
+
enum msm_vidc_pixel_depth {
MSM_VIDC_BIT_DEPTH_8,
MSM_VIDC_BIT_DEPTH_10,
MSM_VIDC_BIT_DEPTH_UNSUPPORTED = 0XFFFFFFFF,
};
+enum msm_vidc_video_format {
+ MSM_VIDC_COMPONENT,
+ MSM_VIDC_PAL,
+ MSM_VIDC_NTSC,
+ MSM_VIDC_SECAM,
+ MSM_VIDC_MAC,
+ MSM_VIDC_UNSPECIFIED_FORMAT,
+ MSM_VIDC_RESERVED_1_FORMAT,
+ MSM_VIDC_RESERVED_2_FORMAT,
+};
+
+enum msm_vidc_color_desc_flag {
+ MSM_VIDC_COLOR_DESC_NOT_PRESENT,
+ MSM_VIDC_COLOR_DESC_PRESENT,
+};
+
/*enum msm_vidc_pic_struct */
#define MSM_VIDC_PIC_STRUCT_MAYBE_INTERLACED 0x0
#define MSM_VIDC_PIC_STRUCT_PROGRESSIVE 0x1
diff --git a/input/touchscreen/gt9xx/gt9xx.c b/input/touchscreen/gt9xx/gt9xx.c
deleted file mode 100644
index b1dc08bdd54f..000000000000
--- a/input/touchscreen/gt9xx/gt9xx.c
+++ /dev/null
@@ -1,1810 +0,0 @@
-/* drivers/input/touchscreen/gt9xx.c
- *
- * 2010 - 2013 Goodix Technology.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be a reference
- * to you, when you are integrating the GOODiX's CTP IC into your system,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * Version: 1.8
- * Authors: andrew@goodix.com, meta@goodix.com
- * Release Date: 2013/04/25
- * Revision record:
- * V1.0:
- * first Release. By Andrew, 2012/08/31
- * V1.2:
- * modify gtp_reset_guitar,slot report,tracking_id & 0x0F. By Andrew, 2012/10/15
- * V1.4:
- * modify gt9xx_update.c. By Andrew, 2012/12/12
- * V1.6:
- * 1. new heartbeat/esd_protect mechanism(add external watchdog)
- * 2. doze mode, sliding wakeup
- * 3. 3 more cfg_group(GT9 Sensor_ID: 0~5)
- * 3. config length verification
- * 4. names & comments
- * By Meta, 2013/03/11
- * V1.8:
- * 1. pen/stylus identification
- * 2. read double check & fixed config support
- * 2. new esd & slide wakeup optimization
- * By Meta, 2013/06/08
- */
-
-#include <linux/irq.h>
-#include "gt9xx.h"
-
-#if GTP_ICS_SLOT_REPORT
- #include <linux/input/mt.h>
-#endif
-
-static const char *goodix_ts_name = "Goodix Capacitive TouchScreen";
-static struct workqueue_struct *goodix_wq;
-struct i2c_client * i2c_connect_client = NULL;
-u8 config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH]
- = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};
-
-#if GTP_HAVE_TOUCH_KEY
- static const u16 touch_key_array[] = GTP_KEY_TAB;
- #define GTP_MAX_KEY_NUM (sizeof(touch_key_array)/sizeof(touch_key_array[0]))
-
-#if GTP_DEBUG_ON
- static const int key_codes[] = {KEY_HOME, KEY_BACK, KEY_MENU, KEY_SEARCH};
- static const char *key_names[] = {"Key_Home", "Key_Back", "Key_Menu", "Key_Search"};
-#endif
-
-#endif
-
-static s8 gtp_i2c_test(struct i2c_client *client);
-void gtp_reset_guitar(struct i2c_client *client, s32 ms);
-void gtp_int_sync(s32 ms);
-
-#ifdef CONFIG_HAS_EARLYSUSPEND
-static void goodix_ts_early_suspend(struct early_suspend *h);
-static void goodix_ts_late_resume(struct early_suspend *h);
-#endif
-
-#if GTP_CREATE_WR_NODE
-extern s32 init_wr_node(struct i2c_client*);
-extern void uninit_wr_node(void);
-#endif
-
-#if GTP_AUTO_UPDATE
-extern u8 gup_init_update_proc(struct goodix_ts_data *);
-#endif
-
-#if GTP_ESD_PROTECT
-static struct delayed_work gtp_esd_check_work;
-static struct workqueue_struct * gtp_esd_check_workqueue = NULL;
-static void gtp_esd_check_func(struct work_struct *);
-static s32 gtp_init_ext_watchdog(struct i2c_client *client);
-void gtp_esd_switch(struct i2c_client *, s32);
-#endif
-
-
-#if GTP_SLIDE_WAKEUP
-typedef enum
-{
- DOZE_DISABLED = 0,
- DOZE_ENABLED = 1,
- DOZE_WAKEUP = 2,
-}DOZE_T;
-static DOZE_T doze_status = DOZE_DISABLED;
-static s8 gtp_enter_doze(struct goodix_ts_data *ts);
-#endif
-
-static u8 chip_gt9xxs = 0; // true if ic is gt9xxs, like gt915s
-u8 grp_cfg_version = 0;
-
-/*******************************************************
-Function:
- Read data from the i2c slave device.
-Input:
- client: i2c device.
- buf[0~1]: read start address.
- buf[2~len-1]: read data buffer.
- len: GTP_ADDR_LENGTH + read bytes count
-Output:
- numbers of i2c_msgs to transfer:
- 2: succeed, otherwise: failed
-*********************************************************/
-s32 gtp_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
-{
- struct i2c_msg msgs[2];
- s32 ret=-1;
- s32 retries = 0;
-
- GTP_DEBUG_FUNC();
-
- msgs[0].flags = !I2C_M_RD;
- msgs[0].addr = client->addr;
- msgs[0].len = GTP_ADDR_LENGTH;
- msgs[0].buf = &buf[0];
- //msgs[0].scl_rate = 300 * 1000; // for Rockchip
-
- msgs[1].flags = I2C_M_RD;
- msgs[1].addr = client->addr;
- msgs[1].len = len - GTP_ADDR_LENGTH;
- msgs[1].buf = &buf[GTP_ADDR_LENGTH];
- //msgs[1].scl_rate = 300 * 1000;
-
- while(retries < 5)
- {
- ret = i2c_transfer(client->adapter, msgs, 2);
- if(ret == 2)break;
- retries++;
- }
- if((retries >= 5))
- {
- #if GTP_SLIDE_WAKEUP
- // reset chip would quit doze mode
- if (DOZE_ENABLED == doze_status)
- {
- return ret;
- }
- #endif
- GTP_DEBUG("I2C communication timeout, resetting chip...");
- gtp_reset_guitar(client, 10);
- }
- return ret;
-}
-
-/*******************************************************
-Function:
- Write data to the i2c slave device.
-Input:
- client: i2c device.
- buf[0~1]: write start address.
- buf[2~len-1]: data buffer
- len: GTP_ADDR_LENGTH + write bytes count
-Output:
- numbers of i2c_msgs to transfer:
- 1: succeed, otherwise: failed
-*********************************************************/
-s32 gtp_i2c_write(struct i2c_client *client,u8 *buf,s32 len)
-{
- struct i2c_msg msg;
- s32 ret = -1;
- s32 retries = 0;
-
- GTP_DEBUG_FUNC();
-
- msg.flags = !I2C_M_RD;
- msg.addr = client->addr;
- msg.len = len;
- msg.buf = buf;
- //msg.scl_rate = 300 * 1000; // for Rockchip
-
- while(retries < 5)
- {
- ret = i2c_transfer(client->adapter, &msg, 1);
- if (ret == 1)break;
- retries++;
- }
- if((retries >= 5))
- {
- #if GTP_SLIDE_WAKEUP
- if (DOZE_ENABLED == doze_status)
- {
- return ret;
- }
- #endif
- GTP_DEBUG("I2C communication timeout, resetting chip...");
- gtp_reset_guitar(client, 10);
- }
- return ret;
-}
-/*******************************************************
-Function:
- i2c read twice, compare the results
-Input:
- client: i2c device
- addr: operate address
- rxbuf: read data to store, if compare successful
- len: bytes to read
-Output:
- FAIL: read failed
- SUCCESS: read successful
-*********************************************************/
-s32 gtp_i2c_read_dbl_check(struct i2c_client *client, u16 addr, u8 *rxbuf, int len)
-{
- u8 buf[16] = {0};
- u8 confirm_buf[16] = {0};
- u8 retry = 0;
-
- while (retry++ < 3)
- {
- memset(buf, 0xAA, 16);
- buf[0] = (u8)(addr >> 8);
- buf[1] = (u8)(addr & 0xFF);
- gtp_i2c_read(client, buf, len + 2);
-
- memset(confirm_buf, 0xAB, 16);
- confirm_buf[0] = (u8)(addr >> 8);
- confirm_buf[1] = (u8)(addr & 0xFF);
- gtp_i2c_read(client, confirm_buf, len + 2);
-
- if (!memcmp(buf, confirm_buf, len+2))
- {
- break;
- }
- }
- if (retry < 3)
- {
- memcpy(rxbuf, confirm_buf+2, len);
- return SUCCESS;
- }
- else
- {
- GTP_ERROR("i2c read 0x%04X, %d bytes, double check failed!", addr, len);
- return FAIL;
- }
-}
-
-/*******************************************************
-Function:
- Send config.
-Input:
- client: i2c device.
-Output:
- result of i2c write operation.
- 1: succeed, otherwise: failed
-*********************************************************/
-s32 gtp_send_cfg(struct i2c_client *client)
-{
- s32 ret = 2;
-
-#if GTP_DRIVER_SEND_CFG
- s32 retry = 0;
- struct goodix_ts_data *ts = i2c_get_clientdata(client);
-
- if (ts->fixed_cfg)
- {
- GTP_INFO("Ic fixed config, no config sent!");
- return 2;
- }
- GTP_INFO("driver send config");
- for (retry = 0; retry < 5; retry++)
- {
- ret = gtp_i2c_write(client, config , GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH);
- if (ret > 0)
- {
- break;
- }
- }
-#endif
-
- return ret;
-}
-
-/*******************************************************
-Function:
- Disable irq function
-Input:
- ts: goodix i2c_client private data
-Output:
- None.
-*********************************************************/
-void gtp_irq_disable(struct goodix_ts_data *ts)
-{
- unsigned long irqflags;
-
- GTP_DEBUG_FUNC();
-
- spin_lock_irqsave(&ts->irq_lock, irqflags);
- if (!ts->irq_is_disable)
- {
- ts->irq_is_disable = 1;
- disable_irq_nosync(ts->client->irq);
- }
- spin_unlock_irqrestore(&ts->irq_lock, irqflags);
-}
-
-/*******************************************************
-Function:
- Enable irq function
-Input:
- ts: goodix i2c_client private data
-Output:
- None.
-*********************************************************/
-void gtp_irq_enable(struct goodix_ts_data *ts)
-{
- unsigned long irqflags = 0;
-
- GTP_DEBUG_FUNC();
-
- spin_lock_irqsave(&ts->irq_lock, irqflags);
- if (ts->irq_is_disable)
- {
- enable_irq(ts->client->irq);
- ts->irq_is_disable = 0;
- }
- spin_unlock_irqrestore(&ts->irq_lock, irqflags);
-}
-
-
-/*******************************************************
-Function:
- Report touch point event
-Input:
- ts: goodix i2c_client private data
- id: trackId
- x: input x coordinate
- y: input y coordinate
- w: input pressure
-Output:
- None.
-*********************************************************/
-static void gtp_touch_down(struct goodix_ts_data* ts,s32 id,s32 x,s32 y,s32 w)
-{
-#if GTP_CHANGE_X2Y
- GTP_SWAP(x, y);
-#endif
-
-#if GTP_ICS_SLOT_REPORT
- input_mt_slot(ts->input_dev, id);
- input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
- input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
- input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
- input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
- input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
-#else
- input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
- input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
- input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
- input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
- input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
- input_mt_sync(ts->input_dev);
-#endif
-
- GTP_DEBUG("ID:%d, X:%d, Y:%d, W:%d", id, x, y, w);
-}
-
-/*******************************************************
-Function:
- Report touch release event
-Input:
- ts: goodix i2c_client private data
-Output:
- None.
-*********************************************************/
-static void gtp_touch_up(struct goodix_ts_data* ts, s32 id)
-{
-#if GTP_ICS_SLOT_REPORT
- input_mt_slot(ts->input_dev, id);
- input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
- GTP_DEBUG("Touch id[%2d] release!", id);
-#else
- input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
- input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0);
- input_mt_sync(ts->input_dev);
-#endif
-}
-
-
-/*******************************************************
-Function:
- Goodix touchscreen work function
-Input:
- work: work struct of goodix_workqueue
-Output:
- None.
-*********************************************************/
-static void goodix_ts_work_func(struct work_struct *work)
-{
- u8 end_cmd[3] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF, 0};
- u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1]={GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF};
- u8 touch_num = 0;
- u8 finger = 0;
- static u16 pre_touch = 0;
- static u8 pre_key = 0;
-#if GTP_WITH_PEN
- static u8 pre_pen = 0;
-#endif
- u8 key_value = 0;
- u8* coor_data = NULL;
- s32 input_x = 0;
- s32 input_y = 0;
- s32 input_w = 0;
- s32 id = 0;
- s32 i = 0;
- s32 ret = -1;
- struct goodix_ts_data *ts = NULL;
-
-#if GTP_SLIDE_WAKEUP
- u8 doze_buf[3] = {0x81, 0x4B};
-#endif
-
- GTP_DEBUG_FUNC();
- ts = container_of(work, struct goodix_ts_data, work);
- if (ts->enter_update)
- {
- return;
- }
-#if GTP_SLIDE_WAKEUP
- if (DOZE_ENABLED == doze_status)
- {
- ret = gtp_i2c_read(i2c_connect_client, doze_buf, 3);
- GTP_DEBUG("0x814B = 0x%02X", doze_buf[2]);
- if (ret > 0)
- {
- if (doze_buf[2] == 0xAA)
- {
- GTP_INFO("Slide(0xAA) To Light up the screen!");
- doze_status = DOZE_WAKEUP;
- input_report_key(ts->input_dev, KEY_POWER, 1);
- input_sync(ts->input_dev);
- input_report_key(ts->input_dev, KEY_POWER, 0);
- input_sync(ts->input_dev);
- // clear 0x814B
- doze_buf[2] = 0x00;
- gtp_i2c_write(i2c_connect_client, doze_buf, 3);
- }
- else if (doze_buf[2] == 0xBB)
- {
- GTP_INFO("Slide(0xBB) To Light up the screen!");
- doze_status = DOZE_WAKEUP;
- input_report_key(ts->input_dev, KEY_POWER, 1);
- input_sync(ts->input_dev);
- input_report_key(ts->input_dev, KEY_POWER, 0);
- input_sync(ts->input_dev);
- // clear 0x814B
- doze_buf[2] = 0x00;
- gtp_i2c_write(i2c_connect_client, doze_buf, 3);
- }
- else if (0xC0 == (doze_buf[2] & 0xC0))
- {
- GTP_INFO("double click to light up the screen!");
- doze_status = DOZE_WAKEUP;
- input_report_key(ts->input_dev, KEY_POWER, 1);
- input_sync(ts->input_dev);
- input_report_key(ts->input_dev, KEY_POWER, 0);
- input_sync(ts->input_dev);
- // clear 0x814B
- doze_buf[2] = 0x00;
- gtp_i2c_write(i2c_connect_client, doze_buf, 3);
- }
- else
- {
- gtp_enter_doze(ts);
- }
- }
- if (ts->use_irq)
- {
- gtp_irq_enable(ts);
- }
- return;
- }
-#endif
-
- ret = gtp_i2c_read(ts->client, point_data, 12);
- if (ret < 0)
- {
- GTP_ERROR("I2C transfer error. errno:%d\n ", ret);
- goto exit_work_func;
- }
-
- finger = point_data[GTP_ADDR_LENGTH];
- if((finger & 0x80) == 0)
- {
- goto exit_work_func;
- }
-
- touch_num = finger & 0x0f;
- if (touch_num > GTP_MAX_TOUCH)
- {
- goto exit_work_func;
- }
-
- if (touch_num > 1)
- {
- u8 buf[8 * GTP_MAX_TOUCH] = {(GTP_READ_COOR_ADDR + 10) >> 8, (GTP_READ_COOR_ADDR + 10) & 0xff};
-
- ret = gtp_i2c_read(ts->client, buf, 2 + 8 * (touch_num - 1));
- memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));
- }
-
-#if GTP_HAVE_TOUCH_KEY
- key_value = point_data[3 + 8 * touch_num];
-
- if(key_value || pre_key)
- {
- for (i = 0; i < GTP_MAX_KEY_NUM; i++)
- {
- #if GTP_DEBUG_ON
- for (ret = 0; ret < 4; ++ret)
- {
- if (key_codes[ret] == touch_key_array[i])
- {
- GTP_DEBUG("Key: %s %s", key_names[ret], (key_value & (0x01 << i)) ? "Down" : "Up");
- break;
- }
- }
- #endif
- input_report_key(ts->input_dev, touch_key_array[i], key_value & (0x01<<i));
- }
- touch_num = 0;
- pre_touch = 0;
- }
-#endif
- pre_key = key_value;
-
- GTP_DEBUG("pre_touch:%02x, finger:%02x.", pre_touch, finger);
-
-#if GTP_ICS_SLOT_REPORT
-
-#if GTP_WITH_PEN
- if (pre_pen && (touch_num == 0))
- {
- GTP_DEBUG("Pen touch UP(Slot)!");
- input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
- input_mt_slot(ts->input_dev, 5);
- input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
- pre_pen = 0;
- }
-#endif
- if (pre_touch || touch_num)
- {
- s32 pos = 0;
- u16 touch_index = 0;
-
- coor_data = &point_data[3];
-
- if(touch_num)
- {
- id = coor_data[pos] & 0x0F;
-
- #if GTP_WITH_PEN
- id = coor_data[pos];
- if ((id == 128))
- {
- GTP_DEBUG("Pen touch DOWN(Slot)!");
- input_x = coor_data[pos + 1] | (coor_data[pos + 2] << 8);
- input_y = coor_data[pos + 3] | (coor_data[pos + 4] << 8);
- input_w = coor_data[pos + 5] | (coor_data[pos + 6] << 8);
-
- input_report_key(ts->input_dev, BTN_TOOL_PEN, 1);
- input_mt_slot(ts->input_dev, 5);
- input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 5);
- input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
- input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
- input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);
- GTP_DEBUG("Pen/Stylus: (%d, %d)[%d]", input_x, input_y, input_w);
- pre_pen = 1;
- pre_touch = 0;
- }
- #endif
-
- touch_index |= (0x01<<id);
- }
-
- GTP_DEBUG("id = %d,touch_index = 0x%x, pre_touch = 0x%x\n",id, touch_index,pre_touch);
- for (i = 0; i < GTP_MAX_TOUCH; i++)
- {
- #if GTP_WITH_PEN
- if (pre_pen == 1)
- {
- break;
- }
- #endif
-
- if (touch_index & (0x01<<i))
- {
- input_x = coor_data[pos + 1] | (coor_data[pos + 2] << 8);
- input_y = coor_data[pos + 3] | (coor_data[pos + 4] << 8);
- input_w = coor_data[pos + 5] | (coor_data[pos + 6] << 8);
-
- gtp_touch_down(ts, id, input_x, input_y, input_w);
- pre_touch |= 0x01 << i;
-
- pos += 8;
- id = coor_data[pos] & 0x0F;
- touch_index |= (0x01<<id);
- }
- else
- {
- gtp_touch_up(ts, i);
- pre_touch &= ~(0x01 << i);
- }
- }
- }
-#else
- input_report_key(ts->input_dev, BTN_TOUCH, (touch_num || key_value));
- if (touch_num)
- {
- for (i = 0; i < touch_num; i++)
- {
- coor_data = &point_data[i * 8 + 3];
-
- id = coor_data[0]; // & 0x0F;
- input_x = coor_data[1] | (coor_data[2] << 8);
- input_y = coor_data[3] | (coor_data[4] << 8);
- input_w = coor_data[5] | (coor_data[6] << 8);
-
- #if GTP_WITH_PEN
- if (id == 128)
- {
- GTP_DEBUG("Pen touch DOWN!");
- input_report_key(ts->input_dev, BTN_TOOL_PEN, 1);
- pre_pen = 1;
- id = 0;
- }
- #endif
-
- gtp_touch_down(ts, id, input_x, input_y, input_w);
- }
- }
- else if (pre_touch)
- {
-
- #if GTP_WITH_PEN
- if (pre_pen == 1)
- {
- GTP_DEBUG("Pen touch UP!");
- input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
- pre_pen = 0;
- }
- #endif
-
- GTP_DEBUG("Touch Release!");
- gtp_touch_up(ts, 0);
- }
-
- pre_touch = touch_num;
-#endif
-
- input_sync(ts->input_dev);
-
-exit_work_func:
- if(!ts->gtp_rawdiff_mode)
- {
- ret = gtp_i2c_write(ts->client, end_cmd, 3);
- if (ret < 0)
- {
- GTP_INFO("I2C write end_cmd error!");
- }
- }
- if (ts->use_irq)
- {
- gtp_irq_enable(ts);
- }
-}
-
-/*******************************************************
-Function:
- Timer interrupt service routine for polling mode.
-Input:
- timer: timer struct pointer
-Output:
- Timer work mode.
- HRTIMER_NORESTART: no restart mode
-*********************************************************/
-static enum hrtimer_restart goodix_ts_timer_handler(struct hrtimer *timer)
-{
- struct goodix_ts_data *ts = container_of(timer, struct goodix_ts_data, timer);
-
- GTP_DEBUG_FUNC();
-
- queue_work(goodix_wq, &ts->work);
- hrtimer_start(&ts->timer, ktime_set(0, (GTP_POLL_TIME+6)*1000000), HRTIMER_MODE_REL);
- return HRTIMER_NORESTART;
-}
-
-/*******************************************************
-Function:
- External interrupt service routine for interrupt mode.
-Input:
- irq: interrupt number.
- dev_id: private data pointer
-Output:
- Handle Result.
- IRQ_HANDLED: interrupt handled successfully
-*********************************************************/
-static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
-{
- struct goodix_ts_data *ts = dev_id;
-
- GTP_DEBUG_FUNC();
-
- gtp_irq_disable(ts);
-
- queue_work(goodix_wq, &ts->work);
-
- return IRQ_HANDLED;
-}
-/*******************************************************
-Function:
- Synchronization.
-Input:
- ms: synchronization time in millisecond.
-Output:
- None.
-*******************************************************/
-void gtp_int_sync(s32 ms)
-{
- GTP_GPIO_OUTPUT(GTP_INT_PORT, 0);
- msleep(ms);
- GTP_GPIO_AS_INT(GTP_INT_PORT);
-}
-
-/*******************************************************
-Function:
- Reset chip.
-Input:
- ms: reset time in millisecond
-Output:
- None.
-*******************************************************/
-void gtp_reset_guitar(struct i2c_client *client, s32 ms)
-{
- GTP_DEBUG_FUNC();
-
- GTP_GPIO_OUTPUT(GTP_RST_PORT, 0); // begin select I2C slave addr
- msleep(ms); // T2: > 10ms
- // HIGH: 0x28/0x29, LOW: 0xBA/0xBB
- GTP_GPIO_OUTPUT(GTP_INT_PORT, client->addr == 0x14);
-
- msleep(2); // T3: > 100us
- GTP_GPIO_OUTPUT(GTP_RST_PORT, 1);
-
- msleep(6); // T4: > 5ms
-
- GTP_GPIO_AS_INPUT(GTP_RST_PORT); // end select I2C slave addr
-
- gtp_int_sync(50);
-
-#if GTP_ESD_PROTECT
- gtp_init_ext_watchdog(client);
-#endif
-}
-
-#if GTP_SLIDE_WAKEUP
-/*******************************************************
-Function:
- Enter doze mode for sliding wakeup.
-Input:
- ts: goodix tp private data
-Output:
- 1: succeed, otherwise failed
-*******************************************************/
-static s8 gtp_enter_doze(struct goodix_ts_data *ts)
-{
- s8 ret = -1;
- s8 retry = 0;
- u8 i2c_control_buf[3] = {(u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 8};
-
- GTP_DEBUG_FUNC();
-
-#if GTP_DBL_CLK_WAKEUP
- i2c_control_buf[2] = 0x09;
-#endif
-
- gtp_irq_disable(ts);
-
- GTP_DEBUG("entering doze mode...");
- while(retry++ < 5)
- {
- i2c_control_buf[0] = 0x80;
- i2c_control_buf[1] = 0x46;
- ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
- if (ret < 0)
- {
- GTP_DEBUG("failed to set doze flag into 0x8046, %d", retry);
- continue;
- }
- i2c_control_buf[0] = 0x80;
- i2c_control_buf[1] = 0x40;
- ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
- if (ret > 0)
- {
- doze_status = DOZE_ENABLED;
- GTP_INFO("GTP has been working in doze mode!");
- gtp_irq_enable(ts);
- return ret;
- }
- msleep(10);
- }
- GTP_ERROR("GTP send doze cmd failed.");
- gtp_irq_enable(ts);
- return ret;
-}
-#else
-/*******************************************************
-Function:
- Enter sleep mode.
-Input:
- ts: private data.
-Output:
- Executive outcomes.
- 1: succeed, otherwise failed.
-*******************************************************/
-static s8 gtp_enter_sleep(struct goodix_ts_data * ts)
-{
- s8 ret = -1;
- s8 retry = 0;
- u8 i2c_control_buf[3] = {(u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 5};
-
- GTP_DEBUG_FUNC();
-
- GTP_GPIO_OUTPUT(GTP_INT_PORT, 0);
- msleep(5);
-
- while(retry++ < 5)
- {
- ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
- if (ret > 0)
- {
- GTP_INFO("GTP enter sleep!");
-
- return ret;
- }
- msleep(10);
- }
- GTP_ERROR("GTP send sleep cmd failed.");
- return ret;
-}
-#endif
-/*******************************************************
-Function:
- Wakeup from sleep.
-Input:
- ts: private data.
-Output:
- Executive outcomes.
- >0: succeed, otherwise: failed.
-*******************************************************/
-static s8 gtp_wakeup_sleep(struct goodix_ts_data * ts)
-{
- u8 retry = 0;
- s8 ret = -1;
-
- GTP_DEBUG_FUNC();
-
-#if GTP_POWER_CTRL_SLEEP
- while(retry++ < 5)
- {
- gtp_reset_guitar(ts->client, 20);
-
- ret = gtp_send_cfg(ts->client);
- if (ret < 0)
- {
- GTP_INFO("Wakeup sleep send config failed!");
- continue;
- }
- GTP_INFO("GTP wakeup sleep");
- return 1;
- }
-#else
- while(retry++ < 10)
- {
- #if GTP_SLIDE_WAKEUP
- if (DOZE_WAKEUP != doze_status) // wakeup not by slide
- {
- gtp_reset_guitar(ts->client, 10);
- }
- else // wakeup by slide
- {
- doze_status = DOZE_DISABLED;
- }
- #else
- if (chip_gt9xxs == 1)
- {
- gtp_reset_guitar(ts->client, 10);
- }
- else
- {
- GTP_GPIO_OUTPUT(GTP_INT_PORT, 1);
- msleep(5);
- }
- #endif
- ret = gtp_i2c_test(ts->client);
- if (ret > 0)
- {
- GTP_INFO("GTP wakeup sleep.");
-
- #if (!GTP_SLIDE_WAKEUP)
- if (chip_gt9xxs == 0)
- {
- gtp_int_sync(25);
- msleep(20);
- #if GTP_ESD_PROTECT
- gtp_init_ext_watchdog(ts->client);
- #endif
- }
- #endif
- return ret;
- }
- gtp_reset_guitar(ts->client, 20);
- }
-#endif
-
- GTP_ERROR("GTP wakeup sleep failed.");
- return ret;
-}
-
-/*******************************************************
-Function:
- Initialize gtp.
-Input:
- ts: goodix private data
-Output:
- Executive outcomes.
- 0: succeed, otherwise: failed
-*******************************************************/
-static s32 gtp_init_panel(struct goodix_ts_data *ts)
-{
- s32 ret = -1;
-
-#if GTP_DRIVER_SEND_CFG
- s32 i;
- u8 check_sum = 0;
- u8 opr_buf[16];
- u8 sensor_id = 0;
-
- u8 cfg_info_group1[] = CTP_CFG_GROUP1;
- u8 cfg_info_group2[] = CTP_CFG_GROUP2;
- u8 cfg_info_group3[] = CTP_CFG_GROUP3;
- u8 cfg_info_group4[] = CTP_CFG_GROUP4;
- u8 cfg_info_group5[] = CTP_CFG_GROUP5;
- u8 cfg_info_group6[] = CTP_CFG_GROUP6;
- u8 *send_cfg_buf[] = {cfg_info_group1, cfg_info_group2, cfg_info_group3,
- cfg_info_group4, cfg_info_group5, cfg_info_group6};
- u8 cfg_info_len[] = { CFG_GROUP_LEN(cfg_info_group1),
- CFG_GROUP_LEN(cfg_info_group2),
- CFG_GROUP_LEN(cfg_info_group3),
- CFG_GROUP_LEN(cfg_info_group4),
- CFG_GROUP_LEN(cfg_info_group5),
- CFG_GROUP_LEN(cfg_info_group6)};
-
- GTP_DEBUG("Config Groups\' Lengths: %d, %d, %d, %d, %d, %d",
- cfg_info_len[0], cfg_info_len[1], cfg_info_len[2], cfg_info_len[3],
- cfg_info_len[4], cfg_info_len[5]);
-
- ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1);
- if (SUCCESS == ret)
- {
- if (opr_buf[0] != 0xBE)
- {
- ts->fw_error = 1;
- GTP_ERROR("Firmware error, no config sent!");
- return -1;
- }
- }
-
- if ((!cfg_info_len[1]) && (!cfg_info_len[2]) &&
- (!cfg_info_len[3]) && (!cfg_info_len[4]) &&
- (!cfg_info_len[5]))
- {
- sensor_id = 0;
- }
- else
- {
- ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID, &sensor_id, 1);
- if (SUCCESS == ret)
- {
- if (sensor_id >= 0x06)
- {
- GTP_ERROR("Invalid sensor_id(0x%02X), No Config Sent!", sensor_id);
- return -1;
- }
- }
- else
- {
- GTP_ERROR("Failed to get sensor_id, No config sent!");
- return -1;
- }
- }
- GTP_DEBUG("Sensor_ID: %d", sensor_id);
-
- ts->gtp_cfg_len = cfg_info_len[sensor_id];
-
- if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH)
- {
- GTP_ERROR("Sensor_ID(%d) matches with NULL or INVALID CONFIG GROUP! NO Config Sent! You need to check you header file CFG_GROUP section!", sensor_id);
- return -1;
- }
-
- ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, &opr_buf[0], 1);
-
- if (ret == SUCCESS)
- {
- GTP_DEBUG("CFG_GROUP%d Config Version: %d, 0x%02X; IC Config Version: %d, 0x%02X", sensor_id+1,
- send_cfg_buf[sensor_id][0], send_cfg_buf[sensor_id][0], opr_buf[0], opr_buf[0]);
-
- if (opr_buf[0] < 90)
- {
- grp_cfg_version = send_cfg_buf[sensor_id][0]; // backup group config version
- send_cfg_buf[sensor_id][0] = 0x00;
- ts->fixed_cfg = 0;
- }
- else // treated as fixed config, not send config
- {
- GTP_INFO("Ic fixed config with config version(%d, 0x%02X)", opr_buf[0], opr_buf[0]);
- ts->fixed_cfg = 1;
- }
- }
- else
- {
- GTP_ERROR("Failed to get ic config version!No config sent!");
- return -1;
- }
-
- memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH);
- memcpy(&config[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id], ts->gtp_cfg_len);
-
-#if GTP_CUSTOM_CFG
- config[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH;
- config[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8);
- config[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT;
- config[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8);
-
- if (GTP_INT_TRIGGER == 0) //RISING
- {
- config[TRIGGER_LOC] &= 0xfe;
- }
- else if (GTP_INT_TRIGGER == 1) //FALLING
- {
- config[TRIGGER_LOC] |= 0x01;
- }
-#endif // GTP_CUSTOM_CFG
-
- check_sum = 0;
- for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++)
- {
- check_sum += config[i];
- }
- config[ts->gtp_cfg_len] = (~check_sum) + 1;
-
-#else // DRIVER NOT SEND CONFIG
- ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH;
- ret = gtp_i2c_read(ts->client, config, ts->gtp_cfg_len + GTP_ADDR_LENGTH);
- if (ret < 0)
- {
- GTP_ERROR("Read Config Failed, Using Default Resolution & INT Trigger!");
- ts->abs_x_max = GTP_MAX_WIDTH;
- ts->abs_y_max = GTP_MAX_HEIGHT;
- ts->int_trigger_type = GTP_INT_TRIGGER;
- }
-#endif // GTP_DRIVER_SEND_CFG
-
- GTP_DEBUG_FUNC();
- if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0))
- {
- ts->abs_x_max = (config[RESOLUTION_LOC + 1] << 8) + config[RESOLUTION_LOC];
- ts->abs_y_max = (config[RESOLUTION_LOC + 3] << 8) + config[RESOLUTION_LOC + 2];
- ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03;
- }
- ret = gtp_send_cfg(ts->client);
- if (ret < 0)
- {
- GTP_ERROR("Send config error.");
- }
- GTP_DEBUG("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x",
- ts->abs_x_max,ts->abs_y_max,ts->int_trigger_type);
-
- msleep(10);
- return 0;
-}
-
-/*******************************************************
-Function:
- Read chip version.
-Input:
- client: i2c device
- version: buffer to keep ic firmware version
-Output:
- read operation return.
- 2: succeed, otherwise: failed
-*******************************************************/
-s32 gtp_read_version(struct i2c_client *client, u16* version)
-{
- s32 ret = -1;
- u8 buf[8] = {GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff};
-
- GTP_DEBUG_FUNC();
-
- ret = gtp_i2c_read(client, buf, sizeof(buf));
- if (ret < 0)
- {
- GTP_ERROR("GTP read version failed");
- return ret;
- }
-
- if (version)
- {
- *version = (buf[7] << 8) | buf[6];
- }
-
- if (buf[5] == 0x00)
- {
- GTP_INFO("IC Version: %c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[7], buf[6]);
- }
- else
- {
- if (buf[5] == 'S' || buf[5] == 's')
- {
- chip_gt9xxs = 1;
- }
- GTP_INFO("IC Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);
- }
- return ret;
-}
-
-/*******************************************************
-Function:
- I2c test Function.
-Input:
- client:i2c client.
-Output:
- Executive outcomes.
- 2: succeed, otherwise failed.
-*******************************************************/
-static s8 gtp_i2c_test(struct i2c_client *client)
-{
- u8 test[3] = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};
- u8 retry = 0;
- s8 ret = -1;
-
- GTP_DEBUG_FUNC();
-
- while(retry++ < 5)
- {
- ret = gtp_i2c_read(client, test, 3);
- if (ret > 0)
- {
- return ret;
- }
- GTP_ERROR("GTP i2c test failed time %d.",retry);
- msleep(10);
- }
- return ret;
-}
-
-/*******************************************************
-Function:
- Request gpio(INT & RST) ports.
-Input:
- ts: private data.
-Output:
- Executive outcomes.
- >= 0: succeed, < 0: failed
-*******************************************************/
-static s8 gtp_request_io_port(struct goodix_ts_data *ts)
-{
- s32 ret = 0;
-
- ret = GTP_GPIO_REQUEST(GTP_INT_PORT, "GTP_INT_IRQ");
- if (ret < 0)
- {
- GTP_ERROR("Failed to request GPIO:%d, ERRNO:%d", (s32)GTP_INT_PORT, ret);
- ret = -ENODEV;
- }
- else
- {
- GTP_GPIO_AS_INT(GTP_INT_PORT);
- ts->client->irq = GTP_INT_IRQ;
- }
-
- ret = GTP_GPIO_REQUEST(GTP_RST_PORT, "GTP_RST_PORT");
- if (ret < 0)
- {
- GTP_ERROR("Failed to request GPIO:%d, ERRNO:%d",(s32)GTP_RST_PORT,ret);
- ret = -ENODEV;
- }
-
- GTP_GPIO_AS_INPUT(GTP_RST_PORT);
- gtp_reset_guitar(ts->client, 20);
-
-
- if(ret < 0)
- {
- GTP_GPIO_FREE(GTP_RST_PORT);
- GTP_GPIO_FREE(GTP_INT_PORT);
- }
-
- return ret;
-}
-
-/*******************************************************
-Function:
- Request interrupt.
-Input:
- ts: private data.
-Output:
- Executive outcomes.
- 0: succeed, -1: failed.
-*******************************************************/
-static s8 gtp_request_irq(struct goodix_ts_data *ts)
-{
- s32 ret = -1;
- const u8 irq_table[] = GTP_IRQ_TAB;
-
- GTP_DEBUG("INT trigger type:%x", ts->int_trigger_type);
-
- ret = request_irq(ts->client->irq,
- goodix_ts_irq_handler,
- irq_table[ts->int_trigger_type],
- ts->client->name,
- ts);
- if (ret)
- {
- GTP_ERROR("Request IRQ failed!ERRNO:%d.", ret);
- GTP_GPIO_AS_INPUT(GTP_INT_PORT);
- GTP_GPIO_FREE(GTP_INT_PORT);
-
- hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- ts->timer.function = goodix_ts_timer_handler;
- hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
- return -1;
- }
- else
- {
- gtp_irq_disable(ts);
- ts->use_irq = 1;
- return 0;
- }
-}
-
-/*******************************************************
-Function:
- Request input device Function.
-Input:
- ts:private data.
-Output:
- Executive outcomes.
- 0: succeed, otherwise: failed.
-*******************************************************/
-static s8 gtp_request_input_dev(struct goodix_ts_data *ts)
-{
- s8 ret = -1;
- s8 phys[32];
-#if GTP_HAVE_TOUCH_KEY
- u8 index = 0;
-#endif
-
- GTP_DEBUG_FUNC();
-
- ts->input_dev = input_allocate_device();
- if (ts->input_dev == NULL)
- {
- GTP_ERROR("Failed to allocate input device.");
- return -ENOMEM;
- }
-
- ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;
-#if GTP_ICS_SLOT_REPORT
- __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
- input_mt_init_slots(ts->input_dev, 10); // in case of "out of memory"
-#else
- ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
-#endif
-
-#if GTP_HAVE_TOUCH_KEY
- for (index = 0; index < GTP_MAX_KEY_NUM; index++)
- {
- input_set_capability(ts->input_dev, EV_KEY, touch_key_array[index]);
- }
-#endif
-
-#if GTP_SLIDE_WAKEUP
- input_set_capability(ts->input_dev, EV_KEY, KEY_POWER);
-#endif
-
-#if GTP_WITH_PEN
- // pen support
- __set_bit(BTN_TOOL_PEN, ts->input_dev->keybit);
- __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
- __set_bit(INPUT_PROP_POINTER, ts->input_dev->propbit);
-#endif
-
-#if GTP_CHANGE_X2Y
- GTP_SWAP(ts->abs_x_max, ts->abs_y_max);
-#endif
-
- input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0);
- input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0);
- input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
- input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
- input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, 255, 0, 0);
-
- sprintf(phys, "input/ts");
- ts->input_dev->name = goodix_ts_name;
- ts->input_dev->phys = phys;
- ts->input_dev->id.bustype = BUS_I2C;
- ts->input_dev->id.vendor = 0xDEAD;
- ts->input_dev->id.product = 0xBEEF;
- ts->input_dev->id.version = 10427;
-
- ret = input_register_device(ts->input_dev);
- if (ret)
- {
- GTP_ERROR("Register %s input device failed", ts->input_dev->name);
- return -ENODEV;
- }
-
-#ifdef CONFIG_HAS_EARLYSUSPEND
- ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
- ts->early_suspend.suspend = goodix_ts_early_suspend;
- ts->early_suspend.resume = goodix_ts_late_resume;
- register_early_suspend(&ts->early_suspend);
-#endif
-
- return 0;
-}
-
-/*******************************************************
-Function:
- I2c probe.
-Input:
- client: i2c device struct.
- id: device id.
-Output:
- Executive outcomes.
- 0: succeed.
-*******************************************************/
-static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
-{
- s32 ret = -1;
- struct goodix_ts_data *ts;
- u16 version_info;
-
- GTP_DEBUG_FUNC();
-
- //do NOT remove these logs
- GTP_INFO("GTP Driver Version: %s", GTP_DRIVER_VERSION);
- GTP_INFO("GTP Driver Built@%s, %s", __TIME__, __DATE__);
- GTP_INFO("GTP I2C Address: 0x%02x", client->addr);
-
- i2c_connect_client = client;
-
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
- {
- GTP_ERROR("I2C check functionality failed.");
- return -ENODEV;
- }
- ts = kzalloc(sizeof(*ts), GFP_KERNEL);
- if (ts == NULL)
- {
- GTP_ERROR("Alloc GFP_KERNEL memory failed.");
- return -ENOMEM;
- }
-
- memset(ts, 0, sizeof(*ts));
- INIT_WORK(&ts->work, goodix_ts_work_func);
- ts->client = client;
- spin_lock_init(&ts->irq_lock); // 2.6.39 later
- // ts->irq_lock = SPIN_LOCK_UNLOCKED; // 2.6.39 & before
- i2c_set_clientdata(client, ts);
-
- ts->gtp_rawdiff_mode = 0;
-
- ret = gtp_request_io_port(ts);
- if (ret < 0)
- {
- GTP_ERROR("GTP request IO port failed.");
- kfree(ts);
- return ret;
- }
-
- ret = gtp_i2c_test(client);
- if (ret < 0)
- {
- GTP_ERROR("I2C communication ERROR!");
- }
-
-#if GTP_AUTO_UPDATE
- ret = gup_init_update_proc(ts);
- if (ret < 0)
- {
- GTP_ERROR("Create update thread error.");
- }
-#endif
-
- ret = gtp_init_panel(ts);
- if (ret < 0)
- {
- GTP_ERROR("GTP init panel failed.");
- ts->abs_x_max = GTP_MAX_WIDTH;
- ts->abs_y_max = GTP_MAX_HEIGHT;
- ts->int_trigger_type = GTP_INT_TRIGGER;
- }
-
- ret = gtp_request_input_dev(ts);
- if (ret < 0)
- {
- GTP_ERROR("GTP request input dev failed");
- }
-
- ret = gtp_request_irq(ts);
- if (ret < 0)
- {
- GTP_INFO("GTP works in polling mode.");
- }
- else
- {
- GTP_INFO("GTP works in interrupt mode.");
- }
-
- ret = gtp_read_version(client, &version_info);
- if (ret < 0)
- {
- GTP_ERROR("Read version failed.");
- }
- if (ts->use_irq)
- {
- gtp_irq_enable(ts);
- }
-
-#if GTP_CREATE_WR_NODE
- init_wr_node(client);
-#endif
-
-#if GTP_ESD_PROTECT
- gtp_esd_switch(client, SWITCH_ON);
-#endif
- return 0;
-}
-
-
-/*******************************************************
-Function:
- Goodix touchscreen driver release function.
-Input:
- client: i2c device struct.
-Output:
- Executive outcomes. 0---succeed.
-*******************************************************/
-static int goodix_ts_remove(struct i2c_client *client)
-{
- struct goodix_ts_data *ts = i2c_get_clientdata(client);
-
- GTP_DEBUG_FUNC();
-
-#ifdef CONFIG_HAS_EARLYSUSPEND
- unregister_early_suspend(&ts->early_suspend);
-#endif
-
-#if GTP_CREATE_WR_NODE
- uninit_wr_node();
-#endif
-
-#if GTP_ESD_PROTECT
- destroy_workqueue(gtp_esd_check_workqueue);
-#endif
-
- if (ts)
- {
- if (ts->use_irq)
- {
- GTP_GPIO_AS_INPUT(GTP_INT_PORT);
- GTP_GPIO_FREE(GTP_INT_PORT);
- free_irq(client->irq, ts);
- }
- else
- {
- hrtimer_cancel(&ts->timer);
- }
- }
-
- GTP_INFO("GTP driver removing...");
- i2c_set_clientdata(client, NULL);
- input_unregister_device(ts->input_dev);
- kfree(ts);
-
- return 0;
-}
-
-#ifdef CONFIG_HAS_EARLYSUSPEND
-/*******************************************************
-Function:
- Early suspend function.
-Input:
- h: early_suspend struct.
-Output:
- None.
-*******************************************************/
-static void goodix_ts_early_suspend(struct early_suspend *h)
-{
- struct goodix_ts_data *ts;
- s8 ret = -1;
- ts = container_of(h, struct goodix_ts_data, early_suspend);
-
- GTP_DEBUG_FUNC();
-
-#if GTP_ESD_PROTECT
- ts->gtp_is_suspend = 1;
- gtp_esd_switch(ts->client, SWITCH_OFF);
-#endif
-
-#if GTP_SLIDE_WAKEUP
- ret = gtp_enter_doze(ts);
-#else
- if (ts->use_irq)
- {
- gtp_irq_disable(ts);
- }
- else
- {
- hrtimer_cancel(&ts->timer);
- }
- ret = gtp_enter_sleep(ts);
-#endif
- if (ret < 0)
- {
- GTP_ERROR("GTP early suspend failed.");
- }
- // to avoid waking up while not sleeping
- // delay 48 + 10ms to ensure reliability
- msleep(58);
-}
-
-/*******************************************************
-Function:
- Late resume function.
-Input:
- h: early_suspend struct.
-Output:
- None.
-*******************************************************/
-static void goodix_ts_late_resume(struct early_suspend *h)
-{
- struct goodix_ts_data *ts;
- s8 ret = -1;
- ts = container_of(h, struct goodix_ts_data, early_suspend);
-
- GTP_DEBUG_FUNC();
-
- ret = gtp_wakeup_sleep(ts);
-
-#if GTP_SLIDE_WAKEUP
- doze_status = DOZE_DISABLED;
-#endif
-
- if (ret < 0)
- {
- GTP_ERROR("GTP later resume failed.");
- }
-
- if (ts->use_irq)
- {
- gtp_irq_enable(ts);
- }
- else
- {
- hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
- }
-
-#if GTP_ESD_PROTECT
- ts->gtp_is_suspend = 0;
- gtp_esd_switch(ts->client, SWITCH_ON);
-#endif
-}
-#endif
-
-#if GTP_ESD_PROTECT
-/*******************************************************
-Function:
- switch on & off esd delayed work
-Input:
- client: i2c device
- on: SWITCH_ON / SWITCH_OFF
-Output:
- void
-*********************************************************/
-void gtp_esd_switch(struct i2c_client *client, s32 on)
-{
- struct goodix_ts_data *ts;
-
- ts = i2c_get_clientdata(client);
- if (SWITCH_ON == on) // switch on esd
- {
- if (!ts->esd_running)
- {
- ts->esd_running = 1;
- GTP_INFO("Esd started");
- queue_delayed_work(gtp_esd_check_workqueue, &gtp_esd_check_work, GTP_ESD_CHECK_CIRCLE);
- }
- }
- else // switch off esd
- {
- if (ts->esd_running)
- {
- ts->esd_running = 0;
- GTP_INFO("Esd cancelled");
- cancel_delayed_work_sync(&gtp_esd_check_work);
- }
- }
-}
-
-/*******************************************************
-Function:
- Initialize external watchdog for esd protect
-Input:
- client: i2c device.
-Output:
- result of i2c write operation.
- 1: succeed, otherwise: failed
-*********************************************************/
-static s32 gtp_init_ext_watchdog(struct i2c_client *client)
-{
- u8 opr_buffer[4] = {0x80, 0x40, 0xAA, 0xAA};
-
- struct i2c_msg msg; // in case of recursively reset by calling gtp_i2c_write
- s32 ret = -1;
- s32 retries = 0;
-
- GTP_DEBUG("Init external watchdog...");
- GTP_DEBUG_FUNC();
-
- msg.flags = !I2C_M_RD;
- msg.addr = client->addr;
- msg.len = 4;
- msg.buf = opr_buffer;
-
- while(retries < 5)
- {
- ret = i2c_transfer(client->adapter, &msg, 1);
- if (ret == 1)
- {
- return 1;
- }
- retries++;
- }
- if (retries >= 5)
- {
- GTP_ERROR("init external watchdog failed!");
- }
- return 0;
-}
-
-/*******************************************************
-Function:
- Esd protect function.
- Added external watchdog by meta, 2013/03/07
-Input:
- work: delayed work
-Output:
- None.
-*******************************************************/
-static void gtp_esd_check_func(struct work_struct *work)
-{
- s32 i;
- s32 ret = -1;
- struct goodix_ts_data *ts = NULL;
- u8 test[4] = {0x80, 0x40};
-
- GTP_DEBUG_FUNC();
-
- ts = i2c_get_clientdata(i2c_connect_client);
-
- if (ts->gtp_is_suspend)
- {
- ts->esd_running = 0;
- GTP_INFO("Esd terminated!");
- return;
- }
-
- for (i = 0; i < 3; i++)
- {
- ret = gtp_i2c_read(ts->client, test, 4);
-
- GTP_DEBUG("0x8040 = 0x%02X, 0x8041 = 0x%02X", test[2], test[3]);
- if ((ret < 0))
- {
- // IIC communication problem
- continue;
- }
- else
- {
- if ((test[2] == 0xAA) || (test[3] != 0xAA))
- {
- // IC works abnormally..
- i = 3;
- break;
- }
- else
- {
- // IC works normally, Write 0x8040 0xAA, feed the dog
- test[2] = 0xAA;
- gtp_i2c_write(ts->client, test, 3);
- break;
- }
- }
- }
- if (i >= 3)
- {
- GTP_ERROR("IC Working ABNORMALLY, Resetting Guitar...");
- gtp_reset_guitar(ts->client, 50);
- }
-
- if(!ts->gtp_is_suspend)
- {
- queue_delayed_work(gtp_esd_check_workqueue, &gtp_esd_check_work, GTP_ESD_CHECK_CIRCLE);
- }
- else
- {
- GTP_INFO("Esd terminated!");
- ts->esd_running = 0;
- }
- return;
-}
-#endif
-
-static const struct i2c_device_id goodix_ts_id[] = {
- { GTP_I2C_NAME, 0 },
- { }
-};
-
-static struct i2c_driver goodix_ts_driver = {
- .probe = goodix_ts_probe,
- .remove = goodix_ts_remove,
-#ifndef CONFIG_HAS_EARLYSUSPEND
- .suspend = goodix_ts_early_suspend,
- .resume = goodix_ts_late_resume,
-#endif
- .id_table = goodix_ts_id,
- .driver = {
- .name = GTP_I2C_NAME,
- .owner = THIS_MODULE,
- },
-};
-
-/*******************************************************
-Function:
- Driver Install function.
-Input:
- None.
-Output:
- Executive Outcomes. 0---succeed.
-********************************************************/
-static int __devinit goodix_ts_init(void)
-{
- s32 ret;
-
- GTP_DEBUG_FUNC();
- GTP_INFO("GTP driver installing...");
- goodix_wq = create_singlethread_workqueue("goodix_wq");
- if (!goodix_wq)
- {
- GTP_ERROR("Creat workqueue failed.");
- return -ENOMEM;
- }
-#if GTP_ESD_PROTECT
- INIT_DELAYED_WORK(&gtp_esd_check_work, gtp_esd_check_func);
- gtp_esd_check_workqueue = create_workqueue("gtp_esd_check");
-#endif
- ret = i2c_add_driver(&goodix_ts_driver);
- return ret;
-}
-
-/*******************************************************
-Function:
- Driver uninstall function.
-Input:
- None.
-Output:
- Executive Outcomes. 0---succeed.
-********************************************************/
-static void __exit goodix_ts_exit(void)
-{
- GTP_DEBUG_FUNC();
- GTP_INFO("GTP driver exited.");
- i2c_del_driver(&goodix_ts_driver);
- if (goodix_wq)
- {
- destroy_workqueue(goodix_wq);
- }
-}
-
-late_initcall(goodix_ts_init);
-module_exit(goodix_ts_exit);
-
-MODULE_DESCRIPTION("GTP Series Driver");
-MODULE_LICENSE("GPL");
diff --git a/input/touchscreen/gt9xx/gt9xx.h b/input/touchscreen/gt9xx/gt9xx.h
deleted file mode 100644
index e375af530d57..000000000000
--- a/input/touchscreen/gt9xx/gt9xx.h
+++ /dev/null
@@ -1,241 +0,0 @@
-/* drivers/input/touchscreen/gt9xx.h
- *
- * 2010 - 2013 Goodix Technology.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be a reference
- * to you, when you are integrating the GOODiX's CTP IC into your system,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- */
-
-#ifndef _GOODIX_GT9XX_H_
-#define _GOODIX_GT9XX_H_
-
-#include <linux/kernel.h>
-#include <linux/hrtimer.h>
-#include <linux/i2c.h>
-#include <linux/input.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/proc_fs.h>
-#include <linux/string.h>
-#include <asm/uaccess.h>
-#include <linux/vmalloc.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <mach/gpio.h>
-#include <linux/earlysuspend.h>
-
-struct goodix_ts_data {
- spinlock_t irq_lock;
- struct i2c_client *client;
- struct input_dev *input_dev;
- struct hrtimer timer;
- struct work_struct work;
- struct early_suspend early_suspend;
- s32 irq_is_disable;
- s32 use_irq;
- u16 abs_x_max;
- u16 abs_y_max;
- u8 max_touch_num;
- u8 int_trigger_type;
- u8 green_wake_mode;
- u8 chip_type;
- u8 enter_update;
- u8 gtp_is_suspend;
- u8 gtp_rawdiff_mode;
- u8 gtp_cfg_len;
- u8 fixed_cfg;
- u8 esd_running;
- u8 fw_error;
-};
-
-extern u16 show_len;
-extern u16 total_len;
-
-//***************************PART1:ON/OFF define*******************************
-#define GTP_CUSTOM_CFG 0
-#define GTP_CHANGE_X2Y 0
-#define GTP_DRIVER_SEND_CFG 1
-#define GTP_HAVE_TOUCH_KEY 0
-#define GTP_POWER_CTRL_SLEEP 0
-#define GTP_ICS_SLOT_REPORT 0
-
-#define GTP_AUTO_UPDATE 1 // auto updated by .bin file as default
-#define GTP_HEADER_FW_UPDATE 0 // auto updated by head_fw_array in gt9xx_firmware.h, function together with GTP_AUTO_UPDATE
-
-#define GTP_CREATE_WR_NODE 1
-#define GTP_ESD_PROTECT 0
-#define GTP_WITH_PEN 0
-
-#define GTP_SLIDE_WAKEUP 0
-#define GTP_DBL_CLK_WAKEUP 0 // double-click wakeup, function together with GTP_SLIDE_WAKEUP
-
-#define GTP_DEBUG_ON 1
-#define GTP_DEBUG_ARRAY_ON 0
-#define GTP_DEBUG_FUNC_ON 0
-
-//*************************** PART2:TODO define **********************************
-// STEP_1(REQUIRED): Define Configuration Information Group(s)
-// Sensor_ID Map:
-/* sensor_opt1 sensor_opt2 Sensor_ID
- GND GND 0
- VDDIO GND 1
- NC GND 2
- GND NC/300K 3
- VDDIO NC/300K 4
- NC NC/300K 5
-*/
-// TODO: define your own default or for Sensor_ID == 0 config here.
-// The predefined one is just a sample config, which is not suitable for your tp in most cases.
-#define CTP_CFG_GROUP1 {\
- 0x41,0x1C,0x02,0xC0,0x03,0x0A,0x05,0x01,0x01,0x0F,\
- 0x23,0x0F,0x5F,0x41,0x03,0x05,0x00,0x00,0x00,0x00,\
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x91,0x00,0x0A,\
- 0x28,0x00,0xB8,0x0B,0x00,0x00,0x00,0x9A,0x03,0x25,\
- 0x00,0x00,0x00,0x00,0x00,0x03,0x64,0x32,0x00,0x00,\
- 0x00,0x32,0x8C,0x94,0x05,0x01,0x05,0x00,0x00,0x96,\
- 0x0C,0x22,0xD8,0x0E,0x23,0x56,0x11,0x25,0xFF,0x13,\
- 0x28,0xA7,0x15,0x2E,0x00,0x00,0x10,0x30,0x48,0x00,\
- 0x56,0x4A,0x3A,0xFF,0xFF,0x16,0x00,0x00,0x00,0x00,\
- 0x00,0x01,0x1B,0x14,0x0D,0x19,0x00,0x00,0x01,0x00,\
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
- 0x00,0x00,0x1A,0x18,0x16,0x14,0x12,0x10,0x0E,0x0C,\
- 0x0A,0x08,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,\
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,\
- 0xFF,0xFF,0x1D,0x1E,0x1F,0x20,0x22,0x24,0x28,0x29,\
- 0x0C,0x0A,0x08,0x00,0x02,0x04,0x05,0x06,0x0E,0xFF,\
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,\
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,\
- 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,\
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
- 0x00,0x00,0x00,0x00,0x00,0x00,0x91,0x01\
- }
-
-// TODO: define your config for Sensor_ID == 1 here, if needed
-#define CTP_CFG_GROUP2 {\
- }
-// TODO: define your config for Sensor_ID == 2 here, if needed
-#define CTP_CFG_GROUP3 {\
- }
-
-// TODO: define your config for Sensor_ID == 3 here, if needed
-#define CTP_CFG_GROUP4 {\
- }
-
-// TODO: define your config for Sensor_ID == 4 here, if needed
-#define CTP_CFG_GROUP5 {\
- }
-
-// TODO: define your config for Sensor_ID == 5 here, if needed
-#define CTP_CFG_GROUP6 {\
- }
-
-// STEP_2(REQUIRED): Customize your I/O ports & I/O operations
-#define GTP_RST_PORT S5PV210_GPJ3(6)
-#define GTP_INT_PORT S5PV210_GPH1(3)
-#define GTP_INT_IRQ gpio_to_irq(GTP_INT_PORT)
-#define GTP_INT_CFG S3C_GPIO_SFN(0xF)
-
-#define GTP_GPIO_AS_INPUT(pin) do{\
- gpio_direction_input(pin);\
- s3c_gpio_setpull(pin, S3C_GPIO_PULL_NONE);\
- }while(0)
-#define GTP_GPIO_AS_INT(pin) do{\
- GTP_GPIO_AS_INPUT(pin);\
- s3c_gpio_cfgpin(pin, GTP_INT_CFG);\
- }while(0)
-#define GTP_GPIO_GET_VALUE(pin) gpio_get_value(pin)
-#define GTP_GPIO_OUTPUT(pin,level) gpio_direction_output(pin,level)
-#define GTP_GPIO_REQUEST(pin, label) gpio_request(pin, label)
-#define GTP_GPIO_FREE(pin) gpio_free(pin)
-#define GTP_IRQ_TAB {IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_LOW, IRQ_TYPE_LEVEL_HIGH}
-
-// STEP_3(optional): Specify your special config info if needed
-#if GTP_CUSTOM_CFG
- #define GTP_MAX_HEIGHT 800
- #define GTP_MAX_WIDTH 480
- #define GTP_INT_TRIGGER 0 // 0: Rising 1: Falling
-#else
- #define GTP_MAX_HEIGHT 4096
- #define GTP_MAX_WIDTH 4096
- #define GTP_INT_TRIGGER 1
-#endif
-#define GTP_MAX_TOUCH 5
-#define GTP_ESD_CHECK_CIRCLE 2000 // jiffy: ms
-
-// STEP_4(optional): If keys are available and reported as keys, config your key info here
-#if GTP_HAVE_TOUCH_KEY
- #define GTP_KEY_TAB {KEY_MENU, KEY_HOME, KEY_BACK}
-#endif
-
-//***************************PART3:OTHER define*********************************
-#define GTP_DRIVER_VERSION "V1.8<2013/06/08>"
-#define GTP_I2C_NAME "Goodix-TS"
-#define GTP_POLL_TIME 10 // jiffy: ms
-#define GTP_ADDR_LENGTH 2
-#define GTP_CONFIG_MIN_LENGTH 186
-#define GTP_CONFIG_MAX_LENGTH 240
-#define FAIL 0
-#define SUCCESS 1
-#define SWITCH_OFF 0
-#define SWITCH_ON 1
-
-// Registers define
-#define GTP_READ_COOR_ADDR 0x814E
-#define GTP_REG_SLEEP 0x8040
-#define GTP_REG_SENSOR_ID 0x814A
-#define GTP_REG_CONFIG_DATA 0x8047
-#define GTP_REG_VERSION 0x8140
-
-#define RESOLUTION_LOC 3
-#define TRIGGER_LOC 8
-
-#define CFG_GROUP_LEN(p_cfg_grp) (sizeof(p_cfg_grp) / sizeof(p_cfg_grp[0]))
-// Log define
-#define GTP_INFO(fmt,arg...) printk("<<-GTP-INFO->> "fmt"\n",##arg)
-#define GTP_ERROR(fmt,arg...) printk("<<-GTP-ERROR->> "fmt"\n",##arg)
-#define GTP_DEBUG(fmt,arg...) do{\
- if(GTP_DEBUG_ON)\
- printk("<<-GTP-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
- }while(0)
-#define GTP_DEBUG_ARRAY(array, num) do{\
- s32 i;\
- u8* a = array;\
- if(GTP_DEBUG_ARRAY_ON)\
- {\
- printk("<<-GTP-DEBUG-ARRAY->>\n");\
- for (i = 0; i < (num); i++)\
- {\
- printk("%02x ", (a)[i]);\
- if ((i + 1 ) %10 == 0)\
- {\
- printk("\n");\
- }\
- }\
- printk("\n");\
- }\
- }while(0)
-#define GTP_DEBUG_FUNC() do{\
- if(GTP_DEBUG_FUNC_ON)\
- printk("<<-GTP-FUNC->> Func:%s@Line:%d\n",__func__,__LINE__);\
- }while(0)
-#define GTP_SWAP(x, y) do{\
- typeof(x) z = x;\
- x = y;\
- y = z;\
- }while (0)
-
-//*****************************End of Part III********************************
-
-#endif /* _GOODIX_GT9XX_H_ */
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 4504ca66118d..50da680c479f 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -166,7 +166,7 @@ static u64 bpf_get_current_comm(u64 r1, u64 size, u64 r3, u64 r4, u64 r5)
if (!task)
return -EINVAL;
- memcpy(buf, task->comm, min_t(size_t, size, sizeof(task->comm)));
+ strlcpy(buf, task->comm, min_t(size_t, size, sizeof(task->comm)));
return 0;
}
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index d5512e1e7138..e8d71110ed2a 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -2498,6 +2498,14 @@ static void cgroup_migrate_add_src(struct css_set *src_cset,
lockdep_assert_held(&cgroup_mutex);
lockdep_assert_held(&css_set_lock);
+ /*
+ * If ->dead, @src_set is associated with one or more dead cgroups
+ * and doesn't contain any migratable tasks. Ignore it early so
+ * that the rest of migration path doesn't get confused by it.
+ */
+ if (src_cset->dead)
+ return;
+
src_cgrp = cset_cgroup_from_root(src_cset, dst_cgrp->root);
if (!list_empty(&src_cset->mg_preload_node))
@@ -5186,6 +5194,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
__releases(&cgroup_mutex) __acquires(&cgroup_mutex)
{
struct cgroup_subsys_state *css;
+ struct cgrp_cset_link *link;
int ssid;
lockdep_assert_held(&cgroup_mutex);
@@ -5206,11 +5215,18 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
return -EBUSY;
/*
- * Mark @cgrp dead. This prevents further task migration and child
- * creation by disabling cgroup_lock_live_group().
+ * Mark @cgrp and the associated csets dead. The former prevents
+ * further task migration and child creation by disabling
+ * cgroup_lock_live_group(). The latter makes the csets ignored by
+ * the migration path.
*/
cgrp->self.flags &= ~CSS_ONLINE;
+ spin_lock_bh(&css_set_lock);
+ list_for_each_entry(link, &cgrp->cset_links, cset_link)
+ link->cset->dead = true;
+ spin_unlock_bh(&css_set_lock);
+
/* initiate massacre of all css's */
for_each_css(css, ssid, cgrp)
kill_css(css);
diff --git a/kernel/drivers/input/touchscreen/synaptics_fw_update.c b/kernel/drivers/input/touchscreen/synaptics_fw_update.c
index 4867d1f73c4d..8b6d7c7e368d 100644
--- a/kernel/drivers/input/touchscreen/synaptics_fw_update.c
+++ b/kernel/drivers/input/touchscreen/synaptics_fw_update.c
@@ -30,7 +30,10 @@
#define DEBUG_FW_UPDATE
#define SHOW_PROGRESS
-#define FW_IMAGE_NAME "PR12345678.img"
+#define FW_IMAGE_NAME "PR1063486-s7301_00000000.img"
+#define MAX_FIRMWARE_ID_LEN 10
+#define FORCE_UPDATE false
+#define INSIDE_FIRMWARE_UPDATE
#define CHECKSUM_OFFSET 0x00
#define BOOTLOADER_VERSION_OFFSET 0x07
@@ -73,6 +76,12 @@ enum flash_command {
CMD_ENABLE_FLASH_PROG = 0xF,
};
+enum flash_area {
+ NONE,
+ UI_FIRMWARE,
+ CONFIG_AREA
+};
+
#define SLEEP_MODE_NORMAL (0x00)
#define SLEEP_MODE_SENSOR_SLEEP (0x01)
#define SLEEP_MODE_RESERVED0 (0x02)
@@ -81,9 +90,9 @@ enum flash_command {
#define ENABLE_WAIT_MS (1 * 1000)
#define WRITE_WAIT_MS (3 * 1000)
#define ERASE_WAIT_MS (5 * 1000)
+#define RESET_WAIT_MS (500)
-#define MIN_SLEEP_TIME_US 50
-#define MAX_SLEEP_TIME_US 100
+#define SLEEP_TIME_US 50
static ssize_t fwu_sysfs_show_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
@@ -204,6 +213,7 @@ struct f34_flash_properties {
struct synaptics_rmi4_fwu_handle {
bool initialized;
+ bool force_update;
char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
unsigned int image_size;
unsigned int data_pos;
@@ -231,6 +241,8 @@ struct synaptics_rmi4_fwu_handle {
struct synaptics_rmi4_data *rmi4_data;
struct f34_flash_control flash_control;
struct f34_flash_properties flash_properties;
+ struct workqueue_struct *fwu_workqueue;
+ struct delayed_work fwu_work;
};
static struct bin_attribute dev_attr_data = {
@@ -313,53 +325,6 @@ static void parse_header(struct image_header *header,
return;
}
-static int fwu_check_version(void)
-{
- int retval;
- unsigned char firmware_id[4];
- unsigned char config_id[4];
- struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client;
-
- /* device firmware id */
- retval = fwu->fn_ptr->read(fwu->rmi4_data,
- fwu->f01_fd.query_base_addr + 18,
- firmware_id,
- sizeof(firmware_id));
- if (retval < 0) {
- dev_err(&i2c_client->dev,
- "Failed to read firmware ID (code %d).\n", retval);
- return retval;
- }
- firmware_id[3] = 0;
-
- dev_info(&i2c_client->dev, "Device firmware ID%d\n",
- extract_uint(firmware_id));
-
- /* device config id */
- retval = fwu->fn_ptr->read(fwu->rmi4_data,
- fwu->f34_fd.ctrl_base_addr,
- config_id,
- sizeof(config_id));
- if (retval < 0) {
- dev_err(&i2c_client->dev,
- "Failed to read config ID (code %d).\n", retval);
- return retval;
- }
-
- dev_info(&i2c_client->dev,
- "Device config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
- config_id[0], config_id[1], config_id[2], config_id[3]);
-
- /* .img config id */
- dev_info(&i2c_client->dev,
- ".img config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
- fwu->config_data[0],
- fwu->config_data[1],
- fwu->config_data[2],
- fwu->config_data[3]);
- return 0;
-}
-
static int fwu_read_f01_device_status(struct f01_device_status *status)
{
int retval;
@@ -407,7 +372,7 @@ static int fwu_read_f34_queries(void)
return retval;
}
- dev_info(&i2c_client->dev, "%s perm:%d, bl%d, display:%d\n",
+ dev_info(&i2c_client->dev, "%s perm:%d, bl:%d, display:%d\n",
__func__,
fwu->flash_properties.has_perm_config,
fwu->flash_properties.has_bl_config,
@@ -506,25 +471,13 @@ static int fwu_read_f34_flash_status(void)
static int fwu_reset_device(void)
{
int retval;
- unsigned char reset = 0x01;
#ifdef DEBUG_FW_UPDATE
- dev_info(&fwu->rmi4_data->i2c_client->dev, "Reset device\n");
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Reset device\n",
+ __func__);
#endif
- retval = fwu->fn_ptr->write(fwu->rmi4_data,
- fwu->f01_fd.cmd_base_addr,
- &reset,
- sizeof(reset));
- if (retval < 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
- "%s: Failed to reset device (addr : 0x%02x)\n",
- __func__, fwu->f01_fd.cmd_base_addr);
- return retval;
- }
-
- fwu_wait_for_idle(WRITE_WAIT_MS);
-
retval = fwu->rmi4_data->reset_device(fwu->rmi4_data);
if (retval < 0) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
@@ -539,37 +492,34 @@ static int fwu_write_f34_command(unsigned char cmd)
{
int retval;
+ fwu->flash_control.data[0] = cmd;
retval = fwu->fn_ptr->write(fwu->rmi4_data,
fwu->addr_f34_flash_control,
- &cmd,
- sizeof(cmd));
+ fwu->flash_control.data,
+ sizeof(fwu->flash_control.data));
if (retval < 0) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"%s: Failed to write command 0x%02x\n",
- __func__, cmd);
+ __func__, fwu->flash_control.data[0]);
return retval;
}
return 0;
}
-static unsigned char fwu_check_flash_status(void)
-{
- fwu_read_f34_flash_status();
- return fwu->flash_control.status;
-}
-
static int fwu_wait_for_idle(int timeout_ms)
{
int count = 0;
- int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1;
-
+ int timeout_count = ((timeout_ms * 1000) / SLEEP_TIME_US) + 1;
do {
- if (fwu_read_interrupt_status() > 0)
+ if (fwu->flash_control.command == 0x00)
return 0;
- usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US);
- count++;
- } while (count < timeout_count);
+ usleep_range(SLEEP_TIME_US, SLEEP_TIME_US + 100);
+ } while (count++ < timeout_count);
+
+ fwu_read_f34_flash_status();
+ if (fwu->flash_control.command == 0x00)
+ return 0;
dev_err(&fwu->rmi4_data->i2c_client->dev,
"%s: Timed out waiting for idle status\n",
@@ -578,6 +528,133 @@ static int fwu_wait_for_idle(int timeout_ms)
return -ETIMEDOUT;
}
+static enum flash_area fwu_go_nogo(void)
+{
+ int retval = 0;
+ int index = 0;
+ int deviceFirmwareID;
+ int imageConfigID;
+ int deviceConfigID;
+ unsigned long imageFirmwareID;
+ unsigned char firmware_id[4];
+ unsigned char config_id[4];
+ char *strptr;
+ char *imagePR = kzalloc(sizeof(MAX_FIRMWARE_ID_LEN), GFP_KERNEL);
+ enum flash_area flash_area = NONE;
+ struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client;
+ struct f01_device_status f01_device_status;
+
+ if (fwu->force_update) {
+ flash_area = UI_FIRMWARE;
+ goto exit;
+ }
+
+ retval = fwu_read_f01_device_status(&f01_device_status);
+ if (retval < 0) {
+ flash_area = NONE;
+ goto exit;
+ }
+
+ imagePR = kzalloc(sizeof(MAX_FIRMWARE_ID_LEN), GFP_KERNEL);
+
+ /* Force update firmware when device is in bootloader mode */
+ if (f01_device_status.flash_prog) {
+ dev_info(&i2c_client->dev,
+ "%s: In flash prog mode\n",
+ __func__);
+ flash_area = UI_FIRMWARE;
+ goto exit;
+ }
+
+
+ /* device firmware id */
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f01_fd.query_base_addr + 18,
+ firmware_id,
+ sizeof(firmware_id));
+ if (retval < 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to read firmware ID (code %d).\n", retval);
+ goto exit;
+ }
+ firmware_id[3] = 0;
+ deviceFirmwareID = extract_uint(firmware_id);
+
+ /* .img firmware id */
+ strptr = strstr(FW_IMAGE_NAME, "PR");
+ if (!strptr) {
+ dev_err(&i2c_client->dev,
+ "No valid PR number (PRxxxxxxx)" \
+ "found in image file name...\n");
+ goto exit;
+ }
+
+ strptr += 2;
+ while (strptr[index] >= '0' && strptr[index] <= '9') {
+ imagePR[index] = strptr[index];
+ index++;
+ }
+ imagePR[index] = 0;
+
+ retval = sstrtoul(imagePR, 10, &imageFirmwareID);
+ if (retval == -EINVAL) {
+ dev_err(&i2c_client->dev,
+ "invalid image firmware id...\n");
+ goto exit;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Device firmware id %d, .img firmware id %d\n",
+ deviceFirmwareID,
+ (unsigned int)imageFirmwareID);
+ if (imageFirmwareID > deviceFirmwareID) {
+ flash_area = UI_FIRMWARE;
+ goto exit;
+ }
+
+ /* device config id */
+ retval = fwu->fn_ptr->read(fwu->rmi4_data,
+ fwu->f34_fd.ctrl_base_addr,
+ config_id,
+ sizeof(config_id));
+ if (retval < 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to read config ID (code %d).\n", retval);
+ flash_area = NONE;
+ goto exit;
+ }
+ deviceConfigID = extract_uint(config_id);
+
+ dev_info(&i2c_client->dev,
+ "Device config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
+ config_id[0], config_id[1], config_id[2], config_id[3]);
+
+ /* .img config id */
+ dev_info(&i2c_client->dev,
+ ".img config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
+ fwu->config_data[0],
+ fwu->config_data[1],
+ fwu->config_data[2],
+ fwu->config_data[3]);
+ imageConfigID = extract_uint(fwu->config_data);
+
+ if (imageConfigID > deviceConfigID) {
+ flash_area = CONFIG_AREA;
+ goto exit;
+ }
+
+exit:
+ kfree(imagePR);
+ if (flash_area == NONE)
+ dev_info(&i2c_client->dev,
+ "Nothing needs to be updated\n");
+ else
+ dev_info(&i2c_client->dev,
+ "Update %s block\n",
+ flash_area == UI_FIRMWARE ? "UI FW" : "CONFIG");
+ return flash_area;
+}
+
static int fwu_scan_pdt(void)
{
int retval;
@@ -649,16 +726,25 @@ static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt,
int retval;
unsigned char block_offset[] = {0, 0};
unsigned short block_num;
+ struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client;
#ifdef SHOW_PROGRESS
unsigned int progress = (command == CMD_WRITE_CONFIG_BLOCK) ?
10 : 100;
#endif
+
+#ifdef DEBUG_FW_UPDATE
+ dev_info(&i2c_client->dev,
+ "%s: Start to update %s blocks\n",
+ __func__,
+ command == CMD_WRITE_CONFIG_BLOCK ?
+ "config" : "firmware");
+#endif
retval = fwu->fn_ptr->write(fwu->rmi4_data,
fwu->f34_fd.data_base_addr + BLOCK_NUMBER_OFFSET,
block_offset,
sizeof(block_offset));
if (retval < 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
+ dev_err(&i2c_client->dev,
"%s: Failed to write to block number registers\n",
__func__);
return retval;
@@ -667,20 +753,19 @@ static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt,
for (block_num = 0; block_num < block_cnt; block_num++) {
#ifdef SHOW_PROGRESS
if (block_num % progress == 0)
- dev_info(&fwu->rmi4_data->i2c_client->dev,
- "%s: update %s %3d / %3d\n",
- __func__,
- command == CMD_WRITE_CONFIG_BLOCK ?
- "config" : "firmware",
- block_num,
- block_cnt);
+ dev_info(&i2c_client->dev,
+ "%s: update %s %3d / %3d\n",
+ __func__,
+ command == CMD_WRITE_CONFIG_BLOCK ?
+ "config" : "firmware",
+ block_num, block_cnt);
#endif
retval = fwu->fn_ptr->write(fwu->rmi4_data,
fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET,
block_ptr,
fwu->block_size);
if (retval < 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
+ dev_err(&i2c_client->dev,
"%s: Failed to write block data (block %d)\n",
__func__, block_num);
return retval;
@@ -688,7 +773,7 @@ static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt,
retval = fwu_write_f34_command(command);
if (retval < 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
+ dev_err(&i2c_client->dev,
"%s: Failed to write command for block %d\n",
__func__, block_num);
return retval;
@@ -696,30 +781,28 @@ static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt,
retval = fwu_wait_for_idle(WRITE_WAIT_MS);
if (retval < 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
- "%s: Failed to wait for idle status \
- (block %d)\n",
- __func__, block_num);
+ dev_err(&i2c_client->dev,
+ "%s: Failed to wait for idle status (block %d)\n",
+ __func__, block_num);
return retval;
}
- retval = fwu_check_flash_status();
- if (retval != 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
- "%s: Flash block %d status %d\n",
+ if (fwu->flash_control.status != 0x00) {
+ dev_err(&i2c_client->dev,
+ "%s: Flash block %d failed, status 0x%02X\n",
__func__, block_num, retval);
return -1;
}
+
block_ptr += fwu->block_size;
}
#ifdef SHOW_PROGRESS
- dev_info(&fwu->rmi4_data->i2c_client->dev,
- "%s: update %s %3d / %3d\n",
- __func__,
- command == CMD_WRITE_CONFIG_BLOCK ?
- "config" : "firmware",
- block_cnt,
- block_cnt);
+ dev_info(&i2c_client->dev,
+ "%s: update %s %3d / %3d\n",
+ __func__,
+ command == CMD_WRITE_CONFIG_BLOCK ?
+ "config" : "firmware",
+ block_cnt, block_cnt);
#endif
return 0;
}
@@ -741,7 +824,10 @@ static int fwu_write_bootloader_id(void)
int retval;
#ifdef DEBUG_FW_UPDATE
- dev_info(&fwu->rmi4_data->i2c_client->dev, "Write bootloader ID\n");
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "Write bootloader ID 0x%02X 0x%02X\n",
+ fwu->bootloader_id[0],
+ fwu->bootloader_id[1]);
#endif
retval = fwu->fn_ptr->write(fwu->rmi4_data,
fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET,
@@ -789,17 +875,6 @@ static int fwu_enter_flash_prog(void)
if (retval < 0)
return retval;
- retval = fwu_read_f01_device_status(&f01_device_status);
- if (retval < 0)
- return retval;
-
- if (!f01_device_status.flash_prog) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
- "%s: Program enabled bit not set\n",
- __func__);
- return -EINVAL;
- }
-
retval = fwu_scan_pdt();
if (retval < 0)
return retval;
@@ -879,9 +954,12 @@ static int fwu_do_reflash(void)
if (retval < 0)
return retval;
- dev_dbg(&fwu->rmi4_data->i2c_client->dev,
- "%s: Idle status detected\n",
- __func__);
+ if (fwu->flash_control.status != 0x00) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Erase all command failed, status 0x%02X\n",
+ __func__, retval);
+ return -1;
+ }
if (fwu->firmware_data) {
retval = fwu_write_firmware();
@@ -900,90 +978,6 @@ static int fwu_do_reflash(void)
return retval;
}
-static int fwu_start_reflash(void)
-{
- int retval;
- struct image_header header;
- const unsigned char *fw_image;
- const struct firmware *fw_entry = NULL;
- struct f01_device_status f01_device_status;
-
- pr_notice("%s: Start of reflash process\n", __func__);
-
- if (fwu->ext_data_source)
- fw_image = fwu->ext_data_source;
- else {
- dev_dbg(&fwu->rmi4_data->i2c_client->dev,
- "%s: Requesting firmware image %s\n",
- __func__, FW_IMAGE_NAME);
-
- retval = request_firmware(&fw_entry, FW_IMAGE_NAME,
- &fwu->rmi4_data->i2c_client->dev);
- if (retval != 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
- "%s: Firmware image %s not available\n",
- __func__, FW_IMAGE_NAME);
- retval = -EINVAL;
- goto exit;
- }
-
- dev_dbg(&fwu->rmi4_data->i2c_client->dev,
- "%s: Firmware image size = %d\n",
- __func__, fw_entry->size);
-
- fw_image = fw_entry->data;
- }
-
- parse_header(&header, fw_image);
-
- if (header.image_size)
- fwu->firmware_data = fw_image + FW_IMAGE_OFFSET;
- if (header.config_size) {
- fwu->config_data = fw_image + FW_IMAGE_OFFSET +
- header.image_size;
- }
-
- fwu->fn_ptr->enable(fwu->rmi4_data, false);
-
- fwu_check_version();
-
- retval = fwu_do_reflash();
- if (retval < 0) {
- dev_err(&fwu->rmi4_data->i2c_client->dev,
- "%s: Failed to do reflash\n",
- __func__);
- }
-
- /* reset device */
- fwu_reset_device();
-
- /* check device status */
- retval = fwu_read_f01_device_status(&f01_device_status);
- if (retval < 0)
- goto exit;
-
- dev_info(&fwu->rmi4_data->i2c_client->dev, "Device is in %s mode\n",
- f01_device_status.flash_prog == 1 ? "bootloader" : "UI");
- if (f01_device_status.flash_prog)
- dev_info(&fwu->rmi4_data->i2c_client->dev, "Flash status %d\n",
- f01_device_status.status_code);
-
- if (f01_device_status.flash_prog) {
- dev_info(&fwu->rmi4_data->i2c_client->dev,
- "%s: Device is in flash prog mode 0x%02X\n",
- __func__, f01_device_status.status_code);
- retval = 0;
- goto exit;
- }
- fwu->fn_ptr->enable(fwu->rmi4_data, true);
- if (fw_entry)
- release_firmware(fw_entry);
-
- pr_notice("%s: End of reflash process\n", __func__);
-exit:
- return retval;
-}
-
static int fwu_do_write_config(void)
{
int retval;
@@ -1205,6 +1199,110 @@ exit:
return retval;
}
+static int fwu_start_reflash(void)
+{
+ int retval;
+ struct image_header header;
+ const unsigned char *fw_image;
+ const struct firmware *fw_entry = NULL;
+ struct f01_device_status f01_device_status;
+ enum flash_area flash_area;
+
+ pr_notice("%s: Start of reflash process\n", __func__);
+
+ if (fwu->ext_data_source)
+ fw_image = fwu->ext_data_source;
+ else {
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Requesting firmware image %s\n",
+ __func__, FW_IMAGE_NAME);
+
+ retval = request_firmware(&fw_entry, FW_IMAGE_NAME,
+ &fwu->rmi4_data->i2c_client->dev);
+ if (retval != 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Firmware image %s not available\n",
+ __func__, FW_IMAGE_NAME);
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Firmware image size = %d\n",
+ __func__, fw_entry->size);
+
+ fw_image = fw_entry->data;
+ }
+
+ parse_header(&header, fw_image);
+
+ if (header.image_size)
+ fwu->firmware_data = fw_image + FW_IMAGE_OFFSET;
+ if (header.config_size) {
+ fwu->config_data = fw_image + FW_IMAGE_OFFSET +
+ header.image_size;
+ }
+
+ if (fwu->ext_data_source)
+ flash_area = UI_FIRMWARE;
+ else
+ flash_area = fwu_go_nogo();
+
+ switch (flash_area) {
+ case NONE:
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "%s: No need to do reflash.\n",
+ __func__);
+ goto exit;
+ case UI_FIRMWARE:
+ retval = fwu_do_reflash();
+ break;
+ case CONFIG_AREA:
+ retval = fwu_do_write_config();
+ break;
+ default:
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Unknown flash area\n",
+ __func__);
+ goto exit;
+ }
+
+ if (retval < 0) {
+ dev_err(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Failed to do reflash\n",
+ __func__);
+ }
+
+ /* reset device */
+ fwu_reset_device();
+
+ /* check device status */
+ retval = fwu_read_f01_device_status(&f01_device_status);
+ if (retval < 0)
+ goto exit;
+
+ dev_info(&fwu->rmi4_data->i2c_client->dev, "Device is in %s mode\n",
+ f01_device_status.flash_prog == 1 ? "bootloader" : "UI");
+ if (f01_device_status.flash_prog)
+ dev_info(&fwu->rmi4_data->i2c_client->dev, "Flash status %d\n",
+ f01_device_status.status_code);
+
+ if (f01_device_status.flash_prog) {
+ dev_info(&fwu->rmi4_data->i2c_client->dev,
+ "%s: Device is in flash prog mode 0x%02X\n",
+ __func__, f01_device_status.status_code);
+ retval = 0;
+ goto exit;
+ }
+
+ if (fw_entry)
+ release_firmware(fw_entry);
+
+ pr_notice("%s: End of reflash process\n", __func__);
+exit:
+ return retval;
+}
+
int synaptics_fw_updater(unsigned char *fw_data)
{
int retval;
@@ -1431,6 +1529,11 @@ static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data,
return;
}
+static void synaptics_rmi4_fwu_work(struct work_struct *work)
+{
+ fwu_start_reflash();
+}
+
static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
{
int retval;
@@ -1497,6 +1600,7 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
goto exit_free_mem;
fwu->initialized = true;
+ fwu->force_update = FORCE_UPDATE;
retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj,
&dev_attr_data);
@@ -1519,6 +1623,13 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
}
}
+#ifdef INSIDE_FIRMWARE_UPDATE
+ fwu->fwu_workqueue = create_singlethread_workqueue("fwu_workqueue");
+ INIT_DELAYED_WORK(&fwu->fwu_work, synaptics_rmi4_fwu_work);
+ queue_delayed_work(fwu->fwu_workqueue,
+ &fwu->fwu_work,
+ msecs_to_jiffies(1000));
+#endif
return 0;
exit_remove_attrs:
diff --git a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c
index 85530225abd2..76f9155bd49c 100644
--- a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c
+++ b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c
@@ -347,28 +347,32 @@ static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev,
if (rmi4_data->button_0d_enabled == input)
return count;
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) {
- ii = fhandler->intr_reg_num;
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) {
+ ii = fhandler->intr_reg_num;
- retval = synaptics_rmi4_i2c_read(rmi4_data,
- rmi4_data->f01_ctrl_base_addr + 1 + ii,
- &intr_enable,
- sizeof(intr_enable));
- if (retval < 0)
- return retval;
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr +
+ 1 + ii,
+ &intr_enable,
+ sizeof(intr_enable));
+ if (retval < 0)
+ return retval;
- if (input == 1)
- intr_enable |= fhandler->intr_mask;
- else
- intr_enable &= ~fhandler->intr_mask;
+ if (input == 1)
+ intr_enable |= fhandler->intr_mask;
+ else
+ intr_enable &= ~fhandler->intr_mask;
- retval = synaptics_rmi4_i2c_write(rmi4_data,
- rmi4_data->f01_ctrl_base_addr + 1 + ii,
- &intr_enable,
- sizeof(intr_enable));
- if (retval < 0)
- return retval;
+ retval = synaptics_rmi4_i2c_write(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr +
+ 1 + ii,
+ &intr_enable,
+ sizeof(intr_enable));
+ if (retval < 0)
+ return retval;
+ }
}
}
@@ -637,28 +641,22 @@ static int synaptics_rmi4_f11_abs_report(struct synaptics_rmi4_data *rmi4_data,
finger_status,
x, y, wx, wy);
-#ifdef TYPE_B_PROTOCOL
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_POSITION_X, x);
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_POSITION_Y, y);
-#ifdef REPORT_2D_W
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_TOUCH_MAJOR, max(wx, wy));
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_TOUCH_MINOR, min(wx, wy));
-#endif
-#else
+ input_report_key(rmi4_data->input_dev,
+ BTN_TOUCH, 1);
+ input_report_key(rmi4_data->input_dev,
+ BTN_TOOL_FINGER, 1);
input_report_abs(rmi4_data->input_dev,
ABS_MT_POSITION_X, x);
input_report_abs(rmi4_data->input_dev,
ABS_MT_POSITION_Y, y);
+
#ifdef REPORT_2D_W
input_report_abs(rmi4_data->input_dev,
ABS_MT_TOUCH_MAJOR, max(wx, wy));
input_report_abs(rmi4_data->input_dev,
ABS_MT_TOUCH_MINOR, min(wx, wy));
#endif
+#ifndef TYPE_B_PROTOCOL
input_mt_sync(rmi4_data->input_dev);
#endif
touch_count++;
@@ -853,12 +851,14 @@ static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data)
* Traverse the function handler list and service the source(s)
* of the interrupt accordingly.
*/
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->num_of_data_sources) {
- if (fhandler->intr_mask &
- intr[fhandler->intr_reg_num]) {
- synaptics_rmi4_report_touch(rmi4_data,
- fhandler, &touch_count);
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->num_of_data_sources) {
+ if (fhandler->intr_mask &
+ intr[fhandler->intr_reg_num]) {
+ synaptics_rmi4_report_touch(rmi4_data,
+ fhandler, &touch_count);
+ }
}
}
}
@@ -1088,8 +1088,8 @@ static int synaptics_rmi4_capacitance_button_map(
if (!pdata->capacitance_button_map) {
dev_err(&rmi4_data->i2c_client->dev,
- "%s: capacitance_button_map is \
- NULL in board file\n",
+ "%s: capacitance_button_map is" \
+ "NULL in board file\n",
__func__);
return -ENODEV;
} else if (!pdata->capacitance_button_map->map) {
@@ -1194,6 +1194,63 @@ static int synaptics_rmi4_alloc_fh(struct synaptics_rmi4_fn **fhandler,
return 0;
}
+
+ /**
+ * synaptics_rmi4_query_device_info()
+ *
+ * Called by synaptics_rmi4_query_device().
+ *
+ */
+static int synaptics_rmi4_query_device_info(
+ struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char f01_query[F01_STD_QUERY_LEN];
+ struct synaptics_rmi4_device_info *rmi = &(rmi4_data->rmi4_mod_info);
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_query_base_addr,
+ f01_query,
+ sizeof(f01_query));
+ if (retval < 0)
+ return retval;
+
+ /* RMI Version 4.0 currently supported */
+ rmi->version_major = 4;
+ rmi->version_minor = 0;
+
+ rmi->manufacturer_id = f01_query[0];
+ rmi->product_props = f01_query[1];
+ rmi->product_info[0] = f01_query[2] & MASK_7BIT;
+ rmi->product_info[1] = f01_query[3] & MASK_7BIT;
+ rmi->date_code[0] = f01_query[4] & MASK_5BIT;
+ rmi->date_code[1] = f01_query[5] & MASK_4BIT;
+ rmi->date_code[2] = f01_query[6] & MASK_5BIT;
+ rmi->tester_id = ((f01_query[7] & MASK_7BIT) << 8) |
+ (f01_query[8] & MASK_7BIT);
+ rmi->serial_number = ((f01_query[9] & MASK_7BIT) << 8) |
+ (f01_query[10] & MASK_7BIT);
+ memcpy(rmi->product_id_string, &f01_query[11], 10);
+
+ if (rmi->manufacturer_id != 1) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Non-Synaptics device found, manufacturer ID = %d\n",
+ __func__, rmi->manufacturer_id);
+ }
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_query_base_addr + F01_BUID_ID_OFFSET,
+ rmi->build_id,
+ sizeof(rmi->build_id));
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to read firmware build id (code %d)\n",
+ __func__, retval);
+ return retval;
+ }
+ return retval;
+}
+
/**
* synaptics_rmi4_query_device()
*
@@ -1214,7 +1271,6 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
unsigned char page_number;
unsigned char intr_count = 0;
unsigned char data_sources = 0;
- unsigned char f01_query[F01_STD_QUERY_LEN];
unsigned short pdt_entry_addr;
unsigned short intr_addr;
struct synaptics_rmi4_f01_device_status status;
@@ -1264,6 +1320,11 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
rmi4_data->f01_cmd_base_addr =
rmi_fd.cmd_base_addr;
+ retval =
+ synaptics_rmi4_query_device_info(rmi4_data);
+ if (retval < 0)
+ return retval;
+
retval = synaptics_rmi4_i2c_read(rmi4_data,
rmi4_data->f01_data_base_addr,
status.data,
@@ -1277,7 +1338,17 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
status.status_code);
goto flash_prog_mode;
}
- break;
+ break;
+
+ case SYNAPTICS_RMI4_F34:
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi_fd.ctrl_base_addr,
+ rmi->config_id,
+ sizeof(rmi->config_id));
+ if (retval < 0)
+ return retval;
+ break;
+
case SYNAPTICS_RMI4_F11:
if (rmi_fd.intr_src_count == 0)
break;
@@ -1335,56 +1406,24 @@ flash_prog_mode:
"%s: Number of interrupt registers = %d\n",
__func__, rmi4_data->num_of_intr_regs);
- retval = synaptics_rmi4_i2c_read(rmi4_data,
- rmi4_data->f01_query_base_addr,
- f01_query,
- sizeof(f01_query));
- if (retval < 0)
- return retval;
-
- /* RMI Version 4.0 currently supported */
- rmi->version_major = 4;
- rmi->version_minor = 0;
-
- rmi->manufacturer_id = f01_query[0];
- rmi->product_props = f01_query[1];
- rmi->product_info[0] = f01_query[2] & MASK_7BIT;
- rmi->product_info[1] = f01_query[3] & MASK_7BIT;
- rmi->date_code[0] = f01_query[4] & MASK_5BIT;
- rmi->date_code[1] = f01_query[5] & MASK_4BIT;
- rmi->date_code[2] = f01_query[6] & MASK_5BIT;
- rmi->tester_id = ((f01_query[7] & MASK_7BIT) << 8) |
- (f01_query[8] & MASK_7BIT);
- rmi->serial_number = ((f01_query[9] & MASK_7BIT) << 8) |
- (f01_query[10] & MASK_7BIT);
- memcpy(rmi->product_id_string, &f01_query[11], 10);
-
- if (rmi->manufacturer_id != 1) {
- dev_err(&rmi4_data->i2c_client->dev,
- "%s: Non-Synaptics device found, manufacturer ID = %d\n",
- __func__, rmi->manufacturer_id);
- }
-
- retval = synaptics_rmi4_i2c_read(rmi4_data,
- rmi4_data->f01_query_base_addr + F01_BUID_ID_OFFSET,
- rmi->build_id,
- sizeof(rmi->build_id));
- if (retval < 0)
- return retval;
-
memset(rmi4_data->intr_mask, 0x00, sizeof(rmi4_data->intr_mask));
/*
* Map out the interrupt bit masks for the interrupt sources
* from the registered function handlers.
*/
- list_for_each_entry(fhandler, &rmi->support_fn_list, link)
- data_sources += fhandler->num_of_data_sources;
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link)
+ data_sources += fhandler->num_of_data_sources;
+ }
if (data_sources) {
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->num_of_data_sources) {
- rmi4_data->intr_mask[fhandler->intr_reg_num] |=
- fhandler->intr_mask;
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler,
+ &rmi->support_fn_list, link) {
+ if (fhandler->num_of_data_sources) {
+ rmi4_data->intr_mask[fhandler->intr_reg_num] |=
+ fhandler->intr_mask;
+ }
}
}
}
@@ -1430,12 +1469,14 @@ static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data)
msleep(100);
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
- synaptics_rmi4_f1a_kfree(fhandler);
- else
- kfree(fhandler->data);
- kfree(fhandler);
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
+ synaptics_rmi4_f1a_kfree(fhandler);
+ else
+ kfree(fhandler->data);
+ kfree(fhandler);
+ }
}
retval = synaptics_rmi4_query_device(rmi4_data);
@@ -1534,12 +1575,14 @@ void synaptics_rmi4_new_function(enum exp_fn fn_type, bool insert,
exp_fhandler->inserted = false;
list_add_tail(&exp_fhandler->link, &exp_fn_list);
} else {
- list_for_each_entry(exp_fhandler, &exp_fn_list, link) {
- if (exp_fhandler->func_init == func_init) {
- exp_fhandler->inserted = false;
- exp_fhandler->func_init = NULL;
- exp_fhandler->func_attn = NULL;
- goto exit;
+ if (!list_empty(&exp_fn_list)) {
+ list_for_each_entry(exp_fhandler, &exp_fn_list, link) {
+ if (exp_fhandler->func_init == func_init) {
+ exp_fhandler->inserted = false;
+ exp_fhandler->func_init = NULL;
+ exp_fhandler->func_attn = NULL;
+ goto exit;
+ }
}
}
}
@@ -1611,7 +1654,7 @@ static int __devinit synaptics_rmi4_probe(struct i2c_client *client,
retval = -ENOMEM;
goto err_input_device;
}
-/*
+
if (platform_data->regulator_en) {
rmi4_data->regulator = regulator_get(&client->dev, "vdd");
if (IS_ERR(rmi4_data->regulator)) {
@@ -1623,7 +1666,7 @@ static int __devinit synaptics_rmi4_probe(struct i2c_client *client,
}
regulator_enable(rmi4_data->regulator);
}
-*/
+
rmi4_data->i2c_client = client;
rmi4_data->current_page = MASK_8BIT;
rmi4_data->board = platform_data;
@@ -1652,12 +1695,16 @@ static int __devinit synaptics_rmi4_probe(struct i2c_client *client,
rmi4_data->input_dev->name = DRIVER_NAME;
rmi4_data->input_dev->phys = INPUT_PHYS_NAME;
rmi4_data->input_dev->id.bustype = BUS_I2C;
+ rmi4_data->input_dev->id.product = SYNAPTICS_RMI4_DRIVER_PRODUCT;
+ rmi4_data->input_dev->id.version = SYNAPTICS_RMI4_DRIVER_VERSION;
rmi4_data->input_dev->dev.parent = &client->dev;
input_set_drvdata(rmi4_data->input_dev, rmi4_data);
set_bit(EV_SYN, rmi4_data->input_dev->evbit);
set_bit(EV_KEY, rmi4_data->input_dev->evbit);
set_bit(EV_ABS, rmi4_data->input_dev->evbit);
+ set_bit(BTN_TOUCH, rmi4_data->input_dev->keybit);
+ set_bit(BTN_TOOL_FINGER, rmi4_data->input_dev->keybit);
#ifdef INPUT_PROP_DIRECT
set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit);
@@ -1681,9 +1728,11 @@ static int __devinit synaptics_rmi4_probe(struct i2c_client *client,
#endif
f1a = NULL;
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
- f1a = fhandler->data;
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
+ f1a = fhandler->data;
+ }
}
if (f1a) {
@@ -1775,16 +1824,17 @@ err_query_device:
regulator_put(rmi4_data->regulator);
}
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
- synaptics_rmi4_f1a_kfree(fhandler);
- else
- kfree(fhandler->data);
- kfree(fhandler);
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
+ synaptics_rmi4_f1a_kfree(fhandler);
+ else
+ kfree(fhandler->data);
+ kfree(fhandler);
+ }
}
-/*
+
err_regulator:
-*/
input_free_device(rmi4_data->input_dev);
rmi4_data->input_dev = NULL;
@@ -1836,12 +1886,14 @@ static int __devexit synaptics_rmi4_remove(struct i2c_client *client)
regulator_put(rmi4_data->regulator);
}
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
- synaptics_rmi4_f1a_kfree(fhandler);
- else
- kfree(fhandler->data);
- kfree(fhandler);
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
+ synaptics_rmi4_f1a_kfree(fhandler);
+ else
+ kfree(fhandler->data);
+ kfree(fhandler);
+ }
}
input_free_device(rmi4_data->input_dev);
diff --git a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h
index 7ee0a925959a..ecb9b9415e8a 100644
--- a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h
+++ b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h
@@ -20,7 +20,10 @@
#ifndef _SYNAPTICS_DSX_RMI4_H_
#define _SYNAPTICS_DSX_RMI4_H_
-#define SYNAPTICS_RMI4_DRIVER_VERSION "DSX 1.0"
+#define SYNAPTICS_RMI4_DS4 0x0001
+#define SYNAPTICS_RMI4_DS5 0x0002
+#define SYNAPTICS_RMI4_DRIVER_PRODUCT SYNAPTICS_RMI4_DS4
+#define SYNAPTICS_RMI4_DRIVER_VERSION 0x1001
#include <linux/version.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
@@ -158,6 +161,7 @@ struct synaptics_rmi4_device_info {
unsigned short serial_number;
unsigned char product_id_string[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
unsigned char build_id[SYNAPTICS_RMI4_BUILD_ID_SIZE];
+ unsigned char config_id[3];
struct list_head support_fn_list;
};
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 1cb22d44ccbb..96100cc046c5 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -1584,14 +1584,14 @@ event_sched_out(struct perf_event *event,
perf_pmu_disable(event->pmu);
+ event->tstamp_stopped = tstamp;
+ event->pmu->del(event, 0);
+ event->oncpu = -1;
event->state = PERF_EVENT_STATE_INACTIVE;
if (event->pending_disable) {
event->pending_disable = 0;
event->state = PERF_EVENT_STATE_OFF;
}
- event->tstamp_stopped = tstamp;
- event->pmu->del(event, 0);
- event->oncpu = -1;
if (!is_software_event(event))
cpuctx->active_oncpu--;
@@ -8032,6 +8032,9 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
}
}
+ /* symmetric to unaccount_event() in _free_event() */
+ account_event(event);
+
return event;
err_per_task:
@@ -8398,8 +8401,6 @@ SYSCALL_DEFINE5(perf_event_open,
}
}
- account_event(event);
-
/*
* Special case software events and allow them to be part of
* any hardware group.
@@ -8638,7 +8639,12 @@ err_context:
perf_unpin_context(ctx);
put_ctx(ctx);
err_alloc:
- free_event(event);
+ /*
+ * If event_file is set, the fput() above will have called ->release()
+ * and that will take care of freeing the event.
+ */
+ if (!event_file)
+ free_event(event);
err_cpus:
put_online_cpus();
err_task:
@@ -8682,8 +8688,6 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
/* Mark owner so we could distinguish it from user events. */
event->owner = EVENT_OWNER_KERNEL;
- account_event(event);
-
ctx = find_get_context(event->pmu, task, event);
if (IS_ERR(ctx)) {
err = PTR_ERR(ctx);
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index b7342a24f559..b7dd5718836e 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -339,6 +339,7 @@ int hibernation_snapshot(int platform_mode)
pm_message_t msg;
int error;
+ pm_suspend_clear_flags();
error = platform_begin(platform_mode);
if (error)
goto Close;
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index f640c0e3c7ea..58303b3dc356 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -8986,6 +8986,7 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
set_window_start(rq);
raw_spin_unlock_irqrestore(&rq->lock, flags);
rq->calc_load_update = calc_load_update;
+ account_reset_rq(rq);
break;
case CPU_ONLINE:
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index 647f184f8aec..f29b132a9f8b 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -270,21 +270,21 @@ static __always_inline bool steal_account_process_tick(void)
#ifdef CONFIG_PARAVIRT
if (static_key_false(&paravirt_steal_enabled)) {
u64 steal;
- cputime_t steal_ct;
+ unsigned long steal_jiffies;
steal = paravirt_steal_clock(smp_processor_id());
steal -= this_rq()->prev_steal_time;
/*
- * cputime_t may be less precise than nsecs (eg: if it's
- * based on jiffies). Lets cast the result to cputime
+ * steal is in nsecs but our caller is expecting steal
+ * time in jiffies. Lets cast the result to jiffies
* granularity and account the rest on the next rounds.
*/
- steal_ct = nsecs_to_cputime(steal);
- this_rq()->prev_steal_time += cputime_to_nsecs(steal_ct);
+ steal_jiffies = nsecs_to_jiffies(steal);
+ this_rq()->prev_steal_time += jiffies_to_nsecs(steal_jiffies);
- account_steal_time(steal_ct);
- return steal_ct;
+ account_steal_time(jiffies_to_cputime(steal_jiffies));
+ return steal_jiffies;
}
#endif
return false;
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 45b76cfff1ec..b9566cf3ad37 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -2363,3 +2363,16 @@ static inline u64 irq_time_read(int cpu)
}
#endif /* CONFIG_64BIT */
#endif /* CONFIG_IRQ_TIME_ACCOUNTING */
+
+static inline void account_reset_rq(struct rq *rq)
+{
+#ifdef CONFIG_IRQ_TIME_ACCOUNTING
+ rq->prev_irq_time = 0;
+#endif
+#ifdef CONFIG_PARAVIRT
+ rq->prev_steal_time = 0;
+#endif
+#ifdef CONFIG_PARAVIRT_TIME_ACCOUNTING
+ rq->prev_steal_time_rq = 0;
+#endif
+}
diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c
index b65c78c15ab3..4a816bab38a2 100644
--- a/kernel/sysctl_binary.c
+++ b/kernel/sysctl_binary.c
@@ -1324,7 +1324,7 @@ static ssize_t binary_sysctl(const int *name, int nlen,
}
mnt = task_active_pid_ns(current)->proc_mnt;
- file = file_open_root(mnt->mnt_root, mnt, pathname, flags);
+ file = file_open_root(mnt->mnt_root, mnt, pathname, flags, 0);
result = PTR_ERR(file);
if (IS_ERR(file))
goto out_putname;
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 530bbc44082c..ae68222c5a74 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -5044,7 +5044,10 @@ static ssize_t tracing_splice_read_pipe(struct file *filp,
spd.nr_pages = i;
- ret = splice_to_pipe(pipe, &spd);
+ if (i)
+ ret = splice_to_pipe(pipe, &spd);
+ else
+ ret = 0;
out:
splice_shrink_spd(&spd);
return ret;
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
index e4e56589ec1d..be3222b7d72e 100644
--- a/kernel/trace/trace_irqsoff.c
+++ b/kernel/trace/trace_irqsoff.c
@@ -109,8 +109,12 @@ static int func_prolog_dec(struct trace_array *tr,
return 0;
local_save_flags(*flags);
- /* slight chance to get a false positive on tracing_cpu */
- if (!irqs_disabled_flags(*flags))
+ /*
+ * Slight chance to get a false positive on tracing_cpu,
+ * although I'm starting to think there isn't a chance.
+ * Leave this for now just to be paranoid.
+ */
+ if (!irqs_disabled_flags(*flags) && !preempt_count())
return 0;
*data = per_cpu_ptr(tr->trace_buffer.data, cpu);
diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c
index 060df67dbdd1..f96f0383f6c6 100644
--- a/kernel/trace/trace_printk.c
+++ b/kernel/trace/trace_printk.c
@@ -296,6 +296,9 @@ static int t_show(struct seq_file *m, void *v)
const char *str = *fmt;
int i;
+ if (!*fmt)
+ return 0;
+
seq_printf(m, "0x%lx : \"", *(unsigned long *)fmt);
/*
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index b40f7f35413d..9472691c1eb0 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -1038,6 +1038,9 @@ static int proc_watchdog_common(int which, struct ctl_table *table, int write,
* both lockup detectors are disabled if proc_watchdog_update()
* returns an error.
*/
+ if (old == new)
+ goto out;
+
err = proc_watchdog_update();
}
out:
@@ -1082,7 +1085,7 @@ int proc_soft_watchdog(struct ctl_table *table, int write,
int proc_watchdog_thresh(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
- int err, old;
+ int err, old, new;
get_online_cpus();
mutex_lock(&watchdog_proc_mutex);
@@ -1102,6 +1105,10 @@ int proc_watchdog_thresh(struct ctl_table *table, int write,
/*
* Update the sample period. Restore on failure.
*/
+ new = ACCESS_ONCE(watchdog_thresh);
+ if (old == new)
+ goto out;
+
set_sample_period();
err = proc_watchdog_update();
if (err) {
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 5c714f33f494..7535ef32a75b 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1332,7 +1332,7 @@ static unsigned long mem_cgroup_get_limit(struct mem_cgroup *memcg)
return limit;
}
-static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
+static bool mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
int order)
{
struct oom_control oc = {
@@ -1410,6 +1410,7 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
}
unlock:
mutex_unlock(&oom_lock);
+ return chosen;
}
#if MAX_NUMNODES > 1
@@ -5130,6 +5131,7 @@ static ssize_t memory_high_write(struct kernfs_open_file *of,
char *buf, size_t nbytes, loff_t off)
{
struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of));
+ unsigned long nr_pages;
unsigned long high;
int err;
@@ -5140,6 +5142,11 @@ static ssize_t memory_high_write(struct kernfs_open_file *of,
memcg->high = high;
+ nr_pages = page_counter_read(&memcg->memory);
+ if (nr_pages > high)
+ try_to_free_mem_cgroup_pages(memcg, nr_pages - high,
+ GFP_KERNEL, true);
+
memcg_wb_domain_size_changed(memcg);
return nbytes;
}
@@ -5161,6 +5168,8 @@ static ssize_t memory_max_write(struct kernfs_open_file *of,
char *buf, size_t nbytes, loff_t off)
{
struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of));
+ unsigned int nr_reclaims = MEM_CGROUP_RECLAIM_RETRIES;
+ bool drained = false;
unsigned long max;
int err;
@@ -5169,9 +5178,36 @@ static ssize_t memory_max_write(struct kernfs_open_file *of,
if (err)
return err;
- err = mem_cgroup_resize_limit(memcg, max);
- if (err)
- return err;
+ xchg(&memcg->memory.limit, max);
+
+ for (;;) {
+ unsigned long nr_pages = page_counter_read(&memcg->memory);
+
+ if (nr_pages <= max)
+ break;
+
+ if (signal_pending(current)) {
+ err = -EINTR;
+ break;
+ }
+
+ if (!drained) {
+ drain_all_stock(memcg);
+ drained = true;
+ continue;
+ }
+
+ if (nr_reclaims) {
+ if (!try_to_free_mem_cgroup_pages(memcg, nr_pages - max,
+ GFP_KERNEL, true))
+ nr_reclaims--;
+ continue;
+ }
+
+ mem_cgroup_events(memcg, MEMCG_OOM, 1);
+ if (!mem_cgroup_out_of_memory(memcg, GFP_KERNEL, 0))
+ break;
+ }
memcg_wb_domain_size_changed(memcg);
return nbytes;
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index c7d6f92f041b..ffcb2b56f6c1 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -734,7 +734,7 @@ continue_merging:
if (migratetype != buddy_mt
&& (is_migrate_isolate(migratetype) ||
- is_migrate_isolate(buddy_mt)))
+ is_migrate_isolate(buddy_mt)))
goto done_merging;
}
max_order++;
@@ -742,7 +742,6 @@ continue_merging:
}
done_merging:
-
set_page_order(page, order);
/*
diff --git a/mm/page_isolation.c b/mm/page_isolation.c
index 4568fd58f70a..00c96462cc36 100644
--- a/mm/page_isolation.c
+++ b/mm/page_isolation.c
@@ -283,11 +283,11 @@ struct page *alloc_migrate_target(struct page *page, unsigned long private,
* now as a simple work-around, we use the next node for destination.
*/
if (PageHuge(page)) {
- nodemask_t src = nodemask_of_node(page_to_nid(page));
- nodemask_t dst;
- nodes_complement(dst, src);
+ int node = next_online_node(page_to_nid(page));
+ if (node == MAX_NUMNODES)
+ node = first_online_node;
return alloc_huge_page_node(page_hstate(compound_head(page)),
- next_node(page_to_nid(page), dst));
+ node);
}
if (PageHighMem(page))
diff --git a/net/Kconfig b/net/Kconfig
index dd79708d4262..5cff5877d7d1 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -102,14 +102,6 @@ config ANDROID_PARANOID_NETWORK
help
none
-config NET_ACTIVITY_STATS
- bool "Network activity statistics tracking"
- default y
- help
- Network activity statistics are useful for tracking wireless
- modem activity on 2G, 3G, 4G wireless networks. Counts number of
- transmissions and groups them in specified time buckets.
-
config NETWORK_SECMARK
bool "Security Marking"
help
diff --git a/net/Makefile b/net/Makefile
index e04baae76e3c..e700aa62b1af 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -77,6 +77,5 @@ endif
ifneq ($(CONFIG_NET_L3_MASTER_DEV),)
obj-y += l3mdev/
endif
-obj-$(CONFIG_NET_ACTIVITY_STATS) += activity_stats.o
obj-$(CONFIG_IPC_ROUTER) += ipc_router/
obj-$(CONFIG_RMNET_DATA) += rmnet_data/
diff --git a/net/activity_stats.c b/net/activity_stats.c
deleted file mode 100644
index 8a3e93470069..000000000000
--- a/net/activity_stats.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/* net/activity_stats.c
- *
- * Copyright (C) 2010 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.
- *
- * Author: Mike Chan (mike@android.com)
- */
-
-#include <linux/proc_fs.h>
-#include <linux/suspend.h>
-#include <net/net_namespace.h>
-
-/*
- * Track transmission rates in buckets (power of 2).
- * 1,2,4,8...512 seconds.
- *
- * Buckets represent the count of network transmissions at least
- * N seconds apart, where N is 1 << bucket index.
- */
-#define BUCKET_MAX 10
-
-/* Track network activity frequency */
-static unsigned long activity_stats[BUCKET_MAX];
-static ktime_t last_transmit;
-static ktime_t suspend_time;
-static DEFINE_SPINLOCK(activity_lock);
-
-void activity_stats_update(void)
-{
- int i;
- unsigned long flags;
- ktime_t now;
- s64 delta;
-
- spin_lock_irqsave(&activity_lock, flags);
- now = ktime_get();
- delta = ktime_to_ns(ktime_sub(now, last_transmit));
-
- for (i = BUCKET_MAX - 1; i >= 0; i--) {
- /*
- * Check if the time delta between network activity is within the
- * minimum bucket range.
- */
- if (delta < (1000000000ULL << i))
- continue;
-
- activity_stats[i]++;
- last_transmit = now;
- break;
- }
- spin_unlock_irqrestore(&activity_lock, flags);
-}
-
-static int activity_stats_read_proc(char *page, char **start, off_t off,
- int count, int *eof, void *data)
-{
- int i;
- int len;
- char *p = page;
-
- /* Only print if offset is 0, or we have enough buffer space */
- if (off || count < (30 * BUCKET_MAX + 22))
- return -ENOMEM;
-
- len = snprintf(p, count, "Min Bucket(sec) Count\n");
- count -= len;
- p += len;
-
- for (i = 0; i < BUCKET_MAX; i++) {
- len = snprintf(p, count, "%15d %lu\n", 1 << i, activity_stats[i]);
- count -= len;
- p += len;
- }
- *eof = 1;
-
- return p - page;
-}
-
-static int activity_stats_notifier(struct notifier_block *nb,
- unsigned long event, void *dummy)
-{
- switch (event) {
- case PM_SUSPEND_PREPARE:
- suspend_time = ktime_get_real();
- break;
-
- case PM_POST_SUSPEND:
- suspend_time = ktime_sub(ktime_get_real(), suspend_time);
- last_transmit = ktime_sub(last_transmit, suspend_time);
- }
-
- return 0;
-}
-
-static struct notifier_block activity_stats_notifier_block = {
- .notifier_call = activity_stats_notifier,
-};
-
-static int __init activity_stats_init(void)
-{
- create_proc_read_entry("activity", S_IRUGO,
- init_net.proc_net_stat, activity_stats_read_proc, NULL);
- return register_pm_notifier(&activity_stats_notifier_block);
-}
-
-subsys_initcall(activity_stats_init);
-
diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c
index b563a3f5f2a8..2fa3be965101 100644
--- a/net/ax25/ax25_ip.c
+++ b/net/ax25/ax25_ip.c
@@ -228,8 +228,23 @@ netdev_tx_t ax25_ip_xmit(struct sk_buff *skb)
}
#endif
+static bool ax25_validate_header(const char *header, unsigned int len)
+{
+ ax25_digi digi;
+
+ if (!len)
+ return false;
+
+ if (header[0])
+ return true;
+
+ return ax25_addr_parse(header + 1, len - 1, NULL, NULL, &digi, NULL,
+ NULL);
+}
+
const struct header_ops ax25_header_ops = {
.create = ax25_hard_header,
+ .validate = ax25_validate_header,
};
EXPORT_SYMBOL(ax25_header_ops);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index f00e788e0184..db399c1662ab 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -7155,6 +7155,10 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
status);
+ if (data_len != sizeof(*cp) + cp->adv_data_len + cp->scan_rsp_len)
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
+ MGMT_STATUS_INVALID_PARAMS);
+
flags = __le32_to_cpu(cp->flags);
timeout = __le16_to_cpu(cp->timeout);
duration = __le16_to_cpu(cp->duration);
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index 5f3f64553179..eff69cb270d2 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -567,6 +567,14 @@ int br_set_max_age(struct net_bridge *br, unsigned long val)
}
+/* Set time interval that dynamic forwarding entries live
+ * For pure software bridge, allow values outside the 802.1
+ * standard specification for special cases:
+ * 0 - entry never ages (all permanant)
+ * 1 - entry disappears (no persistance)
+ *
+ * Offloaded switch entries maybe more restrictive
+ */
int br_set_ageing_time(struct net_bridge *br, u32 ageing_time)
{
struct switchdev_attr attr = {
@@ -577,11 +585,8 @@ int br_set_ageing_time(struct net_bridge *br, u32 ageing_time)
unsigned long t = clock_t_to_jiffies(ageing_time);
int err;
- if (t < BR_MIN_AGEING_TIME || t > BR_MAX_AGEING_TIME)
- return -ERANGE;
-
err = switchdev_port_attr_set(br->dev, &attr);
- if (err)
+ if (err && err != -EOPNOTSUPP)
return err;
br->ageing_time = t;
diff --git a/net/core/filter.c b/net/core/filter.c
index 37157c4c1a78..f393a22b9d50 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -1139,7 +1139,8 @@ void bpf_prog_destroy(struct bpf_prog *fp)
}
EXPORT_SYMBOL_GPL(bpf_prog_destroy);
-static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk)
+static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk,
+ bool locked)
{
struct sk_filter *fp, *old_fp;
@@ -1155,10 +1156,8 @@ static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk)
return -ENOMEM;
}
- old_fp = rcu_dereference_protected(sk->sk_filter,
- sock_owned_by_user(sk));
+ old_fp = rcu_dereference_protected(sk->sk_filter, locked);
rcu_assign_pointer(sk->sk_filter, fp);
-
if (old_fp)
sk_filter_uncharge(sk, old_fp);
@@ -1175,7 +1174,8 @@ static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk)
* occurs or there is insufficient memory for the filter a negative
* errno code is returned. On success the return is zero.
*/
-int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
+int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk,
+ bool locked)
{
unsigned int fsize = bpf_classic_proglen(fprog);
unsigned int bpf_fsize = bpf_prog_size(fprog->len);
@@ -1213,7 +1213,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
if (IS_ERR(prog))
return PTR_ERR(prog);
- err = __sk_attach_prog(prog, sk);
+ err = __sk_attach_prog(prog, sk, locked);
if (err < 0) {
__bpf_prog_release(prog);
return err;
@@ -1221,7 +1221,12 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
return 0;
}
-EXPORT_SYMBOL_GPL(sk_attach_filter);
+EXPORT_SYMBOL_GPL(__sk_attach_filter);
+
+int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
+{
+ return __sk_attach_filter(fprog, sk, sock_owned_by_user(sk));
+}
int sk_attach_bpf(u32 ufd, struct sock *sk)
{
@@ -1240,7 +1245,7 @@ int sk_attach_bpf(u32 ufd, struct sock *sk)
return -EINVAL;
}
- err = __sk_attach_prog(prog, sk);
+ err = __sk_attach_prog(prog, sk, sock_owned_by_user(sk));
if (err < 0) {
bpf_prog_put(prog);
return err;
@@ -1913,7 +1918,7 @@ static int __init register_sk_filter_ops(void)
}
late_initcall(register_sk_filter_ops);
-int sk_detach_filter(struct sock *sk)
+int __sk_detach_filter(struct sock *sk, bool locked)
{
int ret = -ENOENT;
struct sk_filter *filter;
@@ -1921,8 +1926,7 @@ int sk_detach_filter(struct sock *sk)
if (sock_flag(sk, SOCK_FILTER_LOCKED))
return -EPERM;
- filter = rcu_dereference_protected(sk->sk_filter,
- sock_owned_by_user(sk));
+ filter = rcu_dereference_protected(sk->sk_filter, locked);
if (filter) {
RCU_INIT_POINTER(sk->sk_filter, NULL);
sk_filter_uncharge(sk, filter);
@@ -1931,7 +1935,12 @@ int sk_detach_filter(struct sock *sk)
return ret;
}
-EXPORT_SYMBOL_GPL(sk_detach_filter);
+EXPORT_SYMBOL_GPL(__sk_detach_filter);
+
+int sk_detach_filter(struct sock *sk)
+{
+ return __sk_detach_filter(sk, sock_owned_by_user(sk));
+}
int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf,
unsigned int len)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 34ba7a08876d..ca966f7de351 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -905,6 +905,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
+ rtnl_link_get_af_size(dev, ext_filter_mask) /* IFLA_AF_SPEC */
+ nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_PORT_ID */
+ nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_SWITCH_ID */
+ + nla_total_size(IFNAMSIZ) /* IFLA_PHYS_PORT_NAME */
+ nla_total_size(1); /* IFLA_PROTO_DOWN */
}
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 2d1dd16327a2..732be5afa6ce 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2965,6 +2965,24 @@ int skb_append_pagefrags(struct sk_buff *skb, struct page *page,
EXPORT_SYMBOL_GPL(skb_append_pagefrags);
/**
+ * skb_push_rcsum - push skb and update receive checksum
+ * @skb: buffer to update
+ * @len: length of data pulled
+ *
+ * This function performs an skb_push on the packet and updates
+ * the CHECKSUM_COMPLETE checksum. It should be used on
+ * receive path processing instead of skb_push unless you know
+ * that the checksum difference is zero (e.g., a valid IP header)
+ * or you are setting ip_summed to CHECKSUM_NONE.
+ */
+static unsigned char *skb_push_rcsum(struct sk_buff *skb, unsigned len)
+{
+ skb_push(skb, len);
+ skb_postpush_rcsum(skb, skb->data, len);
+ return skb->data;
+}
+
+/**
* skb_pull_rcsum - pull skb and update receive checksum
* @skb: buffer to update
* @len: length of data pulled
@@ -4101,9 +4119,9 @@ struct sk_buff *skb_checksum_trimmed(struct sk_buff *skb,
if (!pskb_may_pull(skb_chk, offset))
goto err;
- __skb_pull(skb_chk, offset);
+ skb_pull_rcsum(skb_chk, offset);
ret = skb_chkf(skb_chk);
- __skb_push(skb_chk, offset);
+ skb_push_rcsum(skb_chk, offset);
if (ret)
goto err;
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 902d606324a0..8be8f27bfacc 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -204,8 +204,6 @@ void dccp_req_err(struct sock *sk, u64 seq)
* ICMPs are not backlogged, hence we cannot get an established
* socket here.
*/
- WARN_ON(req->sk);
-
if (!between48(seq, dccp_rsk(req)->dreq_iss, dccp_rsk(req)->dreq_gss)) {
NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
} else {
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 011e73357ba5..926169c94a0b 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -335,6 +335,9 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
ASSERT_RTNL();
+ if (in_dev->dead)
+ goto no_promotions;
+
/* 1. Deleting primary ifaddr forces deletion all secondaries
* unless alias promotion is set
**/
@@ -381,6 +384,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
fib_del_ifaddr(ifa, ifa1);
}
+no_promotions:
/* 2. Unlink it */
*ifap = ifa1->ifa_next;
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index e10edb5e78b0..f97ae9d93ee9 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -280,7 +280,6 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb)
struct in_device *in_dev;
struct fib_result res;
struct rtable *rt;
- struct flowi4 fl4;
struct net *net;
int scope;
@@ -296,14 +295,13 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb)
scope = RT_SCOPE_UNIVERSE;
if (!ipv4_is_zeronet(ip_hdr(skb)->saddr)) {
- fl4.flowi4_oif = 0;
- fl4.flowi4_iif = LOOPBACK_IFINDEX;
- fl4.daddr = ip_hdr(skb)->saddr;
- fl4.saddr = 0;
- fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
- fl4.flowi4_scope = scope;
- fl4.flowi4_mark = IN_DEV_SRC_VMARK(in_dev) ? skb->mark : 0;
- fl4.flowi4_tun_key.tun_id = 0;
+ struct flowi4 fl4 = {
+ .flowi4_iif = LOOPBACK_IFINDEX,
+ .daddr = ip_hdr(skb)->saddr,
+ .flowi4_tos = RT_TOS(ip_hdr(skb)->tos),
+ .flowi4_scope = scope,
+ .flowi4_mark = IN_DEV_SRC_VMARK(in_dev) ? skb->mark : 0,
+ };
if (!fib_lookup(net, &fl4, &res, 0))
return FIB_RES_PREFSRC(net, res);
} else {
@@ -923,6 +921,9 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim)
subnet = 1;
}
+ if (in_dev->dead)
+ goto no_promotions;
+
/* Deletion is more complicated than add.
* We should take care of not to delete too much :-)
*
@@ -998,6 +999,7 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim)
}
}
+no_promotions:
if (!(ok & BRD_OK))
fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
if (subnet && ifa->ifa_prefixlen < 31) {
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 05e4cba14162..b3086cf27027 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -356,9 +356,8 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu)
skb_dst_set(skb, &rt->dst);
skb->dev = dev;
- skb->reserved_tailroom = skb_end_offset(skb) -
- min(mtu, skb_end_offset(skb));
skb_reserve(skb, hlen);
+ skb_tailroom_reserve(skb, mtu, tlen);
skb_reset_network_header(skb);
pip = ip_hdr(skb);
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index e73b4f8b2096..dbf7f7ee2958 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1237,13 +1237,16 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page,
if (!skb)
return -EINVAL;
- cork->length += size;
if ((size + skb->len > mtu) &&
(sk->sk_protocol == IPPROTO_UDP) &&
(rt->dst.dev->features & NETIF_F_UFO)) {
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ return -EOPNOTSUPP;
+
skb_shinfo(skb)->gso_size = mtu - fragheaderlen;
skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
}
+ cork->length += size;
while (size > 0) {
if (skb_is_gso(skb)) {
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index cbb51f3fac06..ce30c8b72457 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -663,6 +663,8 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
inner_iph = (const struct iphdr *)skb_inner_network_header(skb);
connected = (tunnel->parms.iph.daddr != 0);
+ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
+
dst = tnl_params->daddr;
if (dst == 0) {
/* NBMA tunnel */
@@ -760,7 +762,6 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
tunnel->err_time + IPTUNNEL_ERR_TIMEO)) {
tunnel->err_count--;
- memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
dst_link_failure(skb);
} else
tunnel->err_count = 0;
diff --git a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
index c6eb42100e9a..ea91058b5f6f 100644
--- a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
+++ b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
@@ -108,10 +108,18 @@ static int masq_inet_event(struct notifier_block *this,
unsigned long event,
void *ptr)
{
- struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev;
+ struct in_device *idev = ((struct in_ifaddr *)ptr)->ifa_dev;
struct netdev_notifier_info info;
- netdev_notifier_info_init(&info, dev);
+ /* The masq_dev_notifier will catch the case of the device going
+ * down. So if the inetdev is dead and being destroyed we have
+ * no work to do. Otherwise this is an individual address removal
+ * and we have to perform the flush.
+ */
+ if (idev->dead)
+ return NOTIFY_DONE;
+
+ netdev_notifier_info_init(&info, idev->dev);
return masq_device_event(this, event, &info);
}
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index e5cdafcc2140..2ee7b830a35e 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -269,7 +269,6 @@
#include <linux/crypto.h>
#include <linux/time.h>
#include <linux/slab.h>
-#include <linux/uid_stat.h>
#include <net/icmp.h>
#include <net/inet_common.h>
@@ -1294,10 +1293,6 @@ out:
tcp_push(sk, flags, mss_now, tp->nonagle, size_goal);
out_nopush:
release_sock(sk);
-
- if (copied + copied_syn)
- uid_stat_tcp_snd(from_kuid(&init_user_ns, current_uid()),
- copied + copied_syn);
return copied + copied_syn;
do_fault:
@@ -1575,8 +1570,6 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
if (copied > 0) {
tcp_recv_skb(sk, seq, &offset);
tcp_cleanup_rbuf(sk, copied);
- uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()),
- copied);
}
return copied;
}
@@ -1910,10 +1903,6 @@ skip_copy:
tcp_cleanup_rbuf(sk, copied);
release_sock(sk);
-
- if (copied > 0)
- uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()),
- copied);
return copied;
out:
@@ -1922,9 +1911,6 @@ out:
recv_urg:
err = tcp_recv_urg(sk, msg, len, flags);
- if (err > 0)
- uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()),
- err);
goto out;
recv_sndq:
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index f5cfc5ff0712..07408b81bcd7 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -320,8 +320,6 @@ void tcp_req_err(struct sock *sk, u32 seq, bool abort)
/* ICMPs are not backlogged, hence we cannot get
* an established socket here.
*/
- WARN_ON(req->sk);
-
if (seq != tcp_rsk(req)->snt_isn) {
NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
} else if (abort) {
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c
index c8cbc2b4b792..a726d7853ce5 100644
--- a/net/ipv4/tcp_metrics.c
+++ b/net/ipv4/tcp_metrics.c
@@ -550,7 +550,7 @@ reset:
*/
if (crtt > tp->srtt_us) {
/* Set RTO like tcp_rtt_estimator(), but from cached RTT. */
- crtt /= 8 * USEC_PER_MSEC;
+ crtt /= 8 * USEC_PER_SEC / HZ;
inet_csk(sk)->icsk_rto = crtt + max(2 * crtt, tcp_rto_min(sk));
} else if (tp->srtt_us == 0) {
/* RFC6298: 5.7 We've failed to get a valid RTT sample from
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index ac6b1961ffeb..9475a2748a9a 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -458,7 +458,7 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,
newtp->rcv_wup = newtp->copied_seq =
newtp->rcv_nxt = treq->rcv_isn + 1;
- newtp->segs_in = 0;
+ newtp->segs_in = 1;
newtp->snd_sml = newtp->snd_una =
newtp->snd_nxt = newtp->snd_up = treq->snt_isn + 1;
@@ -818,6 +818,7 @@ int tcp_child_process(struct sock *parent, struct sock *child,
int ret = 0;
int state = child->sk_state;
+ tcp_sk(child)->segs_in += max_t(u16, 1, skb_shinfo(skb)->gso_segs);
if (!sock_owned_by_user(child)) {
ret = tcp_rcv_state_process(child, skb);
/* Wakeup parent, send SIGIO */
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 5411b65b7702..eeac33e94527 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1996,10 +1996,14 @@ void udp_v4_early_demux(struct sk_buff *skb)
if (!in_dev)
return;
- ours = ip_check_mc_rcu(in_dev, iph->daddr, iph->saddr,
- iph->protocol);
- if (!ours)
- return;
+ /* we are supposed to accept bcast packets */
+ if (skb->pkt_type == PACKET_MULTICAST) {
+ ours = ip_check_mc_rcu(in_dev, iph->daddr, iph->saddr,
+ iph->protocol);
+ if (!ours)
+ return;
+ }
+
sk = __udp4_lib_mcast_demux_lookup(net, uh->dest, iph->daddr,
uh->source, iph->saddr, dif);
} else if (skb->pkt_type == PACKET_HOST) {
diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c
index aba428626b52..280a9bdeddee 100644
--- a/net/ipv4/udp_tunnel.c
+++ b/net/ipv4/udp_tunnel.c
@@ -89,6 +89,8 @@ int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
uh->source = src_port;
uh->len = htons(skb->len);
+ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
+
udp_set_csum(nocheck, skb, src, dst, skb->len);
return iptunnel_xmit(sk, rt, skb, src, dst, IPPROTO_UDP,
diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c
index 835ec57c233b..840a4388f860 100644
--- a/net/ipv6/exthdrs_core.c
+++ b/net/ipv6/exthdrs_core.c
@@ -260,7 +260,11 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
return -EINVAL;
}
}
- return -ENOENT;
+ if (!found)
+ return -ENOENT;
+ if (fragoff)
+ *fragoff = _frag_off;
+ break;
}
hdrlen = 8;
} else if (nexthdr == NEXTHDR_AUTH) {
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index e5ea177d34c6..4650c6824783 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -778,6 +778,8 @@ static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)
__u32 mtu;
int err;
+ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
+
if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
encap_limit = t->parms.encap_limit;
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index c2b523c498d8..c87ca6987f9c 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1090,8 +1090,8 @@ static inline int ip6_ufo_append_data(struct sock *sk,
int getfrag(void *from, char *to, int offset, int len,
int odd, struct sk_buff *skb),
void *from, int length, int hh_len, int fragheaderlen,
- int transhdrlen, int mtu, unsigned int flags,
- const struct flowi6 *fl6)
+ int exthdrlen, int transhdrlen, int mtu,
+ unsigned int flags, const struct flowi6 *fl6)
{
struct sk_buff *skb;
@@ -1116,7 +1116,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
skb_put(skb, fragheaderlen + transhdrlen);
/* initialize network header pointer */
- skb_reset_network_header(skb);
+ skb_set_network_header(skb, exthdrlen);
/* initialize protocol header pointer */
skb->transport_header = skb->network_header + fragheaderlen;
@@ -1358,7 +1358,7 @@ emsgsize:
(rt->dst.dev->features & NETIF_F_UFO) &&
(sk->sk_type == SOCK_DGRAM) && !udp_get_no_check6_tx(sk)) {
err = ip6_ufo_append_data(sk, queue, getfrag, from, length,
- hh_len, fragheaderlen,
+ hh_len, fragheaderlen, exthdrlen,
transhdrlen, mtu, flags, fl6);
if (err)
goto error;
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 137fca42aaa6..3991b21e24ad 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -343,12 +343,12 @@ static int ip6_tnl_create2(struct net_device *dev)
t = netdev_priv(dev);
+ dev->rtnl_link_ops = &ip6_link_ops;
err = register_netdevice(dev);
if (err < 0)
goto out;
strcpy(t->parms.name, dev->name);
- dev->rtnl_link_ops = &ip6_link_ops;
dev_hold(dev);
ip6_tnl_link(ip6n, t);
@@ -1180,6 +1180,8 @@ ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
u8 tproto;
int err;
+ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
+
tproto = ACCESS_ONCE(t->parms.proto);
if (tproto != IPPROTO_IPIP && tproto != 0)
return -1;
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 5ee56d0a8699..d64ee7e83664 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1574,9 +1574,8 @@ static struct sk_buff *mld_newpack(struct inet6_dev *idev, unsigned int mtu)
return NULL;
skb->priority = TC_PRIO_CONTROL;
- skb->reserved_tailroom = skb_end_offset(skb) -
- min(mtu, skb_end_offset(skb));
skb_reserve(skb, hlen);
+ skb_tailroom_reserve(skb, mtu, tlen);
if (__ipv6_get_lladdr(idev, &addr_buf, IFA_F_TENTATIVE)) {
/* <draft-ietf-magma-mld-source-05.txt>:
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index a1b6adc20e1e..9cb0ff304336 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -837,8 +837,8 @@ start_lookup:
flush_stack(stack, count, skb, count - 1);
} else {
if (!inner_flushed)
- UDP_INC_STATS_BH(net, UDP_MIB_IGNOREDMULTI,
- proto == IPPROTO_UDPLITE);
+ UDP6_INC_STATS_BH(net, UDP_MIB_IGNOREDMULTI,
+ proto == IPPROTO_UDPLITE);
consume_skb(skb);
}
return 0;
@@ -916,11 +916,9 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
ret = udpv6_queue_rcv_skb(sk, skb);
sock_put(sk);
- /* a return value > 0 means to resubmit the input, but
- * it wants the return to be -protocol, or 0
- */
+ /* a return value > 0 means to resubmit the input */
if (ret > 0)
- return -ret;
+ return ret;
return 0;
}
diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c
index ec22078b0914..42de4ccd159f 100644
--- a/net/l2tp/l2tp_ip.c
+++ b/net/l2tp/l2tp_ip.c
@@ -123,12 +123,11 @@ static int l2tp_ip_recv(struct sk_buff *skb)
struct l2tp_tunnel *tunnel = NULL;
int length;
- /* Point to L2TP header */
- optr = ptr = skb->data;
-
if (!pskb_may_pull(skb, 4))
goto discard;
+ /* Point to L2TP header */
+ optr = ptr = skb->data;
session_id = ntohl(*((__be32 *) ptr));
ptr += 4;
@@ -156,6 +155,9 @@ static int l2tp_ip_recv(struct sk_buff *skb)
if (!pskb_may_pull(skb, length))
goto discard;
+ /* Point to L2TP header */
+ optr = ptr = skb->data;
+ ptr += 4;
pr_debug("%s: ip recv\n", tunnel->name);
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, ptr, length);
}
diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c
index a2c8747d2936..9ee4ddb6b397 100644
--- a/net/l2tp/l2tp_ip6.c
+++ b/net/l2tp/l2tp_ip6.c
@@ -135,12 +135,11 @@ static int l2tp_ip6_recv(struct sk_buff *skb)
struct l2tp_tunnel *tunnel = NULL;
int length;
- /* Point to L2TP header */
- optr = ptr = skb->data;
-
if (!pskb_may_pull(skb, 4))
goto discard;
+ /* Point to L2TP header */
+ optr = ptr = skb->data;
session_id = ntohl(*((__be32 *) ptr));
ptr += 4;
@@ -168,6 +167,9 @@ static int l2tp_ip6_recv(struct sk_buff *skb)
if (!pskb_may_pull(skb, length))
goto discard;
+ /* Point to L2TP header */
+ optr = ptr = skb->data;
+ ptr += 4;
pr_debug("%s: ip recv\n", tunnel->name);
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, ptr, length);
}
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 6a12b0f5cac8..980e9e9b6684 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -7,6 +7,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* 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
@@ -1484,14 +1485,21 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
- num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy,
- &ifibss->chandef,
- channels,
- ARRAY_SIZE(channels));
scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
- ieee80211_request_ibss_scan(sdata, ifibss->ssid,
- ifibss->ssid_len, channels, num,
- scan_width);
+
+ if (ifibss->fixed_channel) {
+ num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy,
+ &ifibss->chandef,
+ channels,
+ ARRAY_SIZE(channels));
+ ieee80211_request_ibss_scan(sdata, ifibss->ssid,
+ ifibss->ssid_len, channels,
+ num, scan_width);
+ } else {
+ ieee80211_request_ibss_scan(sdata, ifibss->ssid,
+ ifibss->ssid_len, NULL,
+ 0, scan_width);
+ }
} else {
int interval = IEEE80211_SCAN_INTERVAL;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index c9e325d2e120..7a2b7915093b 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -977,7 +977,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
if (sdata->vif.txq) {
struct txq_info *txqi = to_txq_info(sdata->vif.txq);
+ spin_lock_bh(&txqi->queue.lock);
ieee80211_purge_tx_queue(&local->hw, &txqi->queue);
+ spin_unlock_bh(&txqi->queue.lock);
+
atomic_set(&sdata->txqs_len[txqi->txq.ac], 0);
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 4cbf36cae806..a3bb8f7f5fc5 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2250,7 +2250,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
struct ieee80211_local *local = rx->local;
struct ieee80211_sub_if_data *sdata = rx->sdata;
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- u16 q, hdrlen;
+ u16 ac, q, hdrlen;
hdr = (struct ieee80211_hdr *) skb->data;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
@@ -2319,7 +2319,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
ether_addr_equal(sdata->vif.addr, hdr->addr3))
return RX_CONTINUE;
- q = ieee80211_select_queue_80211(sdata, skb, hdr);
+ ac = ieee80211_select_queue_80211(sdata, skb, hdr);
+ q = sdata->vif.hw_queue[ac];
if (ieee80211_queue_stopped(&local->hw, q)) {
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion);
return RX_DROP_MONITOR;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index f91d1873218c..67066d048e6f 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -256,11 +256,11 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
}
/* Caller must hold local->sta_mtx */
-static void sta_info_hash_add(struct ieee80211_local *local,
- struct sta_info *sta)
+static int sta_info_hash_add(struct ieee80211_local *local,
+ struct sta_info *sta)
{
- rhashtable_insert_fast(&local->sta_hash, &sta->hash_node,
- sta_rht_params);
+ return rhashtable_insert_fast(&local->sta_hash, &sta->hash_node,
+ sta_rht_params);
}
static void sta_deliver_ps_frames(struct work_struct *wk)
@@ -484,11 +484,17 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
- struct station_info sinfo;
+ struct station_info *sinfo;
int err = 0;
lockdep_assert_held(&local->sta_mtx);
+ sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
+ if (!sinfo) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
/* check if STA exists already */
if (sta_info_get_bss(sdata, sta->sta.addr)) {
err = -EEXIST;
@@ -503,7 +509,9 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
set_sta_flag(sta, WLAN_STA_BLOCK_BA);
/* make the station visible */
- sta_info_hash_add(local, sta);
+ err = sta_info_hash_add(local, sta);
+ if (err)
+ goto out_drop_sta;
list_add_tail_rcu(&sta->list, &local->sta_list);
@@ -520,10 +528,9 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
ieee80211_sta_debugfs_add(sta);
rate_control_add_sta_debugfs(sta);
- memset(&sinfo, 0, sizeof(sinfo));
- sinfo.filled = 0;
- sinfo.generation = local->sta_generation;
- cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
+ sinfo->generation = local->sta_generation;
+ cfg80211_new_sta(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL);
+ kfree(sinfo);
sta_dbg(sdata, "Inserted STA %pM\n", sta->sta.addr);
@@ -538,6 +545,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
out_remove:
sta_info_hash_del(local, sta);
list_del_rcu(&sta->list);
+ out_drop_sta:
local->num_sta--;
synchronize_net();
__cleanup_single_sta(sta);
@@ -882,7 +890,7 @@ static void __sta_info_destroy_part2(struct sta_info *sta)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
- struct station_info sinfo = {};
+ struct station_info *sinfo;
int ret;
/*
@@ -920,8 +928,11 @@ static void __sta_info_destroy_part2(struct sta_info *sta)
sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr);
- sta_set_sinfo(sta, &sinfo);
- cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
+ sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
+ if (sinfo)
+ sta_set_sinfo(sta, sinfo);
+ cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL);
+ kfree(sinfo);
rate_control_remove_sta_debugfs(sta);
ieee80211_sta_debugfs_remove(sta);
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index c32fc411a911..881bc2072809 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -518,6 +518,9 @@ static struct net_device *find_outdev(struct net *net,
if (!dev)
return ERR_PTR(-ENODEV);
+ if (IS_ERR(dev))
+ return dev;
+
/* The caller is holding rtnl anyways, so release the dev reference */
dev_put(dev);
diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c
index e1442bfb668d..e2e7d54f9bb1 100644
--- a/net/netfilter/xt_qtaguid.c
+++ b/net/netfilter/xt_qtaguid.c
@@ -1606,7 +1606,7 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb,
* When in TCP_TIME_WAIT the sk is not a "struct sock" but
* "struct inet_timewait_sock" which is missing fields.
*/
- if (sk->sk_state == TCP_TIME_WAIT) {
+ if (!sk_fullsock(sk) || sk->sk_state == TCP_TIME_WAIT) {
sock_gen_put(sk);
sk = NULL;
}
@@ -1689,7 +1689,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
/* default: Fall through and do UID releated work */
}
- sk = skb->sk;
+ sk = skb_to_full_sk(skb);
/*
* When in TCP_TIME_WAIT the sk is not a "struct sock" but
* "struct inet_timewait_sock" which is missing fields.
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 992396aa635c..da1ae0e13cb5 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1916,6 +1916,10 @@ retry:
goto retry;
}
+ if (!dev_validate_header(dev, skb->data, len)) {
+ err = -EINVAL;
+ goto out_unlock;
+ }
if (len > (dev->mtu + dev->hard_header_len + extra_len) &&
!packet_extra_vlan_len_allowed(dev, skb)) {
err = -EMSGSIZE;
@@ -2326,18 +2330,6 @@ static void tpacket_destruct_skb(struct sk_buff *skb)
sock_wfree(skb);
}
-static bool ll_header_truncated(const struct net_device *dev, int len)
-{
- /* net device doesn't like empty head */
- if (unlikely(len < dev->hard_header_len)) {
- net_warn_ratelimited("%s: packet size is too short (%d < %d)\n",
- current->comm, len, dev->hard_header_len);
- return true;
- }
-
- return false;
-}
-
static void tpacket_set_protocol(const struct net_device *dev,
struct sk_buff *skb)
{
@@ -2420,19 +2412,19 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
if (unlikely(err < 0))
return -EINVAL;
} else if (dev->hard_header_len) {
- if (ll_header_truncated(dev, tp_len))
- return -EINVAL;
+ int hdrlen = min_t(int, dev->hard_header_len, tp_len);
skb_push(skb, dev->hard_header_len);
- err = skb_store_bits(skb, 0, data,
- dev->hard_header_len);
+ err = skb_store_bits(skb, 0, data, hdrlen);
if (unlikely(err))
return err;
+ if (!dev_validate_header(dev, skb->data, hdrlen))
+ return -EINVAL;
if (!skb->protocol)
tpacket_set_protocol(dev, skb);
- data += dev->hard_header_len;
- to_write -= dev->hard_header_len;
+ data += hdrlen;
+ to_write -= hdrlen;
}
offset = offset_in_page(data);
@@ -2763,9 +2755,6 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
offset = dev_hard_header(skb, dev, ntohs(proto), addr, NULL, len);
if (unlikely(offset < 0))
goto out_free;
- } else {
- if (ll_header_truncated(dev, len))
- goto out_free;
}
/* Returns -EFAULT on error */
@@ -2773,6 +2762,12 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
if (err)
goto out_free;
+ if (sock->type == SOCK_RAW &&
+ !dev_validate_header(dev, skb->data, len)) {
+ err = -EINVAL;
+ goto out_free;
+ }
+
sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);
if (!gso_type && (len > dev->mtu + reserve + extra_len) &&
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index ec529121f38a..ce46f1c7f133 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -526,6 +526,8 @@ static int sctp_v6_cmp_addr(const union sctp_addr *addr1,
}
return 0;
}
+ if (addr1->v6.sin6_port != addr2->v6.sin6_port)
+ return 0;
if (!ipv6_addr_equal(&addr1->v6.sin6_addr, &addr2->v6.sin6_addr))
return 0;
/* If this is a linklocal address, compare the scope_id. */
diff --git a/net/socket.c b/net/socket.c
index b3d29ea2dd75..5211c40daecc 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2280,31 +2280,31 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
break;
}
-out_put:
- fput_light(sock->file, fput_needed);
-
if (err == 0)
- return datagrams;
+ goto out_put;
- if (datagrams != 0) {
+ if (datagrams == 0) {
+ datagrams = err;
+ goto out_put;
+ }
+
+ /*
+ * We may return less entries than requested (vlen) if the
+ * sock is non block and there aren't enough datagrams...
+ */
+ if (err != -EAGAIN) {
/*
- * We may return less entries than requested (vlen) if the
- * sock is non block and there aren't enough datagrams...
+ * ... or if recvmsg returns an error after we
+ * received some datagrams, where we record the
+ * error to return on the next call or if the
+ * app asks about it using getsockopt(SO_ERROR).
*/
- if (err != -EAGAIN) {
- /*
- * ... or if recvmsg returns an error after we
- * received some datagrams, where we record the
- * error to return on the next call or if the
- * app asks about it using getsockopt(SO_ERROR).
- */
- sock->sk->sk_err = -err;
- }
-
- return datagrams;
+ sock->sk->sk_err = -err;
}
+out_put:
+ fput_light(sock->file, fput_needed);
- return err;
+ return datagrams;
}
SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg,
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index b53246fb0412..e53003cf7703 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -673,7 +673,7 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq,
struct tipc_sock *tsk = tipc_sk(sk);
struct net *net = sock_net(sk);
struct tipc_msg *mhdr = &tsk->phdr;
- struct sk_buff_head *pktchain = &sk->sk_write_queue;
+ struct sk_buff_head pktchain;
struct iov_iter save = msg->msg_iter;
uint mtu;
int rc;
@@ -687,14 +687,16 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq,
msg_set_nameupper(mhdr, seq->upper);
msg_set_hdr_sz(mhdr, MCAST_H_SIZE);
+ skb_queue_head_init(&pktchain);
+
new_mtu:
mtu = tipc_bcast_get_mtu(net);
- rc = tipc_msg_build(mhdr, msg, 0, dsz, mtu, pktchain);
+ rc = tipc_msg_build(mhdr, msg, 0, dsz, mtu, &pktchain);
if (unlikely(rc < 0))
return rc;
do {
- rc = tipc_bcast_xmit(net, pktchain);
+ rc = tipc_bcast_xmit(net, &pktchain);
if (likely(!rc))
return dsz;
@@ -704,7 +706,7 @@ new_mtu:
if (!rc)
continue;
}
- __skb_queue_purge(pktchain);
+ __skb_queue_purge(&pktchain);
if (rc == -EMSGSIZE) {
msg->msg_iter = save;
goto new_mtu;
@@ -863,7 +865,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz)
struct net *net = sock_net(sk);
struct tipc_msg *mhdr = &tsk->phdr;
u32 dnode, dport;
- struct sk_buff_head *pktchain = &sk->sk_write_queue;
+ struct sk_buff_head pktchain;
struct sk_buff *skb;
struct tipc_name_seq *seq;
struct iov_iter save;
@@ -924,17 +926,18 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz)
msg_set_hdr_sz(mhdr, BASIC_H_SIZE);
}
+ skb_queue_head_init(&pktchain);
save = m->msg_iter;
new_mtu:
mtu = tipc_node_get_mtu(net, dnode, tsk->portid);
- rc = tipc_msg_build(mhdr, m, 0, dsz, mtu, pktchain);
+ rc = tipc_msg_build(mhdr, m, 0, dsz, mtu, &pktchain);
if (rc < 0)
return rc;
do {
- skb = skb_peek(pktchain);
+ skb = skb_peek(&pktchain);
TIPC_SKB_CB(skb)->wakeup_pending = tsk->link_cong;
- rc = tipc_node_xmit(net, pktchain, dnode, tsk->portid);
+ rc = tipc_node_xmit(net, &pktchain, dnode, tsk->portid);
if (likely(!rc)) {
if (sock->state != SS_READY)
sock->state = SS_CONNECTING;
@@ -946,7 +949,7 @@ new_mtu:
if (!rc)
continue;
}
- __skb_queue_purge(pktchain);
+ __skb_queue_purge(&pktchain);
if (rc == -EMSGSIZE) {
m->msg_iter = save;
goto new_mtu;
@@ -1016,7 +1019,7 @@ static int __tipc_send_stream(struct socket *sock, struct msghdr *m, size_t dsz)
struct net *net = sock_net(sk);
struct tipc_sock *tsk = tipc_sk(sk);
struct tipc_msg *mhdr = &tsk->phdr;
- struct sk_buff_head *pktchain = &sk->sk_write_queue;
+ struct sk_buff_head pktchain;
DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name);
u32 portid = tsk->portid;
int rc = -EINVAL;
@@ -1044,17 +1047,19 @@ static int __tipc_send_stream(struct socket *sock, struct msghdr *m, size_t dsz)
timeo = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT);
dnode = tsk_peer_node(tsk);
+ skb_queue_head_init(&pktchain);
next:
save = m->msg_iter;
mtu = tsk->max_pkt;
send = min_t(uint, dsz - sent, TIPC_MAX_USER_MSG_SIZE);
- rc = tipc_msg_build(mhdr, m, sent, send, mtu, pktchain);
+ rc = tipc_msg_build(mhdr, m, sent, send, mtu, &pktchain);
if (unlikely(rc < 0))
return rc;
+
do {
if (likely(!tsk_conn_cong(tsk))) {
- rc = tipc_node_xmit(net, pktchain, dnode, portid);
+ rc = tipc_node_xmit(net, &pktchain, dnode, portid);
if (likely(!rc)) {
tsk->sent_unacked++;
sent += send;
@@ -1063,7 +1068,7 @@ next:
goto next;
}
if (rc == -EMSGSIZE) {
- __skb_queue_purge(pktchain);
+ __skb_queue_purge(&pktchain);
tsk->max_pkt = tipc_node_get_mtu(net, dnode,
portid);
m->msg_iter = save;
@@ -1077,7 +1082,7 @@ next:
rc = tipc_wait_for_sndpkt(sock, &timeo);
} while (!rc);
- __skb_queue_purge(pktchain);
+ __skb_queue_purge(&pktchain);
return sent ? sent : rc;
}
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 7426ab13cede..6d3402434a63 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -730,6 +730,36 @@ int wiphy_register(struct wiphy *wiphy)
nl80211_send_reg_change_event(&request);
}
+ /* Check that nobody globally advertises any capabilities they do not
+ * advertise on all possible interface types.
+ */
+ if (wiphy->extended_capabilities_len &&
+ wiphy->num_iftype_ext_capab &&
+ wiphy->iftype_ext_capab) {
+ u8 supported_on_all, j;
+ const struct wiphy_iftype_ext_capab *capab;
+
+ capab = wiphy->iftype_ext_capab;
+ for (j = 0; j < wiphy->extended_capabilities_len; j++) {
+ if (capab[0].extended_capabilities_len > j)
+ supported_on_all =
+ capab[0].extended_capabilities[j];
+ else
+ supported_on_all = 0x00;
+ for (i = 1; i < wiphy->num_iftype_ext_capab; i++) {
+ if (j >= capab[i].extended_capabilities_len) {
+ supported_on_all = 0x00;
+ break;
+ }
+ supported_on_all &=
+ capab[i].extended_capabilities[j];
+ }
+ if (WARN_ON(wiphy->extended_capabilities[j] &
+ ~supported_on_all))
+ break;
+ }
+ }
+
rdev->wiphy.registered = true;
rtnl_unlock();
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index ad4b729262fd..30f54d1fc841 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1253,7 +1253,7 @@ nl80211_send_mgmt_stypes(struct sk_buff *msg,
struct nl80211_dump_wiphy_state {
s64 filter_wiphy;
long start;
- long split_start, band_start, chan_start;
+ long split_start, band_start, chan_start, capa_start;
bool split;
};
@@ -1731,6 +1731,47 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
rdev->wiphy.ext_features))
goto nla_put_failure;
+ state->split_start++;
+ break;
+ case 13:
+ if (rdev->wiphy.num_iftype_ext_capab &&
+ rdev->wiphy.iftype_ext_capab) {
+ struct nlattr *nested_ext_capab, *nested;
+
+ nested = nla_nest_start(msg,
+ NL80211_ATTR_IFTYPE_EXT_CAPA);
+ if (!nested)
+ goto nla_put_failure;
+
+ for (i = state->capa_start;
+ i < rdev->wiphy.num_iftype_ext_capab; i++) {
+ const struct wiphy_iftype_ext_capab *capab;
+
+ capab = &rdev->wiphy.iftype_ext_capab[i];
+
+ nested_ext_capab = nla_nest_start(msg, i);
+ if (!nested_ext_capab ||
+ nla_put_u32(msg, NL80211_ATTR_IFTYPE,
+ capab->iftype) ||
+ nla_put(msg, NL80211_ATTR_EXT_CAPA,
+ capab->extended_capabilities_len,
+ capab->extended_capabilities) ||
+ nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
+ capab->extended_capabilities_len,
+ capab->extended_capabilities_mask))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, nested_ext_capab);
+ if (state->split)
+ break;
+ }
+ nla_nest_end(msg, nested);
+ if (i < rdev->wiphy.num_iftype_ext_capab) {
+ state->capa_start = i + 1;
+ break;
+ }
+ }
+
/* done */
state->split_start = 0;
break;
diff --git a/scripts/coccinelle/iterators/use_after_iter.cocci b/scripts/coccinelle/iterators/use_after_iter.cocci
index f085f5968c52..ce8cc9c006e5 100644
--- a/scripts/coccinelle/iterators/use_after_iter.cocci
+++ b/scripts/coccinelle/iterators/use_after_iter.cocci
@@ -123,7 +123,7 @@ list_remove_head(x,c,...)
|
sizeof(<+...c...+>)
|
-&c->member
+ &c->member
|
c = E
|
diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index d79cba4ce3eb..ebced77deb9c 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -96,13 +96,15 @@ savedefconfig: $(obj)/conf
defconfig: $(obj)/conf
ifeq ($(KBUILD_DEFCONFIG),)
$< $(silent) --defconfig $(Kconfig)
-else ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG)),)
+else
+ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG)),)
@$(kecho) "*** Default configuration is based on '$(KBUILD_DEFCONFIG)'"
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig)
else
@$(kecho) "*** Default configuration is based on target '$(KBUILD_DEFCONFIG)'"
$(Q)$(MAKE) -f $(srctree)/Makefile $(KBUILD_DEFCONFIG)
endif
+endif
%_defconfig: $(obj)/conf
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
diff --git a/scripts/package/mkspec b/scripts/package/mkspec
index 71004daefe31..fe44d68e9344 100755
--- a/scripts/package/mkspec
+++ b/scripts/package/mkspec
@@ -131,11 +131,11 @@ echo 'rm -rf $RPM_BUILD_ROOT'
echo ""
echo "%post"
echo "if [ -x /sbin/installkernel -a -r /boot/vmlinuz-$KERNELRELEASE -a -r /boot/System.map-$KERNELRELEASE ]; then"
-echo "cp /boot/vmlinuz-$KERNELRELEASE /boot/vmlinuz-$KERNELRELEASE-rpm"
-echo "cp /boot/System.map-$KERNELRELEASE /boot/System.map-$KERNELRELEASE-rpm"
+echo "cp /boot/vmlinuz-$KERNELRELEASE /boot/.vmlinuz-$KERNELRELEASE-rpm"
+echo "cp /boot/System.map-$KERNELRELEASE /boot/.System.map-$KERNELRELEASE-rpm"
echo "rm -f /boot/vmlinuz-$KERNELRELEASE /boot/System.map-$KERNELRELEASE"
-echo "/sbin/installkernel $KERNELRELEASE /boot/vmlinuz-$KERNELRELEASE-rpm /boot/System.map-$KERNELRELEASE-rpm"
-echo "rm -f /boot/vmlinuz-$KERNELRELEASE-rpm /boot/System.map-$KERNELRELEASE-rpm"
+echo "/sbin/installkernel $KERNELRELEASE /boot/.vmlinuz-$KERNELRELEASE-rpm /boot/.System.map-$KERNELRELEASE-rpm"
+echo "rm -f /boot/.vmlinuz-$KERNELRELEASE-rpm /boot/.System.map-$KERNELRELEASE-rpm"
echo "fi"
echo ""
echo "%files"
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index a41e481e99a3..328b4a43f09a 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -325,7 +325,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
char name[16];
snd_pcm_debug_name(substream, name, sizeof(name));
pcm_err(substream->pcm,
- "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
+ "invalid position: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
name, pos, runtime->buffer_size,
runtime->period_size);
}
diff --git a/sound/core/timer.c b/sound/core/timer.c
index f24c9fccf008..b982d1b089bd 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -1051,8 +1051,8 @@ static int snd_timer_s_start(struct snd_timer * timer)
njiff += timer->sticks - priv->correction;
priv->correction = 0;
}
- priv->last_expires = priv->tlist.expires = njiff;
- add_timer(&priv->tlist);
+ priv->last_expires = njiff;
+ mod_timer(&priv->tlist, njiff);
return 0;
}
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index c1c855a6c0af..a47e8ae0eb30 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -174,8 +174,12 @@ static void cs_automute(struct hda_codec *codec)
snd_hda_gen_update_outputs(codec);
if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) {
- spec->gpio_data = spec->gen.hp_jack_present ?
- spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
+ if (spec->gen.automute_speaker)
+ spec->gpio_data = spec->gen.hp_jack_present ?
+ spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
+ else
+ spec->gpio_data =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
snd_hda_codec_write(codec, 0x01, 0,
AC_VERB_SET_GPIO_DATA, spec->gpio_data);
}
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index ef198903c0c3..600af5878e75 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -204,8 +204,13 @@ static void cx_auto_reboot_notify(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- if (codec->core.vendor_id != 0x14f150f2)
+ switch (codec->core.vendor_id) {
+ case 0x14f150f2: /* CX20722 */
+ case 0x14f150f4: /* CX20724 */
+ break;
+ default:
return;
+ }
/* Turn the CX20722 codec into D3 to avoid spurious noises
from the internal speaker during (and after) reboot */
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 70c945603379..f7bcd8dbac14 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -2353,6 +2353,10 @@ static void intel_pin_eld_notify(void *audio_ptr, int port)
struct hda_codec *codec = audio_ptr;
int pin_nid = port + 0x04;
+ /* we assume only from port-B to port-D */
+ if (port < 1 || port > 3)
+ return;
+
/* skip notification during system suspend (but not in runtime PM);
* the state will be updated at resume
*/
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index c2430b36e1ce..1402ba954b3d 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -3801,6 +3801,10 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
static void alc_headset_mode_default(struct hda_codec *codec)
{
+ static struct coef_fw coef0225[] = {
+ UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10),
+ {}
+ };
static struct coef_fw coef0255[] = {
WRITE_COEF(0x45, 0xc089),
WRITE_COEF(0x45, 0xc489),
@@ -3842,6 +3846,9 @@ static void alc_headset_mode_default(struct hda_codec *codec)
};
switch (codec->core.vendor_id) {
+ case 0x10ec0225:
+ alc_process_coef_fw(codec, coef0225);
+ break;
case 0x10ec0255:
case 0x10ec0256:
alc_process_coef_fw(codec, coef0255);
@@ -4750,6 +4757,10 @@ enum {
ALC293_FIXUP_LENOVO_SPK_NOISE,
ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
ALC255_FIXUP_DELL_SPK_NOISE,
+ ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC280_FIXUP_HP_HEADSET_MIC,
+ ALC221_FIXUP_HP_FRONT_MIC,
+ ALC292_FIXUP_TPT460,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -5375,6 +5386,36 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
},
+ [ALC225_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Disable pass-through path for FRONT 14h */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x36 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x57d7 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC280_FIXUP_HP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC,
+ },
+ [ALC221_FIXUP_HP_FRONT_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x02a19020 }, /* Front Mic */
+ { }
+ },
+ },
+ [ALC292_FIXUP_TPT460] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_tpt440_dock,
+ .chained = true,
+ .chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -5479,6 +5520,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x2335, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x2336, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC),
SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -5527,8 +5570,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x2218, "Thinkpad X1 Carbon 2nd", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK),
- SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
+ SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC292_FIXUP_TPT460),
SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
+ SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
@@ -5621,6 +5665,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"},
{.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"},
{.id = ALC292_FIXUP_TPT440, .name = "tpt440"},
+ {.id = ALC292_FIXUP_TPT460, .name = "tpt460"},
{}
};
#define ALC225_STANDARD_PINS \
@@ -5647,10 +5692,10 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{0x21, 0x03211020}
static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
- SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC225_STANDARD_PINS,
{0x14, 0x901701a0}),
- SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC225_STANDARD_PINS,
{0x14, 0x901701b0}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
@@ -6378,6 +6423,7 @@ enum {
ALC668_FIXUP_AUTO_MUTE,
ALC668_FIXUP_DELL_DISABLE_AAMIX,
ALC668_FIXUP_DELL_XPS13,
+ ALC662_FIXUP_ASUS_Nx50,
};
static const struct hda_fixup alc662_fixups[] = {
@@ -6618,6 +6664,12 @@ static const struct hda_fixup alc662_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_bass_chmap,
},
+ [ALC662_FIXUP_ASUS_Nx50] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_auto_mute_via_amp,
+ .chained = true,
+ .chain_id = ALC662_FIXUP_BASS_1A
+ },
};
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
@@ -6640,8 +6692,9 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
- SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_BASS_1A),
+ SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50),
SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A),
+ SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50),
SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16),
SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16),
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 42bcbac801a3..ccdab29a8b66 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -2879,6 +2879,7 @@ static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
static struct snd_pci_quirk intel8x0_clock_list[] = {
SND_PCI_QUIRK(0x0e11, 0x008a, "AD1885", 41000),
+ SND_PCI_QUIRK(0x1014, 0x0581, "AD1981B", 48000),
SND_PCI_QUIRK(0x1028, 0x00be, "AD1885", 44100),
SND_PCI_QUIRK(0x1028, 0x0177, "AD1980", 48000),
SND_PCI_QUIRK(0x1028, 0x01ad, "AD1981B", 48000),
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 871c2980f4f7..6fc326421108 100755
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -762,6 +762,13 @@ config SND_SOC_WCD_MBHC
tristate
default y if (SND_SOC_MSM8909_WCD=y || SND_SOC_MSM8X16_WCD=y || SND_SOC_WCD9335=y) && SND_SOC_MDMCALIFORNIUM!=y
+config SND_SOC_WCD_DSP_MGR
+ tristate
+
+config SND_SOC_WCD_SPI
+ depends on CONFIG_SPI
+ tristate
+
config SND_SOC_WL1273
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index ed2e49ddf432..4b79b277ce29 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -143,6 +143,9 @@ snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o
snd-soc-msm8952-wcd-objs := msm8x16-wcd.o msm8x16-wcd-tables.o
snd-soc-wsa881x-analog-objs := wsa881x-analog.o wsa881x-tables-analog.o
snd-soc-wsa881x-analog-objs += wsa881x-regmap-analog.o wsa881x-irq.o
+snd-soc-wcd-dsp-utils-objs := wcd-dsp-utils.o
+snd-soc-wcd-dsp-mgr-objs := wcd-dsp-mgr.o
+snd-soc-wcd-spi-objs := wcd-spi.o
snd-soc-wl1273-objs := wl1273.o
snd-soc-wm-adsp-objs := wm_adsp.o
snd-soc-wm0010-objs := wm0010.o
@@ -351,6 +354,8 @@ obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o
obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o
obj-$(CONFIG_SND_SOC_WSA881X_ANALOG) += snd-soc-wsa881x-analog.o
obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
+obj-$(CONFIG_SND_SOC_WCD_DSP_MGR) += snd-soc-wcd-dsp-mgr.o snd-soc-wcd-dsp-utils.o
+obj-$(CONFIG_SND_SOC_WCD_SPI) += snd-soc-wcd-spi.o
obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o
obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o
diff --git a/sound/soc/codecs/audio-ext-clk.c b/sound/soc/codecs/audio-ext-clk.c
index 7faabcfb1db1..c422267dbf2c 100755
--- a/sound/soc/codecs/audio-ext-clk.c
+++ b/sound/soc/codecs/audio-ext-clk.c
@@ -91,6 +91,10 @@ static int audio_ext_clk2_prepare(struct clk *clk)
struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
int ret;
+
+ if (!pnctrl_info->pinctrl || !pnctrl_info->active)
+ return 0;
+
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->active);
if (ret) {
@@ -115,6 +119,9 @@ static void audio_ext_clk2_unprepare(struct clk *clk)
struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
int ret;
+ if (!pnctrl_info->pinctrl || !pnctrl_info->sleep)
+ return;
+
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->sleep);
if (ret)
diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c
new file mode 100644
index 000000000000..69246ac9cc87
--- /dev/null
+++ b/sound/soc/codecs/wcd-dsp-mgr.c
@@ -0,0 +1,803 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/stringify.h>
+#include <linux/of.h>
+#include <linux/component.h>
+#include <sound/wcd-dsp-mgr.h>
+#include "wcd-dsp-utils.h"
+
+/* Forward declarations */
+static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type);
+
+/* Component related macros */
+#define WDSP_GET_COMPONENT(wdsp, x) (&(wdsp->cmpnts[x]))
+#define WDSP_GET_CMPNT_TYPE_STR(x) wdsp_get_cmpnt_type_string(x)
+
+/*
+ * These #defines indicate the bit number in status field
+ * for each of the status. If bit is set, it indicates
+ * the status as done, else if bit is not set, it indicates
+ * the status is either failed or not done.
+ */
+#define WDSP_STATUS_INITIALIZED BIT(0)
+#define WDSP_STATUS_CODE_DLOADED BIT(1)
+#define WDSP_STATUS_DATA_DLOADED BIT(2)
+#define WDSP_STATUS_BOOTED BIT(3)
+
+/* Helper macros for printing wdsp messages */
+#define WDSP_ERR(wdsp, fmt, ...) \
+ dev_err(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
+#define WDSP_DBG(wdsp, fmt, ...) \
+ dev_dbg(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
+
+/* Helper macros for locking */
+#define WDSP_MGR_MUTEX_LOCK(wdsp, lock) \
+{ \
+ WDSP_DBG(wdsp, "mutex_lock(%s)", \
+ __stringify_1(lock)); \
+ mutex_lock(&lock); \
+}
+
+#define WDSP_MGR_MUTEX_UNLOCK(wdsp, lock) \
+{ \
+ WDSP_DBG(wdsp, "mutex_unlock(%s)", \
+ __stringify_1(lock)); \
+ mutex_unlock(&lock); \
+}
+
+/* Helper macros for using status mask */
+#define WDSP_SET_STATUS(wdsp, state) \
+{ \
+ wdsp->status |= state; \
+ WDSP_DBG(wdsp, "set 0x%lx, new_state = 0x%x", \
+ state, wdsp->status); \
+}
+
+#define WDSP_CLEAR_STATUS(wdsp, state) \
+{ \
+ wdsp->status &= (~state); \
+ WDSP_DBG(wdsp, "clear 0x%lx, new_state = 0x%x", \
+ state, wdsp->status); \
+}
+
+#define WDSP_STATUS_IS_SET(wdsp, state) (wdsp->status & state)
+
+struct wdsp_cmpnt {
+
+ /* OF node of the phandle */
+ struct device_node *np;
+
+ /*
+ * Child component's dev_name, should be set in DT for the child's
+ * phandle if child's dev->of_node does not match the phandle->of_node
+ */
+ const char *cdev_name;
+
+ /* Child component's device node */
+ struct device *cdev;
+
+ /* Private data that component may want back on callbacks */
+ void *priv_data;
+
+ /* Child ops */
+ struct wdsp_cmpnt_ops *ops;
+};
+
+struct wdsp_mgr_priv {
+
+ /* Manager driver's struct device pointer */
+ struct device *mdev;
+
+ /* Match struct for component framework */
+ struct component_match *match;
+
+ /* Manager's ops/function callbacks */
+ struct wdsp_mgr_ops *ops;
+
+ /* Array to store information for all expected components */
+ struct wdsp_cmpnt cmpnts[WDSP_CMPNT_TYPE_MAX];
+
+ /* The filename of image to be downloaded */
+ const char *img_fname;
+
+ /* Keeps track of current state of manager driver */
+ u32 status;
+
+ /* Work to load the firmware image after component binding */
+ struct work_struct load_fw_work;
+
+ /* List of segments in image to be downloaded */
+ struct list_head *seg_list;
+
+ /* Base address of the image in memory */
+ u32 base_addr;
+
+ /* Instances using dsp */
+ int dsp_users;
+
+ /* Lock for serializing ops called by components */
+ struct mutex api_mutex;
+};
+
+static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type)
+{
+ switch (type) {
+ case WDSP_CMPNT_CONTROL:
+ return "control";
+ case WDSP_CMPNT_IPC:
+ return "ipc";
+ case WDSP_CMPNT_TRANSPORT:
+ return "transport";
+ default:
+ pr_err("%s: Invalid component type %d\n",
+ __func__, type);
+ return "Invalid";
+ }
+}
+
+static void wdsp_broadcast_event_upseq(struct wdsp_mgr_priv *wdsp,
+ enum wdsp_event_type event,
+ void *data)
+{
+ struct wdsp_cmpnt *cmpnt;
+ int i;
+
+ for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) {
+ cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+ if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler)
+ cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data,
+ event, data);
+ }
+}
+
+static void wdsp_broadcast_event_downseq(struct wdsp_mgr_priv *wdsp,
+ enum wdsp_event_type event,
+ void *data)
+{
+ struct wdsp_cmpnt *cmpnt;
+ int i;
+
+ for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) {
+ cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+ if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler)
+ cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data,
+ event, data);
+ }
+}
+
+static int wdsp_unicast_event(struct wdsp_mgr_priv *wdsp,
+ enum wdsp_cmpnt_type type,
+ enum wdsp_event_type event,
+ void *data)
+{
+ struct wdsp_cmpnt *cmpnt;
+ int ret;
+
+ cmpnt = WDSP_GET_COMPONENT(wdsp, type);
+ if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) {
+ ret = cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data,
+ event, data);
+ } else {
+ WDSP_ERR(wdsp, "not valid event_handler for %s",
+ WDSP_GET_CMPNT_TYPE_STR(type));
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int wdsp_init_components(struct wdsp_mgr_priv *wdsp)
+{
+ struct wdsp_cmpnt *cmpnt;
+ int fail_idx = WDSP_CMPNT_TYPE_MAX;
+ int i, ret = 0;
+
+ for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) {
+
+ cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+
+ /* Init is allowed to be NULL */
+ if (!cmpnt->ops || !cmpnt->ops->init)
+ continue;
+ ret = cmpnt->ops->init(cmpnt->cdev, cmpnt->priv_data);
+ if (ret) {
+ WDSP_ERR(wdsp, "Init failed (%d) for component %s",
+ ret, WDSP_GET_CMPNT_TYPE_STR(i));
+ fail_idx = i;
+ break;
+ }
+ }
+
+ if (fail_idx < WDSP_CMPNT_TYPE_MAX) {
+ /* Undo init for already initialized components */
+ for (i = fail_idx - 1; i >= 0; i--) {
+ struct wdsp_cmpnt *cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+
+ if (cmpnt->ops && cmpnt->ops->deinit)
+ cmpnt->ops->deinit(cmpnt->cdev,
+ cmpnt->priv_data);
+ }
+ }
+
+ return ret;
+}
+
+static int wdsp_load_each_segment(struct wdsp_mgr_priv *wdsp,
+ struct wdsp_img_segment *seg)
+{
+ struct wdsp_img_section img_section;
+ int ret;
+
+ WDSP_DBG(wdsp,
+ "base_addr 0x%x, split_fname %s, load_addr 0x%x, size 0x%zx",
+ wdsp->base_addr, seg->split_fname, seg->load_addr, seg->size);
+
+ if (seg->load_addr < wdsp->base_addr) {
+ WDSP_ERR(wdsp, "Invalid addr 0x%x, base_addr = 0x%x",
+ seg->load_addr, wdsp->base_addr);
+ return -EINVAL;
+ }
+
+ img_section.addr = seg->load_addr - wdsp->base_addr;
+ img_section.size = seg->size;
+ img_section.data = seg->data;
+
+ ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT,
+ WDSP_EVENT_DLOAD_SECTION,
+ &img_section);
+ if (IS_ERR_VALUE(ret))
+ WDSP_ERR(wdsp,
+ "Failed, err = %d for base_addr = 0x%x split_fname = %s, load_addr = 0x%x, size = 0x%zx",
+ ret, wdsp->base_addr, seg->split_fname,
+ seg->load_addr, seg->size);
+ return ret;
+}
+
+static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp,
+ unsigned int type)
+{
+ struct wdsp_cmpnt *ctl;
+ struct wdsp_img_segment *seg = NULL;
+ enum wdsp_event_type pre, post;
+ int ret;
+
+ ctl = WDSP_GET_COMPONENT(wdsp, WDSP_CMPNT_CONTROL);
+
+ if (type == WDSP_ELF_FLAG_RE) {
+ pre = WDSP_EVENT_PRE_DLOAD_CODE;
+ post = WDSP_EVENT_POST_DLOAD_CODE;
+ } else if (type == WDSP_ELF_FLAG_WRITE) {
+ pre = WDSP_EVENT_PRE_DLOAD_DATA;
+ post = WDSP_EVENT_POST_DLOAD_DATA;
+ } else {
+ WDSP_ERR(wdsp, "Invalid type %u", type);
+ return -EINVAL;
+ }
+
+ ret = wdsp_get_segment_list(ctl->cdev, wdsp->img_fname,
+ type, wdsp->seg_list, &wdsp->base_addr);
+ if (IS_ERR_VALUE(ret) ||
+ list_empty(wdsp->seg_list)) {
+ WDSP_ERR(wdsp, "Error %d to get image segments for type %d",
+ ret, type);
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_DLOAD_FAILED,
+ NULL);
+ goto done;
+ }
+
+ /* Notify all components that image is about to be downloaded */
+ wdsp_broadcast_event_upseq(wdsp, pre, NULL);
+
+ /* Go through the list of segments and download one by one */
+ list_for_each_entry(seg, wdsp->seg_list, list) {
+ ret = wdsp_load_each_segment(wdsp, seg);
+ if (IS_ERR_VALUE(ret)) {
+ wdsp_broadcast_event_downseq(wdsp,
+ WDSP_EVENT_DLOAD_FAILED,
+ NULL);
+ goto dload_error;
+ }
+ }
+
+ /* Notify all components that image is downloaded */
+ wdsp_broadcast_event_downseq(wdsp, post, NULL);
+
+dload_error:
+ wdsp_flush_segment_list(wdsp->seg_list);
+done:
+ return ret;
+}
+
+static void wdsp_load_fw_image(struct work_struct *work)
+{
+ struct wdsp_mgr_priv *wdsp;
+ struct wdsp_cmpnt *cmpnt;
+ int ret, idx;
+
+ wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work);
+ if (!wdsp) {
+ pr_err("%s: Invalid private_data\n", __func__);
+ goto done;
+ }
+
+ /* Initialize the components first */
+ ret = wdsp_init_components(wdsp);
+ if (IS_ERR_VALUE(ret))
+ goto done;
+
+ /* Set init done status */
+ WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
+
+ /* Download the read-execute sections of image */
+ ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_RE);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Error %d to download code sections", ret);
+ for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) {
+ cmpnt = WDSP_GET_COMPONENT(wdsp, idx);
+ if (cmpnt->ops && cmpnt->ops->deinit)
+ cmpnt->ops->deinit(cmpnt->cdev,
+ cmpnt->priv_data);
+ }
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
+ }
+
+ WDSP_SET_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED);
+done:
+ return;
+}
+
+static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp)
+{
+ int ret;
+
+ /* Make sure wdsp is in good state */
+ if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_CODE_DLOADED)) {
+ WDSP_ERR(wdsp, "WDSP in invalid state 0x%x", wdsp->status);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Download the read-write sections of image */
+ ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_WRITE);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Data section download failed, err = %d", ret);
+ goto done;
+ }
+
+ WDSP_SET_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
+
+ wdsp_broadcast_event_upseq(wdsp, WDSP_EVENT_PRE_BOOTUP, NULL);
+
+ ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
+ WDSP_EVENT_DO_BOOT, NULL);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Failed to boot dsp, err = %d", ret);
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
+ goto done;
+ }
+
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_BOOTUP, NULL);
+ WDSP_SET_STATUS(wdsp, WDSP_STATUS_BOOTED);
+done:
+ return ret;
+}
+
+static int wdsp_disable_dsp(struct wdsp_mgr_priv *wdsp)
+{
+ int ret;
+
+ /* Make sure wdsp is in good state */
+ if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) {
+ WDSP_ERR(wdsp, "wdsp in invalid state 0x%x", wdsp->status);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN, NULL);
+ ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
+ WDSP_EVENT_DO_SHUTDOWN, NULL);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Failed to shutdown dsp, err = %d", ret);
+ goto done;
+ }
+
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, NULL);
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED);
+
+ /* Data sections are to be downloaded per boot */
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
+done:
+ return ret;
+}
+
+static int wdsp_register_cmpnt_ops(struct device *wdsp_dev,
+ struct device *cdev,
+ void *priv_data,
+ struct wdsp_cmpnt_ops *ops)
+{
+ struct wdsp_mgr_priv *wdsp;
+ struct wdsp_cmpnt *cmpnt;
+ int i, ret;
+
+ if (!wdsp_dev || !cdev || !ops)
+ return -EINVAL;
+
+ wdsp = dev_get_drvdata(wdsp_dev);
+
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex);
+
+ for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) {
+ cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+ if ((cdev->of_node && cdev->of_node == cmpnt->np) ||
+ (cmpnt->cdev_name &&
+ !strcmp(dev_name(cdev), cmpnt->cdev_name))) {
+ break;
+ }
+ }
+
+ if (i == WDSP_CMPNT_TYPE_MAX) {
+ WDSP_ERR(wdsp, "Failed to register component dev %s",
+ dev_name(cdev));
+ ret = -EINVAL;
+ goto done;
+ }
+
+ cmpnt->cdev = cdev;
+ cmpnt->ops = ops;
+ cmpnt->priv_data = priv_data;
+done:
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex);
+ return 0;
+}
+
+static struct device *wdsp_get_dev_for_cmpnt(struct device *wdsp_dev,
+ enum wdsp_cmpnt_type type)
+{
+ struct wdsp_mgr_priv *wdsp;
+ struct wdsp_cmpnt *cmpnt;
+
+ if (!wdsp_dev || type >= WDSP_CMPNT_TYPE_MAX)
+ return NULL;
+
+ wdsp = dev_get_drvdata(wdsp_dev);
+ cmpnt = WDSP_GET_COMPONENT(wdsp, type);
+
+ return cmpnt->cdev;
+}
+
+static int wdsp_intr_handler(struct device *wdsp_dev,
+ enum wdsp_intr intr)
+{
+ struct wdsp_mgr_priv *wdsp;
+ int ret;
+
+ if (!wdsp_dev)
+ return -EINVAL;
+
+ wdsp = dev_get_drvdata(wdsp_dev);
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex);
+
+ switch (intr) {
+ case WDSP_IPC1_INTR:
+ ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_IPC,
+ WDSP_EVENT_IPC1_INTR, NULL);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (IS_ERR_VALUE(ret))
+ WDSP_ERR(wdsp, "handling intr %d failed with error %d",
+ intr, ret);
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex);
+
+ return ret;
+}
+
+static int wdsp_vote_for_dsp(struct device *wdsp_dev,
+ bool vote)
+{
+ struct wdsp_mgr_priv *wdsp;
+ int ret = 0;
+
+ if (!wdsp_dev)
+ return -EINVAL;
+
+ wdsp = dev_get_drvdata(wdsp_dev);
+
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex);
+ WDSP_DBG(wdsp, "request %s, current users = %d",
+ vote ? "enable" : "disable", wdsp->dsp_users);
+
+ if (vote) {
+ wdsp->dsp_users++;
+ if (wdsp->dsp_users == 1)
+ ret = wdsp_enable_dsp(wdsp);
+ } else {
+ if (wdsp->dsp_users == 0)
+ goto done;
+
+ wdsp->dsp_users--;
+ if (wdsp->dsp_users == 0)
+ ret = wdsp_disable_dsp(wdsp);
+ }
+
+ if (IS_ERR_VALUE(ret))
+ WDSP_DBG(wdsp, "wdsp %s failed, err = %d",
+ vote ? "enable" : "disable", ret);
+
+done:
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex);
+ return ret;
+}
+
+static int wdsp_suspend(struct device *wdsp_dev)
+{
+ return 0;
+}
+
+static int wdsp_resume(struct device *wdsp_dev)
+{
+ return 0;
+}
+
+static struct wdsp_mgr_ops wdsp_ops = {
+ .register_cmpnt_ops = wdsp_register_cmpnt_ops,
+ .get_dev_for_cmpnt = wdsp_get_dev_for_cmpnt,
+ .intr_handler = wdsp_intr_handler,
+ .vote_for_dsp = wdsp_vote_for_dsp,
+ .suspend = wdsp_suspend,
+ .resume = wdsp_resume,
+};
+
+static int wdsp_mgr_compare_of(struct device *dev, void *data)
+{
+ struct wdsp_cmpnt *cmpnt = data;
+
+ /*
+ * First try to match based on of_node, if of_node is not
+ * present, try to match on the dev_name
+ */
+ return ((dev->of_node && dev->of_node == cmpnt->np) ||
+ (cmpnt->cdev_name &&
+ !strcmp(dev_name(dev), cmpnt->cdev_name)));
+}
+
+static int wdsp_mgr_bind(struct device *dev)
+{
+ struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev);
+ struct wdsp_cmpnt *cmpnt;
+ int ret, idx;
+
+ wdsp->ops = &wdsp_ops;
+
+ ret = component_bind_all(dev, wdsp->ops);
+ if (IS_ERR_VALUE(ret))
+ WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret);
+
+ /* Make sure all components registered ops */
+ for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) {
+ cmpnt = WDSP_GET_COMPONENT(wdsp, idx);
+ if (!cmpnt->cdev || !cmpnt->ops) {
+ WDSP_ERR(wdsp, "%s did not register ops\n",
+ WDSP_GET_CMPNT_TYPE_STR(idx));
+ ret = -EINVAL;
+ component_unbind_all(dev, wdsp->ops);
+ break;
+ }
+ }
+
+ /* Schedule the work to download image if binding was successful. */
+ if (!ret)
+ schedule_work(&wdsp->load_fw_work);
+
+ return ret;
+}
+
+static void wdsp_mgr_unbind(struct device *dev)
+{
+ struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev);
+ struct wdsp_cmpnt *cmpnt;
+ int idx;
+
+ component_unbind_all(dev, wdsp->ops);
+
+ /* Clear all status bits */
+ wdsp->status = 0x00;
+
+ /* clean up the components */
+ for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) {
+ cmpnt = WDSP_GET_COMPONENT(wdsp, idx);
+ cmpnt->cdev = NULL;
+ cmpnt->ops = NULL;
+ cmpnt->priv_data = NULL;
+ }
+}
+
+static const struct component_master_ops wdsp_master_ops = {
+ .bind = wdsp_mgr_bind,
+ .unbind = wdsp_mgr_unbind,
+};
+
+static void *wdsp_mgr_parse_phandle(struct wdsp_mgr_priv *wdsp,
+ int index)
+{
+ struct device *mdev = wdsp->mdev;
+ struct device_node *np;
+ struct wdsp_cmpnt *cmpnt = NULL;
+ struct of_phandle_args pargs;
+ u32 value;
+ int ret;
+
+ ret = of_parse_phandle_with_fixed_args(mdev->of_node,
+ "qcom,wdsp-components", 1,
+ index, &pargs);
+ if (ret) {
+ WDSP_ERR(wdsp, "parse_phandle at index %d failed %d",
+ index, ret);
+ return NULL;
+ }
+
+ np = pargs.np;
+ value = pargs.args[0];
+
+ if (value >= WDSP_CMPNT_TYPE_MAX) {
+ WDSP_ERR(wdsp, "invalid phandle_arg to of_node %s", np->name);
+ goto done;
+ }
+
+ cmpnt = WDSP_GET_COMPONENT(wdsp, value);
+ if (cmpnt->np || cmpnt->cdev_name) {
+ WDSP_ERR(wdsp, "cmpnt %d already added", value);
+ cmpnt = NULL;
+ goto done;
+ }
+
+ cmpnt->np = np;
+ of_property_read_string(np, "qcom,wdsp-cmpnt-dev-name",
+ &cmpnt->cdev_name);
+done:
+ of_node_put(np);
+ return cmpnt;
+}
+
+static int wdsp_mgr_parse_dt_entries(struct wdsp_mgr_priv *wdsp)
+{
+ struct device *dev = wdsp->mdev;
+ void *match_data;
+ int ph_idx, ret;
+
+ ret = of_property_read_string(dev->of_node, "qcom,img-filename",
+ &wdsp->img_fname);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Reading property %s failed, error = %d",
+ "qcom,img-filename", ret);
+ return ret;
+ }
+
+ ret = of_count_phandle_with_args(dev->of_node,
+ "qcom,wdsp-components",
+ NULL);
+ if (ret == -ENOENT) {
+ WDSP_ERR(wdsp, "Property %s not defined in DT",
+ "qcom,wdsp-components");
+ goto done;
+ } else if (ret != WDSP_CMPNT_TYPE_MAX * 2) {
+ WDSP_ERR(wdsp, "Invalid phandle + arg count %d, expected %d",
+ ret, WDSP_CMPNT_TYPE_MAX * 2);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = 0;
+
+ for (ph_idx = 0; ph_idx < WDSP_CMPNT_TYPE_MAX; ph_idx++) {
+
+ match_data = wdsp_mgr_parse_phandle(wdsp, ph_idx);
+ if (!match_data) {
+ WDSP_ERR(wdsp, "component not found at idx %d", ph_idx);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ component_match_add(dev, &wdsp->match,
+ wdsp_mgr_compare_of, match_data);
+ }
+
+done:
+ return ret;
+}
+
+static int wdsp_mgr_probe(struct platform_device *pdev)
+{
+ struct wdsp_mgr_priv *wdsp;
+ struct device *mdev = &pdev->dev;
+ int ret;
+
+ wdsp = devm_kzalloc(mdev, sizeof(*wdsp), GFP_KERNEL);
+ if (!wdsp)
+ return -ENOMEM;
+ wdsp->mdev = mdev;
+ wdsp->seg_list = devm_kzalloc(mdev, sizeof(struct list_head),
+ GFP_KERNEL);
+ if (!wdsp->seg_list) {
+ devm_kfree(mdev, wdsp);
+ return -ENOMEM;
+ }
+
+ ret = wdsp_mgr_parse_dt_entries(wdsp);
+ if (ret)
+ goto err_dt_parse;
+
+ INIT_WORK(&wdsp->load_fw_work, wdsp_load_fw_image);
+ INIT_LIST_HEAD(wdsp->seg_list);
+ mutex_init(&wdsp->api_mutex);
+ dev_set_drvdata(mdev, wdsp);
+
+ ret = component_master_add_with_match(mdev, &wdsp_master_ops,
+ wdsp->match);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Failed to add master, err = %d", ret);
+ goto err_master_add;
+ }
+
+ return 0;
+
+err_master_add:
+ mutex_destroy(&wdsp->api_mutex);
+err_dt_parse:
+ devm_kfree(mdev, wdsp->seg_list);
+ devm_kfree(mdev, wdsp);
+ dev_set_drvdata(mdev, NULL);
+
+ return ret;
+}
+
+static int wdsp_mgr_remove(struct platform_device *pdev)
+{
+ struct device *mdev = &pdev->dev;
+ struct wdsp_mgr_priv *wdsp = dev_get_drvdata(mdev);
+
+ component_master_del(mdev, &wdsp_master_ops);
+
+ mutex_destroy(&wdsp->api_mutex);
+ devm_kfree(mdev, wdsp->seg_list);
+ devm_kfree(mdev, wdsp);
+ dev_set_drvdata(mdev, NULL);
+
+ return 0;
+};
+
+static const struct of_device_id wdsp_mgr_dt_match[] = {
+ {.compatible = "qcom,wcd-dsp-mgr" },
+ { }
+};
+
+static struct platform_driver wdsp_mgr_driver = {
+ .driver = {
+ .name = "wcd-dsp-mgr",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(wdsp_mgr_dt_match),
+ },
+ .probe = wdsp_mgr_probe,
+ .remove = wdsp_mgr_remove,
+};
+module_platform_driver(wdsp_mgr_driver);
+
+MODULE_DESCRIPTION("WCD DSP manager driver");
+MODULE_DEVICE_TABLE(of, wdsp_mgr_dt_match);
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd-dsp-utils.c b/sound/soc/codecs/wcd-dsp-utils.c
new file mode 100644
index 000000000000..1f048917a6c8
--- /dev/null
+++ b/sound/soc/codecs/wcd-dsp-utils.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/elf.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include "wcd-dsp-utils.h"
+
+static bool wdsp_is_valid_elf_hdr(const struct elf32_hdr *ehdr,
+ size_t fw_size)
+{
+ if (fw_size < sizeof(*ehdr)) {
+ pr_err("%s: Firmware too small\n", __func__);
+ goto elf_check_fail;
+ }
+
+ if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
+ pr_err("%s: Not an ELF file\n", __func__);
+ goto elf_check_fail;
+ }
+
+ if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
+ pr_err("%s: Not an executable image\n", __func__);
+ goto elf_check_fail;
+ }
+
+ if (ehdr->e_phnum == 0) {
+ pr_err("%s: no segments to load\n", __func__);
+ goto elf_check_fail;
+ }
+
+ if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
+ sizeof(struct elf32_hdr) > fw_size) {
+ pr_err("%s: Too small MDT file\n", __func__);
+ goto elf_check_fail;
+ }
+
+ return true;
+
+elf_check_fail:
+ return false;
+}
+
+static int wdsp_add_segment_to_list(struct device *dev,
+ const char *img_fname,
+ const struct elf32_phdr *phdr,
+ int phdr_idx,
+ struct list_head *seg_list)
+{
+ struct wdsp_img_segment *seg;
+ int ret = 0;
+
+ /* Do not load segments with zero size */
+ if (phdr->p_filesz == 0 || phdr->p_memsz == 0)
+ goto done;
+
+ seg = kzalloc(sizeof(*seg), GFP_KERNEL);
+ if (!seg) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ snprintf(seg->split_fname, sizeof(seg->split_fname),
+ "%s.b%02d", img_fname, phdr_idx);
+ ret = request_firmware(&seg->split_fw, seg->split_fname, dev);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(dev, "%s: firmware %s not found\n",
+ __func__, seg->split_fname);
+ goto bad_seg;
+ }
+
+ seg->load_addr = phdr->p_paddr;
+ seg->size = phdr->p_filesz;
+ seg->data = (u8 *) seg->split_fw->data;
+
+ list_add_tail(&seg->list, seg_list);
+done:
+ return ret;
+bad_seg:
+ kfree(seg);
+ return ret;
+}
+
+/*
+ * wdsp_flush_segment_list: Flush the list of segments
+ * @seg_list: List of segments to be flushed
+ * This API will traverse through the list of segments provided in
+ * seg_list, release the firmware for each segment and delete the
+ * segment from the list.
+ */
+void wdsp_flush_segment_list(struct list_head *seg_list)
+{
+ struct wdsp_img_segment *seg, *next;
+
+ list_for_each_entry_safe(seg, next, seg_list, list) {
+ release_firmware(seg->split_fw);
+ list_del(&seg->list);
+ kfree(seg);
+ }
+}
+EXPORT_SYMBOL(wdsp_flush_segment_list);
+
+/*
+ * wdsp_get_segment_list: Get the list of requested segments
+ * @dev: struct device pointer of caller
+ * @img_fname: Image name for the mdt and split firmware files
+ * @segment_type: Requested segment type, should be either
+ * WDSP_ELF_FLAG_RE or WDSP_ELF_FLAG_WRITE
+ * @seg_list: An initialized head for list of segmented to be returned
+ * @entry_point: Pointer to return the entry point of the image
+ * This API will parse the mdt file for img_fname and create
+ * an struct wdsp_img_segment for each segment that matches segment_type
+ * and add this structure to list pointed by seg_list
+ */
+int wdsp_get_segment_list(struct device *dev,
+ const char *img_fname,
+ unsigned int segment_type,
+ struct list_head *seg_list,
+ u32 *entry_point)
+{
+ const struct firmware *fw;
+ const struct elf32_hdr *ehdr;
+ const struct elf32_phdr *phdr;
+ const u8 *elf_ptr;
+ char mdt_name[WDSP_IMG_NAME_LEN_MAX];
+ int ret, phdr_idx;
+ bool segment_match;
+
+ if (!dev) {
+ ret = -EINVAL;
+ pr_err("%s: Invalid device handle\n", __func__);
+ goto done;
+ }
+
+ if (!img_fname || !seg_list || !entry_point) {
+ ret = -EINVAL;
+ dev_err(dev, "%s: Invalid input params\n",
+ __func__);
+ goto done;
+ }
+
+ if (segment_type != WDSP_ELF_FLAG_RE &&
+ segment_type != WDSP_ELF_FLAG_WRITE) {
+ dev_err(dev, "%s: Invalid request for segment_type %d\n",
+ __func__, segment_type);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ snprintf(mdt_name, sizeof(mdt_name), "%s.mdt", img_fname);
+ ret = request_firmware(&fw, mdt_name, dev);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(dev, "%s: firmware %s not found\n",
+ __func__, mdt_name);
+ goto done;
+ }
+
+ ehdr = (struct elf32_hdr *) fw->data;
+ *entry_point = ehdr->e_entry;
+ if (!wdsp_is_valid_elf_hdr(ehdr, fw->size)) {
+ dev_err(dev, "%s: fw mdt %s is invalid\n",
+ __func__, mdt_name);
+ ret = -EINVAL;
+ goto bad_elf;
+ }
+
+ elf_ptr = fw->data + sizeof(*ehdr);
+ for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) {
+ phdr = (struct elf32_phdr *) elf_ptr;
+ segment_match = false;
+
+ switch (segment_type) {
+ case WDSP_ELF_FLAG_RE:
+ /*
+ * Flag can be READ or EXECUTE or both but
+ * WRITE flag should not be set.
+ */
+ if ((phdr->p_flags & segment_type) &&
+ !(phdr->p_flags & WDSP_ELF_FLAG_WRITE))
+ segment_match = true;
+ break;
+ case WDSP_ELF_FLAG_WRITE:
+ /*
+ * If WRITE flag is set, other flags do not
+ * matter.
+ */
+ if (phdr->p_flags & segment_type)
+ segment_match = true;
+ break;
+ }
+
+ if (segment_match) {
+ ret = wdsp_add_segment_to_list(dev, img_fname, phdr,
+ phdr_idx, seg_list);
+ if (IS_ERR_VALUE(ret)) {
+ wdsp_flush_segment_list(seg_list);
+ goto bad_elf;
+ }
+ }
+ elf_ptr = elf_ptr + sizeof(*phdr);
+ }
+
+bad_elf:
+ release_firmware(fw);
+done:
+ return ret;
+}
+EXPORT_SYMBOL(wdsp_get_segment_list);
diff --git a/sound/soc/codecs/wcd-dsp-utils.h b/sound/soc/codecs/wcd-dsp-utils.h
new file mode 100644
index 000000000000..81842f77260e
--- /dev/null
+++ b/sound/soc/codecs/wcd-dsp-utils.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD_DSP_UTILS_H__
+#define __WCD_DSP_UTILS_H__
+
+#define WDSP_IMG_NAME_LEN_MAX 64
+
+#define WDSP_ELF_FLAG_EXECUTE (1 << 0)
+#define WDSP_ELF_FLAG_WRITE (1 << 1)
+#define WDSP_ELF_FLAG_READ (1 << 2)
+
+#define WDSP_ELF_FLAG_RE (WDSP_ELF_FLAG_READ | WDSP_ELF_FLAG_EXECUTE)
+
+struct wdsp_img_segment {
+
+ /* Firmware for the slit image */
+ const struct firmware *split_fw;
+
+ /* Name of the split firmware file */
+ char split_fname[WDSP_IMG_NAME_LEN_MAX];
+
+ /* Address where the segment is to be loaded */
+ u32 load_addr;
+
+ /* Buffer to hold the data to be loaded */
+ u8 *data;
+
+ /* Size of the data to be loaded */
+ size_t size;
+
+ /* List node pointing to next segment */
+ struct list_head list;
+};
+
+int wdsp_get_segment_list(struct device *, const char *,
+ unsigned int, struct list_head *,
+ u32 *);
+void wdsp_flush_segment_list(struct list_head *);
+
+#endif /* __WCD_DSP_UTILS_H__ */
diff --git a/sound/soc/codecs/wcd-spi-registers.h b/sound/soc/codecs/wcd-spi-registers.h
new file mode 100644
index 000000000000..4e579696cc49
--- /dev/null
+++ b/sound/soc/codecs/wcd-spi-registers.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD_SPI_REGISTERS_H__
+#define __WCD_SPI_REGISTERS_H__
+
+#include <linux/regmap.h>
+
+#define WCD_SPI_SLAVE_SANITY (0x00)
+#define WCD_SPI_SLAVE_DEVICE_ID (0x04)
+#define WCD_SPI_SLAVE_STATUS (0x08)
+#define WCD_SPI_SLAVE_CONFIG (0x0c)
+#define WCD_SPI_SLAVE_SW_RESET (0x10)
+#define WCD_SPI_SLAVE_IRQ_STATUS (0x14)
+#define WCD_SPI_SLAVE_IRQ_EN (0x18)
+#define WCD_SPI_SLAVE_IRQ_CLR (0x1c)
+#define WCD_SPI_SLAVE_IRQ_FORCE (0x20)
+#define WCD_SPI_SLAVE_TX (0x24)
+#define WCD_SPI_SLAVE_TEST_BUS_DATA (0x2c)
+#define WCD_SPI_SLAVE_TEST_BUS_CTRL (0x30)
+#define WCD_SPI_SLAVE_SW_RST_IRQ (0x34)
+#define WCD_SPI_SLAVE_CHAR_CFG (0x38)
+#define WCD_SPI_SLAVE_CHAR_DATA_MOSI (0x3c)
+#define WCD_SPI_SLAVE_CHAR_DATA_CS_N (0x40)
+#define WCD_SPI_SLAVE_CHAR_DATA_MISO (0x44)
+#define WCD_SPI_SLAVE_TRNS_BYTE_CNT (0x4c)
+#define WCD_SPI_SLAVE_TRNS_LEN (0x50)
+#define WCD_SPI_SLAVE_FIFO_LEVEL (0x54)
+#define WCD_SPI_SLAVE_GENERICS (0x58)
+#define WCD_SPI_SLAVE_EXT_BASE_ADDR (0x5c)
+#define WCD_SPI_MAX_REGISTER (0x5F)
+
+#endif /* End __WCD_SPI_REGISTERS_H__ */
diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c
new file mode 100644
index 000000000000..3049d87c6c05
--- /dev/null
+++ b/sound/soc/codecs/wcd-spi.c
@@ -0,0 +1,1250 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/component.h>
+#include <linux/ratelimit.h>
+#include <sound/wcd-dsp-mgr.h>
+#include <sound/wcd-spi.h>
+#include "wcd-spi-registers.h"
+
+/* Byte manipulations */
+#define SHIFT_1_BYTES (8)
+#define SHIFT_2_BYTES (16)
+#define SHIFT_3_BYTES (24)
+
+/* Command opcodes */
+#define WCD_SPI_CMD_NOP (0x00)
+#define WCD_SPI_CMD_WREN (0x06)
+#define WCD_SPI_CMD_CLKREQ (0xDA)
+#define WCD_SPI_CMD_RDSR (0x05)
+#define WCD_SPI_CMD_IRR (0x81)
+#define WCD_SPI_CMD_IRW (0x82)
+#define WCD_SPI_CMD_MIOR (0x83)
+#define WCD_SPI_CMD_FREAD (0x0B)
+#define WCD_SPI_CMD_MIOW (0x02)
+#define WCD_SPI_WRITE_FRAME_OPCODE \
+ (WCD_SPI_CMD_MIOW << SHIFT_3_BYTES)
+#define WCD_SPI_READ_FRAME_OPCODE \
+ (WCD_SPI_CMD_MIOR << SHIFT_3_BYTES)
+#define WCD_SPI_FREAD_FRAME_OPCODE \
+ (WCD_SPI_CMD_FREAD << SHIFT_3_BYTES)
+
+/* Command lengths */
+#define WCD_SPI_OPCODE_LEN (0x01)
+#define WCD_SPI_CMD_NOP_LEN (0x01)
+#define WCD_SPI_CMD_WREN_LEN (0x01)
+#define WCD_SPI_CMD_CLKREQ_LEN (0x04)
+#define WCD_SPI_CMD_IRR_LEN (0x04)
+#define WCD_SPI_CMD_IRW_LEN (0x06)
+#define WCD_SPI_WRITE_SINGLE_LEN (0x08)
+#define WCD_SPI_READ_SINGLE_LEN (0x13)
+#define WCD_SPI_CMD_FREAD_LEN (0x13)
+
+/* Command delays */
+#define WCD_SPI_CLKREQ_DELAY_USECS (500)
+#define WCD_SPI_CLK_OFF_TIMER_MS (3000)
+
+/* Command masks */
+#define WCD_CMD_ADDR_MASK \
+ (0xFF | \
+ (0xFF << SHIFT_1_BYTES) | \
+ (0xFF << SHIFT_2_BYTES))
+
+/* Clock ctrl request related */
+#define WCD_SPI_CLK_ENABLE true
+#define WCD_SPI_CLK_DISABLE false
+#define WCD_SPI_CLK_FLAG_DELAYED (1 << 0)
+#define WCD_SPI_CLK_FLAG_IMMEDIATE (1 << 1)
+
+/* Internal addresses */
+#define WCD_SPI_ADDR_IPC_CTL_HOST (0x012014)
+
+/* Word sizes and min/max lengths */
+#define WCD_SPI_WORD_BYTE_CNT (4)
+#define WCD_SPI_RW_MULTI_MIN_LEN (16)
+#define WCD_SPI_RW_MULTI_MAX_LEN (64 * 1024)
+
+/* Alignment requirements */
+#define WCD_SPI_RW_MIN_ALIGN WCD_SPI_WORD_BYTE_CNT
+#define WCD_SPI_RW_MULTI_ALIGN (16)
+
+/* Status mask bits */
+#define WCD_SPI_CLK_STATE_ENABLED BIT(0)
+
+/* Locking related */
+#define WCD_SPI_MUTEX_LOCK(spi, lock) \
+{ \
+ dev_vdbg(&spi->dev, "%s: mutex_lock(%s)\n", \
+ __func__, __stringify_1(lock)); \
+ mutex_lock(&lock); \
+}
+
+#define WCD_SPI_MUTEX_UNLOCK(spi, lock) \
+{ \
+ dev_vdbg(&spi->dev, "%s: mutex_unlock(%s)\n", \
+ __func__, __stringify_1(lock)); \
+ mutex_unlock(&lock); \
+}
+
+struct wcd_spi_priv {
+ struct spi_device *spi;
+ u32 mem_base_addr;
+
+ struct regmap *regmap;
+
+ /* Message for single transfer */
+ struct spi_message msg1;
+ struct spi_transfer xfer1;
+
+ /* Message for two transfers */
+ struct spi_message msg2;
+ struct spi_transfer xfer2[2];
+
+ /* Register access related */
+ u32 reg_bytes;
+ u32 val_bytes;
+
+ /* Clock requests related */
+ struct mutex clk_mutex;
+ int clk_users;
+ unsigned long status_mask;
+ struct delayed_work clk_dwork;
+
+ /* Transaction related */
+ struct mutex xfer_mutex;
+
+ struct device *m_dev;
+ struct wdsp_mgr_ops *m_ops;
+};
+
+enum xfer_request {
+ WCD_SPI_XFER_WRITE,
+ WCD_SPI_XFER_READ,
+};
+
+
+static char *wcd_spi_xfer_req_str(enum xfer_request req)
+{
+ if (req == WCD_SPI_XFER_WRITE)
+ return "xfer_write";
+ else if (req == WCD_SPI_XFER_READ)
+ return "xfer_read";
+ else
+ return "xfer_invalid";
+}
+
+static void wcd_spi_reinit_xfer(struct spi_transfer *xfer)
+{
+ xfer->tx_buf = NULL;
+ xfer->rx_buf = NULL;
+ xfer->delay_usecs = 0;
+ xfer->len = 0;
+}
+
+static int wcd_spi_read_single(struct spi_device *spi,
+ u32 remote_addr, u32 *val)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0];
+ struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1];
+ u8 *tx_buf;
+ u32 frame = 0;
+ int ret;
+
+ dev_dbg(&spi->dev, "%s: remote_addr = 0x%x\n",
+ __func__, remote_addr);
+
+ tx_buf = kzalloc(WCD_SPI_READ_SINGLE_LEN,
+ GFP_KERNEL | GFP_DMA);
+ if (!tx_buf)
+ return -ENOMEM;
+
+ frame |= WCD_SPI_READ_FRAME_OPCODE;
+ frame |= remote_addr & WCD_CMD_ADDR_MASK;
+
+ wcd_spi_reinit_xfer(tx_xfer);
+ frame = cpu_to_be32(frame);
+ memcpy(tx_buf, &frame, sizeof(frame));
+ tx_xfer->tx_buf = tx_buf;
+ tx_xfer->len = WCD_SPI_READ_SINGLE_LEN;
+
+ wcd_spi_reinit_xfer(rx_xfer);
+ rx_xfer->rx_buf = val;
+ rx_xfer->len = sizeof(*val);
+
+ ret = spi_sync(spi, &wcd_spi->msg2);
+ kfree(tx_buf);
+
+ return ret;
+}
+
+static int wcd_spi_read_multi(struct spi_device *spi,
+ u32 remote_addr, u8 *data,
+ size_t len)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct spi_transfer *xfer = &wcd_spi->xfer1;
+ u8 *tx_buf;
+ u8 *rx_buf;
+ u32 frame = 0;
+ int ret;
+
+ dev_dbg(&spi->dev, "%s: addr 0x%x, len = %zd\n",
+ __func__, remote_addr, len);
+
+ frame |= WCD_SPI_FREAD_FRAME_OPCODE;
+ frame |= remote_addr & WCD_CMD_ADDR_MASK;
+
+ tx_buf = kzalloc(WCD_SPI_CMD_FREAD_LEN + len,
+ GFP_KERNEL | GFP_DMA);
+ if (!tx_buf)
+ return -ENOMEM;
+
+ rx_buf = kzalloc(WCD_SPI_CMD_FREAD_LEN + len,
+ GFP_KERNEL | GFP_DMA);
+ if (!rx_buf) {
+ kfree(tx_buf);
+ return -ENOMEM;
+ }
+
+ wcd_spi_reinit_xfer(xfer);
+ frame = cpu_to_be32(frame);
+ memcpy(tx_buf, &frame, sizeof(frame));
+ xfer->tx_buf = tx_buf;
+ xfer->rx_buf = rx_buf;
+ xfer->len = WCD_SPI_CMD_FREAD_LEN + len;
+
+ ret = spi_sync(spi, &wcd_spi->msg1);
+ if (ret) {
+ dev_err(&spi->dev, "%s: failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ memcpy(data, rx_buf + WCD_SPI_CMD_FREAD_LEN, len);
+done:
+ kfree(tx_buf);
+ kfree(rx_buf);
+ return ret;
+}
+
+static int wcd_spi_write_single(struct spi_device *spi,
+ u32 remote_addr, u32 val)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct spi_transfer *xfer = &wcd_spi->xfer1;
+ u8 buf[WCD_SPI_WRITE_SINGLE_LEN];
+ u32 frame = 0;
+
+ dev_dbg(&spi->dev, "%s: remote_addr = 0x%x, val = 0x%x\n",
+ __func__, remote_addr, val);
+
+ memset(buf, 0, WCD_SPI_WRITE_SINGLE_LEN);
+ frame |= WCD_SPI_WRITE_FRAME_OPCODE;
+ frame |= (remote_addr & WCD_CMD_ADDR_MASK);
+
+ frame = cpu_to_be32(frame);
+ memcpy(buf, &frame, sizeof(frame));
+ memcpy(buf + sizeof(frame), &val, sizeof(val));
+
+ wcd_spi_reinit_xfer(xfer);
+ xfer->tx_buf = buf;
+ xfer->len = WCD_SPI_WRITE_SINGLE_LEN;
+
+ return spi_sync(spi, &wcd_spi->msg1);
+}
+
+static int wcd_spi_write_multi(struct spi_device *spi,
+ u32 remote_addr, u8 *data,
+ size_t len)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct spi_transfer *xfer = &wcd_spi->xfer1;
+ u32 frame = 0;
+ u8 *tx_buf;
+ int xfer_len, ret;
+
+ dev_dbg(&spi->dev, "%s: addr = 0x%x len = %zd\n",
+ __func__, remote_addr, len);
+
+ frame |= WCD_SPI_WRITE_FRAME_OPCODE;
+ frame |= (remote_addr & WCD_CMD_ADDR_MASK);
+
+ frame = cpu_to_be32(frame);
+ xfer_len = len + sizeof(frame);
+
+ tx_buf = kzalloc(xfer_len, GFP_KERNEL);
+ if (!tx_buf)
+ return -ENOMEM;
+
+ memcpy(tx_buf, &frame, sizeof(frame));
+ memcpy(tx_buf + sizeof(frame), data, len);
+
+ wcd_spi_reinit_xfer(xfer);
+ xfer->tx_buf = tx_buf;
+ xfer->len = xfer_len;
+
+ ret = spi_sync(spi, &wcd_spi->msg1);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev,
+ "%s: Failed, addr = 0x%x, len = %zd\n",
+ __func__, remote_addr, len);
+ kfree(tx_buf);
+
+ return ret;
+}
+
+static int wcd_spi_transfer_split(struct spi_device *spi,
+ struct wcd_spi_msg *data_msg,
+ enum xfer_request xfer_req)
+{
+ u32 addr = data_msg->remote_addr;
+ u8 *data = data_msg->data;
+ int remain_size = data_msg->len;
+ int to_xfer, loop_cnt, ret;
+
+ /* Perform single writes until multi word alignment is met */
+ loop_cnt = 1;
+ while (remain_size &&
+ !IS_ALIGNED(addr, WCD_SPI_RW_MULTI_ALIGN)) {
+ if (xfer_req == WCD_SPI_XFER_WRITE)
+ ret = wcd_spi_write_single(spi, addr,
+ (*(u32 *)data));
+ else
+ ret = wcd_spi_read_single(spi, addr,
+ (u32 *)data);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev,
+ "%s: %s fail iter(%d) start-word addr (0x%x)\n",
+ __func__, wcd_spi_xfer_req_str(xfer_req),
+ loop_cnt, addr);
+ goto done;
+ }
+
+ addr += WCD_SPI_WORD_BYTE_CNT;
+ data += WCD_SPI_WORD_BYTE_CNT;
+ remain_size -= WCD_SPI_WORD_BYTE_CNT;
+ loop_cnt++;
+ }
+
+ /* Perform multi writes for max allowed multi writes */
+ loop_cnt = 1;
+ while (remain_size >= WCD_SPI_RW_MULTI_MAX_LEN) {
+ if (xfer_req == WCD_SPI_XFER_WRITE)
+ ret = wcd_spi_write_multi(spi, addr, data,
+ WCD_SPI_RW_MULTI_MAX_LEN);
+ else
+ ret = wcd_spi_read_multi(spi, addr, data,
+ WCD_SPI_RW_MULTI_MAX_LEN);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev,
+ "%s: %s fail iter(%d) max-write addr (0x%x)\n",
+ __func__, wcd_spi_xfer_req_str(xfer_req),
+ loop_cnt, addr);
+ goto done;
+ }
+
+ addr += WCD_SPI_RW_MULTI_MAX_LEN;
+ data += WCD_SPI_RW_MULTI_MAX_LEN;
+ remain_size -= WCD_SPI_RW_MULTI_MAX_LEN;
+ loop_cnt++;
+ }
+
+ /*
+ * Perform write for max possible data that is multiple
+ * of the minimum size for multi-write commands.
+ */
+ to_xfer = remain_size - (remain_size % WCD_SPI_RW_MULTI_MIN_LEN);
+ if (remain_size >= WCD_SPI_RW_MULTI_MIN_LEN &&
+ to_xfer > 0) {
+ if (xfer_req == WCD_SPI_XFER_WRITE)
+ ret = wcd_spi_write_multi(spi, addr, data, to_xfer);
+ else
+ ret = wcd_spi_read_multi(spi, addr, data, to_xfer);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev,
+ "%s: %s fail write addr (0x%x), size (0x%x)\n",
+ __func__, wcd_spi_xfer_req_str(xfer_req),
+ addr, to_xfer);
+ goto done;
+ }
+
+ addr += to_xfer;
+ data += to_xfer;
+ remain_size -= to_xfer;
+ }
+
+ /* Perform single writes for the last remaining data */
+ loop_cnt = 1;
+ while (remain_size > 0) {
+ if (xfer_req == WCD_SPI_XFER_WRITE)
+ ret = wcd_spi_write_single(spi, addr, (*((u32 *)data)));
+ else
+ ret = wcd_spi_read_single(spi, addr, (u32 *) data);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev,
+ "%s: %s fail iter(%d) end-write addr (0x%x)\n",
+ __func__, wcd_spi_xfer_req_str(xfer_req),
+ loop_cnt, addr);
+ goto done;
+ }
+
+ addr += WCD_SPI_WORD_BYTE_CNT;
+ data += WCD_SPI_WORD_BYTE_CNT;
+ remain_size -= WCD_SPI_WORD_BYTE_CNT;
+ loop_cnt++;
+ }
+
+done:
+ return ret;
+}
+
+static int wcd_spi_cmd_nop(struct spi_device *spi)
+{
+ u8 nop = WCD_SPI_CMD_NOP;
+
+ return spi_write(spi, &nop, WCD_SPI_CMD_NOP_LEN);
+}
+
+static int wcd_spi_cmd_clkreq(struct spi_device *spi)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct spi_transfer *xfer = &wcd_spi->xfer1;
+ u8 cmd[WCD_SPI_CMD_CLKREQ_LEN] = {
+ WCD_SPI_CMD_CLKREQ,
+ 0xBA, 0x80, 0x00};
+
+ wcd_spi_reinit_xfer(xfer);
+ xfer->tx_buf = cmd;
+ xfer->len = WCD_SPI_CMD_CLKREQ_LEN;
+ xfer->delay_usecs = WCD_SPI_CLKREQ_DELAY_USECS;
+
+ return spi_sync(spi, &wcd_spi->msg1);
+}
+
+static int wcd_spi_cmd_wr_en(struct spi_device *spi)
+{
+ u8 wr_en = WCD_SPI_CMD_WREN;
+
+ return spi_write(spi, &wr_en, WCD_SPI_CMD_WREN_LEN);
+}
+
+static int wcd_spi_cmd_rdsr(struct spi_device *spi,
+ u32 *rdsr_status)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0];
+ struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1];
+ u8 rdsr_cmd;
+ u32 status;
+ int ret;
+
+ rdsr_cmd = WCD_SPI_CMD_RDSR;
+ wcd_spi_reinit_xfer(tx_xfer);
+ tx_xfer->tx_buf = &rdsr_cmd;
+ tx_xfer->len = sizeof(rdsr_cmd);
+
+
+ wcd_spi_reinit_xfer(rx_xfer);
+ rx_xfer->rx_buf = &status;
+ rx_xfer->len = sizeof(status);
+
+ ret = spi_sync(spi, &wcd_spi->msg2);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev, "%s: RDSR failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ *rdsr_status = be32_to_cpu(status);
+
+ dev_dbg(&spi->dev, "%s: RDSR success, value = 0x%x\n",
+ __func__, *rdsr_status);
+done:
+ return ret;
+}
+
+static int wcd_spi_clk_enable(struct spi_device *spi)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ int ret;
+ u32 rd_status;
+
+ ret = wcd_spi_cmd_nop(spi);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev, "%s: NOP1 failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = wcd_spi_cmd_clkreq(spi);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev, "%s: CLK_REQ failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = wcd_spi_cmd_nop(spi);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev, "%s: NOP2 failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+ wcd_spi_cmd_rdsr(spi, &rd_status);
+ /*
+ * Read status zero means reads are not
+ * happenning on the bus, possibly because
+ * clock request failed.
+ */
+ if (rd_status) {
+ set_bit(WCD_SPI_CLK_STATE_ENABLED,
+ &wcd_spi->status_mask);
+ } else {
+ dev_err(&spi->dev, "%s: RDSR status is zero\n",
+ __func__);
+ ret = -EIO;
+ }
+done:
+ return ret;
+}
+
+static int wcd_spi_clk_disable(struct spi_device *spi)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ int ret;
+
+ ret = wcd_spi_write_single(spi, WCD_SPI_ADDR_IPC_CTL_HOST, 0x01);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev, "%s: Failed, err = %d\n",
+ __func__, ret);
+ else
+ clear_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask);
+
+ return ret;
+}
+
+static int wcd_spi_clk_ctrl(struct spi_device *spi,
+ bool request, u32 flags)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ int ret = 0;
+ const char *delay_str;
+
+ delay_str = (flags == WCD_SPI_CLK_FLAG_DELAYED) ?
+ "delayed" : "immediate";
+
+ WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+
+ /* Reject any unbalanced disable request */
+ if (wcd_spi->clk_users < 0 ||
+ (!request && wcd_spi->clk_users == 0)) {
+ dev_err(&spi->dev, "%s: Unbalanced clk_users %d for %s\n",
+ __func__, wcd_spi->clk_users,
+ request ? "enable" : "disable");
+ ret = -EINVAL;
+
+ /* Reset the clk_users to 0 */
+ wcd_spi->clk_users = 0;
+
+ goto done;
+ }
+
+ if (request == WCD_SPI_CLK_ENABLE) {
+ /* Cancel the disable clk work */
+ WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+ cancel_delayed_work_sync(&wcd_spi->clk_dwork);
+ WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+
+ wcd_spi->clk_users++;
+
+ /*
+ * If clk state is already set,
+ * then clk wasnt really disabled
+ */
+ if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask))
+ goto done;
+ else if (wcd_spi->clk_users == 1)
+ ret = wcd_spi_clk_enable(spi);
+
+ } else {
+ wcd_spi->clk_users--;
+
+ /* Clock is still voted for */
+ if (wcd_spi->clk_users > 0)
+ goto done;
+
+ /*
+ * If we are here, clk_users must be 0 and needs
+ * to be disabled. Call the disable based on the
+ * flags.
+ */
+ if (flags == WCD_SPI_CLK_FLAG_DELAYED) {
+ schedule_delayed_work(&wcd_spi->clk_dwork,
+ msecs_to_jiffies(WCD_SPI_CLK_OFF_TIMER_MS));
+ } else {
+ ret = wcd_spi_clk_disable(spi);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev,
+ "%s: Failed to disable clk err = %d\n",
+ __func__, ret);
+ }
+ }
+
+done:
+ dev_dbg(&spi->dev, "%s: updated clk_users = %d, request_%s %s\n",
+ __func__, wcd_spi->clk_users, request ? "enable" : "disable",
+ request ? "" : delay_str);
+ WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+
+ return ret;
+}
+
+static int wcd_spi_init(struct spi_device *spi)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ int ret;
+
+ ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE,
+ WCD_SPI_CLK_FLAG_IMMEDIATE);
+ if (IS_ERR_VALUE(ret))
+ goto done;
+
+ ret = wcd_spi_cmd_wr_en(spi);
+ if (IS_ERR_VALUE(ret))
+ goto err_wr_en;
+
+ regmap_write(wcd_spi->regmap, WCD_SPI_SLAVE_CONFIG,
+ 0x0F3D0800);
+
+ /* Write the MTU to 64K */
+ regmap_update_bits(wcd_spi->regmap,
+ WCD_SPI_SLAVE_TRNS_LEN,
+ 0xFFFF0000,
+ (WCD_SPI_RW_MULTI_MAX_LEN / 4) << 16);
+done:
+ return ret;
+
+err_wr_en:
+ wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE,
+ WCD_SPI_CLK_FLAG_IMMEDIATE);
+ return ret;
+}
+
+static void wcd_spi_clk_work(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct wcd_spi_priv *wcd_spi;
+ struct spi_device *spi;
+ int ret;
+
+ dwork = to_delayed_work(work);
+ wcd_spi = container_of(dwork, struct wcd_spi_priv, clk_dwork);
+ spi = wcd_spi->spi;
+
+ WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+ ret = wcd_spi_clk_disable(spi);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev,
+ "%s: Failed to disable clk, err = %d\n",
+ __func__, ret);
+ WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+}
+
+static int __wcd_spi_data_xfer(struct spi_device *spi,
+ struct wcd_spi_msg *msg,
+ enum xfer_request xfer_req)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ int ret;
+
+ /* Check for minimum alignment requirements */
+ if (!IS_ALIGNED(msg->remote_addr, WCD_SPI_RW_MIN_ALIGN)) {
+ dev_err(&spi->dev,
+ "%s addr 0x%x is not aligned to 0x%x\n",
+ __func__, msg->remote_addr, WCD_SPI_RW_MIN_ALIGN);
+ return -EINVAL;
+ } else if (msg->len % WCD_SPI_WORD_BYTE_CNT) {
+ dev_err(&spi->dev,
+ "%s len 0x%zx is not multiple of %d\n",
+ __func__, msg->len, WCD_SPI_WORD_BYTE_CNT);
+ return -EINVAL;
+ }
+
+ WCD_SPI_MUTEX_LOCK(spi, wcd_spi->xfer_mutex);
+ if (msg->len == WCD_SPI_WORD_BYTE_CNT) {
+ if (xfer_req == WCD_SPI_XFER_WRITE)
+ ret = wcd_spi_write_single(spi, msg->remote_addr,
+ (*((u32 *)msg->data)));
+ else
+ ret = wcd_spi_read_single(spi, msg->remote_addr,
+ (u32 *) msg->data);
+ } else {
+ ret = wcd_spi_transfer_split(spi, msg, xfer_req);
+ }
+ WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->xfer_mutex);
+
+ return ret;
+}
+
+static int wcd_spi_data_xfer(struct spi_device *spi,
+ struct wcd_spi_msg *msg,
+ enum xfer_request req)
+{
+ int ret, ret1;
+
+ if (msg->len <= 0) {
+ dev_err(&spi->dev, "%s: Invalid size %zd\n",
+ __func__, msg->len);
+ return -EINVAL;
+ }
+
+ /* Request for clock */
+ ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE,
+ WCD_SPI_CLK_FLAG_IMMEDIATE);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev, "%s: clk enable failed %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ /* Perform the transaction */
+ ret = __wcd_spi_data_xfer(spi, msg, req);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev,
+ "%s: Failed %s, addr = 0x%x, size = 0x%zx, err = %d\n",
+ __func__, wcd_spi_xfer_req_str(req),
+ msg->remote_addr, msg->len, ret);
+
+ /* Release the clock even if xfer failed */
+ ret1 = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE,
+ WCD_SPI_CLK_FLAG_DELAYED);
+ if (IS_ERR_VALUE(ret1))
+ dev_err(&spi->dev, "%s: clk disable failed %d\n",
+ __func__, ret1);
+done:
+ return ret;
+}
+
+/*
+ * wcd_spi_data_write: Write data to WCD SPI
+ * @spi: spi_device struct
+ * @msg: msg that needs to be written to WCD
+ *
+ * This API writes length of data to address specified. These details
+ * about the write are encapsulated in @msg. Write size should be multiple
+ * of 4 bytes and write address should be 4-byte aligned.
+ */
+int wcd_spi_data_write(struct spi_device *spi,
+ struct wcd_spi_msg *msg)
+{
+ if (!spi || !msg) {
+ pr_err("%s: Invalid %s\n", __func__,
+ (!spi) ? "spi device" : "msg");
+ return -EINVAL;
+ }
+
+ dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x, len = %zu\n",
+ __func__, msg->remote_addr, msg->len);
+ return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_WRITE);
+}
+EXPORT_SYMBOL(wcd_spi_data_write);
+
+/*
+ * wcd_spi_data_read: Read data from WCD SPI
+ * @spi: spi_device struct
+ * @msg: msg that needs to be read from WCD
+ *
+ * This API reads length of data from address specified. These details
+ * about the read are encapsulated in @msg. Read size should be multiple
+ * of 4 bytes and read address should be 4-byte aligned.
+ */
+int wcd_spi_data_read(struct spi_device *spi,
+ struct wcd_spi_msg *msg)
+{
+ if (!spi || !msg) {
+ pr_err("%s: Invalid %s\n", __func__,
+ (!spi) ? "spi device" : "msg");
+ return -EINVAL;
+ }
+
+ dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x,len = %zu\n",
+ __func__, msg->remote_addr, msg->len);
+ return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_READ);
+}
+EXPORT_SYMBOL(wcd_spi_data_read);
+
+static int wdsp_spi_dload_section(struct spi_device *spi,
+ void *data)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct wdsp_img_section *sec = data;
+ struct wcd_spi_msg msg;
+ int ret;
+
+ dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n",
+ __func__, sec->addr, sec->size);
+
+ msg.remote_addr = sec->addr + wcd_spi->mem_base_addr;
+ msg.data = sec->data;
+ msg.len = sec->size;
+
+ ret = __wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_WRITE);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n",
+ __func__, msg.remote_addr, msg.len);
+ return ret;
+}
+
+static int wdsp_spi_event_handler(struct device *dev, void *priv_data,
+ enum wdsp_event_type event,
+ void *data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ int ret;
+
+ dev_dbg(&spi->dev, "%s: event type %d\n",
+ __func__, event);
+
+ switch (event) {
+ case WDSP_EVENT_PRE_DLOAD_DATA:
+ ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE,
+ WCD_SPI_CLK_FLAG_IMMEDIATE);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev, "%s: clk_req failed %d\n",
+ __func__, ret);
+ break;
+
+ case WDSP_EVENT_POST_DLOAD_CODE:
+ case WDSP_EVENT_POST_DLOAD_DATA:
+ case WDSP_EVENT_DLOAD_FAILED:
+
+ ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE,
+ WCD_SPI_CLK_FLAG_IMMEDIATE);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev, "%s: clk unvote failed %d\n",
+ __func__, ret);
+ break;
+
+ case WDSP_EVENT_DLOAD_SECTION:
+ ret = wdsp_spi_dload_section(spi, data);
+ break;
+ default:
+ dev_dbg(&spi->dev, "%s: Unhandled event %d\n",
+ __func__, event);
+ break;
+ }
+
+ return ret;
+}
+
+static int wcd_spi_bus_gwrite(void *context, const void *reg,
+ size_t reg_len, const void *val,
+ size_t val_len)
+{
+ struct device *dev = context;
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ u8 tx_buf[WCD_SPI_CMD_IRW_LEN];
+
+ if (!reg || !val || reg_len != wcd_spi->reg_bytes ||
+ val_len != wcd_spi->val_bytes) {
+ dev_err(&spi->dev,
+ "%s: Invalid input, reg_len = %zd, val_len = %zd",
+ __func__, reg_len, val_len);
+ return -EINVAL;
+ }
+
+ tx_buf[0] = WCD_SPI_CMD_IRW;
+ tx_buf[1] = *((u8 *)reg);
+ memcpy(&tx_buf[WCD_SPI_OPCODE_LEN + reg_len],
+ val, val_len);
+
+ return spi_write(spi, tx_buf, WCD_SPI_CMD_IRW_LEN);
+}
+
+static int wcd_spi_bus_write(void *context, const void *data,
+ size_t count)
+{
+ struct device *dev = context;
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+
+ if (count < (wcd_spi->reg_bytes + wcd_spi->val_bytes)) {
+ dev_err(&spi->dev, "%s: Invalid size %zd\n",
+ __func__, count);
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ return wcd_spi_bus_gwrite(context, data, wcd_spi->reg_bytes,
+ data + wcd_spi->reg_bytes,
+ count - wcd_spi->reg_bytes);
+}
+
+static int wcd_spi_bus_read(void *context, const void *reg,
+ size_t reg_len, void *val,
+ size_t val_len)
+{
+ struct device *dev = context;
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0];
+ struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1];
+ u8 tx_buf[WCD_SPI_CMD_IRR_LEN];
+
+ if (!reg || !val || reg_len != wcd_spi->reg_bytes ||
+ val_len != wcd_spi->val_bytes) {
+ dev_err(&spi->dev,
+ "%s: Invalid input, reg_len = %zd, val_len = %zd",
+ __func__, reg_len, val_len);
+ return -EINVAL;
+ }
+
+ memset(tx_buf, 0, WCD_SPI_OPCODE_LEN);
+ tx_buf[0] = WCD_SPI_CMD_IRR;
+ tx_buf[1] = *((u8 *)reg);
+
+ wcd_spi_reinit_xfer(tx_xfer);
+ tx_xfer->tx_buf = tx_buf;
+ tx_xfer->rx_buf = NULL;
+ tx_xfer->len = WCD_SPI_CMD_IRR_LEN;
+
+ wcd_spi_reinit_xfer(rx_xfer);
+ rx_xfer->tx_buf = NULL;
+ rx_xfer->rx_buf = val;
+ rx_xfer->len = val_len;
+
+ return spi_sync(spi, &wcd_spi->msg2);
+}
+
+static struct regmap_bus wcd_spi_regmap_bus = {
+ .write = wcd_spi_bus_write,
+ .gather_write = wcd_spi_bus_gwrite,
+ .read = wcd_spi_bus_read,
+ .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static int wcd_spi_state_show(struct seq_file *f, void *ptr)
+{
+ struct spi_device *spi = f->private;
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ const char *clk_state, *clk_mutex, *xfer_mutex;
+
+ if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask))
+ clk_state = "enabled";
+ else
+ clk_state = "disabled";
+
+ clk_mutex = mutex_is_locked(&wcd_spi->clk_mutex) ?
+ "locked" : "unlocked";
+
+ xfer_mutex = mutex_is_locked(&wcd_spi->xfer_mutex) ?
+ "locked" : "unlocked";
+
+ seq_printf(f, "clk_state = %s\nclk_users = %d\n"
+ "clk_mutex = %s\nxfer_mutex = %s\n",
+ clk_state, wcd_spi->clk_users, clk_mutex,
+ xfer_mutex);
+ return 0;
+}
+
+static int wcd_spi_state_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, wcd_spi_state_show, inode->i_private);
+}
+
+static const struct file_operations state_fops = {
+ .open = wcd_spi_state_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int wcd_spi_debugfs_init(struct spi_device *spi)
+{
+ int rc = 0;
+ struct dentry *dir;
+
+ dir = debugfs_create_dir("wcd_spi", NULL);
+ if (IS_ERR_OR_NULL(dir)) {
+ dir = NULL;
+ rc = -ENODEV;
+ goto done;
+ }
+
+ debugfs_create_file("state", 0444, dir, spi, &state_fops);
+
+done:
+ return rc;
+}
+
+
+static const struct reg_default wcd_spi_defaults[] = {
+ {WCD_SPI_SLAVE_SANITY, 0xDEADBEEF},
+ {WCD_SPI_SLAVE_DEVICE_ID, 0x00500000},
+ {WCD_SPI_SLAVE_STATUS, 0x80100000},
+ {WCD_SPI_SLAVE_CONFIG, 0x0F200808},
+ {WCD_SPI_SLAVE_SW_RESET, 0x00000000},
+ {WCD_SPI_SLAVE_IRQ_STATUS, 0x00000000},
+ {WCD_SPI_SLAVE_IRQ_EN, 0x00000000},
+ {WCD_SPI_SLAVE_IRQ_CLR, 0x00000000},
+ {WCD_SPI_SLAVE_IRQ_FORCE, 0x00000000},
+ {WCD_SPI_SLAVE_TX, 0x00000000},
+ {WCD_SPI_SLAVE_TEST_BUS_DATA, 0x00000000},
+ {WCD_SPI_SLAVE_TEST_BUS_CTRL, 0x00000000},
+ {WCD_SPI_SLAVE_SW_RST_IRQ, 0x00000000},
+ {WCD_SPI_SLAVE_CHAR_CFG, 0x00000000},
+ {WCD_SPI_SLAVE_CHAR_DATA_MOSI, 0x00000000},
+ {WCD_SPI_SLAVE_CHAR_DATA_CS_N, 0x00000000},
+ {WCD_SPI_SLAVE_CHAR_DATA_MISO, 0x00000000},
+ {WCD_SPI_SLAVE_TRNS_BYTE_CNT, 0x00000000},
+ {WCD_SPI_SLAVE_TRNS_LEN, 0x00000000},
+ {WCD_SPI_SLAVE_FIFO_LEVEL, 0x00000000},
+ {WCD_SPI_SLAVE_GENERICS, 0x80000000},
+ {WCD_SPI_SLAVE_EXT_BASE_ADDR, 0x00000000},
+};
+
+static bool wcd_spi_is_volatile_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case WCD_SPI_SLAVE_SANITY:
+ case WCD_SPI_SLAVE_STATUS:
+ case WCD_SPI_SLAVE_IRQ_STATUS:
+ case WCD_SPI_SLAVE_TX:
+ case WCD_SPI_SLAVE_SW_RST_IRQ:
+ case WCD_SPI_SLAVE_TRNS_BYTE_CNT:
+ case WCD_SPI_SLAVE_FIFO_LEVEL:
+ case WCD_SPI_SLAVE_GENERICS:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wcd_spi_is_readable_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case WCD_SPI_SLAVE_SW_RESET:
+ case WCD_SPI_SLAVE_IRQ_CLR:
+ case WCD_SPI_SLAVE_IRQ_FORCE:
+ return false;
+ }
+
+ return true;
+}
+
+static struct regmap_config wcd_spi_regmap_cfg = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wcd_spi_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wcd_spi_defaults),
+ .max_register = WCD_SPI_MAX_REGISTER,
+ .volatile_reg = wcd_spi_is_volatile_reg,
+ .readable_reg = wcd_spi_is_readable_reg,
+};
+
+static int wdsp_spi_init(struct device *dev, void *priv_data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ int ret;
+
+ wcd_spi->reg_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.reg_bits, 8);
+ wcd_spi->val_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.val_bits, 8);
+
+ wcd_spi->regmap = devm_regmap_init(&spi->dev, &wcd_spi_regmap_bus,
+ &spi->dev, &wcd_spi_regmap_cfg);
+ if (IS_ERR(wcd_spi->regmap)) {
+ ret = PTR_ERR(wcd_spi->regmap);
+ dev_err(&spi->dev, "%s: Failed to allocate regmap, err = %d\n",
+ __func__, ret);
+ goto err_regmap;
+ }
+
+ if (wcd_spi_debugfs_init(spi))
+ dev_err(&spi->dev, "%s: Failed debugfs init\n", __func__);
+
+ spi_message_init(&wcd_spi->msg1);
+ spi_message_add_tail(&wcd_spi->xfer1, &wcd_spi->msg1);
+
+ spi_message_init(&wcd_spi->msg2);
+ spi_message_add_tail(&wcd_spi->xfer2[0], &wcd_spi->msg2);
+ spi_message_add_tail(&wcd_spi->xfer2[1], &wcd_spi->msg2);
+
+ ret = wcd_spi_init(spi);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev, "%s: Init failed, err = %d\n",
+ __func__, ret);
+ goto err_init;
+ }
+
+ return 0;
+
+err_init:
+ spi_transfer_del(&wcd_spi->xfer1);
+ spi_transfer_del(&wcd_spi->xfer2[0]);
+ spi_transfer_del(&wcd_spi->xfer2[1]);
+
+err_regmap:
+ return ret;
+}
+
+static int wdsp_spi_deinit(struct device *dev, void *priv_data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+
+ spi_transfer_del(&wcd_spi->xfer1);
+ spi_transfer_del(&wcd_spi->xfer2[0]);
+ spi_transfer_del(&wcd_spi->xfer2[1]);
+
+ return 0;
+}
+
+static struct wdsp_cmpnt_ops wdsp_spi_ops = {
+ .init = wdsp_spi_init,
+ .deinit = wdsp_spi_deinit,
+ .event_handler = wdsp_spi_event_handler,
+};
+
+static int wcd_spi_component_bind(struct device *dev,
+ struct device *master,
+ void *data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ int ret = 0;
+
+ wcd_spi->m_dev = master;
+ wcd_spi->m_ops = data;
+
+ if (wcd_spi->m_ops &&
+ wcd_spi->m_ops->register_cmpnt_ops)
+ ret = wcd_spi->m_ops->register_cmpnt_ops(master, dev,
+ wcd_spi,
+ &wdsp_spi_ops);
+ if (ret)
+ dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n",
+ __func__, ret);
+ return ret;
+}
+
+static void wcd_spi_component_unbind(struct device *dev,
+ struct device *master,
+ void *data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+
+ wcd_spi->m_dev = NULL;
+ wcd_spi->m_ops = NULL;
+}
+
+static const struct component_ops wcd_spi_component_ops = {
+ .bind = wcd_spi_component_bind,
+ .unbind = wcd_spi_component_unbind,
+};
+
+static int wcd_spi_probe(struct spi_device *spi)
+{
+ struct wcd_spi_priv *wcd_spi;
+ int ret = 0;
+
+ wcd_spi = devm_kzalloc(&spi->dev, sizeof(*wcd_spi),
+ GFP_KERNEL);
+ if (!wcd_spi)
+ return -ENOMEM;
+
+ ret = of_property_read_u32(spi->dev.of_node,
+ "qcom,mem-base-addr",
+ &wcd_spi->mem_base_addr);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&spi->dev, "%s: Missing %s DT entry",
+ __func__, "qcom,mem-base-addr");
+ goto err_ret;
+ }
+
+ dev_dbg(&spi->dev,
+ "%s: mem_base_addr 0x%x\n", __func__, wcd_spi->mem_base_addr);
+
+ mutex_init(&wcd_spi->clk_mutex);
+ mutex_init(&wcd_spi->xfer_mutex);
+ INIT_DELAYED_WORK(&wcd_spi->clk_dwork, wcd_spi_clk_work);
+
+ wcd_spi->spi = spi;
+ spi_set_drvdata(spi, wcd_spi);
+
+ ret = component_add(&spi->dev, &wcd_spi_component_ops);
+ if (ret) {
+ dev_err(&spi->dev, "%s: component_add failed err = %d\n",
+ __func__, ret);
+ goto err_component_add;
+ }
+
+ return ret;
+
+err_component_add:
+ mutex_destroy(&wcd_spi->clk_mutex);
+ mutex_destroy(&wcd_spi->xfer_mutex);
+err_ret:
+ devm_kfree(&spi->dev, wcd_spi);
+ spi_set_drvdata(spi, NULL);
+ return ret;
+}
+
+static int wcd_spi_remove(struct spi_device *spi)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+
+ component_del(&spi->dev, &wcd_spi_component_ops);
+
+ mutex_destroy(&wcd_spi->clk_mutex);
+ mutex_destroy(&wcd_spi->xfer_mutex);
+
+ devm_kfree(&spi->dev, wcd_spi);
+ spi_set_drvdata(spi, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id wcd_spi_of_match[] = {
+ { .compatible = "qcom,wcd-spi-v2", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wcd_spi_of_match);
+
+static struct spi_driver wcd_spi_driver = {
+ .driver = {
+ .name = "wcd-spi-v2",
+ .of_match_table = wcd_spi_of_match,
+ },
+ .probe = wcd_spi_probe,
+ .remove = wcd_spi_remove,
+};
+
+module_spi_driver(wcd_spi_driver);
+
+MODULE_DESCRIPTION("WCD SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index f46057d027e0..11993bb9a639 100755..100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -369,6 +369,7 @@ enum {
AIF_MIX1_PB,
AIF4_MAD_TX,
AIF4_VIFEED,
+ AIF5_CPE_TX,
NUM_CODEC_DAIS,
};
@@ -477,15 +478,18 @@ static const struct wcd9xxx_ch tasha_tx_chs[TASHA_TX_MAX] = {
};
static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = {
- 0, /* AIF1_PB */
- BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF1_CAP */
- 0, /* AIF2_PB */
- BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF2_CAP */
- 0, /* AIF3_PB */
- BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX), /* AIF3_CAP */
- 0, /* AIF4_PB */
- 0, /* AIF_MIX1_PB */
- BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP), /* AIF4_MAD_TX */
+ /* Needs to define in the same order of DAI enum definitions */
+ 0,
+ BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX),
+ 0,
+ BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX),
+ 0,
+ BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX),
+ 0,
+ 0,
+ BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF5_CPE_TX),
+ 0,
+ BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX),
};
static const u32 vport_i2s_check_table[NUM_CODEC_DAIS] = {
@@ -584,6 +588,8 @@ static int wcd9335_get_micb_vout_ctl_val(u32 micb_mv);
static int tasha_config_compander(struct snd_soc_codec *, int, int);
static void tasha_codec_set_tx_hold(struct snd_soc_codec *, u16, bool);
+static int tasha_codec_internal_rco_ctrl(struct snd_soc_codec *codec,
+ bool enable);
/* Hold instance to soundwire platform device */
struct tasha_swr_ctrl_data {
@@ -837,6 +843,8 @@ struct tasha_priv {
int hph_r_gain;
int rx_7_count;
int rx_8_count;
+ bool clk_mode;
+ bool clk_internal;
};
static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec,
@@ -2286,6 +2294,30 @@ static int tasha_put_anc_func(struct snd_kcontrol *kcontrol,
return 0;
}
+static int tasha_get_clkmode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = tasha->clk_mode;
+ dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode);
+
+ return 0;
+}
+
+static int tasha_put_clkmode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ tasha->clk_mode = ucontrol->value.enumerated.item[0];
+ dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode);
+
+ return 0;
+}
+
static int tasha_get_iir_enable_audio_mixer(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -2520,6 +2552,7 @@ static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
}
break;
case AIF4_MAD_TX:
+ case AIF5_CPE_TX:
break;
default:
pr_err("Unknown AIF %d\n", dai_id);
@@ -3289,7 +3322,7 @@ static int __tasha_codec_enable_slimtx(struct snd_soc_codec *codec,
ret = wcd9xxx_disconnect_port(core,
&dai->wcd9xxx_ch_list,
dai->grph);
- pr_debug("%s: Disconnect RX port, ret = %d\n",
+ pr_debug("%s: Disconnect TX port, ret = %d\n",
__func__, ret);
}
@@ -3971,13 +4004,16 @@ static int tasha_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
set_bit(HPH_PA_DELAY, &tasha->status_mask);
break;
case SND_SOC_DAPM_POST_PMU:
- if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) != 0xC0)
- /*
- * If PA_EN is not set (potentially in ANC case) then
- * do nothing for POST_PMU and let left channel handle
- * everything.
- */
- break;
+ if (!(strcmp(w->name, "ANC HPHR PA"))) {
+ if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0)
+ != 0xC0)
+ /*
+ * If PA_EN is not set (potentially in ANC case)
+ * then do nothing for POST_PMU and let left
+ * channel handle everything.
+ */
+ break;
+ }
/*
* 7ms sleep is required after PA is enabled as per
* HW requirement
@@ -4062,13 +4098,16 @@ static int tasha_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
set_bit(HPH_PA_DELAY, &tasha->status_mask);
break;
case SND_SOC_DAPM_POST_PMU:
- if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) != 0xC0)
- /*
- * If PA_EN is not set (potentially in ANC case) then
- * do nothing for POST_PMU and let right channel handle
- * everything.
- */
- break;
+ if (!(strcmp(w->name, "ANC HPHL PA"))) {
+ if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0)
+ != 0xC0)
+ /*
+ * If PA_EN is not set (potentially in ANC case)
+ * then do nothing for POST_PMU and let right
+ * channel handle everything.
+ */
+ break;
+ }
/*
* 7ms sleep is required after PA is enabled as per
* HW requirement
@@ -5781,6 +5820,18 @@ static u32 tasha_get_dmic_sample_rate(struct snd_soc_codec *codec,
tx_stream_fs = snd_soc_read(codec, tx_fs_reg) & 0x0F;
dmic_fs = tx_stream_fs <= 4 ? WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ :
WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
+
+ /*
+ * Check for ECPP path selection and DEC1 not connected to
+ * any other audio path to apply ECPP DMIC sample rate
+ */
+ if ((adc_mux_index == 1) &&
+ ((snd_soc_read(codec, WCD9335_CPE_SS_US_EC_MUX_CFG)
+ & 0x0F) == 0x0A) &&
+ ((snd_soc_read(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0)
+ & 0x0C) == 0x00)) {
+ dmic_fs = pdata->ecpp_dmic_sample_rate;
+ }
} else {
dmic_fs = pdata->dmic_sample_rate;
}
@@ -6078,6 +6129,9 @@ static const char *const tasha_anc_func_text[] = {"OFF", "ON"};
static const struct soc_enum tasha_anc_func_enum =
SOC_ENUM_SINGLE_EXT(2, tasha_anc_func_text);
+static const char *const tasha_clkmode_text[] = {"EXTERNAL", "INTERNAL"};
+static SOC_ENUM_SINGLE_EXT_DECL(tasha_clkmode_enum, tasha_clkmode_text);
+
/* Cutoff frequency for high pass filter */
static const char * const cf_text[] = {
"CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ"
@@ -6199,6 +6253,9 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"AIF4 MAD", NULL, "AIF4_MAD Mixer"},
{"AIF4 MAD", NULL, "AIF4"},
+ {"EC BUF MUX INP", "DEC1", "ADC MUX1"},
+ {"AIF5 CPE", NULL, "EC BUF MUX INP"},
+
/* SLIMBUS Connections */
{"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
{"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
@@ -8203,6 +8260,9 @@ static const struct snd_kcontrol_new tasha_snd_controls[] = {
SOC_ENUM_EXT("ANC Function", tasha_anc_func_enum, tasha_get_anc_func,
tasha_put_anc_func),
+ SOC_ENUM_EXT("CLK MODE", tasha_clkmode_enum, tasha_get_clkmode,
+ tasha_put_clkmode),
+
SOC_ENUM("TX0 HPF cut off", cf_dec0_enum),
SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
@@ -9960,6 +10020,45 @@ static const struct snd_kcontrol_new anc0_fb_mux =
static const struct snd_kcontrol_new anc1_fb_mux =
SOC_DAPM_ENUM("ANC1 FB MUX Mux", anc1_fb_mux_enum);
+static int tasha_codec_ec_buf_mux_enable(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ dev_dbg(codec->dev, "%s: event = %d name = %s\n",
+ __func__, event, w->name);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x3B);
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x68, 0x28);
+ snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0,
+ 0x08, 0x08);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0,
+ 0x08, 0x00);
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x68, 0x40);
+ snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x00);
+ break;
+ }
+
+ return 0;
+};
+
+static const char * const ec_buf_mux_text[] = {
+ "ZERO", "RXMIXEC", "SB_RX0", "SB_RX1", "SB_RX2", "SB_RX3",
+ "I2S_RX_SD0_L", "I2S_RX_SD0_R", "I2S_RX_SD1_L", "I2S_RX_SD1_R",
+ "DEC1"
+};
+
+static SOC_ENUM_SINGLE_DECL(ec_buf_mux_enum, WCD9335_CPE_SS_US_EC_MUX_CFG,
+ 0, ec_buf_mux_text);
+
+static const struct snd_kcontrol_new ec_buf_mux =
+ SOC_DAPM_ENUM("EC BUF Mux", ec_buf_mux_enum);
+
static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("EAR"),
SND_SOC_DAPM_OUTPUT("ANC EAR"),
@@ -10449,6 +10548,14 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = {
aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)),
SND_SOC_DAPM_INPUT("VIINPUT"),
+
+ SND_SOC_DAPM_AIF_OUT("AIF5 CPE", "AIF5 CPE TX", 0, SND_SOC_NOPM,
+ AIF5_CPE_TX, 0),
+
+ SND_SOC_DAPM_MUX_E("EC BUF MUX INP", SND_SOC_NOPM, 0, 0, &ec_buf_mux,
+ tasha_codec_ec_buf_mux_enable,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
/* Digital Mic Inputs */
SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0,
tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
@@ -11432,6 +11539,19 @@ static struct snd_soc_dai_driver tasha_dai[] = {
},
.ops = &tasha_dai_ops,
},
+ {
+ .name = "tasha_cpe",
+ .id = AIF5_CPE_TX,
+ .capture = {
+ .stream_name = "AIF5 CPE TX",
+ .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000,
+ .formats = TASHA_FORMATS_S16_S24_S32_LE,
+ .rate_min = 16000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 1,
+ },
+ },
};
static struct snd_soc_dai_driver tasha_i2s_dai[] = {
@@ -11681,6 +11801,44 @@ int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable, bool dapm)
}
EXPORT_SYMBOL(tasha_cdc_mclk_enable);
+int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable, bool dapm)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ dev_dbg(tasha->dev, "%s: clk_mode: %d, enable: %d, clk_internal: %d\n",
+ __func__, tasha->clk_mode, enable, tasha->clk_internal);
+ if (tasha->clk_mode || tasha->clk_internal) {
+ if (enable) {
+ tasha_cdc_sido_ccl_enable(tasha, true);
+ wcd_resmgr_enable_master_bias(tasha->resmgr);
+ tasha_dig_core_power_collapse(tasha, POWER_RESUME);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+ 0x01, 0x01);
+ set_bit(CPE_NOMINAL, &tasha->status_mask);
+ tasha_codec_update_sido_voltage(tasha,
+ SIDO_VOLTAGE_NOMINAL_MV);
+ tasha->clk_internal = true;
+ } else {
+ tasha->clk_internal = false;
+ clear_bit(CPE_NOMINAL, &tasha->status_mask);
+ tasha_codec_update_sido_voltage(tasha,
+ sido_buck_svs_voltage);
+ tasha_dig_core_power_collapse(tasha, POWER_COLLAPSE);
+ wcd_resmgr_disable_master_bias(tasha->resmgr);
+ tasha_cdc_sido_ccl_enable(tasha, false);
+ }
+ } else {
+ ret = __tasha_cdc_mclk_enable(tasha, enable);
+ }
+ return ret;
+}
+EXPORT_SYMBOL(tasha_cdc_mclk_tx_enable);
+
static ssize_t tasha_codec_version_read(struct snd_info_entry *entry,
void *file_private_data, struct file *file,
char __user *buf, size_t count, loff_t pos)
@@ -12330,6 +12488,17 @@ static int tasha_handle_pdata(struct tasha_priv *tasha,
*/
pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate;
}
+ if (pdata->ecpp_dmic_sample_rate ==
+ WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+ dev_info(codec->dev,
+ "%s: ecpp_dmic_rate invalid default = %d\n",
+ __func__, def_dmic_rate);
+ /*
+ * use dmic_sample_rate as the default for ECPP DMIC
+ * if ecpp dmic sample rate is undefined
+ */
+ pdata->ecpp_dmic_sample_rate = pdata->dmic_sample_rate;
+ }
if (pdata->dmic_clk_drv ==
WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED) {
@@ -13098,6 +13267,20 @@ static int tasha_codec_probe(struct snd_soc_codec *codec)
control->tx_chs = ptr + sizeof(tasha_rx_chs);
memcpy(control->tx_chs, tasha_tx_chs, sizeof(tasha_tx_chs));
+ snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF2 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture");
+
+ if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+ snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF Mix Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX");
+ snd_soc_dapm_ignore_suspend(dapm, "VIfeed");
+ }
+
snd_soc_dapm_sync(dapm);
ret = tasha_setup_irqs(tasha);
diff --git a/sound/soc/codecs/wcd9335.h b/sound/soc/codecs/wcd9335.h
index 67280c583b2a..8d38399ba92f 100644
--- a/sound/soc/codecs/wcd9335.h
+++ b/sound/soc/codecs/wcd9335.h
@@ -179,6 +179,8 @@ extern void *tasha_get_afe_config(struct snd_soc_codec *codec,
enum afe_config_type config_type);
extern int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable,
bool dapm);
+extern int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable,
+ bool dapm);
extern int tasha_enable_efuse_sensing(struct snd_soc_codec *codec);
extern int tasha_mbhc_hs_detect(struct snd_soc_codec *codec,
struct wcd_mbhc_config *mbhc_cfg);
diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c
index 75387b7c2069..d7103f1ff00f 100644
--- a/sound/soc/codecs/wcd934x/wcd934x.c
+++ b/sound/soc/codecs/wcd934x/wcd934x.c
@@ -4924,14 +4924,18 @@ static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = {
{WCD934X_CDC_RX0_RX_PATH_SEC0, 0x08, 0x0},
{WCD934X_CDC_CLSH_DECAY_CTRL, 0x03, 0x0},
{WCD934X_MICB1_TEST_CTL_2, 0x07, 0x01},
+ {WCD934X_CDC_BOOST0_BOOST_CFG1, 0x3F, 0x12},
+ {WCD934X_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08},
+ {WCD934X_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12},
+ {WCD934X_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08},
};
static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = {
{WCD934X_CDC_CLSH_K2_MSB, 0x0F, 0x00},
{WCD934X_CDC_CLSH_K2_LSB, 0xFF, 0x60},
{WCD934X_CPE_SS_DMIC_CFG, 0x80, 0x00},
- {WCD934X_CDC_BOOST0_BOOST_CTL, 0x70, 0x40},
- {WCD934X_CDC_BOOST1_BOOST_CTL, 0x70, 0x40},
+ {WCD934X_CDC_BOOST0_BOOST_CTL, 0x70, 0x50},
+ {WCD934X_CDC_BOOST1_BOOST_CTL, 0x70, 0x50},
{WCD934X_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08},
{WCD934X_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08},
{WCD934X_CDC_TOP_TOP_CFG1, 0x02, 0x02},
@@ -4949,6 +4953,10 @@ static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = {
{WCD934X_CDC_RX8_RX_PATH_MIX_CFG, 0x01, 0x01},
{WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0x01, 0x01},
{WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x01, 0x01},
+ {WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x80},
+ {WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x80},
+ {WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x01},
+ {WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x01},
};
static void tavil_codec_init_reg(struct snd_soc_codec *codec)
diff --git a/sound/soc/msm/msm-cpe-lsm.c b/sound/soc/msm/msm-cpe-lsm.c
index a509107ea9f2..45eed236c5c9 100644
--- a/sound/soc/msm/msm-cpe-lsm.c
+++ b/sound/soc/msm/msm-cpe-lsm.c
@@ -28,7 +28,11 @@
#include <sound/pcm_params.h>
#include <sound/msm-slim-dma.h>
+#define SAMPLE_RATE_48KHZ 48000
+#define SAMPLE_RATE_16KHZ 16000
#define LSM_VOICE_WAKEUP_APP_V2 2
+#define AFE_PORT_ID_1 1
+#define AFE_PORT_ID_3 3
#define AFE_OUT_PORT_2 2
#define LISTEN_MIN_NUM_PERIODS 2
#define LISTEN_MAX_NUM_PERIODS 12
@@ -135,6 +139,7 @@ struct cpe_priv {
struct wcd_cpe_lsm_ops lsm_ops;
struct wcd_cpe_afe_ops afe_ops;
bool afe_mad_ctl;
+ u32 input_port_id;
};
struct cpe_lsm_data {
@@ -1156,12 +1161,6 @@ static int msm_cpe_lsm_ioctl_shared(struct snd_pcm_substream *substream,
__func__, rc);
return rc;
}
- rc = lsm_ops->lsm_lab_control(cpe->core_handle,
- session, false);
- if (IS_ERR_VALUE(rc))
- dev_err(rtd->dev,
- "%s: Lab Disable Failed rc %d\n",
- __func__, rc);
/*
* Buffer has to be de-allocated even if
* lab_control failed.
@@ -1390,14 +1389,6 @@ static int msm_cpe_lsm_ioctl_shared(struct snd_pcm_substream *substream,
dev_dbg(rtd->dev,
"%s: %s\n",
__func__, "SNDRV_LSM_START");
- rc = lsm_ops->lsm_get_afe_out_port_id(cpe->core_handle,
- session);
- if (rc != 0) {
- dev_err(rtd->dev,
- "%s: failed to get port id, err = %d\n",
- __func__, rc);
- return rc;
- }
rc = lsm_ops->lsm_start(cpe->core_handle, session);
if (rc != 0) {
dev_err(rtd->dev,
@@ -2680,6 +2671,8 @@ static int msm_cpe_lsm_prepare(struct snd_pcm_substream *substream)
struct cpe_lsm_session *lsm_session;
struct cpe_lsm_lab *lab_d = &lsm_d->lab;
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_hw_params lsm_param;
+ struct wcd_cpe_lsm_ops *lsm_ops;
if (!cpe || !cpe->core_handle) {
dev_err(rtd->dev,
@@ -2712,23 +2705,74 @@ static int msm_cpe_lsm_prepare(struct snd_pcm_substream *substream)
return 0;
}
+ lsm_ops = &cpe->lsm_ops;
afe_ops = &cpe->afe_ops;
afe_cfg = &(lsm_d->lsm_session->afe_port_cfg);
- afe_cfg->port_id = 1;
- afe_cfg->bit_width = 16;
- afe_cfg->num_channels = 1;
- afe_cfg->sample_rate = 16000;
+ switch (cpe->input_port_id) {
+ case AFE_PORT_ID_3:
+ afe_cfg->port_id = AFE_PORT_ID_3;
+ afe_cfg->bit_width = 16;
+ afe_cfg->num_channels = 1;
+ afe_cfg->sample_rate = SAMPLE_RATE_48KHZ;
+ rc = afe_ops->afe_port_cmd_cfg(cpe->core_handle, afe_cfg);
+ break;
+ case AFE_PORT_ID_1:
+ default:
+ afe_cfg->port_id = AFE_PORT_ID_1;
+ afe_cfg->bit_width = 16;
+ afe_cfg->num_channels = 1;
+ afe_cfg->sample_rate = SAMPLE_RATE_16KHZ;
+ rc = afe_ops->afe_set_params(cpe->core_handle,
+ afe_cfg, cpe->afe_mad_ctl);
+ break;
+ }
- rc = afe_ops->afe_set_params(cpe->core_handle,
- afe_cfg, cpe->afe_mad_ctl);
if (rc != 0) {
dev_err(rtd->dev,
- "%s: cpe afe params failed, err = %d\n",
- __func__, rc);
+ "%s: cpe afe params failed for port = %d, err = %d\n",
+ __func__, afe_cfg->port_id, rc);
+ return rc;
+ }
+ lsm_param.sample_rate = afe_cfg->sample_rate;
+ lsm_param.num_chs = afe_cfg->num_channels;
+ lsm_param.bit_width = afe_cfg->bit_width;
+ rc = lsm_ops->lsm_set_media_fmt_params(cpe->core_handle, lsm_session,
+ &lsm_param);
+ if (rc)
+ dev_dbg(rtd->dev,
+ "%s: failed to set lsm media fmt params, err = %d\n",
+ __func__, rc);
+
+ /* Send connect to port (input) */
+ rc = lsm_ops->lsm_set_port(cpe->core_handle, lsm_session,
+ &cpe->input_port_id);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: Failed to set connect input port, err=%d\n",
+ __func__, rc);
return rc;
}
+ if (cpe->input_port_id != 3) {
+ rc = lsm_ops->lsm_get_afe_out_port_id(cpe->core_handle,
+ lsm_session);
+ if (rc != 0) {
+ dev_err(rtd->dev,
+ "%s: failed to get port id, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ /* Send connect to port (output) */
+ rc = lsm_ops->lsm_set_port(cpe->core_handle, lsm_session,
+ &lsm_session->afe_out_port_id);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: Failed to set connect output port, err=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
rc = msm_cpe_afe_port_cntl(substream,
cpe->core_handle,
afe_ops, afe_cfg,
@@ -2978,6 +3022,9 @@ static int msm_asoc_cpe_lsm_probe(struct snd_soc_platform *platform)
struct cpe_priv *cpe_priv;
const struct snd_kcontrol_new *kcontrol;
bool found_runtime = false;
+ const char *cpe_dev_id = "qcom,msm-cpe-lsm-id";
+ u32 port_id = 0;
+ int ret = 0;
int i;
if (!platform || !platform->component.card) {
@@ -3007,6 +3054,14 @@ static int msm_asoc_cpe_lsm_probe(struct snd_soc_platform *platform)
return -EINVAL;
}
+ ret = of_property_read_u32(platform->dev->of_node, cpe_dev_id,
+ &port_id);
+ if (ret) {
+ dev_dbg(platform->dev,
+ "%s: missing 0x%x in dt node\n", __func__, port_id);
+ port_id = 1;
+ }
+
codec = rtd->codec;
cpe_priv = kzalloc(sizeof(struct cpe_priv),
@@ -3019,6 +3074,7 @@ static int msm_asoc_cpe_lsm_probe(struct snd_soc_platform *platform)
}
cpe_priv->codec = codec;
+ cpe_priv->input_port_id = port_id;
wcd_cpe_get_lsm_ops(&cpe_priv->lsm_ops);
wcd_cpe_get_afe_ops(&cpe_priv->afe_ops);
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index e98e31c77e7a..85fa45ff400f 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -63,6 +63,7 @@ static int fe_dai_probe(struct snd_soc_dai *dai)
dev_dbg(dai->dev, "%s src %s sink %s\n",
__func__, intercon.source, intercon.sink);
snd_soc_dapm_add_routes(dapm, &intercon, 1);
+ snd_soc_dapm_ignore_suspend(dapm, intercon.source);
}
if (dai->driver->capture.stream_name &&
dai->driver->capture.aif_name) {
@@ -73,6 +74,7 @@ static int fe_dai_probe(struct snd_soc_dai *dai)
dev_dbg(dai->dev, "%s src %s sink %s\n",
__func__, intercon.source, intercon.sink);
snd_soc_dapm_add_routes(dapm, &intercon, 1);
+ snd_soc_dapm_ignore_suspend(dapm, intercon.sink);
}
return 0;
}
diff --git a/sound/soc/msm/msm8996.c b/sound/soc/msm/msm8996.c
index 1d4f723dd572..225726cad812 100644
--- a/sound/soc/msm/msm8996.c
+++ b/sound/soc/msm/msm8996.c
@@ -488,6 +488,37 @@ static int msm8996_mclk_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec,
+ int enable, bool dapm)
+{
+ int ret = 0;
+
+ if (!strcmp(dev_name(codec->dev), "tasha_codec"))
+ ret = tasha_cdc_mclk_tx_enable(codec, enable, dapm);
+ else {
+ dev_err(codec->dev, "%s: unknown codec to enable ext clk\n",
+ __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int msm8996_mclk_tx_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ pr_debug("%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return msm_snd_enable_codec_ext_tx_clk(codec, 1, true);
+ case SND_SOC_DAPM_POST_PMD:
+ return msm_snd_enable_codec_ext_tx_clk(codec, 0, true);
+ }
+ return 0;
+}
+
static int msm_hifi_ctrl_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
@@ -532,6 +563,9 @@ static const struct snd_soc_dapm_widget msm8996_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
msm8996_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0,
+ msm8996_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
SND_SOC_DAPM_SPK("Lineout_1 amp", NULL),
SND_SOC_DAPM_SPK("Lineout_3 amp", NULL),
SND_SOC_DAPM_SPK("Lineout_2 amp", NULL),
@@ -557,10 +591,10 @@ static const struct snd_soc_dapm_widget msm8996_dapm_widgets[] = {
};
static struct snd_soc_dapm_route wcd9335_audio_paths[] = {
- {"MIC BIAS1", NULL, "MCLK"},
- {"MIC BIAS2", NULL, "MCLK"},
- {"MIC BIAS3", NULL, "MCLK"},
- {"MIC BIAS4", NULL, "MCLK"},
+ {"MIC BIAS1", NULL, "MCLK TX"},
+ {"MIC BIAS2", NULL, "MCLK TX"},
+ {"MIC BIAS3", NULL, "MCLK TX"},
+ {"MIC BIAS4", NULL, "MCLK TX"},
};
static int slim5_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
@@ -2999,6 +3033,20 @@ static struct snd_soc_dai_link msm8996_tasha_fe_dai_links[] = {
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
+ /* CPE LSM EC PP direct dai-link */
+ {
+ .name = "CPE Listen service ECPP",
+ .stream_name = "CPE Listen Audio Service ECPP",
+ .cpu_dai_name = "CPE_LSM_NOHOST",
+ .platform_name = "msm-cpe-lsm.3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "tasha_cpe",
+ .codec_name = "tasha_codec",
+ },
};
static struct snd_soc_dai_link msm8996_common_be_dai_links[] = {
diff --git a/sound/soc/msm/msmcobalt.c b/sound/soc/msm/msmcobalt.c
index 706a9aec6a89..945250babf2d 100644
--- a/sound/soc/msm/msmcobalt.c
+++ b/sound/soc/msm/msmcobalt.c
@@ -170,7 +170,7 @@ static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four",
"Five", "Six", "Seven",
"Eight"};
static const char *const vi_feed_ch_text[] = {"One", "Two"};
-static char const *bit_format_text[] = {"S16_LE", "S24_LE"};
+static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"};
static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16",
"KHZ_32", "KHZ_44P1", "KHZ_48",
"KHZ_96", "KHZ_192"};
@@ -325,6 +325,9 @@ static int slim_get_bit_format_val(int bit_format)
int val = 0;
switch (bit_format) {
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ val = 2;
+ break;
case SNDRV_PCM_FORMAT_S24_LE:
val = 1;
break;
@@ -347,6 +350,9 @@ static int slim_get_bit_format(int val)
case 1:
bit_fmt = SNDRV_PCM_FORMAT_S24_LE;
break;
+ case 2:
+ bit_fmt = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
default:
bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
break;
@@ -749,6 +755,9 @@ static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
switch (usb_rx_cfg.bit_format) {
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
case SNDRV_PCM_FORMAT_S24_LE:
ucontrol->value.integer.value[0] = 1;
break;
@@ -770,6 +779,9 @@ static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol,
int rc = 0;
switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
case 1:
usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE;
break;
@@ -893,6 +905,9 @@ static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
switch (usb_tx_cfg.bit_format) {
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
case SNDRV_PCM_FORMAT_S24_LE:
ucontrol->value.integer.value[0] = 1;
break;
@@ -914,6 +929,9 @@ static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol,
int rc = 0;
switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
case 1:
usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE;
break;
@@ -1058,7 +1076,7 @@ static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol,
{
pr_debug("%s: proxy_rx channels = %d\n",
__func__, proxy_rx_cfg.channels);
- ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 1;
+ ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2;
return 0;
}
@@ -1066,7 +1084,7 @@ static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol,
static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 1;
+ proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2;
pr_debug("%s: proxy_rx channels = %d\n",
__func__, proxy_rx_cfg.channels);
@@ -1213,6 +1231,40 @@ static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit)
}
}
+static int msm_slim_get_ch_from_beid(int32_t be_id)
+{
+ int ch_id = 0;
+
+ switch (be_id) {
+ case MSM_BACKEND_DAI_SLIMBUS_0_RX:
+ ch_id = SLIM_RX_0;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_1_RX:
+ ch_id = SLIM_RX_1;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_3_RX:
+ ch_id = SLIM_RX_3;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_4_RX:
+ ch_id = SLIM_RX_4;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_6_RX:
+ ch_id = SLIM_RX_6;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_0_TX:
+ ch_id = SLIM_TX_0;
+ break;
+ case MSM_BACKEND_DAI_SLIMBUS_3_TX:
+ ch_id = SLIM_TX_3;
+ break;
+ default:
+ ch_id = SLIM_RX_0;
+ break;
+ }
+
+ return ch_id;
+}
+
static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -1224,6 +1276,7 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
int rc = 0;
void *config = NULL;
struct snd_soc_codec *codec = NULL;
+ int ch_num;
pr_debug("%s: format = %d, rate = %d\n",
__func__, params_format(params), params_rate(params));
@@ -1234,18 +1287,20 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
case MSM_BACKEND_DAI_SLIMBUS_3_RX:
case MSM_BACKEND_DAI_SLIMBUS_4_RX:
case MSM_BACKEND_DAI_SLIMBUS_6_RX:
+ ch_num = msm_slim_get_ch_from_beid(dai_link->be_id);
param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
- slim_rx_cfg[0].bit_format);
- rate->min = rate->max = slim_rx_cfg[0].sample_rate;
- channels->min = channels->max = slim_rx_cfg[0].channels;
+ slim_rx_cfg[ch_num].bit_format);
+ rate->min = rate->max = slim_rx_cfg[ch_num].sample_rate;
+ channels->min = channels->max = slim_rx_cfg[ch_num].channels;
break;
case MSM_BACKEND_DAI_SLIMBUS_0_TX:
case MSM_BACKEND_DAI_SLIMBUS_3_TX:
+ ch_num = msm_slim_get_ch_from_beid(dai_link->be_id);
param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
- slim_tx_cfg[0].bit_format);
- rate->min = rate->max = slim_tx_cfg[0].sample_rate;
- channels->min = channels->max = slim_tx_cfg[0].channels;
+ slim_tx_cfg[ch_num].bit_format);
+ rate->min = rate->max = slim_tx_cfg[ch_num].sample_rate;
+ channels->min = channels->max = slim_tx_cfg[ch_num].channels;
break;
case MSM_BACKEND_DAI_SLIMBUS_1_TX:
@@ -1320,15 +1375,11 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
case MSM_BACKEND_DAI_HDMI_RX:
param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
hdmi_rx_cfg.bit_format);
- if (channels->max < 2)
- channels->min = channels->max = 2;
rate->min = rate->max = hdmi_rx_cfg.sample_rate;
channels->min = channels->max = hdmi_rx_cfg.channels;
break;
case MSM_BACKEND_DAI_AFE_PCM_RX:
- if (channels->max < 2)
- channels->min = channels->max = 2;
channels->min = channels->max = proxy_rx_cfg.channels;
rate->min = rate->max = SAMPLING_RATE_48KHZ;
break;
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index f577637ee2b2..26528e6a2bb8 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -1070,6 +1070,7 @@ static int msm_compr_ioctl_shared(struct snd_pcm_substream *substream,
__func__, ddp->params_length);
return -EINVAL;
}
+ params_length = ddp->params_length*sizeof(int);
if (params_length > MAX_AC3_PARAM_SIZE) {
/*MAX is 36*sizeof(int) this should not happen*/
pr_err("%s: params_length(%d) is greater than %zd\n",
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
index 3c75e30fb419..ff7cf5812c0c 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
@@ -50,7 +50,7 @@ struct msm_pcm_loopback {
int capture_start;
int session_id;
struct audio_client *audio_client;
- int volume;
+ uint32_t volume;
};
struct fe_dai_session_map {
@@ -65,6 +65,8 @@ static struct fe_dai_session_map session_map[LOOPBACK_SESSION_MAX] = {
{ {}, NULL},
};
+static u32 hfp_tx_mute;
+
static void stop_pcm(struct msm_pcm_loopback *pcm);
static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd,
struct msm_pcm_loopback **pcm);
@@ -109,6 +111,13 @@ static void msm_pcm_loopback_event_handler(uint32_t opcode, uint32_t token,
}
}
+static int msm_loopback_session_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = hfp_tx_mute;
+ return 0;
+}
+
static int msm_loopback_session_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -123,7 +132,7 @@ static int msm_loopback_session_mute_put(struct snd_kcontrol *kcontrol,
}
pr_debug("%s: mute=%d\n", __func__, mute);
-
+ hfp_tx_mute = mute;
for (n = 0; n < LOOPBACK_SESSION_MAX; n++) {
if (!strcmp(session_map[n].stream_name, "MultiMedia6"))
pcm = session_map[n].loopback_priv;
@@ -140,7 +149,8 @@ done:
static struct snd_kcontrol_new msm_loopback_controls[] = {
SOC_SINGLE_EXT("HFP TX Mute", SND_SOC_NOPM, 0, 1, 0,
- NULL, msm_loopback_session_mute_put),
+ msm_loopback_session_mute_get,
+ msm_loopback_session_mute_put),
};
static int msm_pcm_loopback_probe(struct snd_soc_platform *platform)
@@ -150,7 +160,8 @@ static int msm_pcm_loopback_probe(struct snd_soc_platform *platform)
return 0;
}
-static int pcm_loopback_set_volume(struct msm_pcm_loopback *prtd, int volume)
+static int pcm_loopback_set_volume(struct msm_pcm_loopback *prtd,
+ uint32_t volume)
{
int rc = -EINVAL;
@@ -459,10 +470,49 @@ static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol,
int rc = 0;
struct snd_pcm_volume *vol = kcontrol->private_data;
struct snd_pcm_substream *substream = vol->pcm->streams[0].substream;
- struct msm_pcm_loopback *prtd = substream->runtime->private_data;
+ struct msm_pcm_loopback *prtd;
int volume = ucontrol->value.integer.value[0];
+ pr_debug("%s: volume : 0x%x\n", __func__, volume);
+ if ((!substream) || (!substream->runtime)) {
+ pr_err("%s substream or runtime not found\n", __func__);
+ rc = -ENODEV;
+ goto exit;
+ }
+ prtd = substream->runtime->private_data;
+ if (!prtd) {
+ rc = -ENODEV;
+ goto exit;
+ }
rc = pcm_loopback_set_volume(prtd, volume);
+
+exit:
+ return rc;
+}
+
+static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+ struct snd_pcm_substream *substream =
+ vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ struct msm_pcm_loopback *prtd;
+
+ pr_debug("%s\n", __func__);
+ if ((!substream) || (!substream->runtime)) {
+ pr_err("%s substream or runtime not found\n", __func__);
+ rc = -ENODEV;
+ goto exit;
+ }
+ prtd = substream->runtime->private_data;
+ if (!prtd) {
+ rc = -ENODEV;
+ goto exit;
+ }
+ ucontrol->value.integer.value[0] = prtd->volume;
+
+exit:
return rc;
}
@@ -482,6 +532,7 @@ static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
return ret;
kctl = volume_info->kctl;
kctl->put = msm_pcm_volume_ctl_put;
+ kctl->get = msm_pcm_volume_ctl_get;
kctl->tlv.p = loopback_rx_vol_gain;
return 0;
}
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index 8efa04c3807e..b76cb7f4b210 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -3312,7 +3312,7 @@ int afe_loopback(u16 enable, u16 rx_port, u16 tx_port)
sizeof(struct afe_port_param_data_v2);
lb_cmd.dst_port_id = rx_port;
- lb_cmd.routing_mode = LB_MODE_EC_REF_VOICE_AUDIO;
+ lb_cmd.routing_mode = LB_MODE_DEFAULT;
lb_cmd.enable = (enable ? 1 : 0);
lb_cmd.loopback_cfg_minor_version = AFE_API_VERSION_LOOPBACK_CONFIG;
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
index e4145509d63c..9c5219392460 100644
--- a/sound/soc/samsung/ac97.c
+++ b/sound/soc/samsung/ac97.c
@@ -324,7 +324,7 @@ static const struct snd_soc_component_driver s3c_ac97_component = {
static int s3c_ac97_probe(struct platform_device *pdev)
{
- struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
+ struct resource *mem_res, *irq_res;
struct s3c_audio_pdata *ac97_pdata;
int ret;
@@ -335,24 +335,6 @@ static int s3c_ac97_probe(struct platform_device *pdev)
}
/* Check for availability of necessary resource */
- dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!dmatx_res) {
- dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n");
- return -ENXIO;
- }
-
- dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!dmarx_res) {
- dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n");
- return -ENXIO;
- }
-
- dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
- if (!dmamic_res) {
- dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n");
- return -ENXIO;
- }
-
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq_res) {
dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
@@ -364,11 +346,11 @@ static int s3c_ac97_probe(struct platform_device *pdev)
if (IS_ERR(s3c_ac97.regs))
return PTR_ERR(s3c_ac97.regs);
- s3c_ac97_pcm_out.channel = dmatx_res->start;
+ s3c_ac97_pcm_out.slave = ac97_pdata->dma_playback;
s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
- s3c_ac97_pcm_in.channel = dmarx_res->start;
+ s3c_ac97_pcm_in.slave = ac97_pdata->dma_capture;
s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
- s3c_ac97_mic_in.channel = dmamic_res->start;
+ s3c_ac97_mic_in.slave = ac97_pdata->dma_capture_mic;
s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
init_completion(&s3c_ac97.done);
diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h
index 0e85dcfec023..085ef30f5ca2 100644
--- a/sound/soc/samsung/dma.h
+++ b/sound/soc/samsung/dma.h
@@ -15,7 +15,7 @@
#include <sound/dmaengine_pcm.h>
struct s3c_dma_params {
- int channel; /* Channel ID */
+ void *slave; /* Channel ID */
dma_addr_t dma_addr;
int dma_size; /* Size of the DMA transfer */
char *ch_name;
diff --git a/sound/soc/samsung/dmaengine.c b/sound/soc/samsung/dmaengine.c
index 506f5bf6d082..727008d57d14 100644
--- a/sound/soc/samsung/dmaengine.c
+++ b/sound/soc/samsung/dmaengine.c
@@ -50,14 +50,14 @@ void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
if (playback) {
playback_data = &playback->dma_data;
- playback_data->filter_data = (void *)playback->channel;
+ playback_data->filter_data = playback->slave;
playback_data->chan_name = playback->ch_name;
playback_data->addr = playback->dma_addr;
playback_data->addr_width = playback->dma_size;
}
if (capture) {
capture_data = &capture->dma_data;
- capture_data->filter_data = (void *)capture->channel;
+ capture_data->filter_data = capture->slave;
capture_data->chan_name = capture->ch_name;
capture_data->addr = capture->dma_addr;
capture_data->addr_width = capture->dma_size;
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 7dbf899b2af2..e163b0148c4b 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -1260,27 +1260,14 @@ static int samsung_i2s_probe(struct platform_device *pdev)
pri_dai->lock = &pri_dai->spinlock;
if (!np) {
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!res) {
- dev_err(&pdev->dev,
- "Unable to get I2S-TX dma resource\n");
- return -ENXIO;
- }
- pri_dai->dma_playback.channel = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!res) {
- dev_err(&pdev->dev,
- "Unable to get I2S-RX dma resource\n");
- return -ENXIO;
- }
- pri_dai->dma_capture.channel = res->start;
-
if (i2s_pdata == NULL) {
dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n");
return -EINVAL;
}
+ pri_dai->dma_playback.slave = i2s_pdata->dma_playback;
+ pri_dai->dma_capture.slave = i2s_pdata->dma_capture;
+
if (&i2s_pdata->type)
i2s_cfg = &i2s_pdata->type.i2s;
@@ -1341,11 +1328,8 @@ static int samsung_i2s_probe(struct platform_device *pdev)
sec_dai->dma_playback.dma_addr = regs_base + I2STXDS;
sec_dai->dma_playback.ch_name = "tx-sec";
- if (!np) {
- res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
- if (res)
- sec_dai->dma_playback.channel = res->start;
- }
+ if (!np)
+ sec_dai->dma_playback.slave = i2s_pdata->dma_play_sec;
sec_dai->dma_playback.dma_size = 4;
sec_dai->addr = pri_dai->addr;
diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c
index b320a9d3fbf8..c77f324e0bb8 100644
--- a/sound/soc/samsung/pcm.c
+++ b/sound/soc/samsung/pcm.c
@@ -486,7 +486,7 @@ static const struct snd_soc_component_driver s3c_pcm_component = {
static int s3c_pcm_dev_probe(struct platform_device *pdev)
{
struct s3c_pcm_info *pcm;
- struct resource *mem_res, *dmatx_res, *dmarx_res;
+ struct resource *mem_res;
struct s3c_audio_pdata *pcm_pdata;
int ret;
@@ -499,18 +499,6 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
pcm_pdata = pdev->dev.platform_data;
/* Check for availability of necessary resource */
- dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!dmatx_res) {
- dev_err(&pdev->dev, "Unable to get PCM-TX dma resource\n");
- return -ENXIO;
- }
-
- dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!dmarx_res) {
- dev_err(&pdev->dev, "Unable to get PCM-RX dma resource\n");
- return -ENXIO;
- }
-
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_res) {
dev_err(&pdev->dev, "Unable to get register resource\n");
@@ -568,8 +556,10 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
s3c_pcm_stereo_out[pdev->id].dma_addr = mem_res->start
+ S3C_PCM_TXFIFO;
- s3c_pcm_stereo_in[pdev->id].channel = dmarx_res->start;
- s3c_pcm_stereo_out[pdev->id].channel = dmatx_res->start;
+ if (pcm_pdata) {
+ s3c_pcm_stereo_in[pdev->id].slave = pcm_pdata->dma_capture;
+ s3c_pcm_stereo_out[pdev->id].slave = pcm_pdata->dma_playback;
+ }
pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id];
pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id];
diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c
index 2b766d212ce0..77d27c85a32a 100644
--- a/sound/soc/samsung/s3c2412-i2s.c
+++ b/sound/soc/samsung/s3c2412-i2s.c
@@ -34,13 +34,13 @@
#include "s3c2412-i2s.h"
static struct s3c_dma_params s3c2412_i2s_pcm_stereo_out = {
- .channel = DMACH_I2S_OUT,
+ .slave = (void *)(uintptr_t)DMACH_I2S_OUT,
.ch_name = "tx",
.dma_size = 4,
};
static struct s3c_dma_params s3c2412_i2s_pcm_stereo_in = {
- .channel = DMACH_I2S_IN,
+ .slave = (void *)(uintptr_t)DMACH_I2S_IN,
.ch_name = "rx",
.dma_size = 4,
};
diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c
index 5bf723689692..9da3a77ea2c7 100644
--- a/sound/soc/samsung/s3c24xx-i2s.c
+++ b/sound/soc/samsung/s3c24xx-i2s.c
@@ -32,13 +32,13 @@
#include "s3c24xx-i2s.h"
static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_out = {
- .channel = DMACH_I2S_OUT,
+ .slave = (void *)(uintptr_t)DMACH_I2S_OUT,
.ch_name = "tx",
.dma_size = 2,
};
static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_in = {
- .channel = DMACH_I2S_IN,
+ .slave = (void *)(uintptr_t)DMACH_I2S_IN,
.ch_name = "rx",
.dma_size = 2,
};
diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c
index 36dbc0e96004..9dd7ee6d03ff 100644
--- a/sound/soc/samsung/spdif.c
+++ b/sound/soc/samsung/spdif.c
@@ -359,7 +359,7 @@ static const struct snd_soc_component_driver samsung_spdif_component = {
static int spdif_probe(struct platform_device *pdev)
{
struct s3c_audio_pdata *spdif_pdata;
- struct resource *mem_res, *dma_res;
+ struct resource *mem_res;
struct samsung_spdif_info *spdif;
int ret;
@@ -367,12 +367,6 @@ static int spdif_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "Entered %s\n", __func__);
- dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!dma_res) {
- dev_err(&pdev->dev, "Unable to get dma resource.\n");
- return -ENXIO;
- }
-
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_res) {
dev_err(&pdev->dev, "Unable to get register resource.\n");
@@ -432,7 +426,7 @@ static int spdif_probe(struct platform_device *pdev)
spdif_stereo_out.dma_size = 2;
spdif_stereo_out.dma_addr = mem_res->start + DATA_OUTBUF;
- spdif_stereo_out.channel = dma_res->start;
+ spdif_stereo_out.slave = spdif_pdata ? spdif_pdata->dma_playback : NULL;
spdif->dma_playback = &spdif_stereo_out;
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 2ed260b10f6d..7ccbcaf6a147 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -285,6 +285,8 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
unsigned char data[3];
int err, crate;
+ if (get_iface_desc(alts)->bNumEndpoints < 1)
+ return -EINVAL;
ep = get_endpoint(alts, 0)->bEndpointAddress;
/* if endpoint doesn't have sampling rate control, bail out */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 7b1cb365ffab..c07a7eda42a2 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -438,6 +438,9 @@ exit_clear:
*
* New endpoints will be added to chip->ep_list and must be freed by
* calling snd_usb_endpoint_free().
+ *
+ * For SND_USB_ENDPOINT_TYPE_SYNC, the caller needs to guarantee that
+ * bNumEndpoints > 1 beforehand.
*/
struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
struct usb_host_interface *alts,
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index ddca6547399b..1f8fb0d904e0 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -349,6 +349,16 @@ static struct usbmix_name_map bose_companion5_map[] = {
};
/*
+ * Dell usb dock with ALC4020 codec had a firmware problem where it got
+ * screwed up when zero volume is passed; just skip it as a workaround
+ */
+static const struct usbmix_name_map dell_alc4020_map[] = {
+ { 16, NULL },
+ { 19, NULL },
+ { 0 }
+};
+
+/*
* Control map entries
*/
@@ -431,6 +441,10 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
.map = aureon_51_2_map,
},
{
+ .id = USB_ID(0x0bda, 0x4014),
+ .map = dell_alc4020_map,
+ },
+ {
.id = USB_ID(0x0dba, 0x1000),
.map = mbox1_map,
},
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 279025650568..f6c3bf79af9a 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -1519,7 +1519,11 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
/* use known values for that card: interface#1 altsetting#1 */
iface = usb_ifnum_to_if(chip->dev, 1);
+ if (!iface || iface->num_altsetting < 2)
+ return -EINVAL;
alts = &iface->altsetting[1];
+ if (get_iface_desc(alts)->bNumEndpoints < 1)
+ return -EINVAL;
ep = get_endpoint(alts, 0)->bEndpointAddress;
err = snd_usb_ctl_msg(chip->dev,
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 9a9b789a56ef..3033bf7ce309 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -159,6 +159,8 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
unsigned char data[1];
int err;
+ if (get_iface_desc(alts)->bNumEndpoints < 1)
+ return -EINVAL;
ep = get_endpoint(alts, 0)->bEndpointAddress;
data[0] = 1;
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index c458d60d5030..001fb4dc0722 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -150,6 +150,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
usb_audio_err(chip, "cannot memdup\n");
return -ENOMEM;
}
+ INIT_LIST_HEAD(&fp->list);
if (fp->nr_rates > MAX_NR_RATES) {
kfree(fp);
return -EINVAL;
@@ -167,19 +168,20 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
stream = (fp->endpoint & USB_DIR_IN)
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
err = snd_usb_add_audio_stream(chip, stream, fp);
- if (err < 0) {
- kfree(fp);
- kfree(rate_table);
- return err;
- }
+ if (err < 0)
+ goto error;
if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber ||
fp->altset_idx >= iface->num_altsetting) {
- kfree(fp);
- kfree(rate_table);
- return -EINVAL;
+ err = -EINVAL;
+ goto error;
}
alts = &iface->altsetting[fp->altset_idx];
altsd = get_iface_desc(alts);
+ if (altsd->bNumEndpoints < 1) {
+ err = -EINVAL;
+ goto error;
+ }
+
fp->protocol = altsd->bInterfaceProtocol;
if (fp->datainterval == 0)
@@ -190,6 +192,12 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
snd_usb_init_pitch(chip, fp->iface, alts, fp);
snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max);
return 0;
+
+ error:
+ list_del(&fp->list); /* unlink for avoiding double-free */
+ kfree(fp);
+ kfree(rate_table);
+ return err;
}
static int create_auto_pcm_quirk(struct snd_usb_audio *chip,
@@ -462,6 +470,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
fp->datainterval = 0;
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+ INIT_LIST_HEAD(&fp->list);
switch (fp->maxpacksize) {
case 0x120:
@@ -485,6 +494,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0) {
+ list_del(&fp->list); /* unlink for avoiding double-free */
kfree(fp);
return err;
}
@@ -1121,12 +1131,15 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
switch (chip->usb_id) {
case USB_ID(0x045E, 0x075D): /* MS Lifecam Cinema */
case USB_ID(0x045E, 0x076D): /* MS Lifecam HD-5000 */
+ case USB_ID(0x045E, 0x076E): /* MS Lifecam HD-5001 */
case USB_ID(0x045E, 0x076F): /* MS Lifecam HD-6000 */
case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */
case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */
+ case USB_ID(0x047F, 0x0415): /* Plantronics BT-300 */
case USB_ID(0x047F, 0xAA05): /* Plantronics DA45 */
case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */
case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */
+ case USB_ID(0x1de7, 0x0014): /* Phoenix Audio TMX320 */
case USB_ID(0x21B4, 0x0081): /* AudioQuest DragonFly */
return true;
}
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 81172a395e55..98b2c28ae46b 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -321,7 +321,9 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
/*
* add this endpoint to the chip instance.
* if a stream with the same endpoint already exists, append to it.
- * if not, create a new pcm stream.
+ * if not, create a new pcm stream. note, fp is added to the substream
+ * fmt_list and will be freed on the chip instance release. do not free
+ * fp or do remove it from the substream fmt_list to avoid double-free.
*/
int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
int stream,
@@ -682,6 +684,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
* (fp->maxpacksize & 0x7ff);
fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no);
fp->clock = clock;
+ INIT_LIST_HEAD(&fp->list);
/* some quirks for attributes here */
@@ -730,6 +733,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint);
err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0) {
+ list_del(&fp->list); /* unlink for avoiding double-free */
kfree(fp->rate_table);
kfree(fp->chmap);
kfree(fp);
diff --git a/tools/hv/Makefile b/tools/hv/Makefile
index a8ab79556926..a8c4644022a6 100644
--- a/tools/hv/Makefile
+++ b/tools/hv/Makefile
@@ -5,6 +5,8 @@ PTHREAD_LIBS = -lpthread
WARNINGS = -Wall -Wextra
CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS) $(shell getconf LFS_CFLAGS)
+CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include
+
all: hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon
%: %.c
$(CC) $(CFLAGS) -o $@ $^
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index b48e87693aa5..a35db828bd0d 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -2101,11 +2101,11 @@ char *parse_events_formats_error_string(char *additional_terms)
/* valid terms */
if (additional_terms) {
- if (!asprintf(&str, "valid terms: %s,%s",
- additional_terms, static_terms))
+ if (asprintf(&str, "valid terms: %s,%s",
+ additional_terms, static_terms) < 0)
goto fail;
} else {
- if (!asprintf(&str, "valid terms: %s", static_terms))
+ if (asprintf(&str, "valid terms: %s", static_terms) < 0)
goto fail;
}
return str;
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index e4b173dec4b9..6f2a0279476c 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -283,13 +283,12 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
{
struct dirent *evt_ent;
DIR *event_dir;
- int ret = 0;
event_dir = opendir(dir);
if (!event_dir)
return -EINVAL;
- while (!ret && (evt_ent = readdir(event_dir))) {
+ while ((evt_ent = readdir(event_dir))) {
char path[PATH_MAX];
char *name = evt_ent->d_name;
FILE *file;
@@ -305,17 +304,19 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
snprintf(path, PATH_MAX, "%s/%s", dir, name);
- ret = -EINVAL;
file = fopen(path, "r");
- if (!file)
- break;
+ if (!file) {
+ pr_debug("Cannot open %s\n", path);
+ continue;
+ }
- ret = perf_pmu__new_alias(head, dir, name, file);
+ if (perf_pmu__new_alias(head, dir, name, file) < 0)
+ pr_debug("Cannot set up %s\n", name);
fclose(file);
}
closedir(event_dir);
- return ret;
+ return 0;
}
/*
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py
index 1833103768cb..c8680984d2d6 100644
--- a/tools/perf/util/setup.py
+++ b/tools/perf/util/setup.py
@@ -22,6 +22,7 @@ cflags = getenv('CFLAGS', '').split()
# switch off several checks (need to be at the end of cflags list)
cflags += ['-fno-strict-aliasing', '-Wno-write-strings', '-Wno-unused-parameter' ]
+src_perf = getenv('srctree') + '/tools/perf'
build_lib = getenv('PYTHON_EXTBUILD_LIB')
build_tmp = getenv('PYTHON_EXTBUILD_TMP')
libtraceevent = getenv('LIBTRACEEVENT')
@@ -30,6 +31,9 @@ libapikfs = getenv('LIBAPI')
ext_sources = [f.strip() for f in file('util/python-ext-sources')
if len(f.strip()) > 0 and f[0] != '#']
+# use full paths with source files
+ext_sources = map(lambda x: '%s/%s' % (src_perf, x) , ext_sources)
+
perf = Extension('perf',
sources = ext_sources,
include_dirs = ['util/include'],
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 7338e30421d8..fefbf2d148ef 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -547,6 +547,16 @@ static struct kvm *kvm_create_vm(unsigned long type)
if (!kvm)
return ERR_PTR(-ENOMEM);
+ spin_lock_init(&kvm->mmu_lock);
+ atomic_inc(&current->mm->mm_count);
+ kvm->mm = current->mm;
+ kvm_eventfd_init(kvm);
+ mutex_init(&kvm->lock);
+ mutex_init(&kvm->irq_lock);
+ mutex_init(&kvm->slots_lock);
+ atomic_set(&kvm->users_count, 1);
+ INIT_LIST_HEAD(&kvm->devices);
+
r = kvm_arch_init_vm(kvm, type);
if (r)
goto out_err_no_disable;
@@ -579,16 +589,6 @@ static struct kvm *kvm_create_vm(unsigned long type)
goto out_err;
}
- spin_lock_init(&kvm->mmu_lock);
- kvm->mm = current->mm;
- atomic_inc(&kvm->mm->mm_count);
- kvm_eventfd_init(kvm);
- mutex_init(&kvm->lock);
- mutex_init(&kvm->irq_lock);
- mutex_init(&kvm->slots_lock);
- atomic_set(&kvm->users_count, 1);
- INIT_LIST_HEAD(&kvm->devices);
-
r = kvm_init_mmu_notifier(kvm);
if (r)
goto out_err;
@@ -613,6 +613,7 @@ out_err_no_disable:
for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++)
kvm_free_memslots(kvm, kvm->memslots[i]);
kvm_arch_free_vm(kvm);
+ mmdrop(current->mm);
return ERR_PTR(r);
}