summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-06-16 10:31:48 -0700
committerLinux Build Service Account <lnxbuild@localhost>2016-06-16 10:31:49 -0700
commitfa22bff01fb9199590e7c5cfe1ba87ed9d27b837 (patch)
treeea683c39d53d436e1144ad44fef23ebbc8eaf6fe
parentf89e635bb12882f4c09f9d57fa0aec08c9207064 (diff)
parent3a3f6a5cb21e391d57c2900a522943fcb3468b4d (diff)
Promotion of kernel.lnx.4.4-160615.
CRs Change ID Subject -------------------------------------------------------------------------------------------------------------- 1008549 I47ea7c22250264da206e1fb8691e77224c825ab0 msm: ipa: clear uC interrupt only before processing 1027456 Idc857357a87ba91cdb7a69deeaf05aa8d5d13628 ARM: msm: add 64-bit IO accessor support 1012001 I93f72f18145ddef6a0caf2c59a9af5f23e6e20a3 ASoC: wcd-mbhc: disable moisture detection for NC Jack 1027269 I6671bb51259515acb0733ce65be8084716d3bfbf mmc: sdhci: Fix command response INDEX/END bit error han 1026796 Id354f7cf6979aa58621408cfcfbd8ef62015fdbd ion: Fix DMA operations for ARM64 1015036 Ib0f6d0ecfa992e2b8aaae9ab6cea3a2e441f65dd msm: pcie: add stubs for !CONFIG_PCI_MSM 1023141 I8e367dbe7acf72471a5a474f0e2a00a4004fcbfb qcom-charger: pmic-voter: destroy only valid votables 1027651 Iff8e1c0367d13bb0d89946d81fb79427b6ef070e msm: sensor: Add support for downloading OIS coefficient 1027456 Ib8218135dc923d1ba4098d74dbd7da159368a188 ARM: build correct dtbs to append to zImage 1027750 Ia01a1ff42af29915093270b9591d97dcc2b2ce54 ARM: dts: msm: update touch panel resolution on msmcobal 1005685 Ia9e45f539bd5ec0d2edfe9bfb90942d88b6d30da msm:camera: Enable/Disable camera daemon 1027456 I3146989f6e73bfe101ac9363428bd0beecd09c32 ARM: dts: Make sure to add the dts files to compilation 997662 I311ad8f158b0be6e9d6481512860f9fac10afc1f ASoC: wcd9335: Infinite loop when routing DMIC for hands 1026732 Ic234c29b9c86e7095ab39f633eda57560b271c1f ARM: dts: msm: Add support for MSMCOBALT QRD 1027311 If6b4340b80b313fda87d648baaa1d78a588079ac spcom: increase max ion buffers per channel 1012546 Ibcbdde0ea59ff80a798de0b894c2239899260860 ASoC: msm: qdsp6v2: do not set cmd_interrupt flag in eos 1004850 Ia5679310fc59f25643e7c8d572cc230d262c5937 power: qcom: Move power table notification outside of cr 1027269 I73ac950f096fa2e81f29ecb40bdd01153c05891f mmc: sdhci: fix command response CRC error handling 1027269 987918 I41cf8908dd0f129c54b941c318e938ad7e9d36c9 mmc: core: Add deferred resume support to CQ 1027248 Iddb96afac87cf3e7a1cc48f04b3c550e81bdae4b scsi: ufs-qcom: Fix null pointer dereference 1027456 I412a83a2f756b02d6b521983501de780835dc118 arch: arm: generalise ARCH_QCOM platform 1018090 Ie947cc2c74550c98f64dd028c728afa57723c70f ARM: dts: msm: add QPNP QNOVO charger device to PMICOBAL 1027456 I4d52ad23cc40d03d2bae1d3942c8d35543a0d461 ARM: msm: add support for logged IO accessors 1027248 Ib37534eaf15ad76abb800fe3917f9c0a832bd30a ARM: dts: msm: enable UFS PM QoS for msmcobalt 1023141 975120 I5ebe6d0bcb7c097124ba9b35c56579815dda234f mfd: introduce I2C PMIC controller 1023141 I885289a1eec68335645912c3ecbbe91a85836647 qcom-charger: smb-lib: differentiate between parallel an 1015036 I69b1839d1afe8f65235e5c47f0d55abe75acb6a8 ARM: dts: msm: enable L1ss for PCIe on msmcobalt 1027456 I69017d73da1065a5eeb9c87c899b6a51be5ebfe6 Revert "ARM: dma-mapping: remove dmac_clean_range and dm 1018090 I2ddea8adf1aa9d999cc2fd3fd4f0e0f830147d4c qcom-charger: enable qnovo bit in the qpnp-smb2 driver 1026135 I68fa6f510d55822b01c2ea5062d4876c4420c5f7 icnss: Add statistics to know driver status 1026507 I4fae5f442e4cc2c2414a69e960d42c05c3062415 Revert "kernel/sysctl.c: detect overflows when convertin 1023141 I96a4877196be78c0eeecc3fc08419e8990572aaa qcom-charger: smb-lib: add get charge param and usb susp 1027456 I80bac808f2916e49a77be24e7a39d0839165d306 arm: io: Add readb and writeb relaxed_no_log variants 1027653 I843f35ee08159c59aaee7df4a23dfb4ae9c6b689 msm: vidc: Fix Low latency step size 1027836 I117348084e2bec49d0fcd7eb0b0149fc00ae639d ARM: dts: msm: add display panel configuration for msmco 1027269 I1eae7dffec97d34b066bb5738c84a7e5a82f68d7 mmc: Fix pm_notifier obeying deferred resume 1027269 I077d7dc9311ff12e6e16de631abeac965c8facd9 mmc: core: Add deferred bus resume policy. 1026738 If440e1ef4bdbcc73ac7a0569a6bbf093db8aefef ARM: dts: apq: Add initial device tree files for APQCOBA 971183 If3076f017d476cfb57fa22b75cc74ed615c8882e ASoC: wcd9335: Adjust DMIC clock based on sample rate 1018090 I2573f719f4b2c2fa9a169659a65433fb834ea74e qcom-charger: add qnovo driver 1024966 I4706b353e63b044368ea54a8ed74d61dc44dc95c defconfig: arm64: msmcortex: Add defconfigs for IPSec da 1024022 Ie713f020201cafe6d815c7da5e87ea1566ac36ad ARM: dts: msm: enable charging in msmcobalt Change-Id: Ie27ccb268e107e8748e42fbfdd9bbb480561e1e1 CRs-Fixed: 1027456, 1012001, 1026796, 1026135, 971183, 1027750, 1027269, 1012546, 1026738, 1027248, 1005685, 1026732, 1024966, 1008549, 1024022, 975120, 1026507, 1015036, 997662, 1004850, 1027651, 1027653, 1027311, 1023141, 987918, 1027836, 1018090
-rw-r--r--Documentation/devicetree/bindings/arm/msm/msm.txt6
-rw-r--r--Documentation/devicetree/bindings/mfd/qcom-i2c-pmic.txt98
-rw-r--r--Documentation/devicetree/bindings/power/qcom-charger/qpnp-qnovo.txt32
-rw-r--r--arch/arm/Kconfig27
-rw-r--r--arch/arm/Kconfig.debug2
-rw-r--r--arch/arm/Makefile14
-rw-r--r--arch/arm/boot/Makefile9
-rw-r--r--arch/arm/boot/dts/Makefile6
-rw-r--r--arch/arm/boot/dts/qcom/Makefile21
-rw-r--r--arch/arm/boot/dts/qcom/apqcobalt-cdp.dts22
-rw-r--r--arch/arm/boot/dts/qcom/apqcobalt-mtp.dts22
-rw-r--r--arch/arm/boot/dts/qcom/apqcobalt.dtsi18
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi10
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi81
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-qrd.dts23
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-rumi.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-sim.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt.dtsi8
-rw-r--r--arch/arm/include/asm/cacheflush.h21
-rw-r--r--arch/arm/include/asm/glue-cache.h2
-rw-r--r--arch/arm/include/asm/io.h126
-rw-r--r--arch/arm/mach-qcom/Kconfig30
-rw-r--r--arch/arm/mm/cache-v7.S4
-rw-r--r--arch/arm/mm/proc-macros.S2
-rw-r--r--arch/arm/mm/proc-syms.c3
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig11
-rw-r--r--arch/arm64/configs/msmcortex_defconfig13
-rw-r--r--drivers/media/platform/msm/camera_v2/camera/camera.c113
-rw-r--r--drivers/media/platform/msm/camera_v2/msm.c12
-rw-r--r--drivers/media/platform/msm/camera_v2/msm.h10
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c181
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.h7
-rw-r--r--drivers/media/platform/msm/vidc/msm_venc.c1
-rw-r--r--drivers/mfd/Kconfig14
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/qcom-i2c-pmic.c658
-rw-r--r--drivers/mmc/card/block.c4
-rw-r--r--drivers/mmc/core/bus.c14
-rw-r--r--drivers/mmc/core/core.c46
-rw-r--r--drivers/mmc/host/sdhci.c19
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_interrupts.c49
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_interrupts.c29
-rw-r--r--drivers/power/qcom-charger/Kconfig10
-rw-r--r--drivers/power/qcom-charger/Makefile1
-rw-r--r--drivers/power/qcom-charger/pmic-voter.c3
-rw-r--r--drivers/power/qcom-charger/qpnp-qnovo.c1427
-rw-r--r--drivers/power/qcom-charger/qpnp-smb2.c7
-rw-r--r--drivers/power/qcom-charger/smb-lib.c79
-rw-r--r--drivers/power/qcom-charger/smb-lib.h15
-rw-r--r--drivers/power/qcom/msm-core.c15
-rw-r--r--drivers/scsi/ufs/ufs-qcom.c3
-rw-r--r--drivers/soc/qcom/icnss.c315
-rw-r--r--drivers/soc/qcom/spcom.c27
-rwxr-xr-xdrivers/staging/android/ion/ion.c2
-rw-r--r--drivers/staging/android/ion/ion_carveout_heap.c9
-rw-r--r--drivers/staging/android/ion/ion_chunk_heap.c8
-rw-r--r--drivers/staging/android/ion/ion_page_pool.c7
-rw-r--r--drivers/staging/android/ion/ion_priv.h10
-rw-r--r--drivers/staging/android/ion/ion_system_heap.c27
-rw-r--r--drivers/staging/android/ion/msm/msm_ion.c10
-rw-r--r--include/linux/mmc/host.h18
-rw-r--r--include/linux/msm_pcie.h50
-rw-r--r--include/uapi/media/msm_cam_sensor.h25
-rw-r--r--include/uapi/media/msmb_camera.h3
-rw-r--r--kernel/sysctl.c10
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c6
-rwxr-xr-xsound/soc/codecs/wcd9335.c47
-rwxr-xr-xsound/soc/msm/qdsp6v2/msm-compress-q6-v2.c11
69 files changed, 3721 insertions, 205 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt
index d387336a2b5d..1c0dfa43952c 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm.txt
@@ -47,6 +47,9 @@ SoCs:
- APQTITANIUM
compatible = "qcom,apqtitanium"
+- APQCOBALT
+ compatible = "qcom,apqcobalt"
+
- MDM9630
compatible = "qcom,mdm9630"
@@ -177,6 +180,8 @@ compatible = "qcom,apq8037-cdp"
compatible = "qcom,apq8037-mtp"
compatible = "qcom,apqtitanium-cdp"
compatible = "qcom,apqtitanium-mtp"
+compatible = "qcom,apqcobalt-cdp"
+compatible = "qcom,apqcobalt-mtp"
compatible = "qcom,mdm9630-cdp"
compatible = "qcom,mdm9630-mtp"
compatible = "qcom,mdm9630-sim"
@@ -245,6 +250,7 @@ compatible = "qcom,msm8996-mtp"
compatible = "qcom,msm8996-adp"
compatible = "qcom,msmcobalt-sim"
compatible = "qcom,msmcobalt-rumi"
+compatible = "qcom,msmcobalt-qrd"
compatible = "qcom,msmhamster-rumi"
compatible = "qcom,msmhamster-cdp"
compatible = "qcom,msmhamster-mtp"
diff --git a/Documentation/devicetree/bindings/mfd/qcom-i2c-pmic.txt b/Documentation/devicetree/bindings/mfd/qcom-i2c-pmic.txt
new file mode 100644
index 000000000000..9c7c856ea42b
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/qcom-i2c-pmic.txt
@@ -0,0 +1,98 @@
+Qualcomm Technologies, Inc. I2C PMIC Interrupt Controller
+Platform Independent Bindings
+
+The I2C PMIC Controller is used by multi-function PMIC devices which communicate
+over the I2C bus. An I2C PMIC controller node typically contains one or more
+child nodes representing the device's peripherals. Each of the peripherals
+typically has its own driver on the platform bus and will be enumerated by this
+controller. The controller exposes a regmap to the peripherals to communicate
+over the I2C bus.
+
+The controller also controls interrupts for all of the peripherals on the bus.
+The controller takes a summary interrupt, deciphers which peripheral triggered
+the interrupt, and which of the peripheral's interrupts were triggered. Finally,
+it calls the handlers for each of the virtual interrupts that were registered.
+
+This document describes the common platform independent bindings that apply
+to all I2C PMIC interrupt controllers.
+
+========================================
+First Level Nodes - I2C PMIC Controllers
+========================================
+
+Platform independent properties:
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: Must be "qcom,i2c-pmic".
+
+- reg
+ Usage: required
+ Value type: <u32>
+ Definition: 7-bit I2C address of the device.
+
+- interrupt-parent
+ Usage: required
+ Value type: <phandle>
+ Definition: phandle of the interrupt controller which services the
+ summary interrupt.
+
+- interrupts
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Summary interrupt specifier.
+
+- interrupt-controller
+ Usage: required
+ Value type: <empty>
+ Definition: Boolean flag which indicates this device node is an
+ interrupt controller.
+
+- #interrupt-cells
+ Usage: required
+ Value type: <u32>
+ Definition: Number of cells to encode an interrupt source.
+
+- qcom,periph-map
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: A list of u32 arrays. This provides a mapping between the
+ summary status register bits and peripheral addresses.
+
+ The number of arrays should match the number of summary
+ registers with up to 8 elements each. One element per bit
+ of the summary status register in order from the least
+ sigificant bit to the most significant bit.
+
+- pinctrl-names
+ Usage: optional
+ Value type: <string-list>
+ Definition: Should be "default".
+ Please refer to pinctrl-bindings.txt
+
+- pinctrl-0
+ Usage: optional
+ Value type: <phandle-list>
+ Definition: phandle of the pin configuration.
+ Please refer to pinctrl-bindings.txt
+
+=======
+Example
+=======
+
+&i2c_3 {
+ status = "ok";
+ qcom,smb138x@8 {
+ compatible = "qcom,i2c-pmic";
+ reg = <0x8>;
+ interrupt-parent = <&tlmm_pinmux>;
+ interrupts = <83 0>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_stat_active>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ qcom,periph-map = <0x10 0x11 0x12 0x13 0x14 0x16 0x36>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-qnovo.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-qnovo.txt
new file mode 100644
index 000000000000..438bd68b0e05
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-qnovo.txt
@@ -0,0 +1,32 @@
+QPNP Qnovo pulse engine
+
+QPNP Qnovo is a pulse charging engine which works in tandem with the QPNP SMB2
+Charger device. It configures the QPNP SMB2 charger to charge/discharge as per
+pulse characteristics.
+
+The QPNP Qnovo pulse engine has a single peripheral assigned to it.
+
+Required properties:
+- compatible: Must be "qcom,qpnp-qnovo"
+- qcom,pmic-revid: Should specify the phandle of PMIC
+ revid module. This is used to identify
+ the PMIC subtype.
+
+- reg: The address for this peripheral
+- interrupts: Specifies the interrupt associated with the peripheral.
+- interrupt-names: Specifies the interrupt name for the peripheral. Qnovo
+ peripheral has only one interrupt "ptrain-done".
+
+Optional Properties:
+- qcom,external-rsense: To indicate whether the platform uses external or
+ internal rsense for measuring battery current.
+
+Example:
+
+ qcom,qpnp-qnovo@1500 {
+ compatible = "qcom,qpnp-qnovo";
+ reg = <0x1500 0x100>;
+ interrupts = <0x2 0x15 0x0 IRQ_TYPE_NONE>;
+ interrupt-names = "ptrain-done";
+ qcom,pmic-revid = <&pmicobalt_revid>;
+ };
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index d3159ff4de5b..6b09a67c9a0d 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -632,6 +632,31 @@ config ARCH_PXA
help
Support for Intel/Marvell's PXA2xx/PXA3xx processor line.
+config ARCH_QCOM
+ bool "Qualcomm MSM (non-multiplatform)"
+ select ARCH_REQUIRE_GPIOLIB
+ select CPU_V7
+ select AUTO_ZRELADDR
+ select HAVE_SMP
+ select CLKDEV_LOOKUP
+ select GENERIC_CLOCKEVENTS
+ select GENERIC_ALLOCATOR
+ select ARM_PATCH_PHYS_VIRT
+ select ARM_HAS_SG_CHAIN
+ select ARCH_HAS_OPP
+ select SOC_BUS
+ select MULTI_IRQ_HANDLER
+ select PM_OPP
+ select SPARSE_IRQ
+ select USE_OF
+ select PINCTRL
+ help
+ Support for Qualcomm MSM/QSD based systems. This runs on the
+ apps processor of the MSM/QSD and depends on a shared memory
+ interface to the modem processor which runs the baseband
+ stack and controls some vital subsystems
+ (clock and power control, etc).
+
config ARCH_RPC
bool "RiscPC"
depends on MMU
@@ -1505,7 +1530,7 @@ config ARM_PSCI
config ARCH_NR_GPIO
int
default 1024 if ARCH_BRCMSTB || ARCH_SHMOBILE || ARCH_TEGRA || \
- ARCH_ZYNQ
+ ARCH_ZYNQ || ARCH_QCOM
default 512 if ARCH_EXYNOS || ARCH_KEYSTONE || SOC_OMAP5 || \
SOC_DRA7XX || ARCH_S3C24XX || ARCH_S3C64XX || ARCH_S5PV210
default 416 if ARCH_SUNXI
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 0901c6bd458f..71afa854e1c4 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -1612,7 +1612,7 @@ config DEBUG_UNCOMPRESS
config UNCOMPRESS_INCLUDE
string
default "debug/uncompress.h" if ARCH_MULTIPLATFORM || ARCH_MSM || \
- PLAT_SAMSUNG || ARM_SINGLE_ARMV7M
+ ARCH_QCOM || PLAT_SAMSUNG || ARM_SINGLE_ARMV7M
default "mach/uncompress.h"
config EARLY_PRINTK
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 97f822a87c1c..24e19deb1f28 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -307,6 +307,8 @@ ifeq ($(CONFIG_USE_OF),y)
KBUILD_DTBS := dtbs
endif
+DTSSUBDIR := qcom
+
all: $(KBUILD_IMAGE) $(KBUILD_DTBS)
boot := arch/arm/boot
@@ -334,22 +336,18 @@ $(INSTALL_TARGETS):
%.dtb: | scripts
$(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) $(boot)/dts/$@
-PHONY += dtbs dtbs_install
-
-dtbs: prepare scripts
- $(Q)$(MAKE) $(build)=$(boot)/dts
-
PHONY += vdso_install
vdso_install:
ifeq ($(CONFIG_VDSO),y)
$(Q)$(MAKE) $(build)=arch/arm/vdso $@
endif
-dtbs dtbs_install: prepare scripts
- $(Q)$(MAKE) $(build)=$(boot)/dts/qcom MACHINE=$(MACHINE) $@
+dtbs: scripts
+ $(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) dtbs
+ $(foreach DIR, $(DTSSUBDIR), $(Q)$(MAKE) $(build)=$(boot)/dts/$(DIR) MACHINE=$(MACHINE) dtbs)
zImage-dtb: vmlinux scripts dtbs
- $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
+ $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) DTSSUBDIR=$(DTSSUBDIR) $(boot)/$@
# We use MRPROPER_FILES and CLEAN_FILES now
archclean:
diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile
index 4a04ed3daf97..8849c2d20ac5 100644
--- a/arch/arm/boot/Makefile
+++ b/arch/arm/boot/Makefile
@@ -11,11 +11,14 @@
# Copyright (C) 1995-2002 Russell King
#
-ifneq ($(MACHINE),)
-include $(MACHINE)/Makefile.boot
-endif
include $(srctree)/arch/arm/boot/dts/Makefile
+include $(srctree)/arch/arm/boot/dts/Makefile
+ifneq ($(DTSSUBDIR),)
+DTSSUBDIR_INCS=$(foreach DIR, $(DTSSUBDIR), $(addsuffix /Makefile, $(addprefix $(srctree)/arch/arm/boot/dts/, $(DIR))))
+include $(DTSSUBDIR_INCS)
+endif
+
# Note: the following conditions must always be true:
# ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET)
# PARAMS_PHYS must be within 4MB of ZRELADDR
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index d83f20080c0a..67273ababc30 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -768,7 +768,6 @@ dtb-$(CONFIG_MACH_DOVE) += \
dove-cubox-es.dtb \
dove-d2plug.dtb \
dove-d3plug.dtb \
-<<<<<<< HEAD
dove-dove-db.dtb \
dove-sbc-a510.dtb
dtb-$(CONFIG_ARCH_MEDIATEK) += \
@@ -806,5 +805,10 @@ endif
targets += dtbs dtbs_install
targets += $(DTB_LIST)
+ifeq ($(CONFIG_ARM64),y)
always := $(DTB_LIST)
+else
+dtbs: $(addprefix $(obj)/, $(DTB_LIST))
+ $(Q)rm -f $(obj)/../*.dtb
+endif
clean-files := *.dtb
diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile
index faf39d2a8970..dd955be6c908 100644
--- a/arch/arm/boot/dts/qcom/Makefile
+++ b/arch/arm/boot/dts/qcom/Makefile
@@ -106,15 +106,28 @@ dtb-$(CONFIG_ARCH_MSMCOBALT) += msmcobalt-sim.dtb \
msmcobalt-rumi.dtb \
msmcobalt-cdp.dtb \
msmcobalt-mtp.dtb \
+ msmcobalt-qrd.dtb \
msmcobalt-v2-sim.dtb \
msmcobalt-v2-rumi.dtb \
msmcobalt-v2-mtp.dtb \
- msmcobalt-v2-cdp.dtb
+ msmcobalt-v2-cdp.dtb \
+ apqcobalt-mtp.dtb \
+ apqcobalt-cdp.dtb
dtb-$(CONFIG_ARCH_MSMHAMSTER) += msmhamster-rumi.dtb
dtb-$(CONFIG_ARCH_MSMFALCON) += msmfalcon-sim.dtb
-always := $(dtb-y)
-subdir-y := $(dts-dirs)
-clean-files := *.dtb
+ifeq ($(CONFIG_ARM64),y)
+always := $(dtb-y)
+subdir-y := $(dts-dirs)
+else
+targets += dtbs
+targets += $(addprefix ../, $(dtb-y))
+
+$(obj)/../%.dtb: $(src)/%.dts FORCE
+ $(call if_changed_dep,dtc)
+
+dtbs: $(addprefix $(obj)/../,$(dtb-y))
+endif
+clean-files := *.dtb
diff --git a/arch/arm/boot/dts/qcom/apqcobalt-cdp.dts b/arch/arm/boot/dts/qcom/apqcobalt-cdp.dts
new file mode 100644
index 000000000000..1f0066d15aaa
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/apqcobalt-cdp.dts
@@ -0,0 +1,22 @@
+/* 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.
+ */
+
+/dts-v1/;
+
+#include "apqcobalt.dtsi"
+#include "msmcobalt-cdp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ COBALT CDP";
+ compatible = "qcom,apqcobalt-cdp", "qcom,apqcobalt", "qcom,cdp";
+ qcom,board-id = <1 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/apqcobalt-mtp.dts b/arch/arm/boot/dts/qcom/apqcobalt-mtp.dts
new file mode 100644
index 000000000000..b63e9e027797
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/apqcobalt-mtp.dts
@@ -0,0 +1,22 @@
+/* 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.
+ */
+
+/dts-v1/;
+
+#include "apqcobalt.dtsi"
+#include "msmcobalt-mtp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ COBALT MTP";
+ compatible = "qcom,apqcobalt-mtp", "qcom,apqcobalt", "qcom,mtp";
+ qcom,board-id = <8 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/apqcobalt.dtsi b/arch/arm/boot/dts/qcom/apqcobalt.dtsi
new file mode 100644
index 000000000000..6122915975a1
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/apqcobalt.dtsi
@@ -0,0 +1,18 @@
+/* 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 "msmcobalt.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ COBALT";
+ qcom,msm-id = <319 0x10000>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
index c0bae6c6e5f8..b612ec3f42b5 100644
--- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
@@ -130,6 +130,14 @@
};
};
+ qcom,qpnp-qnovo@1500 {
+ compatible = "qcom,qpnp-qnovo";
+ reg = <0x1500 0x100>;
+ interrupts = <0x2 0x15 0x0 IRQ_TYPE_NONE>;
+ interrupt-names = "ptrain-done";
+ qcom,pmic-revid = <&pmicobalt_revid>;
+ };
+
pmicobalt_charger: qcom,qpnp-smb2 {
compatible = "qcom,qpnp-smb2";
#address-cells = <1>;
@@ -137,8 +145,6 @@
qcom,pmic-revid = <&pmicobalt_revid>;
- /* do not draw current from USB or DC */
- qcom,suspend-input;
dpdm-supply = <&qusb_phy0>;
qcom,chgr@1000 {
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi
index acb4abb788f1..806610db5772 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi
@@ -299,6 +299,10 @@
&mdss_hdmi_ddc_suspend &mdss_hdmi_cec_suspend>;
};
+&pmicobalt_charger {
+ qcom,suspend-input;
+};
+
&pmicobalt_haptics {
status = "okay";
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi
index fa91ed674d2c..0b18de7d9e27 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi
@@ -165,7 +165,7 @@
pinctrl-0 = <&ts_active>;
pinctrl-1 = <&ts_suspend>;
synaptics,display-coords = <0 0 1439 2559>;
- synaptics,panel-coords = <0 0 1439 2703>;
+ synaptics,panel-coords = <0 0 1439 2559>;
synaptics,reset-gpio = <&tlmm 89 0x00>;
synaptics,irq-gpio = <&tlmm 125 0x2008>;
synaptics,disable-gpios;
@@ -210,6 +210,85 @@
&mdss_hdmi_ddc_suspend &mdss_hdmi_cec_suspend>;
};
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "split_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_dual_nt35597_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_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>;
+};
+
+&labibb {
+ status = "ok";
+ qpnp,qpnp-labibb-mode = "lcd";
+};
+
+&dsi_dual_nt35597_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_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>;
+};
+
+&dsi_nt35597_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,mdss-dsi-mode-sel-gpio-state = "single_port";
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
+&dsi_nt35597_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,mdss-dsi-mode-sel-gpio-state = "single_port";
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
+&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>;
+};
+
&pmicobalt_haptics {
status = "okay";
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dts b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dts
new file mode 100644
index 000000000000..9848d6da33a5
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dts
@@ -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.
+ */
+
+
+/dts-v1/;
+
+#include "msmcobalt.dtsi"
+#include "msmcobalt-mtp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM COBALT QRD";
+ compatible = "qcom,msmcobalt-qrd", "qcom,msmcobalt", "qcom,qrd";
+ qcom,board-id = <11 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-rumi.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-rumi.dtsi
index 27e9bd321b1b..20d38f5616cb 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-rumi.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-rumi.dtsi
@@ -273,3 +273,7 @@
};
};
};
+
+&pmicobalt_charger {
+ qcom,suspend-input;
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-sim.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-sim.dtsi
index 0793f22a08b0..3b79cee0ae1e 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-sim.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-sim.dtsi
@@ -110,3 +110,7 @@
#interrupt-cells = <1>;
interrupt-names = "cdc-int";
};
+
+&pmicobalt_charger {
+ qcom,suspend-input;
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi
index 5e31da726464..366569cd81c9 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi
@@ -1414,6 +1414,8 @@
RPM_SMD_REGULATOR_LEVEL_SVS 0>;
qcom,l1-supported;
+ qcom,l1ss-supported;
+ qcom,aux-clk-sync;
qcom,ep-latency = <10>;
@@ -1635,8 +1637,10 @@
"HS_RB_G1_L1", "HS_RB_G2_L1", "HS_RB_G3_L1",
"MAX";
- qcom,cpu-affinity = "affine_cores";
- qcom,cpu-dma-latency-us = <301>;
+ /* PM QoS */
+ qcom,pm-qos-cpu-groups = <0x0F 0xF0>;
+ qcom,pm-qos-cpu-group-latency-us = <70 70>;
+ qcom,pm-qos-default-cpu = <0>;
status = "disabled";
};
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index d5525bfc7e3e..9623a8a87e18 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -94,6 +94,21 @@
* DMA Cache Coherency
* ===================
*
+ * dma_inv_range(start, end)
+ *
+ * Invalidate (discard) the specified virtual address range.
+ * May not write back any entries. If 'start' or 'end'
+ * are not cache line aligned, those lines must be written
+ * back.
+ * - start - virtual start address
+ * - end - virtual end address
+ *
+ * dma_clean_range(start, end)
+ *
+ * Clean (write back) the specified virtual address range.
+ * - start - virtual start address
+ * - end - virtual end address
+ *
* dma_flush_range(start, end)
*
* Clean and invalidate the specified virtual address range.
@@ -115,6 +130,8 @@ struct cpu_cache_fns {
void (*dma_map_area)(const void *, size_t, int);
void (*dma_unmap_area)(const void *, size_t, int);
+ void (*dma_inv_range)(const void *, const void *);
+ void (*dma_clean_range)(const void *, const void *);
void (*dma_flush_range)(const void *, const void *);
};
@@ -140,6 +157,8 @@ extern struct cpu_cache_fns cpu_cache;
* is visible to DMA, or data written by DMA to system memory is
* visible to the CPU.
*/
+#define dmac_inv_range cpu_cache.dma_inv_range
+#define dmac_clean_range cpu_cache.dma_clean_range
#define dmac_flush_range cpu_cache.dma_flush_range
#else
@@ -159,6 +178,8 @@ extern void __cpuc_flush_dcache_area(void *, size_t);
* is visible to DMA, or data written by DMA to system memory is
* visible to the CPU.
*/
+extern void dmac_inv_range(const void *, const void *);
+extern void dmac_clean_range(const void *, const void *);
extern void dmac_flush_range(const void *, const void *);
#endif
diff --git a/arch/arm/include/asm/glue-cache.h b/arch/arm/include/asm/glue-cache.h
index cab07f69382d..1f442995cdbf 100644
--- a/arch/arm/include/asm/glue-cache.h
+++ b/arch/arm/include/asm/glue-cache.h
@@ -159,6 +159,8 @@ static inline void nop_dma_unmap_area(const void *s, size_t l, int f) { }
#define __cpuc_flush_dcache_area __glue(_CACHE,_flush_kern_dcache_area)
#define dmac_flush_range __glue(_CACHE,_dma_flush_range)
+#define dmac_inv_range __glue(_CACHE, _dma_inv_range)
+#define dmac_clean_range __glue(_CACHE, _dma_clean_range)
#endif
#endif
diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h
index 485982084fe9..c5d7c8b995eb 100644
--- a/arch/arm/include/asm/io.h
+++ b/arch/arm/include/asm/io.h
@@ -29,6 +29,7 @@
#include <asm/byteorder.h>
#include <asm/memory.h>
#include <asm-generic/pci_iomap.h>
+#include <linux/msm_rtb.h>
#include <xen/xen.h>
/*
@@ -62,23 +63,21 @@ void __raw_readsl(const volatile void __iomem *addr, void *data, int longlen);
* the bus. Rather than special-case the machine, just let the compiler
* generate the access for CPUs prior to ARMv6.
*/
-#define __raw_readw(a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a))
-#define __raw_writew(v,a) ((void)(__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v)))
+#define __raw_readw_no_log(a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a))
+#define __raw_writew_no_log(v, a) ((void)(__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v)))
#else
/*
* When running under a hypervisor, we want to avoid I/O accesses with
* writeback addressing modes as these incur a significant performance
* overhead (the address generation must be emulated in software).
*/
-#define __raw_writew __raw_writew
-static inline void __raw_writew(u16 val, volatile void __iomem *addr)
+static inline void __raw_writew_no_log(u16 val, volatile void __iomem *addr)
{
asm volatile("strh %1, %0"
: : "Q" (*(volatile u16 __force *)addr), "r" (val));
}
-#define __raw_readw __raw_readw
-static inline u16 __raw_readw(const volatile void __iomem *addr)
+static inline u16 __raw_readw_no_log(const volatile void __iomem *addr)
{
u16 val;
asm volatile("ldrh %0, %1"
@@ -88,22 +87,30 @@ static inline u16 __raw_readw(const volatile void __iomem *addr)
}
#endif
-#define __raw_writeb __raw_writeb
-static inline void __raw_writeb(u8 val, volatile void __iomem *addr)
+static inline void __raw_writeb_no_log(u8 val, volatile void __iomem *addr)
{
asm volatile("strb %1, %0"
: : "Qo" (*(volatile u8 __force *)addr), "r" (val));
}
-#define __raw_writel __raw_writel
-static inline void __raw_writel(u32 val, volatile void __iomem *addr)
+static inline void __raw_writel_no_log(u32 val, volatile void __iomem *addr)
{
asm volatile("str %1, %0"
: : "Qo" (*(volatile u32 __force *)addr), "r" (val));
}
-#define __raw_readb __raw_readb
-static inline u8 __raw_readb(const volatile void __iomem *addr)
+static inline void __raw_writeq_no_log(u64 val, volatile void __iomem *addr)
+{
+ register u64 v asm ("r2");
+
+ v = val;
+
+ asm volatile("strd %1, %0"
+ : "+Qo" (*(volatile u64 __force *)addr)
+ : "r" (v));
+}
+
+static inline u8 __raw_readb_no_log(const volatile void __iomem *addr)
{
u8 val;
asm volatile("ldrb %0, %1"
@@ -112,8 +119,7 @@ static inline u8 __raw_readb(const volatile void __iomem *addr)
return val;
}
-#define __raw_readl __raw_readl
-static inline u32 __raw_readl(const volatile void __iomem *addr)
+static inline u32 __raw_readl_no_log(const volatile void __iomem *addr)
{
u32 val;
asm volatile("ldr %0, %1"
@@ -122,6 +128,58 @@ static inline u32 __raw_readl(const volatile void __iomem *addr)
return val;
}
+static inline u64 __raw_readq_no_log(const volatile void __iomem *addr)
+{
+ register u64 val asm ("r2");
+
+ asm volatile("ldrd %1, %0"
+ : "+Qo" (*(volatile u64 __force *)addr),
+ "=r" (val));
+ return val;
+}
+
+/*
+ * There may be cases when clients don't want to support or can't support the
+ * logging. The appropriate functions can be used but clients should carefully
+ * consider why they can't support the logging.
+ */
+
+#define __raw_write_logged(v, a, _t) ({ \
+ int _ret; \
+ volatile void __iomem *_a = (a); \
+ void *_addr = (void __force *)(_a); \
+ _ret = uncached_logk(LOGK_WRITEL, _addr); \
+ ETB_WAYPOINT; \
+ __raw_write##_t##_no_log((v), _a); \
+ if (_ret) \
+ LOG_BARRIER; \
+ })
+
+
+#define __raw_writeb(v, a) __raw_write_logged((v), (a), b)
+#define __raw_writew(v, a) __raw_write_logged((v), (a), w)
+#define __raw_writel(v, a) __raw_write_logged((v), (a), l)
+#define __raw_writeq(v, a) __raw_write_logged((v), (a), q)
+
+#define __raw_read_logged(a, _l, _t) ({ \
+ unsigned _t __a; \
+ const volatile void __iomem *_a = (a); \
+ void *_addr = (void __force *)(_a); \
+ int _ret; \
+ _ret = uncached_logk(LOGK_READL, _addr); \
+ ETB_WAYPOINT; \
+ __a = __raw_read##_l##_no_log(_a);\
+ if (_ret) \
+ LOG_BARRIER; \
+ __a; \
+ })
+
+
+#define __raw_readb(a) __raw_read_logged((a), b, char)
+#define __raw_readw(a) __raw_read_logged((a), w, short)
+#define __raw_readl(a) __raw_read_logged((a), l, int)
+#define __raw_readq(a) __raw_read_logged((a), q, long long)
+
/*
* Architecture ioremap implementation.
*/
@@ -291,18 +349,32 @@ extern void _memset_io(volatile void __iomem *, int, size_t);
__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((__force u16) cpu_to_le16(v),c)
-#define writel_relaxed(v,c) __raw_writel((__force u32) cpu_to_le32(v),c)
+#define readq_relaxed(c) ({ u64 __r = le64_to_cpu((__force __le64) \
+ __raw_readq(c)); __r; })
+#define readb_relaxed_no_log(c) ({ u8 __r = __raw_readb_no_log(c); __r; })
+#define readl_relaxed_no_log(c) ({ u32 __r = le32_to_cpu((__force __le32) \
+ __raw_readl_no_log(c)); __r; })
+#define readq_relaxed_no_log(c) ({ u64 __r = le64_to_cpu((__force __le64) \
+ __raw_readq_no_log(c)); __r; })
+
+
+#define writeb_relaxed(v, c) __raw_writeb(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)
+#define writeq_relaxed(v, c) __raw_writeq((__force u64) cpu_to_le64(v), c)
+#define writeb_relaxed_no_log(v, c) ((void)__raw_writeb_no_log((v), (c)))
+#define writel_relaxed_no_log(v, c) __raw_writel_no_log((__force u32) cpu_to_le32(v), c)
+#define writeq_relaxed_no_log(v, c) __raw_writeq_no_log((__force u64) cpu_to_le64(v), c)
#define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; })
#define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; })
#define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; })
+#define readq(c) ({ u64 __v = readq_relaxed(c); __iormb(); __v; })
#define writeb(v,c) ({ __iowmb(); writeb_relaxed(v,c); })
#define writew(v,c) ({ __iowmb(); writew_relaxed(v,c); })
#define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); })
+#define writeq(v, c) ({ __iowmb(); writeq_relaxed(v, c); })
#define readsb(p,d,l) __raw_readsb(p,d,l)
#define readsw(p,d,l) __raw_readsw(p,d,l)
@@ -401,6 +473,23 @@ void __iomem *ioremap_wc(resource_size_t res_cookie, size_t size);
void iounmap(volatile void __iomem *iomem_cookie);
#define iounmap iounmap
+/*
+ * io{read,write}{8,16,32,64} macros
+ */
+#ifndef ioread8
+#define ioread8(p) ({ unsigned int __v = __raw_readb(p); __iormb(); __v; })
+#define ioread16(p) ({ unsigned int __v = le16_to_cpu((__force __le16)__raw_readw(p)); __iormb(); __v; })
+#define ioread32(p) ({ unsigned int __v = le32_to_cpu((__force __le32)__raw_readl(p)); __iormb(); __v; })
+#define ioread64(p) ({ unsigned int __v = le64_to_cpu((__force __le64)__raw_readq(p)); __iormb(); __v; })
+
+#define ioread64be(p) ({ unsigned int __v = be64_to_cpu((__force __be64)__raw_readq(p)); __iormb(); __v; })
+
+#define iowrite8(v, p) ({ __iowmb(); __raw_writeb(v, p); })
+#define iowrite16(v, p) ({ __iowmb(); __raw_writew((__force __u16)cpu_to_le16(v), p); })
+#define iowrite32(v, p) ({ __iowmb(); __raw_writel((__force __u32)cpu_to_le32(v), p); })
+#define iowrite64(v, p) ({ __iowmb(); __raw_writeq((__force __u64)cpu_to_le64(v), p); })
+
+#define iowrite64be(v, p) ({ __iowmb(); __raw_writeq((__force __u64)cpu_to_be64(v), p); })
/*
* io{read,write}{16,32}be() macros
@@ -419,6 +508,7 @@ extern void __iomem *ioport_map(unsigned long port, unsigned int nr);
#define ioport_unmap ioport_unmap
extern void ioport_unmap(void __iomem *addr);
#endif
+#endif
struct pci_dev;
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index 2256cd1e25d1..918a3fa7e938 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -1,25 +1,35 @@
-menuconfig ARCH_QCOM
- bool "Qualcomm Support" if ARCH_MULTI_V7
- select ARCH_SUPPORTS_BIG_ENDIAN
- select ARM_GIC
- select ARM_AMBA
- select PINCTRL
- select QCOM_SCM if SMP
- help
- Support for Qualcomm's devicetree based systems.
-
if ARCH_QCOM
+menu "QCOM SoC Type"
config ARCH_MSM8X60
bool "Enable support for MSM8X60"
+ select ARCH_SUPPORTS_BIG_ENDIAN
+ select ARM_GIC
+ select ARM_AMBA
+ select QCOM_SCM if SMP
select CLKSRC_QCOM
+ select CLKSRC_OF
+ select COMMON_CLK
config ARCH_MSM8960
bool "Enable support for MSM8960"
select CLKSRC_QCOM
+ select ARCH_SUPPORTS_BIG_ENDIAN
+ select ARM_GIC
+ select ARM_AMBA
+ select QCOM_SCM if SMP
+ select CLKSRC_OF
+ select COMMON_CLK
config ARCH_MSM8974
bool "Enable support for MSM8974"
select HAVE_ARM_ARCH_TIMER
+ select ARCH_SUPPORTS_BIG_ENDIAN
+ select ARM_GIC
+ select ARM_AMBA
+ select QCOM_SCM if SMP
+ select CLKSRC_OF
+ select COMMON_CLK
+endmenu
endif
diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S
index a134d8a13d00..2ddf36403d1c 100644
--- a/arch/arm/mm/cache-v7.S
+++ b/arch/arm/mm/cache-v7.S
@@ -349,7 +349,7 @@ ENDPROC(v7_flush_kern_dcache_area)
* - start - virtual start address of region
* - end - virtual end address of region
*/
-v7_dma_inv_range:
+ENTRY(v7_dma_inv_range)
dcache_line_size r2, r3
sub r3, r2, #1
tst r0, r3
@@ -377,7 +377,7 @@ ENDPROC(v7_dma_inv_range)
* - start - virtual start address of region
* - end - virtual end address of region
*/
-v7_dma_clean_range:
+ENTRY(v7_dma_clean_range)
dcache_line_size r2, r3
sub r3, r2, #1
bic r0, r0, r3
diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S
index c671f345266a..9d14cdd02e00 100644
--- a/arch/arm/mm/proc-macros.S
+++ b/arch/arm/mm/proc-macros.S
@@ -309,6 +309,8 @@ ENTRY(\name\()_cache_fns)
.long \name\()_flush_kern_dcache_area
.long \name\()_dma_map_area
.long \name\()_dma_unmap_area
+ .long \name\()_dma_inv_range
+ .long \name\()_dma_clean_range
.long \name\()_dma_flush_range
.size \name\()_cache_fns, . - \name\()_cache_fns
.endm
diff --git a/arch/arm/mm/proc-syms.c b/arch/arm/mm/proc-syms.c
index 054b491ff764..70e8b7d34434 100644
--- a/arch/arm/mm/proc-syms.c
+++ b/arch/arm/mm/proc-syms.c
@@ -30,6 +30,9 @@ EXPORT_SYMBOL(__cpuc_flush_user_all);
EXPORT_SYMBOL(__cpuc_flush_user_range);
EXPORT_SYMBOL(__cpuc_coherent_kern_range);
EXPORT_SYMBOL(__cpuc_flush_dcache_area);
+EXPORT_SYMBOL(dmac_inv_range);
+EXPORT_SYMBOL(dmac_clean_range);
+EXPORT_SYMBOL(dmac_flush_range);
#else
EXPORT_SYMBOL(cpu_cache);
#endif
diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig
index 63934efc42af..efee2e854d2f 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -82,6 +82,9 @@ CONFIG_IP_MULTIPLE_TABLES=y
CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
# CONFIG_INET_LRO is not set
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_ROUTE_INFO=y
@@ -238,6 +241,7 @@ CONFIG_DM_UEVENT=y
CONFIG_DM_VERITY=y
CONFIG_NETDEVICES=y
CONFIG_BONDING=y
+CONFIG_DUMMY=y
CONFIG_TUN=y
CONFIG_SKY2=y
CONFIG_RNDIS_IPA=y
@@ -510,7 +514,8 @@ CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
-# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_ECRYPT_FS=y
+CONFIG_ECRYPT_FS_MESSAGING=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
@@ -526,9 +531,11 @@ CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_DEBUG_RODATA=y
CONFIG_DEBUG_ALIGN_RODATA=y
CONFIG_KEYS=y
+CONFIG_PFK=y
CONFIG_SECURITY=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SMACK=y
+CONFIG_CRYPTO_ECHAINIV=y
CONFIG_CRYPTO_XCBC=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_TWOFISH=y
@@ -537,6 +544,8 @@ CONFIG_CRYPTO_DEV_QCRYPTO=y
CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
+CONFIG_CRYPTO_DEV_QCE=y
+CONFIG_CRYPTO_DEV_QCOM_ICE=y
CONFIG_ARM64_CRYPTO=y
CONFIG_CRYPTO_SHA1_ARM64_CE=y
CONFIG_CRYPTO_SHA2_ARM64_CE=y
diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig
index 66a4f29c552c..5a0b5dd1a26b 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -83,6 +83,9 @@ CONFIG_IP_MULTIPLE_TABLES=y
CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
# CONFIG_INET_LRO is not set
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_ROUTE_INFO=y
@@ -243,6 +246,7 @@ CONFIG_DM_UEVENT=y
CONFIG_DM_VERITY=y
CONFIG_NETDEVICES=y
CONFIG_BONDING=y
+CONFIG_DUMMY=y
CONFIG_TUN=y
CONFIG_RNDIS_IPA=y
CONFIG_PHYLIB=y
@@ -542,7 +546,8 @@ CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_EFIVAR_FS=y
-# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_ECRYPT_FS=y
+CONFIG_ECRYPT_FS_MESSAGING=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
@@ -612,10 +617,12 @@ CONFIG_CORESIGHT_TPDM=y
CONFIG_CORESIGHT_QPDI=y
CONFIG_CORESIGHT_SOURCE_DUMMY=y
CONFIG_KEYS=y
+CONFIG_PFK=y
CONFIG_SECURITY=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SMACK=y
-CONFIG_CRYPTO_XCBC=m
+CONFIG_CRYPTO_ECHAINIV=y
+CONFIG_CRYPTO_XCBC=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_TWOFISH=y
CONFIG_CRYPTO_ANSI_CPRNG=y
@@ -623,6 +630,8 @@ CONFIG_CRYPTO_DEV_QCRYPTO=y
CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
+CONFIG_CRYPTO_DEV_QCE=y
+CONFIG_CRYPTO_DEV_QCOM_ICE=y
CONFIG_ARM64_CRYPTO=y
CONFIG_CRYPTO_SHA1_ARM64_CE=y
CONFIG_CRYPTO_SHA2_ARM64_CE=y
diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c
index d88c993bcc2d..60a834588767 100644
--- a/drivers/media/platform/msm/camera_v2/camera/camera.c
+++ b/drivers/media/platform/msm/camera_v2/camera/camera.c
@@ -93,6 +93,9 @@ static int camera_v4l2_querycap(struct file *filep, void *fh,
int rc;
struct v4l2_event event;
+ if (msm_is_daemon_present() == false)
+ return 0;
+
/* can use cap->driver to make differentiation */
camera_pack_event(filep, MSM_CAMERA_GET_PARM,
MSM_CAMERA_PRIV_QUERY_CAP, -1, &event);
@@ -112,6 +115,9 @@ static int camera_v4l2_s_crop(struct file *filep, void *fh,
int rc = 0;
struct v4l2_event event;
+ if (msm_is_daemon_present() == false)
+ return 0;
+
if (crop->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
camera_pack_event(filep, MSM_CAMERA_SET_PARM,
@@ -133,6 +139,9 @@ static int camera_v4l2_g_crop(struct file *filep, void *fh,
int rc = 0;
struct v4l2_event event;
+ if (msm_is_daemon_present() == false)
+ return 0;
+
if (crop->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
camera_pack_event(filep, MSM_CAMERA_GET_PARM,
MSM_CAMERA_PRIV_G_CROP, -1, &event);
@@ -153,6 +162,9 @@ static int camera_v4l2_queryctrl(struct file *filep, void *fh,
int rc = 0;
struct v4l2_event event;
+ if (msm_is_daemon_present() == false)
+ return 0;
+
if (ctrl->type == V4L2_CTRL_TYPE_MENU) {
camera_pack_event(filep, MSM_CAMERA_GET_PARM,
@@ -282,6 +294,10 @@ static int camera_v4l2_streamon(struct file *filep, void *fh,
mutex_lock(&sp->lock);
rc = vb2_streamon(&sp->vb2_q, buf_type);
mutex_unlock(&sp->lock);
+
+ if (msm_is_daemon_present() == false)
+ return 0;
+
camera_pack_event(filep, MSM_CAMERA_SET_PARM,
MSM_CAMERA_PRIV_STREAM_ON, -1, &event);
@@ -297,17 +313,18 @@ static int camera_v4l2_streamoff(struct file *filep, void *fh,
enum v4l2_buf_type buf_type)
{
struct v4l2_event event;
- int rc;
+ int rc = 0;
struct camera_v4l2_private *sp = fh_to_private(fh);
- camera_pack_event(filep, MSM_CAMERA_SET_PARM,
- MSM_CAMERA_PRIV_STREAM_OFF, -1, &event);
-
- rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
- if (rc < 0)
- return rc;
+ if (msm_is_daemon_present() != false) {
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_STREAM_OFF, -1, &event);
- rc = camera_check_event_status(&event);
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+ rc = camera_check_event_status(&event);
+ }
mutex_lock(&sp->lock);
vb2_streamoff(&sp->vb2_q, buf_type);
mutex_unlock(&sp->lock);
@@ -319,6 +336,9 @@ static int camera_v4l2_g_fmt_vid_cap_mplane(struct file *filep, void *fh,
{
int rc = -EINVAL;
+ if (msm_is_daemon_present() == false)
+ return 0;
+
if (pfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
struct v4l2_event event;
@@ -363,22 +383,22 @@ static int camera_v4l2_s_fmt_vid_cap_mplane(struct file *filep, void *fh,
pr_debug("%s: plane size[%d]\n", __func__,
user_fmt->plane_sizes[i]);
- camera_pack_event(filep, MSM_CAMERA_SET_PARM,
- MSM_CAMERA_PRIV_S_FMT, -1, &event);
-
- rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
- if (rc < 0)
- return rc;
+ if (msm_is_daemon_present() != false) {
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_S_FMT, -1, &event);
- rc = camera_check_event_status(&event);
- if (rc < 0)
- return rc;
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+ rc = camera_check_event_status(&event);
+ if (rc < 0)
+ return rc;
+ }
sp->is_vb2_valid = 1;
}
return rc;
-
}
static int camera_v4l2_try_fmt_vid_cap_mplane(struct file *filep, void *fh,
@@ -412,14 +432,15 @@ static int camera_v4l2_s_parm(struct file *filep, void *fh,
if (rc < 0)
return rc;
- rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
- if (rc < 0)
- goto error;
-
- rc = camera_check_event_status(&event);
- if (rc < 0)
- goto error;
+ if (msm_is_daemon_present() != false) {
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ goto error;
+ rc = camera_check_event_status(&event);
+ if (rc < 0)
+ goto error;
+ }
/* use stream_id as stream index */
parm->parm.capture.extendedmode = sp->stream_id;
sp->stream_created = true;
@@ -607,20 +628,19 @@ static int camera_v4l2_open(struct file *filep)
goto command_ack_q_fail;
}
- camera_pack_event(filep, MSM_CAMERA_NEW_SESSION, 0, -1, &event);
- rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
- if (rc < 0) {
- pr_err("%s : posting of NEW_SESSION event failed\n",
- __func__);
- pr_err("%s : Line %d rc %d\n", __func__, __LINE__, rc);
- goto post_fail;
- }
+ if (msm_is_daemon_present() != false) {
+ camera_pack_event(filep, MSM_CAMERA_NEW_SESSION,
+ 0, -1, &event);
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0) {
+ pr_err("%s : NEW_SESSION event failed,rc %d\n",
+ __func__, rc);
+ goto post_fail;
+ }
- rc = camera_check_event_status(&event);
- if (rc < 0) {
- pr_err("%s : checking event status fails Line %d rc %d\n",
- __func__, __LINE__, rc);
- goto post_fail;
+ rc = camera_check_event_status(&event);
+ if (rc < 0)
+ goto post_fail;
}
/* Enable power collapse latency */
msm_pm_qos_update_request(CAMERA_ENABLE_PC_LATENCY);
@@ -671,7 +691,6 @@ static unsigned int camera_v4l2_poll(struct file *filep,
static int camera_v4l2_close(struct file *filep)
{
- int rc = 0;
struct v4l2_event event;
struct msm_video_device *pvdev = video_drvdata(filep);
struct camera_v4l2_private *sp = fh_to_private(filep->private_data);
@@ -688,18 +707,22 @@ static int camera_v4l2_close(struct file *filep)
opn_idx &= ~mask;
atomic_set(&pvdev->opened, opn_idx);
- if (sp->stream_created == true) {
+ if (msm_is_daemon_present() != false && sp->stream_created == true) {
pr_debug("%s: close stream_id=%d\n", __func__, sp->stream_id);
camera_pack_event(filep, MSM_CAMERA_SET_PARM,
MSM_CAMERA_PRIV_DEL_STREAM, -1, &event);
msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
- sp->stream_created = false;
}
- if (atomic_read(&pvdev->opened) == 0) {
- camera_pack_event(filep, MSM_CAMERA_DEL_SESSION, 0, -1, &event);
- msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (sp->stream_created == true)
+ sp->stream_created = false;
+ if (atomic_read(&pvdev->opened) == 0) {
+ if (msm_is_daemon_present() != false) {
+ camera_pack_event(filep, MSM_CAMERA_DEL_SESSION,
+ 0, -1, &event);
+ msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ }
msm_delete_command_ack_q(pvdev->vdev->num, 0);
msm_delete_stream(pvdev->vdev->num, sp->stream_id);
mutex_unlock(&session->close_lock);
@@ -720,7 +743,7 @@ static int camera_v4l2_close(struct file *filep)
camera_v4l2_fh_release(filep);
- return rc;
+ return 0;
}
#ifdef CONFIG_COMPAT
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index 2f1b87148106..a34dbb80b468 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -40,6 +40,12 @@ static struct pm_qos_request msm_v4l2_pm_qos_request;
static struct msm_queue_head *msm_session_q;
+/* This variable represent daemon status
+ * true = daemon present (default state)
+ * false = daemon is NOT present
+ */
+bool is_daemon_status = true;
+
/* config node envent queue */
static struct v4l2_fh *msm_eventq;
spinlock_t msm_eventq_lock;
@@ -681,6 +687,12 @@ static long msm_private_ioctl(struct file *file, void *fh,
unsigned long spin_flags = 0;
struct msm_sd_subdev *msm_sd;
+ if (cmd == MSM_CAM_V4L2_IOCTL_DAEMON_DISABLED) {
+ is_daemon_status = false;
+ return 0;
+ }
+
+ memset(&event, 0, sizeof(struct v4l2_event));
session_id = event_data->session_id;
stream_id = event_data->stream_id;
diff --git a/drivers/media/platform/msm/camera_v2/msm.h b/drivers/media/platform/msm/camera_v2/msm.h
index 6920facef777..cab07df2a5bb 100644
--- a/drivers/media/platform/msm/camera_v2/msm.h
+++ b/drivers/media/platform/msm/camera_v2/msm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -40,6 +40,8 @@
#define CAMERA_DISABLE_PC_LATENCY 100
#define CAMERA_ENABLE_PC_LATENCY PM_QOS_DEFAULT_VALUE
+extern bool is_daemon_status;
+
struct msm_video_device {
struct video_device *vdev;
atomic_t opened;
@@ -110,8 +112,12 @@ struct msm_session {
struct mutex close_lock;
};
-void msm_pm_qos_update_request(int val);
+static inline bool msm_is_daemon_present(void)
+{
+ return is_daemon_status;
+}
+void msm_pm_qos_update_request(int val);
int msm_post_event(struct v4l2_event *event, int timeout);
int msm_create_session(unsigned int session, struct video_device *vdev);
int msm_destroy_session(unsigned int session_id);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c
index 82c9e5c5befa..5a891592b44f 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c
@@ -13,6 +13,7 @@
#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
#include <linux/module.h>
+#include <linux/firmware.h>
#include "msm_sd.h"
#include "msm_ois.h"
#include "msm_cci.h"
@@ -32,6 +33,124 @@ static int32_t msm_ois_power_down(struct msm_ois_ctrl_t *o_ctrl);
static struct i2c_driver msm_ois_i2c_driver;
+static int32_t msm_ois_download(struct msm_ois_ctrl_t *o_ctrl)
+{
+ uint16_t bytes_in_tx = 0;
+ uint16_t total_bytes = 0;
+ uint8_t *ptr = NULL;
+ int32_t rc = 0;
+ const struct firmware *fw = NULL;
+ const char *fw_name_prog = NULL;
+ const char *fw_name_coeff = NULL;
+ char name_prog[MAX_SENSOR_NAME] = {0};
+ char name_coeff[MAX_SENSOR_NAME] = {0};
+ struct device *dev = &(o_ctrl->pdev->dev);
+ enum msm_camera_i2c_reg_addr_type save_addr_type;
+
+ CDBG("Enter\n");
+ save_addr_type = o_ctrl->i2c_client.addr_type;
+ o_ctrl->i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR;
+
+ snprintf(name_coeff, MAX_SENSOR_NAME, "%s.coeff",
+ o_ctrl->oboard_info->ois_name);
+
+ snprintf(name_prog, MAX_SENSOR_NAME, "%s.prog",
+ o_ctrl->oboard_info->ois_name);
+
+ /* cast pointer as const pointer*/
+ fw_name_prog = name_prog;
+ fw_name_coeff = name_coeff;
+
+ /* Load FW */
+ rc = request_firmware(&fw, fw_name_prog, dev);
+ if (rc) {
+ dev_err(dev, "Failed to locate %s\n", fw_name_prog);
+ o_ctrl->i2c_client.addr_type = save_addr_type;
+ return rc;
+ }
+
+ total_bytes = fw->size;
+ for (ptr = (uint8_t *)fw->data; total_bytes;
+ total_bytes -= bytes_in_tx, ptr += bytes_in_tx) {
+ bytes_in_tx = (total_bytes > 10) ? 10 : total_bytes;
+ rc = o_ctrl->i2c_client.i2c_func_tbl->i2c_write_seq(
+ &o_ctrl->i2c_client, o_ctrl->oboard_info->opcode.prog,
+ ptr, bytes_in_tx);
+ if (rc < 0) {
+ pr_err("Failed:remaining bytes to be downloaded:%d\n",
+ bytes_in_tx);
+ /* abort download fw and return error*/
+ goto release_firmware;
+ }
+ }
+ release_firmware(fw);
+
+ rc = request_firmware(&fw, fw_name_coeff, dev);
+ if (rc) {
+ dev_err(dev, "Failed to locate %s\n", fw_name_coeff);
+ o_ctrl->i2c_client.addr_type = save_addr_type;
+ return rc;
+ }
+ total_bytes = fw->size;
+ for (ptr = (uint8_t *)fw->data; total_bytes;
+ total_bytes -= bytes_in_tx, ptr += bytes_in_tx) {
+ bytes_in_tx = (total_bytes > 10) ? 10 : total_bytes;
+ rc = o_ctrl->i2c_client.i2c_func_tbl->i2c_write_seq(
+ &o_ctrl->i2c_client, o_ctrl->oboard_info->opcode.coeff,
+ ptr, bytes_in_tx);
+ if (rc < 0) {
+ pr_err("Failed:remaining bytes to be downloaded:%d\n",
+ total_bytes);
+ /* abort download fw*/
+ break;
+ }
+ }
+release_firmware:
+ release_firmware(fw);
+ o_ctrl->i2c_client.addr_type = save_addr_type;
+
+ return rc;
+}
+
+static int32_t msm_ois_data_config(struct msm_ois_ctrl_t *o_ctrl,
+ struct msm_ois_slave_info *slave_info)
+{
+ int rc = 0;
+ struct msm_camera_cci_client *cci_client = NULL;
+
+ CDBG("Enter\n");
+ if (!slave_info) {
+ pr_err("failed : invalid slave_info\n");
+ return -EINVAL;
+ }
+ /* fill ois slave info*/
+ if (strlcpy(o_ctrl->oboard_info->ois_name, slave_info->ois_name,
+ sizeof(o_ctrl->oboard_info->ois_name)) < 0) {
+ pr_err("failed: copy_from_user\n");
+ return -EFAULT;
+ }
+ memcpy(&(o_ctrl->oboard_info->opcode), &(slave_info->opcode),
+ sizeof(struct msm_ois_opcode));
+ o_ctrl->oboard_info->i2c_slaveaddr = slave_info->i2c_addr;
+
+ /* config cci_client*/
+ if (o_ctrl->ois_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ cci_client = o_ctrl->i2c_client.cci_client;
+ cci_client->sid =
+ o_ctrl->oboard_info->i2c_slaveaddr >> 1;
+ cci_client->retries = 3;
+ cci_client->id_map = 0;
+ cci_client->cci_i2c_master = o_ctrl->cci_master;
+ } else {
+ o_ctrl->i2c_client.client->addr =
+ o_ctrl->oboard_info->i2c_slaveaddr;
+ }
+ o_ctrl->i2c_client.addr_type = MSM_CAMERA_I2C_WORD_ADDR;
+
+ CDBG("Exit\n");
+ return rc;
+}
+
static int32_t msm_ois_write_settings(struct msm_ois_ctrl_t *o_ctrl,
uint16_t size, struct reg_settings_ois_t *settings)
{
@@ -370,6 +489,40 @@ static int32_t msm_ois_config(struct msm_ois_ctrl_t *o_ctrl,
return rc;
}
+static int32_t msm_ois_config_download(struct msm_ois_ctrl_t *o_ctrl,
+ void __user *argp)
+{
+ struct msm_ois_cfg_download_data *cdata =
+ (struct msm_ois_cfg_download_data *)argp;
+ int32_t rc = 0;
+
+ if (!o_ctrl || !cdata) {
+ pr_err("failed: Invalid data\n");
+ return -EINVAL;
+ }
+ mutex_lock(o_ctrl->ois_mutex);
+ CDBG("Enter\n");
+ CDBG("%s type %d\n", __func__, cdata->cfgtype);
+ switch (cdata->cfgtype) {
+ case CFG_OIS_DATA_CONFIG:
+ rc = msm_ois_data_config(o_ctrl, &cdata->slave_info);
+ if (rc < 0)
+ pr_err("Failed ois data config %d\n", rc);
+ break;
+ case CFG_OIS_DOWNLOAD:
+ rc = msm_ois_download(o_ctrl);
+ if (rc < 0)
+ pr_err("Failed ois download %d\n", rc);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(o_ctrl->ois_mutex);
+ CDBG("Exit\n");
+ return rc;
+}
+
+
static int32_t msm_ois_get_subdev_id(struct msm_ois_ctrl_t *o_ctrl,
void *arg)
{
@@ -454,6 +607,8 @@ static long msm_ois_subdev_ioctl(struct v4l2_subdev *sd,
return msm_ois_get_subdev_id(o_ctrl, argp);
case VIDIOC_MSM_OIS_CFG:
return msm_ois_config(o_ctrl, argp);
+ case VIDIOC_MSM_OIS_CFG_DOWNLOAD:
+ return msm_ois_config_download(o_ctrl, argp);
case MSM_SD_SHUTDOWN:
if (!o_ctrl->i2c_client.i2c_func_tbl) {
pr_err("o_ctrl->i2c_client.i2c_func_tbl NULL\n");
@@ -696,22 +851,28 @@ static int32_t msm_ois_platform_probe(struct platform_device *pdev)
pr_err("%s:%d failed no memory\n", __func__, __LINE__);
return -ENOMEM;
}
+
+ msm_ois_t->oboard_info = kzalloc(sizeof(
+ struct msm_ois_board_info), GFP_KERNEL);
+ if (!msm_ois_t->oboard_info) {
+ kfree(msm_ois_t);
+ return -ENOMEM;
+ }
+
rc = of_property_read_u32((&pdev->dev)->of_node, "cell-index",
&pdev->id);
CDBG("cell-index %d, rc %d\n", pdev->id, rc);
if (rc < 0) {
- kfree(msm_ois_t);
pr_err("failed rc %d\n", rc);
- return rc;
+ goto release_memory;
}
rc = of_property_read_u32((&pdev->dev)->of_node, "qcom,cci-master",
&msm_ois_t->cci_master);
CDBG("qcom,cci-master %d, rc %d\n", msm_ois_t->cci_master, rc);
if (rc < 0 || msm_ois_t->cci_master >= MASTER_MAX) {
- kfree(msm_ois_t);
pr_err("failed rc %d\n", rc);
- return rc;
+ goto release_memory;
}
if (of_find_property((&pdev->dev)->of_node,
@@ -720,9 +881,8 @@ static int32_t msm_ois_platform_probe(struct platform_device *pdev)
rc = msm_camera_get_dt_vreg_data((&pdev->dev)->of_node,
&vreg_cfg->cam_vreg, &vreg_cfg->num_vreg);
if (rc < 0) {
- kfree(msm_ois_t);
pr_err("failed rc %d\n", rc);
- return rc;
+ goto release_memory;
}
}
@@ -753,9 +913,8 @@ static int32_t msm_ois_platform_probe(struct platform_device *pdev)
struct msm_camera_cci_client), GFP_KERNEL);
if (!msm_ois_t->i2c_client.cci_client) {
kfree(msm_ois_t->vreg_cfg.cam_vreg);
- kfree(msm_ois_t);
- pr_err("failed no memory\n");
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto release_memory;
}
cci_client = msm_ois_t->i2c_client.cci_client;
@@ -784,6 +943,10 @@ static int32_t msm_ois_platform_probe(struct platform_device *pdev)
CDBG("Exit\n");
return rc;
+release_memory:
+ kfree(msm_ois_t->oboard_info);
+ kfree(msm_ois_t);
+ return rc;
}
static const struct of_device_id msm_ois_i2c_dt_match[] = {
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.h b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.h
index b9f8d4a0c3c8..614cc6c8cd5d 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.h
@@ -41,6 +41,12 @@ struct msm_ois_vreg {
int num_vreg;
};
+struct msm_ois_board_info {
+ char ois_name[MAX_OIS_NAME_SIZE];
+ uint32_t i2c_slaveaddr;
+ struct msm_ois_opcode opcode;
+};
+
struct msm_ois_ctrl_t {
struct i2c_driver *i2c_driver;
struct platform_driver *pdriver;
@@ -61,6 +67,7 @@ struct msm_ois_ctrl_t {
struct msm_camera_gpio_conf *gconf;
struct msm_pinctrl_info pinctrl_info;
uint8_t cam_pinctrl_status;
+ struct msm_ois_board_info *oboard_info;
};
#endif
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 1d5191373d36..14b62984eae9 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -1186,6 +1186,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.minimum = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_DISABLE,
.maximum = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_ENABLE,
.default_value = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_DISABLE,
+ .step = 1,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH,
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 8e7812a89e67..0140c727e44d 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -721,6 +721,20 @@ config MFD_SPMI_PMIC
Say M here if you want to include support for the SPMI PMIC
series as a module. The module will be called "qcom-spmi-pmic".
+config MFD_I2C_PMIC
+ tristate "QTI I2C PMICs"
+ depends on OF
+ depends on I2C
+ select IRQ_DOMAIN
+ select REGMAP_I2C
+ help
+ This enables support for controlling Qualcomm Technologies, Inc.
+ PMICs over I2C. The driver controls interrupts, and provides register
+ access for all of the device's peripherals.
+
+ Say M here if you want to include support for the I2C PMIC series as
+ a module. The module will be called "qcom-i2c-pmic".
+
config MFD_RDC321X
tristate "RDC R-321x southbridge"
select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 5bd54625c3e4..26c118f77484 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -161,6 +161,7 @@ obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o
obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o
obj-$(CONFIG_MFD_QCOM_RPM) += qcom_rpm.o
obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o
+obj-$(CONFIG_MFD_I2C_PMIC) += qcom-i2c-pmic.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
obj-$(CONFIG_MFD_TPS65090) += tps65090.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
diff --git a/drivers/mfd/qcom-i2c-pmic.c b/drivers/mfd/qcom-i2c-pmic.c
new file mode 100644
index 000000000000..4d0bdce755a6
--- /dev/null
+++ b/drivers/mfd/qcom-i2c-pmic.c
@@ -0,0 +1,658 @@
+/* Copyright (c) 2016 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "I2C PMIC: %s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define I2C_INTR_STATUS_BASE 0x0550
+#define INT_RT_STS_OFFSET 0x10
+#define INT_SET_TYPE_OFFSET 0x11
+#define INT_POL_HIGH_OFFSET 0x12
+#define INT_POL_LOW_OFFSET 0x13
+#define INT_LATCHED_CLR_OFFSET 0x14
+#define INT_EN_SET_OFFSET 0x15
+#define INT_EN_CLR_OFFSET 0x16
+#define INT_LATCHED_STS_OFFSET 0x18
+#define INT_PENDING_STS_OFFSET 0x19
+#define INT_MID_SEL_OFFSET 0x1A
+#define INT_MID_SEL_MASK GENMASK(1, 0)
+#define INT_PRIORITY_OFFSET 0x1B
+#define INT_PRIORITY_BIT BIT(0)
+
+enum {
+ IRQ_SET_TYPE = 0,
+ IRQ_POL_HIGH,
+ IRQ_POL_LOW,
+ IRQ_LATCHED_CLR, /* not needed but makes life easy */
+ IRQ_EN_SET,
+ IRQ_MAX_REGS,
+};
+
+struct i2c_pmic_periph {
+ void *data;
+ u16 addr;
+ u8 cached[IRQ_MAX_REGS];
+ u8 synced[IRQ_MAX_REGS];
+ u8 wake;
+ struct mutex lock;
+};
+
+struct i2c_pmic {
+ struct device *dev;
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct irq_domain *domain;
+ struct i2c_pmic_periph *periph;
+ struct pinctrl *pinctrl;
+ const char *pinctrl_name;
+ int num_periphs;
+};
+
+static void i2c_pmic_irq_bus_lock(struct irq_data *d)
+{
+ struct i2c_pmic_periph *periph = irq_data_get_irq_chip_data(d);
+
+ mutex_lock(&periph->lock);
+}
+
+static void i2c_pmic_sync_type_polarity(struct i2c_pmic *chip,
+ struct i2c_pmic_periph *periph)
+{
+ int rc;
+
+ /* did any irq type change? */
+ if (periph->cached[IRQ_SET_TYPE] ^ periph->synced[IRQ_SET_TYPE]) {
+ rc = regmap_write(chip->regmap,
+ periph->addr | INT_SET_TYPE_OFFSET,
+ periph->cached[IRQ_SET_TYPE]);
+ if (rc < 0) {
+ pr_err("Couldn't set periph 0x%04x irqs 0x%02x type rc=%d\n",
+ periph->addr, periph->cached[IRQ_SET_TYPE], rc);
+ return;
+ }
+
+ periph->synced[IRQ_SET_TYPE] = periph->cached[IRQ_SET_TYPE];
+ }
+
+ /* did any polarity high change? */
+ if (periph->cached[IRQ_POL_HIGH] ^ periph->synced[IRQ_POL_HIGH]) {
+ rc = regmap_write(chip->regmap,
+ periph->addr | INT_POL_HIGH_OFFSET,
+ periph->cached[IRQ_POL_HIGH]);
+ if (rc < 0) {
+ pr_err("Couldn't set periph 0x%04x irqs 0x%02x polarity high rc=%d\n",
+ periph->addr, periph->cached[IRQ_POL_HIGH], rc);
+ return;
+ }
+
+ periph->synced[IRQ_POL_HIGH] = periph->cached[IRQ_POL_HIGH];
+ }
+
+ /* did any polarity low change? */
+ if (periph->cached[IRQ_POL_LOW] ^ periph->synced[IRQ_POL_LOW]) {
+ rc = regmap_write(chip->regmap,
+ periph->addr | INT_POL_LOW_OFFSET,
+ periph->cached[IRQ_POL_LOW]);
+ if (rc < 0) {
+ pr_err("Couldn't set periph 0x%04x irqs 0x%02x polarity low rc=%d\n",
+ periph->addr, periph->cached[IRQ_POL_LOW], rc);
+ return;
+ }
+
+ periph->synced[IRQ_POL_LOW] = periph->cached[IRQ_POL_LOW];
+ }
+}
+
+static void i2c_pmic_sync_enable(struct i2c_pmic *chip,
+ struct i2c_pmic_periph *periph)
+{
+ u8 en_set, en_clr;
+ int rc;
+
+ /* determine which irqs were enabled and which were disabled */
+ en_clr = periph->synced[IRQ_EN_SET] & ~periph->cached[IRQ_EN_SET];
+ en_set = ~periph->synced[IRQ_EN_SET] & periph->cached[IRQ_EN_SET];
+
+ /* were any irqs disabled? */
+ if (en_clr) {
+ rc = regmap_write(chip->regmap,
+ periph->addr | INT_EN_CLR_OFFSET, en_clr);
+ if (rc < 0) {
+ pr_err("Couldn't disable periph 0x%04x irqs 0x%02x rc=%d\n",
+ periph->addr, en_clr, rc);
+ return;
+ }
+ }
+
+ /* were any irqs enabled? */
+ if (en_set) {
+ rc = regmap_write(chip->regmap,
+ periph->addr | INT_EN_SET_OFFSET, en_set);
+ if (rc < 0) {
+ pr_err("Couldn't enable periph 0x%04x irqs 0x%02x rc=%d\n",
+ periph->addr, en_set, rc);
+ return;
+ }
+ }
+
+ /* irq enabled status was written to hardware */
+ periph->synced[IRQ_EN_SET] = periph->cached[IRQ_EN_SET];
+}
+
+static void i2c_pmic_irq_bus_sync_unlock(struct irq_data *d)
+{
+ struct i2c_pmic_periph *periph = irq_data_get_irq_chip_data(d);
+ struct i2c_pmic *chip = periph->data;
+
+ i2c_pmic_sync_type_polarity(chip, periph);
+ i2c_pmic_sync_enable(chip, periph);
+ mutex_unlock(&periph->lock);
+}
+
+static void i2c_pmic_irq_disable(struct irq_data *d)
+{
+ struct i2c_pmic_periph *periph = irq_data_get_irq_chip_data(d);
+
+ periph->cached[IRQ_EN_SET] &= ~d->hwirq & 0xFF;
+}
+
+static void i2c_pmic_irq_enable(struct irq_data *d)
+{
+ struct i2c_pmic_periph *periph = irq_data_get_irq_chip_data(d);
+
+ periph->cached[IRQ_EN_SET] |= d->hwirq & 0xFF;
+}
+
+static int i2c_pmic_irq_set_type(struct irq_data *d, unsigned int irq_type)
+{
+ struct i2c_pmic_periph *periph = irq_data_get_irq_chip_data(d);
+
+ switch (irq_type) {
+ case IRQ_TYPE_EDGE_RISING:
+ periph->cached[IRQ_SET_TYPE] |= d->hwirq & 0xFF;
+ periph->cached[IRQ_POL_HIGH] |= d->hwirq & 0xFF;
+ periph->cached[IRQ_POL_LOW] &= ~d->hwirq & 0xFF;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ periph->cached[IRQ_SET_TYPE] |= d->hwirq & 0xFF;
+ periph->cached[IRQ_POL_HIGH] &= ~d->hwirq & 0xFF;
+ periph->cached[IRQ_POL_LOW] |= d->hwirq & 0xFF;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ periph->cached[IRQ_SET_TYPE] |= d->hwirq & 0xFF;
+ periph->cached[IRQ_POL_HIGH] |= d->hwirq & 0xFF;
+ periph->cached[IRQ_POL_LOW] |= d->hwirq & 0xFF;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ periph->cached[IRQ_SET_TYPE] &= ~d->hwirq & 0xFF;
+ periph->cached[IRQ_POL_HIGH] |= d->hwirq & 0xFF;
+ periph->cached[IRQ_POL_LOW] &= ~d->hwirq & 0xFF;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ periph->cached[IRQ_SET_TYPE] &= ~d->hwirq & 0xFF;
+ periph->cached[IRQ_POL_HIGH] &= ~d->hwirq & 0xFF;
+ periph->cached[IRQ_POL_LOW] |= d->hwirq & 0xFF;
+ break;
+ default:
+ pr_err("irq type 0x%04x is not supported\n", irq_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int i2c_pmic_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ struct i2c_pmic_periph *periph = irq_data_get_irq_chip_data(d);
+
+ if (on)
+ periph->wake |= d->hwirq & 0xFF;
+ else
+ periph->wake &= ~d->hwirq & 0xFF;
+
+ return 0;
+}
+#else
+#define i2c_pmic_irq_set_wake NULL
+#endif
+
+static struct irq_chip i2c_pmic_irq_chip = {
+ .name = "i2c_pmic_irq_chip",
+ .irq_bus_lock = i2c_pmic_irq_bus_lock,
+ .irq_bus_sync_unlock = i2c_pmic_irq_bus_sync_unlock,
+ .irq_disable = i2c_pmic_irq_disable,
+ .irq_enable = i2c_pmic_irq_enable,
+ .irq_set_type = i2c_pmic_irq_set_type,
+ .irq_set_wake = i2c_pmic_irq_set_wake,
+};
+
+static struct i2c_pmic_periph *i2c_pmic_find_periph(struct i2c_pmic *chip,
+ irq_hw_number_t hwirq)
+{
+ int i;
+
+ for (i = 0; i < chip->num_periphs; i++)
+ if (chip->periph[i].addr == (hwirq & 0xFF00))
+ return &chip->periph[i];
+
+ pr_err_ratelimited("Couldn't find periph struct for hwirq 0x%04lx\n",
+ hwirq);
+ return NULL;
+}
+
+static int i2c_pmic_domain_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hwirq)
+{
+ struct i2c_pmic *chip = d->host_data;
+ struct i2c_pmic_periph *periph = i2c_pmic_find_periph(chip, hwirq);
+
+ if (!periph)
+ return -ENODEV;
+
+ irq_set_chip_data(virq, periph);
+ irq_set_chip_and_handler(virq, &i2c_pmic_irq_chip, handle_level_irq);
+ irq_set_nested_thread(virq, 1);
+ irq_set_noprobe(virq);
+ return 0;
+}
+
+static int i2c_pmic_domain_xlate(struct irq_domain *d,
+ struct device_node *ctrlr, const u32 *intspec,
+ unsigned int intsize, unsigned long *out_hwirq,
+ unsigned int *out_type)
+{
+ if (intsize != 3)
+ return -EINVAL;
+
+ if (intspec[0] > 0xFF || intspec[1] > 0x7 ||
+ intspec[2] > IRQ_TYPE_SENSE_MASK)
+ return -EINVAL;
+
+ /*
+ * Interrupt specifiers are triplets
+ * <peripheral-address, irq-number, IRQ_TYPE_*>
+ *
+ * peripheral-address - The base address of the peripheral
+ * irq-number - The zero based bit position of the peripheral's
+ * interrupt registers corresponding to the irq
+ * where the LSB is 0 and the MSB is 7
+ * IRQ_TYPE_* - Please refer to linux/irq.h
+ */
+ *out_hwirq = intspec[0] << 8 | BIT(intspec[1]);
+ *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
+
+ return 0;
+}
+
+static const struct irq_domain_ops i2c_pmic_domain_ops = {
+ .map = i2c_pmic_domain_map,
+ .xlate = i2c_pmic_domain_xlate,
+};
+
+static void i2c_pmic_irq_ack_now(struct i2c_pmic *chip, u16 hwirq)
+{
+ int rc;
+
+ rc = regmap_write(chip->regmap,
+ (hwirq & 0xFF00) | INT_LATCHED_CLR_OFFSET,
+ hwirq & 0xFF);
+ if (rc < 0)
+ pr_err_ratelimited("Couldn't ack 0x%04x rc=%d\n", hwirq, rc);
+}
+
+static void i2c_pmic_irq_disable_now(struct i2c_pmic *chip, u16 hwirq)
+{
+ struct i2c_pmic_periph *periph = i2c_pmic_find_periph(chip, hwirq);
+ int rc;
+
+ if (!periph)
+ return;
+
+ mutex_lock(&periph->lock);
+ periph->cached[IRQ_EN_SET] &= ~hwirq & 0xFF;
+
+ rc = regmap_write(chip->regmap,
+ (hwirq & 0xFF00) | INT_EN_CLR_OFFSET,
+ hwirq & 0xFF);
+ if (rc < 0) {
+ pr_err_ratelimited("Couldn't disable irq 0x%04x rc=%d\n",
+ hwirq, rc);
+ goto unlock;
+ }
+
+ periph->synced[IRQ_EN_SET] = periph->cached[IRQ_EN_SET];
+
+unlock:
+ mutex_unlock(&periph->lock);
+}
+
+static void i2c_pmic_periph_status_handler(struct i2c_pmic *chip,
+ u16 periph_address, u8 periph_status)
+{
+ unsigned int hwirq, virq;
+ int i;
+
+ while (periph_status) {
+ i = ffs(periph_status) - 1;
+ periph_status &= ~BIT(i);
+ hwirq = periph_address | BIT(i);
+ virq = irq_find_mapping(chip->domain, hwirq);
+ if (virq == 0) {
+ pr_err_ratelimited("Couldn't find mapping; disabling 0x%04x\n",
+ hwirq);
+ i2c_pmic_irq_disable_now(chip, hwirq);
+ continue;
+ }
+
+ handle_nested_irq(virq);
+ i2c_pmic_irq_ack_now(chip, hwirq);
+ }
+}
+
+static void i2c_pmic_summary_status_handler(struct i2c_pmic *chip,
+ struct i2c_pmic_periph *periph,
+ u8 summary_status)
+{
+ unsigned int periph_status;
+ int rc, i;
+
+ while (summary_status) {
+ i = ffs(summary_status) - 1;
+ summary_status &= ~BIT(i);
+
+ rc = regmap_read(chip->regmap,
+ periph[i].addr | INT_LATCHED_STS_OFFSET,
+ &periph_status);
+ if (rc < 0) {
+ pr_err_ratelimited("Couldn't read 0x%04x | INT_LATCHED_STS rc=%d\n",
+ periph[i].addr, rc);
+ continue;
+ }
+
+ i2c_pmic_periph_status_handler(chip, periph[i].addr,
+ periph_status);
+ }
+}
+
+static irqreturn_t i2c_pmic_irq_handler(int irq, void *dev_id)
+{
+ struct i2c_pmic *chip = dev_id;
+ struct i2c_pmic_periph *periph;
+ unsigned int summary_status;
+ int rc, i;
+
+ for (i = 0; i < DIV_ROUND_UP(chip->num_periphs, BITS_PER_BYTE); i++) {
+ rc = regmap_read(chip->regmap, I2C_INTR_STATUS_BASE + i,
+ &summary_status);
+ if (rc < 0) {
+ pr_err_ratelimited("Couldn't read I2C_INTR_STATUS%d rc=%d\n",
+ i, rc);
+ continue;
+ }
+
+ if (summary_status == 0)
+ continue;
+
+ periph = &chip->periph[i * 8];
+ i2c_pmic_summary_status_handler(chip, periph, summary_status);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int i2c_pmic_parse_dt(struct i2c_pmic *chip)
+{
+ struct device_node *node = chip->dev->of_node;
+ int rc, i;
+ u32 temp;
+
+ if (!node) {
+ pr_err("missing device tree\n");
+ return -EINVAL;
+ }
+
+ chip->num_periphs = of_property_count_u32_elems(node,
+ "qcom,periph-map");
+ if (chip->num_periphs < 0) {
+ pr_err("missing qcom,periph-map property rc=%d\n",
+ chip->num_periphs);
+ return chip->num_periphs;
+ }
+
+ if (chip->num_periphs == 0) {
+ pr_err("qcom,periph-map must contain at least one address\n");
+ return -EINVAL;
+ }
+
+ chip->periph = devm_kcalloc(chip->dev, chip->num_periphs,
+ sizeof(*chip->periph), GFP_KERNEL);
+ if (!chip->periph)
+ return -ENOMEM;
+
+ for (i = 0; i < chip->num_periphs; i++) {
+ rc = of_property_read_u32_index(node, "qcom,periph-map",
+ i, &temp);
+ if (rc < 0) {
+ pr_err("Couldn't read qcom,periph-map[%d] rc=%d\n",
+ i, rc);
+ return rc;
+ }
+
+ chip->periph[i].addr = (u16)(temp << 8);
+ chip->periph[i].data = chip;
+ mutex_init(&chip->periph[i].lock);
+ }
+
+ of_property_read_string(node, "pinctrl-names", &chip->pinctrl_name);
+
+ return rc;
+}
+
+static int i2c_pmic_determine_initial_status(struct i2c_pmic *chip)
+{
+ int rc, i;
+
+ for (i = 0; i < chip->num_periphs; i++) {
+ rc = regmap_bulk_read(chip->regmap,
+ chip->periph[i].addr | INT_SET_TYPE_OFFSET,
+ chip->periph[i].cached, IRQ_MAX_REGS);
+ if (rc < 0) {
+ pr_err("Couldn't read irq data rc=%d\n", rc);
+ return rc;
+ }
+
+ memcpy(chip->periph[i].synced, chip->periph[i].cached,
+ IRQ_MAX_REGS * sizeof(*chip->periph[i].synced));
+ }
+
+ return 0;
+}
+
+static struct regmap_config i2c_pmic_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = 0xFFFF,
+};
+
+static int i2c_pmic_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc;
+ struct i2c_pmic *chip;
+
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->regmap = devm_regmap_init_i2c(client, &i2c_pmic_regmap_config);
+ if (!chip->regmap)
+ return -ENODEV;
+
+ chip->domain = irq_domain_add_tree(client->dev.of_node,
+ &i2c_pmic_domain_ops, chip);
+ if (!chip->domain)
+ return -ENOMEM;
+
+ chip->client = client;
+ chip->dev = &client->dev;
+ i2c_set_clientdata(client, chip);
+
+ rc = i2c_pmic_parse_dt(chip);
+ if (rc < 0) {
+ pr_err("Couldn't parse device tree rc=%d\n", rc);
+ goto cleanup;
+ }
+
+ rc = i2c_pmic_determine_initial_status(chip);
+ if (rc < 0) {
+ pr_err("Couldn't determine initial status rc=%d\n", rc);
+ goto cleanup;
+ }
+
+ if (chip->pinctrl_name) {
+ chip->pinctrl = devm_pinctrl_get_select(chip->dev,
+ chip->pinctrl_name);
+ if (IS_ERR(chip->pinctrl)) {
+ pr_err("Couldn't select %s pinctrl rc=%ld\n",
+ chip->pinctrl_name, PTR_ERR(chip->pinctrl));
+ rc = PTR_ERR(chip->pinctrl);
+ goto cleanup;
+ }
+ }
+
+ rc = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ i2c_pmic_irq_handler, IRQF_ONESHOT,
+ "i2c_pmic_stat_irq", chip);
+ if (rc < 0) {
+ pr_err("Couldn't request irq %d rc=%d\n", client->irq, rc);
+ goto cleanup;
+ }
+
+ enable_irq_wake(client->irq);
+ of_platform_populate(chip->dev->of_node, NULL, NULL, chip->dev);
+ pr_info("I2C PMIC probe successful\n");
+ return rc;
+
+cleanup:
+ if (chip->domain)
+ irq_domain_remove(chip->domain);
+ i2c_set_clientdata(client, NULL);
+ return rc;
+}
+
+static int i2c_pmic_remove(struct i2c_client *client)
+{
+ struct i2c_pmic *chip = i2c_get_clientdata(client);
+
+ of_platform_depopulate(chip->dev);
+ irq_domain_remove(chip->domain);
+ i2c_set_clientdata(client, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int i2c_pmic_suspend(struct device *dev)
+{
+ struct i2c_pmic *chip = dev_get_drvdata(dev);
+ struct i2c_pmic_periph *periph;
+ int rc = 0, i;
+
+ for (i = 0; i < chip->num_periphs; i++) {
+ periph = &chip->periph[i];
+
+ rc = regmap_write(chip->regmap,
+ periph->addr | INT_EN_CLR_OFFSET, 0xFF);
+ if (rc < 0) {
+ pr_err_ratelimited("Couldn't clear 0x%04x irqs rc=%d\n",
+ periph->addr, rc);
+ continue;
+ }
+
+ rc = regmap_write(chip->regmap,
+ periph->addr | INT_EN_SET_OFFSET,
+ periph->wake);
+ if (rc < 0)
+ pr_err_ratelimited("Couldn't enable 0x%04x wake irqs 0x%02x rc=%d\n",
+ periph->addr, periph->wake, rc);
+ }
+
+ return rc;
+}
+
+static int i2c_pmic_resume(struct device *dev)
+{
+ struct i2c_pmic *chip = dev_get_drvdata(dev);
+ struct i2c_pmic_periph *periph;
+ int rc = 0, i;
+
+ for (i = 0; i < chip->num_periphs; i++) {
+ periph = &chip->periph[i];
+
+ rc = regmap_write(chip->regmap,
+ periph->addr | INT_EN_CLR_OFFSET, 0xFF);
+ if (rc < 0) {
+ pr_err("Couldn't clear 0x%04x irqs rc=%d\n",
+ periph->addr, rc);
+ continue;
+ }
+
+ rc = regmap_write(chip->regmap,
+ periph->addr | INT_EN_SET_OFFSET,
+ periph->synced[IRQ_EN_SET]);
+ if (rc < 0)
+ pr_err("Couldn't restore 0x%04x synced irqs 0x%02x rc=%d\n",
+ periph->addr, periph->synced[IRQ_EN_SET], rc);
+ }
+
+ return rc;
+}
+#endif
+static SIMPLE_DEV_PM_OPS(i2c_pmic_pm_ops, i2c_pmic_suspend, i2c_pmic_resume);
+
+static const struct of_device_id i2c_pmic_match_table[] = {
+ { .compatible = "qcom,i2c-pmic", },
+ { },
+};
+
+static const struct i2c_device_id i2c_pmic_id[] = {
+ { "i2c-pmic", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, i2c_pmic_id);
+
+static struct i2c_driver i2c_pmic_driver = {
+ .driver = {
+ .name = "i2c_pmic",
+ .owner = THIS_MODULE,
+ .pm = &i2c_pmic_pm_ops,
+ .of_match_table = i2c_pmic_match_table,
+ },
+ .probe = i2c_pmic_probe,
+ .remove = i2c_pmic_remove,
+ .id_table = i2c_pmic_id,
+};
+
+module_i2c_driver(i2c_pmic_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:i2c_pmic");
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 71caa588a019..dafdbd99ce71 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -3570,6 +3570,10 @@ static int mmc_blk_cmdq_issue_rq(struct mmc_queue *mq, struct request *req)
mmc_get_card(card);
+#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
+ if (mmc_bus_needs_resume(card->host))
+ mmc_resume_bus(card->host);
+#endif
if (!card->host->cmdq_ctx.active_reqs && mmc_card_doing_bkops(card)) {
ret = mmc_cmdq_halt(card->host, true);
if (ret)
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 9128d6fb6169..8101b77c2acf 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -164,6 +164,8 @@ static int mmc_bus_suspend(struct device *dev)
if (ret)
return ret;
+ if (mmc_bus_needs_resume(host))
+ return 0;
ret = host->bus_ops->suspend(host);
return ret;
}
@@ -174,11 +176,17 @@ static int mmc_bus_resume(struct device *dev)
struct mmc_host *host = card->host;
int ret;
+ if (mmc_bus_manual_resume(host)) {
+ host->bus_resume_flags |= MMC_BUSRESUME_NEEDS_RESUME;
+ goto skip_full_resume;
+ }
+
ret = host->bus_ops->resume(host);
if (ret)
pr_warn("%s: error %d during resume (card was removed?)\n",
mmc_hostname(host), ret);
+skip_full_resume:
ret = pm_generic_resume(dev);
return ret;
}
@@ -190,6 +198,9 @@ static int mmc_runtime_suspend(struct device *dev)
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
+ if (mmc_bus_needs_resume(host))
+ return 0;
+
return host->bus_ops->runtime_suspend(host);
}
@@ -198,6 +209,9 @@ static int mmc_runtime_resume(struct device *dev)
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
+ if (mmc_bus_needs_resume(host))
+ host->bus_resume_flags &= ~MMC_BUSRESUME_NEEDS_RESUME;
+
return host->bus_ops->runtime_resume(host);
}
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index add448035213..b76bb7a74049 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -3048,6 +3048,44 @@ static inline void mmc_bus_put(struct mmc_host *host)
spin_unlock_irqrestore(&host->lock, flags);
}
+int mmc_resume_bus(struct mmc_host *host)
+{
+ unsigned long flags;
+ int err = 0;
+
+ if (!mmc_bus_needs_resume(host))
+ return -EINVAL;
+
+ pr_debug("%s: Starting deferred resume\n", mmc_hostname(host));
+ spin_lock_irqsave(&host->lock, flags);
+ host->bus_resume_flags &= ~MMC_BUSRESUME_NEEDS_RESUME;
+ host->rescan_disable = 0;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ mmc_bus_get(host);
+ if (host->bus_ops && !host->bus_dead && host->card) {
+ mmc_power_up(host, host->card->ocr);
+ BUG_ON(!host->bus_ops->resume);
+ host->bus_ops->resume(host);
+ if (mmc_card_cmdq(host->card)) {
+ err = mmc_cmdq_halt(host, false);
+ if (err)
+ pr_err("%s: %s: unhalt failed: %d\n",
+ mmc_hostname(host), __func__, err);
+ else
+ mmc_card_clr_suspended(host->card);
+ }
+ }
+
+ if (host->bus_ops->detect && !host->bus_dead)
+ host->bus_ops->detect(host);
+
+ mmc_bus_put(host);
+ pr_debug("%s: Deferred resume completed\n", mmc_hostname(host));
+ return 0;
+}
+EXPORT_SYMBOL(mmc_resume_bus);
+
/*
* Assign a mmc bus handler to a host. Only one bus handler may control a
* host at any given time.
@@ -4226,6 +4264,10 @@ int mmc_pm_notify(struct notifier_block *notify_block,
case PM_SUSPEND_PREPARE:
case PM_RESTORE_PREPARE:
spin_lock_irqsave(&host->lock, flags);
+ if (mmc_bus_needs_resume(host)) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ break;
+ }
host->rescan_disable = 1;
spin_unlock_irqrestore(&host->lock, flags);
cancel_delayed_work_sync(&host->detect);
@@ -4254,6 +4296,10 @@ int mmc_pm_notify(struct notifier_block *notify_block,
spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 0;
+ if (mmc_bus_manual_resume(host)) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ break;
+ }
spin_unlock_irqrestore(&host->lock, flags);
_mmc_detect_change(host, 0, false);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index b32b87f44f92..6bfd95823da7 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2821,6 +2821,25 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask)
}
if (host->cmd->error) {
+ /*
+ * If this command initiates a data phase and a response
+ * CRC error is signalled, the card can start transferring
+ * data - the card may have received the command without
+ * error. We must not terminate the mmc_request early.
+ *
+ * If the card did not receive the command or returned an
+ * error which prevented it sending data, the data phase
+ * will time out.
+ *
+ * Even in case of cmd INDEX OR ENDBIT error we
+ * handle it the same way.
+ */
+ if (host->cmd->data &&
+ (((intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) ==
+ SDHCI_INT_CRC) || (host->cmd->error == -EILSEQ))) {
+ host->cmd = NULL;
+ return;
+ }
tasklet_schedule(&host->finish_tasklet);
return;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_interrupts.c b/drivers/platform/msm/ipa/ipa_v2/ipa_interrupts.c
index f30fd4c60171..17f577ab6c4c 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_interrupts.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_interrupts.c
@@ -151,31 +151,58 @@ fail_alloc_work:
return res;
}
+static inline bool is_uc_irq(int irq_num)
+{
+ if (ipa_interrupt_to_cb[irq_num].interrupt >= IPA_UC_IRQ_0 &&
+ ipa_interrupt_to_cb[irq_num].interrupt <= IPA_UC_IRQ_3)
+ return true;
+ else
+ return false;
+}
+
static void ipa_process_interrupts(bool isr_context)
{
u32 reg;
u32 bmsk;
u32 i = 0;
u32 en;
+ bool uc_irq;
en = ipa_read_reg(ipa_ctx->mmio, IPA_IRQ_EN_EE_n_ADDR(ipa_ee));
reg = ipa_read_reg(ipa_ctx->mmio, IPA_IRQ_STTS_EE_n_ADDR(ipa_ee));
while (en & reg) {
- /* Clear interrupt before processing to avoid
- clearing unhandled interrupts */
- ipa_write_reg(ipa_ctx->mmio,
- IPA_IRQ_CLR_EE_n_ADDR(ipa_ee), reg);
-
- /* Process the interrupts */
bmsk = 1;
for (i = 0; i < IPA_IRQ_NUM_MAX; i++) {
- if (en & reg & bmsk)
- handle_interrupt(i, isr_context);
+ if (!(en & reg & bmsk)) {
+ bmsk = bmsk << 1;
+ continue;
+ }
+ uc_irq = is_uc_irq(i);
+ /*
+ * Clear uC interrupt before processing to avoid
+ * clearing unhandled interrupts
+ */
+ if (uc_irq)
+ ipa_write_reg(ipa_ctx->mmio,
+ IPA_IRQ_CLR_EE_n_ADDR(ipa_ee), bmsk);
+
+ /* Process the interrupts */
+ handle_interrupt(i, isr_context);
+
+ /*
+ * Clear non uC interrupt after processing
+ * to avoid clearing interrupt data
+ */
+ if (!uc_irq)
+ ipa_write_reg(ipa_ctx->mmio,
+ IPA_IRQ_CLR_EE_n_ADDR(ipa_ee), bmsk);
+
bmsk = bmsk << 1;
}
-
- /* Check pending interrupts that may have
- been raised since last read */
+ /*
+ * Check pending interrupts that may have
+ * been raised since last read
+ */
reg = ipa_read_reg(ipa_ctx->mmio,
IPA_IRQ_STTS_EE_n_ADDR(ipa_ee));
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_interrupts.c b/drivers/platform/msm/ipa/ipa_v3/ipa_interrupts.c
index 45d3b13049bc..f0102a703812 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_interrupts.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_interrupts.c
@@ -235,6 +235,15 @@ static void ipa3_tx_suspend_interrupt_wa(void)
IPADBG_LOW("Exit\n");
}
+static inline bool is_uc_irq(int irq_num)
+{
+ if (ipa_interrupt_to_cb[irq_num].interrupt >= IPA_UC_IRQ_0 &&
+ ipa_interrupt_to_cb[irq_num].interrupt <= IPA_UC_IRQ_3)
+ return true;
+ else
+ return false;
+}
+
static void ipa3_process_interrupts(bool isr_context)
{
u32 reg;
@@ -242,6 +251,7 @@ static void ipa3_process_interrupts(bool isr_context)
u32 i = 0;
u32 en;
unsigned long flags;
+ bool uc_irq;
IPADBG_LOW("Enter\n");
@@ -252,6 +262,16 @@ static void ipa3_process_interrupts(bool isr_context)
bmsk = 1;
for (i = 0; i < IPA_IRQ_NUM_MAX; i++) {
if (en & reg & bmsk) {
+ uc_irq = is_uc_irq(i);
+
+ /*
+ * Clear uC interrupt before processing to avoid
+ * clearing unhandled interrupts
+ */
+ if (uc_irq)
+ ipa3_uc_rg10_write_reg(IPA_IRQ_CLR_EE_n,
+ ipa_ee, bmsk);
+
/*
* handle the interrupt with spin_lock
* unlocked to avoid calling client in atomic
@@ -262,6 +282,14 @@ static void ipa3_process_interrupts(bool isr_context)
spin_unlock_irqrestore(&suspend_wa_lock, flags);
ipa3_handle_interrupt(i, isr_context);
spin_lock_irqsave(&suspend_wa_lock, flags);
+
+ /*
+ * Clear non uC interrupt after processing
+ * to avoid clearing interrupt data
+ */
+ if (!uc_irq)
+ ipa3_uc_rg10_write_reg(IPA_IRQ_CLR_EE_n,
+ ipa_ee, bmsk);
}
bmsk = bmsk << 1;
}
@@ -272,7 +300,6 @@ static void ipa3_process_interrupts(bool isr_context)
if (ipa3_ctx->apply_rg10_wa && ipa3_ctx->uc_ctx.uc_failed)
break;
- ipa3_uc_rg10_write_reg(IPA_IRQ_CLR_EE_n, ipa_ee, reg);
reg = ipahal_read_reg_n(IPA_IRQ_STTS_EE_n, ipa_ee);
/* since the suspend interrupt HW bug we must
* read again the EN register, otherwise the while is endless
diff --git a/drivers/power/qcom-charger/Kconfig b/drivers/power/qcom-charger/Kconfig
index e36ecc66a04e..4a299b66e588 100644
--- a/drivers/power/qcom-charger/Kconfig
+++ b/drivers/power/qcom-charger/Kconfig
@@ -77,4 +77,14 @@ config QPNP_SMB2
help
Enables support for the SMB2 charging peripheral
+config QPNP_QNOVO
+ bool "QPNP QNOVO driver"
+ depends on SPMI
+ select POWER_SUPPLY
+ help
+ Say Y here to enable the Qnovo pulse charging engine. Qnovo driver
+ accepts pulse parameters via sysfs entries and programs the hardware
+ module. It also allows userspace code to read diagnostics of voltage
+ and current measured during certain phases of the pulses.
+
endmenu
diff --git a/drivers/power/qcom-charger/Makefile b/drivers/power/qcom-charger/Makefile
index 21af050d96f1..ee2a335079e3 100644
--- a/drivers/power/qcom-charger/Makefile
+++ b/drivers/power/qcom-charger/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_MSM_BCL_CTL) += msm_bcl.o
obj-$(CONFIG_MSM_BCL_PERIPHERAL_CTL) += bcl_peripheral.o
obj-$(CONFIG_BATTERY_BCL) += battery_current_limit.o
obj-$(CONFIG_QPNP_SMB2) += qpnp-smb2.o smb-lib.o pmic-voter.o
+obj-$(CONFIG_QPNP_QNOVO) += qpnp-qnovo.o
diff --git a/drivers/power/qcom-charger/pmic-voter.c b/drivers/power/qcom-charger/pmic-voter.c
index e1b6ced4ea58..d0bad7dec094 100644
--- a/drivers/power/qcom-charger/pmic-voter.c
+++ b/drivers/power/qcom-charger/pmic-voter.c
@@ -544,6 +544,9 @@ void destroy_votable(struct votable *votable)
unsigned long flags;
int i;
+ if (!votable)
+ return;
+
spin_lock_irqsave(&votable_list_slock, flags);
list_del(&votable->list);
spin_unlock_irqrestore(&votable_list_slock, flags);
diff --git a/drivers/power/qcom-charger/qpnp-qnovo.c b/drivers/power/qcom-charger/qpnp-qnovo.c
new file mode 100644
index 000000000000..d50188a5efbf
--- /dev/null
+++ b/drivers/power/qcom-charger/qpnp-qnovo.c
@@ -0,0 +1,1427 @@
+/* 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/power_supply.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/qpnp/qpnp-revid.h>
+#include "pmic-voter.h"
+
+#define QNOVO_REVISION1 0x00
+#define QNOVO_REVISION2 0x01
+#define QNOVO_PERPH_TYPE 0x04
+#define QNOVO_PERPH_SUBTYPE 0x05
+#define QNOVO_PTTIME_STS 0x07
+#define QNOVO_PTRAIN_STS 0x08
+#define QNOVO_ERROR_STS 0x09
+#define QNOVO_ERROR_BIT BIT(0)
+#define QNOVO_INT_RT_STS 0x10
+#define QNOVO_INT_SET_TYPE 0x11
+#define QNOVO_INT_POLARITY_HIGH 0x12
+#define QNOVO_INT_POLARITY_LOW 0x13
+#define QNOVO_INT_LATCHED_CLR 0x14
+#define QNOVO_INT_EN_SET 0x15
+#define QNOVO_INT_EN_CLR 0x16
+#define QNOVO_INT_LATCHED_STS 0x18
+#define QNOVO_INT_PENDING_STS 0x19
+#define QNOVO_INT_MID_SEL 0x1A
+#define QNOVO_INT_PRIORITY 0x1B
+#define QNOVO_PE_CTRL 0x40
+#define QNOVO_PREST1_CTRL 0x41
+#define QNOVO_PPULS1_LSB_CTRL 0x42
+#define QNOVO_PPULS1_MSB_CTRL 0x43
+#define QNOVO_NREST1_CTRL 0x44
+#define QNOVO_NPULS1_CTRL 0x45
+#define QNOVO_PPCNT_CTRL 0x46
+#define QNOVO_VLIM1_LSB_CTRL 0x47
+#define QNOVO_VLIM1_MSB_CTRL 0x48
+#define QNOVO_PTRAIN_EN 0x49
+#define QNOVO_PTRAIN_EN_BIT BIT(0)
+#define QNOVO_PE_CTRL2 0x4A
+#define QNOVO_PREST2_LSB_CTRL 0x50
+#define QNOVO_PREST2_MSB_CTRL 0x51
+#define QNOVO_PPULS2_LSB_CTRL 0x52
+#define QNOVO_PPULS2_MSB_CTRL 0x53
+#define QNOVO_NREST2_CTRL 0x54
+#define QNOVO_NPULS2_CTRL 0x55
+#define QNOVO_VLIM2_LSB_CTRL 0x56
+#define QNOVO_VLIM2_MSB_CTRL 0x57
+#define QNOVO_PVOLT1_LSB 0x60
+#define QNOVO_PVOLT1_MSB 0x61
+#define QNOVO_PCUR1_LSB 0x62
+#define QNOVO_PCUR1_MSB 0x63
+#define QNOVO_PVOLT2_LSB 0x70
+#define QNOVO_PVOLT2_MSB 0x71
+#define QNOVO_RVOLT2_LSB 0x72
+#define QNOVO_RVOLT2_MSB 0x73
+#define QNOVO_PCUR2_LSB 0x74
+#define QNOVO_PCUR2_MSB 0x75
+#define QNOVO_SCNT 0x80
+#define QNOVO_VMAX_LSB 0x90
+#define QNOVO_VMAX_MSB 0x91
+#define QNOVO_SNUM 0x92
+
+/* Registers ending in 0 imply external rsense */
+#define QNOVO_IADC_OFFSET_0 0xA0
+#define QNOVO_IADC_OFFSET_1 0xA1
+#define QNOVO_IADC_GAIN_0 0xA2
+#define QNOVO_IADC_GAIN_1 0xA3
+#define QNOVO_VADC_OFFSET 0xA4
+#define QNOVO_VADC_GAIN 0xA5
+#define QNOVO_IADC_GAIN_2 0xA6
+#define QNOVO_SPARE 0xA7
+#define QNOVO_STRM_CTRL 0xA8
+#define QNOVO_IADC_OFFSET_OVR_VAL 0xA9
+#define QNOVO_IADC_OFFSET_OVR 0xAA
+#define QNOVO_DISABLE_CHARGING 0xAB
+
+#define QNOVO_TR_IADC_OFFSET_0 0xF1
+#define QNOVO_TR_IADC_OFFSET_1 0xF2
+
+#define DRV_MAJOR_VERSION 1
+#define DRV_MINOR_VERSION 0
+
+#define IADC_LSB_NA 2441400
+#define VADC_LSB_NA 1220700
+#define GAIN_LSB_FACTOR 976560
+
+#define MIN_EN_UA 1000000
+
+#define USER_VOTER "user_voter"
+#define OK_TO_QNOVO_VOTER "ok_to_qnovo_voter"
+
+#define QNOVO_VOTER "qnovo_voter"
+
+struct qnovo_dt_props {
+ bool external_rsense;
+ struct device_node *revid_dev_node;
+};
+
+enum {
+ QNOVO_ERASE_OFFSET_WA_BIT = BIT(0),
+ QNOVO_NO_ERR_STS_BIT = BIT(1),
+};
+
+struct chg_props {
+ bool charging;
+ bool usb_online;
+ int usb_input_uA;
+ bool dc_online;
+ int dc_input_uA;
+};
+
+struct chg_status {
+ bool ok_to_qnovo;
+};
+
+struct qnovo {
+ int base;
+ struct mutex write_lock;
+ struct regmap *regmap;
+ struct qnovo_dt_props dt;
+ struct device *dev;
+ struct votable *disable_votable;
+ struct class qnovo_class;
+ struct pmic_revid_data *pmic_rev_id;
+ u32 wa_flags;
+ s64 external_offset_nA;
+ s64 internal_offset_nA;
+ s64 offset_nV;
+ s64 external_i_gain_mega;
+ s64 internal_i_gain_mega;
+ s64 v_gain_mega;
+ struct notifier_block nb;
+ struct power_supply *batt_psy;
+ struct power_supply *usb_psy;
+ struct power_supply *dc_psy;
+ struct chg_props cp;
+ struct chg_status cs;
+ struct work_struct status_change_work;
+ int fv_uV_request;
+ int fcc_uA_request;
+ struct votable *fcc_votable;
+ struct votable *fv_votable;
+};
+
+static int debug_mask;
+module_param_named(debug_mask, debug_mask, int, S_IRUSR | S_IWUSR);
+
+#define qnovo_dbg(chip, reason, fmt, ...) \
+ do { \
+ if (debug_mask & (reason)) \
+ dev_info(chip->dev, fmt, ##__VA_ARGS__); \
+ else \
+ dev_dbg(chip->dev, fmt, ##__VA_ARGS__); \
+ } while (0)
+
+static bool is_secure(struct qnovo *chip, int addr)
+{
+ /* assume everything above 0x40 is secure */
+ return (bool)(addr >= 0x40);
+}
+
+static int qnovo_read(struct qnovo *chip, u16 addr, u8 *buf, int len)
+{
+ return regmap_bulk_read(chip->regmap, chip->base + addr, buf, len);
+}
+
+static int qnovo_masked_write(struct qnovo *chip, u16 addr, u8 mask, u8 val)
+{
+ int rc = 0;
+
+ mutex_lock(&chip->write_lock);
+ if (is_secure(chip, addr)) {
+ rc = regmap_write(chip->regmap,
+ ((chip->base + addr) & ~(0xFF)) | 0xD0, 0xA5);
+ if (rc < 0)
+ goto unlock;
+ }
+
+ rc = regmap_update_bits(chip->regmap, chip->base + addr, mask, val);
+
+unlock:
+ mutex_unlock(&chip->write_lock);
+ return rc;
+}
+
+static int qnovo_write(struct qnovo *chip, u16 addr, u8 *buf, int len)
+{
+ int i, rc = 0;
+ bool is_start_secure, is_end_secure;
+
+ is_start_secure = is_secure(chip, addr);
+ is_end_secure = is_secure(chip, addr + len);
+
+ if (!is_start_secure && !is_end_secure) {
+ mutex_lock(&chip->write_lock);
+ rc = regmap_bulk_write(chip->regmap, chip->base + addr,
+ buf, len);
+ goto unlock;
+ }
+
+ mutex_lock(&chip->write_lock);
+ for (i = addr; i < addr + len; i++) {
+ if (is_secure(chip, i)) {
+ rc = regmap_write(chip->regmap,
+ ((chip->base + i) & ~(0xFF)) | 0xD0, 0xA5);
+ if (rc < 0)
+ goto unlock;
+ }
+ rc = regmap_write(chip->regmap, chip->base + i, buf[i - addr]);
+ if (rc < 0)
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&chip->write_lock);
+ return rc;
+}
+
+static int qnovo_disable_cb(struct votable *votable, void *data, int disable,
+ const char *client)
+{
+ struct qnovo *chip = data;
+ int rc = 0;
+
+ if (disable) {
+ if (chip->fv_uV_request != -EINVAL) {
+ if (chip->fv_votable)
+ 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);
+ }
+ }
+
+ rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT,
+ disable ? 0 : QNOVO_PTRAIN_EN_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n",
+ disable ? "disable" : "enable", rc);
+ return rc;
+ }
+
+ if (!disable) {
+ if (chip->fv_uV_request != -EINVAL) {
+ if (!chip->fv_votable)
+ chip->fv_votable = find_votable("FV");
+ if (chip->fv_votable)
+ vote(chip->fv_votable, QNOVO_VOTER,
+ 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,
+ true, chip->fcc_uA_request);
+ }
+ }
+
+ return rc;
+}
+
+static int qnovo_parse_dt(struct qnovo *chip)
+{
+ struct device_node *node = chip->dev->of_node;
+ int rc;
+
+ if (!node) {
+ pr_err("device tree node missing\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node, "reg", &chip->base);
+ if (rc < 0) {
+ pr_err("Couldn't read base rc = %d\n", rc);
+ return rc;
+ }
+
+ chip->dt.external_rsense = of_property_read_bool(node,
+ "qcom,external-rsense");
+
+ chip->dt.revid_dev_node = of_parse_phandle(node, "qcom,pmic-revid", 0);
+ if (!chip->dt.revid_dev_node) {
+ pr_err("Missing qcom,pmic-revid property - driver failed\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int qnovo_check_chg_version(struct qnovo *chip)
+{
+ int rc;
+
+ chip->pmic_rev_id = get_revid_data(chip->dt.revid_dev_node);
+ if (IS_ERR(chip->pmic_rev_id)) {
+ rc = PTR_ERR(chip->pmic_rev_id);
+ if (rc != -EPROBE_DEFER)
+ pr_err("Unable to get pmic_revid rc=%d\n", rc);
+ return rc;
+ }
+
+ if ((chip->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE)
+ && (chip->pmic_rev_id->rev4 < PMICOBALT_V2P0_REV4)) {
+ chip->wa_flags |= QNOVO_ERASE_OFFSET_WA_BIT;
+ chip->wa_flags |= QNOVO_NO_ERR_STS_BIT;
+ }
+
+ return 0;
+}
+
+enum {
+ VER = 0,
+ OK_TO_QNOVO,
+ ENABLE,
+ FV_REQUEST,
+ FCC_REQUEST,
+ PE_CTRL_REG,
+ PE_CTRL2_REG,
+ PTRAIN_STS_REG,
+ INT_RT_STS_REG,
+ PREST1,
+ PPULS1,
+ NREST1,
+ NPULS1,
+ PPCNT,
+ VLIM1,
+ PVOLT1,
+ PCUR1,
+ PTTIME,
+ PREST2,
+ PPULS2,
+ NREST2,
+ NPULS2,
+ VLIM2,
+ PVOLT2,
+ RVOLT2,
+ PCUR2,
+ SCNT,
+ VMAX,
+ SNUM,
+ VBATT,
+ IBATT,
+ BATTTEMP,
+ BATTSOC,
+};
+
+struct param_info {
+ char *name;
+ int start_addr;
+ int num_regs;
+ int reg_to_unit_multiplier;
+ int reg_to_unit_divider;
+ int reg_to_unit_offset;
+ int min_val;
+ int max_val;
+ char *units_str;
+};
+
+static struct param_info params[] = {
+ [FV_REQUEST] = {
+ .units_str = "uV",
+ },
+ [FCC_REQUEST] = {
+ .units_str = "uA",
+ },
+ [PE_CTRL_REG] = {
+ .name = "CTRL_REG",
+ .start_addr = QNOVO_PE_CTRL,
+ .num_regs = 1,
+ .units_str = "",
+ },
+ [PE_CTRL2_REG] = {
+ .name = "PE_CTRL2_REG",
+ .start_addr = QNOVO_PE_CTRL2,
+ .num_regs = 1,
+ .units_str = "",
+ },
+ [PTRAIN_STS_REG] = {
+ .name = "PTRAIN_STS",
+ .start_addr = QNOVO_PTRAIN_STS,
+ .num_regs = 1,
+ .units_str = "",
+ },
+ [INT_RT_STS_REG] = {
+ .name = "INT_RT_STS",
+ .start_addr = QNOVO_INT_RT_STS,
+ .num_regs = 1,
+ .units_str = "",
+ },
+ [PREST1] = {
+ .name = "PREST1",
+ .start_addr = QNOVO_PREST1_CTRL,
+ .num_regs = 1,
+ .reg_to_unit_multiplier = 5,
+ .reg_to_unit_divider = 1,
+ .min_val = 5,
+ .max_val = 1275,
+ .units_str = "mS",
+ },
+ [PPULS1] = {
+ .name = "PPULS1",
+ .start_addr = QNOVO_PPULS1_LSB_CTRL,
+ .num_regs = 2,
+ .reg_to_unit_multiplier = 1600, /* converts to uC */
+ .reg_to_unit_divider = 1,
+ .min_val = 0,
+ .max_val = 104856000,
+ .units_str = "uC",
+ },
+ [NREST1] = {
+ .name = "NREST1",
+ .start_addr = QNOVO_NREST1_CTRL,
+ .num_regs = 1,
+ .reg_to_unit_multiplier = 5,
+ .reg_to_unit_divider = 1,
+ .min_val = 5,
+ .max_val = 1275,
+ .units_str = "mS",
+ },
+ [NPULS1] = {
+ .name = "NPULS1",
+ .start_addr = QNOVO_NPULS1_CTRL,
+ .num_regs = 1,
+ .reg_to_unit_multiplier = 5,
+ .reg_to_unit_divider = 1,
+ .min_val = 5,
+ .max_val = 1275,
+ .units_str = "mS",
+ },
+ [PPCNT] = {
+ .name = "PPCNT",
+ .start_addr = QNOVO_PPCNT_CTRL,
+ .num_regs = 1,
+ .reg_to_unit_multiplier = 1,
+ .reg_to_unit_divider = 1,
+ .units_str = "pulses",
+ },
+ [VLIM1] = {
+ .name = "VLIM1",
+ .start_addr = QNOVO_VLIM1_LSB_CTRL,
+ .num_regs = 2,
+ .reg_to_unit_multiplier = 610350, /* converts to nV */
+ .reg_to_unit_divider = 1,
+ .min_val = 0,
+ .max_val = 5000000,
+ .units_str = "uV",
+ },
+ [PVOLT1] = {
+ .name = "PVOLT1",
+ .start_addr = QNOVO_PVOLT1_LSB,
+ .num_regs = 2,
+ .reg_to_unit_multiplier = 610350, /* converts to nV */
+ .reg_to_unit_divider = 1,
+ .units_str = "uV",
+ },
+ [PCUR1] = {
+ .name = "PCUR1",
+ .start_addr = QNOVO_PCUR1_LSB,
+ .num_regs = 2,
+ .reg_to_unit_multiplier = 1220700, /* converts to nA */
+ .reg_to_unit_divider = 1,
+ .units_str = "uA",
+ },
+ [PTTIME] = {
+ .name = "PTTIME",
+ .start_addr = QNOVO_PTTIME_STS,
+ .num_regs = 1,
+ .reg_to_unit_multiplier = 2,
+ .reg_to_unit_divider = 1,
+ .min_val = 5,
+ .max_val = 1275,
+ .units_str = "S",
+ },
+ [PREST2] = {
+ .name = "PREST2",
+ .start_addr = QNOVO_PREST2_LSB_CTRL,
+ .num_regs = 2,
+ .reg_to_unit_multiplier = 5,
+ .reg_to_unit_divider = 1,
+ .min_val = 5,
+ .max_val = 327675,
+ .units_str = "mS",
+ },
+ [PPULS2] = {
+ .name = "PPULS2",
+ .start_addr = QNOVO_PPULS2_LSB_CTRL,
+ .num_regs = 2,
+ .reg_to_unit_multiplier = 1600, /* converts to uC */
+ .reg_to_unit_divider = 1,
+ .min_val = 0,
+ .max_val = 104856000,
+ .units_str = "uC",
+ },
+ [NREST2] = {
+ .name = "NREST2",
+ .start_addr = QNOVO_NREST2_CTRL,
+ .num_regs = 1,
+ .reg_to_unit_multiplier = 5,
+ .reg_to_unit_divider = 1,
+ .reg_to_unit_offset = -5,
+ .min_val = 5,
+ .max_val = 1280,
+ .units_str = "mS",
+ },
+ [NPULS2] = {
+ .name = "NPULS2",
+ .start_addr = QNOVO_NPULS2_CTRL,
+ .num_regs = 1,
+ .reg_to_unit_multiplier = 5,
+ .reg_to_unit_divider = 1,
+ .min_val = 5,
+ .max_val = 1275,
+ .units_str = "mS",
+ },
+ [VLIM2] = {
+ .name = "VLIM1",
+ .start_addr = QNOVO_VLIM2_LSB_CTRL,
+ .num_regs = 2,
+ .reg_to_unit_multiplier = 610350, /* converts to nV */
+ .reg_to_unit_divider = 1,
+ .min_val = 0,
+ .max_val = 5000000,
+ .units_str = "uV",
+ },
+ [PVOLT2] = {
+ .name = "PVOLT2",
+ .start_addr = QNOVO_PVOLT2_LSB,
+ .num_regs = 2,
+ .reg_to_unit_multiplier = 610350, /* converts to nV */
+ .reg_to_unit_divider = 1,
+ .units_str = "uV",
+ },
+ [RVOLT2] = {
+ .name = "RVOLT2",
+ .start_addr = QNOVO_RVOLT2_LSB,
+ .num_regs = 2,
+ .reg_to_unit_multiplier = 610350,
+ .reg_to_unit_divider = 1,
+ .units_str = "uV",
+ },
+ [PCUR2] = {
+ .name = "PCUR2",
+ .start_addr = QNOVO_PCUR2_LSB,
+ .num_regs = 2,
+ .reg_to_unit_multiplier = 1220700, /* converts to nA */
+ .reg_to_unit_divider = 1,
+ .units_str = "uA",
+ },
+ [SCNT] = {
+ .name = "SCNT",
+ .start_addr = QNOVO_SCNT,
+ .num_regs = 1,
+ .reg_to_unit_multiplier = 1,
+ .reg_to_unit_divider = 1,
+ .units_str = "pulses",
+ },
+ [VMAX] = {
+ .name = "VMAX",
+ .start_addr = QNOVO_VMAX_LSB,
+ .num_regs = 2,
+ .reg_to_unit_multiplier = 814000, /* converts to nV */
+ .reg_to_unit_divider = 1,
+ .units_str = "uV",
+ },
+ [SNUM] = {
+ .name = "SNUM",
+ .start_addr = QNOVO_SNUM,
+ .num_regs = 1,
+ .reg_to_unit_multiplier = 1,
+ .reg_to_unit_divider = 1,
+ .units_str = "pulses",
+ },
+ [VBATT] = {
+ .name = "POWER_SUPPLY_PROP_VOLTAGE_NOW",
+ .start_addr = POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ .units_str = "uV",
+ },
+ [IBATT] = {
+ .name = "POWER_SUPPLY_PROP_CURRENT_NOW",
+ .start_addr = POWER_SUPPLY_PROP_CURRENT_NOW,
+ .units_str = "uA",
+ },
+ [BATTTEMP] = {
+ .name = "POWER_SUPPLY_PROP_TEMP",
+ .start_addr = POWER_SUPPLY_PROP_TEMP,
+ .units_str = "uV",
+ },
+ [BATTSOC] = {
+ .name = "POWER_SUPPLY_PROP_CAPACITY",
+ .start_addr = POWER_SUPPLY_PROP_CAPACITY,
+ .units_str = "%",
+ },
+};
+
+static struct class_attribute qnovo_attributes[];
+
+static ssize_t version_show(struct class *c, struct class_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d.%d\n",
+ DRV_MAJOR_VERSION, DRV_MINOR_VERSION);
+}
+
+static ssize_t ok_to_qnovo_show(struct class *c, struct class_attribute *attr,
+ char *buf)
+{
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", chip->cs.ok_to_qnovo);
+}
+
+static ssize_t enable_show(struct class *c, struct class_attribute *attr,
+ char *ubuf)
+{
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ int val;
+
+ val = get_client_vote(chip->disable_votable, USER_VOTER);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", val);
+}
+
+static ssize_t enable_store(struct class *c, struct class_attribute *attr,
+ const char *ubuf, size_t count)
+{
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ unsigned long val;
+ bool disable;
+
+ if (kstrtoul(ubuf, 10, &val))
+ return -EINVAL;
+
+ disable = !val;
+
+ vote(chip->disable_votable, USER_VOTER, disable, 0);
+ return count;
+}
+
+static ssize_t val_show(struct class *c, struct class_attribute *attr,
+ char *ubuf)
+{
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ int i = attr - qnovo_attributes;
+ int val = 0;
+
+ if (i == FV_REQUEST)
+ val = chip->fv_uV_request;
+
+ if (i == FCC_REQUEST)
+ val = chip->fcc_uA_request;
+
+ return snprintf(ubuf, PAGE_SIZE, "%d%s\n", val, params[i].units_str);
+}
+
+static ssize_t val_store(struct class *c, struct class_attribute *attr,
+ const char *ubuf, size_t count)
+{
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ int i = attr - qnovo_attributes;
+ unsigned long val;
+
+ if (kstrtoul(ubuf, 10, &val))
+ return -EINVAL;
+
+ if (i == FV_REQUEST)
+ chip->fv_uV_request = val;
+
+ if (i == FCC_REQUEST)
+ chip->fcc_uA_request = val;
+
+ return count;
+}
+
+static ssize_t reg_show(struct class *c, struct class_attribute *attr,
+ char *ubuf)
+{
+ int i = attr - qnovo_attributes;
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ u8 buf[2] = {0, 0};
+ u16 regval;
+ int rc;
+
+ rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
+ if (rc < 0) {
+ pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
+ return -EINVAL;
+ }
+ regval = buf[1] << 8 | buf[0];
+
+ return snprintf(ubuf, PAGE_SIZE, "0x%04x%s\n",
+ regval, params[i].units_str);
+}
+
+static ssize_t reg_store(struct class *c, struct class_attribute *attr,
+ const char *ubuf, size_t count)
+{
+ int i = attr - qnovo_attributes;
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ u8 buf[2] = {0, 0};
+ unsigned long val;
+ int rc;
+
+ if (kstrtoul(ubuf, 16, &val))
+ return -EINVAL;
+
+ buf[0] = val & 0xFF;
+ buf[1] = (val >> 8) & 0xFF;
+
+ rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs);
+ if (rc < 0) {
+ pr_err("Couldn't write %s rc = %d\n", params[i].name, rc);
+ return -EINVAL;
+ }
+ return count;
+}
+
+static ssize_t time_show(struct class *c, struct class_attribute *attr,
+ char *ubuf)
+{
+ int i = attr - qnovo_attributes;
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ u8 buf[2] = {0, 0};
+ u16 regval;
+ int val;
+ int rc;
+
+ rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
+ if (rc < 0) {
+ pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
+ return -EINVAL;
+ }
+ regval = buf[1] << 8 | buf[0];
+
+ val = ((regval * params[i].reg_to_unit_multiplier)
+ / params[i].reg_to_unit_divider)
+ - params[i].reg_to_unit_offset;
+
+ return snprintf(ubuf, PAGE_SIZE, "%d%s\n", val, params[i].units_str);
+}
+
+static ssize_t time_store(struct class *c, struct class_attribute *attr,
+ const char *ubuf, size_t count)
+{
+ int i = attr - qnovo_attributes;
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ u8 buf[2] = {0, 0};
+ u16 regval;
+ unsigned long val;
+ int rc;
+
+ if (kstrtoul(ubuf, 10, &val))
+ return -EINVAL;
+
+ if (val < params[i].min_val || val > params[i].max_val) {
+ pr_err("Out of Range %d%s for %s\n", (int)val,
+ params[i].units_str,
+ params[i].name);
+ return -ERANGE;
+ }
+
+ regval = (((int)val + params[i].reg_to_unit_offset)
+ * params[i].reg_to_unit_divider)
+ / params[i].reg_to_unit_multiplier;
+ buf[0] = regval & 0xFF;
+ buf[1] = (regval >> 8) & 0xFF;
+
+ rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs);
+ if (rc < 0) {
+ pr_err("Couldn't write %s rc = %d\n", params[i].name, rc);
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t current_show(struct class *c, struct class_attribute *attr,
+ char *ubuf)
+{
+ int i = attr - qnovo_attributes;
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ u8 buf[2] = {0, 0};
+ int rc;
+ int comp_val_uA;
+ s64 regval_nA;
+ s64 gain, offset_nA, comp_val_nA;
+
+ rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
+ if (rc < 0) {
+ pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
+ return -EINVAL;
+ }
+ regval_nA = buf[1] << 8 | buf[0];
+ regval_nA = div_s64(regval_nA * params[i].reg_to_unit_multiplier,
+ params[i].reg_to_unit_divider)
+ - params[i].reg_to_unit_offset;
+
+ if (chip->dt.external_rsense) {
+ offset_nA = chip->external_offset_nA;
+ gain = chip->external_i_gain_mega;
+ } else {
+ offset_nA = chip->internal_offset_nA;
+ gain = chip->internal_i_gain_mega;
+ }
+
+ comp_val_nA = div_s64(regval_nA * gain, 1000000) + offset_nA;
+ comp_val_uA = comp_val_nA / 1000;
+
+ return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
+ comp_val_uA, params[i].units_str);
+}
+
+static ssize_t voltage_show(struct class *c, struct class_attribute *attr,
+ char *ubuf)
+{
+ int i = attr - qnovo_attributes;
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ u8 buf[2] = {0, 0};
+ int rc;
+ int comp_val_uV;
+ s64 regval_nV;
+ s64 gain, offset_nV, comp_val_nV;
+
+ rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
+ if (rc < 0) {
+ pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
+ return -EINVAL;
+ }
+ regval_nV = buf[1] << 8 | buf[0];
+ regval_nV = div_s64(regval_nV * params[i].reg_to_unit_multiplier,
+ params[i].reg_to_unit_divider)
+ - params[i].reg_to_unit_offset;
+
+ offset_nV = chip->offset_nV;
+ gain = chip->v_gain_mega;
+
+ comp_val_nV = div_s64(regval_nV * gain, 1000000) + offset_nV;
+ comp_val_uV = comp_val_nV / 1000;
+
+ return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
+ comp_val_uV, params[i].units_str);
+}
+
+static ssize_t voltage_store(struct class *c, struct class_attribute *attr,
+ const char *ubuf, size_t count)
+{
+ int i = attr - qnovo_attributes;
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ u8 buf[2] = {0, 0};
+ int rc;
+ unsigned long val_uV;
+ s64 regval_nV;
+ s64 gain, offset_nV;
+
+ if (kstrtoul(ubuf, 10, &val_uV))
+ return -EINVAL;
+
+ if (val_uV < params[i].min_val || val_uV > params[i].max_val) {
+ pr_err("Out of Range %d%s for %s\n", (int)val_uV,
+ params[i].units_str,
+ params[i].name);
+ return -ERANGE;
+ }
+
+ offset_nV = chip->offset_nV;
+ gain = chip->v_gain_mega;
+
+ regval_nV = (s64)val_uV * 1000 - offset_nV;
+ regval_nV = div_s64(regval_nV * 1000000, gain);
+
+ regval_nV = div_s64((regval_nV + params[i].reg_to_unit_offset)
+ * params[i].reg_to_unit_divider,
+ params[i].reg_to_unit_multiplier);
+ buf[0] = regval_nV & 0xFF;
+ buf[1] = ((u64)regval_nV >> 8) & 0xFF;
+
+ rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs);
+ if (rc < 0) {
+ pr_err("Couldn't write %s rc = %d\n", params[i].name, rc);
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t coulomb_show(struct class *c, struct class_attribute *attr,
+ char *ubuf)
+{
+ int i = attr - qnovo_attributes;
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ u8 buf[2] = {0, 0};
+ int rc;
+ int comp_val_uC;
+ s64 regval_uC, gain;
+
+ rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
+ if (rc < 0) {
+ pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
+ return -EINVAL;
+ }
+ regval_uC = buf[1] << 8 | buf[0];
+ regval_uC = div_s64(regval_uC * params[i].reg_to_unit_multiplier,
+ params[i].reg_to_unit_divider)
+ - params[i].reg_to_unit_offset;
+
+ if (chip->dt.external_rsense)
+ gain = chip->external_i_gain_mega;
+ else
+ gain = chip->internal_i_gain_mega;
+
+ comp_val_uC = div_s64(regval_uC * gain, 1000000);
+ return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
+ comp_val_uC, params[i].units_str);
+}
+
+static ssize_t coulomb_store(struct class *c, struct class_attribute *attr,
+ const char *ubuf, size_t count)
+{
+ int i = attr - qnovo_attributes;
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ u8 buf[2] = {0, 0};
+ int rc;
+ unsigned long val_uC;
+ s64 regval;
+ s64 gain;
+
+ if (kstrtoul(ubuf, 10, &val_uC))
+ return -EINVAL;
+
+ if (val_uC < params[i].min_val || val_uC > params[i].max_val) {
+ pr_err("Out of Range %d%s for %s\n", (int)val_uC,
+ params[i].units_str,
+ params[i].name);
+ return -ERANGE;
+ }
+
+ if (chip->dt.external_rsense)
+ gain = chip->external_i_gain_mega;
+ else
+ gain = chip->internal_i_gain_mega;
+
+ regval = div_s64((s64)val_uC * 1000000, gain);
+
+ regval = div_s64((regval + params[i].reg_to_unit_offset)
+ * params[i].reg_to_unit_divider,
+ params[i].reg_to_unit_multiplier);
+
+ buf[0] = regval & 0xFF;
+ buf[1] = ((u64)regval >> 8) & 0xFF;
+
+ rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs);
+ if (rc < 0) {
+ pr_err("Couldn't write %s rc = %d\n", params[i].name, rc);
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t batt_prop_show(struct class *c, struct class_attribute *attr,
+ char *ubuf)
+{
+ int i = attr - qnovo_attributes;
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ int rc = -EINVAL;
+ int prop = params[i].start_addr;
+ union power_supply_propval pval = {0};
+
+ if (!chip->batt_psy)
+ chip->batt_psy = power_supply_get_by_name("battery");
+
+ if (!chip->batt_psy)
+ return -EINVAL;
+
+ rc = power_supply_get_property(chip->batt_psy, prop, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't read battery prop %s rc = %d\n",
+ params[i].name, rc);
+ return -EINVAL;
+ }
+
+ return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
+ pval.intval, params[i].units_str);
+}
+
+static struct class_attribute qnovo_attributes[] = {
+ [VER] = __ATTR_RO(version),
+ [OK_TO_QNOVO] = __ATTR_RO(ok_to_qnovo),
+ [ENABLE] = __ATTR(enable, S_IRUGO | S_IWUSR,
+ enable_show, enable_store),
+ [FV_REQUEST] = __ATTR(fv_uV_request, S_IRUGO | S_IWUSR,
+ val_show, val_store),
+ [FCC_REQUEST] = __ATTR(fcc_uA_request, S_IRUGO | S_IWUSR,
+ val_show, val_store),
+ [PE_CTRL_REG] = __ATTR(PE_CTRL_REG, S_IRUGO | S_IWUSR,
+ reg_show, reg_store),
+ [PE_CTRL2_REG] = __ATTR(PE_CTRL2_REG, S_IRUGO | S_IWUSR,
+ reg_show, reg_store),
+ [PTRAIN_STS_REG] = __ATTR(PTRAIN_STS_REG, S_IRUGO | S_IWUSR,
+ reg_show, reg_store),
+ [INT_RT_STS_REG] = __ATTR(INT_RT_STS_REG, S_IRUGO | S_IWUSR,
+ reg_show, reg_store),
+ [PREST1] = __ATTR(PREST1_mS, S_IRUGO | S_IWUSR,
+ time_show, time_store),
+ [PPULS1] = __ATTR(PPULS1_uC, S_IRUGO | S_IWUSR,
+ coulomb_show, coulomb_store),
+ [NREST1] = __ATTR(NREST1_mS, S_IRUGO | S_IWUSR,
+ time_show, time_store),
+ [NPULS1] = __ATTR(NPULS1_mS, S_IRUGO | S_IWUSR,
+ time_show, time_store),
+ [PPCNT] = __ATTR(PPCNT, S_IRUGO | S_IWUSR,
+ time_show, time_store),
+ [VLIM1] = __ATTR(VLIM1_uV, S_IRUGO | S_IWUSR,
+ voltage_show, voltage_store),
+ [PVOLT1] = __ATTR(PVOLT1_uV, S_IRUGO,
+ voltage_show, NULL),
+ [PCUR1] = __ATTR(PCUR1_uA, S_IRUGO,
+ current_show, NULL),
+ [PTTIME] = __ATTR(PTTIME_S, S_IRUGO,
+ time_show, NULL),
+ [PREST2] = __ATTR(PREST2_mS, S_IRUGO | S_IWUSR,
+ time_show, time_store),
+ [PPULS2] = __ATTR(PPULS2_mS, S_IRUGO | S_IWUSR,
+ coulomb_show, coulomb_store),
+ [NREST2] = __ATTR(NREST2_mS, S_IRUGO | S_IWUSR,
+ time_show, time_store),
+ [NPULS2] = __ATTR(NPULS2_mS, S_IRUGO | S_IWUSR,
+ time_show, time_store),
+ [VLIM2] = __ATTR(VLIM2_uV, S_IRUGO | S_IWUSR,
+ voltage_show, voltage_store),
+ [PVOLT2] = __ATTR(PVOLT2_uV, S_IRUGO,
+ voltage_show, NULL),
+ [RVOLT2] = __ATTR(RVOLT2_uV, S_IRUGO,
+ voltage_show, NULL),
+ [PCUR2] = __ATTR(PCUR2_uA, S_IRUGO,
+ current_show, NULL),
+ [SCNT] = __ATTR(SCNT, S_IRUGO | S_IWUSR,
+ time_show, time_store),
+ [VMAX] = __ATTR(VMAX_uV, S_IRUGO,
+ voltage_show, NULL),
+ [SNUM] = __ATTR(SNUM, S_IRUGO | S_IWUSR,
+ time_show, time_store),
+ [VBATT] = __ATTR(VBATT_uV, S_IRUGO,
+ batt_prop_show, NULL),
+ [IBATT] = __ATTR(IBATT_uA, S_IRUGO,
+ batt_prop_show, NULL),
+ [BATTTEMP] = __ATTR(BATTTEMP_deciDegC, S_IRUGO,
+ batt_prop_show, NULL),
+ [BATTSOC] = __ATTR(BATTSOC, S_IRUGO,
+ batt_prop_show, NULL),
+ __ATTR_NULL,
+};
+
+static void get_chg_props(struct qnovo *chip, struct chg_props *cp)
+{
+ union power_supply_propval pval;
+ u8 val = 0;
+ int rc;
+
+ cp->charging = true;
+ rc = qnovo_read(chip, QNOVO_ERROR_STS, &val, 1);
+ if (rc < 0) {
+ pr_err("Couldn't read error sts rc = %d\n", rc);
+ cp->charging = false;
+ } else {
+ cp->charging = (!(val & QNOVO_ERROR_BIT));
+ }
+
+ if (chip->wa_flags & QNOVO_NO_ERR_STS_BIT) {
+ /*
+ * on v1.0 and v1.1 pmic's force charging to true
+ * if things are not good to charge s/w gets a PTRAIN_DONE
+ * interrupt
+ */
+ cp->charging = true;
+ }
+
+ cp->usb_online = false;
+ cp->usb_input_uA = 0;
+ if (!chip->usb_psy)
+ chip->usb_psy = power_supply_get_by_name("usb");
+ if (chip->usb_psy) {
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_ONLINE, &pval);
+ if (rc < 0)
+ pr_err("Couldn't read usb online rc = %d\n", rc);
+ else
+ cp->usb_online = (bool)pval.intval;
+
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
+ if (rc < 0)
+ pr_err("Couldn't read usb current max rc = %d\n", rc);
+ else
+ cp->usb_input_uA = pval.intval;
+ }
+
+ cp->dc_online = false;
+ cp->dc_input_uA = 0;
+ if (!chip->dc_psy)
+ chip->dc_psy = power_supply_get_by_name("dc");
+ if (chip->dc_psy) {
+ rc = power_supply_get_property(chip->dc_psy,
+ POWER_SUPPLY_PROP_ONLINE, &pval);
+ if (rc < 0)
+ pr_err("Couldn't read dc online rc = %d\n", rc);
+ else
+ cp->dc_online = (bool)pval.intval;
+
+ rc = power_supply_get_property(chip->dc_psy,
+ POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
+ if (rc < 0)
+ pr_err("Couldn't read dc current max rc = %d\n", rc);
+ else
+ cp->dc_input_uA = pval.intval;
+ }
+}
+
+static void get_chg_status(struct qnovo *chip, const struct chg_props *cp,
+ struct chg_status *cs)
+{
+ cs->ok_to_qnovo = false;
+
+ if (cp->charging &&
+ ((cp->usb_online && cp->usb_input_uA >= MIN_EN_UA)
+ || (cp->dc_online && cp->dc_input_uA >= MIN_EN_UA)))
+ cs->ok_to_qnovo = true;
+}
+
+static void status_change_work(struct work_struct *work)
+{
+ struct qnovo *chip = container_of(work,
+ struct qnovo, status_change_work);
+ bool notify_uevent = false;
+ struct chg_props cp;
+ struct chg_status cs;
+
+ get_chg_props(chip, &cp);
+ get_chg_status(chip, &cp, &cs);
+
+ if (cs.ok_to_qnovo ^ chip->cs.ok_to_qnovo) {
+ /*
+ * when it is not okay to Qnovo charge, disable both voters,
+ * so that when it becomes okay to Qnovo charge the user voter
+ * has to specifically enable its vote to being Qnovo charging
+ */
+ if (!cs.ok_to_qnovo) {
+ vote(chip->disable_votable, OK_TO_QNOVO_VOTER, 1, 0);
+ vote(chip->disable_votable, USER_VOTER, 1, 0);
+ } else {
+ vote(chip->disable_votable, OK_TO_QNOVO_VOTER, 0, 0);
+ }
+ notify_uevent = true;
+ }
+
+ memcpy(&chip->cp, &cp, sizeof(struct chg_props));
+ memcpy(&chip->cs, &cs, sizeof(struct chg_status));
+
+ if (notify_uevent)
+ kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
+}
+
+static int qnovo_notifier_call(struct notifier_block *nb,
+ unsigned long ev, void *v)
+{
+ struct power_supply *psy = v;
+ struct qnovo *chip = container_of(nb, struct qnovo, nb);
+
+ if (ev != PSY_EVENT_PROP_CHANGED)
+ return NOTIFY_OK;
+ if ((strcmp(psy->desc->name, "battery") == 0)
+ || (strcmp(psy->desc->name, "usb") == 0))
+ schedule_work(&chip->status_change_work);
+
+ return NOTIFY_OK;
+}
+
+static irqreturn_t handle_ptrain_done(int irq, void *data)
+{
+ struct qnovo *chip = data;
+
+ /* disable user voter here */
+ vote(chip->disable_votable, USER_VOTER, 0, 0);
+ kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
+ return IRQ_HANDLED;
+}
+
+static int qnovo_hw_init(struct qnovo *chip)
+{
+ int rc;
+ u8 iadc_offset_external, iadc_offset_internal;
+ u8 iadc_gain_external, iadc_gain_internal;
+ u8 vadc_offset, vadc_gain;
+ u8 buf[2] = {0, 0};
+
+ vote(chip->disable_votable, USER_VOTER, 1, 0);
+
+ rc = qnovo_read(chip, QNOVO_IADC_OFFSET_0, &iadc_offset_external, 1);
+ if (rc < 0) {
+ pr_err("Couldn't read iadc exernal offset rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = qnovo_read(chip, QNOVO_IADC_OFFSET_1, &iadc_offset_internal, 1);
+ if (rc < 0) {
+ pr_err("Couldn't read iadc internal offset rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = qnovo_read(chip, QNOVO_IADC_GAIN_0, &iadc_gain_external, 1);
+ if (rc < 0) {
+ pr_err("Couldn't read iadc external gain rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = qnovo_read(chip, QNOVO_IADC_GAIN_1, &iadc_gain_internal, 1);
+ if (rc < 0) {
+ pr_err("Couldn't read iadc internal gain rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = qnovo_read(chip, QNOVO_VADC_OFFSET, &vadc_offset, 1);
+ if (rc < 0) {
+ pr_err("Couldn't read vadc offset rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = qnovo_read(chip, QNOVO_VADC_GAIN, &vadc_gain, 1);
+ if (rc < 0) {
+ pr_err("Couldn't read vadc external gain rc = %d\n", rc);
+ return rc;
+ }
+
+ chip->external_offset_nA = (s64)iadc_offset_external * IADC_LSB_NA;
+ chip->internal_offset_nA = (s64)iadc_offset_internal * IADC_LSB_NA;
+ chip->offset_nV = (s64)vadc_offset * VADC_LSB_NA;
+ chip->external_i_gain_mega
+ = 1000000000 + (s64)iadc_gain_external * GAIN_LSB_FACTOR;
+ chip->external_i_gain_mega
+ = div_s64(chip->external_i_gain_mega, 1000);
+ chip->internal_i_gain_mega
+ = 1000000000 + (s64)iadc_gain_internal * GAIN_LSB_FACTOR;
+ chip->internal_i_gain_mega
+ = div_s64(chip->internal_i_gain_mega, 1000);
+ chip->v_gain_mega = 1000000000 + (s64)vadc_gain * GAIN_LSB_FACTOR;
+ chip->v_gain_mega = div_s64(chip->v_gain_mega, 1000);
+
+ if (chip->wa_flags & QNOVO_ERASE_OFFSET_WA_BIT) {
+ rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_0, buf, 2);
+ if (rc < 0) {
+ pr_err("Couldn't erase offset rc = %d\n", rc);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+static int qnovo_register_notifier(struct qnovo *chip)
+{
+ int rc;
+
+ chip->nb.notifier_call = qnovo_notifier_call;
+ rc = power_supply_reg_notifier(&chip->nb);
+ if (rc < 0) {
+ pr_err("Couldn't register psy notifier rc = %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int qnovo_determine_initial_status(struct qnovo *chip)
+{
+ status_change_work(&chip->status_change_work);
+ return 0;
+}
+
+static int qnovo_request_interrupts(struct qnovo *chip)
+{
+ int rc = 0;
+ int irq_ptrain_done = of_irq_get_byname(chip->dev->of_node,
+ "ptrain-done");
+
+ rc = devm_request_threaded_irq(chip->dev, irq_ptrain_done, NULL,
+ handle_ptrain_done,
+ IRQF_ONESHOT, "ptrain-done", chip);
+ if (rc < 0) {
+ pr_err("Couldn't request irq %d rc = %d\n",
+ irq_ptrain_done, rc);
+ return rc;
+ }
+ return rc;
+}
+
+static int qnovo_probe(struct platform_device *pdev)
+{
+ struct qnovo *chip;
+ int rc = 0;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->fv_uV_request = -EINVAL;
+ chip->fcc_uA_request = -EINVAL;
+ chip->dev = &pdev->dev;
+ mutex_init(&chip->write_lock);
+
+ chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+ if (!chip->regmap) {
+ pr_err("parent regmap is missing\n");
+ return -EINVAL;
+ }
+
+ rc = qnovo_parse_dt(chip);
+ if (rc < 0) {
+ pr_err("Couldn't parse device tree rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qnovo_check_chg_version(chip);
+ if (rc < 0) {
+ if (rc != -EPROBE_DEFER)
+ pr_err("Couldn't check version rc=%d\n", rc);
+ return rc;
+ }
+
+ /* set driver data before resources request it */
+ platform_set_drvdata(pdev, chip);
+
+ chip->disable_votable = create_votable("QNOVO_DISABLE", VOTE_SET_ANY,
+ qnovo_disable_cb, chip);
+ if (IS_ERR(chip->disable_votable)) {
+ rc = PTR_ERR(chip->disable_votable);
+ goto cleanup;
+ }
+
+ INIT_WORK(&chip->status_change_work, status_change_work);
+
+ rc = qnovo_hw_init(chip);
+ if (rc < 0) {
+ pr_err("Couldn't initialize hardware rc=%d\n", rc);
+ goto destroy_votable;
+ }
+
+ rc = qnovo_register_notifier(chip);
+ if (rc < 0) {
+ pr_err("Couldn't register psy notifier rc = %d\n", rc);
+ goto unreg_notifier;
+ }
+
+ rc = qnovo_determine_initial_status(chip);
+ if (rc < 0) {
+ pr_err("Couldn't determine initial status rc=%d\n", rc);
+ goto unreg_notifier;
+ }
+
+ rc = qnovo_request_interrupts(chip);
+ if (rc < 0) {
+ pr_err("Couldn't request interrupts rc=%d\n", rc);
+ goto unreg_notifier;
+ }
+ chip->qnovo_class.name = "qnovo",
+ chip->qnovo_class.owner = THIS_MODULE,
+ chip->qnovo_class.class_attrs = qnovo_attributes;
+
+ rc = class_register(&chip->qnovo_class);
+ if (rc < 0) {
+ pr_err("couldn't register qnovo sysfs class rc = %d\n", rc);
+ goto unreg_notifier;
+ }
+
+ return rc;
+
+unreg_notifier:
+ power_supply_unreg_notifier(&chip->nb);
+destroy_votable:
+ destroy_votable(chip->disable_votable);
+cleanup:
+ platform_set_drvdata(pdev, NULL);
+ return rc;
+}
+
+static int qnovo_remove(struct platform_device *pdev)
+{
+ struct qnovo *chip = platform_get_drvdata(pdev);
+
+ class_unregister(&chip->qnovo_class);
+ power_supply_unreg_notifier(&chip->nb);
+ destroy_votable(chip->disable_votable);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static const struct of_device_id match_table[] = {
+ { .compatible = "qcom,qpnp-qnovo", },
+ { },
+};
+
+static struct platform_driver qnovo_driver = {
+ .driver = {
+ .name = "qcom,qnovo-driver",
+ .owner = THIS_MODULE,
+ .of_match_table = match_table,
+ },
+ .probe = qnovo_probe,
+ .remove = qnovo_remove,
+};
+module_platform_driver(qnovo_driver);
+
+MODULE_DESCRIPTION("QPNP Qnovo Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c
index c22d70a6669e..bbd56caaf0a0 100644
--- a/drivers/power/qcom-charger/qpnp-smb2.c
+++ b/drivers/power/qcom-charger/qpnp-smb2.c
@@ -555,6 +555,13 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
+ rc = smblib_masked_write(chg, QNOVO_PT_ENABLE_CMD_REG,
+ QNOVO_PT_ENABLE_CMD_BIT, QNOVO_PT_ENABLE_CMD_BIT);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't enable qnovo rc=%d\n", rc);
+ return rc;
+ }
+
return rc;
}
diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c
index aea697417bda..c53659c07fc2 100644
--- a/drivers/power/qcom-charger/smb-lib.c
+++ b/drivers/power/qcom-charger/smb-lib.c
@@ -86,6 +86,41 @@ unlock:
* REGISTER GETTERS *
********************/
+int smblib_get_charge_param(struct smb_charger *chg,
+ struct smb_chg_param *param, int *val_u)
+{
+ int rc = 0;
+ u8 val_raw;
+
+ rc = smblib_read(chg, param->reg, &val_raw);
+ if (rc < 0) {
+ dev_err(chg->dev, "%s: Couldn't read from 0x%04x rc=%d\n",
+ param->name, param->reg, rc);
+ return rc;
+ }
+
+ *val_u = val_raw * param->step_u + param->min_u;
+ smblib_dbg(chg, PR_REGISTER, "%s = %d (0x%02x)\n",
+ param->name, *val_u, val_raw);
+
+ return rc;
+}
+
+int smblib_get_usb_suspend(struct smb_charger *chg, int *suspend)
+{
+ int rc = 0;
+ u8 temp;
+
+ rc = smblib_read(chg, USBIN_CMD_IL_REG, &temp);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't read USBIN_CMD_IL rc=%d\n", rc);
+ return rc;
+ }
+ *suspend = temp & USBIN_SUSPEND_BIT;
+
+ return rc;
+}
+
struct apsd_result {
const char * const name;
const u8 bit;
@@ -156,8 +191,8 @@ int smblib_enable_charging(struct smb_charger *chg, bool enable)
return rc;
}
-static int smblib_set_charge_param(struct smb_charger *chg,
- struct smb_chg_param *param, int val_u)
+int smblib_set_charge_param(struct smb_charger *chg,
+ struct smb_chg_param *param, int val_u)
{
int rc = 0;
u8 val_raw;
@@ -182,7 +217,7 @@ static int smblib_set_charge_param(struct smb_charger *chg,
return rc;
}
-static int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend)
+int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend)
{
int rc = 0;
@@ -195,7 +230,7 @@ static int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend)
return rc;
}
-static int smblib_set_dc_suspend(struct smb_charger *chg, bool suspend)
+int smblib_set_dc_suspend(struct smb_charger *chg, bool suspend)
{
int rc = 0;
@@ -1320,17 +1355,10 @@ static void smblib_hvdcp_detect_work(struct work_struct *work)
}
}
-/********
- * INIT *
- * ******/
-
-int smblib_init(struct smb_charger *chg)
+int smblib_create_votables(struct smb_charger *chg)
{
int rc = 0;
- mutex_init(&chg->write_lock);
- INIT_DELAYED_WORK(&chg->hvdcp_detect_work, smblib_hvdcp_detect_work);
-
chg->usb_suspend_votable = create_votable("INPUT_SUSPEND", VOTE_SET_ANY,
smblib_usb_suspend_vote_callback,
chg);
@@ -1386,7 +1414,32 @@ int smblib_init(struct smb_charger *chg)
return rc;
}
- return 0;
+ return rc;
+}
+
+int smblib_init(struct smb_charger *chg)
+{
+ int rc = 0;
+
+ mutex_init(&chg->write_lock);
+ INIT_DELAYED_WORK(&chg->hvdcp_detect_work, smblib_hvdcp_detect_work);
+
+ switch (chg->mode) {
+ case PARALLEL_MASTER:
+ rc = smblib_create_votables(chg);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't create votables rc=%d\n",
+ rc);
+ }
+ break;
+ case PARALLEL_SLAVE:
+ break;
+ default:
+ dev_err(chg->dev, "Unsupported mode %d\n", chg->mode);
+ return -EINVAL;
+ }
+
+ return rc;
}
int smblib_deinit(struct smb_charger *chg)
diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h
index af3d29cc2d20..ac550eb09a0b 100644
--- a/drivers/power/qcom-charger/smb-lib.h
+++ b/drivers/power/qcom-charger/smb-lib.h
@@ -27,6 +27,12 @@ enum print_reason {
#define USER_VOTER "USER_VOTER"
#define PD_VOTER "PD_VOTER"
+enum smb_mode {
+ PARALLEL_MASTER = 0,
+ PARALLEL_SLAVE,
+ NUM_MODES,
+};
+
struct smb_regulator {
struct regulator_dev *rdev;
struct regulator_desc rdesc;
@@ -57,6 +63,7 @@ struct smb_charger {
struct regmap *regmap;
struct smb_params param;
int *debug_mask;
+ enum smb_mode mode;
/* locks */
struct mutex write_lock;
@@ -96,7 +103,15 @@ int smblib_read(struct smb_charger *chg, u16 addr, u8 *val);
int smblib_masked_write(struct smb_charger *chg, u16 addr, u8 mask, u8 val);
int smblib_write(struct smb_charger *chg, u16 addr, u8 val);
+int smblib_get_charge_param(struct smb_charger *chg,
+ struct smb_chg_param *param, int *val_u);
+int smblib_get_usb_suspend(struct smb_charger *chg, int *suspend);
+
int smblib_enable_charging(struct smb_charger *chg, bool enable);
+int smblib_set_charge_param(struct smb_charger *chg,
+ struct smb_chg_param *param, int val_u);
+int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend);
+int smblib_set_dc_suspend(struct smb_charger *chg, bool suspend);
int smblib_vbus_regulator_enable(struct regulator_dev *rdev);
int smblib_vbus_regulator_disable(struct regulator_dev *rdev);
diff --git a/drivers/power/qcom/msm-core.c b/drivers/power/qcom/msm-core.c
index daa842498d96..49ed2eb3e40f 100644
--- a/drivers/power/qcom/msm-core.c
+++ b/drivers/power/qcom/msm-core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -377,6 +377,7 @@ static int update_userspace_power(struct sched_params __user *argp)
struct cpu_activity_info *node;
struct cpu_static_info *sp, *clear_sp;
int cpumask, cluster, mpidr;
+ bool pdata_valid[NR_CPUS] = {0};
get_user(cpumask, &argp->cpumask);
get_user(cluster, &argp->cluster);
@@ -448,13 +449,19 @@ static int update_userspace_power(struct sched_params __user *argp)
}
cpu_stats[cpu].ptable = per_cpu(ptable, cpu);
repopulate_stats(cpu);
-
- blocking_notifier_call_chain(
- &msm_core_stats_notifier_list, cpu, NULL);
+ pdata_valid[cpu] = true;
}
}
spin_unlock(&update_lock);
+ for_each_possible_cpu(cpu) {
+ if (!pdata_valid[cpu])
+ continue;
+
+ blocking_notifier_call_chain(
+ &msm_core_stats_notifier_list, cpu, NULL);
+ }
+
activate_power_table = true;
return 0;
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 52268783dd23..36fd682edab8 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -1829,6 +1829,9 @@ static void ufs_qcom_pm_qos_suspend(struct ufs_qcom_host *host)
{
int i;
+ if (!host->pm_qos.groups)
+ return;
+
for (i = 0; i < host->pm_qos.num_groups; i++)
flush_work(&host->pm_qos.groups[i].unvote_work);
}
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index d29fd5f3adcd..f4b63ec4b325 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -105,6 +105,7 @@ enum icnss_driver_event_type {
ICNSS_DRIVER_EVENT_FW_READY_IND,
ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
+ ICNSS_DRIVER_EVENT_MAX,
};
struct icnss_driver_event {
@@ -116,7 +117,7 @@ struct icnss_driver_event {
void *data;
};
-enum cnss_driver_state {
+enum icnss_driver_state {
ICNSS_WLFW_QMI_CONNECTED,
ICNSS_POWER_ON,
ICNSS_FW_READY,
@@ -137,6 +138,44 @@ struct icnss_vreg_info {
bool state;
};
+struct icnss_stats {
+ struct {
+ uint32_t posted;
+ uint32_t processed;
+ } events[ICNSS_DRIVER_EVENT_MAX];
+
+ struct {
+ uint32_t request;
+ uint32_t free;
+ uint32_t enable;
+ uint32_t disable;
+ } ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
+
+ uint32_t ind_register_req;
+ uint32_t ind_register_resp;
+ uint32_t ind_register_err;
+ uint32_t msa_info_req;
+ uint32_t msa_info_resp;
+ uint32_t msa_info_err;
+ uint32_t msa_ready_req;
+ uint32_t msa_ready_resp;
+ uint32_t msa_ready_err;
+ uint32_t msa_ready_ind;
+ uint32_t cap_req;
+ uint32_t cap_resp;
+ uint32_t cap_err;
+ uint32_t pin_connect_result;
+ uint32_t cfg_req;
+ uint32_t cfg_resp;
+ uint32_t cfg_req_err;
+ uint32_t mode_req;
+ uint32_t mode_resp;
+ uint32_t mode_req_err;
+ uint32_t ini_req;
+ uint32_t ini_resp;
+ uint32_t ini_req_err;
+};
+
static struct icnss_data {
struct platform_device *pdev;
struct icnss_driver_ops *ops;
@@ -173,6 +212,7 @@ static struct icnss_data {
bool skip_qmi;
struct dentry *root_dentry;
spinlock_t on_off_lock;
+ struct icnss_stats stats;
} *penv;
static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
@@ -188,6 +228,8 @@ static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
return "REGISTER_DRIVER";
case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
return "UNREGISTER_DRIVER";
+ case ICNSS_DRIVER_EVENT_MAX:
+ return "EVENT_MAX";
}
return "UNKNOWN";
@@ -196,7 +238,7 @@ static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
static int icnss_driver_event_post(enum icnss_driver_event_type type,
bool sync, void *data)
{
- struct icnss_driver_event *event = NULL;
+ struct icnss_driver_event *event;
unsigned long flags;
int gfp = GFP_KERNEL;
int ret = 0;
@@ -205,6 +247,11 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type,
icnss_driver_event_to_str(type), type,
sync ? "-sync" : "", penv->state);
+ if (type >= ICNSS_DRIVER_EVENT_MAX) {
+ icnss_pr_err("Invalid Event type: %d, can't post", type);
+ return -EINVAL;
+ }
+
if (in_interrupt() || irqs_disabled())
gfp = GFP_ATOMIC;
@@ -221,6 +268,7 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type,
list_add_tail(&event->list, &penv->event_list);
spin_unlock_irqrestore(&penv->event_lock, flags);
+ penv->stats.events[type].posted++;
queue_work(penv->event_wq, &penv->event_work);
if (sync) {
ret = wait_for_completion_interruptible(&event->complete);
@@ -265,6 +313,8 @@ static int icnss_qmi_pin_connect_result_ind(void *msg, unsigned int msg_len)
icnss_pr_dbg("Pin connect Result: pwr_pin: 0x%x phy_io_pin: 0x%x rf_io_pin: 0x%x\n",
ind_msg.pwr_pin_result, ind_msg.phy_io_pin_result,
ind_msg.rf_pin_result);
+
+ penv->stats.pin_connect_result++;
out:
return ret;
}
@@ -485,7 +535,7 @@ static int wlfw_msa_mem_info_send_sync_msg(void)
goto out;
}
- icnss_pr_dbg("Sending MSA info, state: 0x%lx\n", penv->state);
+ icnss_pr_dbg("Sending MSA mem info, state: 0x%lx\n", penv->state);
memset(&req, 0, sizeof(req));
memset(&resp, 0, sizeof(resp));
@@ -501,6 +551,8 @@ static int wlfw_msa_mem_info_send_sync_msg(void)
resp_desc.msg_id = QMI_WLFW_MSA_INFO_RESP_V01;
resp_desc.ei_array = wlfw_msa_info_resp_msg_v01_ei;
+ penv->stats.msa_info_req++;
+
ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
&resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
if (ret < 0) {
@@ -512,6 +564,7 @@ static int wlfw_msa_mem_info_send_sync_msg(void)
icnss_pr_err("QMI request failed %d %d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
+ penv->stats.msa_info_err++;
goto out;
}
@@ -522,9 +575,11 @@ static int wlfw_msa_mem_info_send_sync_msg(void)
icnss_pr_err("Invalid memory region length received%d\n",
resp.mem_region_info_len);
ret = -EINVAL;
+ penv->stats.msa_info_err++;
goto out;
}
+ penv->stats.msa_info_resp++;
for (i = 0; i < resp.mem_region_info_len; i++) {
penv->icnss_mem_region[i].reg_addr =
resp.mem_region_info[i].region_addr;
@@ -554,7 +609,8 @@ static int wlfw_msa_ready_send_sync_msg(void)
goto out;
}
- icnss_pr_dbg("Sending MSA sync message, state: 0x%lx\n", penv->state);
+ icnss_pr_dbg("Sending MSA ready request message, state: 0x%lx\n",
+ penv->state);
memset(&req, 0, sizeof(req));
memset(&resp, 0, sizeof(resp));
@@ -567,9 +623,11 @@ static int wlfw_msa_ready_send_sync_msg(void)
resp_desc.msg_id = QMI_WLFW_MSA_READY_RESP_V01;
resp_desc.ei_array = wlfw_msa_ready_resp_msg_v01_ei;
+ penv->stats.msa_ready_req++;
ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
&resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
if (ret < 0) {
+ penv->stats.msa_ready_err++;
icnss_pr_err("Send req failed %d\n", ret);
goto out;
}
@@ -577,9 +635,11 @@ static int wlfw_msa_ready_send_sync_msg(void)
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
icnss_pr_err("QMI request failed %d %d\n",
resp.resp.result, resp.resp.error);
+ penv->stats.msa_ready_err++;
ret = resp.resp.result;
goto out;
}
+ penv->stats.msa_ready_resp++;
out:
return ret;
}
@@ -596,7 +656,8 @@ static int wlfw_ind_register_send_sync_msg(void)
goto out;
}
- icnss_pr_dbg("Sending Sync message, state: 0x%lx\n", penv->state);
+ icnss_pr_dbg("Sending indication register message, state: 0x%lx\n",
+ penv->state);
memset(&req, 0, sizeof(req));
memset(&resp, 0, sizeof(resp));
@@ -616,11 +677,15 @@ static int wlfw_ind_register_send_sync_msg(void)
resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01;
resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei;
+ penv->stats.ind_register_req++;
+
ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
&resp_desc, &resp, sizeof(resp),
WLFW_TIMEOUT_MS);
+ penv->stats.ind_register_resp++;
if (ret < 0) {
icnss_pr_err("Send req failed %d\n", ret);
+ penv->stats.ind_register_err++;
goto out;
}
@@ -628,6 +693,7 @@ static int wlfw_ind_register_send_sync_msg(void)
icnss_pr_err("QMI request failed %d %d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
+ penv->stats.ind_register_err++;
goto out;
}
out:
@@ -658,11 +724,13 @@ static int wlfw_cap_send_sync_msg(void)
resp_desc.msg_id = QMI_WLFW_CAP_RESP_V01;
resp_desc.ei_array = wlfw_cap_resp_msg_v01_ei;
+ penv->stats.cap_req++;
ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
&resp_desc, &resp, sizeof(resp),
WLFW_TIMEOUT_MS);
if (ret < 0) {
icnss_pr_err("Send req failed %d\n", ret);
+ penv->stats.cap_err++;
goto out;
}
@@ -670,9 +738,11 @@ static int wlfw_cap_send_sync_msg(void)
icnss_pr_err("QMI request failed %d %d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
+ penv->stats.cap_err++;
goto out;
}
+ penv->stats.cap_resp++;
/* store cap locally */
if (resp.chip_info_valid)
penv->chip_info = resp.chip_info;
@@ -685,11 +755,11 @@ static int wlfw_cap_send_sync_msg(void)
if (resp.fw_version_info_valid)
penv->fw_version_info = resp.fw_version_info;
- icnss_pr_dbg("Capability, chip_id: 0x%0x, chip_family: 0x%0x, board_id: 0x%0x, soc_id: 0x%0x, fw_version: 0x%0x, fw_build_timestamp: %s",
- penv->chip_info.chip_id, penv->chip_info.chip_family,
- penv->board_info.board_id, penv->soc_info.soc_id,
- penv->fw_version_info.fw_version,
- penv->fw_version_info.fw_build_timestamp);
+ icnss_pr_dbg("Capability, chip_id: 0x%x, chip_family: 0x%x, board_id: 0x%x, soc_id: 0x%x, fw_version: 0x%x, fw_build_timestamp: %s",
+ penv->chip_info.chip_id, penv->chip_info.chip_family,
+ penv->board_info.board_id, penv->soc_info.soc_id,
+ penv->fw_version_info.fw_version,
+ penv->fw_version_info.fw_build_timestamp);
out:
return ret;
}
@@ -722,11 +792,13 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01;
resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei;
+ penv->stats.mode_req++;
ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
&resp_desc, &resp, sizeof(resp),
WLFW_TIMEOUT_MS);
if (ret < 0) {
icnss_pr_err("Send req failed %d\n", ret);
+ penv->stats.mode_req_err++;
goto out;
}
@@ -734,8 +806,10 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
icnss_pr_err("QMI request failed %d %d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
+ penv->stats.mode_req_err++;
goto out;
}
+ penv->stats.mode_resp++;
out:
return ret;
}
@@ -767,11 +841,13 @@ static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01;
resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei;
+ penv->stats.cfg_req++;
ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
&resp_desc, &resp, sizeof(resp),
WLFW_TIMEOUT_MS);
if (ret < 0) {
icnss_pr_err("Send req failed %d\n", ret);
+ penv->stats.cfg_req_err++;
goto out;
}
@@ -779,8 +855,10 @@ static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
icnss_pr_err("QMI request failed %d %d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
+ penv->stats.cfg_req_err++;
goto out;
}
+ penv->stats.cfg_resp++;
out:
return ret;
}
@@ -814,10 +892,13 @@ static int wlfw_ini_send_sync_msg(bool enable_fw_log)
resp_desc.msg_id = QMI_WLFW_INI_RESP_V01;
resp_desc.ei_array = wlfw_ini_resp_msg_v01_ei;
+ penv->stats.ini_req++;
+
ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
&resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
if (ret < 0) {
icnss_pr_err("send req failed %d\n", ret);
+ penv->stats.ini_req_err++;
goto out;
}
@@ -825,8 +906,10 @@ static int wlfw_ini_send_sync_msg(bool enable_fw_log)
icnss_pr_err("QMI request failed %d %d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
+ penv->stats.ini_req_err++;
goto out;
}
+ penv->stats.ini_resp++;
out:
return ret;
}
@@ -884,6 +967,7 @@ static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
case QMI_WLFW_MSA_READY_IND_V01:
icnss_pr_dbg("Received MSA Ready Indication msg_id 0x%x\n",
msg_id);
+ penv->stats.msa_ready_ind++;
break;
case QMI_WLFW_PIN_CONNECT_RESULT_IND_V01:
icnss_pr_dbg("Received Pin Connect Test Result msg_id 0x%x\n",
@@ -1131,13 +1215,18 @@ static void icnss_driver_event_work(struct work_struct *work)
break;
default:
icnss_pr_err("Invalid Event type: %d", event->type);
- break;
+ kfree(event);
+ continue;
}
+
+ penv->stats.events[event->type].processed++;
+
if (event->sync) {
event->ret = ret;
complete(&event->complete);
} else
kfree(event);
+
spin_lock_irqsave(&penv->event_lock, flags);
}
spin_unlock_irqrestore(&penv->event_lock, flags);
@@ -1245,7 +1334,7 @@ int icnss_ce_request_irq(unsigned int ce_id,
icnss_pr_dbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
- icnss_pr_err("Invalid CE ID %d\n", ce_id);
+ icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id);
ret = -EINVAL;
goto out;
}
@@ -1267,7 +1356,10 @@ int icnss_ce_request_irq(unsigned int ce_id,
}
irq_entry->irq = irq;
irq_entry->handler = handler;
+
icnss_pr_dbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id);
+
+ penv->stats.ce_irqs[ce_id].request++;
out:
return ret;
}
@@ -1286,6 +1378,12 @@ int icnss_ce_free_irq(unsigned int ce_id, void *ctx)
icnss_pr_dbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
+ if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
+ icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id);
+ ret = -EINVAL;
+ goto out;
+ }
+
irq = penv->ce_irqs[ce_id];
irq_entry = &penv->ce_irq_list[ce_id];
if (!irq_entry->handler || !irq_entry->irq) {
@@ -1296,6 +1394,8 @@ int icnss_ce_free_irq(unsigned int ce_id, void *ctx)
free_irq(irq, ctx);
irq_entry->irq = 0;
irq_entry->handler = NULL;
+
+ penv->stats.ce_irqs[ce_id].free++;
out:
return ret;
}
@@ -1313,6 +1413,13 @@ void icnss_enable_irq(unsigned int ce_id)
icnss_pr_dbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
penv->state);
+ if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
+ icnss_pr_err("Invalid CE ID to enable IRQ, ce_id: %d\n", ce_id);
+ return;
+ }
+
+ penv->stats.ce_irqs[ce_id].enable++;
+
irq = penv->ce_irqs[ce_id];
enable_irq(irq);
}
@@ -1330,8 +1437,16 @@ void icnss_disable_irq(unsigned int ce_id)
icnss_pr_dbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
penv->state);
+ if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
+ icnss_pr_err("Invalid CE ID to disable IRQ, ce_id: %d\n",
+ ce_id);
+ return;
+ }
+
irq = penv->ce_irqs[ce_id];
disable_irq(irq);
+
+ penv->stats.ce_irqs[ce_id].disable++;
}
EXPORT_SYMBOL(icnss_disable_irq);
@@ -1806,6 +1921,9 @@ static ssize_t icnss_test_mode_write(struct file *fp, const char __user *buf,
if (ret)
return ret;
+ if (ret == 0)
+ memset(&priv->stats, 0, sizeof(priv->stats));
+
return count;
}
@@ -1823,6 +1941,172 @@ static const struct file_operations icnss_test_mode_fops = {
.llseek = seq_lseek,
};
+static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *off)
+{
+ struct icnss_data *priv =
+ ((struct seq_file *)fp->private_data)->private;
+ int ret;
+ u32 val;
+
+ ret = kstrtou32_from_user(buf, count, 0, &val);
+ if (ret)
+ return ret;
+
+ if (ret == 0)
+ memset(&priv->stats, 0, sizeof(priv->stats));
+
+ return count;
+}
+
+static int icnss_stats_show_state(struct seq_file *s, struct icnss_data *priv)
+{
+ int i;
+ int skip = 0;
+ unsigned long state;
+
+ seq_printf(s, "\nState: 0x%lx(", priv->state);
+ for (i = 0, state = priv->state; state != 0; state >>= 1, i++) {
+
+ if (!(state & 0x1))
+ continue;
+
+ if (skip++)
+ seq_puts(s, " | ");
+
+ switch (i) {
+ case ICNSS_WLFW_QMI_CONNECTED:
+ seq_puts(s, "QMI CONN");
+ continue;
+ case ICNSS_POWER_ON:
+ seq_puts(s, "POWER ON");
+ continue;
+ case ICNSS_FW_READY:
+ seq_puts(s, "FW READY");
+ continue;
+ case ICNSS_DRIVER_PROBED:
+ seq_puts(s, "DRIVER PROBED");
+ continue;
+ case ICNSS_FW_TEST_MODE:
+ seq_puts(s, "FW TEST MODE");
+ continue;
+ }
+
+ seq_printf(s, "UNKNOWN-%d", i);
+ }
+ seq_puts(s, ")\n");
+
+ return 0;
+}
+
+static int icnss_stats_show_capability(struct seq_file *s,
+ struct icnss_data *priv)
+{
+ if (test_bit(ICNSS_FW_READY, &priv->state)) {
+ seq_puts(s, "\n<---------------- FW Capability ----------------->\n");
+ seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id);
+ seq_printf(s, "Chip family: 0x%x\n",
+ priv->chip_info.chip_family);
+ seq_printf(s, "Board ID: 0x%x\n", priv->board_info.board_id);
+ seq_printf(s, "SOC Info: 0x%x\n", priv->soc_info.soc_id);
+ seq_printf(s, "Firmware Version: 0x%x\n",
+ priv->fw_version_info.fw_version);
+ seq_printf(s, "Firmware Build Timestamp: %s\n",
+ priv->fw_version_info.fw_build_timestamp);
+ }
+
+ return 0;
+}
+
+static int icnss_stats_show_events(struct seq_file *s, struct icnss_data *priv)
+{
+ int i;
+
+ seq_puts(s, "\n<----------------- Events stats ------------------->\n");
+ seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed");
+ for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++)
+ seq_printf(s, "%24s %16u %16u\n",
+ icnss_driver_event_to_str(i),
+ priv->stats.events[i].posted,
+ priv->stats.events[i].processed);
+
+ return 0;
+}
+
+static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_data *priv)
+{
+ int i;
+
+ seq_puts(s, "\n<------------------ IRQ stats ------------------->\n");
+ seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request",
+ "Free", "Enable", "Disable");
+ for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++)
+ seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i,
+ priv->ce_irqs[i], priv->stats.ce_irqs[i].request,
+ priv->stats.ce_irqs[i].free,
+ priv->stats.ce_irqs[i].enable,
+ priv->stats.ce_irqs[i].disable);
+
+ return 0;
+}
+
+static int icnss_stats_show(struct seq_file *s, void *data)
+{
+#define ICNSS_STATS_DUMP(_s, _priv, _x) \
+ seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x)
+
+ struct icnss_data *priv = s->private;
+
+ ICNSS_STATS_DUMP(s, priv, ind_register_req);
+ ICNSS_STATS_DUMP(s, priv, ind_register_resp);
+ ICNSS_STATS_DUMP(s, priv, ind_register_err);
+ ICNSS_STATS_DUMP(s, priv, msa_info_req);
+ ICNSS_STATS_DUMP(s, priv, msa_info_resp);
+ ICNSS_STATS_DUMP(s, priv, msa_info_err);
+ ICNSS_STATS_DUMP(s, priv, msa_ready_req);
+ ICNSS_STATS_DUMP(s, priv, msa_ready_resp);
+ ICNSS_STATS_DUMP(s, priv, msa_ready_err);
+ ICNSS_STATS_DUMP(s, priv, msa_ready_ind);
+ ICNSS_STATS_DUMP(s, priv, cap_req);
+ ICNSS_STATS_DUMP(s, priv, cap_resp);
+ ICNSS_STATS_DUMP(s, priv, cap_err);
+ ICNSS_STATS_DUMP(s, priv, pin_connect_result);
+ ICNSS_STATS_DUMP(s, priv, cfg_req);
+ ICNSS_STATS_DUMP(s, priv, cfg_resp);
+ ICNSS_STATS_DUMP(s, priv, cfg_req_err);
+ ICNSS_STATS_DUMP(s, priv, mode_req);
+ ICNSS_STATS_DUMP(s, priv, mode_resp);
+ ICNSS_STATS_DUMP(s, priv, mode_req_err);
+ ICNSS_STATS_DUMP(s, priv, ini_req);
+ ICNSS_STATS_DUMP(s, priv, ini_resp);
+ ICNSS_STATS_DUMP(s, priv, ini_req_err);
+
+ icnss_stats_show_irqs(s, priv);
+
+ icnss_stats_show_capability(s, priv);
+
+ icnss_stats_show_events(s, priv);
+
+ icnss_stats_show_state(s, priv);
+
+ return 0;
+#undef ICNSS_STATS_DUMP
+}
+
+static int icnss_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, icnss_stats_show, inode->i_private);
+}
+
+static const struct file_operations icnss_stats_fops = {
+ .read = seq_read,
+ .write = icnss_stats_write,
+ .release = single_release,
+ .open = icnss_stats_open,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+};
+
static int icnss_debugfs_create(struct icnss_data *priv)
{
int ret = 0;
@@ -1838,8 +2122,11 @@ static int icnss_debugfs_create(struct icnss_data *priv)
priv->root_dentry = root_dentry;
- debugfs_create_file("test_mode", S_IRUSR | S_IWUSR,
- root_dentry, priv, &icnss_test_mode_fops);
+ debugfs_create_file("test_mode", 0644, root_dentry, priv,
+ &icnss_test_mode_fops);
+
+ debugfs_create_file("stats", 0644, root_dentry, priv,
+ &icnss_stats_fops);
out:
return ret;
diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c
index 584936c3910d..d2204b9b0f6f 100644
--- a/drivers/soc/qcom/spcom.c
+++ b/drivers/soc/qcom/spcom.c
@@ -96,7 +96,13 @@
/* SPCOM driver name */
#define DEVICE_NAME "spcom"
-#define SPCOM_MAX_CHANNELS 20
+#define SPCOM_MAX_CHANNELS 0x20
+
+/* maximum ION buffers should be >= SPCOM_MAX_CHANNELS */
+#define SPCOM_MAX_ION_BUF_PER_CH (SPCOM_MAX_CHANNELS + 4)
+
+/* maximum ION buffer per send request/response command */
+#define SPCOM_MAX_ION_BUF_PER_CMD SPCOM_MAX_ION_BUF
/* Maximum command size */
#define SPCOM_MAX_COMMAND_SIZE (PAGE_SIZE)
@@ -215,8 +221,8 @@ struct spcom_channel {
const void *glink_rx_buf;
/* ION lock/unlock support */
- int ion_fd_table[SPCOM_MAX_ION_BUF];
- struct ion_handle *ion_handle_table[SPCOM_MAX_ION_BUF];
+ int ion_fd_table[SPCOM_MAX_ION_BUF_PER_CH];
+ struct ion_handle *ion_handle_table[SPCOM_MAX_ION_BUF_PER_CH];
};
/**
@@ -607,13 +613,10 @@ static struct spcom_channel *spcom_find_channel_by_name(const char *name)
struct spcom_channel *ch = &spcom_dev->channels[i];
if (strcmp(ch->name, name) == 0) {
- pr_debug("channel [%s] found.\n", name);
return ch;
}
}
- pr_err("failed to find channel [%s].\n", name);
-
return NULL;
}
@@ -1441,7 +1444,7 @@ static int spcom_handle_send_modified_command(struct spcom_channel *ch,
void *tx_buf;
int tx_buf_size;
uint32_t timeout_msec;
- struct spcom_ion_info ion_info[SPCOM_MAX_ION_BUF];
+ struct spcom_ion_info ion_info[SPCOM_MAX_ION_BUF_PER_CMD];
int i;
pr_debug("send req/resp ch [%s] size [%d] .\n", ch->name, size);
@@ -1477,7 +1480,7 @@ static int spcom_handle_send_modified_command(struct spcom_channel *ch,
/* user buf */
memcpy(hdr->buf, buf, buf_size);
- for (i = 0 ; i < SPCOM_MAX_ION_BUF ; i++) {
+ for (i = 0 ; i < ARRAY_SIZE(ion_info) ; i++) {
if (ion_info[i].fd >= 0) {
ret = modify_ion_addr(hdr->buf, buf_size, ion_info[i]);
if (ret < 0) {
@@ -1532,7 +1535,7 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch,
pr_debug("ion handle ok.\n");
/* Check if this ION buffer is already locked */
- for (i = 0 ; i < SPCOM_MAX_ION_BUF ; i++) {
+ for (i = 0 ; i < ARRAY_SIZE(ch->ion_handle_table) ; i++) {
if (ch->ion_handle_table[i] == ion_handle) {
pr_debug("fd [%d] ion buf is already locked.\n", fd);
/* decrement back the ref count */
@@ -1542,7 +1545,7 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch,
}
/* Store the ION handle */
- for (i = 0 ; i < SPCOM_MAX_ION_BUF ; i++) {
+ for (i = 0 ; i < ARRAY_SIZE(ch->ion_handle_table) ; i++) {
if (ch->ion_handle_table[i] == NULL) {
ch->ion_handle_table[i] = ion_handle;
ch->ion_fd_table[i] = fd;
@@ -1576,7 +1579,7 @@ static int spcom_handle_unlock_ion_buf_command(struct spcom_channel *ch,
if (fd == (int) SPCOM_ION_FD_UNLOCK_ALL) {
/* unlock all ION buf */
- for (i = 0 ; i < SPCOM_MAX_ION_BUF ; i++) {
+ for (i = 0 ; i < ARRAY_SIZE(ch->ion_handle_table) ; i++) {
if (ch->ion_handle_table[i] != NULL) {
ion_free(ion_client, ch->ion_handle_table[i]);
ch->ion_handle_table[i] = NULL;
@@ -1586,7 +1589,7 @@ static int spcom_handle_unlock_ion_buf_command(struct spcom_channel *ch,
}
} else {
/* unlock specific ION buf */
- for (i = 0 ; i < SPCOM_MAX_ION_BUF ; i++) {
+ for (i = 0 ; i < ARRAY_SIZE(ch->ion_handle_table) ; i++) {
if (ch->ion_fd_table[i] == fd) {
ion_free(ion_client, ch->ion_handle_table[i]);
ch->ion_handle_table[i] = NULL;
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
index 53a31e79536b..e9237202e79f 100755
--- a/drivers/staging/android/ion/ion.c
+++ b/drivers/staging/android/ion/ion.c
@@ -1118,6 +1118,8 @@ void ion_pages_sync_for_device(struct device *dev, struct page *page,
{
struct scatterlist sg;
+ WARN_ONCE(!dev, "A device is required for dma_sync\n");
+
sg_init_table(&sg, 1);
sg_set_page(&sg, page, size, 0);
/*
diff --git a/drivers/staging/android/ion/ion_carveout_heap.c b/drivers/staging/android/ion/ion_carveout_heap.c
index e702ce6461fc..eab137a85703 100644
--- a/drivers/staging/android/ion/ion_carveout_heap.c
+++ b/drivers/staging/android/ion/ion_carveout_heap.c
@@ -111,13 +111,15 @@ static void ion_carveout_heap_free(struct ion_buffer *buffer)
struct ion_heap *heap = buffer->heap;
struct sg_table *table = buffer->priv_virt;
struct page *page = sg_page(table->sgl);
+ struct device *dev = heap->priv;
+
ion_phys_addr_t paddr = PFN_PHYS(page_to_pfn(page));
ion_heap_buffer_zero(buffer);
if (ion_buffer_cached(buffer))
- dma_sync_sg_for_device(NULL, table->sgl, table->nents,
- DMA_BIDIRECTIONAL);
+ dma_sync_sg_for_device(dev, table->sgl, table->nents,
+ DMA_BIDIRECTIONAL);
ion_carveout_free(heap, paddr, buffer->size);
sg_free_table(table);
@@ -153,11 +155,12 @@ struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
struct page *page;
size_t size;
+ struct device *dev = heap_data->priv;
page = pfn_to_page(PFN_DOWN(heap_data->base));
size = heap_data->size;
- ion_pages_sync_for_device(NULL, page, size, DMA_BIDIRECTIONAL);
+ ion_pages_sync_for_device(dev, page, size, DMA_BIDIRECTIONAL);
ret = ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL));
if (ret)
diff --git a/drivers/staging/android/ion/ion_chunk_heap.c b/drivers/staging/android/ion/ion_chunk_heap.c
index 6b3e18aa1c64..1ad68f47bc01 100644
--- a/drivers/staging/android/ion/ion_chunk_heap.c
+++ b/drivers/staging/android/ion/ion_chunk_heap.c
@@ -99,14 +99,15 @@ static void ion_chunk_heap_free(struct ion_buffer *buffer)
struct scatterlist *sg;
int i;
unsigned long allocated_size;
+ struct device *dev = heap->priv;
allocated_size = ALIGN(buffer->size, chunk_heap->chunk_size);
ion_heap_buffer_zero(buffer);
if (ion_buffer_cached(buffer))
- dma_sync_sg_for_device(NULL, table->sgl, table->nents,
- DMA_BIDIRECTIONAL);
+ dma_sync_sg_for_device(dev, table->sgl, table->nents,
+ DMA_BIDIRECTIONAL);
for_each_sg(table->sgl, sg, table->nents, i) {
gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)),
@@ -144,11 +145,12 @@ struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data)
int ret;
struct page *page;
size_t size;
+ struct device *dev = heap_data->priv;
page = pfn_to_page(PFN_DOWN(heap_data->base));
size = heap_data->size;
- ion_pages_sync_for_device(NULL, page, size, DMA_BIDIRECTIONAL);
+ ion_pages_sync_for_device(dev, page, size, DMA_BIDIRECTIONAL);
ret = ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL));
if (ret)
diff --git a/drivers/staging/android/ion/ion_page_pool.c b/drivers/staging/android/ion/ion_page_pool.c
index 8dc72085f7a8..d549d6271d89 100644
--- a/drivers/staging/android/ion/ion_page_pool.c
+++ b/drivers/staging/android/ion/ion_page_pool.c
@@ -35,7 +35,8 @@ static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool)
return NULL;
if (pool->gfp_mask & __GFP_ZERO)
- if (msm_ion_heap_high_order_page_zero(page, pool->order))
+ if (msm_ion_heap_high_order_page_zero(pool->dev, page,
+ pool->order))
goto error_free_pages;
ion_page_pool_alloc_set_cache_policy(pool, page);
@@ -222,12 +223,14 @@ int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
return ion_page_pool_total(pool, high);
}
-struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order)
+struct ion_page_pool *ion_page_pool_create(struct device *dev, gfp_t gfp_mask,
+ unsigned int order)
{
struct ion_page_pool *pool = kmalloc(sizeof(struct ion_page_pool),
GFP_KERNEL);
if (!pool)
return NULL;
+ pool->dev = dev;
pool->high_count = 0;
pool->low_count = 0;
pool->nr_unreserved = 0;
diff --git a/drivers/staging/android/ion/ion_priv.h b/drivers/staging/android/ion/ion_priv.h
index 65237205f070..4f383661258a 100644
--- a/drivers/staging/android/ion/ion_priv.h
+++ b/drivers/staging/android/ion/ion_priv.h
@@ -262,9 +262,11 @@ int ion_heap_map_user(struct ion_heap *, struct ion_buffer *,
int ion_heap_buffer_zero(struct ion_buffer *buffer);
int ion_heap_pages_zero(struct page *page, size_t size, pgprot_t pgprot);
-int msm_ion_heap_high_order_page_zero(struct page *page, int order);
+int msm_ion_heap_high_order_page_zero(struct device *dev, struct page *page,
+ int order);
struct ion_heap *get_ion_heap(int heap_id);
-int msm_ion_heap_sg_table_zero(struct sg_table *, size_t size);
+int msm_ion_heap_sg_table_zero(struct device *dev, struct sg_table *,
+ size_t size);
int msm_ion_heap_pages_zero(struct page **pages, int num_pages);
int msm_ion_heap_alloc_pages_mem(struct pages_mem *pages_mem);
void msm_ion_heap_free_pages_mem(struct pages_mem *pages_mem);
@@ -435,12 +437,14 @@ struct ion_page_pool {
struct list_head high_items;
struct list_head low_items;
struct mutex mutex;
+ struct device *dev;
gfp_t gfp_mask;
unsigned int order;
struct plist_node list;
};
-struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order);
+struct ion_page_pool *ion_page_pool_create(struct device *dev, gfp_t gfp_mask,
+ unsigned int order);
void ion_page_pool_destroy(struct ion_page_pool *);
void *ion_page_pool_alloc(struct ion_page_pool *, bool *from_pool);
void *ion_page_pool_alloc_pool_only(struct ion_page_pool *);
diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c
index 487dc37cde27..ff75e1690f59 100644
--- a/drivers/staging/android/ion/ion_system_heap.c
+++ b/drivers/staging/android/ion/ion_system_heap.c
@@ -82,6 +82,7 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap,
struct page *page;
struct ion_page_pool *pool;
int vmid = get_secure_vmid(buffer->flags);
+ struct device *dev = heap->heap.priv;
if (*from_pool) {
if (vmid > 0)
@@ -100,6 +101,8 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap,
if (order)
gfp_mask = high_order_gfp_flags;
page = alloc_pages(gfp_mask, order);
+ ion_pages_sync_for_device(dev, page, PAGE_SIZE << order,
+ DMA_BIDIRECTIONAL);
}
if (!page)
return 0;
@@ -222,6 +225,7 @@ static int ion_system_heap_allocate(struct ion_heap *heap,
struct pages_mem data;
unsigned int sz;
int vmid = get_secure_vmid(buffer->flags);
+ struct device *dev = heap->priv;
if (align > PAGE_SIZE)
return -EINVAL;
@@ -311,7 +315,7 @@ static int ion_system_heap_allocate(struct ion_heap *heap,
}
if (nents_sync) {
- dma_sync_sg_for_device(NULL, table_sync.sgl, table_sync.nents,
+ dma_sync_sg_for_device(dev, table_sync.sgl, table_sync.nents,
DMA_BIDIRECTIONAL);
if (vmid > 0) {
ret = ion_system_secure_heap_assign_sg(&table_sync,
@@ -368,11 +372,12 @@ void ion_system_heap_free(struct ion_buffer *buffer)
LIST_HEAD(pages);
int i;
int vmid = get_secure_vmid(buffer->flags);
+ struct device *dev = heap->priv;
if (!(buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE) &&
!(buffer->flags & ION_FLAG_POOL_FORCE_ALLOC)) {
if (vmid < 0)
- msm_ion_heap_sg_table_zero(table, buffer->size);
+ msm_ion_heap_sg_table_zero(dev, table, buffer->size);
} else if (vmid > 0) {
if (ion_system_secure_heap_unassign_sg(table, vmid))
return;
@@ -627,7 +632,8 @@ static void ion_system_heap_destroy_pools(struct ion_page_pool **pools)
* nothing. If it succeeds you'll eventually need to use
* ion_system_heap_destroy_pools to destroy the pools.
*/
-static int ion_system_heap_create_pools(struct ion_page_pool **pools)
+static int ion_system_heap_create_pools(struct device *dev,
+ struct ion_page_pool **pools)
{
int i;
for (i = 0; i < num_orders; i++) {
@@ -636,7 +642,7 @@ static int ion_system_heap_create_pools(struct ion_page_pool **pools)
if (orders[i])
gfp_flags = high_order_gfp_flags;
- pool = ion_page_pool_create(gfp_flags, orders[i]);
+ pool = ion_page_pool_create(dev, gfp_flags, orders[i]);
if (!pool)
goto err_create_pool;
pools[i] = pool;
@@ -647,11 +653,12 @@ err_create_pool:
return 1;
}
-struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused)
+struct ion_heap *ion_system_heap_create(struct ion_platform_heap *data)
{
struct ion_system_heap *heap;
int i;
int pools_size = sizeof(struct ion_page_pool *) * num_orders;
+ struct device *dev = data->priv;
heap = kzalloc(sizeof(struct ion_system_heap), GFP_KERNEL);
if (!heap)
@@ -673,15 +680,16 @@ struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused)
heap->secure_pools[i] = kzalloc(pools_size, GFP_KERNEL);
if (!heap->secure_pools[i])
goto err_create_secure_pools;
- if (ion_system_heap_create_pools(heap->secure_pools[i]))
+ if (ion_system_heap_create_pools(
+ dev, heap->secure_pools[i]))
goto err_create_secure_pools;
}
}
- if (ion_system_heap_create_pools(heap->uncached_pools))
+ if (ion_system_heap_create_pools(dev, heap->uncached_pools))
goto err_create_uncached_pools;
- if (ion_system_heap_create_pools(heap->cached_pools))
+ if (ion_system_heap_create_pools(dev, heap->cached_pools))
goto err_create_cached_pools;
heap->heap.debug_show = ion_system_heap_debug_show;
@@ -738,6 +746,7 @@ static int ion_system_contig_heap_allocate(struct ion_heap *heap,
struct sg_table *table;
unsigned long i;
int ret;
+ struct device *dev = heap->priv;
if (align > (PAGE_SIZE << order))
return -EINVAL;
@@ -766,7 +775,7 @@ static int ion_system_contig_heap_allocate(struct ion_heap *heap,
buffer->priv_virt = table;
- ion_pages_sync_for_device(NULL, page, len, DMA_BIDIRECTIONAL);
+ ion_pages_sync_for_device(dev, page, len, DMA_BIDIRECTIONAL);
return 0;
diff --git a/drivers/staging/android/ion/msm/msm_ion.c b/drivers/staging/android/ion/msm/msm_ion.c
index 49b5270e5162..46d7aae9b1c4 100644
--- a/drivers/staging/android/ion/msm/msm_ion.c
+++ b/drivers/staging/android/ion/msm/msm_ion.c
@@ -859,7 +859,8 @@ void msm_ion_heap_free_pages_mem(struct pages_mem *pages_mem)
pages_mem->free_fn(pages_mem->pages);
}
-int msm_ion_heap_high_order_page_zero(struct page *page, int order)
+int msm_ion_heap_high_order_page_zero(struct device *dev, struct page *page,
+ int order)
{
int i, ret;
struct pages_mem pages_mem;
@@ -873,13 +874,14 @@ int msm_ion_heap_high_order_page_zero(struct page *page, int order)
pages_mem.pages[i] = page + i;
ret = msm_ion_heap_pages_zero(pages_mem.pages, npages);
- dma_sync_single_for_device(NULL, page_to_phys(page), pages_mem.size,
+ dma_sync_single_for_device(dev, page_to_phys(page), pages_mem.size,
DMA_BIDIRECTIONAL);
msm_ion_heap_free_pages_mem(&pages_mem);
return ret;
}
-int msm_ion_heap_sg_table_zero(struct sg_table *table, size_t size)
+int msm_ion_heap_sg_table_zero(struct device *dev, struct sg_table *table,
+ size_t size)
{
struct scatterlist *sg;
int i, j, ret = 0, npages = 0;
@@ -901,7 +903,7 @@ int msm_ion_heap_sg_table_zero(struct sg_table *table, size_t size)
}
ret = msm_ion_heap_pages_zero(pages_mem.pages, npages);
- dma_sync_sg_for_device(NULL, table->sgl, table->nents,
+ dma_sync_sg_for_device(dev, table->sgl, table->nents,
DMA_BIDIRECTIONAL);
msm_ion_heap_free_pages_mem(&pages_mem);
return ret;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 4438833ca935..5cfed430b8d4 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -511,6 +511,10 @@ struct mmc_host {
const struct mmc_bus_ops *bus_ops; /* current bus driver */
unsigned int bus_refs; /* reference counter */
+ unsigned int bus_resume_flags;
+#define MMC_BUSRESUME_MANUAL_RESUME (1 << 0)
+#define MMC_BUSRESUME_NEEDS_RESUME (1 << 1)
+
unsigned int sdio_irqs;
struct task_struct *sdio_irq_thread;
bool sdio_irq_pending;
@@ -614,6 +618,20 @@ static inline void *mmc_cmdq_private(struct mmc_host *host)
#define mmc_dev(x) ((x)->parent)
#define mmc_classdev(x) (&(x)->class_dev)
#define mmc_hostname(x) (dev_name(&(x)->class_dev))
+#define mmc_bus_needs_resume(host) ((host)->bus_resume_flags & \
+ MMC_BUSRESUME_NEEDS_RESUME)
+#define mmc_bus_manual_resume(host) ((host)->bus_resume_flags & \
+ MMC_BUSRESUME_MANUAL_RESUME)
+
+static inline void mmc_set_bus_resume_policy(struct mmc_host *host, int manual)
+{
+ if (manual)
+ host->bus_resume_flags |= MMC_BUSRESUME_MANUAL_RESUME;
+ else
+ host->bus_resume_flags &= ~MMC_BUSRESUME_MANUAL_RESUME;
+}
+
+extern int mmc_resume_bus(struct mmc_host *host);
int mmc_power_save_host(struct mmc_host *host);
int mmc_power_restore_host(struct mmc_host *host);
diff --git a/include/linux/msm_pcie.h b/include/linux/msm_pcie.h
index aba7fe20835e..c9c467ff7428 100644
--- a/include/linux/msm_pcie.h
+++ b/include/linux/msm_pcie.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
@@ -59,6 +59,7 @@ struct msm_pcie_register_event {
u32 options;
};
+#ifdef CONFIG_PCI_MSM
/**
* msm_pcie_pm_control - control the power state of a PCIe link.
* @pm_opt: power management operation
@@ -168,4 +169,49 @@ int msm_pcie_debug_info(struct pci_dev *dev, u32 option, u32 base,
*/
int msm_pcie_configure_sid(struct device *dev, u32 *sid,
int *domain);
-#endif
+#else /* !CONFIG_PCI_MSM */
+static inline int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr,
+ void *user, void *data, u32 options)
+{
+ return -ENODEV;
+}
+
+static inline int msm_pcie_register_event(struct msm_pcie_register_event *reg)
+{
+ return -ENODEV;
+}
+
+static inline int msm_pcie_deregister_event(struct msm_pcie_register_event *reg)
+{
+ return -ENODEV;
+}
+
+static inline int msm_pcie_recover_config(struct pci_dev *dev)
+{
+ return -ENODEV;
+}
+
+static inline int msm_pcie_enumerate(u32 rc_idx)
+{
+ return -ENODEV;
+}
+
+static inline int msm_pcie_shadow_control(struct pci_dev *dev, bool enable)
+{
+ return -ENODEV;
+}
+
+static inline int msm_pcie_debug_info(struct pci_dev *dev, u32 option, u32 base,
+ u32 offset, u32 mask, u32 value)
+{
+ return -ENODEV;
+}
+
+static inline int msm_pcie_configure_sid(struct device *dev, u32 *sid,
+ int *domain)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_PCI_MSM */
+
+#endif /* __MSM_PCIE_H */
diff --git a/include/uapi/media/msm_cam_sensor.h b/include/uapi/media/msm_cam_sensor.h
index d83e00876fd5..5d340b9a2523 100644
--- a/include/uapi/media/msm_cam_sensor.h
+++ b/include/uapi/media/msm_cam_sensor.h
@@ -342,6 +342,13 @@ enum msm_actuator_cfg_type_t {
CFG_ACTUATOR_INIT,
};
+struct msm_ois_opcode {
+ uint32_t prog;
+ uint32_t coeff;
+ uint32_t pheripheral;
+ uint32_t memory;
+};
+
enum msm_ois_cfg_type_t {
CFG_OIS_INIT,
CFG_OIS_POWERDOWN,
@@ -350,6 +357,11 @@ enum msm_ois_cfg_type_t {
CFG_OIS_I2C_WRITE_SEQ_TABLE,
};
+enum msm_ois_cfg_download_type_t {
+ CFG_OIS_DOWNLOAD,
+ CFG_OIS_DATA_CONFIG,
+};
+
enum msm_ois_i2c_operation {
MSM_OIS_WRITE = 0,
MSM_OIS_POLL,
@@ -448,6 +460,11 @@ enum af_camera_name {
ACTUATOR_WEB_CAM_2,
};
+struct msm_ois_slave_info {
+ char ois_name[MAX_OIS_NAME_SIZE];
+ uint32_t i2c_addr;
+ struct msm_ois_opcode opcode;
+};
struct msm_ois_cfg_data {
int cfgtype;
union {
@@ -456,6 +473,11 @@ struct msm_ois_cfg_data {
} cfg;
};
+struct msm_ois_cfg_download_data {
+ int cfgtype;
+ struct msm_ois_slave_info slave_info;
+};
+
struct msm_actuator_set_position_t {
uint16_t number_of_steps;
uint32_t hw_params;
@@ -560,5 +582,8 @@ struct sensor_init_cfg_data {
#define VIDIOC_MSM_FLASH_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE + 13, struct msm_flash_cfg_data_t)
+#define VIDIOC_MSM_OIS_CFG_DOWNLOAD \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 14, struct msm_ois_cfg_download_data)
+
#endif
diff --git a/include/uapi/media/msmb_camera.h b/include/uapi/media/msmb_camera.h
index f16479c250e2..092551d0cc16 100644
--- a/include/uapi/media/msmb_camera.h
+++ b/include/uapi/media/msmb_camera.h
@@ -23,6 +23,9 @@
#define MSM_CAM_V4L2_IOCTL_NOTIFY_DEBUG \
_IOW('V', BASE_VIDIOC_PRIVATE + 34, struct msm_v4l2_event_data)
+#define MSM_CAM_V4L2_IOCTL_DAEMON_DISABLED \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 35, struct msm_v4l2_event_data)
+
#define QCAMERA_DEVICE_GROUP_ID 1
#define QCAMERA_VNODE_GROUP_ID 2
#define MSM_CAMERA_NAME "msm_camera"
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 6a7751e99e82..81fbed978da3 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2244,15 +2244,7 @@ static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
int write, void *data)
{
if (write) {
- if (*negp) {
- if (*lvalp > (unsigned long) INT_MAX + 1)
- return -EINVAL;
- *valp = -*lvalp;
- } else {
- if (*lvalp > (unsigned long) INT_MAX)
- return -EINVAL;
- *valp = *lvalp;
- }
+ *valp = *negp ? -*lvalp : *lvalp;
} else {
int val = *valp;
if (val < 0) {
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index 85c48b38e3db..36ab897b8dc0 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -2023,6 +2023,12 @@ static void wcd_mbhc_moisture_config(struct wcd_mbhc *mbhc)
if (mbhc->mbhc_cfg->moist_cfg.m_vref_ctl == V_OFF)
return;
+ /* Donot enable moisture detection if jack type is NC */
+ if (!mbhc->hphl_swh) {
+ pr_debug("%s: disable moisture detection for NC\n", __func__);
+ return;
+ }
+
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MOISTURE_VREF,
mbhc->mbhc_cfg->moist_cfg.m_vref_ctl);
if (mbhc->mbhc_cb->hph_pull_up_control)
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index 8c7ab5a8f358..7ba7ec93b779 100755
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -5429,6 +5429,48 @@ out:
return ret;
}
+static u32 tasha_get_dmic_sample_rate(struct snd_soc_codec *codec,
+ unsigned int dmic, struct wcd9xxx_pdata *pdata)
+{
+ u8 tx_stream_fs;
+ u8 adc_mux_index = 0, adc_mux_sel = 0;
+ bool dec_found = false;
+ u16 adc_mux_ctl_reg, tx_fs_reg;
+ u32 dmic_fs;
+
+ while (dec_found == 0 && adc_mux_index < WCD9335_MAX_VALID_ADC_MUX) {
+ if (adc_mux_index < 4) {
+ adc_mux_ctl_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 +
+ (adc_mux_index * 2);
+ adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) &
+ 0x78) >> 3) - 1;
+ } else if (adc_mux_index < 9) {
+ adc_mux_ctl_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ ((adc_mux_index - 4) * 1);
+ adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) &
+ 0x38) >> 3) - 1;
+ } else if (adc_mux_index == 9) {
+ ++adc_mux_index;
+ continue;
+ }
+ if (adc_mux_sel == dmic)
+ dec_found = true;
+ else
+ ++adc_mux_index;
+ }
+
+ if (dec_found == true && adc_mux_index <= 8) {
+ tx_fs_reg = WCD9335_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index);
+ 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;
+ } else {
+ dmic_fs = pdata->dmic_sample_rate;
+ }
+
+ return dmic_fs;
+}
+
static u8 tasha_get_dmic_clk_val(struct snd_soc_codec *codec,
u32 mclk_rate, u32 dmic_clk_rate)
{
@@ -5512,6 +5554,7 @@ static int tasha_codec_enable_dmic(struct snd_soc_dapm_widget *w,
s32 *dmic_clk_cnt;
u8 dmic_rate_val, dmic_rate_shift = 1;
unsigned int dmic;
+ u32 dmic_sample_rate;
int ret;
char *wname;
@@ -5554,10 +5597,12 @@ static int tasha_codec_enable_dmic(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
+ dmic_sample_rate = tasha_get_dmic_sample_rate(codec, dmic,
+ pdata);
dmic_rate_val =
tasha_get_dmic_clk_val(codec,
pdata->mclk_rate,
- pdata->dmic_sample_rate);
+ dmic_sample_rate);
(*dmic_clk_cnt)++;
if (*dmic_clk_cnt == 1) {
diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
index a7ed5381e690..00519a454fb4 100755
--- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
@@ -1533,8 +1533,15 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
}
if (atomic_read(&prtd->eos)) {
pr_debug("%s: interrupt eos wait queues", __func__);
- prtd->cmd_interrupt = 1;
- wake_up(&prtd->eos_wait);
+ /*
+ * Gapless playback does not wait for eos, do not set
+ * cmd_int and do not wake up eos_wait during gapless
+ * transition
+ */
+ if (!prtd->gapless_state.gapless_transition) {
+ prtd->cmd_interrupt = 1;
+ wake_up(&prtd->eos_wait);
+ }
atomic_set(&prtd->eos, 0);
}
if (atomic_read(&prtd->drain)) {